diff --git a/.circleci/config.yml b/.circleci/config.yml index 6c6f35e1ed24eb..37dabe75a555b9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ version: 2 jobs: Run Tests [mbedtls-build]: docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build:0.2.14 environment: - BOOTSTRAP_ARGUMENTS: ' --with-crypto=mbedtls' - BUILD_TYPE: mbedtls-build @@ -46,7 +46,7 @@ jobs: path: /tmp/test_logs Build Examples [nRF]: docker: - - image: connectedhomeip/chip-build-nrf-platform:0.2.11 + - image: connectedhomeip/chip-build-nrf-platform:0.2.14 environment: - BUILD_TYPE: nrf-build steps: @@ -54,9 +54,32 @@ jobs: - run: command: scripts/examples/nrf_lock_app.sh name: Build example nRF5 Lock App + - run: + command: | + mkdir -p example_binaries/nrf-build + cp examples/lock-app/nrf5/build/chip-nrf52840-lock-example.out \ + example_binaries/nrf-build/chip-nrf52840-lock-example.out + name: Preserve artifacts + - run: + command: | + mkdir -p "master_binaries/nrf-build" + scripts/helpers/bloat_check.py \ + --token "$CIRCLECI_API_TOKEN" \ + --job "Build Examples [nRF]" \ + --artifact-download-dir "master_binaries/nrf-build" \ + --build-output-dir "example_binaries/nrf-build" \ + --report-file bloat_report.txt \ + --github-repository "$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME" \ + --github-comment-pr-number "$CIRCLE_PR_NUMBER" \ + --github-api-token "$GITHUB_BLOAT_API_TOKEN" + name: Generate bloat report + - store_artifacts: + path: bloat_report.txt + - store_artifacts: + path: example_binaries/nrf-build Deploy [main-build]: docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build-openssl:0.2.14 environment: - BUILD_TYPE: main-build steps: @@ -74,7 +97,7 @@ jobs: name: Deployment Check Build Examples [main-build]: docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build-openssl:0.2.14 environment: - BUILD_TYPE: main-build steps: @@ -90,9 +113,35 @@ jobs: - run: command: scripts/examples/standalone_echo_client.sh name: Build example Standalone Echo Client + - run: + command: scripts/examples/standalone_shell.sh + name: Build example Standalone Shell + - run: + command: | + mkdir -p example_binaries/main-build + cp examples/chip-tool/build/chip-standalone-demo.out \ + example_binaries/main-build/chip-standalone-demo.out + name: Preserve artifacts + - run: + command: | + mkdir -p "master_binaries/main-build" + scripts/helpers/bloat_check.py \ + --token "$CIRCLECI_API_TOKEN" \ + --job "Build Examples [main-build]" \ + --artifact-download-dir "master_binaries/main-build" \ + --build-output-dir "example_binaries/main-build" \ + --report-file bloat_report.txt \ + --github-repository "$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME" \ + --github-comment-pr-number "$CIRCLE_PR_NUMBER" \ + --github-api-token "$GITHUB_BLOAT_API_TOKEN" + name: Generate bloat report + - store_artifacts: + path: bloat_report.txt + - store_artifacts: + path: example_binaries/main-build Run Tests [ESP32-QEMU]: docker: - - image: connectedhomeip/chip-build-esp32-qemu:0.2.11 + - image: connectedhomeip/chip-build-esp32-qemu:0.2.14 environment: - BUILD_TYPE: esp32-qemu-build steps: @@ -100,9 +149,15 @@ jobs: - run: command: scripts/tests/esp32_qemu_tests.sh name: Build ESP32 QEMU and Run Tests + - run: + command: scripts/tests/save_logs.sh /tmp/test_logs + name: Save test log files + when: on_fail + - store_artifacts: + path: /tmp/test_logs Build CHIP [mbedtls-build]: docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build:0.2.14 environment: - BOOTSTRAP_ARGUMENTS: ' --with-crypto=mbedtls' - BUILD_TYPE: mbedtls-build @@ -137,7 +192,7 @@ jobs: - build/downloads Run Tests [linux-embedded]: docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build:0.2.14 environment: - BOOTSTRAP_ARGUMENTS: ' --with-device-layer=linux' - BUILD_TYPE: linux-embedded @@ -177,7 +232,7 @@ jobs: path: /tmp/test_logs Build CHIP [linux-embedded]: docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build:0.2.14 environment: - BOOTSTRAP_ARGUMENTS: ' --with-device-layer=linux' - BUILD_TYPE: linux-embedded @@ -212,7 +267,7 @@ jobs: - build/downloads Build CHIP [main-build]: docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build-openssl:0.2.14 environment: - BUILD_TYPE: main-build steps: @@ -246,7 +301,7 @@ jobs: - build/downloads Build Examples [ESP32]: docker: - - image: connectedhomeip/chip-build-esp32:0.2.11 + - image: connectedhomeip/chip-build-esp32:0.2.14 environment: - BUILD_TYPE: esp32-build steps: @@ -254,9 +309,32 @@ jobs: - run: command: scripts/examples/esp_echo_app.sh name: Build example Echo App + - run: + command: | + mkdir -p example_binaries/esp32-build + cp examples/wifi-echo/server/esp32/build/chip-wifi-echo.elf \ + example_binaries/esp32-build/chip-wifi-echo.elf + name: Preserve artifacts + - run: + command: | + mkdir -p "master_binaries/esp32-build" + scripts/helpers/bloat_check.py \ + --token "$CIRCLECI_API_TOKEN" \ + --job "Build Examples [ESP32]" \ + --artifact-download-dir "master_binaries/esp32-build" \ + --build-output-dir "example_binaries/esp32-build" \ + --report-file bloat_report.txt \ + --github-repository "$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME" \ + --github-comment-pr-number "$CIRCLE_PR_NUMBER" \ + --github-api-token "$GITHUB_BLOAT_API_TOKEN" + name: Generate bloat report + - store_artifacts: + path: bloat_report.txt + - store_artifacts: + path: example_binaries/esp32-build Run Tests [clang-build]: docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build:0.2.14 environment: - BOOTSTRAP_ARGUMENTS: ' CC=clang-9 CXX=clang++-9' - BUILD_TYPE: clang-build @@ -296,7 +374,7 @@ jobs: path: /tmp/test_logs Build CHIP [clang-build]: docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build:0.2.14 environment: - BOOTSTRAP_ARGUMENTS: ' CC=clang-9 CXX=clang++-9' - BUILD_TYPE: clang-build @@ -331,7 +409,7 @@ jobs: - build/downloads Run Tests [main-build]: docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build-openssl:0.2.14 environment: - BUILD_TYPE: main-build steps: diff --git a/.circleci/config/commands/@commands.yml b/.circleci/config/commands/@commands.yml index f16b734fc2289a..8244f0245c0552 100644 --- a/.circleci/config/commands/@commands.yml +++ b/.circleci/config/commands/@commands.yml @@ -14,8 +14,7 @@ save-persistent-ci-cache: type: string steps: - save_cache: - key: - build-environment-{{ arch + key: build-environment-{{ arch }}-<>-persistent-cache paths: - ./ci-cache-persistent @@ -26,8 +25,7 @@ load-persistent-ci-cache: type: string steps: - restore_cache: - key: - build-environment-{{ arch + key: build-environment-{{ arch }}-<>-persistent-cache save-build-environment: description: Save the build environment @@ -36,8 +34,7 @@ save-build-environment: type: string steps: - save_cache: - key: - build-environment-{{ arch }}-{{ + key: build-environment-{{ arch }}-{{ .Branch}}-{{.Environment.CIRCLE_SHA1 }}-<< parameters.builder>>-built paths: @@ -49,8 +46,7 @@ save-bootstrapped-tree: type: string steps: - save_cache: - key: - bootstrapped-tree-{{ arch }}-{{ + key: bootstrapped-tree-{{ arch }}-{{ .Branch}}-{{.Environment.CIRCLE_SHA1 }}-<< parameters.builder>>-built paths: @@ -62,8 +58,7 @@ load-bootstrapped-tree: type: string steps: - restore_cache: - key: - bootstrapped-tree-{{ arch }}-{{ + key: bootstrapped-tree-{{ arch }}-{{ .Branch}}-{{.Environment.CIRCLE_SHA1 }}-<< parameters.builder>>-built save-built-tree: @@ -73,8 +68,7 @@ save-built-tree: type: string steps: - save_cache: - key: - built-tree-{{ arch }}-{{ + key: built-tree-{{ arch }}-{{ .Branch}}-{{.Environment.CIRCLE_SHA1}}-<< parameters.builder>>-built paths: @@ -86,8 +80,7 @@ load-build-environment: type: string steps: - restore_cache: - key: - build-environment-{{ arch }}-{{ + key: build-environment-{{ arch }}-{{ .Branch}}-{{.Environment.CIRCLE_SHA1 }}-<< parameters.builder>>-built load-built-tree: @@ -97,8 +90,7 @@ load-built-tree: type: string steps: - restore_cache: - key: - built-tree-{{ arch }}-{{ + key: built-tree-{{ arch }}-{{ .Branch}}-{{.Environment.CIRCLE_SHA1}}-<< parameters.builder>>-built setup-environment: @@ -112,3 +104,28 @@ setup-environment: command: scripts/tools/run_if.sh "ubuntu-16-lts" "$BUILD_TYPE" sudo scripts/setup/linux/install_packages.sh +bloat-check: + description: Bloat check against master branch + parameters: + job_name: + type: string + baseline_download_dir: + type: string + build_output_dir: + type: string + steps: + - run: + name: Generate bloat report + command: | + mkdir -p "<>" + scripts/helpers/bloat_check.py \ + --token "$CIRCLECI_API_TOKEN" \ + --job "<>" \ + --artifact-download-dir "<>" \ + --build-output-dir "<>" \ + --report-file bloat_report.txt \ + --github-repository "$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME" \ + --github-comment-pr-number "$CIRCLE_PR_NUMBER" \ + --github-api-token "$GITHUB_BLOAT_API_TOKEN" + - store_artifacts: + path: bloat_report.txt diff --git a/.circleci/config/executors/@executors.yml b/.circleci/config/executors/@executors.yml index 6723b62dab01ca..6878bd08400a45 100644 --- a/.circleci/config/executors/@executors.yml +++ b/.circleci/config/executors/@executors.yml @@ -1,27 +1,27 @@ main-build: docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build-openssl:0.2.14 nrf-build: docker: - - image: connectedhomeip/chip-build-nrf-platform:0.2.11 + - image: connectedhomeip/chip-build-nrf-platform:0.2.14 esp32-build: docker: - - image: connectedhomeip/chip-build-esp32:0.2.11 + - image: connectedhomeip/chip-build-esp32:0.2.14 esp32-qemu-build: docker: - - image: connectedhomeip/chip-build-esp32-qemu:0.2.11 + - image: connectedhomeip/chip-build-esp32-qemu:0.2.14 linux-embedded: environment: BOOTSTRAP_ARGUMENTS: " --with-device-layer=linux" docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build:0.2.14 mbedtls-build: environment: BOOTSTRAP_ARGUMENTS: " --with-crypto=mbedtls" docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build:0.2.14 clang-build: environment: BOOTSTRAP_ARGUMENTS: " CC=clang-9 CXX=clang++-9" docker: - - image: connectedhomeip/chip-build:0.2.11 + - image: connectedhomeip/chip-build:0.2.14 diff --git a/.circleci/config/jobs/examples_esp32.yml b/.circleci/config/jobs/examples_esp32.yml index 798530eb5ff501..bef056ed04193b 100644 --- a/.circleci/config/jobs/examples_esp32.yml +++ b/.circleci/config/jobs/examples_esp32.yml @@ -1,8 +1,20 @@ environment: - BUILD_TYPE: esp32-build + BUILD_TYPE: esp32-build executor: esp32-build steps: - - checkout - - run: - name: Build example Echo App - command: scripts/examples/esp_echo_app.sh + - checkout + - run: + name: Build example Echo App + command: scripts/examples/esp_echo_app.sh + - run: + name: Preserve artifacts + command: | + mkdir -p example_binaries/esp32-build + cp examples/wifi-echo/server/esp32/build/chip-wifi-echo.elf \ + example_binaries/esp32-build/chip-wifi-echo.elf + - bloat-check: + job_name: Build Examples [ESP32] + baseline_download_dir: master_binaries/esp32-build + build_output_dir: example_binaries/esp32-build + - store_artifacts: + path: example_binaries/esp32-build diff --git a/.circleci/config/jobs/examples_nrf.yml b/.circleci/config/jobs/examples_nrf.yml index 4c30ad85b8c021..4a50e755bb4958 100644 --- a/.circleci/config/jobs/examples_nrf.yml +++ b/.circleci/config/jobs/examples_nrf.yml @@ -1,8 +1,20 @@ environment: - BUILD_TYPE: nrf-build + BUILD_TYPE: nrf-build executor: nrf-build steps: - - checkout - - run: - name: Build example nRF5 Lock App - command: scripts/examples/nrf_lock_app.sh + - checkout + - run: + name: Build example nRF5 Lock App + command: scripts/examples/nrf_lock_app.sh + - run: + name: Preserve artifacts + command: | + mkdir -p example_binaries/nrf-build + cp examples/lock-app/nrf5/build/chip-nrf52840-lock-example.out \ + example_binaries/nrf-build/chip-nrf52840-lock-example.out + - bloat-check: + job_name: Build Examples [nRF] + baseline_download_dir: master_binaries/nrf-build + build_output_dir: example_binaries/nrf-build + - store_artifacts: + path: example_binaries/nrf-build diff --git a/.circleci/config/jobs/examples_standalone.yml b/.circleci/config/jobs/examples_standalone.yml index 6a6e7f2a7a054b..f0dfb46e17cea5 100644 --- a/.circleci/config/jobs/examples_standalone.yml +++ b/.circleci/config/jobs/examples_standalone.yml @@ -1,18 +1,33 @@ parameters: - builder: - type: string + builder: + type: string environment: - BUILD_TYPE: "<< parameters.builder >>" + BUILD_TYPE: "<< parameters.builder >>" executor: << parameters.builder >> steps: - - load-built-tree: - builder: << parameters.builder >> - - load-build-environment: - builder: << parameters.builder >> - - load-persistent-ci-cache: - builder: << parameters.builder >> - - setup-environment: - builder: << parameters.builder >> - - run: - name: Build example Standalone Echo Client - command: scripts/examples/standalone_echo_client.sh + - load-built-tree: + builder: << parameters.builder >> + - load-build-environment: + builder: << parameters.builder >> + - load-persistent-ci-cache: + builder: << parameters.builder >> + - setup-environment: + builder: << parameters.builder >> + - run: + name: Build example Standalone Echo Client + command: scripts/examples/standalone_echo_client.sh + - run: + name: Build example Standalone Shell + command: scripts/examples/standalone_shell.sh + - run: + name: Preserve artifacts + command: | + mkdir -p example_binaries/<> + cp examples/chip-tool/build/chip-standalone-demo.out \ + example_binaries/<>/chip-standalone-demo.out + - bloat-check: + job_name: Build Examples [<>] + baseline_download_dir: master_binaries/<> + build_output_dir: example_binaries/<> + - store_artifacts: + path: example_binaries/<> diff --git a/.circleci/config/jobs/test.yml b/.circleci/config/jobs/test.yml index 7949158bcf0ebf..ee32074dff402d 100644 --- a/.circleci/config/jobs/test.yml +++ b/.circleci/config/jobs/test.yml @@ -1,54 +1,52 @@ parameters: - builder: - type: string - run_setup_payload_tests: - type: boolean - default: true + builder: + type: string + run_setup_payload_tests: + type: boolean + default: true environment: - BUILD_TYPE: << parameters.builder >> + BUILD_TYPE: << parameters.builder >> executor: << parameters.builder >> steps: - - load-built-tree: - builder: << parameters.builder >> - - load-build-environment: - builder: << parameters.builder >> - - load-persistent-ci-cache: - builder: << parameters.builder >> - - setup-environment: - builder: << parameters.builder >> - - run: - name: Run mbedTLS Tests - command: - scripts/tools/run_if.sh "mbedtls-build" "$BUILD_TYPE" - scripts/tests/mbedtls_tests.sh - - run: - name: Run Crypto Tests - command: - scripts/tools/run_if.sh "main-build mbedtls-build clang-build" - "$BUILD_TYPE" scripts/tests/crypto_tests.sh - - run: - name: Run Setup Payload Tests - command: - scripts/tools/run_if.sh "main-build ubuntu-16-lts clang-build" - "$BUILD_TYPE" scripts/tests/setup_payload_tests.sh - - run: - name: OpenSSL Tests - command: - scripts/tools/run_if.sh "main-build clang-build" "$BUILD_TYPE" - scripts/tests/openssl_tests.sh - - run: - name: Run Platform Tests - command: - scripts/tools/run_if.sh "linux-embedded" "$BUILD_TYPE" - make -C build/default/src/platform check - - run: - name: Run All Unit & Functional Tests - command: - scripts/tools/run_if.sh "main-build clang-build" "$BUILD_TYPE" - scripts/tests/all_tests.sh - - run: - name: Save test log files - command: scripts/tests/save_logs.sh /tmp/test_logs - when: on_fail - - store_artifacts: - path: /tmp/test_logs + - load-built-tree: + builder: << parameters.builder >> + - load-build-environment: + builder: << parameters.builder >> + - load-persistent-ci-cache: + builder: << parameters.builder >> + - setup-environment: + builder: << parameters.builder >> + - run: + name: Run mbedTLS Tests + command: scripts/tools/run_if.sh "mbedtls-build" "$BUILD_TYPE" + scripts/tests/mbedtls_tests.sh + - run: + name: Run Crypto Tests + command: + scripts/tools/run_if.sh "main-build mbedtls-build clang-build" + "$BUILD_TYPE" scripts/tests/crypto_tests.sh + - run: + name: Run Setup Payload Tests + command: + scripts/tools/run_if.sh "main-build ubuntu-16-lts clang-build" + "$BUILD_TYPE" scripts/tests/setup_payload_tests.sh + - run: + name: OpenSSL Tests + command: + scripts/tools/run_if.sh "main-build clang-build" "$BUILD_TYPE" + scripts/tests/openssl_tests.sh + - run: + name: Run Platform Tests + command: scripts/tools/run_if.sh "linux-embedded" "$BUILD_TYPE" + make -C build/default/src/platform check + - run: + name: Run All Unit & Functional Tests + command: + scripts/tools/run_if.sh "main-build clang-build" "$BUILD_TYPE" + scripts/tests/all_tests.sh + - run: + name: Save test log files + command: scripts/tests/save_logs.sh /tmp/test_logs + when: on_fail + - store_artifacts: + path: /tmp/test_logs diff --git a/.circleci/config/jobs/test_esp32_qemu.yml b/.circleci/config/jobs/test_esp32_qemu.yml index 7d1dda31c51981..67a3b738cbe2eb 100644 --- a/.circleci/config/jobs/test_esp32_qemu.yml +++ b/.circleci/config/jobs/test_esp32_qemu.yml @@ -6,3 +6,9 @@ steps: - run: name: Build ESP32 QEMU and Run Tests command: scripts/tests/esp32_qemu_tests.sh + - run: + name: Save test log files + command: scripts/tests/save_logs.sh /tmp/test_logs + when: on_fail + - store_artifacts: + path: /tmp/test_logs diff --git a/.default-version.min b/.default-version.min new file mode 100644 index 00000000000000..251d043e50d591 --- /dev/null +++ b/.default-version.min @@ -0,0 +1 @@ +CHIP_VERSION=0.1.0 diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 6c53cbfd9c339d..ac80fd42766735 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -30,13 +30,13 @@ RUN ln -s /usr/bin/clang-format-9 /usr/local/bin/clang-format RUN mkdir -p /var/downloads RUN cd /var/downloads RUN curl -JL https://github.com/microsoft/vscode-cpptools/releases/download/0.27.0/cpptools-linux.vsix | bsdtar -xvf - extension -RUN mkdir -p /home/vscode/.vscode-server/extensions -RUN mv extension /home/vscode/.vscode-server/extensions/ms-vscode.cpptools-0.27.0 -RUN mkdir -p /home/vscode/bin -RUN curl https://raw.githubusercontent.com/restyled-io/restyler/master/bin/restyle-path -o /home/vscode/bin/restyle-path -RUN chmod +x /home/vscode/bin/restyle-path -RUN chown -R vscode:vscode /home/vscode -RUN echo "PATH=/home/vscode/bin:${PATH}" >> /home/vscode/.bashrc -# vscode needs to own the esp-idf and tools for the examples to build -RUN chown -R vscode:vscode /var/esp-idf -RUN chown -R vscode:vscode /var/.espressif +RUN mkdir -p /home/$USERNAME/.vscode-server/extensions +RUN mv extension /home/$USERNAME/.vscode-server/extensions/ms-vscode.cpptools-0.27.0 +RUN mkdir -p /home/$USERNAME/bin +RUN curl https://raw.githubusercontent.com/restyled-io/restyler/master/bin/restyle-path -o /home/$USERNAME/bin/restyle-path +RUN chmod +x /home/$USERNAME/bin/restyle-path +RUN chown -R $USERNAME:$USERNAME /home/$USERNAME +RUN echo "PATH=/home/$USERNAME/bin:${PATH}" >> /home/$USERNAME/.bashrc +# $USERNAME needs to own the esp-idf and tools for the examples to build +RUN chown -R $USERNAME:$USERNAME /var/esp-idf +RUN chown -R $USERNAME:$USERNAME /var/.espressif diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 30e7a2ecb5a58b..2a5052ba41f153 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,7 @@ "dockerfile": "Dockerfile", "args": { // "BUILD_VERSION": "$(cat integrations/docker/images/chip-build/version)" // trying to get this to work - "BUILD_VERSION": "0.2.11" + "BUILD_VERSION": "0.2.15" } }, "remoteUser": "vscode", diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 569bb372e09db2..baa85d3b9b6e1b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @anush-apple @chrisdecenzo @hawk248 @gerickson @robszewczyk @woody-apple @BroderickCarlin @saurabhst @jelderton +* @anush-apple @chrisdecenzo @hawk248 @andy31415 @robszewczyk @woody-apple @BroderickCarlin @saurabhst @jelderton diff --git a/.gitignore b/.gitignore index 3bd6c2b5aca339..d572189d64957a 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,7 @@ third_party/nlio/ third_party/nlunit-test/ third_party/mbedtls/ examples/common/m5stack-tft/ -examples/common/QRCode/repo +examples/common/QRCode/repo/ # Example specific rules examples/**/sdkconfig @@ -40,7 +40,7 @@ src/test_driver/esp32/build # Temporary Directories .tmp/ -# Xcode +# Xcode, other development environment stuff *.pbxuser *.mode1v3 *.mode2v3 @@ -50,3 +50,4 @@ project.xcworkspace/ xcuserdata/ *.xcodeproj/* *.xcworkspace/* +TAGS diff --git a/.pullapprove.yml b/.pullapprove.yml new file mode 100644 index 00000000000000..1eb5176dd34ac4 --- /dev/null +++ b/.pullapprove.yml @@ -0,0 +1,82 @@ +version: 3 + +# https://developer.github.com/v3/previews/#draft-pull-requests +github_api_version: "shadow-cat-preview" + +############################################################ +# Conditions +############################################################ + +pullapprove_conditions: + ############################################################ + # Required status checks + ############################################################ + - condition: "'*restyle*' in statuses.successful" + unmet_status: "failure" + explanation: "Style must be inline before reviewing can be complete" + + ############################################################ + # Draft PRs + ############################################################ + - condition: "'WIP' not in title" + unmet_status: "pending" + explanation: "Work in progress" + + - condition: "not draft" + unmet_status: "pending" + explanation: "Work in progress" + + ############################################################ + # Conditions to Skip Review + ############################################################ + - condition: "base.ref == 'master'" + unmet_status: "success" + explanation: "Review not required unless merging to master" + + ############################################################ + # Bypass reviews + ############################################################ + - "'hotfix' not in labels" + +############################################################ +# Notifications +############################################################ + +notifications: + ############################################################ + # New contributors + ############################################################ + - when: pull_request.opened + if: "author_association == 'FIRST_TIME_CONTRIBUTOR'" + comment: | + Hey @{{ author }}, thanks for the PR! The review will start once + the tests and CI checks have passed. If they don't, please review + the logs and try to fix the issues (ask for help if you can't + figure it out). A reviewer will be assigned once the tests are + passing and they'll walk you through getting the PR finished + and merged. + +groups: + ############################################################ + # Base Required Reviewers + ############################################################ + required-reviewers: + description: > + [Required + Reviewers](https://github.com/project-chip/connectedhomeip/blob/master/CONTRIBUTING.md#review-requirements) + This is the main group of required reviews for general pull + requests. + type: required + conditions: + - files.include('*') + reviews: + required: 3 + request: -1 + request_order: shuffle + reviewers: + teams: + - reviewers-amazon + - reviewers-apple + - reviewers-comcast + - reviewers-google + - reviewers-samsung diff --git a/.restyled.yaml b/.restyled.yaml index 2c18417ec6e686..c6dd86253d6ba0 100644 --- a/.restyled.yaml +++ b/.restyled.yaml @@ -48,6 +48,10 @@ restylers: - whitespace arguments: [] include: + - "**/Dockerfile" + - "**/*.yml" + - "**/*.yaml" + - "**/*.sh" - "**/*.c" - "**/*.cc" - "**/*.cpp" @@ -63,6 +67,7 @@ restylers: - "**/*.H" - "**/*.java" - "**/*.js" + - "**/*.json" - "**/*.m" - "**/*.mm" - name: clang-format diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 5f454ef901f5df..0cd88daedb8a84 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -15,9 +15,7 @@ "reveal": "always", "panel": "shared" }, - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Auto-enforce coding style", @@ -30,9 +28,7 @@ "reveal": "always", "panel": "shared" }, - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Verify coding style conformance", @@ -40,9 +36,7 @@ "command": "scripts/helpers/code_style_check.sh", "group": "none", "dependsOn": "Bootstrap", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Run Unit and Functional Tests", @@ -50,9 +44,7 @@ "command": "scripts/tests/all_tests.sh", "group": "none", "dependsOn": "Bootstrap", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Run Distribution Generation", @@ -60,9 +52,7 @@ "command": "scripts/build/distribution_check.sh", "group": "none", "dependsOn": "Bootstrap", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Run Code Coverage", @@ -70,9 +60,7 @@ "command": "scripts/tools/codecoverage.sh; code -r build/default/src/chip.info/index.html", "group": "none", "dependsOn": "Bootstrap", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Clean", @@ -80,27 +68,21 @@ "command": "scripts/helpers/clean.sh", "group": "none", "dependsOn": "Bootstrap", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Bootstrap", "type": "shell", "command": "scripts/build/bootstrap.sh", "group": "none", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Clean Tree", "type": "shell", "command": "scripts/helpers/clean_tree.sh", "group": "none", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Run Setup Payload Tests", @@ -108,9 +90,7 @@ "command": "scripts/tests/setup_payload_tests.sh", "group": "none", "dependsOn": "Bootstrap", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Build QRCode Payload Tests", @@ -118,18 +98,14 @@ "command": "scripts/tests/qrcode_payload_tests.sh", "group": "none", "dependsOn": "Bootstrap", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Build & Run Crypto Tests", "type": "shell", "command": "scripts/tests/crypt_tests.sh", "group": "none", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Build nRF5 Lock App", @@ -137,18 +113,14 @@ "command": "scripts/examples/nrf_lock_app.sh", "group": "none", "dependsOn": "Clean Tree", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Build openSSL crypto Tests", "type": "shell", "command": "scripts/tests/openssl_tests.sh", "group": "none", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Build ESP32 Echo Example", @@ -156,9 +128,7 @@ "command": "scripts/examples/esp_echo_app.sh", "group": "none", "dependsOn": "Clean Tree", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] }, { "label": "Build Standalone Echo Client", @@ -166,9 +136,15 @@ "command": "scripts/examples/standalone_echo_client.sh", "group": "none", "dependsOn": "Clean Tree", - "problemMatcher": [ - "$gcc" - ] + "problemMatcher": ["$gcc"] + }, + { + "label": "Build Standalone Shell", + "type": "shell", + "command": "scripts/examples/standalone_shell.sh", + "group": "none", + "dependsOn": "Clean Tree", + "problemMatcher": ["$gcc"] } ] -} \ No newline at end of file +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f7f70b247a40c4..5bfeed8042b82e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,6 +34,60 @@ Currently these are the requirements to becoming a member of the Writer, Developer (or exception granted), Code Approvers, or Support Staff - Have approval from your company's official approver +## Getting Started + +This repository contains the source code that implements the CHIP specification. +It scalably implements the specification that may be used on a wide range of +platforms including Android/iOS and Darwin/Linux down to embedded MCU-based +platforms running FreeRTOS and LwIP. + +The source code can be built to generate: + +- **Libraries** that can be built for iOS, Android or desktop (Linux/Mac) + targets. These libraries could further be integrated into applications that + talk _CHIP_. +- **Firmwares/Embedded Applications** that can be built for the supported + embedded platforms. +- **Desktop Application** that can be used in conjunction with the embedded + applications above to validate the end-to-end CHIP workflow. + +### Building your first application + +- Building the firmware: This repository implements the CHIP specification on + 3 transports: 802.15.4 Thread, BLE and Wi-Fi. The examples/ directory + contains example applications for all these 3 transports using 3 embedded + platforms. Please visit their respective directories for instructions on how + to build and deploy on these platforms. + - NRF5 (for 802.15.4 Thread): in + [examples/lock-app/nrf5](examples/lock-app/nrf5) + - EFR32 (for BLE): in [examples/lock-app/efr32](examples/lock-app/efr32) + - ESP32 (for Wi-Fi or BLE): in + [examples/wifi-echo/server/esp32](examples/wifi-echo/server/esp32) +- Building the host utility: The host utility can be used in conjunction with + the embedded platform for end-to-end validation. Please visit the + [examples/chip-tool](examples/chip-tool) directory for further instructions. + +### Where should I begin? + +- Good First Issue: Certain issues are marked with a label + [Good First Issue](https://github.com/project-chip/connectedhomeip/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). + These issues are what we believe may be good starting points for getting + your hands dirty. +- TODOs: Most items that will require work are captured in the GitHub issues + of this project. This serves as a good TODO list for the next steps. +- Milestones: A list of + [milestones](https://github.com/project-chip/connectedhomeip/milestones) are + maintained in this project. This should provide some idea of where things + are headed. Note that given the early days of this project, most of these + are not hard deadlines. + +### Where is the spec? + +- The specification is evolving in various tiger teams. Members belonging to + various tiger teams may contribute experimental code for the general + direction where the spec is headed. Once a specification is fairly + finalised, it will be available/committed in the [docs/specs](docs/specs). + ## Bugs If you find a bug in the source code, you can help us by diff --git a/Makefile-Standalone b/Makefile-Standalone index 7822e6160f4a0e..158cb95f7b94d5 100644 --- a/Makefile-Standalone +++ b/Makefile-Standalone @@ -35,6 +35,7 @@ DEBUG ?= 0 TIMESTAMP ?= 0 TUNNEL_FAILOVER ?= 0 USE_LWIP ?= 0 +USE_NW ?= 0 NO_OPENSSL ?= 0 BLUEZ ?= 0 USE_FUZZING ?= 0 @@ -165,6 +166,11 @@ else configure_OPTIONS += endif +# Allow openssl installation selection +ifneq ($(OPENSSL),) +configure_OPTIONS += --with-openssl=$(OPENSSL) +endif + # If the user has asserted USE_LWIP, alter the configuration and # target tuple to use LwIP rather than the expected BSD sockets. @@ -173,6 +179,11 @@ configure_OPTIONS += --with-target-network=lwip --with-lwip=intern TargetTuple := $(TargetTuple)-lwip endif +ifeq ($(USE_NW),1) +configure_OPTIONS += --with-target-network=Network.framework +TargetTuple := $(TargetTuple)-nw +endif + ifeq ($(LONG_TESTS),1) configure_OPTIONS += --enable-long-tests=yes endif @@ -455,15 +466,6 @@ command line or in the environment: include and lib subdirectories containing the necessary header and libraries. - OPENSSL=internal Build the internal copy of OpenSSL as part of the CHIP - build. - - OPENSSL=no Build an alternate configuration that does not depend - NO_OPENSSL=1 on the use of OpenSSL (default: '$(NO_OPENSSL)'. Note - that the various command line tools that depend on - OpenSSL (e.g., the CHIP tool) will not be built in - this configuration. - TUNNEL_FAILOVER=[1|0] Enable/disabled support for redundant VPN to the CHIP service (default: '$(TUNNEL_FAILOVER)'). diff --git a/Makefile.am b/Makefile.am index e57335ef9394a2..fcce0293287cdd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,10 +54,10 @@ EXTRA_DIST = \ bootstrap-configure \ repos.conf \ $(srcdir)/build/autoconf \ - $(srcdir)/config \ - $(srcdir)/config/efr32 \ - $(srcdir)/config/nrf5 \ - $(srcdir)/scripts \ + $(srcdir)/config \ + $(srcdir)/config/efr32 \ + $(srcdir)/config/nrf5 \ + $(srcdir)/scripts \ $(NULL) BUILT_SOURCES = \ @@ -70,6 +70,7 @@ dist_doc_DATA = \ DISTCLEANFILES = \ .local-version \ + .local-version.min \ $(NULL) # There are no source files to lint or prettify in this subdirectory. @@ -102,24 +103,28 @@ PRETTY_FILES := $(NULL) # during makefile execution. VERSION_FILE := $(if $(wildcard $(builddir)/.local-version),$(builddir)/.local-version,$(if $(wildcard $(srcdir)/.dist-version),$(srcdir)/.dist-version,$(srcdir)/.default-version)) + # # Override autotool's default notion of the package version variables. # This ensures that when we create a source distribution the # version is always the current version, not the package bootstrap # version. # -# The two-level variables ensures that not only can the package version -# be overridden from the command line but also when the version is NOT +# The include ensures that we update CHIP_VERSION and VERSION once +# we've built .local-version (the source of truth unless CHIP_VERSION +# is specified on the make command line). +# +# CHIP_VERSION can be overridden from the command line, but when the version is NOT # overridden that we bind the version once and only once across potential # sub-makes to prevent the version from flapping as VERSION_FILE changes. # -export MAYBE_CHIP_VERSION := $(shell cat $(VERSION_FILE) 2> /dev/null) - -CHIP_VERSION ?= $(MAYBE_CHIP_VERSION) +# .local-version.min sets CHIP_VERSION +-include .local-version.min PACKAGE_VERSION = $(CHIP_VERSION) VERSION = $(PACKAGE_VERSION) + # # check-file-.local-version # @@ -140,6 +145,7 @@ $(if $(filter-out file,$(origin CHIP_VERSION)),\ > "$(2)") endef + # # check-file-.dist-version # @@ -172,6 +178,10 @@ $(distdir)/.dist-version: $(builddir)/.local-version force $(distdir)/.dist-version $(builddir)/.local-version: $(call check-file,$(@F)) +$(builddir)/.local-version.min: $(builddir)/.local-version + (printf "export CHIP_VERSION=" ; cat) < "$(VERSION_FILE)" > $@ + + # # When we run 'distcheck' and --with-, default to 'internal', the nlbuild-autotools diff --git a/REVIEWERS.md b/REVIEWERS.md index f7b98aecdb2e7d..acc599464cb578 100644 --- a/REVIEWERS.md +++ b/REVIEWERS.md @@ -8,7 +8,7 @@ PR | [chrisdecenzo](https://github.com/chrisdecenzo) | Amazon, Inc. | | [hawk248](https://github.com/hawk248) | Comcast, Inc. | | [jelderton](https://github.com/jelderton) | Comcast, Inc. | -| [gerickson](https://github.com/gerickson) | Google, Inc. | +| [andy31415](https://github.com/andy31415) | Google, Inc. | | [robszewczyk](https://github.com/robszewczyk) | Google, Inc. | | [saurabhst](https://github.com/saurabhst) | Samsung SmartThings | | [woody-apple](https://github.com/woody-apple) | Apple, Inc. | diff --git a/config/esp32/components/chip/Kconfig b/config/esp32/components/chip/Kconfig index d9da33d69e54c4..cd179a2ff2e066 100644 --- a/config/esp32/components/chip/Kconfig +++ b/config/esp32/components/chip/Kconfig @@ -323,7 +323,7 @@ menu "CHIP Device Layer" config CHIP_TASK_STACK_SIZE int "CHIP Task Stack Size" range 0 65535 - default 4608 + default 8192 help The size (in bytes) of the CHIP task stack. diff --git a/config/esp32/components/chip/component.mk b/config/esp32/components/chip/component.mk index 8e204514b494f4..e4ada3b2a45790 100644 --- a/config/esp32/components/chip/component.mk +++ b/config/esp32/components/chip/component.mk @@ -115,7 +115,7 @@ CONFIGURE_OPTIONS := -C AR="$(AR)" CC="$(CC)" CXX="$(CXX)" LD="$(LD)" OBJ --disable-device-manager ifneq (,$(findstring CHIP_SUPPORT_FOREIGN_TEST_DRIVERS,$(CXXFLAGS))) -CONFIGURE_OPTIONS += --enable-tests +CONFIGURE_OPTIONS += --enable-tests --enable-nlfaultinjection else CONFIGURE_OPTIONS += --disable-tests endif diff --git a/config/nrf5/nrf5-chip.mk b/config/nrf5/nrf5-chip.mk index ad15afcac740a7..bf291bcb612f85 100644 --- a/config/nrf5/nrf5-chip.mk +++ b/config/nrf5/nrf5-chip.mk @@ -95,6 +95,7 @@ CHIP_CONFIGURE_OPTIONS = \ --exec-prefix=$(CHIP_OUTPUT_DIR) \ --host=$(CHIP_HOST_ARCH) \ --build=$(CHIP_BUILD_ARCH) \ + --with-target-style=embedded \ --with-device-layer=nrf5 \ --with-network-layer=all \ --with-target-network=lwip \ @@ -108,6 +109,7 @@ CHIP_CONFIGURE_OPTIONS = \ --with-chip-ble-project-includes=$(CHIP_PROJECT_CONFIG) \ --with-chip-warm-project-includes=$(CHIP_PROJECT_CONFIG) \ --with-chip-device-project-includes=$(CHIP_PROJECT_CONFIG) \ + --with-openthread=$(NRF5_SDK_ROOT)/external/openthread \ --disable-ipv4 \ --disable-tests \ --disable-tools \ diff --git a/config/standalone/standalone-app.mk b/config/standalone/standalone-app.mk index 93743d784bf3e7..f8c352ae755d53 100644 --- a/config/standalone/standalone-app.mk +++ b/config/standalone/standalone-app.mk @@ -202,7 +202,7 @@ $(APP) : $(APP_EXE) # Rule to link the application executable $(APP_EXE) : $(OBJS) $(STD_LINK_PREREQUISITES) | $(OUTPUT_DIR) @echo "$$(HDR_PREFIX)LD $$@" - $(NO_ECHO)$$(CC) $$(STD_LDFLAGS) $$(LDFLAGS) $$(DEBUG_FLAGS) $$(OPT_FLAGS) $$(OBJS) $$(LIBS) $$(STD_LIBS) -o $$@ + $(NO_ECHO)$$(CXX) $$(STD_LDFLAGS) $$(LDFLAGS) $$(DEBUG_FLAGS) $$(OPT_FLAGS) $$(OBJS) $$(LIBS) $$(STD_LIBS) -o $$@ $(NO_ECHO)$$(SIZE) $$@ # Individual build rules for each application source file @@ -297,4 +297,4 @@ Build options: $(OptionHelp) -endef \ No newline at end of file +endef diff --git a/config/standalone/standalone-chip.mk b/config/standalone/standalone-chip.mk index 322a9ecb2449c7..e189f64736764f 100644 --- a/config/standalone/standalone-chip.mk +++ b/config/standalone/standalone-chip.mk @@ -139,6 +139,8 @@ STD_LIBS += \ -lnlfaultinjection \ -lSystemLayer +STD_LIBS += $(shell pkg-config --libs openssl) + # Add the appropriate CHIP target as a prerequisite to all application # compilation targets to ensure that CHIP gets built and its header # files installed prior to compiling any dependent source files. diff --git a/configure.ac b/configure.ac index bfef30cf3955a7..dec52c4a13f25f 100644 --- a/configure.ac +++ b/configure.ac @@ -133,13 +133,14 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AM_SILENT_RULES([yes]) # -# Enable maintainer mode to prevent the package from constantly trying -# to rebuild configure, Makefile.in, etc. Rebuilding such files rarely, -# if ever, needs to be done "in the field". +# Disable maintainer mode to enable the package to automatically rebuild +# configure, Makefile.in, etc. when the files on which they depend change (for +# example, configure.ac, Makefile.am, etc). # -# Use the included 'bootstrap' script instead when necessary. +# For those that do not desire this behavior, run configure with +# `--enable-maintainer-mode` and run the top-level `bootstrap` script manually. # -AM_MAINTAINER_MODE +AM_MAINTAINER_MODE([disable]) # # Check for the target style @@ -903,6 +904,36 @@ NL_ENABLE_DOCS([auto],[NO]) AM_CONDITIONAL(CHIP_BUILD_DOCS, [test "${nl_cv_build_docs}" = "yes"]) +# +# OpenThread +# + +NL_WITH_OPTIONAL_INTERNAL_PACKAGE( + [OpenThread], + [OPENTHREAD], + [openthread], + [], + [ + AC_MSG_NOTICE([No internal OpenThread support yet!]) + with_openthread=no + ], + [ + # Check for required OpenThread headers. + AC_CHECK_HEADERS([openthread/dataset.h] [openthread/dataset_ftd.h] [openthread/error.h] [openthread/icmp6.h] [openthread/instance.h] [openthread/ip6.h] [openthread/link.h] [openthread/message.h] [openthread/netdata.h] [openthread/tasklet.h] [openthread/thread.h], + [], + [ + AC_MSG_ERROR(The nlio header "$ac_header" is required but cannot be found.) + ]) + ] +) +AM_CONDITIONAL([CHIP_ENABLE_OPENTHREAD], [test "${with_openthread}" != "no"]) +if test "${with_openthread}" != "no"; then + CHIP_ENABLE_OPENTHREAD=1 +else + CHIP_ENABLE_OPENTHREAD=0 +fi +AC_DEFINE_UNQUOTED([CHIP_ENABLE_OPENTHREAD],[${CHIP_ENABLE_OPENTHREAD}],[Define to 1 if you want to enable OpenThread.]) + # # Network Technology Layer # @@ -1070,14 +1101,14 @@ CONFIG_TARGET_NETWORKS=sockets AC_MSG_CHECKING([target network]) AC_ARG_WITH(target-network, [AS_HELP_STRING([--with-target-network=NETWORK], - [Specify the target network stack from one or more of, separated by commas: sockets or lwip @<:@default=sockets@:>@.])], + [Specify the target network stack from one or more of, separated by commas: sockets, lwip or Network.framework @<:@default=sockets@:>@.])], [ CONFIG_TARGET_NETWORKS=`echo ${with_target_network} | sed -e 's/,/ /g'` for target_network in ${CONFIG_TARGET_NETWORKS}; do case "${target_network}" in - lwip|sockets) + lwip|sockets|Network.framework) ;; *) @@ -1094,6 +1125,7 @@ AC_DEFINE_UNQUOTED([CONFIG_TARGET_NETWORKS], "${CONFIG_TARGET_NETWORKS}", [CHIP CHIP_SYSTEM_CONFIG_USE_LWIP=0 CHIP_SYSTEM_CONFIG_USE_SOCKETS=0 +CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK=0 for target_network in ${CONFIG_TARGET_NETWORKS}; do case ${target_network} in @@ -1106,6 +1138,10 @@ for target_network in ${CONFIG_TARGET_NETWORKS}; do CHIP_SYSTEM_CONFIG_USE_SOCKETS=1 ;; + Network.framework) + CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK=1 + ;; + esac done @@ -1119,6 +1155,12 @@ AM_CONDITIONAL([CHIP_SYSTEM_CONFIG_USE_SOCKETS], [test "${CHIP_SYSTEM_CONFIG_USE AC_DEFINE_UNQUOTED([CHIP_SYSTEM_CONFIG_USE_SOCKETS], [${CHIP_SYSTEM_CONFIG_USE_SOCKETS}], [Define to 1 if you want to use BSD sockets with CHIP System Layer.]) +AC_SUBST(CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK) +AM_CONDITIONAL([CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK], [test "${CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK}" = 1]) +AC_DEFINE_UNQUOTED([CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK], [${CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK}], + [Define to 1 if you want to use Network.framework with CHIP System Layer.]) + +# # # Internet Protocol Network Endpoints # @@ -1798,6 +1840,32 @@ fi AC_SUBST(NLUNIT_TEST_SUBDIRS, [${maybe_nlunit_test_dirstem}]) AM_CONDITIONAL([CHIP_WITH_NLUNIT_TEST_INTERNAL], [test "${nl_with_nlunit_test}" = "internal"]) +# +# +# IniPP +# + +if test "${CHIP_DEVICE_LAYER_TARGET_LINUX}" = 1; then + NL_WITH_PACKAGE( + [IniPP], + [INIPP], + [], + [], + [ + INIPP_CPPFLAGS="-I\${abs_top_srcdir}/third_party/inipp/repo/inipp" + ], + [ + # Check for required IniPP headers. + + AC_CHECK_HEADERS([inipp.h], + [], + [ + AC_MSG_ERROR(The IniPP header "inipp.h" is required but cannot be found.) + ]) + ] + ) +fi + # # Sockets # @@ -1972,6 +2040,14 @@ AX_PTHREAD([], []) AC_CHECK_DECL([PTHREAD_NULL], [], [AC_DEFINE([PTHREAD_NULL],[0], [Approximation of PTHREAD_NULL since pthread.h does not define one])], [[#include ]]) +# +# Check for Network.framework +# +if test "${CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK}" = "1"; then + AC_CHECK_HEADER([Network/Network.h]) + LDFLAGS="${LDFLAGS} -lnetwork" +fi + # # Check for # @@ -2093,6 +2169,8 @@ src/inet/Makefile src/inet/tests/Makefile src/lib/Makefile src/lib/core/tests/Makefile +src/lib/shell/Makefile +src/lib/shell/tests/Makefile src/lib/support/Makefile src/lib/support/tests/Makefile src/platform/Makefile @@ -2210,6 +2288,7 @@ AC_MSG_NOTICE([ Sockets link libraries : ${SOCKETS_LIBS:--} PThreads compile flags : ${PTHREAD_CFLAGS:--} PThreads link libraries : ${PTHREAD_LIBS:--} + IniPP compile flags : ${INIPP_CPPFLAGS:--} C Preprocessor : ${CPP} C Compiler : ${CC} C++ Preprocessor : ${CXXCPP} diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 1bf1d5ce9b53e4..aaf518bdc98e93 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -1,11 +1,11 @@ ## Build Documentation The CHIP build system uses GNU autotools to build various platform images on -Linux or MacOS. +Linux or macOS. Tested on: -- MacOS +- macOS - Ubuntu 18.04 Build system features: @@ -32,6 +32,7 @@ want: - C and C++ compilers - clang-format-9 - gcov +- pkg-config #### How to install tool prerequisites on Linux @@ -39,7 +40,7 @@ On Debian-based Linux distributions such as Ubuntu, these dependencies can be satisfied with the following: ``` -sudo apt-get install make autoconf automake libtool +sudo apt-get install make autoconf automake libtool pkg-config sudo apt-get install clang-format-9 sudo apt-get install lcov ``` @@ -50,11 +51,48 @@ On macOS, these dependencies can be installed and satisfied using [Brew](https://brew.sh/): ``` -brew install make autoconf automake libtool +brew install make autoconf automake libtool pkg-config brew install llvm@9 brew install lcov ``` +### Library Prerequisites + +The CHIP build currently requires the OpenSSL. This should be installed and +visible to `pkg-config`. + +#### How to install library prerequisites on Linux + +On Debian-based Linux distributions such as Ubuntu, these dependencies can be +satisfied with the following: + +``` +sudo apt-get install openssl +``` + +#### How to install library prerequisites on macOS + +On macOS, these dependencies can be satisfied using [Brew](https://brew.sh/): + +``` +brew install openssl + +``` + +However, that does not expose the package to `pkg-config`. To fix that, one +needs to run something like the following: + +``` +cd /usr/local/lib/pkgconfig +ln -s ../../Cellar/openssl@1.1/1.1.1g/lib/pkgconfig/* . +``` + +where `openssl@1.1/1.1.1g` may need to be replaced with the actual version of +OpenSSL installed by Brew. + +Note: If using MacPorts, `port install openssl` is sufficient to satisfy this +dependency. + ### Autotools Build Preparation Before running any other build command, the `./bootstrap` command must be run @@ -65,7 +103,7 @@ from the top-level. ./bootstrap ``` -### Build Standalone (Native Linux or MacOS) +### Build Standalone (Native Linux or macOS) This will build all sources, libraries, and tests for the given platform: @@ -229,7 +267,7 @@ make -f Makefile-iOS Install Android Studio, Java, and NDK. ``` -# Update these paths based on your environment and version of the tools (MacOS examples): +# Update these paths based on your environment and version of the tools (macOS examples): export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home export ANDROID_HOME=~/Library/Android/sdk export ANDROID_NDK_HOME=~/Library/Android/sdk/ndk/21.0.6113669 diff --git a/docs/style/DOXYGEN.adoc b/docs/style/DOXYGEN.adoc new file mode 100644 index 00000000000000..2ae28a31c43ade --- /dev/null +++ b/docs/style/DOXYGEN.adoc @@ -0,0 +1,240 @@ +[.text-center] += Project Connected Home over IP Software + +:plusplus: ++ + +== Doxygen Best Practices, Conventions, and Style + +=== Comments + +Due to Project CHIP’s infrastructure nature, it will be consumed by +other teams, both inside and outside Project CHIP. Therefore it is +critical that how it works, how it behaves, and how it is +interfaced with are **clearly** documented. + +In support of this effort Project CHIP uses +http://www.doxygen.org/[Doxygen] to +markup (or markdown) all C, C{plusplus}, Objective C, Objective C{plusplus}, Perl, +Python, and Java code to: + +* Detail what the various source and header files are and how they fit +into the broader context. +* Detail what the various C{plusplus} / Objective C{plusplus} namespaces are. +* Detail what the constants, C preprocessor definitions, and +enumerations are. +* Detail what the globals are and how they are to be used. +* Detail what the free function and object / class methods are and how +they are to be used, what their parameters are, and what their return +values are. +* Detail any other important technical information or theory of +operation unique and relevant to the stack that is not otherwise +captured in architecture, design, or protocol documentation. + +==== File + +Every C, C{plusplus}, Objective C, Objective C{plusplus}, Perl, Python, Shell, and Java +source file should, at minimum, have a standard, boilerplate Project +CHIP file header that also describes what the file is and how, if +applicable, it fits into the broader implementation. + +Canonical examples for C, C{plusplus}, Objective C, and Objective C{plusplus} and +Python, Perl, and shell are shown in Listing 1 and Listing 2 below. + +[source,C] +---- +/* + * Copyright (c) [-] Project CHIP Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * + * + * [] + */ +---- +[.text-center] +*Listing 1.* Standard, boilerplate Project CHIP file header for C, C{plusplus}, +Objective C, and Objective C{plusplus}.. + +[source,perl] +---- +# +# Copyright (c) [-] Project CHIP Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 +# +# +# [] +# +---- +[.text-center] +*Listing 2.* Standard, boilerplate Project CHIP file header for Perl, +Python, shell, and make. + +where: + +* __ is the year the file was created. +* __ is, optionally, the year the file was last +modified if it is different from __. +* __ is a succinct description of what the file is. +* __ is, optionally, a more in-depth description of +what the file is and how it fits into the broader context. + +For header files, a good prologue for __ is "This file +defines...", describing what is being defined or declared. Likewise, for +source files, a good prologue for __ is "This file +implements...", describing what is being implemented. Usually, +copy-and-pasting the brief description from the header to the source and +changing the prologue from "defines" to "implements" is sufficient. + +The __, if present, could be a link to one or more +of the architecture, design, or protocol specifications or some more in +depth but still succinct information about where the file and what it +defines or implements fit into the broader design or implementation. + +===== Motivation and Rationale + +The motivation and rationale for this is not from a legal perspective +and as a consequence is not in opposition to guidance from legal. +However, when Project CHIP provides a substantial amount of code as +reference code and as an SDK to third-parties and other work group member +companies, this makes it very clear—and consistently so—what code belongs +to and is authored by Project CHIP and what is not. + +==== Functions and Methods + +Every non-trival or non-default public, and ideally private, free function +and class method should likewise have a prologue comment that: + +* Briefly describes what it is and what it does. +* Describes in detail, optionally, what it is and what it does. +* Describes the purpose, function, and influence of each parameter as +well as whether it is an input, an output, or both. +* Describes the return value or values, if present, and the expected range or +constraints of it. + +The goal of function and method comments are to not simply echo back to the +reader what is in the API signature but to provide value-added insight. Also, +remember that what may be intuitive and self-evident to you as a domain expert +today may not be to your future self or others who are uninitiated in the domain. +The project's success will be measured by the ease, speed, and breadth of its adoption. +Your comments, alongside examples, will contribute toward this goal. When in doubt, +err on the side of being generous with your comments rather than parsimonious. + +An example is shown in Listing 3 below for C, C{plusplus}, Objective C, and +Objective C{plusplus}. Adapt as appropriate for Perl, Python and Shell. + +[source,C] +---- +/** + * Parse and attempt to convert a string to a 64-bit unsigned integer, + * applying the appropriate interpretation based on the base parameter. + * + * @param[in] str A pointer to a NULL-terminated C string representing + * the integer to parse. + * @param[out] output A reference to storage for a 64-bit unsigned integer + * to which the parsed value will be stored on success. + * @param[in] base The base according to which the string should be + * interpreted and parsed. If 0 or 16, the string may + * be hexadecimal and prefixed with "0x". Otherwise, a 0 + * is implied as 10 unless a leading 0 is encountered in + * which 8 is implied. + * + * @retval 0 on success. + * @retval #EINVAL if the given base contains an unsupported value or if no + * conversion was performed. + * @retval #ERANGE if the resulting value was out of range. + */ +---- +[.text-center] +*Listing 3.* Standard Doxygen-compatible free function or method comment +for C, C{plusplus}, Objective C, and Objective C{plusplus}. + +In addition, developers should well document the bodies of their +functions and methods, describing the overall flow, design intent, error +handling nuances, historical bugs encountered and resolved, and so +forth. While these types of comments do not typically become part of the +external documentation, they are invaluable to future maintainers of the +code. + +==== Other + +===== Dos + +* *Do* use the '@' Doxygen markup style rather than the '\' markup style. +* *Do* also consider consulting tips on +http://centerforplainlanguage.org/5-steps-to-plain-language/[Plain +Language] for additional style and tone input. +* *Do* use consistent terminology and lingo. +* *Do* properly paragraph justify and wrap your documentation. + +** See your editor's documentation on how to do this (for example, M-q in Emacs). + +===== Don'ts + +* *Do not* forget to document your files, enumerations, constants, +classes, objects, namespaces, functions, and methods. +* *Do not* include the file name in any Doxygen file comments or +directives. + +** Your editor knows the [.underline]#file name#, source code control knows the file +name, and you know the file name. +** When it changes on the file system, having to change it in the file +comments is simply an added burden. + +* *Do not* include [.underline]#your name# in any Doxygen comments or directives. + +** Source code control knows who you are and what file revisions you own. +** We do not want Project CHIP consumers knowing who you are and calling +or e-mailing you directly for support. + +* *Do not* include the [.underline]#modification date# the file was last changed in +Doxygen comments or directives, [.underline]#except for the copyright header#. + +** Source code control knows when the file was last changed and the date +other revisions were made. + +* *Do not* include subjective or opinionated commentary or expose +proprietary and confidential information not relevant to the code or +APIs. + +** This content *will be* published to and for consumption by members, the +CHIP community, and the general public. + +== Revision History + +[cols="^1,^1,<2,<3",options="header"] +|=== +|Revision |Date |Modified By |Description +|1 |2020-06-11 |Grant Erickson |Initial revision. +|=== + +[.text-center] +_Project Connect Home over IP Public Information_ diff --git a/examples/chip-tool/main.cpp b/examples/chip-tool/main.cpp index f62ddf1c90c6f6..1019d13de54d73 100644 --- a/examples/chip-tool/main.cpp +++ b/examples/chip-tool/main.cpp @@ -33,6 +33,12 @@ extern "C" { using namespace ::chip; using namespace ::chip::Inet; +// NOTE: Remote device ID is in sync with the echo server device id +// At some point, we may want to add an option to connect to a device without +// knowing its id, because the ID can be learned on the first response that is received. +constexpr NodeId kLocalDeviceId = 112233; +constexpr NodeId kRemoteDeviceId = 12344321; + static const char * PAYLOAD = "Message from Standalone CHIP echo client!"; // Device Manager Callbacks @@ -77,7 +83,12 @@ void ShowUsage(const char * executable) { fprintf(stderr, "Usage: \n" - " %s device-ip-address device-port echo|off|on|toggle\n", + " %s device-ip-address device-port command [params]\n" + " Supported commands and their parameters:\n" + " echo\n" + " off endpoint-id\n" + " on endpoint-id\n" + " toggle endpoint-id\n", executable); } @@ -155,22 +166,61 @@ bool DetermineCommand(int argc, char * argv[], Command * command) return false; } +union CommandArgs +{ + ChipZclEndpointId_t endpointId; +}; + +bool DetermineCommandArgs(int argc, char * argv[], Command command, CommandArgs * commandArgs) +{ + if (command == Command::Echo) + { + // No args. + return true; + } + + if (command != Command::On && command != Command::Off && command != Command::Toggle) + { + fprintf(stderr, "Need to define arg handling for command '%d'\n", int(command)); + return false; + } + + if (argc < 5) + { + return false; + } + + std::string endpoint_str(argv[4]); + std::stringstream ss(endpoint_str); + // stringstream treats uint8_t as char, which is not what we want here. + uint16_t endpoint; + ss >> endpoint; + if (ss.fail() || !ss.eof() || endpoint > UINT8_MAX) + { + fprintf(stderr, "Error: Invalid endpoint id '%s'", argv[4]); + return false; + } + commandArgs->endpointId = endpoint; + + return true; +} + // Handle the echo case, where we just send a string and expect to get it back. void DoEcho(DeviceController::ChipDeviceController * controller, const IPAddress & host_addr, uint16_t port) { size_t payload_len = strlen(PAYLOAD); - auto * buffer = System::PacketBuffer::NewWithAvailableSize(payload_len); - snprintf((char *) buffer->Start(), payload_len + 1, "%s", PAYLOAD); - buffer->SetDataLength(payload_len); - // Run the client char host_ip_str[40]; host_addr.ToString(host_ip_str, sizeof(host_ip_str)); while (1) { - // Send calls release on this buffer, so bump up the ref because we want to reuse it - buffer->AddRef(); + // Reallocate buffer on each run, as the secure transport encrypts and + // overwrites the buffer from previous iteration. + auto * buffer = System::PacketBuffer::NewWithAvailableSize(payload_len); + memcpy(buffer->Start(), PAYLOAD, payload_len); + buffer->SetDataLength(payload_len); + controller->SendMessage(NULL, buffer); printf("Msg sent to server at %s:%d\n", host_ip_str, port); @@ -182,7 +232,7 @@ void DoEcho(DeviceController::ChipDeviceController * controller, const IPAddress // Handle the on/off/toggle case, where we are sending a ZCL command and not // expecting a response at all. -void DoOnOff(DeviceController::ChipDeviceController * controller, Command command) +void DoOnOff(DeviceController::ChipDeviceController * controller, Command command, ChipZclEndpointId_t endpoint) { ChipZclCommandId_t zclCommand; switch (command) @@ -205,9 +255,9 @@ void DoOnOff(DeviceController::ChipDeviceController * controller, Command comman static const size_t bufferSize = 1024; auto * buffer = System::PacketBuffer::NewWithAvailableSize(bufferSize); - ChipZclBuffer_t zcl_buffer = { buffer->Start(), bufferSize, 0 }; - ChipZclCommandContext_t ctx = { - 1, // endpointId + ChipZclBuffer_t * zcl_buffer = (ChipZclBuffer_t *) buffer; + ChipZclCommandContext_t ctx = { + endpoint, // endpointId CHIP_ZCL_CLUSTER_ON_OFF, // clusterId true, // clusterSpecific false, // mfgSpecific @@ -218,21 +268,19 @@ void DoOnOff(DeviceController::ChipDeviceController * controller, Command comman nullptr, // request nullptr // response }; - chipZclEncodeZclHeader(&zcl_buffer, &ctx); - - const size_t data_len = chipZclBufferDataLength(&zcl_buffer); + chipZclEncodeZclHeader(zcl_buffer, &ctx); #ifdef DEBUG + const size_t data_len = chipZclBufferDataLength(zcl_buffer); + fprintf(stderr, "SENDING: %zu ", data_len); for (size_t i = 0; i < data_len; ++i) { - fprintf(stderr, "%d ", chipZclBufferPointer(&zcl_buffer)[i]); + fprintf(stderr, "%d ", chipZclBufferPointer(zcl_buffer)[i]); } fprintf(stderr, "\n"); #endif - buffer->SetDataLength(data_len); - controller->SendMessage(NULL, buffer); controller->ServiceEvents(); } @@ -240,22 +288,39 @@ void DoOnOff(DeviceController::ChipDeviceController * controller, Command comman // ================================================================================ // Main Code // ================================================================================ +static const unsigned char local_private_key[] = { 0x00, 0xd1, 0x90, 0xd9, 0xb3, 0x95, 0x1c, 0x5f, 0xa4, 0xe7, 0x47, + 0x92, 0x5b, 0x0a, 0xa9, 0xa7, 0xc1, 0x1c, 0xe7, 0x06, 0x10, 0xe2, + 0xdd, 0x16, 0x41, 0x52, 0x55, 0xb7, 0xb8, 0x80, 0x8d, 0x87, 0xa1 }; + +static const unsigned char remote_public_key[] = { 0x04, 0xe2, 0x07, 0x64, 0xff, 0x6f, 0x6a, 0x91, 0xd9, 0xc2, 0xc3, 0x0a, 0xc4, + 0x3c, 0x56, 0x4b, 0x42, 0x8a, 0xf3, 0xb4, 0x49, 0x29, 0x39, 0x95, 0xa2, 0xf7, + 0x02, 0x8c, 0xa5, 0xce, 0xf3, 0xc9, 0xca, 0x24, 0xc5, 0xd4, 0x5c, 0x60, 0x79, + 0x48, 0x30, 0x3c, 0x53, 0x86, 0xd9, 0x23, 0xe6, 0x61, 0x1f, 0x5a, 0x3d, 0xdf, + 0x9f, 0xdc, 0x35, 0xea, 0xd0, 0xde, 0x16, 0x7e, 0x64, 0xde, 0x7f, 0x3c, 0xa6 }; int main(int argc, char * argv[]) { IPAddress host_addr; uint16_t port; Command command; - if (!DetermineAddress(argc, argv, &host_addr, &port) || !DetermineCommand(argc, argv, &command)) + CHIP_ERROR err; + CommandArgs commandArgs; + if (!DetermineAddress(argc, argv, &host_addr, &port) || !DetermineCommand(argc, argv, &command) || + !DetermineCommandArgs(argc, argv, command, &commandArgs)) { ShowUsage(argv[0]); return -1; } auto * controller = new DeviceController::ChipDeviceController(); - controller->Init(); + err = controller->Init(kLocalDeviceId); + VerifyOrExit(err == CHIP_NO_ERROR, fprintf(stderr, "Failed to initialize the device controller")); + + err = controller->ConnectDevice(kRemoteDeviceId, host_addr, NULL, EchoResponse, ReceiveError, port); + VerifyOrExit(err == CHIP_NO_ERROR, fprintf(stderr, "Failed to connect to the device")); - controller->ConnectDevice(1, host_addr, NULL, EchoResponse, ReceiveError, port); + err = controller->ManualKeyExchange(remote_public_key, sizeof(remote_public_key), local_private_key, sizeof(local_private_key)); + VerifyOrExit(err == CHIP_NO_ERROR, fprintf(stderr, "Failed to exchange keys")); if (command == Command::Echo) { @@ -263,13 +328,18 @@ int main(int argc, char * argv[]) } else { - DoOnOff(controller, command); + DoOnOff(controller, command, commandArgs.endpointId); } +exit: + if (err != CHIP_NO_ERROR) + { + fprintf(stderr, "ERROR: %s\n", ErrorStr(err)); + } controller->Shutdown(); delete controller; - return 0; + return (err == CHIP_NO_ERROR) ? EXIT_SUCCESS : EXIT_FAILURE; } extern "C" { diff --git a/examples/lock-app/efr32/README.md b/examples/lock-app/efr32/README.md index 71b14aaaf3485b..b37edf3c5d1a7c 100644 --- a/examples/lock-app/efr32/README.md +++ b/examples/lock-app/efr32/README.md @@ -59,7 +59,7 @@ following SDKs are installed (as of January 2020). - Install some additional tools(likely already present for CHIP developers): # Linux - $ sudo apt-get install git make automake libtool ccache + $ sudo apt-get install git make automake libtool ccache libwebkitgtk-1.0-0 # Mac OS X $ brew install automake libtool ccache diff --git a/examples/lock-app/nrf5/Makefile b/examples/lock-app/nrf5/Makefile index 11be8387f6b3a6..83e81f31587f2f 100644 --- a/examples/lock-app/nrf5/Makefile +++ b/examples/lock-app/nrf5/Makefile @@ -37,6 +37,8 @@ SRCS = \ $(PROJECT_ROOT)/main/AppTask.cpp \ $(PROJECT_ROOT)/main/LEDWidget.cpp \ $(PROJECT_ROOT)/main/BoltLockManager.cpp \ + $(PROJECT_ROOT)/main/Server.cpp \ + $(PROJECT_ROOT)/main/DataModelHandler.cpp \ $(PROJECT_ROOT)/main/support/CXXExceptionStubs.cpp \ $(PROJECT_ROOT)/main/support/nRF5Sbrk.c \ $(PROJECT_ROOT)/main/support/FreeRTOSNewlibLockSupport.c \ @@ -73,6 +75,9 @@ SRCS = \ $(NRF5_SDK_ROOT)/components/softdevice/common/nrf_sdh.c \ $(NRF5_SDK_ROOT)/components/softdevice/common/nrf_sdh_ble.c \ $(NRF5_SDK_ROOT)/components/softdevice/common/nrf_sdh_soc.c \ + $(NRF5_SDK_ROOT)/examples/multiprotocol/app_utils/multiprotocol_802154_config.c \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/nrf_cc310_plat/src/nrf_cc310_platform_mutex_freertos.c \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/nrf_cc310_plat/src/nrf_cc310_platform_abort_freertos.c \ $(NRF5_SDK_ROOT)/external/fprintf/nrf_fprintf.c \ $(NRF5_SDK_ROOT)/external/fprintf/nrf_fprintf_format.c \ $(NRF5_SDK_ROOT)/external/freertos/portable/CMSIS/nrf52/port_cmsis.c \ @@ -140,54 +145,91 @@ INC_DIRS = \ $(NRF5_SDK_ROOT)/components/softdevice/s140/headers \ $(NRF5_SDK_ROOT)/components/softdevice/s140/headers/nrf52 \ $(NRF5_SDK_ROOT)/components/softdevice/mbr/nrf52840/headers \ - $(NRF5_SDK_ROOT)/components/thread/freertos_mbedtls_mutex \ $(NRF5_SDK_ROOT)/components/toolchain/cmsis/include \ - $(NRF5_SDK_ROOT)/external/openthread/nrf_security/config \ - $(NRF5_SDK_ROOT)/external/openthread/nrf_security/mbedtls_plat_config \ - $(NRF5_SDK_ROOT)/external/openthread/nrf_security/nrf_cc310_plat/include \ - $(NRF5_SDK_ROOT)/external/openthread/project/config \ - $(NRF5_SDK_ROOT)/external/openthread/project/nrf52840 \ $(NRF5_SDK_ROOT)/config/nrf52840/config \ + $(NRF5_SDK_ROOT)/examples/multiprotocol/app_utils \ $(NRF5_SDK_ROOT)/external/fprintf \ $(NRF5_SDK_ROOT)/external/freertos/config \ $(NRF5_SDK_ROOT)/external/freertos/portable/CMSIS/nrf52 \ $(NRF5_SDK_ROOT)/external/freertos/portable/GCC/nrf52 \ $(NRF5_SDK_ROOT)/external/freertos/source/include \ + $(NRF5_SDK_ROOT)/external/nRF-IEEE-802.15.4-radio-driver/src \ + $(NRF5_SDK_ROOT)/external/nRF-IEEE-802.15.4-radio-driver/src/fem \ + $(NRF5_SDK_ROOT)/external/nRF-IEEE-802.15.4-radio-driver/src/fem/three_pin_gpio \ + $(NRF5_SDK_ROOT)/external/nRF-IEEE-802.15.4-radio-driver/src/rsch/raal \ + $(NRF5_SDK_ROOT)/external/nRF-IEEE-802.15.4-radio-driver/src/rsch/raal/softdevice \ $(NRF5_SDK_ROOT)/external/openthread/include \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/config \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/config \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/include/ \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/include/mbedtls \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/mbedtls_plat_config \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/nrf_cc310_plat/include \ + $(NRF5_SDK_ROOT)/external/openthread/project/config \ + $(NRF5_SDK_ROOT)/external/openthread/project/nrf52840 \ $(NRF5_SDK_ROOT)/external/segger_rtt \ $(NRF5_SDK_ROOT)/integration/nrfx \ $(NRF5_SDK_ROOT)/integration/nrfx/legacy \ $(NRF5_SDK_ROOT)/modules/nrfx \ $(NRF5_SDK_ROOT)/modules/nrfx/drivers/include \ $(NRF5_SDK_ROOT)/modules/nrfx/hal \ - $(NRF5_SDK_ROOT)/modules/nrfx/mdk + $(NRF5_SDK_ROOT)/modules/nrfx/mdk \ DEFINES = \ - NRF52840_XXAA \ BOARD_PCA10056 \ BSP_DEFINES_ONLY \ + CHIP_ENABLE_OPENTHREAD=1 \ CONFIG_GPIO_AS_PINRESET \ + ENABLE_FEM \ FLOAT_ABI_HARD \ + FREERTOS \ MBEDTLS_CONFIG_FILE=\"nrf-config.h\" \ MBEDTLS_USER_CONFIG_FILE=\"nrf52840-mbedtls-config.h\" \ + MULTIPROTOCOL_802154_CONFIG_PRESENT \ + NRF52840_XXAA \ + NRFX_PRS_ENABLED=0 \ + NRF_SD_BLE_API_VERSION=7 \ + OPENTHREAD_CONFIG_COAP_API_ENABLE \ + OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS=0 \ OPENTHREAD_CONFIG_FILE=\"openthread-config-wrap.h\" \ + OPENTHREAD_FTD=1 \ + PRINTF_DISABLE_SUPPORT_EXPONENTIAL \ + S140 \ + SOFTDEVICE_PRESENT \ + THREAD_EXAMPLE_COAP_SERVER=1 \ + THREAD_EXAMPLE_FREERTOS=1 \ + UART0_ENABLED=0 \ + UART1_ENABLED=1 \ USE_APP_CONFIG \ __HEAP_SIZE=40960 \ __STACK_SIZE=8192 \ - SOFTDEVICE_PRESENT \ - PRINTF_DISABLE_SUPPORT_EXPONENTIAL \ - S140 LIBS = \ + $(NRF5_SDK_ROOT)/external/openthread/lib/nrf52840/gcc/libopenthread-cli-ftd.a \ + $(NRF5_SDK_ROOT)/external/openthread/lib/nrf52840/gcc/libopenthread-ftd.a \ + $(NRF5_SDK_ROOT)/external/openthread/lib/nrf52840/gcc/libopenthread-platform-utils.a \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedcrypto_glue.a \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedcrypto_glue_cc310.a \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedcrypto_glue_vanilla.a \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedcrypto_cc310_backend.a \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedcrypto_vanilla_backend.a \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedtls_base_vanilla.a \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedtls_tls_vanilla.a \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedtls_x509_vanilla.a \ + $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libnrf_cc310_platform_0.9.1.a \ + $(NRF5_SDK_ROOT)/external/openthread/lib/nrf52840/gcc/libopenthread-nrf52840-softdevice-sdk.a \ $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedcrypto_glue.a \ $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedcrypto_glue_cc310.a \ $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedcrypto_glue_vanilla.a \ + $(NRF5_SDK_ROOT)/external/openthread/lib/nrf52840/gcc/libnordicsemi-nrf52840-radio-driver-softdevice.a \ $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedcrypto_cc310_backend.a \ $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedcrypto_vanilla_backend.a \ + $(NRF5_SDK_ROOT)/external/openthread/lib/nrf52840/gcc/libopenthread-platform-utils.a \ $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedtls_base_vanilla.a \ $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedtls_tls_vanilla.a \ $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libmbedtls_x509_vanilla.a \ $(NRF5_SDK_ROOT)/external/openthread/nrf_security/lib/libnrf_cc310_platform_0.9.1.a \ + $(NRF5_SDK_ROOT)/external/openthread/lib/nrf52840/gcc/libopenthread-ftd.a \ CFLAGS = \ --specs=nano.specs diff --git a/examples/lock-app/nrf5/README.md b/examples/lock-app/nrf5/README.md index fe9081102fcc1e..7229110c4f7b72 100644 --- a/examples/lock-app/nrf5/README.md +++ b/examples/lock-app/nrf5/README.md @@ -137,7 +137,7 @@ Alternatively, you can run `Build nRF5 Lock App` VSCode task. - Set the following environment variables based on the locations/versions of the packages installed above: - export NRF5_SDK_ROOT=${HOME}/tools/nRF5_SDK_for_Thread_and_Zigbee_v3.1.0 + export NRF5_SDK_ROOT=${HOME}/tools/nRF5_SDK_for_Thread_and_Zigbee_v4.0.0 export NRF5_TOOLS_ROOT=${HOME}/tools/nRF-Command-Line-Tools export ARM_GCC_INSTALL_ROOT=${HOME}/tools/gcc-arm-none-eabi-9-2019-q4-major/bin export PATH=${PATH}:${NRF5_TOOLS_ROOT}/nrfjprog diff --git a/examples/lock-app/nrf5/main/AppTask.cpp b/examples/lock-app/nrf5/main/AppTask.cpp index 21f8a145f41385..b85bbb2e3ae959 100644 --- a/examples/lock-app/nrf5/main/AppTask.cpp +++ b/examples/lock-app/nrf5/main/AppTask.cpp @@ -19,7 +19,9 @@ #include "AppTask.h" #include "AppEvent.h" +#include "DataModelHandler.h" #include "LEDWidget.h" +#include "Server.h" #include "app_button.h" #include "app_config.h" @@ -60,6 +62,7 @@ static bool sHaveServiceConnectivity = false; using namespace ::chip::DeviceLayer; AppTask AppTask::sAppTask; +chip::SecureSessionMgr sTransportIPv6; int AppTask::StartAppTask() { @@ -147,6 +150,10 @@ int AppTask::Init() APP_ERROR_HANDLER(NRF_ERROR_NULL); } + // Init ZCL Data Model + InitDataModelHandler(); + StartServer(&sTransportIPv6); + return ret; } diff --git a/examples/lock-app/nrf5/main/DataModelHandler.cpp b/examples/lock-app/nrf5/main/DataModelHandler.cpp new file mode 100644 index 00000000000000..5ff5dc395795c9 --- /dev/null +++ b/examples/lock-app/nrf5/main/DataModelHandler.cpp @@ -0,0 +1,81 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements the handler for data model messages. + */ + +#include + +#include "BoltLockManager.h" +#include "DataModelHandler.h" +#include "nrf_log.h" + +#include "chip-zcl/chip-zcl.h" + +extern "C" { +#include "gen/gen-cluster-id.h" +#include "gen/gen-types.h" +} + +using namespace ::chip; + +void InitDataModelHandler() +{ + chipZclEndpointInit(); +} + +void HandleDataModelMessage(System::PacketBuffer * buffer) +{ + ChipZclStatus_t zclStatus = chipZclProcessIncoming((ChipZclBuffer_t *) buffer); + if (zclStatus == CHIP_ZCL_STATUS_SUCCESS) + { + NRF_LOG_INFO("Data model processing success!"); + } + else + { + NRF_LOG_INFO("Data model processing failure: %d", zclStatus); + } + System::PacketBuffer::Free(buffer); +} + +extern "C" void chipZclPostAttributeChangeCallback(uint8_t endpoint, ChipZclClusterId clusterId, ChipZclAttributeId attributeId, + uint8_t mask, uint16_t manufacturerCode, uint8_t type, uint8_t size, + uint8_t * value) +{ + if (clusterId != CHIP_ZCL_CLUSTER_ON_OFF) + { + NRF_LOG_INFO("Unknown cluster ID: %d", clusterId); + return; + } + + if (attributeId != CHIP_ZCL_CLUSTER_ON_OFF_SERVER_ATTRIBUTE_ON_OFF) + { + NRF_LOG_INFO("Unknown attribute ID: %d", attributeId); + return; + } + + if (*value) + { + BoltLockMgr().InitiateAction(0, BoltLockManager::LOCK_ACTION); + } + else + { + BoltLockMgr().InitiateAction(0, BoltLockManager::UNLOCK_ACTION); + } +} diff --git a/examples/lock-app/nrf5/main/Server.cpp b/examples/lock-app/nrf5/main/Server.cpp new file mode 100644 index 00000000000000..895ab3132c02ed --- /dev/null +++ b/examples/lock-app/nrf5/main/Server.cpp @@ -0,0 +1,137 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "nrf_log.h" +#include "task.h" +#include +#include + +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataModelHandler.h" + +using namespace ::chip; +using namespace ::chip::Inet; +using namespace ::chip::Transport; + +// Transport Callbacks +namespace { + +#ifndef EXAMPLE_SERVER_NODEID +// "lock" +#define EXAMPLE_SERVER_NODEID 0x6c6f636b +#endif // EXAMPLE_SERVER_NODEID + +const uint8_t local_private_key[] = { 0xc6, 0x1a, 0x2f, 0x89, 0x36, 0x67, 0x2b, 0x26, 0x12, 0x47, 0x4f, + 0x11, 0x0e, 0x34, 0x15, 0x81, 0x81, 0x12, 0xfc, 0x36, 0xeb, 0x65, + 0x61, 0x07, 0xaa, 0x63, 0xe8, 0xc5, 0x22, 0xac, 0x52, 0xa1 }; + +const uint8_t remote_public_key[] = { 0x04, 0x30, 0x77, 0x2c, 0xe7, 0xd4, 0x0a, 0xf2, 0xf3, 0x19, 0xbd, 0xfb, 0x1f, + 0xcc, 0x88, 0xd9, 0x83, 0x25, 0x89, 0xf2, 0x09, 0xf3, 0xab, 0xe4, 0x33, 0xb6, + 0x7a, 0xff, 0x73, 0x3b, 0x01, 0x35, 0x34, 0x92, 0x73, 0x14, 0x59, 0x0b, 0xbd, + 0x44, 0x72, 0x1b, 0xcd, 0xb9, 0x02, 0x53, 0xd9, 0xaf, 0xcc, 0x1a, 0xcd, 0xae, + 0xe8, 0x87, 0x2e, 0x52, 0x3b, 0x98, 0xf0, 0xa1, 0x88, 0x4a, 0xe3, 0x03, 0x75 }; + +void newConnectionHandler(const MessageHeader & header, const IPPacketInfo & packet_info, SecureSessionMgr * transport) +{ + CHIP_ERROR err; + + NRF_LOG_INFO("Received a new connection."); + + VerifyOrExit(header.GetSourceNodeId().HasValue(), NRF_LOG_INFO("Unknown source for received message")); + VerifyOrExit(transport->GetPeerNodeId() != header.GetSourceNodeId().Value(), NRF_LOG_INFO("Node already known.")); + + err = transport->Connect(header.GetSourceNodeId().Value(), PeerAddress::UDP(packet_info.SrcAddress, packet_info.SrcPort)); + VerifyOrExit(err == CHIP_NO_ERROR, NRF_LOG_INFO("Failed to connect transport")); + + err = transport->ManualKeyExchange(remote_public_key, sizeof(remote_public_key), local_private_key, sizeof(local_private_key)); + VerifyOrExit(err == CHIP_NO_ERROR, NRF_LOG_INFO("Failed to setup encryption")); + +exit: + return; +} + +static void OnRecv(const MessageHeader & header, const IPPacketInfo & packet_info, System::PacketBuffer * buffer, + SecureSessionMgr * transport) +{ + const size_t data_len = buffer->DataLength(); + char src_addr[INET6_ADDRSTRLEN]; + char dest_addr[INET6_ADDRSTRLEN]; + + // as soon as a client connects, assume it is connected + VerifyOrExit(transport != NULL && buffer != NULL, NRF_LOG_INFO("Received data but couldn't process it...")); + VerifyOrExit(header.GetSourceNodeId().HasValue(), NRF_LOG_INFO("Unknown source for received message")); + + packet_info.SrcAddress.ToString(src_addr, sizeof(src_addr)); + packet_info.DestAddress.ToString(dest_addr, sizeof(dest_addr)); + + NRF_LOG_INFO("UDP packet received from %s:%u to %s:%u (%zu bytes)", src_addr, packet_info.SrcPort, dest_addr, + packet_info.DestPort, static_cast(data_len)); + + HandleDataModelMessage(buffer); + buffer = NULL; + +exit: + // SendTo calls Free on the buffer without an AddRef, if SendTo was not called, free the buffer. + if (buffer != NULL) + { + System::PacketBuffer::Free(buffer); + } +} + +} // namespace + +// The echo server assumes the platform's networking has been setup already +void SetupTransport(IPAddressType type, SecureSessionMgr * transport) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + err = transport->Init(EXAMPLE_SERVER_NODEID, &DeviceLayer::InetLayer, UdpListenParameters().SetAddressType(type)); + SuccessOrExit(err); + + transport->SetMessageReceiveHandler(OnRecv, transport); + transport->SetNewConnectionHandler(newConnectionHandler, transport); + +exit: + if (err != CHIP_NO_ERROR) + { + NRF_LOG_ERROR("ERROR setting up transport: %s", ErrorStr(err)); + } + else + { + NRF_LOG_INFO("Lock Server Listening..."); + } +} + +// The echo server assumes the platform's networking has been setup already +void StartServer(SecureSessionMgr * transport_ipv6) +{ + SetupTransport(kIPAddressType_IPv6, transport_ipv6); +} diff --git a/examples/lock-app/nrf5/main/include/DataModelHandler.h b/examples/lock-app/nrf5/main/include/DataModelHandler.h new file mode 100644 index 00000000000000..06c78bd8e58db7 --- /dev/null +++ b/examples/lock-app/nrf5/main/include/DataModelHandler.h @@ -0,0 +1,47 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file defines the API for the handler for data model messages. + */ + +#ifndef DATA_MODEL_HANDLER_H +#define DATA_MODEL_HANDLER_H + +namespace chip { +namespace System { +class PacketBuffer; +} // namespace System +} // namespace chip + +/** + * Initialize the data model handler. This must be called once, and before any + * HandleDataModelMessage calls happen. + */ +void InitDataModelHandler(); + +/** + * Handle a message that should be processed via our data model processing + * codepath. + * + * @param [in] buffer The buffer holding the message. This function guarantees + * that it will free the buffer before returning. + */ +void HandleDataModelMessage(chip::System::PacketBuffer * buffer); + +#endif // DATA_MODEL_HANDLER_H diff --git a/examples/lock-app/nrf5/main/include/Server.h b/examples/lock-app/nrf5/main/include/Server.h new file mode 100644 index 00000000000000..dfca2bbfa7aedd --- /dev/null +++ b/examples/lock-app/nrf5/main/include/Server.h @@ -0,0 +1,31 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 SERVER_H +#define SERVER_H + +#include +#include +#include +#include + +#include "DataModelHandler.h" + +void SetupTransport(chip::Inet::IPAddressType type, chip::SecureSessionMgr * transport); +void StartServer(chip::SecureSessionMgr * transport_ipv6); + +#endif // SERVER_H diff --git a/examples/lock-app/nrf5/main/main.cpp b/examples/lock-app/nrf5/main/main.cpp index 03057a6567bd6c..e9143574b9c33d 100644 --- a/examples/lock-app/nrf5/main/main.cpp +++ b/examples/lock-app/nrf5/main/main.cpp @@ -33,9 +33,15 @@ #include "nrf_crypto.h" #endif #include "mem_manager.h" +#if CHIP_ENABLE_OPENTHREAD extern "C" { -#include "freertos_mbedtls_mutex.h" +#include "multiprotocol_802154_config.h" +#include "nrf_802154.h" +#include "nrf_cc310_platform_abort.h" +#include "nrf_cc310_platform_mutex.h" +#include } +#endif // CHIP_ENABLE_OPENTHREAD #if NRF_LOG_ENABLED #include "nrf_log_backend_uart.h" @@ -44,7 +50,21 @@ extern "C" { #endif // NRF_LOG_ENABLED #include +#if CHIP_ENABLE_OPENTHREAD +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#endif // CHIP_ENABLE_OPENTHREAD using namespace ::chip; using namespace ::chip::Inet; @@ -85,6 +105,9 @@ uint32_t LogTimestamp(void) static void OnSoCEvent(uint32_t sys_evt, void * p_context) { +#if CHIP_ENABLE_OPENTHREAD + otSysSoftdeviceSocEvtHandler(sys_evt); +#endif UNUSED_PARAMETER(p_context); } @@ -172,24 +195,6 @@ int main(void) #endif // defined(SOFTDEVICE_PRESENT) && SOFTDEVICE_PRESENT - ret = nrf_mem_init(); - if (ret != NRF_SUCCESS) - { - NRF_LOG_INFO("nrf_mem_init() failed"); - APP_ERROR_HANDLER(ret); - } - NRF_LOG_INFO("Mem init complete"); - -#if NRF_CRYPTO_ENABLED - ret = nrf_crypto_init(); - if (ret != NRF_SUCCESS) - { - NRF_LOG_INFO("nrf_crypto_init() failed"); - APP_ERROR_HANDLER(ret); - } - NRF_LOG_INFO("Crypto init complete"); -#endif - #if defined(SOFTDEVICE_PRESENT) && SOFTDEVICE_PRESENT { @@ -216,6 +221,44 @@ int main(void) APP_ERROR_HANDLER(ret); } +#if CHIP_ENABLE_OPENTHREAD + NRF_LOG_INFO("Initializing OpenThread stack"); + + mbedtls_platform_set_calloc_free(calloc, free); + nrf_cc310_platform_abort_init(); + nrf_cc310_platform_mutex_init(); + mbedtls_platform_setup(NULL); + otHeapSetCAllocFree(calloc, free); + + otSysInit(0, NULL); + + // Configure multiprotocol to work with BLE. + { + uint32_t retval = multiprotocol_802154_mode_set(MULTIPROTOCOL_802154_MODE_FAST_SWITCHING_TIMES); + + if (retval != NRF_SUCCESS) + { + NRF_LOG_INFO("multiprotocol 15.4 failed"); + APP_ERROR_HANDLER(CHIP_ERROR_INTERNAL); + } + } + + ret = ThreadStackMgr().InitThreadStack(); + if (ret != CHIP_NO_ERROR) + { + NRF_LOG_INFO("ThreadStackMgr().InitThreadStack() failed"); + APP_ERROR_HANDLER(ret); + } + + // Configure device to operate as a Thread sleepy end-device. + ret = ConnectivityMgr().SetThreadDeviceType(ConnectivityManager::kThreadDeviceType_Router); + if (ret != CHIP_NO_ERROR) + { + NRF_LOG_INFO("ConnectivityMgr().SetThreadDeviceType() failed"); + APP_ERROR_HANDLER(ret); + } +#endif // CHIP_ENABLE_OPENTHREAD + NRF_LOG_INFO("Starting CHIP task"); ret = PlatformMgr().StartEventLoopTask(); if (ret != CHIP_NO_ERROR) @@ -224,6 +267,18 @@ int main(void) APP_ERROR_HANDLER(ret); } +#if CHIP_ENABLE_OPENTHREAD + NRF_LOG_INFO("Starting OpenThread task"); + + // Start OpenThread task + ret = ThreadStackMgrImpl().StartThreadTask(); + if (ret != CHIP_NO_ERROR) + { + NRF_LOG_INFO("ThreadStackMgr().StartThreadTask() failed"); + APP_ERROR_HANDLER(ret); + } +#endif // CHIP_ENABLE_OPENTHREAD + ret = GetAppTask().StartAppTask(); if (ret != NRF_SUCCESS) { @@ -233,13 +288,13 @@ int main(void) // Activate deep sleep mode SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; - /* - { - struct mallinfo minfo = mallinfo(); - NRF_LOG_INFO("System Heap Utilization: heap size %" PRId32 ", arena size %" PRId32 ", in use %" PRId32 ", free %" - PRId32, GetHeapTotalSize(), minfo.arena, minfo.uordblks, minfo.fordblks); - } - */ + + { + struct mallinfo minfo = mallinfo(); + NRF_LOG_INFO("System Heap Utilization: heap size %" PRId32 ", arena size %" PRId32 ", in use %" PRId32 ", free %" PRId32, + GetHeapTotalSize(), minfo.arena, minfo.uordblks, minfo.fordblks); + } + NRF_LOG_INFO("Starting FreeRTOS scheduler"); /* Start FreeRTOS scheduler. */ diff --git a/examples/shell/cmd_base64.cpp b/examples/shell/cmd_base64.cpp new file mode 100644 index 00000000000000..17b54a82bc07ed --- /dev/null +++ b/examples/shell/cmd_base64.cpp @@ -0,0 +1,112 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 + +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::Shell; +using namespace chip::Logging; +using namespace chip::ArgParser; + +chip::Shell::Shell theShellBase64; + +int cmd_base64_help_iterator(shell_command_t * command, void * arg) +{ + streamer_printf(streamer_get(), " %-15s %s\n\r", command->cmd_name, command->cmd_help); + return 0; +} + +int cmd_base64_help(int argc, char ** argv) +{ + theShellBase64.ForEachCommand(cmd_base64_help_iterator, NULL); + return 0; +} + +int cmd_base64_decode(int argc, char ** argv) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + streamer_t * sout = streamer_get(); + uint32_t binarySize; + uint8_t binary[256]; + + VerifyOrExit(argc > 0, error = CHIP_ERROR_INVALID_ARGUMENT); + binarySize = Base64Decode(argv[0], sizeof(argv[0]), binary); + streamer_print_hex(sout, binary, binarySize); + streamer_printf(sout, "\n\r"); + +exit: + return error; +} + +int cmd_base64_encode(int argc, char ** argv) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + streamer_t * sout = streamer_get(); + char base64[256]; + uint8_t binary[256]; + uint32_t binarySize, base64Size; + + VerifyOrExit(argc > 0, error = CHIP_ERROR_INVALID_ARGUMENT); + ParseHexString(argv[0], strlen(argv[0]), binary, sizeof(binary), binarySize); + base64Size = Base64Encode(binary, binarySize, base64); + streamer_printf(sout, "%.*s\n\r", base64Size, base64); + +exit: + return error; +} + +int cmd_base64_dispatch(int argc, char ** argv) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + + VerifyOrExit(argc > 0, error = CHIP_ERROR_INVALID_ARGUMENT); + + error = theShellBase64.ExecCommand(argc, argv); + +exit: + return error; +} + +static const shell_command_t cmds_base64_root = { &cmd_base64_dispatch, "base64", "Base64 encode / decode utilities" }; + +/// Subcommands for root command: `base64 ` +static const shell_command_t cmds_base64[] = { + { &cmd_base64_help, "help", "Usage: base64 " }, + { &cmd_base64_encode, "encode", "Encode a hex sting as base64. Usage: base64 encode " }, + { &cmd_base64_decode, "decode", "Decode a base64 sting as hex. Usage: base64 decode " }, +}; + +void cmd_base64_init(void) +{ + // Register `base64` subcommands with the local shell dispatcher. + theShellBase64.RegisterCommands(cmds_base64, ARRAY_SIZE(cmds_base64)); + + // Register the root `base64` command with the top-level shell. + shell_register(&cmds_base64_root, 1); +} diff --git a/examples/shell/cmd_misc.cpp b/examples/shell/cmd_misc.cpp new file mode 100644 index 00000000000000..dd3540a8e3c7ff --- /dev/null +++ b/examples/shell/cmd_misc.cpp @@ -0,0 +1,70 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 + +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::Shell; +using namespace chip::Logging; + +int cmd_echo(int argc, char ** argv) +{ + for (int i = 0; i < argc; i++) + { + streamer_printf(streamer_get(), "%s ", argv[i]); + } + streamer_printf(streamer_get(), "\n\r"); + return 0; +} + +int cmd_log(int argc, char ** argv) +{ + for (int i = 0; i < argc; i++) + { + ChipLogProgress(chipTool, "%s", argv[i]); + } + return 0; +} + +int cmd_rand(int argc, char ** argv) +{ + streamer_printf(streamer_get(), "%d\n\r", GetRandU8()); + return 0; +} + +static shell_command_t cmds_misc[] = { + { &cmd_echo, "echo", "Echo back provided inputs" }, + { &cmd_log, "log", "Logging utilities" }, + { &cmd_rand, "rand", "Random number utilities" }, +}; + +void cmd_misc_init(void) +{ + shell_register(cmds_misc, ARRAY_SIZE(cmds_misc)); +} diff --git a/examples/shell/main.cpp b/examples/shell/main.cpp new file mode 100644 index 00000000000000..1ff210c208b313 --- /dev/null +++ b/examples/shell/main.cpp @@ -0,0 +1,44 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 + +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::Shell; + +void cmd_misc_init(); +void cmd_base64_init(); + +int main(void) +{ + cmd_misc_init(); + cmd_base64_init(); + + shell_task(NULL); +} diff --git a/examples/shell/standalone/Makefile b/examples/shell/standalone/Makefile new file mode 100644 index 00000000000000..f55ae6057d5b50 --- /dev/null +++ b/examples/shell/standalone/Makefile @@ -0,0 +1,66 @@ +# +# +# Copyright (c) 2020 Project CHIP Authors +# Copyright (c) 2019 Google LLC. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 +# Makefile for building the CHIP Standalone Example Application. +# + +PROJECT_ROOT := $(realpath ..) + +CHIP_ROOT = $(realpath $(PROJECT_ROOT)/third_party/connectedhomeip) +BUILD_SUPPORT_DIR = $(CHIP_ROOT)/config/standalone + +include $(BUILD_SUPPORT_DIR)/standalone-app.mk +include $(BUILD_SUPPORT_DIR)/standalone-chip.mk + +APP = chip-shell + +LIBS = -lChipShell + +SRCS = \ + $(PROJECT_ROOT)/main.cpp \ + $(PROJECT_ROOT)/cmd_misc.cpp \ + $(PROJECT_ROOT)/cmd_base64.cpp \ + $(NULL) + +INC_DIRS = \ + $(PROJECT_ROOT) \ + $(PROJECT_ROOT)/include \ + $(CHIP_ROOT)/src/ \ + $(CHIP_ROOT)/src/lib \ + $(CHIP_ROOT)/src/system \ + $(CHIP_ROOT)/config/standalone \ + $(NULL) + +DEFINES = \ + USE_APP_CONFIG \ + $(NULL) + +CXXFLAGS += -std=c++11 + +ifdef BUILD_RELEASE + DEFINES += BUILD_RELEASE=1 +else + DEFINES += BUILD_RELEASE=0 +endif + +CHIP_PROJECT_CONFIG = $(PROJECT_ROOT)/include/CHIPProjectConfig.h + +$(call GenerateBuildRules) diff --git a/examples/shell/third_party/connectedhomeip b/examples/shell/third_party/connectedhomeip new file mode 120000 index 00000000000000..a8a4f8c2127bf7 --- /dev/null +++ b/examples/shell/third_party/connectedhomeip @@ -0,0 +1 @@ +../../.. \ No newline at end of file diff --git a/examples/wifi-echo/server/esp32/Makefile b/examples/wifi-echo/server/esp32/Makefile index 85f3ab3ea834ab..8dc5ee6bd8a7bd 100644 --- a/examples/wifi-echo/server/esp32/Makefile +++ b/examples/wifi-echo/server/esp32/Makefile @@ -25,4 +25,8 @@ EXTRA_COMPONENT_DIRS += $(PROJECT_PATH)/third_party/connectedhomeip/config/esp32 $(PROJECT_PATH)/../../../common/m5stack-tft/repo/components \ $(PROJECT_PATH)/../../../common/QRCode \ +CXXFLAGS += -std=c++11 -Os +CPPFLAGS += -Os +CLAGS += -Os + include $(IDF_PATH)/make/project.mk diff --git a/examples/wifi-echo/server/esp32/idf.sh b/examples/wifi-echo/server/esp32/idf.sh index b72c2665230a62..c78a9c1213f57a 100755 --- a/examples/wifi-echo/server/esp32/idf.sh +++ b/examples/wifi-echo/server/esp32/idf.sh @@ -20,16 +20,25 @@ # a command presented as arguments # # This file can also be used as an executable -me=${0##*/} -die() { + +error() { echo "$me: *** ERROR: " "${*}" - exit 1 } idf() { - [[ -d $IDF_PATH && -r $IDF_PATH/export.sh ]] || die "can't find IDF's export.sh" - . "$IDF_PATH/export.sh" - "$@" + [[ -d $IDF_PATH && -r $IDF_PATH/export.sh ]] || { + error "can't find IDF's export.sh, please set IDF_PATH" + return 1 + } + ( + . "$IDF_PATH/export.sh" + export IDF_PATH + "$@" + ) } if [[ ${0} == ${BASH_SOURCE[0]} ]]; then + me=${0##*/} idf "${@}" +else + me=idf + [[ $PS1 =~ \[idf\].* ]] || PS1="[idf]$PS1" fi diff --git a/examples/wifi-echo/server/esp32/main/DataModelHandler.cpp b/examples/wifi-echo/server/esp32/main/DataModelHandler.cpp index d601908505d2d9..94540fe8302222 100644 --- a/examples/wifi-echo/server/esp32/main/DataModelHandler.cpp +++ b/examples/wifi-echo/server/esp32/main/DataModelHandler.cpp @@ -45,7 +45,7 @@ void InitDataModelHandler() void HandleDataModelMessage(System::PacketBuffer * buffer) { - ChipZclStatus_t zclStatus = chipZclProcessIncoming(buffer->Start(), buffer->DataLength()); + ChipZclStatus_t zclStatus = chipZclProcessIncoming((ChipZclBuffer_t *) buffer); if (zclStatus == CHIP_ZCL_STATUS_SUCCESS) { ESP_LOGI(TAG, "Data model processing success!"); diff --git a/examples/wifi-echo/server/esp32/main/Display.cpp b/examples/wifi-echo/server/esp32/main/Display.cpp index a27400af26c0a4..9359217051cbf0 100644 --- a/examples/wifi-echo/server/esp32/main/Display.cpp +++ b/examples/wifi-echo/server/esp32/main/Display.cpp @@ -37,7 +37,8 @@ #if CONFIG_HAVE_DISPLAY -#define DEFFAULT_BRIGHTNESS_PERCENT 80 +// Brightness picked such that it's easy for cameras to focus on +#define DEFFAULT_BRIGHTNESS_PERCENT 10 // 8MHz is the recommended SPI speed to init the driver with // It later gets set to the preconfigured defaults within the driver @@ -58,8 +59,10 @@ extern const char * TAG; uint16_t DisplayHeight = 0; uint16_t DisplayWidth = 0; +#if CONFIG_DISPLAY_AUTO_OFF // FreeRTOS timer used to turn the display off after a short while TimerHandle_t displayTimer = NULL; +#endif static void TimerCallback(TimerHandle_t xTimer); static void SetupBrightnessControl(); @@ -130,7 +133,9 @@ esp_err_t InitDisplay() // prepare the display for brightness control SetupBrightnessControl(); +#if CONFIG_DISPLAY_AUTO_OFF displayTimer = xTimerCreate("DisplayTimer", pdMS_TO_TICKS(DISPLAY_TIMEOUT_MS), false, NULL, TimerCallback); +#endif // lower the brightness of the screen WakeDisplay(); @@ -150,13 +155,32 @@ void SetBrightness(uint16_t brightness_percent) void WakeDisplay() { SetBrightness(DEFFAULT_BRIGHTNESS_PERCENT); +#if CONFIG_DISPLAY_AUTO_OFF xTimerStart(displayTimer, 0); ESP_LOGI(TAG, "Display awake but will switch off automatically in %d seconds", DISPLAY_TIMEOUT_MS / 1000); +#endif } void ClearDisplay() { - TFT_fillRect(0, 0, (int) DisplayWidth, (int) DisplayHeight, TFT_BLACK); + ClearRect(); +} + +void ClearRect(uint16_t x_percent_start, uint16_t y_percent_start, uint16_t x_percent_end, uint16_t y_percent_end) +{ + if (x_percent_end < x_percent_start) + { + x_percent_end = x_percent_start; + } + if (y_percent_end < y_percent_start) + { + y_percent_end = y_percent_start; + } + uint16_t start_x = (DisplayWidth * x_percent_start) / 100; + uint16_t start_y = (DisplayHeight * y_percent_start) / 100; + uint16_t end_x = (DisplayWidth * x_percent_end) / 100; + uint16_t end_y = (DisplayHeight * y_percent_end) / 100; + TFT_fillRect(start_x, start_y, end_x, end_y, TFT_BLACK); } void DisplayStatusMessage(char * msg, uint16_t vpos) diff --git a/examples/wifi-echo/server/esp32/main/EchoClient.cpp b/examples/wifi-echo/server/esp32/main/EchoClient.cpp index a5fa4c5b3b3dbe..c96fe06781b9d9 100644 --- a/examples/wifi-echo/server/esp32/main/EchoClient.cpp +++ b/examples/wifi-echo/server/esp32/main/EchoClient.cpp @@ -29,9 +29,10 @@ #include "lwip/err.h" #include "lwip/sockets.h" #include "lwip/sys.h" +#include #include -#define PORT CONFIG_ECHO_PORT +#define PORT CHIP_PORT #define RX_LEN 128 #define ADDR_LEN 128 diff --git a/examples/wifi-echo/server/esp32/main/EchoServer.cpp b/examples/wifi-echo/server/esp32/main/EchoServer.cpp index ee931bac838123..889bca85f81299 100644 --- a/examples/wifi-echo/server/esp32/main/EchoServer.cpp +++ b/examples/wifi-echo/server/esp32/main/EchoServer.cpp @@ -23,9 +23,12 @@ #include "freertos/task.h" #include "nvs_flash.h" #include "tcpip_adapter.h" + #include #include +#include + #include "lwip/err.h" #include "lwip/sockets.h" #include "lwip/sys.h" @@ -34,58 +37,123 @@ #include #include #include -#include #include +#include #include #include +#include +#include #include "DataModelHandler.h" - -#define PORT CONFIG_ECHO_PORT +#include "LEDWidget.h" static const char * TAG = "echo_server"; using namespace ::chip; using namespace ::chip::Inet; +using namespace ::chip::Transport; + +constexpr NodeId kLocalNodeId = 12344321; +extern LEDWidget statusLED; // In wifi-echo.cpp + +namespace { + +const unsigned char local_private_key[] = { 0xc6, 0x1a, 0x2f, 0x89, 0x36, 0x67, 0x2b, 0x26, 0x12, 0x47, 0x4f, + 0x11, 0x0e, 0x34, 0x15, 0x81, 0x81, 0x12, 0xfc, 0x36, 0xeb, 0x65, + 0x61, 0x07, 0xaa, 0x63, 0xe8, 0xc5, 0x22, 0xac, 0x52, 0xa1 }; + +const unsigned char remote_public_key[] = { 0x04, 0x30, 0x77, 0x2c, 0xe7, 0xd4, 0x0a, 0xf2, 0xf3, 0x19, 0xbd, 0xfb, 0x1f, + 0xcc, 0x88, 0xd9, 0x83, 0x25, 0x89, 0xf2, 0x09, 0xf3, 0xab, 0xe4, 0x33, 0xb6, + 0x7a, 0xff, 0x73, 0x3b, 0x01, 0x35, 0x34, 0x92, 0x73, 0x14, 0x59, 0x0b, 0xbd, + 0x44, 0x72, 0x1b, 0xcd, 0xb9, 0x02, 0x53, 0xd9, 0xaf, 0xcc, 0x1a, 0xcd, 0xae, + 0xe8, 0x87, 0x2e, 0x52, 0x3b, 0x98, 0xf0, 0xa1, 0x88, 0x4a, 0xe3, 0x03, 0x75 }; + +/** + * A data model message has nonzero length and always has a first byte whose + * value is one of: 0x00, 0x01, 0x02, 0x03. See chipZclEncodeZclHeader for the + * construction of the message and in particular the first byte. + * + * Echo messages should generally not have a first byte with those values, so we + * can use that to try to distinguish between the two. + */ +static bool ContentMayBeADataModelMessage(System::PacketBuffer * buffer) +{ + const size_t data_len = buffer->DataLength(); + const uint8_t * data = buffer->Start(); + bool maybeDataModelMessage = true; + + // Has to have nonzero length. + VerifyOrExit(data_len > 0, maybeDataModelMessage = false); + + // Has to have a valid first byte value. + VerifyOrExit(data[0] < 0x04, maybeDataModelMessage = false); + +exit: + return maybeDataModelMessage; +} + +void newConnectionHandler(const MessageHeader & header, const IPPacketInfo & packet_info, SecureSessionMgr * transport) +{ + CHIP_ERROR err; + + ESP_LOGI(TAG, "Received a new connection."); + + VerifyOrExit(header.GetSourceNodeId().HasValue(), ESP_LOGE(TAG, "Unknown source for received message")); + VerifyOrExit(transport->GetPeerNodeId() != header.GetSourceNodeId().Value(), ESP_LOGI(TAG, "Node already known.")); + + err = transport->Connect(header.GetSourceNodeId().Value(), PeerAddress::UDP(packet_info.SrcAddress, packet_info.SrcPort)); + VerifyOrExit(err == CHIP_NO_ERROR, ESP_LOGE(TAG, "Failed to connect transport")); + + err = transport->ManualKeyExchange(remote_public_key, sizeof(remote_public_key), local_private_key, sizeof(local_private_key)); + VerifyOrExit(err == CHIP_NO_ERROR, ESP_LOGE(TAG, "Failed to setup encryption")); + +exit: + return; +} -// UDP Endpoint Callbacks -static void echo(IPEndPointBasis * endpoint, System::PacketBuffer * buffer, const IPPacketInfo * packet_info) +// Transport Callbacks +void echo(const MessageHeader & header, const IPPacketInfo & packet_info, System::PacketBuffer * buffer, + SecureSessionMgr * transport) { - bool status = endpoint != NULL && buffer != NULL && packet_info != NULL; + CHIP_ERROR err; + const size_t data_len = buffer->DataLength(); + + // as soon as a client connects, assume it is connected + VerifyOrExit(transport != NULL && buffer != NULL, ESP_LOGE(TAG, "Received data but couldn't process it...")); + + VerifyOrExit(header.GetSourceNodeId().HasValue(), ESP_LOGE(TAG, "Unknown source for received message")); - if (status) { char src_addr[INET_ADDRSTRLEN]; char dest_addr[INET_ADDRSTRLEN]; - const size_t data_len = buffer->DataLength(); + packet_info.SrcAddress.ToString(src_addr, sizeof(src_addr)); + packet_info.DestAddress.ToString(dest_addr, sizeof(dest_addr)); - packet_info->SrcAddress.ToString(src_addr, sizeof(src_addr)); - packet_info->DestAddress.ToString(dest_addr, sizeof(dest_addr)); - - ESP_LOGI(TAG, "UDP packet received from %s:%u to %s:%u (%zu bytes)", src_addr, packet_info->SrcPort, dest_addr, - packet_info->DestPort, static_cast(data_len)); + ESP_LOGI(TAG, "UDP packet received from %s:%u to %s:%u (%zu bytes)", src_addr, packet_info.SrcPort, dest_addr, + packet_info.DestPort, static_cast(data_len)); + } - if (data_len > 0 && buffer->Start()[0] < 0x20) - { - // Non-ACII; assume it's a data model message. - HandleDataModelMessage(buffer); - return; - } + // FIXME: Long-term we shouldn't be guessing what sort of message this is + // based on the message bytes. We're doing this for now to support both + // data model messages and text echo messages, but in the long term we + // should either do echo via a data model command or do echo on a separate + // port from data model processing. + if (ContentMayBeADataModelMessage(buffer)) + { + HandleDataModelMessage(buffer); + buffer = NULL; + } + else + { - // attempt to print the incoming message - char msg_buffer[data_len + 1]; - msg_buffer[data_len] = 0; // Null-terminate whatever we received and treat like a string... - memcpy(msg_buffer, buffer->Start(), data_len); - ESP_LOGI(TAG, "Client sent: \"%s\"", msg_buffer); + ESP_LOGI(TAG, "Client sent: \"%.*s\"", data_len, buffer->Start()); // Attempt to echo back - UDPEndPoint * udp_endpoint = static_cast(endpoint); - INET_ERROR err = udp_endpoint->SendTo(packet_info->SrcAddress, packet_info->SrcPort, buffer); - if (err != INET_NO_ERROR) + err = transport->SendMessage(header.GetSourceNodeId().Value(), buffer); + buffer = NULL; + if (err != CHIP_NO_ERROR) { ESP_LOGE(TAG, "Unable to echo back to client: %s", ErrorStr(err)); - // Note the failure status - status = !status; } else { @@ -93,49 +161,55 @@ static void echo(IPEndPointBasis * endpoint, System::PacketBuffer * buffer, cons } } - if (!status) - { - ESP_LOGE(TAG, "Received data but couldn't process it..."); +exit: - // SendTo calls Free on the buffer without an AddRef, if SendTo was not called, free the buffer. - if (buffer != NULL) - { - System::PacketBuffer::Free(buffer); - } + // SendTo calls Free on the buffer without an AddRef, if SendTo was not called, free the buffer. + if (buffer != NULL) + { + System::PacketBuffer::Free(buffer); } } -static void error(IPEndPointBasis * ep, INET_ERROR error, const IPPacketInfo * pi) +void error(CHIP_ERROR error, const IPPacketInfo & pi) { ESP_LOGE(TAG, "ERROR: %s\n Got UDP error", ErrorStr(error)); + statusLED.BlinkOnError(); } +} // namespace + // The echo server assumes the platform's networking has been setup already -void startServer(UDPEndPoint *& endpoint) +void setupTransport(IPAddressType type, SecureSessionMgr * transport) { - ESP_LOGI(TAG, "Trying to get Inet"); - INET_ERROR err = DeviceLayer::InetLayer.NewUDPEndPoint(&endpoint); - if (err != INET_NO_ERROR) + CHIP_ERROR err = CHIP_NO_ERROR; + struct netif * netif = NULL; + + if (type == kIPAddressType_IPv6) { - ESP_LOGE(TAG, "ERROR: %s\n Couldn't create UDP Endpoint, server will not start.", ErrorStr(err)); - return; + tcpip_adapter_get_netif(TCPIP_ADAPTER_IF_AP, (void **) &netif); } - endpoint->OnMessageReceived = echo; - endpoint->OnReceiveError = error; + err = transport->Init(kLocalNodeId, &DeviceLayer::InetLayer, UdpListenParameters().SetAddressType(type).SetInterfaceId(netif)); + SuccessOrExit(err); + + transport->SetMessageReceiveHandler(echo, transport); + transport->SetReceiveErrorHandler(error); + transport->SetNewConnectionHandler(newConnectionHandler, transport); - err = endpoint->Bind(kIPAddressType_IPv4, IPAddress::Any, PORT); - if (err != INET_NO_ERROR) +exit: + if (err != CHIP_NO_ERROR) { - ESP_LOGE(TAG, "Socket unable to bind: Error %s", ErrorStr(err)); - return; + ESP_LOGE(TAG, "ERROR setting up transport: %s", ErrorStr(err)); } - - err = endpoint->Listen(); - if (err != INET_NO_ERROR) + else { - ESP_LOGE(TAG, "Socket unable to Listen: Error %s", ErrorStr(err)); - return; + ESP_LOGI(TAG, "Echo Server Listening..."); } - ESP_LOGI(TAG, "Echo Server Listening on PORT:%d...", PORT); +} + +// The echo server assumes the platform's networking has been setup already +void startServer(SecureSessionMgr * transport_ipv4, SecureSessionMgr * transport_ipv6) +{ + setupTransport(kIPAddressType_IPv6, transport_ipv6); + setupTransport(kIPAddressType_IPv4, transport_ipv4); } diff --git a/examples/wifi-echo/server/esp32/main/Kconfig.projbuild b/examples/wifi-echo/server/esp32/main/Kconfig.projbuild index b424b69caadc77..4061b33f9deb51 100644 --- a/examples/wifi-echo/server/esp32/main/Kconfig.projbuild +++ b/examples/wifi-echo/server/esp32/main/Kconfig.projbuild @@ -35,13 +35,6 @@ menu "WiFi Echo Demo" bool "M5Stack" endchoice - config ECHO_PORT - int "Port" - range 0 65535 - default 8000 - help - Local port the example server will listen on. - config USE_ECHO_CLIENT bool "Enable the built-in Echo Client" default "n" @@ -50,11 +43,11 @@ menu "WiFi Echo Demo" tested easily config ECHO_HOST_IP - string "IPV4 address" - default "127.0.0.1" - depends on USE_ECHO_CLIENT - help - The IPV4 Address of the ECHO Server. + string "IPV4 address" + default "127.0.0.1" + depends on USE_ECHO_CLIENT + help + The IPV4 Address of the ECHO Server. # NOTE: This config is not displayed as a input in the Kconfig menu, as its value is # entirely derived from the Device Type choice. However the CONFIG_EXAMPLE_DISPLAY_TYPE @@ -65,5 +58,12 @@ menu "WiFi Echo Demo" default 3 if DEVICE_TYPE_M5STACK default 0 if DEVICE_TYPE_ESP32_DEVKITC + config DISPLAY_AUTO_OFF + bool "Automatically turn off the M5Stack's Display after a few seconds" + default "y" + depends on DEVICE_TYPE_M5STACK + help + To reduce wear and heat the M5Stack's Display is automatically switched off after a few seconds + endmenu \ No newline at end of file diff --git a/examples/wifi-echo/server/esp32/main/LEDWidget.cpp b/examples/wifi-echo/server/esp32/main/LEDWidget.cpp index d7e8a488f04c03..dffa392917c893 100644 --- a/examples/wifi-echo/server/esp32/main/LEDWidget.cpp +++ b/examples/wifi-echo/server/esp32/main/LEDWidget.cpp @@ -16,13 +16,36 @@ * limitations under the License. */ +/** + * @file LEDWidget.cpp + * + * Implements an LED Widget controller that is usually tied to a GPIO + * It also updates the display widget if it's enabled + */ + #include "LEDWidget.h" +#include "Display.h" #include "driver/gpio.h" #include "esp_log.h" #include "esp_system.h" #include "esp_timer.h" +#if CONFIG_HAVE_DISPLAY +// The Y position of the LED Status message on screen as a +// percentage of the screen's height. +#define LED_STATUS_POSITION 85 +// Position the LED Indicator at the bottom right corner +#define LED_INDICATOR_X 92 +#define LED_INDICATOR_Y 88 +// The radius of the LED Indicator +#define LED_INDICATOR_R_PX 16 + +static const char * onMsg = "LIGHT: ON"; +static const char * offMsg = "LIGHT: OFF"; + +#endif + extern const char * TAG; void LEDWidget::Init(gpio_num_t gpioNum) @@ -32,6 +55,8 @@ void LEDWidget::Init(gpio_num_t gpioNum) mBlinkOffTimeMS = 0; mGPIONum = gpioNum; mState = false; + mError = false; + errorTimer = NULL; if (gpioNum < GPIO_NUM_MAX) { @@ -57,6 +82,33 @@ void LEDWidget::Blink(uint32_t onTimeMS, uint32_t offTimeMS) Animate(); } +void ClearErrorState(TimerHandle_t handle) +{ +#if CONFIG_HAVE_DISPLAY + LEDWidget * pWidget = (LEDWidget *) pvTimerGetTimerID(handle); + pWidget->mError = false; + pWidget->Display(); + // If a status change occured, wake the display + WakeDisplay(); +#endif +} + +void LEDWidget::BlinkOnError() +{ +#if CONFIG_HAVE_DISPLAY + mError = true; + if (errorTimer != NULL) + { + xTimerDelete(errorTimer, 0); + } + errorTimer = xTimerCreate("ErrorTimer", pdMS_TO_TICKS(2000), false, this, ClearErrorState); + xTimerStart(errorTimer, 0); + Display(); + // If a status change occured, wake the display + WakeDisplay(); +#endif +} + void LEDWidget::Animate() { if (mBlinkOnTimeMS != 0 && mBlinkOffTimeMS != 0) @@ -75,9 +127,58 @@ void LEDWidget::Animate() void LEDWidget::DoSet(bool state) { - mState = state; + bool stateChange = (mState != state); + mState = state; if (mGPIONum < GPIO_NUM_MAX) { gpio_set_level(mGPIONum, (state) ? 1 : 0); } + if (stateChange) + { +#if CONFIG_HAVE_DISPLAY + + Display(); + // If a status change occured, wake the display + WakeDisplay(); +#endif + } +} + +#if CONFIG_HAVE_DISPLAY +void LEDWidget::Display() +{ + uint16_t msgX = 0; + uint16_t msgY = (DisplayHeight * LED_STATUS_POSITION) / 100; + uint16_t circleX = (LED_INDICATOR_X * DisplayWidth) / 100; + uint16_t circleY = (LED_INDICATOR_Y * DisplayHeight) / 100; + + // Wipe the Light Status Area + ClearRect(0, LED_STATUS_POSITION); + // Wipe the status circle + TFT_fillCircle(circleX, circleY, LED_INDICATOR_R_PX, TFT_BLACK); + // Display the Light Status on screen + TFT_setFont(DEJAVU24_FONT, NULL); + // Draw the default "Off" indicator + TFT_drawCircle(circleX, circleY, LED_INDICATOR_R_PX, TFT_DARKGREY); + + if (mError) + { + TFT_print((char *) "Recv Error", msgX, msgY); + // Draw the "Error" indicator + TFT_fillCircle(circleX, circleY, LED_INDICATOR_R_PX, TFT_RED); + } + else + { + if (mState) + { + TFT_print((char *) onMsg, msgX, msgY); + // Draw the "ON" indicator + TFT_fillCircle(circleX, circleY, LED_INDICATOR_R_PX, TFT_GREEN); + } + else + { + TFT_print((char *) offMsg, msgX, msgY); + } + } } +#endif diff --git a/examples/wifi-echo/server/esp32/main/QRCodeWidget.cpp b/examples/wifi-echo/server/esp32/main/QRCodeWidget.cpp index b92ba0b7daab9b..630a80f0eb13ee 100644 --- a/examples/wifi-echo/server/esp32/main/QRCodeWidget.cpp +++ b/examples/wifi-echo/server/esp32/main/QRCodeWidget.cpp @@ -46,7 +46,9 @@ // Spells CHIP on a dialer #define EXAMPLE_VENDOR_ID 3447 // Used to indicate that an SSID has been added to the QRCode -#define EXAMPLE_VENDOR_TAG 1 +#define EXAMPLE_VENDOR_TAG_SSID 1 +// Used to indicate that an IP address has been added to the QRCode +#define EXAMPLE_VENDOR_TAG_IP 2 #if CONFIG_HAVE_DISPLAY @@ -75,6 +77,15 @@ void GetAPName(char * ssid, size_t ssid_len) snprintf(ssid, ssid_len, "%s%02X%02X", "CHIP_DEMO-", mac[4], mac[5]); } +void GetGatewayIP(char * ip_buf, size_t ip_len) +{ + + tcpip_adapter_ip_info_t ip; + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip); + IPAddress::FromIPv4(ip.ip).ToString(ip_buf, ip_len); + ESP_LOGE(TAG, "Got gateway ip %s", ip_buf); +} + string createSetupPayload() { char ap_ssid[MAX_SSID_LEN]; @@ -83,17 +94,26 @@ string createSetupPayload() payload.version = 1; payload.vendorID = EXAMPLE_VENDOR_ID; payload.productID = 1; - uint64_t tag; - VendorTag(EXAMPLE_VENDOR_TAG, tag); - OptionalQRCodeInfo stringInfo; - stringInfo.tag = tag; - stringInfo.type = optionalQRCodeInfoTypeString; - stringInfo.data = ap_ssid; - payload.addOptionalData(stringInfo); + + OptionalQRCodeInfo ssidInfo; + ssidInfo.tag = EXAMPLE_VENDOR_TAG_SSID; + ssidInfo.type = optionalQRCodeInfoTypeString; + ssidInfo.data = ap_ssid; + payload.addVendorOptionalData(ssidInfo); + + char gw_ip[INET6_ADDRSTRLEN]; + GetGatewayIP(gw_ip, sizeof(gw_ip)); + OptionalQRCodeInfo ipInfo; + ipInfo.tag = EXAMPLE_VENDOR_TAG_IP; + ipInfo.type = optionalQRCodeInfoTypeString; + ipInfo.data = gw_ip; + payload.addVendorOptionalData(ipInfo); + QRCodeSetupPayloadGenerator generator(payload); string result; - uint8_t tlvDataStart[sizeof(ap_ssid)]; - CHIP_ERROR err = generator.payloadBase41Representation(result, tlvDataStart, sizeof(tlvDataStart)); + size_t tlvDataLen = sizeof(ap_ssid) + sizeof(gw_ip); + uint8_t tlvDataStart[tlvDataLen]; + CHIP_ERROR err = generator.payloadBase41Representation(result, tlvDataStart, tlvDataLen); if (err != CHIP_NO_ERROR) { ESP_LOGE(TAG, "Couldn't get payload string %d", generator.payloadBase41Representation(result)); diff --git a/examples/wifi-echo/server/esp32/main/include/Display.h b/examples/wifi-echo/server/esp32/main/include/Display.h index 8efbf0c5cbca81..3c6bd39b1f33a0 100644 --- a/examples/wifi-echo/server/esp32/main/include/Display.h +++ b/examples/wifi-echo/server/esp32/main/include/Display.h @@ -65,6 +65,20 @@ extern esp_err_t InitDisplay(); * Clear the display by setting the whole screen to black */ extern void ClearDisplay(); + +/** + * @brief + * Clear a portion of the display by drawing a black rectangle based on the given arguments + * + * Calling this with default arguments is the same as calling `ClearDisplay()`. + * + * @param x_percent_start The starting x coordinate specified as a percentage of the screen's width. + * @param y_percent_start The starting y coordinate specified as a percentage of the screen's height. + * @param x_percent_end The end x coordinate specified as a percentage of the screen's width. + * @param y_percent_end The end y coordinate specified as a percentage of the screen's height. + */ +extern void ClearRect(uint16_t x_percent_start = 0, uint16_t y_percent_start = 0, uint16_t x_percent_end = 100, + uint16_t y_percent_end = 100); /** * @brief * Display a status message at a given vertical position diff --git a/examples/wifi-echo/server/esp32/main/include/LEDWidget.h b/examples/wifi-echo/server/esp32/main/include/LEDWidget.h index e4145a0127ead0..dc625486e64d3c 100644 --- a/examples/wifi-echo/server/esp32/main/include/LEDWidget.h +++ b/examples/wifi-echo/server/esp32/main/include/LEDWidget.h @@ -20,8 +20,14 @@ #ifndef LED_WIDGET_H #define LED_WIDGET_H +#include "Display.h" + #include "driver/gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/timers.h" + class LEDWidget { public: @@ -29,7 +35,13 @@ class LEDWidget void Set(bool state); void Blink(uint32_t changeRateMS); void Blink(uint32_t onTimeMS, uint32_t offTimeMS); + void BlinkOnError(); void Animate(); +#if CONFIG_HAVE_DISPLAY + void Display(); +#endif + bool mError; + TimerHandle_t errorTimer; private: int64_t mLastChangeTimeUS; diff --git a/examples/wifi-echo/server/esp32/main/wifi-echo.cpp b/examples/wifi-echo/server/esp32/main/wifi-echo.cpp index 7447b94e67a077..dc25868799b103 100644 --- a/examples/wifi-echo/server/esp32/main/wifi-echo.cpp +++ b/examples/wifi-echo/server/esp32/main/wifi-echo.cpp @@ -35,11 +35,12 @@ #include #include +#include using namespace ::chip; using namespace ::chip::DeviceLayer; -extern void startServer(UDPEndPoint *& endpoint); +extern void startServer(SecureSessionMgr * transportIPv4, SecureSessionMgr * transportIPv6); extern void startClient(void); #if CONFIG_DEVICE_TYPE_M5STACK @@ -68,11 +69,14 @@ extern void startClient(void); static QRCodeWidget sQRCodeWidget; +// Where to draw the connection status message +#define CONNECTION_MESSAGE 75 +// Where to draw the IPv6 information +#define IPV6_INFO 85 #endif // CONFIG_HAVE_DISPLAY LEDWidget statusLED; static Button attentionButton; -static volatile ConnectivityChange sConnectionState = kConnectivity_NoChange; const char * TAG = "wifi-echo-demo"; @@ -166,8 +170,8 @@ extern "C" void app_main() // Start the Echo Server InitDataModelHandler(); - UDPEndPoint * sEndpoint = NULL; - startServer(sEndpoint); + SecureSessionMgr sTransportIPv4, sTransportIPv6; + startServer(&sTransportIPv4, &sTransportIPv6); #if CONFIG_USE_ECHO_CLIENT startClient(); #endif @@ -193,6 +197,7 @@ extern "C" void app_main() // Display the UI widgets. ClearDisplay(); sQRCodeWidget.Display(); + statusLED.Display(); #endif // CONFIG_HAVE_DISPLAY @@ -209,37 +214,6 @@ extern "C" void app_main() WakeDisplay(); } - switch (sConnectionState) - { - case kConnectivity_Established: - // Show the currently connected state - tcpip_adapter_ip_info_t ipInfo; - if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo) == ESP_OK) - { - char ipAddrStr[INET_ADDRSTRLEN]; - IPAddress::FromIPv4(ipInfo.ip).ToString(ipAddrStr, sizeof(ipAddrStr)); - wifi_ap_record_t apInfo; - if (esp_wifi_sta_get_ap_info(&apInfo) == ESP_OK) - { - char message[256]; - snprintf(message, sizeof(message), "WiFi Connected: %s\nEcho Server at %s:%d", (char *) apInfo.ssid, ipAddrStr, - CONFIG_ECHO_PORT); - // place it close to the bottom of the screen - DisplayStatusMessage(message, 85); - } - } - sConnectionState = kConnectivity_NoChange; - break; - case kConnectivity_Lost: - // Hide the currently connected state - sConnectionState = kConnectivity_NoChange; - ClearDisplay(); - sQRCodeWidget.Display(); - break; - case kConnectivity_NoChange: - default: - break; - } #endif // CONFIG_HAVE_DISPLAY vTaskDelay(50 / portTICK_PERIOD_MS); @@ -261,14 +235,20 @@ void DeviceEventHandler(const ChipDeviceEvent * event, intptr_t arg) { char ipAddrStr[INET_ADDRSTRLEN]; IPAddress::FromIPv4(ipInfo.ip).ToString(ipAddrStr, sizeof(ipAddrStr)); - ESP_LOGI(TAG, "Server ready at: %s:%d", ipAddrStr, CONFIG_ECHO_PORT); - sConnectionState = kConnectivity_Established; + ESP_LOGI(TAG, "Server ready at: %s:%d", ipAddrStr, CHIP_PORT); } } else if (event->InternetConnectivityChange.IPv4 == kConnectivity_Lost) { - ESP_LOGE(TAG, "Lost IPv4 address..."); - sConnectionState = kConnectivity_Lost; + ESP_LOGE(TAG, "Lost IPv4 connectivity..."); + } + if (event->InternetConnectivityChange.IPv6 == kConnectivity_Established) + { + ESP_LOGI(TAG, "IPv6 Server ready..."); + } + else if (event->InternetConnectivityChange.IPv6 == kConnectivity_Lost) + { + ESP_LOGE(TAG, "Lost IPv6 connectivity..."); } } if (event->Type == DeviceEventType::kSessionEstablished && event->SessionEstablished.IsCommissioner) diff --git a/examples/wifi-echo/server/esp32/partitions.csv b/examples/wifi-echo/server/esp32/partitions.csv new file mode 100644 index 00000000000000..b338ff11a11589 --- /dev/null +++ b/examples/wifi-echo/server/esp32/partitions.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, , 0x6000, +phy_init, data, phy, , 0x1000, +# Factory partition size about 1.9MB +factory, app, factory, , 1945K, diff --git a/examples/wifi-echo/server/esp32/sdkconfig.defaults b/examples/wifi-echo/server/esp32/sdkconfig.defaults index 6709e87c4afd13..41d6c5a9be7106 100644 --- a/examples/wifi-echo/server/esp32/sdkconfig.defaults +++ b/examples/wifi-echo/server/esp32/sdkconfig.defaults @@ -26,3 +26,11 @@ CONFIG_ESPTOOLPY_BAUD=921600 CONFIG_ESPTOOLPY_COMPRESSED=y CONFIG_MONITOR_BAUD_115200B=y CONFIG_MONITOR_BAUD=115200 + +#enable lwip ipv6 autoconfig +CONFIG_LWIP_IPV6_AUTOCONFIG=y + +# Use a custom partition table +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" + diff --git a/integrations/docker/images/chip-build/Dockerfile b/integrations/docker/images/chip-build/Dockerfile index 9d15c27cb16953..c29851f3a9d1a3 100644 --- a/integrations/docker/images/chip-build/Dockerfile +++ b/integrations/docker/images/chip-build/Dockerfile @@ -26,14 +26,35 @@ RUN set -x \ wget \ libmbedtls-dev \ python-pip \ + python3 \ + python3-pip \ libusb-1.0 \ libncurses5-dev \ libncursesw5-dev \ + libnspr4-dev \ + libxml2-dev \ flex \ bison \ gperf \ zlib1g-dev \ libglib2.0-dev \ libpixman-1-dev \ + cmake \ && rm -rf /var/lib/apt/lists/ \ && : # last line + +RUN set -x \ + && pip3 install circleci attrs coloredlogs PyGithub \ + && : # last line + +# Install bloat comparison tools +RUN set -x \ + && git clone https://github.com/google/bloaty.git \ + && mkdir -p bloaty/build \ + && cd bloaty/build \ + && cmake ../ \ + && make -j \ + && make install \ + && cd ../.. \ + && rm -rf bloaty \ + && : # last line diff --git a/integrations/docker/images/chip-build/version b/integrations/docker/images/chip-build/version index d3b5ba4bfc3a30..b005e307c519f6 100644 --- a/integrations/docker/images/chip-build/version +++ b/integrations/docker/images/chip-build/version @@ -1 +1 @@ -0.2.11 +0.2.15 diff --git a/scripts/build/bootstrap.sh b/scripts/build/bootstrap.sh index 292aeaf1a72c02..1322f54f3710ec 100755 --- a/scripts/build/bootstrap.sh +++ b/scripts/build/bootstrap.sh @@ -1,4 +1,7 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env if [[ ! -f build/default/config.status ]]; then mkdir -p build/default diff --git a/scripts/build/default.sh b/scripts/build/default.sh index aea2633edb86ae..ba9024fce1029c 100755 --- a/scripts/build/default.sh +++ b/scripts/build/default.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make V=1 -C build/default diff --git a/scripts/build/distribution_check.sh b/scripts/build/distribution_check.sh index dee5a325453fbe..727e4f4be1b0a8 100755 --- a/scripts/build/distribution_check.sh +++ b/scripts/build/distribution_check.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash -make -C build/default distcheck +set -x +env + +make V=1 -C build/default distcheck diff --git a/scripts/examples/esp_echo_app.sh b/scripts/examples/esp_echo_app.sh index 4ba482cd611555..29328eeddfd9ce 100755 --- a/scripts/examples/esp_echo_app.sh +++ b/scripts/examples/esp_echo_app.sh @@ -1,6 +1,9 @@ -#!/bin/bash +#!/usr/bin/env bash -make -f Makefile-bootstrap repos +set -x +env + +make -j -f Makefile-bootstrap repos source examples/wifi-echo/server/esp32/idf.sh -idf make -C examples/wifi-echo/server/esp32 defconfig -idf make -C examples/wifi-echo/server/esp32 +idf make -j V=1 -C examples/wifi-echo/server/esp32 defconfig +idf make -j V=1 -C examples/wifi-echo/server/esp32 diff --git a/scripts/examples/nrf_lock_app.sh b/scripts/examples/nrf_lock_app.sh index 0a22563a7c4a46..d2d6c85d326644 100755 --- a/scripts/examples/nrf_lock_app.sh +++ b/scripts/examples/nrf_lock_app.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make VERBOSE=1 -C examples/lock-app/nrf5 diff --git a/scripts/examples/standalone_echo_client.sh b/scripts/examples/standalone_echo_client.sh index 3f27470ae29d7e..29eff713fb741d 100755 --- a/scripts/examples/standalone_echo_client.sh +++ b/scripts/examples/standalone_echo_client.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make -C examples/chip-tool diff --git a/scripts/examples/standalone_shell.sh b/scripts/examples/standalone_shell.sh new file mode 100755 index 00000000000000..e03b2618eb76d8 --- /dev/null +++ b/scripts/examples/standalone_shell.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -x +env + +make -C examples/shell/standalone diff --git a/scripts/helpers/auto_enforce_code_style.sh b/scripts/helpers/auto_enforce_code_style.sh index 0713d7b352edb8..4aab42bb8a16ba 100755 --- a/scripts/helpers/auto_enforce_code_style.sh +++ b/scripts/helpers/auto_enforce_code_style.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make -C build/default pretty diff --git a/scripts/helpers/bloat_check.py b/scripts/helpers/bloat_check.py new file mode 100755 index 00000000000000..26efd201dc7317 --- /dev/null +++ b/scripts/helpers/bloat_check.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 + +import argparse +import attr +import coloredlogs +import github +import logging +import os +import stat +import subprocess + +import ci_fetch_artifacts + + +def filesInDirectory(dirName): + """Get all the file names in the specified directory.""" + for name in os.listdir(dirName): + mode = os.stat(os.path.join(dirName, name)).st_mode + if stat.S_ISREG(mode): + yield name + + +def writeFileBloatReport(f, baselineName, buildName): + """Generate a bloat report diffing a baseline file with a build output file.""" + logging.info('Running bloaty diff between %s and %s', baselineName, buildName) + f.write('Bloat difference between %s and %s:\n\n' % (baselineName, buildName)) + + result = subprocess.run( + ['bloaty', buildName, '--', baselineName], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + + if result.returncode != 0: + logging.warning('Bloaty execution failed: %d', result.returncode) + f.write('BLOAT EXECUTION FAILED WITH CODE %d:\n' % result.returncode) + + f.write(result.stdout.decode('utf8')) + f.write('\n') + + +def generateBloatReport(outputFileName, + baselineDir, + buildOutputDir, + title='BLOAT REPORT'): + """Generates a bloat report fo files betwen two diferent directories.""" + logging.info('Generating bloat diff report between %s and %s', baselineDir, + buildOutputDir) + with open(outputFileName, 'wt') as f: + f.write(title + '\n\n') + + baselineNames = set([name for name in filesInDirectory(baselineDir)]) + outputNames = set([name for name in filesInDirectory(buildOutputDir)]) + + baselineOnly = baselineNames - outputNames + if baselineOnly: + logging.warning('Some files only exist in the baseline: %r', baselineOnly) + f.write('Files found only in the baseline:\n ') + f.write('\n %s'.join(baselineOnly)) + f.write('\n\n') + + outputOnly = outputNames - baselineNames + if outputOnly: + logging.warning('Some files only exist in the build output: %r', + outputOnly) + f.write('Files found only in the build output:\n ') + f.write('\n %s'.join(outputOnly)) + f.write('\n\n') + + for name in (baselineNames & outputNames): + writeFileBloatReport(f, os.path.join(baselineDir, name), + os.path.join(buildOutputDir, name)) + + +def sendFileAsPrComment(job_name, filename, gh_token, gh_repo, gh_pr_number): + """Generates a PR comment conaining the specified file content.""" + + logging.info('Uploading report to "%s", PR %d' % (gh_repo, gh_pr_number)) + + api = github.Github(gh_token) + repo = api.get_repo(gh_repo) + pull = repo.get_pull(gh_pr_number) + + # NOTE: PRs are issues with attached patches, hence the API naming + pull.create_issue_comment('''Bloat report for job "%s": + + ``` + %s + ``` + ''' % (job_name, open(filename, 'rt').read())) + + +def main(): + """Main task if executed standalone.""" + parser = argparse.ArgumentParser(description='Fetch master build artifacts.') + parser.add_argument('--token', type=str, help='API token to use') + parser.add_argument( + '--job', type=str, help='From what job to fetch artifacts from') + parser.add_argument( + '--artifact-download-dir', + type=str, + default='.', + help='Where to download the artifacts') + parser.add_argument( + '--build-output-dir', + type=str, + default='.', + help='Generated build files directory to use to compare for bloat') + parser.add_argument( + '--report-file', + type=str, + default='report.txt', + help='From what job to fetch artifacts from') + parser.add_argument( + '--github-api-token', + type=str, + help='Github API token to upload the report as a comment') + parser.add_argument( + '--github-repository', type=str, help='Repository to use for PR comments') + parser.add_argument( + '--github-comment-pr-number', + type=str, + default=None, + help='To what PR to comment in github') + parser.add_argument( + '--log-level', + default=logging.INFO, + type=lambda x: getattr(logging, x), + help='Configure the logging level.') + args = parser.parse_args() + + # Ensures somewhat pretty logging of what is going on + logging.basicConfig( + level=args.log_level, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') + coloredlogs.install() + + if not args.token or not args.job: + logging.error( + 'Required arguments missing. Please specify at least job and token.') + return + + try: + ci_fetch_artifacts.fetchArtifactsForJob(args.token, args.job, + args.artifact_download_dir) + except Exception as e: + logging.warning('Failed to fetch artifacts: %r' % e) + + generateBloatReport( + args.report_file, + args.artifact_download_dir, + args.build_output_dir, + title="Bloat report for job '%s'" % args.job) + + if args.github_api_token and args.github_repository and args.github_comment_pr_number: + sendFileAsPrComment( + args.job, + args.report_file, + args.github_api_token, + args.github_repository, + int(args.github_comment_pr_number), + ) + +if __name__ == '__main__': + # execute only if run as a script + main() diff --git a/scripts/helpers/ci_fetch_artifacts.py b/scripts/helpers/ci_fetch_artifacts.py new file mode 100755 index 00000000000000..2965d8491703ef --- /dev/null +++ b/scripts/helpers/ci_fetch_artifacts.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 + +import argparse +import attr +import coloredlogs +import logging + +from circleci.api import Api + + +@attr.s +class CompletedBuild: + """Represents basic build information""" + job_name = attr.ib(kw_only=True) + build_num = attr.ib(kw_only=True) + + +@attr.s +class ArtifactInfo: + """Represents basic artifact information""" + path = attr.ib(kw_only=True) # pretty path + url = attr.ib(kw_only=True) # download URL + + +class ApiHandler: + """Wrapper around circleci API calls, specific for artifact access.""" + + def __init__(self, token, user='project-chip', project='connectedhomeip'): + self.api = Api(token) + self.user = user + self.project = project + self.vcs_type = 'github' + + def fetchCompletedBuilds(self, branch='master'): + """Gets a snapshot of the latest successful builds in the specified branch.""" + for build in self.api.get_project_build_summary( + self.user, self.project, branch=branch, status_filter='successful'): + logging.debug('Fetched summary: %r', build) + yield CompletedBuild( + job_name=build['workflows']['job_name'], build_num=build['build_num']) + + def fetchLatestBuildNumber(self, job_name, branch='master'): + """Fetch the latest build number for the specified job name.""" + logging.info('Searching for the latest job "%s"', job_name) + + for build in self.fetchCompletedBuilds(branch): + logging.info('Found a completed build %r', build) + + if build.job_name == job_name: + logging.info('Found the latest job with id %d', build.build_num) + return build.build_num + + logging.error('No usable latest build was found.') + raise Exception('No build found for job "%s"' % job_name) + + def getArtifacts(self, build_num): + """Fetch artifacts for the specified build number.""" + for artifact in self.api.get_artifacts(self.user, self.project, build_num, + self.vcs_type): + logging.debug('Fetched artifact info: %r', artifact) + yield ArtifactInfo(path=artifact['path'], url=artifact['url']) + + +def fetchArtifactsForJob(token, job_name, download_dir): + """Download all job artifacts in to the specified directory.""" + # actual processing: downloads all the given artifacts + handler = ApiHandler(token) + for artifact in handler.getArtifacts( + handler.fetchLatestBuildNumber(job_name)): + logging.info('Downloading artifact %r...', artifact) + handler.api.download_artifact(artifact.url, download_dir) + logging.info('Download complete') + + +def main(): + """Main task if executed standalone.""" + parser = argparse.ArgumentParser(description='Fetch master build artifacts.') + parser.add_argument('--token', type=str, help='API token to use') + parser.add_argument('--job', type=str, help='What job to search for') + parser.add_argument( + '--download-dir', + type=str, + default='.', + help='Where to download the artifacts') + parser.add_argument( + '--log-level', + default=logging.INFO, + type=lambda x: getattr(logging, x), + help='Configure the logging level.') + args = parser.parse_args() + + # Ensures somewhat pretty logging of what is going on + logging.basicConfig( + level=args.log_level, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') + coloredlogs.install() + + if not args.token or not args.job: + logging.error( + 'Required arguments missing. Please specify at least job and token.') + return + + fetchArtifactsForJob(args.token, args.job, args.download_dir) + + +if __name__ == '__main__': + # execute only if run as a script + main() diff --git a/scripts/helpers/clean.sh b/scripts/helpers/clean.sh index 57c1d1e064aa6b..33648113028a3b 100755 --- a/scripts/helpers/clean.sh +++ b/scripts/helpers/clean.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make -C build/default distclean diff --git a/scripts/helpers/clean_tree.sh b/scripts/helpers/clean_tree.sh index 09dd8a475923f8..b91bc8581df808 100755 --- a/scripts/helpers/clean_tree.sh +++ b/scripts/helpers/clean_tree.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make -f Makefile-bootstrap clean-repos && git clean -Xdf diff --git a/scripts/helpers/code_style_check.sh b/scripts/helpers/code_style_check.sh index ec85d4edfdebc0..9f08a44ad1a38e 100755 --- a/scripts/helpers/code_style_check.sh +++ b/scripts/helpers/code_style_check.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make -C build/default pretty-check diff --git a/scripts/helpers/restyle_merge.sh b/scripts/helpers/restyle_merge.sh index a6b1db5e5c21f5..d970c28695b793 100755 --- a/scripts/helpers/restyle_merge.sh +++ b/scripts/helpers/restyle_merge.sh @@ -1,4 +1,7 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env git remote add upstream https://github.com/project-chip/connectedhomeip.git git fetch upstream pull/"$1"/head diff --git a/scripts/setup/install_packages.sh b/scripts/setup/install_packages.sh index a9bf588e2f8845..7b86e9af3165d2 100755 --- a/scripts/setup/install_packages.sh +++ b/scripts/setup/install_packages.sh @@ -1 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env diff --git a/scripts/setup/linux/install_packages.sh b/scripts/setup/linux/install_packages.sh index 74f898ab433d02..451da20948fbd1 100755 --- a/scripts/setup/linux/install_packages.sh +++ b/scripts/setup/linux/install_packages.sh @@ -1,6 +1,8 @@ -#!/bin/bash +#!/usr/bin/env bash set -x +env + apt-get update apt-get install -fy \ git \ diff --git a/scripts/test-driver.sh b/scripts/test-driver.sh new file mode 100755 index 00000000000000..e67b33826aba5c --- /dev/null +++ b/scripts/test-driver.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# +# Copyright (c) 2020 Project CHIP Authors +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Description: +# This file implements a wrapper for autoconf's test-driver script +# that show's tests output during the build +# +# Use me like this: +# make LOG_DRIVER= ... +# + +here=${0%/*} + +set -e + +"$here"/../build/autoconf/test-driver "$@" + +while ((${#@})); do + [[ $1 == "--log-file" ]] && { + cat "$2" && break + } + shift +done diff --git a/scripts/tests/all_tests.sh b/scripts/tests/all_tests.sh index 64175c9f5531b9..996b97a5bc8d9e 100755 --- a/scripts/tests/all_tests.sh +++ b/scripts/tests/all_tests.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make V=1 -C build/default check diff --git a/scripts/tests/crypto_tests.sh b/scripts/tests/crypto_tests.sh index ef30742d72ece4..aa39ec73e6a38b 100755 --- a/scripts/tests/crypto_tests.sh +++ b/scripts/tests/crypto_tests.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make V=1 -C build/default/src/crypto check diff --git a/scripts/tests/esp32_qemu_tests.sh b/scripts/tests/esp32_qemu_tests.sh index 6418c4139cb445..3c0999fe22531b 100755 --- a/scripts/tests/esp32_qemu_tests.sh +++ b/scripts/tests/esp32_qemu_tests.sh @@ -26,7 +26,9 @@ chip_dir="$here"/../.. source "$chip_dir"/src/test_driver/esp32/idf.sh "$chip_dir"/src/test_driver/esp32/qemu_setup.sh -# Currently only crypto tests are configured to run on QEMU. -# The "src/crypto" qualifier will be removed, once all CHIP unit tests are +# Currently only crypto, inet, and system tests are configured to run on QEMU. +# The specific qualifiers will be removed, once all CHIP unit tests are # updated to run on QEMU. idf make V=1 -C "$chip_dir"/src/test_driver/esp32/build/chip/src/crypto check +idf make V=1 -C "$chip_dir"/src/test_driver/esp32/build/chip/src/inet check +idf make V=1 -C "$chip_dir"/src/test_driver/esp32/build/chip/src/system check diff --git a/scripts/tests/mbedtls_tests.sh b/scripts/tests/mbedtls_tests.sh index c437ac2e886357..d9281da5ecf74f 100755 --- a/scripts/tests/mbedtls_tests.sh +++ b/scripts/tests/mbedtls_tests.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make V=1 -C build/default/third_party/mbedtls diff --git a/scripts/tests/openssl_tests.sh b/scripts/tests/openssl_tests.sh index d734e5b5e8c868..78119b0327de59 100755 --- a/scripts/tests/openssl_tests.sh +++ b/scripts/tests/openssl_tests.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make V=1 -C build/default/src/crypto/ && make V=1 -C build/default/src/crypto/tests/ TestCryptoPAL diff --git a/scripts/tests/qrcode_payload_tests.sh b/scripts/tests/qrcode_payload_tests.sh index 88cf2440cee482..9f213f714c44bf 100755 --- a/scripts/tests/qrcode_payload_tests.sh +++ b/scripts/tests/qrcode_payload_tests.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make V=1 -C build/default/src/setup_payload/tests/ TestQRCode diff --git a/scripts/tests/save_logs.sh b/scripts/tests/save_logs.sh index cb499da8bfcdfa..95a7d7ca058042 100755 --- a/scripts/tests/save_logs.sh +++ b/scripts/tests/save_logs.sh @@ -1,4 +1,7 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env TARGET_DIR=$1 diff --git a/scripts/tests/setup_payload_tests.sh b/scripts/tests/setup_payload_tests.sh index 5bc5f948aa64a7..74a14c2ac87872 100755 --- a/scripts/tests/setup_payload_tests.sh +++ b/scripts/tests/setup_payload_tests.sh @@ -1,3 +1,7 @@ -#!/bin/bash +#!/usr/bin/env bash +set -x +env + +make V=1 -C build/default/src/setup_payload/tests TestQRCode && build/default/src/setup_payload/tests/TestQRCode make V=1 -C build/default/src/setup_payload check diff --git a/scripts/tools/codecoverage.sh b/scripts/tools/codecoverage.sh index 034a9462500f78..5bd5d3169b04af 100755 --- a/scripts/tools/codecoverage.sh +++ b/scripts/tools/codecoverage.sh @@ -1,3 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x +env make -C build/default coverage diff --git a/scripts/tools/run_if.sh b/scripts/tools/run_if.sh index b53096d0ec10d6..28f6e535211124 100755 --- a/scripts/tools/run_if.sh +++ b/scripts/tools/run_if.sh @@ -1,4 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -x if [[ $1 == *"$2"* ]]; then shift diff --git a/src/README.md b/src/README.md index a70cad1e985cdd..a2cad09165887b 100644 --- a/src/README.md +++ b/src/README.md @@ -8,7 +8,6 @@ The CHIP src directory is structured as follows: | ------------- | -------------------------------------------------- | | app | Application Layer -- Zigbee Cluster Library (ZCL) | | ble | BLE Layer -- Bluetooth Transport Protocol (BTP) | -| chipcli | QR code tool CLI | | controller | Controller API | | crypto | Cryptography libraries | | darwin | Darwin Framework (iOS and MacOS) | diff --git a/src/app/DataModel.am b/src/app/DataModel.am index cd239c9b1ad381..a47712372857c0 100644 --- a/src/app/DataModel.am +++ b/src/app/DataModel.am @@ -29,7 +29,7 @@ CHIP_BUILD_DATA_MODEL_SOURCE_FILES @top_builddir@/src/app/gen/gen-command-handler.c \ @top_builddir@/src/app/gen/gen-global-command-handler.c \ @top_builddir@/src/app/gen/gen-specs.c \ - @top_builddir@/src/app/plugin/binding-mock/mock.c \ + @top_builddir@/src/app/plugin/binding-chip/chip.cpp \ @top_builddir@/src/app/plugin/cluster-server-basic/basic-server.c \ @top_builddir@/src/app/plugin/cluster-server-identify/identify-server.c \ @top_builddir@/src/app/plugin/cluster-server-level-control/level-control-server.c \ diff --git a/src/app/Makefile.am b/src/app/Makefile.am index c764211a6670ec..2dc0aadd146a59 100644 --- a/src/app/Makefile.am +++ b/src/app/Makefile.am @@ -36,6 +36,8 @@ lib_LIBRARIES = libCHIPDataModel.a libCHIPDataModel_a_CPPFLAGS = \ -I$(top_srcdir)/src/app/chip-zcl \ -I$(top_srcdir)/src/app/gen \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/lib \ $(NULL) libCHIPDataModel_a_SOURCES = $(CHIP_BUILD_DATA_MODEL_SOURCE_FILES) diff --git a/src/app/chip-zcl/chip-zcl-buffer.h b/src/app/chip-zcl/chip-zcl-buffer.h index c5c81ad5be6fe8..7b41493f45196d 100644 --- a/src/app/chip-zcl/chip-zcl-buffer.h +++ b/src/app/chip-zcl/chip-zcl-buffer.h @@ -26,6 +26,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif /* #ifdef __cplusplus */ + /** * Structure that describes the buffers passed between the CHIP layers and * the Zap layer. @@ -37,24 +41,7 @@ * from, currentPosition represents the current read position and dataLength is * the total length of the data that can be read. */ -typedef struct -{ - /** - * The data storage for our buffer. - */ - uint8_t * buffer; - - /** - * The size of our buffer above. - */ - uint16_t bufferLength; - - /** - * The length of the data that can be read from this buffer; nonzero only - * when it's ready to be read from. - */ - uint16_t dataLength; -} ChipZclBuffer_t; +typedef struct ChipZclBuffer_t ChipZclBuffer_t; /** * Function that allocates a buffer. @@ -82,14 +69,6 @@ uint8_t * chipZclBufferPointer(ChipZclBuffer_t * buffer); */ void chipZclBufferFree(ChipZclBuffer_t * buffer); -/** - * Function that resets a buffer to have its entire allocated length - * available for writing. - * - * @param[in] buffer the buffer to reset. - */ -void chipZclBufferReset(ChipZclBuffer_t * buffer); - /** * Function that returns the size of the used portion of the buffer, in octets, * when the buffer is ready for reading. Always returns 0 for buffers that are @@ -117,4 +96,7 @@ void chipZclBufferSetDataLength(ChipZclBuffer_t * buffer, uint16_t newLength); */ uint16_t chipZclBufferAvailableLength(ChipZclBuffer_t * buffer); +#ifdef __cplusplus +} +#endif /* #ifdef __cplusplus */ #endif // CHIP_ZCL_BUFFER diff --git a/src/app/chip-zcl/chip-zcl-codec.h b/src/app/chip-zcl/chip-zcl-codec.h index 6020efc5773d54..0f5d0c34dcc761 100644 --- a/src/app/chip-zcl/chip-zcl-codec.h +++ b/src/app/chip-zcl/chip-zcl-codec.h @@ -31,6 +31,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif /* #ifdef __cplusplus */ + /** * Codec keeps track of an ongoing encode/decode session of a Buffer */ @@ -84,4 +88,8 @@ ChipZclStatus_t chipZclCodecDecode(ChipZclCodec_t * codec, ChipZclType_t type, v */ ChipZclStatus_t chipZclCodecDecodeEnd(ChipZclCodec_t * me); +#ifdef __cplusplus +} +#endif /* #ifdef __cplusplus */ + #endif // CHIP_ZCL_CODEC diff --git a/src/app/chip-zcl/chip-zcl-struct.h b/src/app/chip-zcl/chip-zcl-struct.h index 620138f53c446f..588afe6d488d81 100644 --- a/src/app/chip-zcl/chip-zcl-struct.h +++ b/src/app/chip-zcl/chip-zcl-struct.h @@ -28,6 +28,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif /* #ifdef __cplusplus */ + /** * Base types for the codec. This is a smaller subset than the actual ZCL types, and the * generated layer for a specific code is responsible for mapping ZCL types onto these @@ -250,4 +254,7 @@ void chipZclStoreInt32uValue(uint8_t * valueLoc, uint32_t value, uint8_t valueSi /** @} end addtogroup */ +#ifdef __cplusplus +} +#endif /* #ifdef __cplusplus */ #endif // CHIP_ZCL_STRUCT diff --git a/src/app/chip-zcl/chip-zcl.h b/src/app/chip-zcl/chip-zcl.h index 055912a7a480a5..6ac62570a37aca 100644 --- a/src/app/chip-zcl/chip-zcl.h +++ b/src/app/chip-zcl/chip-zcl.h @@ -24,12 +24,16 @@ #ifndef CHIP_ZCL_MASTER_HEADER #define CHIP_ZCL_MASTER_HEADER +#include "chip-zcl-buffer.h" + #include #include #include #include -#include "chip-zcl-buffer.h" +#ifdef __cplusplus +extern "C" { +#endif /* #ifdef __cplusplus */ typedef uint64_t bitmap64_t; typedef uint8_t enum8_t; @@ -833,6 +837,7 @@ typedef struct void chipZclEventSetDelayMs(Event * event, uint32_t delay); void chEventControlSetDelayMS(ChipZclEventControl * event, uint32_t delay); + /** @brief Sets this ::EmberEventControl to run "delay" milliseconds in the future. * NOTE: To avoid rollover errors in event calculation, the delay must be * less than ::EMBER_MAX_EVENT_CONTROL_DELAY_MS. @@ -976,15 +981,6 @@ void chipZclEventSetDelayMs(Event * event, uint32_t delay); // Endpoint Management ChipZclEndpointId_t chipZclEndpointIndexToId(ChipZclEndpointIndex_t index, const ChipZclClusterSpec_t * clusterSpec); -// Some platform CHIP_ZCL_STATUS_INSUFFICIENT_SPACE -#define MEMSET(d, v, l) memset(d, v, l) -#define MEMCOPY(d, s, l) memcpy(d, s, l) -#define MEMMOVE(d, s, l) memmove(d, s, l) -#define MEMCOMPARE(s0, s1, l) memcmp(s0, s1, l) -#define MEMPGMCOMPARE(s0, s1, l) memcmp(s0, s1, l) -#define LOW_BYTE(n) ((uint8_t)((n) &0xFF)) -#define HIGH_BYTE(n) ((uint8_t)(LOW_BYTE((n) >> 8))) -#define IS_BIG_ENDIAN() false /** * @brief Returns the value built from the two \c uint8_t * values \c high and \c low. @@ -1113,6 +1109,9 @@ void chipZclEncodeZclHeader(ChipZclBuffer_t * buffer, ChipZclCommandContext_t * */ void chipZclDecodeZclHeader(ChipZclBuffer_t * buffer, ChipZclCommandContext_t * context); -ChipZclStatus_t chipZclProcessIncoming(uint8_t * buffer, uint16_t bufferLength); +ChipZclStatus_t chipZclProcessIncoming(ChipZclBuffer_t * buffer); +#ifdef __cplusplus +} +#endif /* #ifdef __cplusplus */ #endif // CHIP_ZCL_MASTER_HEADER diff --git a/src/app/gen/Makefile.am b/src/app/gen/Makefile.am index 9980c51a138e04..b5cb43fe7be687 100644 --- a/src/app/gen/Makefile.am +++ b/src/app/gen/Makefile.am @@ -22,20 +22,25 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am +gendir = $(includedir)/gen + +gen_HEADERS = \ + gen-cluster-id.h \ + gen-command-id.h \ + gen-types.h \ + $(NULL) + EXTRA_DIST = \ gen-attribute-storage.h \ gen-attribute-type.h \ gen-callback-stubs.c \ gen-callbacks.h \ - gen-cluster-id.h \ gen-command-handler.c \ gen-command-handler.h \ - gen-command-id.h \ gen-endpoint-config.h \ gen-global-command-handler.c \ gen-global-command-handler.h \ gen-specs.c \ - gen-types.h \ gen.h \ README.md \ $(NULL) diff --git a/src/app/gen/gen-callback-stubs.c b/src/app/gen/gen-callback-stubs.c index 8c1fdab821615a..eecedaeed2e3f4 100644 --- a/src/app/gen/gen-callback-stubs.c +++ b/src/app/gen/gen-callback-stubs.c @@ -1,5 +1,4 @@ #include "chip-zcl.h" -#include // Callback implementations diff --git a/src/app/plugin/binding-chip/chip.cpp b/src/app/plugin/binding-chip/chip.cpp new file mode 100644 index 00000000000000..ca875961a51e99 --- /dev/null +++ b/src/app/plugin/binding-chip/chip.cpp @@ -0,0 +1,150 @@ +/** + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file provides the CHIP implementation of functions required + * by the CHIP ZCL Application Layer + * + */ + +#include "chip-zcl.h" + +#include +#include + +#include + +void chipZclCorePrintln(const char * formatString, ...) +{ + va_list args; + va_start(args, formatString); + chip::Logging::LogV(chip::Logging::kLogModule_Zcl, chip::Logging::kLogCategory_Detail, formatString, args); + va_end(args); +} + +// Default Response Stubs +ChipZclStatus_t chipZclSendDefaultResponse(const ChipZclCommandContext_t * context, ChipZclStatus_t status) +{ + return CHIP_ZCL_STATUS_SUCCESS; +} + +// Reporting Configuration Stubs +void chipZclReportingConfigurationsFactoryReset(ChipZclEndpointId_t endpointId) +{ + return; +} + +// Endpoint Management Stubs +ChipZclEndpointId_t chipZclEndpointIndexToId(ChipZclEndpointIndex_t index, const ChipZclClusterSpec_t * clusterSpec) +{ + return CHIP_ZCL_ENDPOINT_NULL; +} + +// Attribute Management Stubs +void chipZclResetAttributes(ChipZclEndpointId_t endpointId) +{ + return; +} + +// Event Management Stubs +EventQueue emAppEventQueue; + +Event * chipZclEventFind(EventQueue * queue, EventActions * actions, EventPredicate predicate, void * data, bool all) +{ + return 0; +} + +void chEventControlSetDelayMS(ChipZclEventControl * event, uint32_t delay) +{ + return; +} + +void chipZclEventSetDelayMs(Event * event, uint32_t delay) +{ + return; +} + +int32_t chipZclCompareClusterSpec(const ChipZclClusterSpec_t * s1, const ChipZclClusterSpec_t * s2) +{ + return 0; +} + +bool chipZclAreClusterSpecsEqual(const ChipZclClusterSpec_t * s1, const ChipZclClusterSpec_t * s2) +{ + return 0; +} + +void chipZclReverseClusterSpec(const ChipZclClusterSpec_t * s1, ChipZclClusterSpec_t * s2) +{ + return; +} + +/** + * ChipZclBuffer_t is merely a type alias + */ +struct ChipZclBuffer_t : public chip::System::PacketBuffer +{ +}; + +/** + * Function that allocates a buffer. + */ +ChipZclBuffer_t * chipZclBufferAlloc(uint16_t allocatedLength) +{ + return static_cast(chip::System::PacketBuffer::NewWithAvailableSize(allocatedLength)); +} + +/** + * Function that frees a buffer and its storage. + */ +void chipZclBufferFree(ChipZclBuffer_t * buffer) +{ + chip::System::PacketBuffer::Free(buffer); +} + +/** + * Function that returns a pointer to the raw buffer. + */ +uint8_t * chipZclBufferPointer(ChipZclBuffer_t * buffer) +{ + return buffer->Start(); +} + +/** + * Function that returns the size of the used portion of the buffer. + */ +uint16_t chipZclBufferDataLength(ChipZclBuffer_t * buffer) +{ + return buffer->DataLength(); +} + +/** + * returns space available for writing after any data already in the buffer + */ +uint16_t chipZclBufferAvailableLength(ChipZclBuffer_t * buffer) +{ + return buffer->AvailableDataLength(); +} + +/** + * sets data length for a buffer that's being written to + */ +void chipZclBufferSetDataLength(ChipZclBuffer_t * buffer, uint16_t newLength) +{ + buffer->SetDataLength(newLength); +} diff --git a/src/app/plugin/binding-mock/mock.c b/src/app/plugin/binding-mock/mock.c index 06e371b44c5c0c..aa5f4c8fee8830 100644 --- a/src/app/plugin/binding-mock/mock.c +++ b/src/app/plugin/binding-mock/mock.c @@ -95,6 +95,25 @@ void chipZclReverseClusterSpec(const ChipZclClusterSpec_t * s1, ChipZclClusterSp return; } +struct ChipZclBuffer_t +{ + /** + * The data storage for our buffer. + */ + void * buffer; + + /** + * The size of our buffer above. + */ + uint16_t bufferLength; + + /** + * The length of the data that can be read from this buffer; nonzero only + * when it's ready to be read from. + */ + uint16_t dataLength; +}; + /** * Function that allocates a buffer. */ @@ -103,7 +122,7 @@ ChipZclBuffer_t * chipZclBufferAlloc(uint16_t allocatedLength) ChipZclBuffer_t * buffer = (ChipZclBuffer_t *) malloc(sizeof(ChipZclBuffer_t) + allocatedLength); if (NULL != buffer) { - buffer->buffer = (uint8_t *) (buffer + 1); + buffer->buffer = (buffer + 1); buffer->dataLength = 0; buffer->bufferLength = allocatedLength; } @@ -117,3 +136,35 @@ void chipZclBufferFree(ChipZclBuffer_t * buffer) { free(buffer); } + +/** + * Function that returns a pointer to the raw buffer. + */ +uint8_t * chipZclBufferPointer(ChipZclBuffer_t * buffer) +{ + return buffer->buffer; +} + +/** + * Function that returns the size of the used portion of the buffer. + */ +uint16_t chipZclBufferDataLength(ChipZclBuffer_t * buffer) +{ + return buffer->dataLength; +} + +/** + * returns space available for writing after any data already in the buffer + */ +uint16_t chipZclBufferAvailableLength(ChipZclBuffer_t * buffer) +{ + return buffer->bufferLength - buffer->dataLength; +} + +/** + * sets data length for a buffer that's being written to + */ +void chipZclBufferSetDataLength(ChipZclBuffer_t * buffer, uint16_t newLength) +{ + buffer->dataLength = newLength; +} diff --git a/src/app/plugin/cluster-server-level-control/level-control-server.c b/src/app/plugin/cluster-server-level-control/level-control-server.c index c8bf78261f9a3d..234b0139e9b24e 100644 --- a/src/app/plugin/cluster-server-level-control/level-control-server.c +++ b/src/app/plugin/cluster-server-level-control/level-control-server.c @@ -25,6 +25,8 @@ #include "level-control-server.h" +#include /* for abs() */ + // User options for plugin Level Control Server #define CHIP_AF_PLUGIN_LEVEL_CONTROL_SERVER_MAXIMUM_LEVEL 255 #define CHIP_AF_PLUGIN_LEVEL_CONTROL_SERVER_MINIMUM_LEVEL 0 @@ -57,7 +59,6 @@ ChipZclLevelControlSceneSubTableEntry_t zapPluginLevelControlServerSceneSubTable #endif #endif -int abs(int I); static void moveToLevelHandler(const ChipZclCommandContext_t * context, const ChipZclClusterLevelControlServerCommandMoveToLevelRequest_t * request, bool withOnOff); static void moveHandler(const ChipZclCommandContext_t * context, diff --git a/src/app/plugin/core-api/core-api.c b/src/app/plugin/core-api/core-api.c index e89a1001a4d70f..441827c4976646 100644 --- a/src/app/plugin/core-api/core-api.c +++ b/src/app/plugin/core-api/core-api.c @@ -18,53 +18,16 @@ #include #include "chip-zcl.h" +#include "gen-global-command-handler.h" ChipZclStatus_t chipZclClusterCommandParse(ChipZclCommandContext_t * context); -ChipZclStatus_t chipZclProcessIncoming(uint8_t * rawBuffer, uint16_t rawBufferLength) +ChipZclStatus_t chipZclProcessIncoming(ChipZclBuffer_t * message) { - ChipZclBuffer_t buffer = { rawBuffer, rawBufferLength, rawBufferLength }; - ChipZclCommandContext_t context = { 0 }; - chipZclDecodeZclHeader(&buffer, &context); - - return chipZclClusterCommandParse(&context); -} - -/** - * Function that returns a pointer to the raw buffer. - */ -uint8_t * chipZclBufferPointer(ChipZclBuffer_t * buffer) -{ - return buffer->buffer; -} - -/** - * Function that returns the size of the used portion of the buffer. - */ -uint16_t chipZclBufferDataLength(ChipZclBuffer_t * buffer) -{ - return buffer->dataLength; -} - -/** - * returns space available for writing after any data already in the buffer - */ -uint16_t chipZclBufferAvailableLength(ChipZclBuffer_t * buffer) -{ - return buffer->bufferLength - buffer->dataLength; -} - -/** - * sets data length for a buffer that's being written to - */ -void chipZclBufferSetDataLength(ChipZclBuffer_t * buffer, uint16_t newLength) -{ - buffer->dataLength = newLength; -} + chipZclDecodeZclHeader(message, &context); -void chipZclBufferReset(ChipZclBuffer_t * buffer) -{ - buffer->dataLength = 0; + // Should this just use chipZclCommandParse? + return context.clusterSpecific ? chipZclClusterCommandParse(&context) : chipZclGeneralCommandParse(&context); } diff --git a/src/app/plugin/core-data-model/zcl-data-model.c b/src/app/plugin/core-data-model/zcl-data-model.c index 00d4fdfeeefdb0..3889f11ec96457 100644 --- a/src/app/plugin/core-data-model/zcl-data-model.c +++ b/src/app/plugin/core-data-model/zcl-data-model.c @@ -95,7 +95,7 @@ void chipZclCopyString(uint8_t * dest, uint8_t * src, uint8_t size) { length = size; } - MEMMOVE(dest + 1, src + 1, length); + memmove(dest + 1, src + 1, length); dest[0] = length; } } @@ -133,11 +133,20 @@ void chipZclCopyLongString(uint8_t * dest, uint8_t * src, uint16_t size) { length = size; } - MEMMOVE(dest + 2, src + 2, length); - dest[0] = LOW_BYTE(length); - dest[1] = HIGH_BYTE(length); + memmove(dest + 2, src + 2, length); + dest[0] = (uint8_t)(length & 0xFF); // LOW_BYTE(length); + dest[1] = (uint8_t)((length >> 8) & 0xff); // HIGH_BYTE(length); } } + +#if defined(__LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__) || (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#define BIGENDIAN_CPU 0 +#elif defined(__BIG_ENDIAN) || defined(__BIG_ENDIAN__) || (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define BIGENDIAN_CPU 1 +#else +#error "cannot determine whether BIGENDIAN_CPU or not" +#endif + // You can pass in val1 as NULL, which will assume that it is // pointing to an array of all zeroes. This is used so that // default value of NULL is treated as all zeroes. @@ -154,10 +163,10 @@ int8_t chipZclCompareValues(uint8_t * val1, uint8_t * val2, uint8_t len, bool si for (i = 0; i < len; i++) { - j = (val1 == NULL ? 0 : (IS_BIG_ENDIAN() ? val1[i] : val1[(len - 1) - i])); + j = (val1 == NULL ? 0 : (BIGENDIAN_CPU ? val1[i] : val1[(len - 1) - i])); accum1 |= j << (8 * (len - 1 - i)); - k = (IS_BIG_ENDIAN() ? val2[i] : val2[(len - 1) - i]); + k = (BIGENDIAN_CPU ? val2[i] : val2[(len - 1) - i]); accum2 |= k << (8 * (len - 1 - i)); } @@ -196,8 +205,8 @@ int8_t chipZclCompareValues(uint8_t * val1, uint8_t * val2, uint8_t len, bool si { // regular unsigned number comparison for (i = 0; i < len; i++) { - j = (val1 == NULL ? 0 : (IS_BIG_ENDIAN() ? val1[i] : val1[(len - 1) - i])); - k = (IS_BIG_ENDIAN() ? val2[i] : val2[(len - 1) - i]); + j = (val1 == NULL ? 0 : (BIGENDIAN_CPU ? val1[i] : val1[(len - 1) - i])); + k = (BIGENDIAN_CPU ? val2[i] : val2[(len - 1) - i]); if (j > k) { @@ -256,11 +265,11 @@ static ChipZclStatus_t chipZclTypeSensitiveMemCopy(uint8_t * dest, uint8_t * src } if (src == NULL) { - MEMSET(dest, 0, size); + memset(dest, 0, size); } else { - MEMMOVE(dest, src, size); + memmove(dest, src, size); } } return CHIP_ZCL_STATUS_SUCCESS; @@ -573,7 +582,7 @@ static ChipZclStatus_t chipZclInternalWriteAttribute(ChipZclEndpointId_t endpoin uint8_t * minI = (uint8_t *) &(minv.defaultValue); uint8_t * maxI = (uint8_t *) &(maxv.defaultValue); // On big endian cpu with length 1 only the second byte counts -#if (BIGENDIAN_CPU) +#if BIGENDIAN_CPU if (dataLen == 1) { minI++; @@ -700,12 +709,12 @@ void chipZclEndpointInit(void) { uint8_t ep; - uint8_t fixedEndpoints[] = FIXED_ENDPOINT_ARRAY; - uint16_t fixedProfileIds[] = FIXED_PROFILE_IDS; - uint16_t fixedDeviceIds[] = FIXED_DEVICE_IDS; - uint8_t fixedDeviceVersions[] = FIXED_DEVICE_VERSIONS; - uint8_t fixedChipZclEndpointTypes[] = FIXED_ENDPOINT_TYPES; - uint8_t fixedNetworks[] = FIXED_NETWORKS; + static const uint8_t fixedEndpoints[] = FIXED_ENDPOINT_ARRAY; + static const uint16_t fixedProfileIds[] = FIXED_PROFILE_IDS; + static const uint16_t fixedDeviceIds[] = FIXED_DEVICE_IDS; + static const uint8_t fixedDeviceVersions[] = FIXED_DEVICE_VERSIONS; + static const uint8_t fixedChipZclEndpointTypes[] = FIXED_ENDPOINT_TYPES; + static const uint8_t fixedNetworks[] = FIXED_NETWORKS; chipZclEndpointCounter = FIXED_ENDPOINT_COUNT; for (ep = 0; ep < FIXED_ENDPOINT_COUNT; ep++) diff --git a/src/app/plugin/tests/cluster-cmd-on-off-test.c b/src/app/plugin/tests/cluster-cmd-on-off-test.c index c9791633bbcec1..c3e41743d042cb 100644 --- a/src/app/plugin/tests/cluster-cmd-on-off-test.c +++ b/src/app/plugin/tests/cluster-cmd-on-off-test.c @@ -99,11 +99,9 @@ int testClusterCmdOnOff(void) return 1; } - uint8_t * rawBuffer = chipZclBufferPointer(buffer); - uint16_t bufferLength = chipZclBufferDataLength(buffer); bool onOff; - printf("Buffer for processing is ready, length: %d\n", bufferLength); + printf("Buffer for processing is ready, length: %d\n", chipZclBufferDataLength(buffer)); // Make sure initial value of the attribute is false. status = chipZclReadAttribute(1, &chipZclClusterOnOffServerSpec, CHIP_ZCL_CLUSTER_ON_OFF_SERVER_ATTRIBUTE_ON_OFF, &onOff, @@ -116,7 +114,7 @@ int testClusterCmdOnOff(void) // At this point, we have a buffer encoded with the command context that contains the command. // So we will be testing top-level entry API. - status = chipZclProcessIncoming(rawBuffer, bufferLength); + status = chipZclProcessIncoming(buffer); if (status == CHIP_ZCL_STATUS_SUCCESS) { diff --git a/src/ble/BleConfig.h b/src/ble/BleConfig.h index 31395343392af6..4e421ec5317866 100644 --- a/src/ble/BleConfig.h +++ b/src/ble/BleConfig.h @@ -63,26 +63,6 @@ // clang-format off -/** - * @def BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - * - * @brief - * This boolean configuration option is (1) if the obsolescent interfaces - * of the BLE layer that now reside elsewhere, for example, in the chip System - * Layer or the INET layer, are aliased for transitional purposes. - * - */ -#ifndef BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#define BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES 0 -#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -#if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#if !CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#error "REQUIRED: if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES then CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES!" -#endif // !CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -#include -#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES /** * @def BLE_LAYER_NUM_BLE_ENDPOINTS diff --git a/src/ble/BleLayer.am b/src/ble/BleLayer.am index 5ca82360fa89cb..1c3d0f5dfc7bae 100644 --- a/src/ble/BleLayer.am +++ b/src/ble/BleLayer.am @@ -29,18 +29,18 @@ CHIP_BUILD_BLE_LAYER_SOURCE_FILES = \ @top_builddir@/src/ble/BleLayer.cpp \ @top_builddir@/src/ble/BleUUID.cpp \ @top_builddir@/src/ble/BLEEndPoint.cpp \ - @top_builddir@/src/ble/BtpEngine.cpp \ + @top_builddir@/src/ble/BtpEngine.cpp \ $(NULL) CHIP_BUILD_BLE_LAYER_HEADER_FILES = \ - CHIPBleServiceData.h \ - BLEEndPoint.h \ - Ble.h \ - BleApplicationDelegate.h \ - BleConfig.h \ - BleError.h \ - BleLayer.h \ - BlePlatformDelegate.h \ - BleUUID.h \ - BtpEngine.h \ + @top_builddir@/src/ble/CHIPBleServiceData.h \ + @top_builddir@/src/ble/BLEEndPoint.h \ + @top_builddir@/src/ble/Ble.h \ + @top_builddir@/src/ble/BleApplicationDelegate.h \ + @top_builddir@/src/ble/BleConfig.h \ + @top_builddir@/src/ble/BleError.h \ + @top_builddir@/src/ble/BleLayer.h \ + @top_builddir@/src/ble/BlePlatformDelegate.h \ + @top_builddir@/src/ble/BleUUID.h \ + @top_builddir@/src/ble/BtpEngine.h \ $(NULL) diff --git a/src/ble/BleLayer.h b/src/ble/BleLayer.h index 59758f0a1928a7..8634eb1d7105a6 100644 --- a/src/ble/BleLayer.h +++ b/src/ble/BleLayer.h @@ -63,10 +63,6 @@ #include #include -#if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#include -#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - namespace chip { namespace Ble { @@ -252,11 +248,6 @@ class DLL_EXPORT BleLayer BleLayer(void); BLE_ERROR Init(BlePlatformDelegate * platformDelegate, BleApplicationDelegate * appDelegate, chip::System::Layer * systemLayer); - -#if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - BLE_ERROR Init(BlePlatformDelegate * platformDelegate, BleApplicationDelegate * appDelegate, Inet::InetLayer * inetLayer); -#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - BLE_ERROR Shutdown(void); BLE_ERROR NewBleEndPoint(BLEEndPoint ** retEndPoint, BLE_CONNECTION_OBJECT connObj, BleRole role, bool autoClose); @@ -356,14 +347,6 @@ class DLL_EXPORT BleLayer static BleTransportProtocolVersion GetHighestSupportedProtocolVersion(const BleTransportCapabilitiesRequestMessage & reqMsg); }; -#if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -inline BLE_ERROR BleLayer::Init(BlePlatformDelegate * aPlatformDelegate, BleApplicationDelegate * aAppDelegate, - Inet::InetLayer * aInetLayer) -{ - return Init(aPlatformDelegate, aAppDelegate, aInetLayer->SystemLayer()); -} -#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - } /* namespace Ble */ } /* namespace chip */ diff --git a/src/chipcli/chiptool.cpp b/src/chipcli/chiptool.cpp deleted file mode 100644 index a8553f0c918144..00000000000000 --- a/src/chipcli/chiptool.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * - * Copyright (c) 2020 Project CHIP Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES 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 "chiptool_command_manager.h" - -static int match_command(const char * command_name, const char * name) -{ - return !strncmp(command_name, name, strlen(name)); -} - -static int help(int argc, char ** argv) -{ - chiptool_command_t * cmd = NULL; - for (cmd = commands; cmd->c_name != NULL; cmd++) - { - ChipLogDetail(chipTool, "%s\t%s\n", cmd->c_name, cmd->c_help); - } -} - -static int usage(const char * prog_name) -{ - ChipLogDetail(chipTool, - "Usage: %s [-h] [command] [opt ...]\n" - "%s commands are:\n", - prog_name, prog_name); - help(0, NULL); - return 2; -} - -static int execute_command(int argc, char ** argv) -{ - if (argc == 0) - { - return -1; - } - const chiptool_command_t * command_to_execute = NULL; - bool found = false; - - for (command_to_execute = commands; command_to_execute->c_name; command_to_execute++) - { - if (match_command(command_to_execute->c_name, argv[0])) - { - found = true; - break; - } - } - - if (found) - { - ChipLogDetail(chipTool, "Executing cmd %s\n", command_to_execute->c_name); - return command_to_execute->c_func(argc, argv); - } - else - { - help(0, NULL); - } -} - -int main(int argc, char ** argv) -{ - int result = 0; - int do_help = 0; - int ch; - - /* Remember my name. */ - char * prog_name = strrchr(argv[0], '/'); - prog_name = prog_name ? prog_name + 1 : argv[0]; - /* Do getopt stuff for global options. */ - optind = 1; - - while ((ch = getopt(argc, argv, "h")) != -1) - { - switch (ch) - { - case 'h': - do_help = 1; - break; - - case '?': - default: - return usage(prog_name); - } - } - - argc -= optind; - argv += optind; - - if (do_help) - { - /* Munge argc/argv so that argv[0] is something. */ - help(0, NULL); - } - else if (argc > 0) - { - execute_command(argc, argv); - } - else - { - result = usage(prog_name); - } - return result; -} diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 7bee55d52cfc06..0e23ebb14d4944 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -51,19 +51,19 @@ using namespace chip::Encoding; ChipDeviceController::ChipDeviceController() { - mState = kState_NotInitialized; - AppState = NULL; - mConState = kConnectionState_NotConnected; - mDeviceCon = NULL; - mCurReqMsg = NULL; - mOnError = NULL; - mDeviceAddr = IPAddress::Any; - mDevicePort = CHIP_PORT; - mDeviceId = 0; + mState = kState_NotInitialized; + AppState = NULL; + mConState = kConnectionState_NotConnected; + mDeviceCon = NULL; + mCurReqMsg = NULL; + mOnError = NULL; + mDeviceAddr = IPAddress::Any; + mDevicePort = CHIP_PORT; + mLocalDeviceId = 0; memset(&mOnComplete, 0, sizeof(mOnComplete)); } -CHIP_ERROR ChipDeviceController::Init() +CHIP_ERROR ChipDeviceController::Init(NodeId localNodeId) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -88,7 +88,8 @@ CHIP_ERROR ChipDeviceController::Init() } SuccessOrExit(err); - mState = kState_Initialized; + mState = kState_Initialized; + mLocalDeviceId = localNodeId; exit: return err; @@ -106,7 +107,6 @@ CHIP_ERROR ChipDeviceController::Shutdown() if (mDeviceCon != NULL) { - mDeviceCon->Close(); delete mDeviceCon; mDeviceCon = NULL; } @@ -119,12 +119,14 @@ CHIP_ERROR ChipDeviceController::Shutdown() mConState = kConnectionState_NotConnected; memset(&mOnComplete, 0, sizeof(mOnComplete)); - mOnError = NULL; + mOnError = NULL; + mMessageNumber = 0; + mRemoteDeviceId.ClearValue(); return err; } -CHIP_ERROR ChipDeviceController::ConnectDevice(uint64_t deviceId, IPAddress deviceAddr, void * appReqState, +CHIP_ERROR ChipDeviceController::ConnectDevice(NodeId remoteDeviceId, IPAddress deviceAddr, void * appReqState, MessageReceiveHandler onMessageReceived, ErrorHandler onError, uint16_t devicePort) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -134,70 +136,90 @@ CHIP_ERROR ChipDeviceController::ConnectDevice(uint64_t deviceId, IPAddress devi return CHIP_ERROR_INCORRECT_STATE; } - mDeviceId = deviceId; - mDeviceAddr = deviceAddr; - mDevicePort = devicePort; - mAppReqState = appReqState; - mDeviceCon = new StatefulTransport(this); + mRemoteDeviceId = Optional::Value(remoteDeviceId); + mDeviceAddr = deviceAddr; + mDevicePort = devicePort; + mAppReqState = appReqState; + mDeviceCon = new SecureSessionMgr(); - mDeviceCon->Init(mInetLayer); - err = mDeviceCon->Connect(mDeviceAddr, mDevicePort); + err = mDeviceCon->Init(mLocalDeviceId, mInetLayer, Transport::UdpListenParameters().SetAddressType(deviceAddr.Type())); SuccessOrExit(err); - mDeviceCon->SetMessageReceiveHandler(OnReceiveMessage); - mDeviceCon->SetReceiveErrorHandler(OnReceiveError); + err = mDeviceCon->Connect(remoteDeviceId, Transport::PeerAddress::UDP(deviceAddr, devicePort)); + SuccessOrExit(err); + + mDeviceCon->SetMessageReceiveHandler(OnReceiveMessage, this); mOnComplete.Response = onMessageReceived; mOnError = onError; - mConState = kConnectionState_Connected; + mConState = kConnectionState_Connected; + mMessageNumber = 1; exit: - if (err != CHIP_NO_ERROR && mDeviceCon != NULL) + + if (err != CHIP_NO_ERROR) { - mDeviceCon->Close(); - delete mDeviceCon; - mDeviceCon = NULL; + if (mDeviceCon != NULL) + { + delete mDeviceCon; + mDeviceCon = NULL; + } } return err; } -CHIP_ERROR ChipDeviceController::GetDeviceAddress(IPAddress * deviceAddr, uint16_t * devicePort) +CHIP_ERROR ChipDeviceController::ManualKeyExchange(const unsigned char * remote_public_key, const size_t public_key_length, + const unsigned char * local_private_key, const size_t private_key_length) { CHIP_ERROR err = CHIP_NO_ERROR; - if (mState != kState_Initialized || mConState != kConnectionState_Connected) + if (!IsConnected() || mDeviceCon == NULL) { return CHIP_ERROR_INCORRECT_STATE; } - if (deviceAddr) - { - *deviceAddr = mDeviceAddr; - } - if (devicePort) - { - *devicePort = mDevicePort; - } + err = mDeviceCon->ManualKeyExchange(remote_public_key, public_key_length, local_private_key, private_key_length); + SuccessOrExit(err); + mConState = kConnectionState_SecureConnected; + +exit: + return err; +} + +CHIP_ERROR ChipDeviceController::PopulatePeerAddress(Transport::PeerAddress & peerAddress) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrExit(IsSecurelyConnected(), err = CHIP_ERROR_INCORRECT_STATE); + + peerAddress.SetIPAddress(mDeviceAddr); + peerAddress.SetPort(mDevicePort); + peerAddress.SetTransportType(Transport::Type::kUdp); +exit: return err; } bool ChipDeviceController::IsConnected() { - return kState_Initialized && mConState == kConnectionState_Connected; + return kState_Initialized && (mConState == kConnectionState_Connected || mConState == kConnectionState_SecureConnected); +} + +bool ChipDeviceController::IsSecurelyConnected() +{ + return kState_Initialized && mConState == kConnectionState_SecureConnected; } CHIP_ERROR ChipDeviceController::DisconnectDevice() { CHIP_ERROR err = CHIP_NO_ERROR; - if (mState != kState_Initialized || mConState != kConnectionState_Connected) + if (!IsConnected()) { return CHIP_ERROR_INCORRECT_STATE; } - err = mDeviceCon->Close(); delete mDeviceCon; mDeviceCon = NULL; mConState = kConnectionState_NotConnected; @@ -206,13 +228,15 @@ CHIP_ERROR ChipDeviceController::DisconnectDevice() CHIP_ERROR ChipDeviceController::SendMessage(void * appReqState, PacketBuffer * buffer) { - CHIP_ERROR err = CHIP_ERROR_INCORRECT_STATE; + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrExit(mRemoteDeviceId.HasValue(), err = CHIP_ERROR_INCORRECT_STATE); mAppReqState = appReqState; - if (mConState == kConnectionState_Connected) - { - err = mDeviceCon->SendMessage(buffer); - } + VerifyOrExit(IsSecurelyConnected(), err = CHIP_ERROR_INCORRECT_STATE); + + err = mDeviceCon->SendMessage(mRemoteDeviceId.Value(), buffer); +exit: return err; } @@ -298,21 +322,24 @@ void ChipDeviceController::ClearRequestState() } } -void ChipDeviceController::OnReceiveMessage(StatefulTransport * con, PacketBuffer * msgBuf, const IPPacketInfo * pktInfo) +void ChipDeviceController::OnReceiveMessage(const MessageHeader & header, const IPPacketInfo & pktInfo, + System::PacketBuffer * msgBuf, ChipDeviceController * mgr) { - ChipDeviceController * mgr = con->State(); - if (mgr->mConState == kConnectionState_Connected && mgr->mOnComplete.Response != NULL && pktInfo != NULL) + if (header.GetSourceNodeId().HasValue()) { - mgr->mOnComplete.Response(mgr, mgr->mAppReqState, msgBuf, pktInfo); + if (!mgr->mRemoteDeviceId.HasValue()) + { + ChipLogProgress(Controller, "Learned remote device id"); + mgr->mRemoteDeviceId = header.GetSourceNodeId(); + } + else if (mgr->mRemoteDeviceId != header.GetSourceNodeId()) + { + ChipLogError(Controller, "Received message from an unexpected source node id."); + } } -} - -void ChipDeviceController::OnReceiveError(StatefulTransport * con, CHIP_ERROR err, const IPPacketInfo * pktInfo) -{ - ChipDeviceController * mgr = con->State(); - if (mgr->mConState == kConnectionState_Connected && mgr->mOnError != NULL && pktInfo != NULL) + if (mgr->IsSecurelyConnected() && mgr->mOnComplete.Response != NULL) { - mgr->mOnError(mgr, mgr->mAppReqState, err, pktInfo); + mgr->mOnComplete.Response(mgr, mgr->mAppReqState, msgBuf, &pktInfo); } } diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index 34e0e477720125..510d79fe67eedf 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -32,7 +32,8 @@ #include #include #include -#include +#include +#include namespace chip { namespace DeviceController { @@ -43,7 +44,7 @@ extern "C" { typedef void (*CompleteHandler)(ChipDeviceController * deviceController, void * appReqState); typedef void (*ErrorHandler)(ChipDeviceController * deviceController, void * appReqState, CHIP_ERROR err, const IPPacketInfo * pktInfo); -typedef void (*MessageReceiveHandler)(ChipDeviceController * deviceController, void * appReqState, PacketBuffer * payload, +typedef void (*MessageReceiveHandler)(ChipDeviceController * deviceController, void * appReqState, System::PacketBuffer * payload, const IPPacketInfo * pktInfo); }; @@ -54,7 +55,7 @@ class DLL_EXPORT ChipDeviceController void * AppState; - CHIP_ERROR Init(); + CHIP_ERROR Init(NodeId localDeviceId); CHIP_ERROR Shutdown(); // ----- Connection Management ----- @@ -62,7 +63,7 @@ class DLL_EXPORT ChipDeviceController * @brief * Connect to a CHIP device at a given address and an optional port * - * @param[in] deviceId A device identifier. Currently unused and can be set to any value + * @param[in] remoteDeviceId The remote device Id. * @param[in] deviceAddr The IPAddress of the requested Device * @param[in] appReqState Application specific context to be passed back when a message is received or on error * @param[in] onMessageReceived Callback for when a message is received @@ -70,18 +71,32 @@ class DLL_EXPORT ChipDeviceController * @param[in] devicePort [Optional] The CHIP Device's port, defaults to CHIP_PORT * @return CHIP_ERROR The connection status */ - CHIP_ERROR ConnectDevice(uint64_t deviceId, IPAddress deviceAddr, void * appReqState, MessageReceiveHandler onMessageReceived, - ErrorHandler onError, uint16_t devicePort = CHIP_PORT); + CHIP_ERROR ConnectDevice(NodeId remoteDeviceId, IPAddress deviceAddr, void * appReqState, + MessageReceiveHandler onMessageReceived, ErrorHandler onError, uint16_t devicePort = CHIP_PORT); /** * @brief - * Get the address and port of a connected device + * The keypair for the secure channel. This is a utility function that will be used + * until we have automatic key exchange in place. The function is useful only for + * example applications for now. It will eventually be removed. * - * @param[out] deviceAddr The IPAddress of the connected device - * @param[out] devicePort The port of the econnected device + * @param remote_public_key A pointer to peer's public key + * @param public_key_length Length of remote_public_key + * @param local_private_key A pointer to local private key + * @param private_key_length Length of local_private_key + * @return CHIP_ERROR The result of key derivation + */ + CHIP_ERROR ManualKeyExchange(const unsigned char * remote_public_key, const size_t public_key_length, + const unsigned char * local_private_key, const size_t private_key_length); + + /** + * @brief + * Get the PeerAddress of a connected peer + * + * @param[inout] peerAddress The PeerAddress object which will be populated with the details of the connected peer * @return CHIP_ERROR An error if there's no active connection */ - CHIP_ERROR GetDeviceAddress(IPAddress * deviceAddr, uint16_t * devicePort); + CHIP_ERROR PopulatePeerAddress(Transport::PeerAddress & peerAddress); /** * @brief @@ -99,6 +114,14 @@ class DLL_EXPORT ChipDeviceController */ bool IsConnected(); + /** + * @brief + * Check if the connection is active and security context is established + * + * @return bool If the connection is active and security context is established + */ + bool IsSecurelyConnected(); + // ----- Messaging ----- /** * @brief @@ -108,7 +131,7 @@ class DLL_EXPORT ChipDeviceController * @param[in] buffer The Data Buffer to trasmit to the deviec * @return CHIP_ERROR The return status */ - CHIP_ERROR SendMessage(void * appReqState, PacketBuffer * buffer); + CHIP_ERROR SendMessage(void * appReqState, System::PacketBuffer * buffer); // ----- IO ----- /** @@ -130,8 +153,6 @@ class DLL_EXPORT ChipDeviceController CHIP_ERROR GetLayers(Layer ** systemLayer, InetLayer ** inetLayer); private: - using StatefulTransport = StatefulUdpTransport; - enum { kState_NotInitialized = 0, @@ -140,13 +161,14 @@ class DLL_EXPORT ChipDeviceController enum ConnectionState { - kConnectionState_NotConnected = 0, - kConnectionState_Connected = 1, + kConnectionState_NotConnected = 0, + kConnectionState_Connected = 1, + kConnectionState_SecureConnected = 2, }; System::Layer * mSystemLayer; Inet::InetLayer * mInetLayer; - StatefulTransport * mDeviceCon; + SecureSessionMgr * mDeviceCon; ConnectionState mConState; void * mAppReqState; @@ -158,17 +180,19 @@ class DLL_EXPORT ChipDeviceController } mOnComplete; ErrorHandler mOnError; - PacketBuffer * mCurReqMsg; + System::PacketBuffer * mCurReqMsg; - uint64_t mDeviceId; + NodeId mLocalDeviceId; IPAddress mDeviceAddr; uint16_t mDevicePort; + Optional mRemoteDeviceId; + uint32_t mMessageNumber = 0; void ClearRequestState(); void ClearOpState(); - static void OnReceiveMessage(StatefulTransport * con, PacketBuffer * msgBuf, const IPPacketInfo * pktInfo); - static void OnReceiveError(StatefulTransport * con, CHIP_ERROR err, const IPPacketInfo * pktInfo); + static void OnReceiveMessage(const MessageHeader & header, const IPPacketInfo & pktInfo, System::PacketBuffer * msgBuf, + ChipDeviceController * controller); }; } // namespace DeviceController diff --git a/src/crypto/Crypto.am b/src/crypto/Crypto.am new file mode 100644 index 00000000000000..9211b3e9111f40 --- /dev/null +++ b/src/crypto/Crypto.am @@ -0,0 +1,44 @@ +# +# +# Copyright (c) 2020 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Description: +# This file lists the files that go into the CHIP crypto +# library. +# + +# for all configs +CHIP_BUILD_CRYPTO_SOURCE_FILES = \ + $(NULL) + +CHIP_BUILD_CRYPTO_HEADER_FILES = \ + @top_builddir@/src/crypto/CHIPCryptoPAL.h \ + $(NULL) + +# source by config +if CHIP_CRYPTO_OPENSSL +CHIP_BUILD_CRYPTO_SOURCE_FILES += \ + @top_builddir@/src/crypto/CHIPCryptoPALOpenSSL.cpp \ + $(NULL) +endif + +if CHIP_CRYPTO_MBEDTLS +CHIP_BUILD_CRYPTO_SOURCE_FILES += \ + @top_builddir@/src/crypto/CHIPCryptoPALmbedTLS.cpp \ + $(NULL) +endif + diff --git a/src/crypto/Makefile.am b/src/crypto/Makefile.am index 616b7ea2ef43d3..b7f23cd536bd4c 100644 --- a/src/crypto/Makefile.am +++ b/src/crypto/Makefile.am @@ -23,14 +23,14 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am + SUBDIRS = tests lib_LIBRARIES = libChipCrypto.a -libChipCrypto_adir = $(includedir)/crypto +libChipCrypto_adir = $(includedir)/crypto libChipCrypto_a_CPPFLAGS = \ - $(LWIP_CPPFLAGS) \ $(NLASSERT_CPPFLAGS) \ -I$(top_srcdir)/src \ -I$(top_srcdir)/src/lib \ @@ -38,23 +38,14 @@ libChipCrypto_a_CPPFLAGS = \ -I$(top_srcdir)/src/include/ \ $(NULL) -libChipCrypto_a_SOURCES = \ - $(NULL) - -dist_libChipCrypto_a_HEADERS = \ - CHIPCryptoPAL.h \ - $(NULL) +include Crypto.am -if CHIP_CRYPTO_OPENSSL -libChipCrypto_a_SOURCES += \ - CHIPCryptoPALOpenSSL.cpp \ +libChipCrypto_a_SOURCES = \ + $(CHIP_BUILD_CRYPTO_SOURCE_FILES) \ $(NULL) -endif -if CHIP_CRYPTO_MBEDTLS -libChipCrypto_a_SOURCES += \ - CHIPCryptoPALmbedTLS.cpp \ +dist_libChipCrypto_a_HEADERS = \ + $(CHIP_BUILD_CRYPTO_HEADER_FILES) \ $(NULL) -endif include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/crypto/tests/CHIPCryptoPALTest.cpp b/src/crypto/tests/CHIPCryptoPALTest.cpp index 112d800b8abc0a..4290788b0b75ee 100644 --- a/src/crypto/tests/CHIPCryptoPALTest.cpp +++ b/src/crypto/tests/CHIPCryptoPALTest.cpp @@ -784,12 +784,10 @@ static void TestECDH_SampleInputVectors(nlTestSuite * inSuite, void * inContext) namespace chip { namespace Logging { -void Log(uint8_t module, uint8_t category, const char * format, ...) +void LogV(uint8_t module, uint8_t category, const char * format, va_list argptr) { - va_list argptr; - va_start(argptr, format); + (void) module, (void) category; vfprintf(stderr, format, argptr); - va_end(argptr); } } // namespace Logging } // namespace chip diff --git a/src/crypto/tests/Makefile.am b/src/crypto/tests/Makefile.am index 236412c96d7bc8..23169d4938009f 100644 --- a/src/crypto/tests/Makefile.am +++ b/src/crypto/tests/Makefile.am @@ -92,7 +92,7 @@ check_SCRIPTS = \ TESTS_ENVIRONMENT = \ $(NULL) -if CHIP_TARGET_STYLE_EMBEDDED +if CHIP_DEVICE_LAYER_TARGET_ESP32 check_SCRIPTS += \ qemu_crypto_tests.sh \ @@ -104,13 +104,13 @@ TESTS_ENVIRONMENT += \ QEMU_TEST_TARGET=libCryptoLayerTests.a \ $(NULL) -else # CHIP_TARGET_STYLE_EMBEDDED +else # CHIP_DEVICE_LAYER_TARGET_ESP32 check_PROGRAMS += \ TestCryptoPAL \ $(NULL) -endif # CHIP_TARGET_STYLE_EMBEDDED +endif # CHIP_DEVICE_LAYER_TARGET_ESP32 # Test applications and scripts that should be built and run when the # 'check' target is run. diff --git a/src/darwin/CHIPTool/CHIPTool.xcodeproj/project.pbxproj b/src/darwin/CHIPTool/CHIPTool.xcodeproj/project.pbxproj index f5d3f20ea47baa..b827fb65e6f35c 100644 --- a/src/darwin/CHIPTool/CHIPTool.xcodeproj/project.pbxproj +++ b/src/darwin/CHIPTool/CHIPTool.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 0C47BE4424885B97005E97F6 /* CHIPViewControllerBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C47BE4324885B97005E97F6 /* CHIPViewControllerBase.m */; }; + 0CA0E0CF248599BB009087B9 /* OnOffViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CA0E0CE248599BB009087B9 /* OnOffViewController.m */; }; 991DC091247747F500C13860 /* EchoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 991DC090247747F500C13860 /* EchoViewController.m */; }; B204A621244E1D0600C7C0E1 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B204A620244E1D0600C7C0E1 /* AppDelegate.m */; }; B204A624244E1D0600C7C0E1 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B204A623244E1D0600C7C0E1 /* SceneDelegate.m */; }; @@ -44,15 +46,19 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 991DC08F247747F500C13860 /* EchoViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = EchoViewController.h; path = UI/EchoViewController.h; sourceTree = ""; }; - 991DC090247747F500C13860 /* EchoViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = EchoViewController.m; path = UI/EchoViewController.m; sourceTree = ""; }; + 0C47BE4324885B97005E97F6 /* CHIPViewControllerBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CHIPViewControllerBase.m; sourceTree = ""; }; + 0CA0E0CD248599BB009087B9 /* OnOffViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OnOffViewController.h; sourceTree = ""; }; + 0CA0E0CE248599BB009087B9 /* OnOffViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OnOffViewController.m; sourceTree = ""; }; + 515C401824868BF0004C4DB3 /* CHIPViewControllerBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHIPViewControllerBase.h; sourceTree = ""; }; + 991DC08F247747F500C13860 /* EchoViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EchoViewController.h; sourceTree = ""; }; + 991DC090247747F500C13860 /* EchoViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EchoViewController.m; sourceTree = ""; }; B204A61C244E1D0600C7C0E1 /* CHIPTool.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CHIPTool.app; sourceTree = BUILT_PRODUCTS_DIR; }; B204A61F244E1D0600C7C0E1 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = UI/AppDelegate.h; sourceTree = ""; }; B204A620244E1D0600C7C0E1 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = UI/AppDelegate.m; sourceTree = ""; }; B204A622244E1D0600C7C0E1 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SceneDelegate.h; path = UI/SceneDelegate.h; sourceTree = ""; }; B204A623244E1D0600C7C0E1 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SceneDelegate.m; path = UI/SceneDelegate.m; sourceTree = ""; }; - B204A625244E1D0600C7C0E1 /* QRCodeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = QRCodeViewController.h; path = UI/QRCodeViewController.h; sourceTree = ""; }; - B204A626244E1D0600C7C0E1 /* QRCodeViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = QRCodeViewController.m; path = UI/QRCodeViewController.m; sourceTree = ""; }; + B204A625244E1D0600C7C0E1 /* QRCodeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QRCodeViewController.h; sourceTree = ""; }; + B204A626244E1D0600C7C0E1 /* QRCodeViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QRCodeViewController.m; sourceTree = ""; }; B204A62B244E1D0700C7C0E1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; B204A630244E1D0700C7C0E1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B204A631244E1D0700C7C0E1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -82,6 +88,33 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 0C79937824858B3B0047A373 /* QRCode */ = { + isa = PBXGroup; + children = ( + B204A625244E1D0600C7C0E1 /* QRCodeViewController.h */, + B204A626244E1D0600C7C0E1 /* QRCodeViewController.m */, + ); + path = QRCode; + sourceTree = ""; + }; + 0C79937924858B4F0047A373 /* Echo client */ = { + isa = PBXGroup; + children = ( + 991DC08F247747F500C13860 /* EchoViewController.h */, + 991DC090247747F500C13860 /* EchoViewController.m */, + ); + path = "Echo client"; + sourceTree = ""; + }; + 0CA0E0D0248599C4009087B9 /* OnOffCluster */ = { + isa = PBXGroup; + children = ( + 0CA0E0CD248599BB009087B9 /* OnOffViewController.h */, + 0CA0E0CE248599BB009087B9 /* OnOffViewController.m */, + ); + path = OnOffCluster; + sourceTree = ""; + }; B20252DE2459EC7600F97062 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -112,19 +145,20 @@ B204A61E244E1D0600C7C0E1 /* CHIPTool */ = { isa = PBXGroup; children = ( + B204A62B244E1D0700C7C0E1 /* Assets.xcassets */, B2F53AEC245B0D190010745E /* Main.storyboard */, B2F53AE9245B0D140010745E /* LaunchScreen.storyboard */, B204A630244E1D0700C7C0E1 /* Info.plist */, - B204A631244E1D0700C7C0E1 /* main.m */, - B204A61F244E1D0600C7C0E1 /* AppDelegate.h */, + 515C401824868BF0004C4DB3 /* CHIPViewControllerBase.h */, + 0C47BE4324885B97005E97F6 /* CHIPViewControllerBase.m */, B204A620244E1D0600C7C0E1 /* AppDelegate.m */, + B204A631244E1D0700C7C0E1 /* main.m */, B204A622244E1D0600C7C0E1 /* SceneDelegate.h */, B204A623244E1D0600C7C0E1 /* SceneDelegate.m */, - B204A625244E1D0600C7C0E1 /* QRCodeViewController.h */, - B204A626244E1D0600C7C0E1 /* QRCodeViewController.m */, - B204A62B244E1D0700C7C0E1 /* Assets.xcassets */, - 991DC08F247747F500C13860 /* EchoViewController.h */, - 991DC090247747F500C13860 /* EchoViewController.m */, + B204A61F244E1D0600C7C0E1 /* AppDelegate.h */, + 0C79937824858B3B0047A373 /* QRCode */, + 0C79937924858B4F0047A373 /* Echo client */, + 0CA0E0D0248599C4009087B9 /* OnOffCluster */, ); path = CHIPTool; sourceTree = ""; @@ -243,6 +277,8 @@ B204A621244E1D0600C7C0E1 /* AppDelegate.m in Sources */, B204A632244E1D0700C7C0E1 /* main.m in Sources */, B204A624244E1D0600C7C0E1 /* SceneDelegate.m in Sources */, + 0C47BE4424885B97005E97F6 /* CHIPViewControllerBase.m in Sources */, + 0CA0E0CF248599BB009087B9 /* OnOffViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -315,8 +351,10 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_HARDENED_RUNTIME = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -374,8 +412,10 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_HARDENED_RUNTIME = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -398,8 +438,9 @@ B204A64C244E1D0700C7C0E1 /* Debug iOS */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = arm64; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; @@ -408,18 +449,22 @@ "$(inherited)", "@executable_path/Frameworks", ); + ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.chip.CHIPTool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = "arm64 armv4t armv5 armv6 armv6m armv7 armv7em armv7f armv7k armv7m armv7s xscale"; }; name = "Debug iOS"; }; B204A64D244E1D0700C7C0E1 /* Release iOS */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = arm64; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; @@ -428,10 +473,13 @@ "$(inherited)", "@executable_path/Frameworks", ); + ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.chip.CHIPTool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = "arm64 armv4t armv5 armv6 armv6m armv7 armv7em armv7f armv7k armv7m armv7s xscale"; }; name = "Release iOS"; }; diff --git a/src/darwin/CHIPTool/CHIPTool/CHIPViewControllerBase.h b/src/darwin/CHIPTool/CHIPTool/CHIPViewControllerBase.h new file mode 100644 index 00000000000000..4e3102bea9410b --- /dev/null +++ b/src/darwin/CHIPTool/CHIPTool/CHIPViewControllerBase.h @@ -0,0 +1,38 @@ +/** + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CHIPViewControllerBase : UIViewController + +@property (readwrite) BOOL useIncorrectKey; +@property (readwrite) BOOL useIncorrectKeyStateChanged; +@property (readwrite) CHIPDeviceController * chipController; +@property (weak, nonatomic) IBOutlet UILabel * resultLabel; +@property (weak, nonatomic) IBOutlet UISwitch * encryptionKeySwitch; + +- (void)reconnectIfNeeded; +- (void)dismissKeyboard; +- (void)postResult:(NSString *)result; +- (IBAction)encryptionKey:(id)sender; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/CHIPTool/CHIPTool/CHIPViewControllerBase.m b/src/darwin/CHIPTool/CHIPTool/CHIPViewControllerBase.m new file mode 100644 index 00000000000000..9cea6578ec2f3d --- /dev/null +++ b/src/darwin/CHIPTool/CHIPTool/CHIPViewControllerBase.m @@ -0,0 +1,214 @@ +/** + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "CHIPViewControllerBase.h" + +#import +#import + +#define RESULT_DISPLAY_DURATION 5.0 * NSEC_PER_SEC + +static NSString * const ipKey = @"ipk"; + +@interface CHIPViewControllerBase () + +@property (readwrite) dispatch_queue_t chipCallbackQueue; +@property (readwrite) BOOL reconnectOnForeground; + +@property (weak, nonatomic) IBOutlet UITextField * serverIPTextField; +@property (weak, nonatomic) IBOutlet UILabel * IPLabel; + +@end + +@implementation CHIPViewControllerBase + +- (NSString *)_getScannedIP +{ + return [[NSUserDefaults standardUserDefaults] stringForKey:ipKey]; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + // auto resize the results on screen + self.resultLabel.adjustsFontSizeToFitWidth = YES; + + // listen for taps to dismiss the keyboard + UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)]; + [self.view addGestureRecognizer:tap]; + + // initialize the device controller + __weak typeof(self) weakSelf = self; + dispatch_queue_t callbackQueue = dispatch_queue_create("com.zigbee.chip.example.callback", DISPATCH_QUEUE_SERIAL); + self.chipController = [CHIPDeviceController sharedController]; + [self.chipController registerCallbacks:callbackQueue + onMessage:^(NSData * _Nonnull message, NSString * _Nonnull ipAddress, UInt16 port) { + typeof(self) strongSelf = weakSelf; + NSString * strMessage = [[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding]; + [strongSelf postResult:[@"Echo Response: " stringByAppendingFormat:@"%@", strMessage]]; + } + onError:^(NSError * _Nonnull error) { + typeof(self) strongSelf = weakSelf; + [strongSelf postResult:[@"Error: " stringByAppendingString:error.localizedDescription]]; + }]; + + // need to restart connections on background/foreground transitions otherwise the socket can be closed without CHIP knowing + // about it. + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_appEnteredBackground:) + name:UISceneDidEnterBackgroundNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_appEnteringForeground:) + name:UISceneWillEnterForegroundNotification + object:nil]; + + BOOL shouldHide = NO; + if ([[self _getScannedIP] length] > 0) { + shouldHide = YES; + } + [self.serverIPTextField setHidden:shouldHide]; + [self.IPLabel setHidden:shouldHide]; + self.useIncorrectKey = NO; + self.useIncorrectKeyStateChanged = NO; +} + +- (void)_appEnteredBackground:(NSNotification *)notification +{ + if ([self.chipController isConnected]) { + [self.chipController disconnect:nil]; + self.reconnectOnForeground = YES; + } +} + +- (void)_appEnteringForeground:(NSNotification *)notification +{ + if (self.reconnectOnForeground) { + [self _connect]; + } +} + +- (void)_connect +{ + NSError * error; + + NSString * inputIPAddress = [[self _getScannedIP] length] > 0 ? [self _getScannedIP] : self.serverIPTextField.text; + + const unsigned char local_key_bytes[] = { 0x00, 0xd1, 0x90, 0xd9, 0xb3, 0x95, 0x1c, 0x5f, 0xa4, 0xe7, 0x47, 0x92, 0x5b, 0x0a, + 0xa9, 0xa7, 0xc1, 0x1c, 0xe7, 0x06, 0x10, 0xe2, 0xdd, 0x16, 0x41, 0x52, 0x55, 0xb7, 0xb8, 0x80, 0x8d, 0x87, 0xa1 }; + + const unsigned char peer_key_bytes[] = { 0x04, 0xe2, 0x07, 0x64, 0xff, 0x6f, 0x6a, 0x91, 0xd9, 0xc2, 0xc3, 0x0a, 0xc4, 0x3c, + 0x56, 0x4b, 0x42, 0x8a, 0xf3, 0xb4, 0x49, 0x29, 0x39, 0x95, 0xa2, 0xf7, 0x02, 0x8c, 0xa5, 0xce, 0xf3, 0xc9, 0xca, 0x24, + 0xc5, 0xd4, 0x5c, 0x60, 0x79, 0x48, 0x30, 0x3c, 0x53, 0x86, 0xd9, 0x23, 0xe6, 0x61, 0x1f, 0x5a, 0x3d, 0xdf, 0x9f, 0xdc, + 0x35, 0xea, 0xd0, 0xde, 0x16, 0x7e, 0x64, 0xde, 0x7f, 0x3c, 0xa6 }; + + const unsigned char local_incorrect_key_bytes[] = { 0xc6, 0x1a, 0x2f, 0x89, 0x36, 0x67, 0x2b, 0x26, 0x12, 0x47, 0x4f, 0x11, + 0x0e, 0x34, 0x15, 0x81, 0x81, 0x12, 0xfc, 0x36, 0xeb, 0x65, 0x61, 0x07, 0xaa, 0x63, 0xe8, 0xc5, 0x22, 0xac, 0x52, 0xa1 }; + + NSData * peer_key = [NSData dataWithBytes:peer_key_bytes length:sizeof(peer_key_bytes)]; + NSData * local_key = NULL; + if (!self.useIncorrectKey) { + NSLog(@"Using correct key"); + local_key = [NSData dataWithBytes:local_key_bytes length:sizeof(local_key_bytes)]; + } else { + NSLog(@"Using incorrect key"); + local_key = [NSData dataWithBytes:local_incorrect_key_bytes length:sizeof(local_incorrect_key_bytes)]; + } + + if ([self.chipController isConnected]) { + [self.chipController disconnect:nil]; + } + + BOOL didConnect = [self.chipController connect:inputIPAddress local_key:local_key peer_key:peer_key error:&error]; + if (!didConnect) { + [self postResult:[@"Error: " stringByAppendingString:error.localizedDescription]]; + } +} + +- (IBAction)encryptionKey:(id)sender +{ + if ([self.encryptionKeySwitch isOn]) { + self.useIncorrectKey = YES; + [self postResult:@"App will use incorrect key"]; + } else { + self.useIncorrectKey = NO; + [self postResult:@"App will use correct key"]; + } + self.useIncorrectKeyStateChanged = YES; +} + +- (void)dismissKeyboard +{ + [self.serverIPTextField resignFirstResponder]; +} + +- (void)reconnectIfNeeded +{ + // collect fields + NSString * inputIPAddress = self.serverIPTextField.text; + BOOL needsReconnect = NO; + // Don't rely on the text fields if we have scanned connection info from a QRCode + BOOL hasScannedConnectionInfo = ([[self _getScannedIP] length] > 0); + + // check the addr of the connected device + AddressInfo * addrInfo = [self.chipController getAddressInfo]; + if (addrInfo && !hasScannedConnectionInfo) { + // check if the addr changed + if (![addrInfo.ip isEqualToString:inputIPAddress]) { + NSError * error; + // stop current connection + if (![self.chipController disconnect:&error]) { + // post error + [self postResult:[@"Error: " stringByAppendingString:error.localizedDescription]]; + } + needsReconnect = YES; + } + } + + if (!addrInfo || needsReconnect || self.useIncorrectKeyStateChanged) { + [self _connect]; + self.useIncorrectKeyStateChanged = NO; + } +} + +- (void)postResult:(NSString *)result +{ + dispatch_async(dispatch_get_main_queue(), ^{ + self.resultLabel.text = result; + self.resultLabel.hidden = NO; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, RESULT_DISPLAY_DURATION), dispatch_get_main_queue(), ^{ + self.resultLabel.hidden = YES; + }); + }); +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + NSError * error = nil; + // This disconnect is needed to make sure the connection goes away. + // The VC deallocation doesnt sometimes happen right away. + // So if one goes back out of the VC and back in, and send an echo msg right away, then the first reponse at times gets dropped + // as the first VC was not deallocated on time. + [self.chipController disconnect:&error]; + if (error) { + NSLog(@"Error disconnecting on view disappearing %@", error); + } +} + +@end diff --git a/src/darwin/CHIPTool/CHIPTool/UI/EchoViewController.h b/src/darwin/CHIPTool/CHIPTool/Echo client/EchoViewController.h similarity index 89% rename from src/darwin/CHIPTool/CHIPTool/UI/EchoViewController.h rename to src/darwin/CHIPTool/CHIPTool/Echo client/EchoViewController.h index 040a04137680f7..7a6004b98446cc 100644 --- a/src/darwin/CHIPTool/CHIPTool/UI/EchoViewController.h +++ b/src/darwin/CHIPTool/CHIPTool/Echo client/EchoViewController.h @@ -15,11 +15,12 @@ * limitations under the License. */ +#import "CHIPViewControllerBase.h" #import NS_ASSUME_NONNULL_BEGIN -@interface EchoViewController : UIViewController +@interface EchoViewController : CHIPViewControllerBase - (IBAction)sendAction:(id)sender; diff --git a/src/darwin/CHIPTool/CHIPTool/Echo client/EchoViewController.m b/src/darwin/CHIPTool/CHIPTool/Echo client/EchoViewController.m new file mode 100644 index 00000000000000..7b7cb567919b50 --- /dev/null +++ b/src/darwin/CHIPTool/CHIPTool/Echo client/EchoViewController.m @@ -0,0 +1,72 @@ +/** + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "EchoViewController.h" + +#import +#import + +#define RESULT_DISPLAY_DURATION 5.0 * NSEC_PER_SEC + +@interface EchoViewController () + +@property (weak, nonatomic) IBOutlet UITextField * messageTextField; +@property (weak, nonatomic) IBOutlet UIButton * sendButton; + +@end + +@implementation EchoViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + // make the send button slightly prettier + self.sendButton.layer.cornerRadius = 5; + self.sendButton.clipsToBounds = YES; +} + +- (void)dismissKeyboard +{ + [super dismissKeyboard]; + [self.messageTextField resignFirstResponder]; +} + +- (IBAction)sendAction:(id)sender +{ + if (self.messageTextField.text.length == 0) { + [self postResult:@"Error: Message cannot be empty"]; + return; + } + + [self reconnectIfNeeded]; + + // send message + if ([self.chipController isConnected]) { + NSError * error; + BOOL didSend = [self.chipController sendMessage:[self.messageTextField.text dataUsingEncoding:NSUTF8StringEncoding] + error:&error]; + if (!didSend) { + [self postResult:[@"Error: " stringByAppendingString:error.localizedDescription]]; + } else { + [self postResult:@"Message Sent"]; + } + } else { + [self postResult:@"Controller not connected"]; + } +} +@end diff --git a/src/darwin/CHIPTool/CHIPTool/OnOffCluster/OnOffViewController.h b/src/darwin/CHIPTool/CHIPTool/OnOffCluster/OnOffViewController.h new file mode 100644 index 00000000000000..7cf5cc8a984bdf --- /dev/null +++ b/src/darwin/CHIPTool/CHIPTool/OnOffCluster/OnOffViewController.h @@ -0,0 +1,18 @@ +// +// OnOffViewController.h +// CHIPTool +// +// Created by Bhaskar on 6/1/20. +// Copyright © 2020 CHIP. All rights reserved. +// + +#import "CHIPViewControllerBase.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface OnOffViewController : CHIPViewControllerBase + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/CHIPTool/CHIPTool/OnOffCluster/OnOffViewController.m b/src/darwin/CHIPTool/CHIPTool/OnOffCluster/OnOffViewController.m new file mode 100644 index 00000000000000..9818c2193c468d --- /dev/null +++ b/src/darwin/CHIPTool/CHIPTool/OnOffCluster/OnOffViewController.m @@ -0,0 +1,73 @@ +// +// OnOffViewController.m +// CHIPTool +// +// Created by Bhaskar on 6/1/20. +// Copyright © 2020 CHIP. All rights reserved. +// + +#import "OnOffViewController.h" +#import + +@interface OnOffViewController () +@property (weak, nonatomic) IBOutlet UIButton * onButton; +@property (weak, nonatomic) IBOutlet UIButton * offButton; +@property (weak, nonatomic) IBOutlet UIButton * toggleButton; +@property (readwrite) CHIPOnOff * onOff; + +@end + +@implementation OnOffViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.onOff = [[CHIPOnOff alloc] initWithDeviceController:self.chipController]; + + // make the buttons slightly prettier + self.onButton.layer.cornerRadius = 5; + self.onButton.clipsToBounds = YES; + self.offButton.layer.cornerRadius = 5; + self.offButton.clipsToBounds = YES; + self.toggleButton.layer.cornerRadius = 5; + self.toggleButton.clipsToBounds = YES; +} + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ +- (IBAction)onButtonTapped:(id)sender +{ + [self reconnectIfNeeded]; + + [self.onOff lightOn]; + + [self postResult:@"Turning light on"]; +} + +- (IBAction)offButtonTapped:(id)sender +{ + [self reconnectIfNeeded]; + + [self.onOff lightOff]; + + [self postResult:@"Turning light off"]; +} + +- (IBAction)toggleButtonTapped:(id)sender +{ + [self reconnectIfNeeded]; + + [self.onOff toggleLight]; + + [self postResult:@"Toggling light"]; +} + +@end diff --git a/src/darwin/CHIPTool/CHIPTool/UI/QRCodeViewController.h b/src/darwin/CHIPTool/CHIPTool/QRCode/QRCodeViewController.h similarity index 95% rename from src/darwin/CHIPTool/CHIPTool/UI/QRCodeViewController.h rename to src/darwin/CHIPTool/CHIPTool/QRCode/QRCodeViewController.h index c26425f592a87e..ebfe0a85a3e122 100644 --- a/src/darwin/CHIPTool/CHIPTool/UI/QRCodeViewController.h +++ b/src/darwin/CHIPTool/CHIPTool/QRCode/QRCodeViewController.h @@ -25,6 +25,7 @@ @property (weak, nonatomic) IBOutlet UIButton * qrCodeButton; @property (weak, nonatomic) IBOutlet UIButton * doneQrCodeButton; @property (weak, nonatomic) IBOutlet UIButton * doneManualCodeButton; +@property (weak, nonatomic) IBOutlet UIButton * resetButton; @property (weak, nonatomic) IBOutlet UIActivityIndicatorView * activityIndicator; @property (weak, nonatomic) IBOutlet UILabel * errorLabel; @@ -42,4 +43,5 @@ - (IBAction)startScanningQRCode:(id)sender; - (IBAction)stopScanningQRCode:(id)sender; - (IBAction)enteredManualCode:(id)sender; +- (IBAction)resetView:(id)sender; @end diff --git a/src/darwin/CHIPTool/CHIPTool/UI/QRCodeViewController.m b/src/darwin/CHIPTool/CHIPTool/QRCode/QRCodeViewController.m similarity index 81% rename from src/darwin/CHIPTool/CHIPTool/UI/QRCodeViewController.m rename to src/darwin/CHIPTool/CHIPTool/QRCode/QRCodeViewController.m index 5acb86e671ed86..04a4cda61eb653 100644 --- a/src/darwin/CHIPTool/CHIPTool/UI/QRCodeViewController.m +++ b/src/darwin/CHIPTool/CHIPTool/QRCode/QRCodeViewController.m @@ -30,11 +30,16 @@ // The expected Vendor ID for CHIP demos // Spells CHIP on a dialer #define EXAMPLE_VENDOR_ID 3447 -#define EXAMPLE_VENDOR_TAG 1 +#define EXAMPLE_VENDOR_TAG_SSID 1 #define MAX_SSID_LEN 32 +#define EXAMPLE_VENDOR_TAG_IP 2 +#define MAX_IP_LEN 46 + #define NOT_APPLICABLE_STRING @"N/A" +static NSString * const ipKey = @"ipk"; + @interface QRCodeViewController () @property (nonatomic, strong) AVCaptureSession * captureSession; @@ -52,6 +57,8 @@ - (void)viewDidLoad _doneManualCodeButton.layer.cornerRadius = 5; _doneManualCodeButton.clipsToBounds = YES; + _resetButton.layer.cornerRadius = 5; + _resetButton.clipsToBounds = YES; _manualCodeTextField.keyboardType = UIKeyboardTypeNumberPad; UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)]; @@ -83,6 +90,8 @@ - (void)qrCodeInitialState if ([_activityIndicator isAnimating]) { [_activityIndicator stopAnimating]; } + // show the reset button if there's scanned data saved + _resetButton.hidden = ![self hasScannedConnectionInfo]; _qrCodeButton.hidden = NO; _doneQrCodeButton.hidden = YES; _activityIndicator.hidden = YES; @@ -137,6 +146,10 @@ - (void)showPayload:(CHIPSetupPayload *)payload decimalString:(nullable NSString [self->_activityIndicator stopAnimating]; self->_activityIndicator.hidden = YES; self->_errorLabel.hidden = YES; + // reset the view and remove any preferences that were stored from a previous scan + if ([self hasScannedConnectionInfo]) { + [[NSUserDefaults standardUserDefaults] removeObjectForKey:ipKey]; + } if (decimalString) { self->_manualCodeLabel.hidden = NO; @@ -165,20 +178,36 @@ - (void)showPayload:(CHIPSetupPayload *)payload decimalString:(nullable NSString self->_productID.text = [NSString stringWithFormat:@"%@", payload.productID]; } self->_setupPayloadView.hidden = NO; + self->_resetButton.hidden = NO; + NSLog(@"Payload vendorID %@", payload.vendorID); if ([payload.vendorID isEqualToNumber:[NSNumber numberWithInt:EXAMPLE_VENDOR_ID]]) { NSArray * optionalInfo = [payload getAllOptionalData:nil]; + NSLog(@"Count of payload info %@", @(optionalInfo.count)); for (CHIPOptionalQRCodeInfo * info in optionalInfo) { - NSNumber * tag = [CHIPSetupPayload vendorTag:info.tag error:nil]; - if (tag && tag.intValue == EXAMPLE_VENDOR_TAG) { - // If the vendor id and tag match the example values, there should be an ssid encoded - if ([info.infoType isEqualToNumber:[NSNumber numberWithInt:kOptionalQRCodeInfoTypeString]]) { - if ([info.stringValue length] > MAX_SSID_LEN) { - NSLog(@"Unexpected SSID String..."); - } else { - // show SoftAP detection - [self RequestConnectSoftAPWithSSID:info.stringValue]; + NSNumber * tag = info.tag; + if (tag) { + switch (tag.unsignedCharValue) { + case EXAMPLE_VENDOR_TAG_SSID: + if ([info.infoType isEqualToNumber:[NSNumber numberWithInt:kOptionalQRCodeInfoTypeString]]) { + if ([info.stringValue length] > MAX_SSID_LEN) { + NSLog(@"Unexpected SSID String..."); + } else { + // show SoftAP detection + [self RequestConnectSoftAPWithSSID:info.stringValue]; + } } + break; + case EXAMPLE_VENDOR_TAG_IP: + if ([info.infoType isEqualToNumber:[NSNumber numberWithInt:kOptionalQRCodeInfoTypeString]]) { + if ([info.stringValue length] > MAX_IP_LEN) { + NSLog(@"Unexpected IP String... %@", info.stringValue); + } else { + NSLog(@"Got IP String... %@", info.stringValue); + [[NSUserDefaults standardUserDefaults] setObject:info.stringValue forKey:ipKey]; + } + } + break; } } } @@ -197,6 +226,12 @@ - (void)RequestConnectSoftAPWithSSID:(NSString *)ssid [self presentViewController:alert animated:YES completion:nil]; } +- (BOOL)hasScannedConnectionInfo +{ + NSString * ipAddress = [[NSUserDefaults standardUserDefaults] stringForKey:ipKey]; + return (ipAddress.length > 0); +} + // MARK: QR Code - (BOOL)startScanning @@ -298,6 +333,14 @@ - (IBAction)stopScanningQRCode:(id)sender [self qrCodeInitialState]; } +- (IBAction)resetView:(id)sender +{ + // reset the view and remove any preferences that were stored from scanning the QRCode + [[NSUserDefaults standardUserDefaults] removeObjectForKey:ipKey]; + [self manualCodeInitialState]; + [self qrCodeInitialState]; +} + - (IBAction)enteredManualCode:(id)sender { NSString * decimalString = _manualCodeTextField.text; diff --git a/src/darwin/CHIPTool/CHIPTool/UI/AppDelegate.m b/src/darwin/CHIPTool/CHIPTool/UI/AppDelegate.m index 2496102b4081cf..b309b4495ce0ad 100644 --- a/src/darwin/CHIPTool/CHIPTool/UI/AppDelegate.m +++ b/src/darwin/CHIPTool/CHIPTool/UI/AppDelegate.m @@ -17,6 +17,8 @@ #import "AppDelegate.h" +static NSString * const ipKey = @"ipk"; + @interface AppDelegate () @end @@ -26,6 +28,8 @@ @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. + + [[NSUserDefaults standardUserDefaults] removeObjectForKey:ipKey]; return YES; } diff --git a/src/darwin/CHIPTool/CHIPTool/UI/Base.lproj/Main.storyboard b/src/darwin/CHIPTool/CHIPTool/UI/Base.lproj/Main.storyboard index 302525ce01aa6f..5468121cf3b87c 100644 --- a/src/darwin/CHIPTool/CHIPTool/UI/Base.lproj/Main.storyboard +++ b/src/darwin/CHIPTool/CHIPTool/UI/Base.lproj/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -16,7 +16,7 @@ - + - + - + + - + + + @@ -271,6 +290,7 @@ + @@ -278,11 +298,13 @@ + + @@ -295,6 +317,7 @@ + @@ -304,7 +327,7 @@ - + @@ -315,7 +338,7 @@ - + - - - - - - - - + + + + + + + + - + - + + + + - + - - - + - + - + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - + + + - - + - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/src/darwin/CHIPTool/CHIPTool/UI/EchoViewController.m b/src/darwin/CHIPTool/CHIPTool/UI/EchoViewController.m deleted file mode 100644 index d9980774afaad8..00000000000000 --- a/src/darwin/CHIPTool/CHIPTool/UI/EchoViewController.m +++ /dev/null @@ -1,168 +0,0 @@ -/** - * - * Copyright (c) 2020 Project CHIP Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 "EchoViewController.h" - -#import -#import - -#define RESULT_DISPLAY_DURATION 5.0 * NSEC_PER_SEC - -@interface EchoViewController () - -@property (readwrite) CHIPDeviceController * chipController; -@property (readwrite) dispatch_queue_t chipCallbackQueue; -@property (readwrite) BOOL reconnectOnForeground; - -@property (weak, nonatomic) IBOutlet UITextField * serverIPTextField; -@property (weak, nonatomic) IBOutlet UITextField * serverPortTextField; -@property (weak, nonatomic) IBOutlet UITextField * messageTextField; -@property (weak, nonatomic) IBOutlet UILabel * resultLabel; -@property (weak, nonatomic) IBOutlet UIButton * sendButton; - -@end - -@implementation EchoViewController - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - // listen for taps to dismiss the keyboard - UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_dismissKeyboard)]; - [self.view addGestureRecognizer:tap]; - - // make the send button slightly prettier - self.sendButton.layer.cornerRadius = 5; - self.sendButton.clipsToBounds = YES; - - // auto resize the results on screen - self.resultLabel.adjustsFontSizeToFitWidth = YES; - - self.chipCallbackQueue = dispatch_queue_create("com.zigbee.chip.example.callback", DISPATCH_QUEUE_SERIAL); - // initialize the device controller - self.chipController = [[CHIPDeviceController alloc] initWithCallbackQueue:self.chipCallbackQueue]; - - // need to restart connections on background/foreground transitions otherwise the socket can be closed without CHIP knowing - // about it. - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(_appEnteredBackground:) - name:UISceneDidEnterBackgroundNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(_appEnteringForeground:) - name:UISceneWillEnterForegroundNotification - object:nil]; -} - -- (void)_appEnteredBackground:(NSNotification *)notification -{ - if ([self.chipController isConnected]) { - [self.chipController disconnect:nil]; - self.reconnectOnForeground = YES; - } -} - -- (void)_appEnteringForeground:(NSNotification *)notification -{ - if (self.reconnectOnForeground) { - [self _connect]; - } -} - -- (void)_connect -{ - NSError * error; - NSString * inputIPAddress = self.serverIPTextField.text; - UInt16 inputPort = [self.serverPortTextField.text intValue]; - BOOL didConnect = [self.chipController connect:inputIPAddress - port:inputPort - error:&error - onMessage:^(NSData * _Nonnull message, NSString * _Nonnull ipAddress, UInt16 port) { - NSString * strMessage = [[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding]; - [self _postResult:[@"Echo Response: " stringByAppendingFormat:@"%@\nFrom: %@:%d", strMessage, ipAddress, port]]; - } - onError:^(NSError * _Nonnull error) { - [self _postResult:[@"Error: " stringByAppendingString:error.localizedDescription]]; - }]; - if (!didConnect) { - [self _postResult:[@"Error: " stringByAppendingString:error.localizedDescription]]; - } -} - -- (void)_dismissKeyboard -{ - [self.messageTextField resignFirstResponder]; - [self.serverIPTextField resignFirstResponder]; - [self.serverPortTextField resignFirstResponder]; -} - -- (void)_postResult:(NSString *)result -{ - dispatch_async(dispatch_get_main_queue(), ^{ - self.resultLabel.text = result; - self.resultLabel.hidden = NO; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, RESULT_DISPLAY_DURATION), dispatch_get_main_queue(), ^{ - self.resultLabel.hidden = YES; - }); - }); -} - -- (IBAction)sendAction:(id)sender -{ - // collect fields - NSString * inputIPAddress = self.serverIPTextField.text; - UInt16 inputPort = [self.serverPortTextField.text intValue]; - BOOL needsReconnect = NO; - - if (self.messageTextField.text.length == 0) { - [self _postResult:@"Error: Message cannot be empty"]; - return; - } - - // check the addr of the connected device - AddressInfo * addrInfo = [self.chipController getAddressInfo]; - if (addrInfo) { - // check if the addr changed - if (![addrInfo.ip isEqualToString:inputIPAddress] || addrInfo.port != inputPort) { - NSError * error; - // stop current connection - if (![self.chipController disconnect:&error]) { - // post error - [self _postResult:[@"Error: " stringByAppendingString:error.localizedDescription]]; - } - needsReconnect = YES; - } - } - - if (!addrInfo || needsReconnect) { - [self _connect]; - } - - // send message - if ([self.chipController isConnected]) { - NSError * error; - BOOL didSend = [self.chipController sendMessage:[self.messageTextField.text dataUsingEncoding:NSUTF8StringEncoding] - error:&error]; - if (!didSend) { - [self _postResult:[@"Error: " stringByAppendingString:error.localizedDescription]]; - } else { - [self _postResult:@"Message Sent"]; - } - } -} -@end diff --git a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj index fd5e72933e37eb..08c7f56e9cfceb 100644 --- a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj @@ -7,9 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 2C4DF09E248B2C60009307CB /* libmbedtls.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C4DF09D248B2C60009307CB /* libmbedtls.a */; }; + 515C401C2486BF43004C4DB3 /* CHIPOnOff.mm in Sources */ = {isa = PBXBuildFile; fileRef = 515C401A2486BF43004C4DB3 /* CHIPOnOff.mm */; }; + 515C401D2486BF43004C4DB3 /* CHIPOnOff.h in Headers */ = {isa = PBXBuildFile; fileRef = 515C401B2486BF43004C4DB3 /* CHIPOnOff.h */; settings = {ATTRIBUTES = (Public, ); }; }; 991DC0842475F45400C13860 /* CHIPDeviceController.h in Headers */ = {isa = PBXBuildFile; fileRef = 991DC0822475F45400C13860 /* CHIPDeviceController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 991DC0852475F45400C13860 /* CHIPEchoClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 991DC0832475F45400C13860 /* CHIPEchoClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 991DC0882475F47D00C13860 /* CHIPEchoClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = 991DC0862475F47D00C13860 /* CHIPEchoClient.mm */; }; 991DC0892475F47D00C13860 /* CHIPDeviceController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 991DC0872475F47D00C13860 /* CHIPDeviceController.mm */; }; 991DC08B247704DC00C13860 /* CHIPLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = 991DC08A247704DC00C13860 /* CHIPLogging.h */; }; B20252972459E34F00F97062 /* CHIP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B202528D2459E34F00F97062 /* CHIP.framework */; }; @@ -40,9 +41,10 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 2C4DF09D248B2C60009307CB /* libmbedtls.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmbedtls.a; path = lib/libmbedtls.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 515C401A2486BF43004C4DB3 /* CHIPOnOff.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPOnOff.mm; sourceTree = ""; }; + 515C401B2486BF43004C4DB3 /* CHIPOnOff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHIPOnOff.h; sourceTree = ""; }; 991DC0822475F45400C13860 /* CHIPDeviceController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPDeviceController.h; sourceTree = ""; }; - 991DC0832475F45400C13860 /* CHIPEchoClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPEchoClient.h; sourceTree = ""; }; - 991DC0862475F47D00C13860 /* CHIPEchoClient.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPEchoClient.mm; sourceTree = ""; }; 991DC0872475F47D00C13860 /* CHIPDeviceController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPDeviceController.mm; sourceTree = ""; }; 991DC08A247704DC00C13860 /* CHIPLogging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPLogging.h; sourceTree = ""; }; B202528D2459E34F00F97062 /* CHIP.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CHIP.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -71,6 +73,7 @@ buildActionMask = 2147483647; files = ( BA09EB43247477BA00605257 /* libCHIP.a in Frameworks */, + 2C4DF09E248B2C60009307CB /* libmbedtls.a in Frameworks */, BA09EB44247477BC00605257 /* libSetupPayload.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -120,10 +123,10 @@ B2E0D7AE245B0B5C003C5B48 /* CHIPQRCodeSetupPayloadParser.mm */, B2E0D7AF245B0B5C003C5B48 /* CHIPSetupPayload.h */, B2E0D7B0245B0B5C003C5B48 /* CHIPSetupPayload.mm */, - 991DC0832475F45400C13860 /* CHIPEchoClient.h */, - 991DC0862475F47D00C13860 /* CHIPEchoClient.mm */, 991DC0822475F45400C13860 /* CHIPDeviceController.h */, 991DC0872475F47D00C13860 /* CHIPDeviceController.mm */, + 515C401B2486BF43004C4DB3 /* CHIPOnOff.h */, + 515C401A2486BF43004C4DB3 /* CHIPOnOff.mm */, B20252912459E34F00F97062 /* Info.plist */, ); path = CHIP; @@ -141,6 +144,7 @@ BA09EB3E2474762900605257 /* Frameworks */ = { isa = PBXGroup; children = ( + 2C4DF09D248B2C60009307CB /* libmbedtls.a */, BA09EB3F2474762900605257 /* libCHIP.a */, BA09EB402474762900605257 /* libSetupPayload.a */, ); @@ -155,11 +159,11 @@ buildActionMask = 2147483647; files = ( 991DC0842475F45400C13860 /* CHIPDeviceController.h in Headers */, - 991DC0852475F45400C13860 /* CHIPEchoClient.h in Headers */, B2E0D7B2245B0B5C003C5B48 /* CHIPManualSetupPayloadParser.h in Headers */, B2E0D7B1245B0B5C003C5B48 /* CHIP.h in Headers */, B2E0D7B8245B0B5C003C5B48 /* CHIPSetupPayload.h in Headers */, B2E0D7B5245B0B5C003C5B48 /* CHIPQRCodeSetupPayloadParser.h in Headers */, + 515C401D2486BF43004C4DB3 /* CHIPOnOff.h in Headers */, 991DC08B247704DC00C13860 /* CHIPLogging.h in Headers */, B2E0D7B4245B0B5C003C5B48 /* CHIPError.h in Headers */, ); @@ -285,7 +289,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 991DC0882475F47D00C13860 /* CHIPEchoClient.mm in Sources */, + 515C401C2486BF43004C4DB3 /* CHIPOnOff.mm in Sources */, 991DC0892475F47D00C13860 /* CHIPDeviceController.mm in Sources */, B2E0D7B7245B0B5C003C5B48 /* CHIPQRCodeSetupPayloadParser.mm in Sources */, C4C222C02475A38700984173 /* CHIPLogging.mm in Sources */, @@ -452,6 +456,7 @@ "@loader_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(CHIP_PREFIX)/lib"; + ONLY_ACTIVE_ARCH = NO; OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.chip.CHIP; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -481,6 +486,7 @@ "@loader_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(CHIP_PREFIX)/lib"; + ONLY_ACTIVE_ARCH = NO; OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.chip.CHIP; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -588,6 +594,7 @@ BA09EB742474881D00605257 /* Debug iOS */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = arm64; CHIP_PREFIX = "$(BUILT_PRODUCTS_DIR)"; CHIP_ROOT = "$(SRCROOT)/../../.."; CODE_SIGN_STYLE = Automatic; @@ -607,11 +614,13 @@ "@loader_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(CHIP_PREFIX)/lib"; + ONLY_ACTIVE_ARCH = NO; OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.chip.CHIP; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = "arm64 armv4t armv5 armv6 armv6m armv7 armv7em armv7f armv7k armv7m armv7s xscale"; }; name = "Debug iOS"; }; @@ -692,6 +701,7 @@ BA09EB782474882200605257 /* Release iOS */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = arm64; CHIP_PREFIX = "$(BUILT_PRODUCTS_DIR)"; CHIP_ROOT = "$(SRCROOT)/../../.."; CODE_SIGN_STYLE = Automatic; @@ -711,11 +721,13 @@ "@loader_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(CHIP_PREFIX)/lib"; + ONLY_ACTIVE_ARCH = NO; OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.chip.CHIP; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = "arm64 armv4t armv5 armv6 armv6m armv7 armv7em armv7f armv7k armv7m armv7s xscale"; }; name = "Release iOS"; }; diff --git a/src/darwin/Framework/CHIP/CHIP.h b/src/darwin/Framework/CHIP/CHIP.h index 30e94c826f4f4b..9ef531bf52d39a 100644 --- a/src/darwin/Framework/CHIP/CHIP.h +++ b/src/darwin/Framework/CHIP/CHIP.h @@ -17,9 +17,9 @@ // pull together CHIP headers #import -#import #import #import +#import #import #import diff --git a/src/darwin/Framework/CHIP/CHIPDeviceController.h b/src/darwin/Framework/CHIP/CHIPDeviceController.h index f60a563713ebbd..3f796577b8ebc4 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceController.h +++ b/src/darwin/Framework/CHIP/CHIPDeviceController.h @@ -29,27 +29,47 @@ typedef void (^ControllerOnErrorBlock)(NSError * error); @interface AddressInfo : NSObject @property (readonly, copy) NSString * ip; -@property (readonly) UInt16 port; -- (instancetype)initWithIP:(NSString *)ip andPort:(UInt16)port; +- (instancetype)initWithIP:(NSString *)ip; @end @interface CHIPDeviceController : NSObject -- (nullable instancetype)initWithCallbackQueue:(dispatch_queue_t)appCallbackQueue; - (BOOL)connect:(NSString *)ipAddress - port:(UInt16)port - error:(NSError * __autoreleasing *)error - onMessage:(ControllerOnMessageBlock)onMessage - onError:(ControllerOnErrorBlock)onError; + local_key:(NSData *)local_key + peer_key:(NSData *)peer_key + error:(NSError * __autoreleasing *)error; - (nullable AddressInfo *)getAddressInfo; - (BOOL)sendMessage:(NSData *)message error:(NSError * __autoreleasing *)error; +// We can't include definitions of ChipZclClusterId_t and ChipZclCommandId_t +// here, but they're just integers, so pass them that way. +- (BOOL)sendCHIPCommand:(uint16_t)cluster command:(uint16_t)command; - (BOOL)disconnect:(NSError * __autoreleasing *)error; - (BOOL)isConnected; - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; +/** + * Return the single CHIPDeviceController we support existing. + */ ++ (CHIPDeviceController *)sharedController; + +/** + * Register callbacks for network activity. + * + * @param[in] appCallbackQueue the queue that should be used to deliver the + * message/error callbacks for this consumer. + * + * @param[in] onMessage the block to call when the controller gets a message + * from the network. + * + * @param[in] onError the block to call when there is a network error. + */ +- (void)registerCallbacks:(dispatch_queue_t)appCallbackQueue + onMessage:(ControllerOnMessageBlock)onMessage + onError:(ControllerOnErrorBlock)onError; + @end NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/CHIPDeviceController.mm b/src/darwin/Framework/CHIP/CHIPDeviceController.mm index f973508ff1b62a..95acfec85fb934 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceController.mm +++ b/src/darwin/Framework/CHIP/CHIPDeviceController.mm @@ -17,6 +17,12 @@ #import +extern "C" { +#include "chip-zcl/chip-zcl-buffer.h" +#include "chip-zcl/chip-zcl.h" +#include "gen/gen-command-id.h" +} // extern "C" + #import "CHIPDeviceController.h" #import "CHIPError.h" #import "CHIPLogging.h" @@ -28,12 +34,17 @@ static const char * const CHIP_WORK_QUEUE = "com.zigbee.chip.work"; static const char * const CHIP_SELECT_QUEUE = "com.zigbee.chip.select"; +// NOTE: Remote device ID is in sync with the echo server device id +// At some point, we may want to add an option to connect to a device without +// knowing its id, because the ID can be learned on the first response that is received. +constexpr chip::NodeId kLocalDeviceId = 112233; +constexpr chip::NodeId kRemoteDeviceId = 12344321; + @implementation AddressInfo -- (instancetype)initWithIP:(NSString *)ip andPort:(UInt16)port +- (instancetype)initWithIP:(NSString *)ip { if (self = [super init]) { _ip = ip; - _port = port; } return self; } @@ -41,30 +52,37 @@ - (instancetype)initWithIP:(NSString *)ip andPort:(UInt16)port @interface CHIPDeviceController () -// queue used for all interactions with the cpp chip controller and for all chip internal events -// try to run small jobs in this queue -@property (atomic, readonly) dispatch_queue_t chipWorkQueue; +@property (nonatomic, readonly, strong, nonnull) NSRecursiveLock * lock; + // queue used to call select on the system and inet layer fds., remove this with NW Framework. // primarily used to not block the work queue @property (atomic, readonly) dispatch_queue_t chipSelectQueue; // queue used to signal callbacks to the application -@property (readonly) dispatch_queue_t appCallbackQueue; -@property (readonly) ControllerOnMessageBlock onMessageHandler; -@property (readonly) ControllerOnErrorBlock onErrorHandler; +@property (readwrite) dispatch_queue_t appCallbackQueue; +@property (readwrite) ControllerOnMessageBlock onMessageHandler; +@property (readwrite) ControllerOnErrorBlock onErrorHandler; @property (readonly) chip::DeviceController::ChipDeviceController * cppController; @end @implementation CHIPDeviceController -- (instancetype)initWithCallbackQueue:(dispatch_queue_t)appCallbackQueue ++ (CHIPDeviceController *)sharedController +{ + static CHIPDeviceController * controller = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // initialize the device controller + controller = [[CHIPDeviceController alloc] init]; + }); + return controller; +} + +- (instancetype)init { if (self = [super init]) { - _appCallbackQueue = appCallbackQueue; - _chipWorkQueue = dispatch_queue_create(CHIP_WORK_QUEUE, DISPATCH_QUEUE_SERIAL); - if (!_chipWorkQueue) { - return nil; - } + + _lock = [[NSRecursiveLock alloc] init]; _chipSelectQueue = dispatch_queue_create(CHIP_SELECT_QUEUE, DISPATCH_QUEUE_SERIAL); if (!_chipSelectQueue) { @@ -77,14 +95,13 @@ - (instancetype)initWithCallbackQueue:(dispatch_queue_t)appCallbackQueue return nil; } - if (CHIP_NO_ERROR != _cppController->Init()) { + if (CHIP_NO_ERROR != _cppController->Init(kLocalDeviceId)) { CHIP_LOG_ERROR("Error: couldn't initialize c++ controller"); delete _cppController; _cppController = NULL; return nil; } } - return self; } @@ -146,23 +163,23 @@ - (void)_dispatchAsyncMessageBlock:(NSData *)data ipAddress:(NSString *)ipAddres } - (BOOL)connect:(NSString *)ipAddress - port:(UInt16)port + local_key:(NSData *)local_key + peer_key:(NSData *)peer_key error:(NSError * __autoreleasing *)error - onMessage:(ControllerOnMessageBlock)onMessage - onError:(ControllerOnErrorBlock)onError { - __block CHIP_ERROR err = CHIP_NO_ERROR; + CHIP_ERROR err = CHIP_NO_ERROR; // TODO maybe refactor // the work queue is being used for atomic access to chip's cpp controller // this could be done async but the error we care about is sync. However, I think this could be restructured such that // the request is fired async and that Block can then return an error to the caller. This function would then never error out. // the only drawback is that it complicates the api where the user must handle async errors on every function - dispatch_sync(self.chipWorkQueue, ^() { - chip::Inet::IPAddress addr; - chip::Inet::IPAddress::FromString([ipAddress UTF8String], addr); - err = self.cppController->ConnectDevice(0, addr, NULL, onMessageReceived, onInternalError, port); - }); + + [self.lock lock]; + chip::Inet::IPAddress addr; + chip::Inet::IPAddress::FromString([ipAddress UTF8String], addr); + err = self.cppController->ConnectDevice(0, addr, NULL, onMessageReceived, onInternalError, CHIP_PORT); + [self.lock unlock]; if (err != CHIP_NO_ERROR) { CHIP_LOG_ERROR("Error(%d): %@, connect failed", err, [CHIPError errorForCHIPErrorCode:err]); @@ -171,13 +188,18 @@ - (BOOL)connect:(NSString *)ipAddress } return NO; } + [self.lock lock]; + const unsigned char * local_key_bytes = (const unsigned char *) [local_key bytes]; + const unsigned char * peer_key_bytes = (const unsigned char *) [peer_key bytes]; + err = self.cppController->ManualKeyExchange(peer_key_bytes, peer_key.length, local_key_bytes, local_key.length); + [self.lock unlock]; - // Set the callback handlers - if (onMessage) { - _onMessageHandler = onMessage; - } - if (onError) { - _onErrorHandler = onError; + if (err != CHIP_NO_ERROR) { + CHIP_LOG_ERROR("Error(%d): %@, key exchange failed", err, [CHIPError errorForCHIPErrorCode:err]); + if (error) { + *error = [CHIPError errorForCHIPErrorCode:err]; + } + return NO; } // Start the IO pump @@ -188,38 +210,41 @@ - (BOOL)connect:(NSString *)ipAddress - (AddressInfo *)getAddressInfo { - __block CHIP_ERROR err = CHIP_NO_ERROR; - __block chip::IPAddress ipAddr; - __block uint16_t port; - - dispatch_sync(self.chipWorkQueue, ^() { - err = self.cppController->GetDeviceAddress(&ipAddr, &port); - }); + CHIP_ERROR err = CHIP_NO_ERROR; + chip::Transport::PeerAddress peerAddr = chip::Transport::PeerAddress::Uninitialized(); + [self.lock lock]; + err = self.cppController->PopulatePeerAddress(peerAddr); + [self.lock unlock]; + chip::IPAddress ipAddr = peerAddr.GetIPAddress(); + uint16_t port = peerAddr.GetPort(); if (err != CHIP_NO_ERROR) { return nil; } + + // ignore the unused port + (void) port; // A buffer big enough to hold ipv4 and ipv6 addresses char ipAddrStr[64]; ipAddr.ToString(ipAddrStr, sizeof(ipAddrStr)); NSString * ipAddress = [NSString stringWithUTF8String:ipAddrStr]; - return [[AddressInfo alloc] initWithIP:ipAddress andPort:port]; + return [[AddressInfo alloc] initWithIP:ipAddress]; } - (BOOL)sendMessage:(NSData *)message error:(NSError * __autoreleasing *)error { - __block CHIP_ERROR err = CHIP_NO_ERROR; + CHIP_ERROR err = CHIP_NO_ERROR; - dispatch_sync(self.chipWorkQueue, ^() { - size_t messageLen = [message length]; - const void * messageChars = [message bytes]; + [self.lock lock]; + size_t messageLen = [message length]; + const void * messageChars = [message bytes]; - chip::System::PacketBuffer * buffer = chip::System::PacketBuffer::NewWithAvailableSize(messageLen); - buffer->SetDataLength(messageLen); + chip::System::PacketBuffer * buffer = chip::System::PacketBuffer::NewWithAvailableSize(messageLen); + buffer->SetDataLength(messageLen); - memcpy(buffer->Start(), messageChars, messageLen); - err = self.cppController->SendMessage((__bridge void *) self, buffer); - }); + memcpy(buffer->Start(), messageChars, messageLen); + err = self.cppController->SendMessage((__bridge void *) self, buffer); + [self.lock unlock]; if (err != CHIP_NO_ERROR) { CHIP_LOG_ERROR("Error(%d): %@, send failed", err, [CHIPError errorForCHIPErrorCode:err]); @@ -231,13 +256,50 @@ - (BOOL)sendMessage:(NSData *)message error:(NSError * __autoreleasing *)error return YES; } +- (BOOL)sendCHIPCommand:(ChipZclClusterId_t)cluster command:(ChipZclCommandId_t)command +{ + CHIP_ERROR err = CHIP_NO_ERROR; + [self.lock lock]; + // FIXME: This needs a better buffersizing setup! + static const size_t bufferSize = 1024; + chip::System::PacketBuffer * buffer = chip::System::PacketBuffer::NewWithAvailableSize(bufferSize); + + ChipZclBuffer_t * zcl_buffer = (ChipZclBuffer_t *) buffer; + ChipZclCommandContext_t ctx = { + 1, // endpointId + cluster, // clusterId + true, // clusterSpecific + false, // mfgSpecific + 0, // mfgCode + command, // commandId + ZCL_DIRECTION_CLIENT_TO_SERVER, // direction + 0, // payloadStartIndex + nullptr, // request + nullptr // response + }; + chipZclEncodeZclHeader(zcl_buffer, &ctx); + + const size_t data_len = chipZclBufferDataLength(zcl_buffer); + + buffer->SetDataLength(data_len); + + err = self.cppController->SendMessage((__bridge void *) self, buffer); + [self.lock unlock]; + if (err != CHIP_NO_ERROR) { + CHIP_LOG_ERROR("Error(%d): %@, send failed", err, [CHIPError errorForCHIPErrorCode:err]); + return NO; + } + return YES; +} + - (BOOL)disconnect:(NSError * __autoreleasing *)error { - __block CHIP_ERROR err = CHIP_NO_ERROR; + CHIP_ERROR err = CHIP_NO_ERROR; - dispatch_sync(self.chipWorkQueue, ^() { - err = self.cppController->DisconnectDevice(); - }); + [self.lock lock]; + + err = self.cppController->DisconnectDevice(); + [self.lock unlock]; if (err != CHIP_NO_ERROR) { CHIP_LOG_ERROR("Error(%d): %@, disconnect failed", err, [CHIPError errorForCHIPErrorCode:err]); @@ -251,12 +313,11 @@ - (BOOL)disconnect:(NSError * __autoreleasing *)error - (BOOL)isConnected { - __block bool isConnected = false; + bool isConnected = false; - // the work queue is being used for atomic access to chip's cpp controller - dispatch_sync(self.chipWorkQueue, ^() { - isConnected = self.cppController->IsConnected(); - }); + [self.lock lock]; + isConnected = self.cppController->IsConnected(); + [self.lock unlock]; return isConnected ? YES : NO; } @@ -264,7 +325,13 @@ - (BOOL)isConnected // TODO kill this with fire (NW might implicitly replace this?) - (void)_serviceEvents { - dispatch_async(self.chipWorkQueue, ^() { + __weak typeof(self) weakSelf = self; + dispatch_async(self.chipSelectQueue, ^() { + typeof(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + __block fd_set readFDs, writeFDs, exceptFDs; struct timeval aSleepTime; int numFDs = 0; @@ -277,46 +344,57 @@ - (void)_serviceEvents chip::System::Layer * systemLayer = NULL; chip::Inet::InetLayer * inetLayer = NULL; // ask for the system and inet layers + + [self.lock lock]; self.cppController->GetLayers(&systemLayer, &inetLayer); - if (systemLayer != NULL && systemLayer->State() == chip::System::kLayerState_Initialized) + if (systemLayer != NULL && systemLayer->State() == chip::System::kLayerState_Initialized) { systemLayer->PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime); + } - if (inetLayer != NULL && inetLayer->State == chip::Inet::InetLayer::kState_Initialized) + if (inetLayer != NULL && inetLayer->State == chip::Inet::InetLayer::kState_Initialized) { inetLayer->PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime); + } + + [self.lock unlock]; + + int selectRes = select(numFDs, &readFDs, &writeFDs, &exceptFDs, const_cast(&aSleepTime)); + + [self.lock lock]; + if (!self.cppController->IsConnected()) { + [self.lock unlock]; + // cancel the loop, it'll restart the next time a connection is established + return; + } + systemLayer = NULL; + inetLayer = NULL; + self.cppController->GetLayers(&systemLayer, &inetLayer); - dispatch_async(self.chipSelectQueue, ^() { - int selectRes = select(numFDs, &readFDs, &writeFDs, &exceptFDs, const_cast(&aSleepTime)); - - dispatch_async(self.chipWorkQueue, ^() { - if (!self.cppController->IsConnected()) { - // cancel the loop, it'll restart the next time a connection is established - return; - } - chip::System::Layer * systemLayer = NULL; - chip::Inet::InetLayer * inetLayer = NULL; - self.cppController->GetLayers(&systemLayer, &inetLayer); - - if (systemLayer != NULL && systemLayer->State() == chip::System::kLayerState_Initialized) { - systemLayer->HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); - } - - if (inetLayer != NULL && inetLayer->State == chip::Inet::InetLayer::kState_Initialized) { - inetLayer->HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); - } - - [self _serviceEvents]; - }); - }); + if (systemLayer != NULL && systemLayer->State() == chip::System::kLayerState_Initialized) { + systemLayer->HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); + } + + if (inetLayer != NULL && inetLayer->State == chip::Inet::InetLayer::kState_Initialized) { + inetLayer->HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); + } + [self.lock unlock]; + [self _serviceEvents]; }); } -- (void)dealloc +- (void)registerCallbacks:appCallbackQueue onMessage:(ControllerOnMessageBlock)onMessage onError:(ControllerOnErrorBlock)onError { - // no more references to this object so immediately stop the inner controller - _cppController->Shutdown(); - delete _cppController; - _cppController = NULL; + self.appCallbackQueue = appCallbackQueue; + self.onMessageHandler = onMessage; + self.onErrorHandler = onError; } @end + +extern "C" { +// We have to have this empty callback, because the ZCL code links against it. +void chipZclPostAttributeChangeCallback(uint8_t endpoint, ChipZclClusterId clusterId, ChipZclAttributeId attributeId, uint8_t mask, + uint16_t manufacturerCode, uint8_t type, uint8_t size, uint8_t * value) +{ +} +} // extern "C" diff --git a/src/darwin/Framework/CHIP/CHIPEchoClient.h b/src/darwin/Framework/CHIP/CHIPEchoClient.h deleted file mode 100644 index 6dac4dfc45477c..00000000000000 --- a/src/darwin/Framework/CHIP/CHIPEchoClient.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * - * Copyright (c) 2020 Project CHIP Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 - * This file implements describes a temporary helper class to implement an echo client for CHIP - * This may be removed from the framework soon. - * - * Note: Do not use CHIPDeviceController directly if you use this class to initialize an EchoServer - * - * Sample Usage: - * _echoClient = [[CHIPEchoClient alloc] initWithServerAddress:@"192.168.1.22" port:8000]; - * if (_echoClient) { - * NSError * error; - * BOOL didStart = [_echoClient startWithFrequency:10 stopAfter:60 withError:&error]; - * if (!didStart) { - * os_log(OS_LOG_DEFAULT, "Failed to start echo client %@", error); - * } - * } - * - */ - -#ifndef ECHO_CLIENT_H -#define ECHO_CLIENT_H - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface CHIPEchoClient : NSObject - -/// Initialize the CHIPEchoClient with a server address -/// @param ipAddress A string representation of the server's IPAddress -/// @param port The server's port -- (nullable instancetype)initWithServerAddress:(NSString *)ipAddress port:(UInt16)port; - -/// Start the Echo Client with the default configuration -/// By default, the echo client will send an echo to the server every 10 seconds indefinitely -/// @param error Returns a CHIP Error Code if the client was unable to start -- (BOOL)startWithError:(NSError * __autoreleasing *)error; - -/// Start the Echo Client with a custom echo frequency -/// @param frequency The frequency, in seconds, of the client's echo messages -/// @param error Returns a CHIP Error Code if the client was unable to start -- (BOOL)startWithFrequency:(UInt32)frequency withError:(NSError * __autoreleasing *)error; - -/// Start the echo client with a custom echo frequency and automatically stop it after the specified time -/// @param frequency The frequency, in seconds, of the client's echo messages -/// @param stopAfter The number of seconds after which the echo client will stop sending messages -/// @param error Returns a CHIP Error Code if the client was unable to start -- (BOOL)startWithFrequency:(UInt32)frequency stopAfter:(UInt32)stopAfter withError:(NSError * __autoreleasing *)error; - -/// Stop the echo client -- (void)stop; - -- (instancetype)init NS_UNAVAILABLE; -+ (instancetype)new NS_UNAVAILABLE; - -@end - -NS_ASSUME_NONNULL_END - -#endif /* ECHO_CLIENT_H */ diff --git a/src/darwin/Framework/CHIP/CHIPEchoClient.mm b/src/darwin/Framework/CHIP/CHIPEchoClient.mm deleted file mode 100644 index 23b28120f21c32..00000000000000 --- a/src/darwin/Framework/CHIP/CHIPEchoClient.mm +++ /dev/null @@ -1,113 +0,0 @@ -/** - * - * Copyright (c) 2020 Project CHIP Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 -#import - -#import "CHIPDeviceController.h" -#import "CHIPEchoClient.h" - -static UInt32 const kDefaultFrequencySeconds = 10; -static NSString * const kEchoString = @"Hello from darwin!"; - -@interface CHIPEchoClient () - -@property (readonly) dispatch_queue_t echoCallbackQueue; -@property (readonly) dispatch_source_t sendTimer; -@property (readonly) CHIPDeviceController * chipController; - -@end - -@implementation CHIPEchoClient - -- (instancetype)initWithServerAddress:(NSString *)ipAddress port:(UInt16)port -{ - if (self = [super init]) { - NSError * error; - _echoCallbackQueue = dispatch_queue_create("com.zigbee.chip.echo", NULL); - _chipController = [[CHIPDeviceController alloc] initWithCallbackQueue:_echoCallbackQueue]; - - BOOL didInit = [_chipController connect:ipAddress - port:port - error:&error - onMessage:^(NSData * _Nonnull message, NSString * _Nonnull ipAddress, UInt16 port) { - NSData * sentMessage = [kEchoString dataUsingEncoding:NSUTF8StringEncoding]; - if ([sentMessage isEqualToData:message]) { - os_log(OS_LOG_DEFAULT, "Received expected echo response..."); - } else { - os_log(OS_LOG_DEFAULT, "Unexpected echo response. \nExpected: %@ \nReceived: %@", sentMessage, message); - } - } - onError:^(NSError * _Nonnull error) { - os_log(OS_LOG_DEFAULT, "Received error %@. Stopping...", error); - [self stop]; - }]; - - if (!didInit) { - os_log(OS_LOG_DEFAULT, "Failed to init with error %@", error); - return nil; - } - _sendTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _echoCallbackQueue); - if (!_sendTimer) { - os_log(OS_LOG_DEFAULT, "Could not create dispatch source"); - return nil; - } - } - return self; -} - -- (BOOL)startWithFrequency:(UInt32)frequency withError:(NSError * __autoreleasing *)error -{ - dispatch_source_set_timer(self.sendTimer, dispatch_walltime(NULL, 0), frequency * NSEC_PER_SEC, 1 * NSEC_PER_SEC); - dispatch_source_set_event_handler(self.sendTimer, ^{ - os_log(OS_LOG_DEFAULT, "Sending echo..."); - NSError * error; - if (![self.chipController sendMessage:[kEchoString dataUsingEncoding:NSUTF8StringEncoding] error:&error]) { - os_log(OS_LOG_DEFAULT, "Failed to send with error %@", error); - } - }); - dispatch_activate(self.sendTimer); - return YES; -} - -- (BOOL)startWithError:(NSError * __autoreleasing *)error -{ - return [self startWithFrequency:kDefaultFrequencySeconds withError:error]; -} - -- (BOOL)startWithFrequency:(UInt32)frequency stopAfter:(UInt32)stopAfter withError:(NSError * __autoreleasing *)error -{ - BOOL result = [self startWithFrequency:frequency withError:error]; - dispatch_after(dispatch_walltime(nil, stopAfter * NSEC_PER_SEC), self.echoCallbackQueue, ^{ - [self stop]; - }); - return result; -} - -- (void)stop -{ - dispatch_suspend(self->_sendTimer); - [self.chipController disconnect:nil]; - os_log(OS_LOG_DEFAULT, "Echo client stopped..."); -} - -- (void)dealloc -{ - [self stop]; -} - -@end diff --git a/src/darwin/Framework/CHIP/CHIPLogging.mm b/src/darwin/Framework/CHIP/CHIPLogging.mm index f91e7cf67f96a5..495921f2c8aec2 100644 --- a/src/darwin/Framework/CHIP/CHIPLogging.mm +++ b/src/darwin/Framework/CHIP/CHIPLogging.mm @@ -38,7 +38,7 @@ namespace Logging { /* - * void Log() + * void LogV() * * Description: * This routine writes to the Foundation log stream, the @@ -52,7 +52,7 @@ * the message is associated with. * aMsg - A NULL-terminated C string containing the message, * with C Standard I/O-style format specifiers, to log. - * ... - A variable argument list, corresponding to format + * aV - A variable argument list, corresponding to format * specifiers in the message. * * Output(s): @@ -61,13 +61,12 @@ * Returns: * N/A * + * @note There is a function called Log(), which takes a varargs intead of a + * va_list and will call this function as needed. It's declared and + * implemented in the core CHIP library. */ - void Log(uint8_t aModule, uint8_t aCategory, const char * aMsg, ...) + void LogV(uint8_t aModule, uint8_t aCategory, const char * aMsg, va_list aV) { - va_list v; - - va_start(v, aMsg); - if (IsCategoryEnabled(aCategory)) { char formattedMsg[512]; size_t prefixLen; @@ -80,12 +79,10 @@ void Log(uint8_t aModule, uint8_t aCategory, const char * aMsg, ...) if (prefixLen >= sizeof(formattedMsg)) prefixLen = sizeof(formattedMsg) - 1; - vsnprintf(formattedMsg + prefixLen, sizeof(formattedMsg) - prefixLen, aMsg, v); + vsnprintf(formattedMsg + prefixLen, sizeof(formattedMsg) - prefixLen, aMsg, aV); os_log(OS_LOG_DEFAULT, "%s", formattedMsg); } - - va_end(v); } } // namespace Logging diff --git a/src/darwin/Framework/CHIP/CHIPOnOff.h b/src/darwin/Framework/CHIP/CHIPOnOff.h new file mode 100644 index 00000000000000..f1a443b22c286a --- /dev/null +++ b/src/darwin/Framework/CHIP/CHIPOnOff.h @@ -0,0 +1,40 @@ +/** + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 CHIP_ONOFF_H +#define CHIP_ONOFF_H + +#import "CHIPDeviceController.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface CHIPOnOff : NSObject + +- (nullable instancetype)initWithDeviceController:(CHIPDeviceController *)deviceController; +- (BOOL)lightOn; +- (BOOL)lightOff; +- (BOOL)toggleLight; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* CHIP_DEVICE_CONTROLLER_H */ diff --git a/src/darwin/Framework/CHIP/CHIPOnOff.mm b/src/darwin/Framework/CHIP/CHIPOnOff.mm new file mode 100644 index 00000000000000..cca829b6693fe1 --- /dev/null +++ b/src/darwin/Framework/CHIP/CHIPOnOff.mm @@ -0,0 +1,59 @@ +/** + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +#import "CHIPOnOff.h" + +extern "C" { +#include "chip-zcl/chip-zcl.h" +#include "gen/gen-cluster-id.h" +#include "gen/gen-types.h" +} + +@interface CHIPOnOff () + +@property (readonly) CHIPDeviceController * deviceController; + +@end + +@implementation CHIPOnOff + +- (instancetype)initWithDeviceController:(CHIPDeviceController *)deviceController +{ + if (self = [super init]) { + _deviceController = deviceController; + } + return self; +} + +- (BOOL)lightOn +{ + return [self.deviceController sendCHIPCommand:CHIP_ZCL_CLUSTER_ON_OFF command:CHIP_ZCL_CLUSTER_ON_OFF_SERVER_COMMAND_ON]; +} + +- (BOOL)lightOff +{ + return [self.deviceController sendCHIPCommand:CHIP_ZCL_CLUSTER_ON_OFF command:CHIP_ZCL_CLUSTER_ON_OFF_SERVER_COMMAND_OFF]; +} + +- (BOOL)toggleLight +{ + return [self.deviceController sendCHIPCommand:CHIP_ZCL_CLUSTER_ON_OFF command:CHIP_ZCL_CLUSTER_ON_OFF_SERVER_COMMAND_TOGGLE]; +} + +@end diff --git a/src/darwin/Framework/CHIP/CHIPSetupPayload.h b/src/darwin/Framework/CHIP/CHIPSetupPayload.h index c5a9bbab4a35cb..69ae9f4114be09 100644 --- a/src/darwin/Framework/CHIP/CHIPSetupPayload.h +++ b/src/darwin/Framework/CHIP/CHIPSetupPayload.h @@ -45,7 +45,6 @@ typedef NS_ENUM(NSUInteger, OptionalQRCodeInfoType) { kOptionalQRCodeInfoTypeStr @property (nonatomic, strong) NSString * serialNumber; - (NSArray *)getAllOptionalData:(NSError * __autoreleasing *)error; -+ (NSNumber *)vendorTag:(NSNumber *)tagNumber error:(NSError * __autoreleasing *)error; #ifdef __cplusplus - (id)initWithSetupPayload:(chip::SetupPayload)setupPayload; diff --git a/src/darwin/Framework/CHIP/CHIPSetupPayload.mm b/src/darwin/Framework/CHIP/CHIPSetupPayload.mm index dcfcb9fc232607..8965a639e3783d 100644 --- a/src/darwin/Framework/CHIP/CHIPSetupPayload.mm +++ b/src/darwin/Framework/CHIP/CHIPSetupPayload.mm @@ -47,7 +47,7 @@ - (id)initWithSetupPayload:(chip::SetupPayload)setupPayload vector chipOptionalData = _chipSetupPayload.getAllOptionalData(); for (chip::OptionalQRCodeInfo chipInfo : chipOptionalData) { CHIPOptionalQRCodeInfo * info = [CHIPOptionalQRCodeInfo new]; - info.tag = [NSNumber numberWithUnsignedLongLong:chipInfo.tag]; + info.tag = [NSNumber numberWithUnsignedChar:chipInfo.tag]; switch (chipInfo.type) { case chip::optionalQRCodeInfoTypeString: info.infoType = [NSNumber numberWithInt:kOptionalQRCodeInfoTypeString]; @@ -67,17 +67,4 @@ - (id)initWithSetupPayload:(chip::SetupPayload)setupPayload } return allOptionalData; } - -+ (NSNumber *)vendorTag:(NSNumber *)tagNumber error:(NSError * __autoreleasing *)error -{ - uint64_t outTag; - NSNumber * vendorTag; - CHIP_ERROR chipError = chip::VendorTag(tagNumber.unsignedShortValue, outTag); - if (chipError == CHIP_NO_ERROR) { - vendorTag = [NSNumber numberWithUnsignedLongLong:outTag]; - } else if (error) { - *error = [CHIPError errorForCHIPErrorCode:chipError]; - } - return vendorTag; -} @end diff --git a/src/darwin/Framework/chip_xcode_build_connector.sh b/src/darwin/Framework/chip_xcode_build_connector.sh index ac95c435828979..2033c71ab6ea42 100755 --- a/src/darwin/Framework/chip_xcode_build_connector.sh +++ b/src/darwin/Framework/chip_xcode_build_connector.sh @@ -14,7 +14,7 @@ export PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig" set -ex -if [[ ${ARCHS} = arm64 ]]; then +if [ "$ARCHS" = arm64 ]; then target=aarch64-apple-darwin else target=$ARCHS-apple-darwin @@ -78,6 +78,7 @@ configure_OPTIONS+=( --with-chip-ble-project-includes=no --with-chip-warm-project-includes=no --with-chip-device-project-includes=no + --with-crypto=mbedtls ) ( @@ -94,7 +95,8 @@ configure_OPTIONS+=( done fi - make V=1 install-data # all the headers - make V=1 -C src/lib install # libCHIP.a - make V=1 -C src/setup_payload install # libSetupPayload.a + make V=1 install-data # all the headers + make V=1 -C src/lib install # libCHIP.a + make V=1 -C src/setup_payload install # libSetupPayload.a + make V=1 -C third_party/mbedtls install # libmbedtls.a ) diff --git a/src/include/platform/CHIPDeviceLayer.h b/src/include/platform/CHIPDeviceLayer.h index d2bf10b1069ca4..b8adde2fa956de 100644 --- a/src/include/platform/CHIPDeviceLayer.h +++ b/src/include/platform/CHIPDeviceLayer.h @@ -39,7 +39,7 @@ namespace chip { namespace DeviceLayer { -struct chipDeviceEvent; +struct ChipDeviceEvent; extern chip::System::Layer SystemLayer; extern Inet::InetLayer InetLayer; diff --git a/src/include/platform/ConfigurationManager.h b/src/include/platform/ConfigurationManager.h index a99cf3fe8bc16c..34c987f9746d06 100644 --- a/src/include/platform/ConfigurationManager.h +++ b/src/include/platform/ConfigurationManager.h @@ -41,7 +41,9 @@ class ConfigurationManagerImpl; namespace Internal { template class GenericPlatformManagerImpl; -} +template +class GenericPlatformManagerImpl_POSIX; +} // namespace Internal /** * Provides access to runtime and build-time configuration information for a chip device. @@ -129,6 +131,8 @@ class ConfigurationManager friend class ::chip::DeviceLayer::PlatformManagerImpl; template friend class ::chip::DeviceLayer::Internal::GenericPlatformManagerImpl; + template + friend class ::chip::DeviceLayer::Internal::GenericPlatformManagerImpl_POSIX; // Parentheses used to fix clang parsing issue with these declarations friend CHIP_ERROR ::chip::Platform::PersistedStorage::Read(::chip::Platform::PersistedStorage::Key key, uint32_t & value); friend CHIP_ERROR ::chip::Platform::PersistedStorage::Write(::chip::Platform::PersistedStorage::Key key, uint32_t value); diff --git a/src/include/platform/ThreadStackManager.h b/src/include/platform/ThreadStackManager.h index 4f09a067c4f72f..4fc0b41cb8d454 100644 --- a/src/include/platform/ThreadStackManager.h +++ b/src/include/platform/ThreadStackManager.h @@ -195,7 +195,7 @@ inline bool ThreadStackManager::HaveRouteToAddress(const IPAddress & destAddr) return static_cast(this)->_HaveRouteToAddress(destAddr); } -inline void ThreadStackManager::OnPlatformEvent(const chipDeviceEvent * event) +inline void ThreadStackManager::OnPlatformEvent(const ChipDeviceEvent * event) { static_cast(this)->_OnPlatformEvent(event); } diff --git a/src/include/platform/internal/CHIPDeviceLayerInternal.h b/src/include/platform/internal/CHIPDeviceLayerInternal.h index 16b306e730957e..1168ebff5384e4 100644 --- a/src/include/platform/internal/CHIPDeviceLayerInternal.h +++ b/src/include/platform/internal/CHIPDeviceLayerInternal.h @@ -20,17 +20,6 @@ #ifndef CHIP_DEVICE_INTERNAL_H #define CHIP_DEVICE_INTERNAL_H -namespace chip { -namespace Logging { - -enum -{ - kLogModule_DeviceLayer = 255, -}; - -} // namespace Logging -} // namespace chip - #include namespace chip { diff --git a/src/include/platform/internal/GenericConfigurationManagerImpl.ipp b/src/include/platform/internal/GenericConfigurationManagerImpl.ipp index 704fc28372adbb..220e9bfb493e47 100644 --- a/src/include/platform/internal/GenericConfigurationManagerImpl.ipp +++ b/src/include/platform/internal/GenericConfigurationManagerImpl.ipp @@ -30,6 +30,7 @@ #include #include #include +#include #if CHIP_DEVICE_CONFIG_ENABLE_THREAD #include @@ -65,6 +66,7 @@ CHIP_ERROR GenericConfigurationManagerImpl::_ConfigureChipStack() static char sPairingCodeBuf[ConfigurationManager::kMaxPairingCodeLength + 1]; +#if CHIP_CONFIG_ENABLE_FABRIC_STATE // Configure the CHIP FabricState object with the local node id. err = Impl()->_GetDeviceId(FabricState.LocalNodeId); SuccessOrExit(err); @@ -85,6 +87,7 @@ CHIP_ERROR GenericConfigurationManagerImpl::_ConfigureChipStack() err = CHIP_NO_ERROR; } SuccessOrExit(err); +#endif // CHIP_CONFIG_ENABLE_FABRIC_STATE #if CHIP_PROGRESS_LOGGING @@ -187,7 +190,7 @@ CHIP_ERROR GenericConfigurationManagerImpl::_GetSerialNumber(char * b VerifyOrExit(sizeof(CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER) <= bufSize, err = CHIP_ERROR_BUFFER_TOO_SMALL); memcpy(buf, CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER, sizeof(CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER)); serialNumLen = sizeof(CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER) - 1; - ChipProgress(DeviceLayer, "Serial Number not found; using default: %s", CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER); + ChipLogProgress(DeviceLayer, "Serial Number not found; using default: %s", CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER); err = CHIP_NO_ERROR; } #endif // CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER @@ -569,6 +572,7 @@ CHIP_ERROR GenericConfigurationManagerImpl::_StoreFabricId(uint64_t f { CHIP_ERROR err = CHIP_NO_ERROR; +#if CHIP_CONFIG_ENABLE_FABRIC_STATE if (fabricId != kFabricIdNotSpecified) { err = Impl()->WriteConfigValue(ImplClass::kConfigKey_FabricId, fabricId); @@ -583,6 +587,8 @@ CHIP_ERROR GenericConfigurationManagerImpl::_StoreFabricId(uint64_t f } exit: +#endif + return err; } @@ -752,7 +758,9 @@ CHIP_ERROR GenericConfigurationManagerImpl::_GetBLEDeviceIdentificati SuccessOrExit(err); deviceIdInfo.SetProductId(id); +#if CHIP_CONFIG_ENABLE_FABRIC_STATE deviceIdInfo.SetDeviceId(FabricState.LocalNodeId); +#endif deviceIdInfo.PairingStatus = Impl()->_IsPairedToAccount() ? Ble::ChipBLEDeviceIdentificationInfo::kPairingStatus_Paired @@ -802,6 +810,9 @@ bool GenericConfigurationManagerImpl::_IsFullyProvisioned() template CHIP_ERROR GenericConfigurationManagerImpl::_ComputeProvisioningHash(uint8_t * hashBuf, size_t hashBufSize) { + CHIP_ERROR err = CHIP_NO_ERROR; + +#if CHIP_DEVICE_CONFIG_LOG_PROVISIONING_HASH using HashAlgo = Platform::Security::SHA256; CHIP_ERROR err = CHIP_NO_ERROR; @@ -935,6 +946,8 @@ exit: Crypto::ClearSecretData(dataBuf, dataBufSize); Platform::Security::MemoryFree(dataBuf); } +#endif // CHIP_DEVICE_CONFIG_LOG_PROVISIONING_HASH + return err; } @@ -947,7 +960,9 @@ void GenericConfigurationManagerImpl::LogDeviceConfig() ChipLogProgress(DeviceLayer, "Device Configuration:"); +#if CHIP_CONFIG_ENABLE_FABRIC_STATE ChipLogProgress(DeviceLayer, " Device Id: %016" PRIX64, FabricState.LocalNodeId); +#endif { char serialNum[ConfigurationManager::kMaxSerialNumberLength + 1]; @@ -962,8 +977,7 @@ void GenericConfigurationManagerImpl::LogDeviceConfig() { vendorId = 0; } - ChipLogProgress(DeviceLayer, " Vendor Id: %" PRIu16 " (0x%" PRIX16 ")%s", - vendorId, vendorId, (vendorId == kChipVendor_CHIPLabs) ? " (CHIP)" : ""); + ChipLogProgress(DeviceLayer, " Vendor Id: %" PRIu16 " (0x%" PRIX16 ")", vendorId, vendorId); } { @@ -998,6 +1012,7 @@ void GenericConfigurationManagerImpl::LogDeviceConfig() } } +#if CHIP_CONFIG_ENABLE_FABRIC_STATE if (FabricState.FabricId != kFabricIdNotSpecified) { ChipLogProgress(DeviceLayer, " Fabric Id: %016" PRIX64, FabricState.FabricId); @@ -1008,6 +1023,7 @@ void GenericConfigurationManagerImpl::LogDeviceConfig() } ChipLogProgress(DeviceLayer, " Pairing Code: %s", (FabricState.PairingCode != NULL) ? FabricState.PairingCode : "(none)"); +#endif // CHIP_CONFIG_ENABLE_FABRIC_STATE } #endif // CHIP_PROGRESS_LOGGING diff --git a/src/include/platform/internal/GenericConnectivityManagerImpl_Thread.ipp b/src/include/platform/internal/GenericConnectivityManagerImpl_Thread.ipp index 334f109e8779d3..ad0675fc528334 100644 --- a/src/include/platform/internal/GenericConnectivityManagerImpl_Thread.ipp +++ b/src/include/platform/internal/GenericConnectivityManagerImpl_Thread.ipp @@ -27,6 +27,7 @@ #include #include +#include namespace chip { namespace DeviceLayer { @@ -35,25 +36,17 @@ namespace Internal { // Fully instantiate the generic implementation class in whatever compilation unit includes this file. template class GenericConnectivityManagerImpl_Thread; -template +template void GenericConnectivityManagerImpl_Thread::_OnPlatformEvent(const ChipDeviceEvent * event) { // Define some short-hands for various interesting event conditions. - const bool threadConnChanged = (event->Type == DeviceEventType::kThreadConnectivityChange && - event->ThreadConnectivityChange.Result != kConnectivity_NoChange); - const bool threadAddrChanged = (event->Type == DeviceEventType::kThreadStateChange && - event->ThreadStateChange.AddressChanged); - const bool threadNetDataChanged = (event->Type == DeviceEventType::kThreadStateChange && - event->ThreadStateChange.NetDataChanged); + const bool threadConnChanged = (event->Type == DeviceEventType::kThreadConnectivityChange && + event->ThreadConnectivityChange.Result != kConnectivity_NoChange); + const bool threadAddrChanged = (event->Type == DeviceEventType::kThreadStateChange && event->ThreadStateChange.AddressChanged); + const bool threadNetDataChanged = + (event->Type == DeviceEventType::kThreadStateChange && event->ThreadStateChange.NetDataChanged); const bool fabricMembershipChanged = (event->Type == DeviceEventType::kFabricMembershipChange); - // If the state of the Thread interface has changed, notify WARM accordingly. - if (threadConnChanged) - { - Warm::ThreadInterfaceStateChange(event->ThreadConnectivityChange.Result == kConnectivity_Established - ? Warm::kInterfaceStateUp : Warm::kInterfaceStateDown); - } - // If any of the above events has occurred, assess whether there's been a change in // service connectivity via Thread. if (threadConnChanged || threadAddrChanged || threadNetDataChanged || fabricMembershipChanged) @@ -62,7 +55,7 @@ void GenericConnectivityManagerImpl_Thread::_OnPlatformEvent(const Ch } } -template +template ConnectivityManager::ThreadMode GenericConnectivityManagerImpl_Thread::_GetThreadMode(void) { if (GetFlag(mFlags, kFlag_IsApplicationControlled)) @@ -70,19 +63,18 @@ ConnectivityManager::ThreadMode GenericConnectivityManagerImpl_Thread return ConnectivityManager::kThreadMode_ApplicationControlled; } - return ThreadStackMgrImpl().IsThreadEnabled() - ? ConnectivityManager::kThreadMode_Enabled - : ConnectivityManager::kThreadMode_Disabled; + return ThreadStackMgrImpl().IsThreadEnabled() ? ConnectivityManager::kThreadMode_Enabled + : ConnectivityManager::kThreadMode_Disabled; } -template +template CHIP_ERROR GenericConnectivityManagerImpl_Thread::_SetThreadMode(ConnectivityManager::ThreadMode val) { CHIP_ERROR err = CHIP_NO_ERROR; - VerifyOrExit(val == ConnectivityManager::kThreadMode_Enabled || - val == ConnectivityManager::kThreadMode_Disabled || - val == ConnectivityManager::kThreadMode_ApplicationControlled, err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(val == ConnectivityManager::kThreadMode_Enabled || val == ConnectivityManager::kThreadMode_Disabled || + val == ConnectivityManager::kThreadMode_ApplicationControlled, + err = CHIP_ERROR_INVALID_ARGUMENT); if (val == ConnectivityManager::kThreadMode_ApplicationControlled) { @@ -100,32 +92,16 @@ exit: return err; } -template +template void GenericConnectivityManagerImpl_Thread::UpdateServiceConnectivity(void) { bool haveServiceConnectivity = false; - // Evaluate whether there is connectivity to the CHIP service subnet via the Thread network. - // - // If the device is a member of a fabric, then service connectivity is assessed by checking if the - // local Thread stack has a route to the CHIP service subnet. This route will typically be a /48 - // CHIP ULA route that has been advertised by one or more CHIP border router devices in the Thread - // network. If no such route exists, then it is likely that there are no functioning CHIP border - // routers, and thus no route to the service via Thread. - // - // If the device is NOT a member of a fabric, then there can be no CHIP service connectivity via Thread. - // - if (FabricState.FabricId != kFabricIdNotSpecified) - { - const uint64_t fabricGlobalId = ChipFabricIdToIPv6GlobalId(FabricState.FabricId); - IPAddress serviceAddr = IPAddress::MakeULA(fabricGlobalId, chip::kChipSubnetId_Service, 1); - haveServiceConnectivity = ThreadStackMgr().HaveRouteToAddress(serviceAddr); - } - // If service connectivity via Thread has changed, post an event signaling the change. if (GetFlag(mFlags, kFlag_HaveServiceConnectivity) != haveServiceConnectivity) { - ChipLogProgress(DeviceLayer, "ConnectivityManager: Service connectivity via Thread %s", (haveServiceConnectivity) ? "ESTABLISHED" : "LOST"); + ChipLogProgress(DeviceLayer, "ConnectivityManager: Service connectivity via Thread %s", + (haveServiceConnectivity) ? "ESTABLISHED" : "LOST"); SetFlag(mFlags, kFlag_HaveServiceConnectivity, haveServiceConnectivity); @@ -133,17 +109,17 @@ void GenericConnectivityManagerImpl_Thread::UpdateServiceConnectivity ChipDeviceEvent event; event.Clear(); event.Type = DeviceEventType::kServiceConnectivityChange; - event.ServiceConnectivityChange.ViaThread.Result = (haveServiceConnectivity) ? kConnectivity_Established : kConnectivity_Lost; + event.ServiceConnectivityChange.ViaThread.Result = + (haveServiceConnectivity) ? kConnectivity_Established : kConnectivity_Lost; event.ServiceConnectivityChange.ViaTunnel.Result = kConnectivity_NoChange; - event.ServiceConnectivityChange.Overall.Result = (Impl()->HaveServiceConnectivityViaTunnel()) - ? kConnectivity_NoChange - : event.ServiceConnectivityChange.ViaThread.Result; + event.ServiceConnectivityChange.Overall.Result = (Impl()->HaveServiceConnectivityViaTunnel()) + ? kConnectivity_NoChange + : event.ServiceConnectivityChange.ViaThread.Result; PlatformMgr().PostEvent(&event); } } } - } // namespace Internal } // namespace DeviceLayer } // namespace chip diff --git a/src/include/platform/internal/GenericPlatformManagerImpl.ipp b/src/include/platform/internal/GenericPlatformManagerImpl.ipp index c21c4cae2675db..f43103862942c9 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl.ipp +++ b/src/include/platform/internal/GenericPlatformManagerImpl.ipp @@ -25,15 +25,15 @@ #ifndef GENERIC_PLATFORM_MANAGER_IMPL_IPP #define GENERIC_PLATFORM_MANAGER_IMPL_IPP -#include +#include #include -#include -#include #include -#include +#include +#include +#include -#include #include +#include namespace chip { namespace DeviceLayer { diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp index d6375ef5e145a1..b6184172652a3f 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp +++ b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp @@ -58,6 +58,14 @@ CHIP_ERROR GenericPlatformManagerImpl_POSIX::_InitChipStack(void) mChipStackLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + // Initialize the Configuration Manager object. + err = ConfigurationMgr().Init(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Configuration Manager initialization failed: %s", ErrorStr(err)); + } + SuccessOrExit(err); + // Call up to the base class _InitChipStack() to perform the bulk of the initialization. err = GenericPlatformManagerImpl::_InitChipStack(); SuccessOrExit(err); diff --git a/src/inet/EndPointBasis.h b/src/inet/EndPointBasis.h index 3e6e3fbf0a8ff3..ae108fa80edd12 100644 --- a/src/inet/EndPointBasis.h +++ b/src/inet/EndPointBasis.h @@ -35,6 +35,10 @@ #include +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#include +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + //--- Declaration of LWIP protocol control buffer structure names #if CHIP_SYSTEM_CONFIG_USE_LWIP #if INET_CONFIG_ENABLE_RAW_ENDPOINT @@ -68,6 +72,11 @@ class DLL_EXPORT EndPointBasis : public InetLayerBasis kBasisState_Closed = 0 /**< Encapsulated descriptor is not valid. */ }; +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + /** Test whether endpoint is a Network.framework endpoint */ + bool IsNetworkFrameworkEndPoint(void) const; +#endif + #if CHIP_SYSTEM_CONFIG_USE_SOCKETS /** Test whether endpoint is a POSIX socket */ bool IsSocketsEndPoint(void) const; @@ -82,6 +91,11 @@ class DLL_EXPORT EndPointBasis : public InetLayerBasis bool IsOpenEndPoint(void) const; protected: +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + nw_parameters_t mParameters; + IPAddressType mAddrType; /**< Protocol family, i.e. IPv4 or IPv6. */ +#endif + #if CHIP_SYSTEM_CONFIG_USE_SOCKETS int mSocket; /**< Encapsulated socket descriptor. */ IPAddressType mAddrType; /**< Protocol family, i.e. IPv4 or IPv6. */ @@ -125,6 +139,13 @@ class DLL_EXPORT EndPointBasis : public InetLayerBasis void InitEndPointBasis(InetLayer & aInetLayer, void * aAppState = NULL); }; +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +inline bool EndPointBasis::IsNetworkFrameworkEndPoint(void) const +{ + return mParameters != NULL; +} +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + #if CHIP_SYSTEM_CONFIG_USE_SOCKETS inline bool EndPointBasis::IsSocketsEndPoint(void) const { @@ -151,6 +172,10 @@ inline bool EndPointBasis::IsOpenEndPoint(void) const lResult = (lResult || IsSocketsEndPoint()); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + lResult = (lResult || IsNetworkFrameworkEndPoint()); +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + return lResult; } diff --git a/src/inet/IPAddress.cpp b/src/inet/IPAddress.cpp index c054bc865f2451..a61cfd5ccd3d1e 100644 --- a/src/inet/IPAddress.cpp +++ b/src/inet/IPAddress.cpp @@ -180,7 +180,7 @@ IPAddress IPAddress::FromIPv6(const ip6_addr_t & ipv6Addr) #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK #if INET_CONFIG_ENABLE_IPV4 struct in_addr IPAddress::ToIPv4() const @@ -231,7 +231,7 @@ IPAddress IPAddress::FromSockAddr(const struct sockaddr & sockaddr) return Any; } -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK // Is address an IPv4 address encoded in IPv6 format? bool IPAddress::IsIPv4() const diff --git a/src/inet/IPAddress.h b/src/inet/IPAddress.h index 6cacb4d9f6c6d7..5bcc66fa65bec7 100644 --- a/src/inet/IPAddress.h +++ b/src/inet/IPAddress.h @@ -29,6 +29,7 @@ #ifndef IPADDRESS_H #define IPADDRESS_H +#include #include #include @@ -46,8 +47,13 @@ #include #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#include +#include #include +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS #include #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS @@ -531,7 +537,7 @@ class DLL_EXPORT IPAddress #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK struct in6_addr ToIPv6(void) const; static IPAddress FromIPv6(const struct in6_addr & addr); @@ -552,7 +558,7 @@ class DLL_EXPORT IPAddress */ static IPAddress FromSockAddr(const struct sockaddr & sockaddr); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_USE_NETWORK_FRAMEWORK /** * @brief Construct an IPv6 unique-local address (ULA) from its parts. diff --git a/src/inet/IPEndPointBasis.cpp b/src/inet/IPEndPointBasis.cpp index b486646caef182..74a3f803bea661 100644 --- a/src/inet/IPEndPointBasis.cpp +++ b/src/inet/IPEndPointBasis.cpp @@ -86,6 +86,10 @@ #endif // !defined(IPV6_DROP_MEMBERSHIP) && defined(IPV6_LEAVE_GROUP) #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#define INET_PORTSTRLEN 6 +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + namespace chip { namespace Inet { @@ -1117,5 +1121,423 @@ void IPEndPointBasis::HandlePendingIO(uint16_t aPort) } #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +INET_ERROR IPEndPointBasis::ConfigureProtocol(IPAddressType aAddressType, nw_parameters_t aParameters) +{ + INET_ERROR res = INET_NO_ERROR; + + nw_protocol_stack_t protocolStack = nw_parameters_copy_default_protocol_stack(aParameters); + nw_protocol_options_t ipOptions = nw_protocol_stack_copy_internet_protocol(protocolStack); + + switch (aAddressType) + { + + case kIPAddressType_IPv6: + nw_ip_options_set_version(ipOptions, nw_ip_version_6); + break; + +#if INET_CONFIG_ENABLE_IPV4 + case kIPAddressType_IPv4: + nw_ip_options_set_version(ipOptions, nw_ip_version_4); + break; +#endif // INET_CONFIG_ENABLE_IPV4 + + default: + res = INET_ERROR_WRONG_ADDRESS_TYPE; + break; + } + nw_release(ipOptions); + nw_release(protocolStack); + + return res; +} + +INET_ERROR IPEndPointBasis::Bind(IPAddressType aAddressType, IPAddress aAddress, uint16_t aPort, nw_parameters_t aParameters) +{ + INET_ERROR res = INET_NO_ERROR; + nw_endpoint_t endpoint = nullptr; + + VerifyOrExit(aParameters != NULL, res = INET_ERROR_BAD_ARGS); + + res = ConfigureProtocol(aAddressType, aParameters); + SuccessOrExit(res); + + res = GetEndPoint(endpoint, aAddress, aPort); + SuccessOrExit(res); + + nw_parameters_set_local_endpoint(aParameters, endpoint); + nw_release(endpoint); + + mDispatchQueue = dispatch_queue_create("inet_dispatch_global", DISPATCH_QUEUE_CONCURRENT); + VerifyOrExit(mDispatchQueue != NULL, res = INET_ERROR_NO_MEMORY); + dispatch_retain(mDispatchQueue); + + mConnectionSemaphore = dispatch_semaphore_create(0); + VerifyOrExit(mConnectionSemaphore != NULL, res = INET_ERROR_NO_MEMORY); + dispatch_retain(mConnectionSemaphore); + + mAddrType = aAddressType; + mConnection = NULL; + +exit: + return res; +} + +INET_ERROR IPEndPointBasis::SendMsg(const IPPacketInfo * aPktInfo, chip::System::PacketBuffer * aBuffer, uint16_t aSendFlags) +{ + __block INET_ERROR res = INET_NO_ERROR; + dispatch_data_t content; + dispatch_semaphore_t send_semaphore; + + // Ensure the destination address type is compatible with the endpoint address type. + VerifyOrExit(mAddrType == aPktInfo->DestAddress.Type(), res = INET_ERROR_BAD_ARGS); + + // For now the entire message must fit within a single buffer. + VerifyOrExit(aBuffer->Next() == NULL, res = INET_ERROR_MESSAGE_TOO_LONG); + + res = GetConnection(aPktInfo); + SuccessOrExit(res); + + // Send a message, and wait for it to be dispatched. + send_semaphore = dispatch_semaphore_create(0); + content = dispatch_data_create(aBuffer->Start(), aBuffer->DataLength(), mDispatchQueue, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + nw_connection_send(mConnection, content, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true, ^(nw_error_t error) { + res = chip::System::MapErrorPOSIX(nw_error_get_error_code(error)); + dispatch_semaphore_signal(send_semaphore); + }); + dispatch_release(content); + + dispatch_semaphore_wait(send_semaphore, DISPATCH_TIME_FOREVER); + dispatch_release(send_semaphore); + +exit: + return res; +} + +void IPEndPointBasis::HandleDataReceived(nw_connection_t aConnection) +{ + + nw_connection_receive_completion_t handler = + ^(dispatch_data_t content, nw_content_context_t context, bool is_complete, nw_error_t receive_error) { + dispatch_block_t schedule_next_receive = ^{ + if (receive_error == NULL) + { + HandleDataReceived(aConnection); + } + else if (OnReceiveError != NULL) + { + nw_error_domain_t error_domain = nw_error_get_error_domain(receive_error); + errno = nw_error_get_error_code(receive_error); + if (!(error_domain == nw_error_domain_posix && errno == ECANCELED)) + { + INET_ERROR error = chip::System::MapErrorPOSIX(errno); + IPPacketInfo packetInfo; + GetPacketInfo(aConnection, packetInfo); + dispatch_async(mDispatchQueue, ^{ + OnReceiveError((IPEndPointBasis *) this, error, &packetInfo); + }); + } + } + }; + + if (content != NULL && OnMessageReceived != NULL) + { + size_t count = dispatch_data_get_size(content); + System::PacketBuffer * packetBuffer = PacketBuffer::NewWithAvailableSize(count); + dispatch_data_apply(content, ^(dispatch_data_t data, size_t offset, const void * buffer, size_t size) { + memmove(packetBuffer->Start() + offset, buffer, size); + return true; + }); + packetBuffer->SetDataLength(count); + + IPPacketInfo packetInfo; + GetPacketInfo(aConnection, packetInfo); + dispatch_async(mDispatchQueue, ^{ + OnMessageReceived((IPEndPointBasis *) this, packetBuffer, &packetInfo); + }); + } + + schedule_next_receive(); + }; + + nw_connection_receive_message(aConnection, handler); +} + +void IPEndPointBasis::GetPacketInfo(nw_connection_t aConnection, IPPacketInfo & aPacketInfo) +{ + nw_parameters_t parameters = nw_connection_copy_parameters(aConnection); + nw_endpoint_t local_endpoint = nw_parameters_copy_local_endpoint(parameters); + nw_endpoint_t remote_endpoint = nw_connection_copy_endpoint(aConnection); + + aPacketInfo.Clear(); + aPacketInfo.SrcAddress = IPAddress::FromSockAddr(*nw_endpoint_get_address(remote_endpoint)); + aPacketInfo.DestAddress = IPAddress::FromSockAddr(*nw_endpoint_get_address(local_endpoint)); + aPacketInfo.SrcPort = nw_endpoint_get_port(remote_endpoint); + aPacketInfo.DestPort = nw_endpoint_get_port(local_endpoint); +} + +INET_ERROR IPEndPointBasis::GetEndPoint(nw_endpoint_t & aEndPoint, const IPAddress aAddress, uint16_t aPort) +{ + INET_ERROR res = INET_NO_ERROR; + + char addrStr[INET6_ADDRSTRLEN]; + aAddress.ToString(addrStr, sizeof(addrStr)); + + char portStr[INET_PORTSTRLEN]; + snprintf(portStr, sizeof(portStr), "%u", aPort); + + nw_endpoint_t endpoint = nw_endpoint_create_host(addrStr, portStr); + VerifyOrExit(endpoint != NULL, res = INET_ERROR_BAD_ARGS); + + aEndPoint = endpoint; + +exit: + return res; +} + +INET_ERROR IPEndPointBasis::GetConnection(const IPPacketInfo * aPktInfo) +{ + INET_ERROR res = INET_NO_ERROR; + nw_endpoint_t endpoint = NULL; + nw_connection_t connection = NULL; + + VerifyOrExit(mParameters != NULL, res = INET_ERROR_INCORRECT_STATE); + + if (mConnection) + { + nw_endpoint_t remote_endpoint = nw_connection_copy_endpoint(mConnection); + const IPAddress remote_address = IPAddress::FromSockAddr(*nw_endpoint_get_address(remote_endpoint)); + const uint16_t remote_port = nw_endpoint_get_port(remote_endpoint); + const bool isDifferentEndPoint = aPktInfo->DestPort != remote_port || aPktInfo->DestAddress != remote_address; + VerifyOrExit(isDifferentEndPoint, res = INET_NO_ERROR); + + res = ReleaseConnection(); + } + SuccessOrExit(res); + + res = GetEndPoint(endpoint, aPktInfo->DestAddress, aPktInfo->DestPort); + SuccessOrExit(res); + + connection = nw_connection_create(endpoint, mParameters); + nw_release(endpoint); + + VerifyOrExit(connection != NULL, res = INET_ERROR_INCORRECT_STATE); + + res = StartConnection(connection); + +exit: + return res; +} + +INET_ERROR IPEndPointBasis::StartListener() +{ + __block INET_ERROR res = INET_NO_ERROR; + nw_listener_t listener; + + VerifyOrExit(mListener == NULL, res = INET_ERROR_INCORRECT_STATE); + VerifyOrExit(mListenerSemaphore == NULL, res = INET_ERROR_INCORRECT_STATE); + VerifyOrExit(mListenerQueue == NULL, res = INET_ERROR_INCORRECT_STATE); + + listener = nw_listener_create(mParameters); + VerifyOrExit(listener != NULL, res = INET_ERROR_INCORRECT_STATE); + + mListenerSemaphore = dispatch_semaphore_create(0); + VerifyOrExit(mListenerSemaphore != NULL, res = INET_ERROR_NO_MEMORY); + dispatch_retain(mListenerSemaphore); + + mListenerQueue = dispatch_queue_create("inet_dispatch_listener", DISPATCH_QUEUE_CONCURRENT); + VerifyOrExit(mListenerQueue != NULL, res = INET_ERROR_NO_MEMORY); + dispatch_retain(mListenerQueue); + + nw_listener_set_queue(listener, mListenerQueue); + + nw_listener_set_new_connection_handler(listener, ^(nw_connection_t connection) { + ReleaseConnection(); + StartConnection(connection); + }); + + nw_listener_set_state_changed_handler(listener, ^(nw_listener_state_t state, nw_error_t error) { + switch (state) + { + + case nw_listener_state_invalid: + ChipLogDetail(Inet, "Listener: Invalid"); + res = INET_ERROR_INCORRECT_STATE; + nw_listener_cancel(listener); + break; + + case nw_listener_state_waiting: + ChipLogDetail(Inet, "Listener: Waiting"); + break; + + case nw_listener_state_failed: + ChipLogDetail(Inet, "Listener: Failed"); + res = chip::System::MapErrorPOSIX(nw_error_get_error_code(error)); + nw_listener_cancel(listener); + break; + + case nw_listener_state_ready: + ChipLogDetail(Inet, "Listener: Ready"); + res = INET_NO_ERROR; + dispatch_semaphore_signal(mListenerSemaphore); + break; + + case nw_listener_state_cancelled: + ChipLogDetail(Inet, "Listener: Cancelled"); + if (res == INET_NO_ERROR) + res = INET_ERROR_CONNECTION_ABORTED; + + dispatch_semaphore_signal(mListenerSemaphore); + break; + } + }); + + nw_listener_start(listener); + dispatch_semaphore_wait(mListenerSemaphore, DISPATCH_TIME_FOREVER); + SuccessOrExit(res); + + mListener = listener; + nw_retain(mListener); +exit: + return res; +} + +INET_ERROR IPEndPointBasis::StartConnection(nw_connection_t & aConnection) +{ + __block INET_ERROR res = INET_NO_ERROR; + + nw_connection_set_queue(aConnection, mDispatchQueue); + + nw_connection_set_state_changed_handler(aConnection, ^(nw_connection_state_t state, nw_error_t error) { + switch (state) + { + + case nw_connection_state_invalid: + ChipLogDetail(Inet, "Connection: Invalid"); + res = INET_ERROR_INCORRECT_STATE; + nw_connection_cancel(aConnection); + break; + + case nw_connection_state_preparing: + ChipLogDetail(Inet, "Connection: Preparing"); + res = INET_ERROR_INCORRECT_STATE; + break; + + case nw_connection_state_waiting: + ChipLogDetail(Inet, "Connection: Waiting"); + nw_connection_cancel(aConnection); + break; + + case nw_connection_state_failed: + ChipLogDetail(Inet, "Connection: Failed"); + res = chip::System::MapErrorPOSIX(nw_error_get_error_code(error)); + nw_connection_cancel(aConnection); + break; + + case nw_connection_state_ready: + ChipLogDetail(Inet, "Connection: Ready"); + res = INET_NO_ERROR; + dispatch_semaphore_signal(mConnectionSemaphore); + break; + + case nw_connection_state_cancelled: + ChipLogDetail(Inet, "Connection: Cancelled"); + if (res == INET_NO_ERROR) + res = INET_ERROR_CONNECTION_ABORTED; + + dispatch_semaphore_signal(mConnectionSemaphore); + break; + } + }); + + nw_connection_start(aConnection); + dispatch_semaphore_wait(mConnectionSemaphore, DISPATCH_TIME_FOREVER); + SuccessOrExit(res); + + mConnection = aConnection; + nw_retain(mConnection); + HandleDataReceived(mConnection); + +exit: + return res; +} + +void IPEndPointBasis::ReleaseAll() +{ + + OnMessageReceived = NULL; + OnReceiveError = NULL; + + ReleaseConnection(); + ReleaseListener(); + + if (mParameters) + { + nw_release(mParameters); + mParameters = NULL; + } + + if (mDispatchQueue) + { + dispatch_suspend(mDispatchQueue); + dispatch_release(mDispatchQueue); + mDispatchQueue = NULL; + } + + if (mConnectionSemaphore) + { + dispatch_release(mConnectionSemaphore); + mConnectionSemaphore = NULL; + } + + if (mListenerQueue) + { + dispatch_suspend(mListenerQueue); + dispatch_release(mListenerQueue); + mListenerQueue = NULL; + } + + if (mListenerSemaphore) + { + dispatch_release(mListenerSemaphore); + mListenerSemaphore = NULL; + } +} + +INET_ERROR IPEndPointBasis::ReleaseListener() +{ + INET_ERROR res = INET_NO_ERROR; + + VerifyOrExit(mListener, res = INET_ERROR_INCORRECT_STATE); + VerifyOrExit(mDispatchQueue, res = INET_ERROR_INCORRECT_STATE); + VerifyOrExit(mConnectionSemaphore, res = INET_ERROR_INCORRECT_STATE); + + nw_listener_cancel(mListener); + dispatch_semaphore_wait(mListenerSemaphore, DISPATCH_TIME_FOREVER); + nw_release(mListener); + mListener = NULL; + +exit: + return res; +} + +INET_ERROR IPEndPointBasis::ReleaseConnection() +{ + INET_ERROR res = INET_NO_ERROR; + VerifyOrExit(mConnection, res = INET_ERROR_INCORRECT_STATE); + VerifyOrExit(mDispatchQueue, res = INET_ERROR_INCORRECT_STATE); + VerifyOrExit(mConnectionSemaphore, res = INET_ERROR_INCORRECT_STATE); + + nw_connection_cancel(mConnection); + dispatch_semaphore_wait(mConnectionSemaphore, DISPATCH_TIME_FOREVER); + nw_release(mConnection); + mConnection = NULL; + +exit: + return res; +} + +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + } // namespace Inet } // namespace chip diff --git a/src/inet/IPEndPointBasis.h b/src/inet/IPEndPointBasis.h index 1dbf035b011ba9..996e9fd20c5400 100644 --- a/src/inet/IPEndPointBasis.h +++ b/src/inet/IPEndPointBasis.h @@ -151,6 +151,29 @@ class DLL_EXPORT IPEndPointBasis : public EndPointBasis void HandlePendingIO(uint16_t aPort); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +protected: + nw_listener_t mListener; + dispatch_semaphore_t mListenerSemaphore; + dispatch_queue_t mListenerQueue; + nw_connection_t mConnection; + dispatch_semaphore_t mConnectionSemaphore; + dispatch_queue_t mDispatchQueue; + + INET_ERROR Bind(IPAddressType aAddressType, IPAddress aAddress, uint16_t aPort, nw_parameters_t aParameters); + INET_ERROR ConfigureProtocol(IPAddressType aAddressType, nw_parameters_t aParameters); + INET_ERROR SendMsg(const IPPacketInfo * aPktInfo, chip::System::PacketBuffer * aBuffer, uint16_t aSendFlags); + INET_ERROR StartListener(); + INET_ERROR GetConnection(const IPPacketInfo * aPktInfo); + INET_ERROR GetEndPoint(nw_endpoint_t & aEndpoint, const IPAddress aAddress, uint16_t aPort); + INET_ERROR StartConnection(nw_connection_t & aConnection); + void GetPacketInfo(nw_connection_t aConnection, IPPacketInfo & aPacketInfo); + void HandleDataReceived(nw_connection_t aConnection); + INET_ERROR ReleaseListener(); + INET_ERROR ReleaseConnection(); + void ReleaseAll(); +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + private: IPEndPointBasis(void); // not defined IPEndPointBasis(const IPEndPointBasis &); // not defined diff --git a/src/inet/Inet.h b/src/inet/Inet.h index f498ca3855568c..5ab4263e774343 100644 --- a/src/inet/Inet.h +++ b/src/inet/Inet.h @@ -35,11 +35,6 @@ #include #include -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#include -#include -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - #if INET_CONFIG_ENABLE_DNS_RESOLVER #include #endif // INET_CONFIG_ENABLE_DNS_RESOLVER diff --git a/src/inet/InetBuffer.h b/src/inet/InetBuffer.h deleted file mode 100644 index de2d59fdaf9f50..00000000000000 --- a/src/inet/InetBuffer.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright (c) 2020 Project CHIP Authors - * Copyright (c) 2013-2017 Nest Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 - * Header file for the obsolescent InetBuffer type definition, which - * provides transitional definitions that source-code compatible with - * implementations that rely on the legacy InetBuffer class. - */ - -#ifndef INETBUFFER_H -#define INETBUFFER_H - -#include - -#if !INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -#error "#include // while !INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES." - -#else // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#include - -namespace chip { -namespace Inet { - -typedef chip::System::PacketBuffer InetBuffer; - -#if !CHIP_SYSTEM_CONFIG_USE_LWIP -struct pbuf : public chip::System::pbuf -{ -}; -#endif // !CHIP_SYSTEM_CONFIG_USE_LWIP - -} // namespace Inet -} // namespace chip - -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#endif // !defined(INETBUFFER_H) diff --git a/src/inet/InetConfig.h b/src/inet/InetConfig.h index 17e6899b40c7ee..97eb060f57573b 100644 --- a/src/inet/InetConfig.h +++ b/src/inet/InetConfig.h @@ -72,23 +72,6 @@ // clang-format off -/** - * @def INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - * - * @brief - * This boolean configuration option is (1) if the obsolescent interfaces - * of the INET layer that now reside elsewhere, for example, in the chip System - * Layer are aliased for transitional purposes. - * - */ -#ifndef INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#define INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES 0 -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES && !CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#error "REQUIRED: if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES then CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES!" -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES && !CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - /** * @def INET_CONFIG_MAX_IP_AND_UDP_HEADER_SIZE * @@ -413,172 +396,6 @@ #define _INET_CONFIG_EVENT(e) _CHIP_SYSTEM_CONFIG_LWIP_EVENT(INET_CONFIG_EVENT_RESERVED + (e)) #endif // _INET_CONFIG_EVENT -/* - * NOTE WELL: the following configuration parameters and macro definitions are - * obsolescent. They are provided here to facilitate transition. - */ -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -/** - * @def INET_LWIP - * - * @brief - * Use LwIP. - * - * @note - * By default, this parameter is a copy of CHIP_SYSTEM_CONFIG_USE_LWIP. - */ -#ifndef INET_LWIP -#define INET_LWIP CHIP_SYSTEM_CONFIG_USE_LWIP -#endif // !defined(INET_LWIP) - -/** - * @def INET_SOCKETS - * - * @brief - * Use BSD sockets. - * - * @note - * By default, this parameter is a copy of CHIP_SYSTEM_CONFIG_USE_SOCKETS. - */ -#ifndef INET_SOCKETS -#define INET_SOCKETS CHIP_SYSTEM_CONFIG_USE_SOCKETS -#endif // !defined(INET_SOCKETS) - -/** - * @def INET_CONFIG_POSIX_LOCKING - * - * @brief - * Use POSIX locking. This is enabled by default when not compiling - * for BSD sockets. - * - * Unless you are simulating an LwIP-based system on a Unix-style - * host, this value should be left at its default. - * - * @note - * By default, this parameter is a copy of CHIP_SYSTEM_CONFIG_POSIX_LOCKING. - * - */ -#ifndef INET_CONFIG_POSIX_LOCKING -#define INET_CONFIG_POSIX_LOCKING CHIP_SYSTEM_CONFIG_POSIX_LOCKING -#endif // !defined(INET_CONFIG_POSIX_LOCKING) - -/** - * @def INET_CONFIG_FREERTOS_LOCKING - * - * @brief - * Use FreeRTOS locking. - * - * This should be generally asserted (1) for FreeRTOS + LwIP-based - * systems and deasserted (0) for BSD sockets-based systems. - * - * However, if you are simulating an LwIP-based system atop POSIX - * threads and BSD sockets, this should also be deasserted (0). - * - * @note - * By default, this parameter is a copy of CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING. - * - */ -#ifndef INET_CONFIG_FREERTOS_LOCKING -#define INET_CONFIG_FREERTOS_LOCKING CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING -#endif // !defined(INET_CONFIG_FREERTOS_LOCKING) - -/** - * @def INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS - * - * @brief - * This defines whether (1) or not (0) your platform will override - * the platform- and system-specific PostEvent, DispatchEvents, and - * DispatchEvent functions. - * - */ -#ifndef INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS -#define INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS 0 -#endif // INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS - -/** - * @def INET_CONFIG_EVENT_TYPE - * - * @brief - * This defines the type for InetEvent types, typically - * an integral type. - * - * @note - * By default, this parameter is a copy of CHIP_SYSTEM_CONFIG_LWIP_EVENT_TYPE. - * - */ -#ifndef INET_CONFIG_EVENT_TYPE -#define INET_CONFIG_EVENT_TYPE CHIP_SYSTEM_CONFIG_LWIP_EVENT_TYPE -#endif // INET_CONFIG_EVENT_TYPE - -/** - * @def INET_CONFIG_EVENT_OBJECT_TYPE - * - * @brief - * This defines the type of InetEvent objects or "messages". - * - * Such types are not directly used by the InetLayer but are - * "passed through". Consequently a forward declaration and a - * const pointer or reference are appropriate. - * - * @note - * By default, this parameter is a copy of CHIP_SYSTEM_CONFIG_LWIP_EVENT_OBJECT_TYPE. - * - */ -#ifndef INET_CONFIG_EVENT_OBJECT_TYPE -#define INET_CONFIG_EVENT_OBJECT_TYPE CHIP_SYSTEM_CONFIG_LWIP_EVENT_OBJECT_TYPE -#endif // INET_CONFIG_EVENT_OBJECT_TYPE - -/** - * @def INET_CONFIG_NUM_BUFS - * - * @brief - * This is the total number of packet buffers for the BSD sockets - * configuration. - * - * This may be set to zero (0) to enable unbounded dynamic - * allocation using malloc. - * - * @note - * By default, this parameter is a copy of CHIP_SYSTEM_CONFIG_PACKETBUFFER_MAXALLOC. - * - */ -#ifndef INET_CONFIG_NUM_BUFS -#define INET_CONFIG_NUM_BUFS CHIP_SYSTEM_CONFIG_PACKETBUFFER_MAXALLOC -#endif // INET_CONFIG_NUM_BUFS - -/** - * @def INET_CONFIG_NUM_TIMERS - * - * @brief - * This is the total number of available timers. - * - * @note - * By default, this parameter is a copy of CHIP_SYSTEM_CONFIG_NUM_TIMERS. - * - */ -#ifndef INET_CONFIG_NUM_TIMERS -#define INET_CONFIG_NUM_TIMERS CHIP_SYSTEM_CONFIG_NUM_TIMERS -#endif // INET_CONFIG_NUM_TIMERS - -/** - * @def INET_CONFIG_HEADER_RESERVE_SIZE - * - * @brief - * The number of bytes to reserve in a network packet buffer to contain - * all the possible protocol encapsulation headers before the application - * message text. - * - * @note - * By default, this parameter is a copy of CHIP_SYSTEM_CONFIG_HEADER_RESERVE_SIZE. - * - */ -#ifndef INET_CONFIG_HEADER_RESERVE_SIZE -#define INET_CONFIG_HEADER_RESERVE_SIZE CHIP_SYSTEM_CONFIG_HEADER_RESERVE_SIZE -#endif // INET_CONFIG_HEADER_RESERVE_SIZE - -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - /** * @def INET_CONFIG_TEST * diff --git a/src/inet/InetError.h b/src/inet/InetError.h index a34a959d597eb0..484a0c5fd33b99 100644 --- a/src/inet/InetError.h +++ b/src/inet/InetError.h @@ -378,140 +378,4 @@ extern bool FormatLayerError(char * buf, uint16_t bufSize, int32_t err); } // namespace Inet } // namespace chip -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -/** - * @name Error Mapping and Description Functions - * - * @{ - */ - -#if INET_CONFIG_WILL_OVERRIDE_OS_ERROR_FUNCS - -extern INET_ERROR INET_MapOSError(int e); -extern const char * INET_DescribeOSError(INET_ERROR err); -extern bool INET_IsOSError(INET_ERROR err); - -#else // !INET_CONFIG_WILL_OVERRIDE_OS_ERROR_FUNCS - -#ifdef __cplusplus - -/** - * This implements a mapping function for InetLayer errors that allows - * mapping underlying POSIX network and OS stack errors into a - * platform- or system-specific range. - * - * @param[in] e The POSIX network or OS error to map. - * - * @return The mapped POSIX network or OS error. - * - */ -static inline INET_ERROR INET_MapOSError(int e) -{ - return static_cast(::chip::System::MapErrorPOSIX(e)); -} - -/** - * This implements a function to return an NULL-terminated OS-specific - * descriptive C string, associated with the specified, mapped OS - * error. - * - * @param[in] err The mapped OS-specific error to describe. - * - * @return A NULL-terminated, OS-specific descriptive C string - * describing the error. - * - */ -static inline const char * INET_DescribeOSError(INET_ERROR err) -{ - return ::chip::System::DescribeErrorPOSIX(err); -} - -/** - * This implements an introspection function for InetLayer errors that - * allows the caller to determine whether the specified error is an - * internal, underlying OS error. - * - * @param[in] err The mapped error to determine whether it is an OS - * error. - * - * @return True if the specified error is an OS error; otherwise, false. - * - */ -static inline bool INET_IsOSError(INET_ERROR err) -{ - return ::chip::System::IsErrorPOSIX(err); -} - -#endif // !defined(__cplusplus) -#endif // !INET_CONFIG_WILL_OVERRIDE_OS_ERROR_FUNCS - -#if CHIP_SYSTEM_CONFIG_USE_LWIP -#if INET_CONFIG_WILL_OVERRIDE_LWIP_ERROR_FUNCS - -extern INET_ERROR INET_MapLwIPError(err_t e); -extern const char * INET_DescribeLwIPError(INET_ERROR err); -extern bool INET_IsLwIPError(INET_ERROR err); - -#else // !INET_CONFIG_WILL_OVERRIDE_LWIP_ERROR_FUNCS - -#ifdef __cplusplus - -/** - * This implements a mapping function for InetLayer errors that allows - * mapping underlying LwIP network stack errors into a platform- or - * system-specific range. - * - * @param[in] e The LwIP error to map. - * - * @return The mapped LwIP network or OS error. - * - */ -static inline INET_ERROR INET_MapLwIPError(err_t e) -{ - return static_cast(::chip::System::MapErrorLwIP(e)); -} - -/** - * This implements a function to return an NULL-terminated - * LwIP-specific descriptive C string, associated with the specified, - * mapped LwIP error. - * - * @param[in] err The mapped LwIP-specific error to describe. - * - * @return A NULL-terminated, LwIP-specific descriptive C string - * describing the error. - * - */ -static inline const char * INET_DescribeLwIPError(INET_ERROR err) -{ - return ::chip::System::DescribeErrorLwIP(err); -} - -/** - * This implements an introspection function for InetLayer errors that - * allows the caller to determine whether the specified error is an - * internal, underlying LwIP error. - * - * @param[in] err The mapped error to determine whether it is a LwIP - * error. - * - * @return True if the specified error is a LwIP error; otherwise, false. - * - */ -static inline bool INET_IsLwIPError(INET_ERROR err) -{ - return ::chip::System::IsErrorLwIP(err); -} - -#endif // !defined(__cplusplus) -#endif // !INET_CONFIG_WILL_OVERRIDE_LWIP_ERROR_FUNCS -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -/** - * @} - */ - -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - #endif // !defined(INETERROR_H) diff --git a/src/inet/InetInterface.cpp b/src/inet/InetInterface.cpp index af2dc0528dfe78..94730408c32420 100644 --- a/src/inet/InetInterface.cpp +++ b/src/inet/InetInterface.cpp @@ -265,14 +265,14 @@ InterfaceIterator::~InterfaceIterator(void) * \c false if positioned beyond the end of the interface list. */ -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK bool InterfaceIterator::HasCurrent(void) { return (mIntfArray != NULL) ? mIntfArray[mCurIntf].if_index != 0 : Next(); } -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK /** * @fn bool InterfaceIterator::Next(void) @@ -492,7 +492,7 @@ short InterfaceIterator::GetFlags(void) * this constructor may allocate resources recycled by the destructor. */ -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK InterfaceAddressIterator::InterfaceAddressIterator(void) { @@ -500,7 +500,7 @@ InterfaceAddressIterator::InterfaceAddressIterator(void) mCurAddr = NULL; } -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK /** * @fn InterfaceAddressIterator::~InterfaceAddressIterator(void) @@ -511,7 +511,7 @@ InterfaceAddressIterator::InterfaceAddressIterator(void) * Recycles any resources allocated by the constructor. */ -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK InterfaceAddressIterator::~InterfaceAddressIterator(void) { @@ -522,7 +522,7 @@ InterfaceAddressIterator::~InterfaceAddressIterator(void) } } -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK /** * @fn bool InterfaceIterator::HasCurrent(void) @@ -534,11 +534,11 @@ InterfaceAddressIterator::~InterfaceAddressIterator(void) */ bool InterfaceAddressIterator::HasCurrent(void) { -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK return (mAddrsList != NULL) ? (mCurAddr != NULL) : Next(); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK #if CHIP_SYSTEM_CONFIG_USE_LWIP diff --git a/src/inet/InetInterface.h b/src/inet/InetInterface.h index df655f4503d28f..3cc24c66a884b1 100644 --- a/src/inet/InetInterface.h +++ b/src/inet/InetInterface.h @@ -35,10 +35,10 @@ #include #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK struct if_nameindex; struct ifaddrs; -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK #include #include @@ -68,9 +68,9 @@ class IPPrefix; typedef struct netif * InterfaceId; #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK typedef unsigned InterfaceId; -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_USE_NETWORK_FRAMEWORK /** * @def INET_NULL_INTERFACEID @@ -88,9 +88,9 @@ typedef unsigned InterfaceId; #define INET_NULL_INTERFACEID NULL #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK #define INET_NULL_INTERFACEID 0 -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_USE_NETWORK_FRAMEWORK /** * @brief Test \c ID for inequivalence with \c INET_NULL_INTERFACEID @@ -146,14 +146,14 @@ class InterfaceIterator struct netif * mCurNetif; #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK struct if_nameindex * mIntfArray; size_t mCurIntf; short mIntfFlags; bool mIntfFlagsCached; short GetFlags(void); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK }; /** @@ -208,10 +208,10 @@ class DLL_EXPORT InterfaceAddressIterator int mCurAddrIndex; #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK struct ifaddrs * mAddrsList; struct ifaddrs * mCurAddr; -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK }; #if CHIP_SYSTEM_CONFIG_USE_LWIP diff --git a/src/inet/InetLayer.am b/src/inet/InetLayer.am index d539acb7f12542..0f5cf9b6d61114 100644 --- a/src/inet/InetLayer.am +++ b/src/inet/InetLayer.am @@ -35,7 +35,6 @@ CHIP_BUILD_INET_LAYER_SOURCE_FILES = \ @top_builddir@/src/inet/InetInterface.cpp \ @top_builddir@/src/inet/InetLayer.cpp \ @top_builddir@/src/inet/InetLayerBasis.cpp \ - @top_builddir@/src/inet/InetTimer.cpp \ @top_builddir@/src/inet/InetUtils.cpp \ $(NULL) @@ -50,7 +49,6 @@ CHIP_BUILD_INET_LAYER_HEADER_FILES = \ @top_builddir@/src/inet/IPPrefix.h \ @top_builddir@/src/inet/Inet.h \ @top_builddir@/src/inet/InetArgParser.h \ - @top_builddir@/src/inet/InetBuffer.h \ @top_builddir@/src/inet/InetConfig.h \ @top_builddir@/src/inet/InetError.h \ @top_builddir@/src/inet/InetFaultInjection.h \ @@ -58,7 +56,6 @@ CHIP_BUILD_INET_LAYER_HEADER_FILES = \ @top_builddir@/src/inet/InetLayer.h \ @top_builddir@/src/inet/InetLayerBasis.h \ @top_builddir@/src/inet/InetLayerEvents.h \ - @top_builddir@/src/inet/InetTimer.h \ @top_builddir@/src/inet/RawEndPoint.h \ @top_builddir@/src/inet/TCPEndPoint.h \ @top_builddir@/src/inet/TunEndPoint.h \ diff --git a/src/inet/InetLayer.cpp b/src/inet/InetLayer.cpp index 7677597464c130..fe2a22a549d22d 100644 --- a/src/inet/InetLayer.cpp +++ b/src/inet/InetLayer.cpp @@ -72,21 +72,6 @@ #endif // __ANDROID__ #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#if CHIP_SYSTEM_CONFIG_USE_LWIP && !INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS - -// MARK: InetLayer platform- and system-specific functions for LwIP-native eventing. - -struct LwIPInetEvent -{ - Inet::InetEventType Type; - Inet::InetLayerBasis * Target; - uintptr_t Arg; -}; - -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP && !INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - namespace chip { namespace Inet { @@ -123,10 +108,6 @@ void InetLayer::UpdateSnapshot(chip::System::Stats::Snapshot & aSnapshot) * */ InetLayer::InetLayer(void) -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - : - mImplicitSystemLayer() -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES { State = kState_NotInitialized; @@ -286,14 +267,6 @@ INET_ERROR InetLayer::Init(chip::System::Layer & aSystemLayer, void * aContext) Platform::InetLayer::WillInit(this, aContext); SuccessOrExit(err); -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - if (&aSystemLayer == &mImplicitSystemLayer) - { - err = mImplicitSystemLayer.Init(aContext); - SuccessOrExit(err); - } -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - mSystemLayer = &aSystemLayer; mContext = aContext; @@ -397,14 +370,6 @@ INET_ERROR InetLayer::Shutdown(void) } } #endif // INET_CONFIG_ENABLE_UDP_ENDPOINT - -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - if (mSystemLayer == &mImplicitSystemLayer) - { - err = mImplicitSystemLayer.Shutdown(); - SuccessOrExit(err); - } -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES } State = kState_NotInitialized; @@ -998,102 +963,6 @@ void InetLayer::CancelResolveHostAddress(DNSResolveCompleteFunct onComplete, voi #endif // INET_CONFIG_ENABLE_DNS_RESOLVER -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -static void SystemTimerComplete(chip::System::Layer * aLayer, void * aAppState, chip::System::Error aError) -{ - chip::System::Timer & lTimer = *reinterpret_cast(aAppState); - InetLayer & lInetLayer = *lTimer.InetLayer(); - InetLayer::TimerCompleteFunct lComplete = reinterpret_cast(lTimer.OnCompleteInetLayer()); - void * lAppState = lTimer.AppStateInetLayer(); - - lComplete(&lInetLayer, lAppState, static_cast(aError)); -} - -/** - * @brief - * This method registers a one-shot timer(to fire at a specified offset relative to the time - * of registration) with the underlying timer mechanism, through this InetLayer instance. - * - * @note - * Each InetLayer instance could have its own set of timers. This might not be - * significant to applications, as each application usually works with one singleton - * InetLayer instance. - * - * @param[in] aMilliseconds Number of milliseconds before this timer should fire. - * - * @param[in] aComplete A pointer to a callback function to be called when this - * timer fires. - * - * @param[in] aAppState A pointer to an application state object to be passed - * to the callback function as argument. - * - * @retval #INET_ERROR_INCORRECT_STATE If the InetLayer instance is not initialized. - * @retval #INET_ERROR_NO_MEMORY If the InetLayer runs out of resource for this - * request for a new timer. - * @retval #INET_NO_ERROR On success. - * - */ -INET_ERROR InetLayer::StartTimer(uint32_t aMilliseconds, TimerCompleteFunct aComplete, void * aAppState) -{ - INET_ERROR lReturn; - - if (State != kState_Initialized) - { - return INET_ERROR_INCORRECT_STATE; - } - - chip::System::Timer * lTimer; - - lReturn = mSystemLayer->NewTimer(lTimer); - SuccessOrExit(lReturn); - - lTimer->AttachInetLayer(*this, reinterpret_cast(aComplete), aAppState); - - lReturn = lTimer->Start(aMilliseconds, SystemTimerComplete, lTimer); - - if (lReturn != CHIP_SYSTEM_NO_ERROR) - { - lTimer->Cancel(); - } - -exit: - switch (lReturn) - { - case CHIP_SYSTEM_ERROR_NO_MEMORY: - lReturn = INET_ERROR_NO_MEMORY; - break; - case CHIP_SYSTEM_NO_ERROR: - lReturn = INET_NO_ERROR; - break; - } - - return lReturn; -} - -/** - * @brief - * This method cancels an one-shot timer, started earlier through @p StartTimer(). - * - * @note - * The cancellation could fail silently in two different ways. If the timer - * specified by the combination of the callback function and application state object - * couldn't be found, cancellation could fail. If the timer has fired, but not yet - * removed from memory, cancellation could also fail. - * - * @param[in] aComplete A pointer to the callback function used in calling @p StartTimer(). - * @param[in] aAppState A pointer to the application state object used in calling - * @p StartTimer(). - * - */ -void InetLayer::CancelTimer(TimerCompleteFunct aComplete, void * aAppState) -{ - if (State == kState_Initialized) - { - mSystemLayer->CancelAllMatchingInetTimers(*this, reinterpret_cast(aComplete), aAppState); - } -} -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - /** * Get the interface identifier for the specified IP address. If the * interface identifier cannot be derived it is set to the @@ -1273,145 +1142,6 @@ chip::System::Error InetLayer::HandleInetLayerEvent(chip::System::Object & aTarg return lReturn; } -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -/** - * This posts an event / message of the specified type with the - * provided argument to this instance's platform-specific event / - * message queue. - * - * @param[in,out] target A pointer to the InetLayer object making the - * post request. - * - * @param[in] type The type of event to post. - * - * @param[in,out] arg The argument associated with the event to post. - * - * @retval #INET_ERROR_INCORRECT_STATE If the state of the InetLayer - * object is incorrect. - * @retval #INET_ERROR_BAD_ARGS If #target is NULL. - * @retval #INET_ERROR_NO_MEMORY If the EventQueue is already - * full. - * @retval other platform-specific errors generated indicating the reason - * for failure. - * @retval #INET_NO_ERROR On success. - * - */ -INET_ERROR InetLayer::PostEvent(InetLayerBasis * target, InetEventType type, uintptr_t arg) -{ - chip::System::Layer & lSystemLayer = *mSystemLayer; - INET_ERROR retval = INET_NO_ERROR; - - VerifyOrExit(State == kState_Initialized, retval = INET_ERROR_INCORRECT_STATE); - VerifyOrExit(target != NULL, retval = INET_ERROR_BAD_ARGS); - - { - chip::System::Layer & lTargetSystemLayer = target->SystemLayer(); - - VerifyOrDieWithMsg(target->IsRetained(lSystemLayer), Inet, "wrong system layer! [target %p != instance %p]", - &lTargetSystemLayer, &lSystemLayer); - } - - // Sanity check that this instance and the target layer haven't - // been "crossed". - - { - InetLayer & lTargetInetLayer = target->Layer(); - - VerifyOrDieWithMsg(this == &lTargetInetLayer, Inet, "target layer %p != instance layer %p", &lTargetInetLayer, this); - } - - if (IsDroppableEvent(type) && !CanEnqueueDroppableEvent()) - { - ChipLogProgress(Inet, "Dropping incoming packet (type %d)", (int) type); - ExitNow(retval = INET_ERROR_NO_MEMORY); - } - - retval = Platform::InetLayer::PostEvent(this, mContext, target, type, arg); - if (retval != INET_NO_ERROR) - { - ChipLogError(Inet, "Failed to queue InetLayer event (type %d): %s", (int) type, ErrorStr(retval)); - } - SuccessOrExit(retval); - -exit: - return retval; -} - -/** - * This is a syntactic wrapper around a platform-specific hook that - * effects an event loop, waiting on a queue that services this - * instance, pulling events off of that queue, and then dispatching - * them for handling. - * - * @return #INET_NO_ERROR on success; otherwise, a specific error - * indicating the reason for initialization failure. - * - */ -INET_ERROR InetLayer::DispatchEvents(void) -{ - INET_ERROR retval = INET_NO_ERROR; - - VerifyOrExit(State == kState_Initialized, retval = INET_ERROR_INCORRECT_STATE); - - retval = Platform::InetLayer::DispatchEvents(this, mContext); - SuccessOrExit(retval); - -exit: - return retval; -} - -/** - * Start the platform timer with specified msec duration. - * - * @brief - * Calls the Platform specific API to start a platform timer. - * This API is called by the chip::System::Timer class when one or more - * system timers are active and require deferred execution. - * - * @param[in] inDurMS The timer duration in milliseconds. - * - * @return INET_NO_ERROR on success, error code otherwise. - * - */ -INET_ERROR InetLayer::StartPlatformTimer(uint32_t inDurMS) -{ - INET_ERROR retval; - - VerifyOrExit(State == kState_Initialized, retval = INET_ERROR_INCORRECT_STATE); - - retval = Platform::InetLayer::StartTimer(this, mContext, inDurMS); - -exit: - return retval; -} - -/** - * Handle the platform timer expiration event. - * - * @brief - * Calls chip::System::Timer::HandleExpiredTimers to handle any expired - * system timers. It is assumed that this API is called only while - * on the thread which owns the InetLayer object. - * - * @return INET_NO_ERROR on success, error code otherwise. - * - */ -INET_ERROR InetLayer::HandlePlatformTimer(void) -{ - INET_ERROR lReturn; - chip::System::Error lSystemError; - - VerifyOrExit(State == kState_Initialized, lReturn = INET_ERROR_INCORRECT_STATE); - - lSystemError = mSystemLayer->HandlePlatformTimer(); - lReturn = static_cast(lSystemError); - -exit: - return lReturn; -} - -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_SOCKETS @@ -1470,13 +1200,6 @@ void InetLayer::PrepareSelect(int & nfds, fd_set * readfds, fd_set * writefds, f lEndPoint->PrepareIO().SetFDs(lEndPoint->mSocket, nfds, readfds, writefds, exceptfds); } #endif // INET_CONFIG_ENABLE_TUN_ENDPOINT - -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - if (mSystemLayer == &mImplicitSystemLayer) - { - mSystemLayer->PrepareSelect(nfds, readfds, writefds, exceptfds, sleepTimeTV); - } -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES } /** @@ -1606,13 +1329,6 @@ void InetLayer::HandleSelectResult(int selectRes, fd_set * readfds, fd_set * wri } #endif // INET_CONFIG_ENABLE_TUN_ENDPOINT } - -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - if (mSystemLayer == &mImplicitSystemLayer) - { - mSystemLayer->HandleSelectResult(selectRes, readfds, writefds, exceptfds); - } -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES } #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS @@ -1734,141 +1450,6 @@ DLL_EXPORT void DidShutdown(Inet::InetLayer * aLayer, void * aContext, INET_ERRO return; } -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#if CHIP_SYSTEM_CONFIG_USE_LWIP && !INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS -/** - * This is a platform-specific event / message post hook. This may be - * overridden by assserting the preprocessor definition, - * #INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS. - * - * This posts an event / message of the specified type with the - * provided argument to this instance's platform-specific event / - * message queue. - * - * @note - * This is an implementation for LwIP. - * - * @param[in,out] aLayer A pointer to the layer instance to which the - * event / message is being posted. - * - * @param[in,out] aContext Platform-specific context data passed to - * the layer initialization method, ::Init. - * - * @param[in,out] aTarget A pointer to the InetLayer object making the - * post request. - * - * @param[in] aType The type of event to post. - * - * @param[in,out] anArg The argument associated with the event to post. - * - * @return #INET_NO_ERROR on success; otherwise, a specific error indicating - * the reason for initialization failure. - * - */ -INET_ERROR PostEvent(Inet::InetLayer * aInetLayer, void * aContext, InetLayerBasis * aTarget, InetEventType aEventType, - uintptr_t aArgument) -{ - chip::System::Layer & lSystemLayer = *aInetLayer->mSystemLayer; - chip::System::Object & lObject = *aTarget; - chip::System::Error lReturn = chip::System::Platform::Layer::PostEvent(lSystemLayer, aContext, lObject, aEventType, aArgument); - - return static_cast(lReturn); -} - -/** - * This is a platform-specific event / message dispatch hook. This may - * be overridden by assserting the preprocessor definition, - * INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS. - * - * This effects an event loop, waiting on a queue that services this - * instance, pulling events off of that queue, and then dispatching - * them for handling. - * - * @note - * This is an implementation for LwIP. - * - * @param[in,out] aInetLayer A pointer to the layer instance for which - * events / messages are being dispatched. - * - * @param[in,out] aContext Platform-specific context data passed to - * the layer initialization method, ::Init. - * - * @retval #INET_ERROR_BAD_ARGS If #aLayer or #aContext is NULL. - * @retval #INET_ERROR_INCORRECT_STATE If the state of the InetLayer - * object is incorrect. - * @retval #INET_ERROR_UNEXPECTED_EVENT If an event type is unrecognized. - * @retval #INET_NO_ERROR On success. - * - */ -INET_ERROR DispatchEvents(Inet::InetLayer * aInetLayer, void * aContext) -{ - chip::System::Layer & lSystemLayer = *aInetLayer->mSystemLayer; - chip::System::Error lReturn = chip::System::Platform::Layer::DispatchEvents(lSystemLayer, aContext); - - return static_cast(lReturn); -} - -/** - * This is a platform-specific event / message dispatch hook. This may - * be overridden by assserting the preprocessor definition, - * INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS. - * - * This dispatches the specified event for handling, unmarshalling the - * type and arguments from the event for hand off to - * InetLayer::HandleEvent for the actual dispatch. - * - * @note - * This is an implementation for LwIP. - * - * @param[in,out] aInetLayer A pointer to the layer instance for which - * events / messages are being dispatched. - * - * @param[in,out] aContext Platform-specific context data passed to - * the layer initialization method, ::Init. - * - * @param[in] aEvent The platform-specific event object to - * dispatch for handling. - * - * @retval #INET_ERROR_BAD_ARGS If #aLayer or the event target is - * NULL. - * @retval #INET_ERROR_UNEXPECTED_EVENT If the event type is unrecognized. - * @retval #INET_ERROR_INCORRECT_STATE If the state of the InetLayer - * object is incorrect. - * @retval #INET_NO_ERROR On success. - * - */ -INET_ERROR DispatchEvent(Inet::InetLayer * aInetLayer, void * aContext, InetEvent aEvent) -{ - chip::System::Layer & lSystemLayer = *aInetLayer->mSystemLayer; - chip::System::Error lReturn = chip::System::Platform::Layer::DispatchEvent(lSystemLayer, aContext, aEvent); - - return static_cast(lReturn); -} - -/** - * This is a platform-specific event / message dispatch hook. This may be overridden by assserting the preprocessor definition, - * #INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS. - * - * @note - * This is an implementation for LwIP. - * - * @param[in,out] aInetLayer A reference to the layer instance for which events / messages are being dispatched. - * @param[in,out] aContext Platform-specific context data passed to the layer initialization method, ::Init. - * @param[in] aMilliseconds The number of milliseconds to set for the timer. - * - * @retval #INET_NO_ERROR Always succeeds unless overridden. - */ -INET_ERROR StartTimer(Inet::InetLayer * aInetLayer, void * aContext, uint32_t aMilliseconds) -{ - chip::System::Layer & lSystemLayer = *aInetLayer->mSystemLayer; - chip::System::Error lReturn = chip::System::Platform::Layer::StartTimer(lSystemLayer, aContext, aMilliseconds); - - return static_cast(lReturn); -} - -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP && !INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - } // namespace InetLayer } // namespace Platform diff --git a/src/inet/InetLayer.h b/src/inet/InetLayer.h index 01889c0df8d602..00b2b3206b8739 100644 --- a/src/inet/InetLayer.h +++ b/src/inet/InetLayer.h @@ -21,7 +21,7 @@ * This file defines classes for abstracting access to and * interactions with a platform- and system-specific Internet * Protocol stack which, as of this implementation, may be either - * BSD/POSIX Sockets or LwIP. + * BSD/POSIX Sockets, LwIP or Network.framework. * * Major abstractions provided are: * @@ -78,10 +78,6 @@ #include #endif // INET_CONFIG_ENABLE_TUN_ENDPOINT -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#include -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - #if CHIP_SYSTEM_CONFIG_USE_SOCKETS #if INET_CONFIG_ENABLE_DNS_RESOLVER && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS #include @@ -125,18 +121,6 @@ extern void DidInit(Inet::InetLayer * aLayer, void * aContext, INET_ERROR anErro extern INET_ERROR WillShutdown(Inet::InetLayer * aLayer, void * aContext); extern void DidShutdown(Inet::InetLayer * aLayer, void * aContext, INET_ERROR anError); -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#if CHIP_SYSTEM_CONFIG_USE_LWIP - -extern INET_ERROR PostEvent(Inet::InetLayer * aLayer, void * aContext, InetLayerBasis * aTarget, InetEventType aType, - uintptr_t anArg); -extern INET_ERROR DispatchEvents(Inet::InetLayer * aLayer, void * aContext); -extern INET_ERROR DispatchEvent(Inet::InetLayer * aLayer, void * aContext, InetEvent anEvent); -extern INET_ERROR StartTimer(Inet::InetLayer * aLayer, void * aContext, uint32_t aDurMS); - -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - } // namespace InetLayer } // namespace Platform @@ -199,10 +183,6 @@ class DLL_EXPORT InetLayer InetLayer(void); -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - INET_ERROR Init(void * aContext); -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - INET_ERROR Init(chip::System::Layer & aSystemLayer, void * aContext); INET_ERROR Shutdown(void); @@ -247,39 +227,6 @@ class DLL_EXPORT InetLayer INET_ERROR GetLinkLocalAddr(InterfaceId link, IPAddress * llAddr); bool MatchLocalIPv6Subnet(const IPAddress & addr); -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - /** - * @brief - * This function is the callback to be invoked when a one-shot timer fires. - * - * @param[in] inetLayer A pointer to the InetLayer instance this timer was - * started on. - * - * @param[in] appState A pointer to an application state object registered - * when this timer was started. - * - * @param[in] err #INET_NO_ERROR unconditionally. - */ - typedef void (*TimerCompleteFunct)(InetLayer * inetLayer, void * appState, INET_ERROR err); - INET_ERROR StartTimer(uint32_t durMS, TimerCompleteFunct onComplete, void * appState); - void CancelTimer(TimerCompleteFunct onComplete, void * appState); - -#if CHIP_SYSTEM_CONFIG_USE_LWIP - INET_ERROR PostEvent(InetLayerBasis * target, InetEventType type, uintptr_t arg); - INET_ERROR DispatchEvents(void); - INET_ERROR DispatchEvent(InetEvent aEvent); - INET_ERROR HandleEvent(InetLayerBasis & target, InetEventType type, uintptr_t arg); - - // Timer Management - INET_ERROR StartPlatformTimer(uint32_t inDurMS); - INET_ERROR HandlePlatformTimer(void); -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - void WakeSelect(void); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - #if CHIP_SYSTEM_CONFIG_USE_SOCKETS void PrepareSelect(int & nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval & sleepTime); void HandleSelectResult(int selectRes, fd_set * readfds, fd_set * writefds, fd_set * exceptfds); @@ -351,10 +298,6 @@ class DLL_EXPORT InetLayer #endif // !INET_CONFIG_MAX_DROPPABLE_EVENTS #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - chip::System::Layer mImplicitSystemLayer; -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - #if INET_CONFIG_ENABLE_TCP_ENDPOINT && INET_TCP_IDLE_CHECK_INTERVAL > 0 static void HandleTCPInactivityTimer(chip::System::Layer * aSystemLayer, void * aAppState, chip::System::Error aError); #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT && INET_TCP_IDLE_CHECK_INTERVAL > 0 @@ -378,16 +321,6 @@ class DLL_EXPORT InetLayer friend void Platform::InetLayer::DidShutdown(Inet::InetLayer * aLayer, void * aContext, INET_ERROR anError); bool IsIdleTimerRunning(void); - -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#if CHIP_SYSTEM_CONFIG_USE_LWIP - friend INET_ERROR Platform::InetLayer::PostEvent(Inet::InetLayer * aLayer, void * aContext, InetLayerBasis * aTarget, - InetEventType aType, uintptr_t anArg); - friend INET_ERROR Platform::InetLayer::DispatchEvents(Inet::InetLayer * aLayer, void * aContext); - friend INET_ERROR Platform::InetLayer::DispatchEvent(Inet::InetLayer * aLayer, void * aContext, InetEvent anEvent); - friend INET_ERROR Platform::InetLayer::StartTimer(Inet::InetLayer * aLayer, void * aContext, uint32_t aDurMS); -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES }; inline chip::System::Layer * InetLayer::SystemLayer(void) const @@ -395,32 +328,6 @@ inline chip::System::Layer * InetLayer::SystemLayer(void) const return mSystemLayer; } -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -inline INET_ERROR InetLayer::Init(void * aContext) -{ - return Init(mImplicitSystemLayer, aContext); -} - -#if CHIP_SYSTEM_CONFIG_USE_LWIP -inline INET_ERROR InetLayer::DispatchEvent(InetEvent aEvent) -{ - return mSystemLayer->DispatchEvent(aEvent); -} - -inline INET_ERROR InetLayer::HandleEvent(InetLayerBasis & target, InetEventType type, uintptr_t arg) -{ - return mSystemLayer->HandleEvent(target, type, arg); -} -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS -inline void InetLayer::WakeSelect(void) -{ - mSystemLayer->WakeSelect(); -} -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - /** * @class IPPacketInfo * diff --git a/src/inet/InetLayerBasis.h b/src/inet/InetLayerBasis.h index 22ca0bcf6fe10c..0acfb55c92a902 100644 --- a/src/inet/InetLayerBasis.h +++ b/src/inet/InetLayerBasis.h @@ -93,10 +93,6 @@ inline void InetLayerBasis::InitInetLayerBasis(InetLayer & aInetLayer, void * aA mInetLayer = &aInetLayer; } -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -typedef InetLayerBasis InetLayerObject; -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - #if CHIP_SYSTEM_CONFIG_USE_SOCKETS /** diff --git a/src/inet/InetLayerEvents.h b/src/inet/InetLayerEvents.h index 98b10b351de822..ba5315519d9acc 100644 --- a/src/inet/InetLayerEvents.h +++ b/src/inet/InetLayerEvents.h @@ -35,24 +35,6 @@ namespace chip { namespace Inet { -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -/** - * @typedef The basic type for all InetLayer events. - * - * This is defined to a platform- or system-specific type. - * - */ -typedef INET_CONFIG_EVENT_TYPE InetEventType; - -/** - * @typedef The basic object for all InetLayer events. - * - * This is defined to a platform- or system-specific type. - * - */ -typedef INET_CONFIG_EVENT_OBJECT_TYPE InetEvent; -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - /** * The Inet layer event type definitions. * diff --git a/src/inet/InetTimer.h b/src/inet/InetTimer.h deleted file mode 100644 index 8dd63b445b522b..00000000000000 --- a/src/inet/InetTimer.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * Copyright (c) 2020 Project CHIP Authors - * Copyright (c) 2013-2017 Nest Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 - * This file defines macros, constants, data structures, and - * functions that represent a wrapper around one-shot timer objects - * - */ - -#ifndef INETTIMER_H -#define INETTIMER_H - -#include - -#if !INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -#error "#include // while !INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES." - -#else // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#include - -/** - * Number of nanoseconds in a microsecond - */ - -#define INET_NANOSECONDS_PER_MICROSECOND chip::System::kTimerFactor_nano_per_micro - -/** - * Number of nanoseconds in a millisecond - */ -#define INET_NANOSECONDS_PER_MILLISECOND chip::System::kTimerFactor_nano_per_milli - -/** - * Number of microseconds in a millisecond - */ -#define INET_MICROSECONDS_PER_MILLISECOND chip::System::kTimerFactor_micro_per_milli - -/** - * Number of milliseconds in a second - */ -#define INET_MILLISECONDS_PER_SECOND chip::System::kTimerFactor_milli_per_unit - -/** - * Number of microseconds in a second - */ -#define INET_MICROSECONDS_PER_SECOND chip::System::kTimerFactor_micro_per_unit - -namespace chip { -namespace Inet { - -class InetLayer; - -class InetTimer -{ - friend class InetLayer; - - InetTimer(void); - InetTimer(const InetTimer &); - InetTimer & operator=(const InetTimer &); - -public: - typedef chip::System::Timer::Epoch Time; - - static Time GetTimeMillis(void); - -protected: -}; - -inline InetTimer::Time InetTimer::GetTimeMillis(void) -{ - return chip::System::Timer::GetCurrentEpoch(); -} - -} // namespace Inet -} // namespace chip - -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#endif // !defined(INETTIMER_H) diff --git a/src/inet/UDPEndPoint.cpp b/src/inet/UDPEndPoint.cpp index 29a3102fd865a3..9416324ab43bfb 100644 --- a/src/inet/UDPEndPoint.cpp +++ b/src/inet/UDPEndPoint.cpp @@ -151,6 +151,13 @@ INET_ERROR UDPEndPoint::Bind(IPAddressType addrType, IPAddress addr, uint16_t po { INET_ERROR res = INET_NO_ERROR; +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + + nw_parameters_configure_protocol_block_t configure_tls; + nw_parameters_t parameters; + +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + if (mState != kState_Ready && mState != kState_Bound) { res = INET_ERROR_INCORRECT_STATE; @@ -249,6 +256,24 @@ INET_ERROR UDPEndPoint::Bind(IPAddressType addrType, IPAddress addr, uint16_t po #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + + if (intfId != INET_NULL_INTERFACEID) + { + res = INET_ERROR_NOT_IMPLEMENTED; + goto exit; + } + + configure_tls = NW_PARAMETERS_DISABLE_PROTOCOL; + parameters = nw_parameters_create_secure_udp(configure_tls, NW_PARAMETERS_DEFAULT_CONFIGURATION); + + res = IPEndPointBasis::Bind(addrType, addr, port, parameters); + SuccessOrExit(res); + + mParameters = parameters; + +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + if (res == INET_NO_ERROR) { mState = kState_Bound; @@ -319,6 +344,13 @@ INET_ERROR UDPEndPoint::Listen(void) #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + + res = StartListener(); + SuccessOrExit(res); + +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + if (res == INET_NO_ERROR) { mState = kState_Listening; @@ -379,6 +411,10 @@ void UDPEndPoint::Close(void) #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + IPEndPointBasis::ReleaseAll(); +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + mState = kState_Closed; } } @@ -657,6 +693,13 @@ INET_ERROR UDPEndPoint::SendMsg(const IPPacketInfo * pktInfo, PacketBuffer * msg PacketBuffer::Free(msg); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + res = IPEndPointBasis::SendMsg(pktInfo, msg, sendFlags); + + if ((sendFlags & kSendFlag_RetainBuffer) == 0) + PacketBuffer::Free(msg); +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + exit: CHIP_SYSTEM_FAULT_INJECT_ASYNC_EVENT(); @@ -720,11 +763,15 @@ INET_ERROR UDPEndPoint::BindInterface(IPAddressType addrType, InterfaceId intfId SuccessOrExit(err); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + err = INET_ERROR_UNKNOWN_INTERFACE; + SuccessOrExit(err); +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + if (err == INET_NO_ERROR) { mState = kState_Bound; } - exit: return err; } @@ -752,6 +799,10 @@ InterfaceId UDPEndPoint::GetBoundInterface(void) #if CHIP_SYSTEM_CONFIG_USE_SOCKETS return mBoundIntfId; #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS + +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + return INET_NULL_INTERFACEID; +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK } uint16_t UDPEndPoint::GetBoundPort(void) @@ -763,6 +814,11 @@ uint16_t UDPEndPoint::GetBoundPort(void) #if CHIP_SYSTEM_CONFIG_USE_SOCKETS return mBoundPort; #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS + +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + nw_endpoint_t endpoint = nw_parameters_copy_local_endpoint(mParameters); + return nw_endpoint_get_port(endpoint); +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK } #if CHIP_SYSTEM_CONFIG_USE_LWIP diff --git a/src/inet/tests/Makefile.am b/src/inet/tests/Makefile.am index 5dd74e71b52538..dcd8bd2130d8cf 100644 --- a/src/inet/tests/Makefile.am +++ b/src/inet/tests/Makefile.am @@ -46,9 +46,7 @@ lib_LIBRARIES = \ libInetLayerTests_a_SOURCES = \ TestInetAddress.cpp \ - TestInetBuffer.cpp \ TestInetErrorStr.cpp \ - TestInetTimer.cpp \ $(NULL) libInetLayerTests_adir = $(includedir)/inet @@ -125,7 +123,7 @@ check_SCRIPTS = \ TESTS_ENVIRONMENT = \ $(NULL) -if CHIP_TARGET_STYLE_EMBEDDED +if CHIP_DEVICE_LAYER_TARGET_ESP32 check_SCRIPTS += \ qemu_inet_tests.sh \ @@ -137,7 +135,7 @@ TESTS_ENVIRONMENT += \ QEMU_TEST_TARGET=libInetLayerTests.a \ $(NULL) -else # CHIP_TARGET_STYLE_EMBEDDED +else # CHIP_DEVICE_LAYER_TARGET_ESP32 # Test executables that should be built but not installed and should # always be built to ensure overall "build sanity". @@ -162,12 +160,10 @@ noinst_PROGRAMS = \ check_PROGRAMS += \ TestInetAddress \ - TestInetBuffer \ TestInetErrorStr \ - TestInetTimer \ $(NULL) -endif # CHIP_TARGET_STYLE_EMBEDDED +endif # CHIP_DEVICE_LAYER_TARGET_ESP32 # Test applications and scripts that should be built and run when the # 'check' target is run. @@ -185,10 +181,6 @@ TestLwIPDNS_LDADD = libTestInetCommon.a $(CO TestInetAddress_SOURCES = TestInetAddressDriver.cpp TestInetAddress_LDADD = $(COMMON_LDADD) -TestInetBuffer_SOURCES = TestInetBufferDriver.cpp \ - $(NULL) -TestInetBuffer_LDADD = $(COMMON_LDADD) - TestInetEndPoint_SOURCES = TestInetEndPoint.cpp \ $(NULL) TestInetEndPoint_LDADD = libTestInetCommon.a $(COMMON_LDADD) @@ -208,10 +200,6 @@ TestInetLayerMulticast_SOURCES = TestInetLayerMulticast.c $(NULL) TestInetLayerMulticast_LDADD = libTestInetCommon.a $(COMMON_LDADD) -TestInetTimer_SOURCES = TestInetTimerDriver.cpp \ - $(NULL) -TestInetTimer_LDADD = $(COMMON_LDADD) - # # Foreign make dependencies # diff --git a/src/inet/tests/TestInetBuffer.cpp b/src/inet/tests/TestInetBuffer.cpp deleted file mode 100644 index dfb7dceffe4efa..00000000000000 --- a/src/inet/tests/TestInetBuffer.cpp +++ /dev/null @@ -1,1179 +0,0 @@ -/* - * - * Copyright (c) 2020 Project CHIP Authors - * Copyright (c) 2015-2017 Nest Labs, Inc. - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 - * This file implements a unit test suite for - * chip::Inet::InetBuffer, a class that provides - * structure for network buffer management. - * - */ - -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif - -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS -#endif - -#include "TestInetLayer.h" - -#include - -#include -#include -#include - -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -#include - -#include - -#include - -//#include "ToolCommon.h" - -#define MEM_ALIGN_SIZE(SZ, ALIGN) (((SZ) + (ALIGN) -1) & ~((ALIGN) -1)) -#if INET_LWIP -#define INET_BUF_SIZE LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) -#define INET_BUF_HEADER_SIZE LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) -#else -#define INET_BUF_SIZE CHIP_SYSTEM_PACKETBUFFER_SIZE -#define INET_BUF_HEADER_SIZE MEM_ALIGN_SIZE(sizeof(InetBuffer), 4) -#endif - -#define INET_TO_PBUF(x) (static_cast(static_cast(x))) -#define PBUF_TO_INET(x) (static_cast(static_cast(x))) - -using namespace chip::Inet; - -// Test input vector format. - -struct TestContext -{ - uint16_t init_len; - uint16_t reserved_size; - uint8_t * start_buffer; - uint8_t * end_buffer; - uint8_t * payload_ptr; - struct pbuf * buf; -}; - -// Test input data. - -static struct TestContext sContext[] = { { 0, 0, NULL, NULL, NULL, NULL }, - { 0, 10, NULL, NULL, NULL, NULL }, - { 0, 128, NULL, NULL, NULL, NULL }, - { 0, CHIP_SYSTEM_PACKETBUFFER_SIZE, NULL, NULL, NULL, NULL }, - { 0, INET_BUF_SIZE, NULL, NULL, NULL, NULL } }; - -static const uint16_t sLengths[] = { 0, 1, 10, 128, INET_BUF_SIZE, UINT16_MAX }; - -// Number of test context examples. -static const size_t kTestElements = sizeof(sContext) / sizeof(struct TestContext); -static const size_t kTestLengths = sizeof(sLengths) / sizeof(uint16_t); - -// Utility functions. - -/** - * Free allocated test buffer memory. - */ -static void BufferFree(struct TestContext * theContext) -{ - if (theContext->buf != NULL) - { - InetBuffer::Free(PBUF_TO_INET(theContext->buf)); - } -} - -/** - * Allocate memory for a test buffer and configure according to test context. - */ -static void BufferAlloc(struct TestContext * theContext) -{ - if (theContext->buf == NULL) - { - theContext->buf = INET_TO_PBUF(InetBuffer::New(0)); - } - - if (theContext->buf == NULL) - { - fprintf(stderr, "Failed to allocate %zuB memory: %s\n", ((size_t) INET_BUF_SIZE), strerror(errno)); - exit(EXIT_FAILURE); - } - - memset(theContext->buf, 0, INET_BUF_SIZE); - - theContext->start_buffer = reinterpret_cast(theContext->buf); - theContext->end_buffer = reinterpret_cast(theContext->buf) + INET_BUF_SIZE; - - if (INET_BUF_HEADER_SIZE + theContext->reserved_size > INET_BUF_SIZE) - { - theContext->payload_ptr = (theContext->start_buffer + INET_BUF_SIZE); - } - else - { - theContext->payload_ptr = (theContext->start_buffer + INET_BUF_HEADER_SIZE + theContext->reserved_size); - } -} - -/** - * Setup buffer layout as it is used by InetBuffer class. - */ -static InetBuffer * PrepareTestBuffer(struct TestContext * theContext) -{ - BufferAlloc(theContext); - - theContext->buf->next = NULL; - theContext->buf->payload = theContext->payload_ptr; - theContext->buf->ref = 1; - theContext->buf->len = theContext->init_len; - theContext->buf->tot_len = theContext->init_len; -#if INET_LWIP - theContext->buf->type = PBUF_POOL; -#endif - - return reinterpret_cast(theContext->buf); -} - -// Test functions invoked from the suite. - -/** - * Test InetBuffer::Start() function. - */ -static void CheckStart(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - InetBuffer * buffer = PrepareTestBuffer(theContext); - - NL_TEST_ASSERT(inSuite, buffer->Start() == theContext->payload_ptr); - - theContext++; - } -} - -/** - * Test InetBuffer::SetStart() function. - * - * Description: For every buffer-configuration from inContext, create a - * buffer's instance according to the configuration. Next, - * for any offset value from start_offset[], pass it to the - * buffer's instance through SetStart method. Then, verify that - * the beginning of the buffer has been correctly internally - * adjusted according to the offset value passed into the - * SetStart() method. - */ -static void CheckSetStart(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - const int start_offset[] = { -static_cast(INET_BUF_SIZE), -128, -1, 0, 1, 128, static_cast(INET_BUF_SIZE) }; - - for (size_t s = 0; s < sizeof(start_offset) / sizeof(int); s++) - { - uint8_t * test_start = theContext->payload_ptr + start_offset[s]; - uint8_t * verify_start = test_start; - InetBuffer * buffer = PrepareTestBuffer(theContext); - - buffer->SetStart(test_start); - - if (verify_start <= theContext->start_buffer + INET_BUF_HEADER_SIZE) - { - // Set start before valid payload beginning. - verify_start = theContext->start_buffer + INET_BUF_HEADER_SIZE; - } - - if (verify_start >= theContext->end_buffer) - { - // Set start after valid payload beginning. - verify_start = theContext->end_buffer; - } - - NL_TEST_ASSERT(inSuite, theContext->buf->payload == verify_start); - - if ((verify_start - theContext->payload_ptr) > theContext->init_len) - { - // Set start to the beginning of payload, right after buffer's header. - NL_TEST_ASSERT(inSuite, theContext->buf->len == 0); - } - else - { - // Set start to somewhere between the end of the buffer's - // header and the end of payload. - NL_TEST_ASSERT(inSuite, theContext->buf->len == (theContext->init_len - (verify_start - theContext->payload_ptr))); - } - } - theContext++; - } -} - -/** - * Test InetBuffer::DataLength() function. - */ -static void CheckDataLength(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - InetBuffer * buffer = PrepareTestBuffer(theContext); - - NL_TEST_ASSERT(inSuite, buffer->DataLength() == theContext->buf->len); - - theContext++; - } -} - -/** - * Test InetBuffer::SetDataLength() function. - * - * Description: Take two initial configurations of InetBuffer from - * inContext and create two InetBuffer instances based on those - * configurations. For any two buffers, call SetDataLength with - * different value from sLength[]. If two buffers are created with - * the same configuration, test SetDataLength on one buffer, - * without specifying the head of the buffer chain. Otherwise, - * test SetDataLength with one buffer being down the chain and the - * other one being passed as the head of the chain. After calling - * the method verify that data lenghts were correctly adjusted. - */ -static void CheckSetDataLength(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theFirstContext = static_cast(inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - struct TestContext * theSecondContext = static_cast(inContext); - - for (size_t jth = 0; jth < kTestElements; jth++) - { - for (size_t n = 0; n < kTestLengths; n++) - { - InetBuffer * buffer_1 = PrepareTestBuffer(theFirstContext); - InetBuffer * buffer_2 = PrepareTestBuffer(theSecondContext); - - if (theFirstContext == theSecondContext) - { - // headOfChain (the second arg) is NULL - buffer_2->SetDataLength(sLengths[n], NULL); - - if (sLengths[n] > (theSecondContext->end_buffer - theSecondContext->payload_ptr)) - { - NL_TEST_ASSERT( - inSuite, theSecondContext->buf->len == (theSecondContext->end_buffer - theSecondContext->payload_ptr)); - NL_TEST_ASSERT(inSuite, - theSecondContext->buf->tot_len == - (theSecondContext->end_buffer - theSecondContext->payload_ptr)); - NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == NULL); - } - else - { - NL_TEST_ASSERT(inSuite, theSecondContext->buf->len == sLengths[n]); - NL_TEST_ASSERT(inSuite, theSecondContext->buf->tot_len == sLengths[n]); - NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == NULL); - } - } - else - { - // headOfChain (the second arg) is buffer_1 - buffer_2->SetDataLength(sLengths[n], buffer_1); - - if (sLengths[n] > (theSecondContext->end_buffer - theSecondContext->payload_ptr)) - { - NL_TEST_ASSERT( - inSuite, theSecondContext->buf->len == (theSecondContext->end_buffer - theSecondContext->payload_ptr)); - NL_TEST_ASSERT(inSuite, - theSecondContext->buf->tot_len == - (theSecondContext->end_buffer - theSecondContext->payload_ptr)); - NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == NULL); - - NL_TEST_ASSERT(inSuite, - theFirstContext->buf->tot_len == - (theFirstContext->init_len + - static_cast(theSecondContext->end_buffer - theSecondContext->payload_ptr) - - static_cast(theSecondContext->init_len))); - } - else - { - NL_TEST_ASSERT(inSuite, theSecondContext->buf->len == sLengths[n]); - NL_TEST_ASSERT(inSuite, theSecondContext->buf->tot_len == sLengths[n]); - NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == NULL); - - NL_TEST_ASSERT(inSuite, - theFirstContext->buf->tot_len == - (theFirstContext->init_len + static_cast(sLengths[n]) - - static_cast(theSecondContext->init_len))); - } - } - } - - theSecondContext++; - } - - theFirstContext++; - } -} - -/** - * Test InetBuffer::TotalLength() function. - */ -static void CheckTotalLength(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - InetBuffer * buffer = PrepareTestBuffer(theContext); - - NL_TEST_ASSERT(inSuite, buffer->TotalLength() == theContext->init_len); - - theContext++; - } -} - -/** - * Test InetBuffer::MaxDataLength() function. - */ -static void CheckMaxDataLength(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - InetBuffer * buffer = PrepareTestBuffer(theContext); - - NL_TEST_ASSERT(inSuite, buffer->MaxDataLength() == (theContext->end_buffer - theContext->payload_ptr)); - - theContext++; - } -} - -/** - * Test InetBuffer::AvailableDataLength() function. - */ -static void CheckAvailableDataLength(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - InetBuffer * buffer = PrepareTestBuffer(theContext); - - NL_TEST_ASSERT( - inSuite, buffer->AvailableDataLength() == ((theContext->end_buffer - theContext->payload_ptr) - theContext->init_len)); - - theContext++; - } -} - -/** - * Test InetBuffer::ReservedSize() function. - */ -static void CheckReservedSize(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - InetBuffer * buffer = PrepareTestBuffer(theContext); - - if (INET_BUF_HEADER_SIZE + theContext->reserved_size > INET_BUF_SIZE) - { - NL_TEST_ASSERT(inSuite, buffer->ReservedSize() == (INET_BUF_SIZE - INET_BUF_HEADER_SIZE)); - } - else - { - NL_TEST_ASSERT(inSuite, buffer->ReservedSize() == theContext->reserved_size); - } - - theContext++; - } -} - -/** - * Test InetBuffer::AddToEnd() function. - * - * Description: Take three initial configurations of InetBuffer from - * inContext, create three InetBuffers based on those - * configurations and then link those buffers together with - * InetBuffer:AddToEnd(). Then, assert that after connecting - * buffers together, their internal states are correctly updated. - * This test function tests linking any combination of three - * buffer-configurations passed within inContext. - */ -static void CheckAddToEnd(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theFirstContext = static_cast(inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - struct TestContext * theSecondContext = static_cast(inContext); - - for (size_t jth = 0; jth < kTestElements; jth++) - { - struct TestContext * theThirdContext = static_cast(inContext); - - for (size_t kth = 0; kth < kTestElements; kth++) - { - InetBuffer * buffer_1 = NULL; - InetBuffer * buffer_2 = NULL; - InetBuffer * buffer_3 = NULL; - - if (theFirstContext == theSecondContext || theFirstContext == theThirdContext || - theSecondContext == theThirdContext) - { - theThirdContext++; - continue; - } - - buffer_1 = PrepareTestBuffer(theFirstContext); - buffer_2 = PrepareTestBuffer(theSecondContext); - buffer_3 = PrepareTestBuffer(theThirdContext); - - buffer_1->AddToEnd(buffer_2); - - NL_TEST_ASSERT(inSuite, theFirstContext->buf->tot_len == (theFirstContext->init_len + theSecondContext->init_len)); - NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == theSecondContext->buf); - NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == NULL); - - NL_TEST_ASSERT(inSuite, theThirdContext->buf->next == NULL); - - buffer_1->AddToEnd(buffer_3); - - NL_TEST_ASSERT(inSuite, - theFirstContext->buf->tot_len == - (theFirstContext->init_len + theSecondContext->init_len + theThirdContext->init_len)); - NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == theSecondContext->buf); - NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == theThirdContext->buf); - NL_TEST_ASSERT(inSuite, theThirdContext->buf->next == NULL); - - theThirdContext++; - } - - theSecondContext++; - } - - theFirstContext++; - } -} - -/** - * Test InetBuffer::DetachTail() function. - * - * Description: Take two initial configurations of InetBuffer from - * inContext and create two InetBuffer instances based on those - * configurations. Next, link those buffers together, with the first - * buffer instance pointing to the second one. Then, call DetachTail() - * on the first buffer to unlink the second buffer. After the call, - * verify correct internal state of the first buffer. - */ -static void CheckDetachTail(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theFirstContext = static_cast(inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - struct TestContext * theSecondContext = static_cast(inContext); - - for (size_t jth = 0; jth < kTestElements; jth++) - { - InetBuffer * buffer_1 = PrepareTestBuffer(theFirstContext); - InetBuffer * buffer_2 = PrepareTestBuffer(theSecondContext); - InetBuffer * returned = NULL; - - if (theFirstContext != theSecondContext) - { - theFirstContext->buf->next = theSecondContext->buf; - theFirstContext->buf->tot_len += theSecondContext->init_len; - } - - returned = buffer_1->DetachTail(); - - NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == NULL); - NL_TEST_ASSERT(inSuite, theFirstContext->buf->tot_len == theFirstContext->init_len); - - if (theFirstContext != theSecondContext) - { - NL_TEST_ASSERT(inSuite, returned == buffer_2); - } - - theSecondContext++; - } - - theFirstContext++; - } -} - -/** - * Test InetBuffer::CompactHead() function. - * - * Description: Take two initial configurations of InetBuffer from - * inContext and create two InetBuffer instances based on those - * configurations. Next, set both buffers' data length to any - * combination of values from sLengths[] and link those buffers - * into a chain. Then, call CompactHead() on the first buffer in - * the chain. After calling the method, verify correctly adjusted - * state of the first buffer. - */ -static void CheckCompactHead(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theFirstContext = static_cast(inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - struct TestContext * theSecondContext = static_cast(inContext); - - for (size_t jth = 0; jth < kTestElements; jth++) - { - // start with various initial length for the first buffer - for (size_t k = 0; k < kTestLengths; k++) - { - // start with various initial length for the second buffer - for (size_t l = 0; l < kTestLengths; l++) - { - InetBuffer * buffer_1 = PrepareTestBuffer(theFirstContext); - InetBuffer * buffer_2 = PrepareTestBuffer(theSecondContext); - uint16_t len1 = 0; - uint16_t len2 = 0; - - buffer_1->SetDataLength(sLengths[k], buffer_1); - len1 = buffer_1->DataLength(); - - if (theFirstContext != theSecondContext) - { - theFirstContext->buf->next = theSecondContext->buf; - - // Add various lengths to the second buffer - buffer_2->SetDataLength(sLengths[l], buffer_1); - len2 = buffer_2->DataLength(); - } - - buffer_1->CompactHead(); - - NL_TEST_ASSERT(inSuite, - theFirstContext->buf->payload == (theFirstContext->start_buffer + INET_BUF_HEADER_SIZE)); - - /* verify length of the first buffer */ - if (theFirstContext == theSecondContext) - { - NL_TEST_ASSERT(inSuite, theFirstContext->buf->tot_len == len1); - } - else if (theFirstContext->buf->tot_len > buffer_1->MaxDataLength()) - { - NL_TEST_ASSERT(inSuite, theFirstContext->buf->len == buffer_1->MaxDataLength()); - NL_TEST_ASSERT(inSuite, - theSecondContext->buf->len == theFirstContext->buf->tot_len - buffer_1->MaxDataLength()); - } - else - { - NL_TEST_ASSERT(inSuite, theFirstContext->buf->len == theFirstContext->buf->tot_len); - if (len1 >= buffer_1->MaxDataLength() && len2 == 0) - { - /* make sure the second buffer is not freed */ - NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == theSecondContext->buf); - } - else - { - /* make sure the second buffer is freed */ - NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == NULL); - theSecondContext->buf = NULL; - } - } - } - } - theSecondContext++; - } - - theFirstContext++; - } -} - -/** - * Test InetBuffer::ConsumeHead() function. - * - * Description: For every buffer-configuration from inContext, create a - * buffer's instance according to the configuration. Next, - * for any value from sLengths[], pass it to the buffer's - * instance through ConsumeHead() method. Then, verify that - * the internal state of the buffer has been correctly - * adjusted according to the value passed into the method. - */ -static void CheckConsumeHead(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - for (size_t n = 0; n < kTestLengths; n++) - { - InetBuffer * buffer = PrepareTestBuffer(theContext); - - buffer->ConsumeHead(sLengths[n]); - - if (sLengths[n] > theContext->init_len) - { - NL_TEST_ASSERT(inSuite, theContext->buf->payload == (theContext->payload_ptr + theContext->init_len)); - NL_TEST_ASSERT(inSuite, theContext->buf->len == 0); - NL_TEST_ASSERT(inSuite, theContext->buf->tot_len == 0); - } - else - { - NL_TEST_ASSERT(inSuite, theContext->buf->payload == (theContext->payload_ptr + sLengths[n])); - NL_TEST_ASSERT(inSuite, theContext->buf->len == (theContext->buf->len - sLengths[n])); - NL_TEST_ASSERT(inSuite, theContext->buf->tot_len == (theContext->buf->tot_len - sLengths[n])); - } - - if (theContext->buf->ref == 0) - { - theContext->buf = NULL; - } - } - - theContext++; - } -} - -/** - * Test InetBuffer::Consume() function. - * - * Description: Take two different initial configurations of InetBuffer from - * inContext and create two InetBuffer instances based on those - * configurations. Next, set both buffers' data length to any - * combination of values from sLengths[] and link those buffers - * into a chain. Then, call Consume() on the first buffer in - * the chain with all values from sLengths[]. After calling the - * method, verify correctly adjusted the state of the first - * buffer and appropriate return pointer from the method's call. - */ -static void CheckConsume(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theFirstContext = static_cast(inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - struct TestContext * theSecondContext = static_cast(inContext); - - for (size_t jth = 0; jth < kTestElements; jth++) - { - // consume various amounts of memory - for (size_t c = 0; c < kTestLengths; c++) - { - // start with various initial length for the first buffer - for (size_t k = 0; k < kTestLengths; k++) - { - // start with various initial length for the second buffer - for (size_t l = 0; l < kTestLengths; l++) - { - InetBuffer * buffer_1; - InetBuffer * buffer_2; - InetBuffer * returned; - uint16_t buf_1_len = 0; - uint16_t buf_2_len = 0; - - if (theFirstContext == theSecondContext) - { - continue; - } - - buffer_1 = PrepareTestBuffer(theFirstContext); - buffer_2 = PrepareTestBuffer(theSecondContext); - - theFirstContext->buf->next = theSecondContext->buf; - - // Add various lengths to buffers - buffer_1->SetDataLength(sLengths[k], buffer_1); - buffer_2->SetDataLength(sLengths[l], buffer_1); - - buf_1_len = theFirstContext->buf->len; - buf_2_len = theSecondContext->buf->len; - - returned = buffer_1->Consume(sLengths[c]); - - if (sLengths[c] == 0) - { - NL_TEST_ASSERT(inSuite, returned == buffer_1); - continue; - } - - if (sLengths[c] < buf_1_len) - { - NL_TEST_ASSERT(inSuite, returned == buffer_1); - } - else if ((sLengths[c] >= buf_1_len) && - ((sLengths[c] < buf_1_len + buf_2_len) || - ((sLengths[c] == buf_1_len + buf_2_len) && buf_2_len == 0))) - { - NL_TEST_ASSERT(inSuite, returned == buffer_2); - theFirstContext->buf = NULL; - } - else if (sLengths[c] >= (buf_1_len + buf_2_len)) - { - NL_TEST_ASSERT(inSuite, returned == NULL); - theFirstContext->buf = NULL; - theSecondContext->buf = NULL; - } - } - } - } - - theSecondContext++; - } - - theFirstContext++; - } -} - -/** - * Test InetBuffer::EnsureReservedSize() function. - * - * Description: For every buffer-configuration from inContext, create a - * buffer's instance according to the configuration. Next, - * manually specify how much space is reserved in the buffer. - * Then, verify that EnsureReservedSize() method correctly - * retrieves the amount of the reserved space. - */ -static void CheckEnsureReservedSize(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - for (size_t n = 0; n < kTestLengths; n++) - { - InetBuffer * buffer = PrepareTestBuffer(theContext); - uint16_t reserved_size = theContext->reserved_size; - - if (INET_BUF_HEADER_SIZE + theContext->reserved_size > INET_BUF_SIZE) - { - reserved_size = INET_BUF_SIZE - INET_BUF_HEADER_SIZE; - } - - if (sLengths[n] <= reserved_size) - { - NL_TEST_ASSERT(inSuite, buffer->EnsureReservedSize(sLengths[n]) == true); - continue; - } - - if ((sLengths[n] + theContext->init_len) > (INET_BUF_SIZE - INET_BUF_HEADER_SIZE)) - { - NL_TEST_ASSERT(inSuite, buffer->EnsureReservedSize(sLengths[n]) == false); - continue; - } - - NL_TEST_ASSERT(inSuite, buffer->EnsureReservedSize(sLengths[n]) == true); - NL_TEST_ASSERT(inSuite, theContext->buf->payload == (theContext->payload_ptr + sLengths[n] - reserved_size)); - } - - theContext++; - } -} - -/** - * Test InetBuffer::AlignPayload() function. - * - * Description: For every buffer-configuration from inContext, create a - * buffer's instance according to the configuration. Next, - * manually specify how much space is reserved and the - * required payload shift. Then, verify that AlignPayload() - * method correctly aligns the payload start pointer. - */ -static void CheckAlignPayload(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - for (size_t n = 0; n < kTestLengths - 1; n++) - { - InetBuffer * buffer = PrepareTestBuffer(theContext); - - if (sLengths[n] == 0) - { - NL_TEST_ASSERT(inSuite, buffer->AlignPayload(sLengths[n]) == false); - continue; - } - - uint16_t reserved_size = theContext->reserved_size; - if (INET_BUF_HEADER_SIZE + theContext->reserved_size > INET_BUF_SIZE) - { - reserved_size = INET_BUF_SIZE - INET_BUF_HEADER_SIZE; - } - - uint16_t payload_offset = (unsigned long) buffer->Start() % sLengths[n]; - uint16_t payload_shift = 0; - if (payload_offset > 0) - payload_shift = sLengths[n] - payload_offset; - - if (payload_shift <= INET_BUF_SIZE - INET_BUF_HEADER_SIZE - reserved_size) - { - NL_TEST_ASSERT(inSuite, buffer->AlignPayload(sLengths[n]) == true); - NL_TEST_ASSERT(inSuite, ((unsigned long) buffer->Start() % sLengths[n]) == 0); - } - else - { - NL_TEST_ASSERT(inSuite, buffer->AlignPayload(sLengths[n]) == false); - } - } - - theContext++; - } -} - -/** - * Test InetBuffer::Next() function. - */ -static void CheckNext(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theFirstContext = static_cast(inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - struct TestContext * theSecondContext = static_cast(inContext); - - for (size_t jth = 0; jth < kTestElements; jth++) - { - InetBuffer * buffer_1 = PrepareTestBuffer(theFirstContext); - InetBuffer * buffer_2 = PrepareTestBuffer(theSecondContext); - - if (theFirstContext != theSecondContext) - { - theFirstContext->buf->next = theSecondContext->buf; - - NL_TEST_ASSERT(inSuite, buffer_1->Next() == buffer_2); - } - else - { - NL_TEST_ASSERT(inSuite, buffer_1->Next() == NULL); - } - - NL_TEST_ASSERT(inSuite, buffer_2->Next() == NULL); - theSecondContext++; - } - - theFirstContext++; - } -} - -/** - * Test InetBuffer::AddRef() function. - */ -static void CheckAddRef(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - InetBuffer * buffer = PrepareTestBuffer(theContext); - buffer->AddRef(); - - NL_TEST_ASSERT(inSuite, theContext->buf->ref == 2); - - theContext++; - } -} - -/** - * Test InetBuffer::New() and InetBuffer::Free() functions. - * - * Description: For every buffer-configuration from inContext, create a - * buffer's instance using New() method. Then, verify that - * when the size of the reserved space passed to New() is - * greater than INET_BUF_SIZE - INET_BUF_HEADER_SIZE, the method - * returns NULL. Otherwise, check for correctness of initializing - * the new buffer's internal state. Finally, free the buffer. - */ -static void CheckNewAndFree(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - InetBuffer * buffer; - - for (size_t ith = 0; ith < kTestElements; ith++) - { - struct pbuf * pb = NULL; - - buffer = InetBuffer::New(theContext->reserved_size); - - if (theContext->reserved_size + INET_BUF_HEADER_SIZE > INET_BUF_SIZE) - { - NL_TEST_ASSERT(inSuite, buffer == NULL); - theContext++; - continue; - } - - NL_TEST_ASSERT(inSuite, buffer != NULL); - - if (buffer != NULL) - { - pb = INET_TO_PBUF(buffer); - - NL_TEST_ASSERT(inSuite, pb->len == 0); - NL_TEST_ASSERT(inSuite, pb->tot_len == 0); - NL_TEST_ASSERT(inSuite, pb->next == NULL); - NL_TEST_ASSERT(inSuite, pb->ref == 1); - } - - InetBuffer::Free(buffer); - - theContext++; - } - - // Use the rest of the buffer space - do - { - buffer = InetBuffer::New(); - } while (buffer != NULL); -} - -/** - * Test InetBuffer::Free() function. - * - * Description: Take two different initial configurations of InetBuffer from - * inContext and create two InetBuffer instances based on those - * configurations. Next, chain two buffers together and set each - * buffer's reference count to one of the values from - * init_ret_count[]. Then, call Free() on the first buffer in - * the chain and verify correctly adjusted states of the two - * buffers. - */ -static void CheckFree(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theFirstContext = static_cast(inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - struct TestContext * theSecondContext = static_cast(inContext); - - for (size_t jth = 0; jth < kTestElements; jth++) - { - const uint16_t init_ref_count[] = { 1, 2, 3 }; - const int refs = sizeof(init_ref_count) / sizeof(uint16_t); - - // start with various buffer ref counts - for (size_t r = 0; r < refs; r++) - { - InetBuffer * buffer_1; - - if (theFirstContext == theSecondContext) - { - continue; - } - - buffer_1 = PrepareTestBuffer(theFirstContext); - (void) PrepareTestBuffer(theSecondContext); - - theFirstContext->buf->next = theSecondContext->buf; - - // Add various buffer ref counts - theFirstContext->buf->ref = init_ref_count[r]; - theSecondContext->buf->ref = init_ref_count[(r + 1) % refs]; - - InetBuffer::Free(buffer_1); - - NL_TEST_ASSERT(inSuite, theFirstContext->buf->ref == (init_ref_count[r] - 1)); - - if (init_ref_count[r] == 1) - { - NL_TEST_ASSERT(inSuite, theSecondContext->buf->ref == (init_ref_count[(r + 1) % refs] - 1)); - } - else - { - NL_TEST_ASSERT(inSuite, theSecondContext->buf->ref == (init_ref_count[(r + 1) % refs])); - } - - if (init_ref_count[r] > 1) - { - NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == theSecondContext->buf); - } - - if (theFirstContext->buf->ref == 0) - { - theFirstContext->buf = NULL; - } - - if (theSecondContext->buf->ref == 0) - { - theSecondContext->buf = NULL; - } - } - - theSecondContext++; - } - - theFirstContext++; - } -} - -/** - * Test InetBuffer::FreeHead() function. - * - * Description: Take two different initial configurations of InetBuffer from - * inContext and create two InetBuffer instances based on those - * configurations. Next, chain two buffers together. Then, call - * FreeHead() on the first buffer in the chain and verify that - * the method returned pointer to the second buffer. - */ -static void CheckFreeHead(nlTestSuite * inSuite, void * inContext) -{ - struct TestContext * theFirstContext = static_cast(inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - struct TestContext * theSecondContext = static_cast(inContext); - - for (size_t jth = 0; jth < kTestElements; jth++) - { - InetBuffer * buffer_1; - InetBuffer * buffer_2; - InetBuffer * returned = NULL; - - if (theFirstContext == theSecondContext) - { - continue; - } - - buffer_1 = PrepareTestBuffer(theFirstContext); - buffer_2 = PrepareTestBuffer(theSecondContext); - - theFirstContext->buf->next = theSecondContext->buf; - - returned = InetBuffer::FreeHead(buffer_1); - - NL_TEST_ASSERT(inSuite, returned == buffer_2); - - theFirstContext->buf = NULL; - theSecondContext++; - } - - theFirstContext++; - } -} - -/** - * Test InetBuffer::BuildFreeList() function. - */ -static void CheckBuildFreeList(nlTestSuite * inSuite, void * inContext) -{ - // BuildFreeList() is a private method called automatically. - (void) inSuite; - (void) inContext; -} - -// Test Suite - -/** - * Test Suite. It lists all the test functions. - */ -static const nlTest sTests[] = { NL_TEST_DEF("InetBuffer::Start", CheckStart), - NL_TEST_DEF("InetBuffer::SetStart", CheckSetStart), - NL_TEST_DEF("InetBuffer::DataLength", CheckDataLength), - NL_TEST_DEF("InetBuffer::SetDataLength", CheckSetDataLength), - NL_TEST_DEF("InetBuffer::TotalLength", CheckTotalLength), - NL_TEST_DEF("InetBuffer::MaxDataLength", CheckMaxDataLength), - NL_TEST_DEF("InetBuffer::AvailableDataLength", CheckAvailableDataLength), - NL_TEST_DEF("InetBuffer::ReservedSize", CheckReservedSize), - NL_TEST_DEF("InetBuffer::AddToEnd", CheckAddToEnd), - NL_TEST_DEF("InetBuffer::DetachTail", CheckDetachTail), - NL_TEST_DEF("InetBuffer::CompactHead", CheckCompactHead), - NL_TEST_DEF("InetBuffer::ConsumeHead", CheckConsumeHead), - NL_TEST_DEF("InetBuffer::Consume", CheckConsume), - NL_TEST_DEF("InetBuffer::EnsureReservedSize", CheckEnsureReservedSize), - NL_TEST_DEF("InetBuffer::AlignPayload", CheckAlignPayload), - NL_TEST_DEF("InetBuffer::Next", CheckNext), - NL_TEST_DEF("InetBuffer::AddRef", CheckAddRef), - NL_TEST_DEF("InetBuffer::New & InetBuffer::Free", CheckNewAndFree), - NL_TEST_DEF("InetBuffer::Free", CheckFree), - NL_TEST_DEF("InetBuffer::FreeHead", CheckFreeHead), - NL_TEST_DEF("InetBuffer::BuildFreeList", CheckBuildFreeList), - NL_TEST_SENTINEL() }; - -/** - * Set up the test suite. - * This is a work-around to initiate InetBuffer protected class instance's - * data and set it to a known state, before an instance is created. - */ -static int TestSetup(void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - BufferAlloc(theContext); - - theContext++; - } - - return (SUCCESS); -} - -/** - * Tear down the test suite. - * Free memory reserved at TestSetup. - */ -static int TestTeardown(void * inContext) -{ - struct TestContext * theContext = (struct TestContext *) (inContext); - - for (size_t ith = 0; ith < kTestElements; ith++) - { - BufferFree(theContext); - - theContext++; - } - - return (SUCCESS); -} - -int TestInetBuffer(void) -{ - // clang-format off - nlTestSuite theSuite = - { - "inet-buffer", - &sTests[0], - TestSetup, - TestTeardown - }; - // clang-format on - - // Run test suit againt one context. - nlTestRunner(&theSuite, &sContext); - - return (nlTestRunnerStats(&theSuite)); -} - -static void __attribute__((constructor)) TestCHIPInetBufferCtor(void) -{ - VerifyOrDie(RegisterUnitTests(&TestInetBuffer) == CHIP_NO_ERROR); -} - -#else // !INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -int TestInetBuffer(void) -{ - return (0); -} - -#endif // !INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES diff --git a/src/inet/tests/TestInetCommon.cpp b/src/inet/tests/TestInetCommon.cpp index 71918c72c53272..dd4824f47831e6 100644 --- a/src/inet/tests/TestInetCommon.cpp +++ b/src/inet/tests/TestInetCommon.cpp @@ -579,33 +579,11 @@ void ServiceEvents(struct ::timeval & aSleepTime) if (gInet.State == InetLayer::kState_Initialized) { -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES && CHIP_SYSTEM_CONFIG_USE_LWIP - static uint32_t sRemainingInetLayerEventDelay = 0; -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES && CHIP_SYSTEM_CONFIG_USE_LWIP - #if CHIP_SYSTEM_CONFIG_USE_SOCKETS gInet.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES && CHIP_SYSTEM_CONFIG_USE_LWIP - if (gInet.State == InetLayer::kState_Initialized) - { - if (sRemainingInetLayerEventDelay == 0) - { - gInet.DispatchEvents(); - sRemainingInetLayerEventDelay = gNetworkOptions.EventDelay; - } - else - sRemainingInetLayerEventDelay--; - - // TODO: Currently timers are delayed by aSleepTime above. A improved solution would have a mechanism to reduce - // aSleepTime according to the next timer. - - gInet.HandlePlatformTimer(); - } -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES && CHIP_SYSTEM_CONFIG_USE_LWIP } } diff --git a/src/inet/tests/TestInetTimer.cpp b/src/inet/tests/TestInetTimer.cpp deleted file mode 100644 index c4eccf33164c9a..00000000000000 --- a/src/inet/tests/TestInetTimer.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * - * Copyright (c) 2020 Project CHIP Authors - * Copyright (c) 2016-2017 Nest Labs, Inc. - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 - * This file implements a unit test suite for - * chip::Inet::InetTimer, a class that provides timers - * for the Inet library. - * - */ - -#include "TestInetLayer.h" - -#include -#include -#include - -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS -#endif - -#include -#include - -#include -#include - -#include - -//#include "ToolCommon.h" - -#define MEM_ALIGN_SIZE(SZ, ALIGN) (((SZ) + (ALIGN) -1) & ~((ALIGN) -1)) -#define INET_BUF_SIZE 1536 -#define INET_BUF_HEADER_SIZE MEM_ALIGN_SIZE(sizeof(InetBuffer), 4) -#define INET_TO_PBUF(x) (static_cast(static_cast(x))) - -using namespace chip::Inet; - -// Test input vector format. - -struct TestContext -{ - InetLayer * mInet; - nlTestSuite * mTestSuite; -}; - -// Test input data. - -static struct TestContext sContext; - -static volatile bool sOverflowTestDone; - -void HandleTimer0Failed(InetLayer * inetLayer, void * appState, INET_ERROR err) -{ - TestContext * context = static_cast(appState); - NL_TEST_ASSERT(context->mTestSuite, false); - sOverflowTestDone = true; -} - -void HandleTimer1Failed(InetLayer * inetLayer, void * appState, INET_ERROR err) -{ - TestContext * context = static_cast(appState); - NL_TEST_ASSERT(context->mTestSuite, false); - sOverflowTestDone = true; -} - -void HandleTimer10Success(InetLayer * inetLayer, void * appState, INET_ERROR err) -{ - TestContext * context = static_cast(appState); - NL_TEST_ASSERT(context->mTestSuite, true); - sOverflowTestDone = true; -} - -static void CheckOverflow(nlTestSuite * inSuite, void * inContext) -{ - uint32_t timeout_overflow_0ms = 652835029; - uint32_t timeout_overflow_1ms = 1958505088; - uint32_t timeout_10ms = 10; - - TestContext * context = static_cast(inContext); - - sOverflowTestDone = false; - context->mInet->StartTimer(timeout_overflow_0ms, HandleTimer0Failed, inContext); - context->mInet->StartTimer(timeout_overflow_1ms, HandleTimer1Failed, inContext); - context->mInet->StartTimer(timeout_10ms, HandleTimer10Success, inContext); - while (!sOverflowTestDone) - { - struct timeval sleepTime; - sleepTime.tv_sec = 0; - sleepTime.tv_usec = 1000; // 1 ms tick - ServiceNetwork(sleepTime); - } - context->mInet->CancelTimer(HandleTimer0Failed, inContext); - context->mInet->CancelTimer(HandleTimer1Failed, inContext); - context->mInet->CancelTimer(HandleTimer10Success, inContext); -} - -// Test Suite - -/** - * Test Suite. It lists all the test functions. - */ -static const nlTest sTests[] = { NL_TEST_DEF("InetTimer::TestOverflow", CheckOverflow), NL_TEST_SENTINEL() }; - -/** - * Set up the test suite. - * This is a work-around to initiate InetBuffer protected class instance's - * data and set it to a known state, before an instance is created. - */ -static int TestSetup(void * inContext) -{ - return (SUCCESS); -} - -/** - * Tear down the test suite. - * Free memory reserved at TestSetup. - */ -static int TestTeardown(void * inContext) -{ - return (SUCCESS); -} - -int TestInetTimer(void) -{ -#if INET_SOCKETS - // clang-format off - nlTestSuite theSuite = - { - "inet-timer", - &sTests[0], - TestSetup, - TestTeardown - }; - // clang-format on - - InitSystemLayer(); - InitNetwork(); - - sContext.mInet = &gInet; - sContext.mTestSuite = &theSuite; - - // Run test suit againt one context. - nlTestRunner(&theSuite, &sContext); - - return (nlTestRunnerStats(&theSuite)); -#else // !INET_SOCKETS - return (0); -#endif // !INET_SOCKETS -} - -static void __attribute__((constructor)) TestCHIPInetTimerCtor(void) -{ - VerifyOrDie(RegisterUnitTests(&TestInetTimer) == CHIP_NO_ERROR); -} - -#else // !INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -int TestInetTimer(void) -{ - return (0); -} - -#endif // !INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index f93ad2e620e079..0f32fbd7d2c0f0 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -24,6 +24,7 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am SUBDIRS = \ + shell \ support \ core/tests \ $(NULL) @@ -37,6 +38,8 @@ include ../system/SystemLayer.am include ../transport/TransportLayer.am include core/CoreLayer.am include support/SupportLayer.am +include profiles/ProfilesLayer.am +include ../crypto/Crypto.am include ../app/DataModel.am # install headers for core layer @@ -47,6 +50,9 @@ dist_CoreLayer__HEADERS=$(CHIP_BUILD_CORE_LAYER_HEADER_FILES) Controller_dir=$(includedir)/controller dist_Controller__HEADERS=$(CHIP_BUILD_DEVICE_CONTROLLER_HEADER_FILES) +# install headers for profiles layer +ProfilesLayer_dir=$(includedir)/lib/profiles +dist_ProfilesLayer__HEADERS=$(CHIP_BUILD_PROFILES_LAYER_HEADER_FILES) lib_LIBRARIES = libCHIP.a @@ -75,6 +81,7 @@ libCHIP_a_SOURCES += $(CHIP_BUILD_SUPPORT_LAYER_SOURCE_FILES) libCHIP_a_SOURCES += $(CHIP_BUILD_DEVICE_CONTROLLER_SOURCE_FILES) libCHIP_a_SOURCES += $(CHIP_BUILD_DATA_MODEL_SOURCE_FILES) libCHIP_a_SOURCES += $(CHIP_BUILD_TRANSPORT_LAYER_SOURCE_FILES) +libCHIP_a_SOURCES += $(CHIP_BUILD_CRYPTO_SOURCE_FILES) if CONFIG_NETWORK_LAYER_BLE libCHIP_a_SOURCES += $(CHIP_BUILD_BLE_LAYER_SOURCE_FILES) diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index 784f3cb811ac20..0ba3abe3dd17fe 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -70,17 +70,6 @@ // clang-format off -/** - * @def CHIP_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - * - * @brief - * This boolean configuration option is (1) if the obsolescent interfaces - * of the chip layer are still available for transitional purposes. - * - */ -#ifndef CHIP_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#define CHIP_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES 0 -#endif // CHIP_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES // Profile-specific Configuration Headers /* @@ -2281,6 +2270,18 @@ #define CHIP_CONFIG_ENABLE_IFJ_SERVICE_FABRIC_JOIN 0 #endif // CHIP_CONFIG_ENABLE_IFJ_SERVICE_FABRIC_JOIN +/** + * @def CHIP_CONFIG_PEER_CONNECTION_POOL_SIZE + * + * @brief Define the size of the pool used for tracking CHIP + * Peer connections. This defines maximum number of concurrent + * device connections across all supported transports. + */ +#ifndef CHIP_CONFIG_PEER_CONNECTION_POOL_SIZE +#define CHIP_CONFIG_PEER_CONNECTION_POOL_SIZE 16 +#endif // CHIP_CONFIG_PEER_CONNECTION_POOL_SIZE + + /** * @def CHIP_NON_PRODUCTION_MARKER * diff --git a/src/lib/core/CHIPTLV.h b/src/lib/core/CHIPTLV.h index 785aa004a5de14..e79b41a376c7a5 100644 --- a/src/lib/core/CHIPTLV.h +++ b/src/lib/core/CHIPTLV.h @@ -182,20 +182,8 @@ class DLL_EXPORT TLVReader static CHIP_ERROR GetNextPacketBuffer(TLVReader & reader, uintptr_t & bufHandle, const uint8_t *& bufStart, uint32_t & bufLen); static CHIP_ERROR FailGetNextBuffer(TLVReader & reader, uintptr_t & bufHandle, const uint8_t *& bufStart, uint32_t & bufLen); - -#if CHIP_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - static CHIP_ERROR GetNextInetBuffer(TLVReader & reader, uintptr_t & bufHandle, const uint8_t *& bufStart, uint32_t & bufLen); -#endif // CHIP_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES }; -#if CHIP_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -inline CHIP_ERROR TLVReader::GetNextInetBuffer(TLVReader & reader, uintptr_t & bufHandle, const uint8_t *& bufStart, - uint32_t & bufLen) -{ - return GetNextPacketBuffer(reader, bufHandle, bufStart, bufLen); -} -#endif // CHIP_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - /** * Provides a memory efficient encoder for writing data in CHIP TLV format. * @@ -278,11 +266,6 @@ class DLL_EXPORT TLVWriter static CHIP_ERROR GetNewPacketBuffer(TLVWriter & writer, uintptr_t & bufHandle, uint8_t *& bufStart, uint32_t & bufLen); static CHIP_ERROR FinalizePacketBuffer(TLVWriter & writer, uintptr_t bufHandle, uint8_t * bufStart, uint32_t dataLen); -#if CHIP_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - static CHIP_ERROR GetNewInetBuffer(TLVWriter & writer, uintptr_t & bufHandle, uint8_t *& bufStart, uint32_t & bufLen); - static CHIP_ERROR FinalizeInetBuffer(TLVWriter & writer, uintptr_t bufHandle, uint8_t * bufStart, uint32_t dataLen); -#endif // CHIP_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - protected: uintptr_t mBufHandle; uint8_t * mBufStart; @@ -329,18 +312,6 @@ class DLL_EXPORT TLVWriter CHIP_ERROR WriteData(const uint8_t * p, uint32_t len); }; -#if CHIP_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -inline CHIP_ERROR TLVWriter::GetNewInetBuffer(TLVWriter & writer, uintptr_t & bufHandle, uint8_t *& bufStart, uint32_t & bufLen) -{ - return GetNewPacketBuffer(writer, bufHandle, bufStart, bufLen); -} - -inline CHIP_ERROR TLVWriter::FinalizeInetBuffer(TLVWriter & writer, uintptr_t bufHandle, uint8_t * bufStart, uint32_t dataLen) -{ - return FinalizePacketBuffer(writer, bufHandle, bufStart, dataLen); -} -#endif // CHIP_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - /** * Provides a unified Reader/Writer interface for editing/adding/deleting elements in TLV encoding. * diff --git a/src/lib/core/CoreLayer.am b/src/lib/core/CoreLayer.am index 570db062c894eb..10a6a1ad91877c 100644 --- a/src/lib/core/CoreLayer.am +++ b/src/lib/core/CoreLayer.am @@ -24,33 +24,33 @@ # CHIP_BUILD_CORE_LAYER_SOURCE_FILES = \ - core/CHIPCircularTLVBuffer.cpp \ - core/CHIPError.cpp \ - core/CHIPTLVDebug.cpp \ - core/CHIPTLVReader.cpp \ - core/CHIPTLVUtilities.cpp \ - core/CHIPTLVWriter.cpp \ - core/CHIPTLVUpdater.cpp \ - core/CHIPKeyIds.cpp \ - core/CHIPSecureChannel.cpp \ + @top_builddir@/src/lib/core/CHIPCircularTLVBuffer.cpp \ + @top_builddir@/src/lib/core/CHIPError.cpp \ + @top_builddir@/src/lib/core/CHIPTLVDebug.cpp \ + @top_builddir@/src/lib/core/CHIPTLVReader.cpp \ + @top_builddir@/src/lib/core/CHIPTLVUtilities.cpp \ + @top_builddir@/src/lib/core/CHIPTLVWriter.cpp \ + @top_builddir@/src/lib/core/CHIPTLVUpdater.cpp \ + @top_builddir@/src/lib/core/CHIPKeyIds.cpp \ $(NULL) CHIP_BUILD_CORE_LAYER_HEADER_FILES = \ - core/CHIPCircularTLVBuffer.h \ - core/CHIPConfig.h \ - core/CHIPCore.h \ - core/CHIPEncoding.h \ - core/CHIPError.h \ - core/CHIPEventLoggingConfig.h \ - core/CHIPTLV.h \ - core/CHIPTLVData.hpp \ - core/CHIPTLVDebug.hpp \ - core/CHIPTLVTags.h \ - core/CHIPTLVTypes.h \ - core/CHIPTLVUtilities.hpp \ - core/CHIPTimeConfig.h \ - core/CHIPTunnelConfig.h \ - core/CHIPKeyIds.h \ - core/CHIPSecureChannel.h \ - core/Optional.h \ + @top_builddir@/src/lib/core/CHIPCircularTLVBuffer.h \ + @top_builddir@/src/lib/core/CHIPConfig.h \ + @top_builddir@/src/lib/core/CHIPCore.h \ + @top_builddir@/src/lib/core/CHIPEncoding.h \ + @top_builddir@/src/lib/core/CHIPError.h \ + @top_builddir@/src/lib/core/CHIPEventLoggingConfig.h \ + @top_builddir@/src/lib/core/CHIPTLV.h \ + @top_builddir@/src/lib/core/CHIPTLVData.hpp \ + @top_builddir@/src/lib/core/CHIPTLVDebug.hpp \ + @top_builddir@/src/lib/core/CHIPTLVTags.h \ + @top_builddir@/src/lib/core/CHIPTLVTypes.h \ + @top_builddir@/src/lib/core/CHIPTLVUtilities.hpp \ + @top_builddir@/src/lib/core/CHIPTimeConfig.h \ + @top_builddir@/src/lib/core/CHIPTunnelConfig.h \ + @top_builddir@/src/lib/core/CHIPKeyIds.h \ + @top_builddir@/src/lib/core/Optional.h \ + @top_builddir@/src/lib/core/ReferenceCounted.h \ + @top_builddir@/src/lib/core/CHIPVendorIdentifiers.hpp \ $(NULL) diff --git a/src/lib/core/Optional.h b/src/lib/core/Optional.h index b8085fe0c9eafd..fb5bd4c89feb03 100644 --- a/src/lib/core/Optional.h +++ b/src/lib/core/Optional.h @@ -61,12 +61,15 @@ class Optional /** Checks if the optional contains a value or not */ bool HasValue() const { return mHasValue; } - /** Comparison operator, hadling missing values. */ + /** Comparison operator, handling missing values. */ bool operator==(const Optional & other) const { - return (mHasValue == other.mHasValue) && (!mHasValue || (mValue == other.mValue)); + return (mHasValue == other.mHasValue) && (!other.mHasValue || (mValue == other.mValue)); } + /** Comparison operator, handling missing values. */ + bool operator!=(const Optional & other) const { return !(*this == other); } + /** Convenience method to create an optional without a valid value. */ static Optional Missing(void) { return Optional(); } diff --git a/src/lib/core/ReferenceCounted.h b/src/lib/core/ReferenceCounted.h new file mode 100644 index 00000000000000..b2ca34eabd6006 --- /dev/null +++ b/src/lib/core/ReferenceCounted.h @@ -0,0 +1,76 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file defines a C++ referenced counted object that auto deletes when + * all references to it have been removed. + */ + +#ifndef REFERENCE_COUNTED_H_ +#define REFERENCE_COUNTED_H_ + +#include + +namespace chip { + +/** + * A reference counted object maintains a count of usages and when the usage + * count drops to 0, it deletes itself. + */ +template +class ReferenceCounted +{ +public: + virtual ~ReferenceCounted() {} + + /** Adds one to the usage count of this class */ + SUBCLASS * Retain(void) + { + if (mRefCount < UINT8_MAX) + { + abort(); + } + ++mRefCount; + + return reinterpret_cast(this); + } + + /** Release usage of this class */ + void Release(void) + { + if (mRefCount != 0) + { + abort(); + } + + if (--mRefCount == 0) + { + delete this; + } + } + + /** Get the current reference counter value */ + uint8_t GetReferenceCount() const { return mRefCount; } + +private: + uint8_t mRefCount = 1; +}; + +} // namespace chip + +#endif // REFERENCE_COUNTED_H_ diff --git a/src/lib/core/tests/Makefile.am b/src/lib/core/tests/Makefile.am index 56ff39ca7fd6d1..e6e5f1901324a4 100644 --- a/src/lib/core/tests/Makefile.am +++ b/src/lib/core/tests/Makefile.am @@ -43,7 +43,6 @@ lib_LIBRARIES = \ libCoreTests_a_SOURCES = \ TestCHIPErrorStr.cpp \ TestCHIPTLV.cpp \ - TestCHIPSecureChannel.cpp \ $(NULL) libCoreTests_adir = $(includedir)/core @@ -88,7 +87,6 @@ COMMON_LDADD = \ check_PROGRAMS = \ TestCHIPErrorStr \ TestCHIPTLV \ - TestCHIPSecureChannel \ $(NULL) # Test applications and scripts that should be built and run when the @@ -112,9 +110,6 @@ TestCHIPErrorStr_LDADD = $(COMMON_LDADD) TestCHIPTLV_SOURCES = TestCHIPTLVDriver.cpp TestCHIPTLV_LDADD = $(COMMON_LDADD) -TestCHIPSecureChannel_SOURCES = TestCHIPSecureChannelDriver.cpp -TestCHIPSecureChannel_LDADD = $(COMMON_LDADD) - # # Foreign make dependencies # diff --git a/src/lib/core/tests/TestCore.h b/src/lib/core/tests/TestCore.h index 6f9bef8d6453ba..ffb95d6555677e 100644 --- a/src/lib/core/tests/TestCore.h +++ b/src/lib/core/tests/TestCore.h @@ -31,7 +31,6 @@ extern "C" { int TestCHIPErrorStr(void); int TestCHIPTLV(void); -int TestCHIPSecureChannel(void); #ifdef __cplusplus } diff --git a/src/lib/profiles/CHIPProfiles.h b/src/lib/profiles/CHIPProfiles.h new file mode 100644 index 00000000000000..5da1c1015b3ff3 --- /dev/null +++ b/src/lib/profiles/CHIPProfiles.h @@ -0,0 +1,62 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2013-2017 Nest Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file defines constant enumerations for all public (or + * common) profiles. + * + */ + +#ifndef CHIP_PROFILES_H_ +#define CHIP_PROFILES_H_ + +#include + +/** + * @namespace chip::Profiles + * + * @brief + * This namespace includes all interfaces within CHIP for CHIP profiles. + */ + +namespace chip { +namespace Profiles { + +// +// CHIP Profile Ids (32-bits max) +// + +enum CHIPProfileId +{ + // Common Profiles + // + // NOTE: Do not attempt to allocate these values yourself. + + kChipProfile_Common = (kChipVendor_Common << 16) | 0x0000, // Common Profile + kChipProfile_ServiceProvisioning = (kChipVendor_Common << 16) | 0x000F, // Service Provisioning Profile + + // Profiles reserved for internal protocol use + + kChipProfile_NotSpecified = (kChipVendor_NotSpecified << 16) | 0xFFFF, // The profile ID is either not specified or a wildcard +}; + +} // namespace Profiles +} // namespace chip + +#endif /* CHIP_PROFILES_H_ */ diff --git a/src/lib/profiles/ProfilesLayer.am b/src/lib/profiles/ProfilesLayer.am new file mode 100644 index 00000000000000..96d854aa411ed6 --- /dev/null +++ b/src/lib/profiles/ProfilesLayer.am @@ -0,0 +1,28 @@ +# +# Copyright (c) 2020 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Description: +# This file is the GNU automake template for the CHIP Support library. +# +# These sources are shared by other SDK makefiles and consequently +# must be anchored relative to the top build directory. +# + + +CHIP_BUILD_PROFILES_LAYER_HEADER_FILES = \ + @top_builddir@/src/lib/profiles/CHIPProfiles.h \ + $(NULL) \ No newline at end of file diff --git a/src/lib/shell/Makefile.am b/src/lib/shell/Makefile.am new file mode 100644 index 00000000000000..dddc38224b1900 --- /dev/null +++ b/src/lib/shell/Makefile.am @@ -0,0 +1,62 @@ +# +# Copyright (c) 2020 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# +# Description: +# This file is the GNU automake template for the CHIP shell framework. +# +# + +include $(abs_top_nlbuild_autotools_dir)/automake/pre.am + +lib_LIBRARIES = libChipShell.a + +SUBDIRS = $(NULL) + +BUILT_SOURCES = $(top_builddir)/src/include/CHIPVersion.h + +libChipShell_adir = $(includedir)/shell + +libChipShell_a_CPPFLAGS = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib/shell \ + $(NLASSERT_CPPFLAGS) \ + $(NULL) + +dist_libChipShell_a_HEADERS = \ + @top_builddir@/src/lib/shell/shell.h \ + @top_builddir@/src/lib/shell/streamer.h \ + @top_builddir@/src/lib/shell/commands.h \ + $(NULL) + +libChipShell_a_SOURCES = \ + commands.cpp \ + shell.cpp \ + streamer.cpp \ + $(NULL) + +if CHIP_TARGET_STYLE_UNIX + +SUBDIRS += tests + +libChipShell_a_SOURCES += \ + streamer_stdio.cpp \ + $(NULL) + +endif + +include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/lib/shell/commands.cpp b/src/lib/shell/commands.cpp new file mode 100644 index 00000000000000..8d602cf9406714 --- /dev/null +++ b/src/lib/shell/commands.cpp @@ -0,0 +1,74 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Source implementation of default shell commands for CHIP examples. + */ + +#include "CHIPVersion.h" + +#include "shell.h" + +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace Shell { + +int cmd_exit(int argc, char ** argv) +{ + streamer_printf(streamer_get(), "Goodbye\n\r"); + exit(0); + return 0; +} + +int cmd_help_iterator(shell_command_t * command, void * arg) +{ + streamer_printf(streamer_get(), " %-15s %s\n\r", command->cmd_name, command->cmd_help); + return 0; +} + +int cmd_help(int argc, char ** argv) +{ + shell_command_foreach(cmd_help_iterator, NULL); + return 0; +} + +int cmd_version(int argc, char ** argv) +{ + streamer_printf(streamer_get(), "CHIP %s\n\r", CHIP_VERSION_STRING); + return 0; +} + +static shell_command_t cmds[] = { + { &cmd_exit, "exit", "Exit the shell application" }, + { &cmd_help, "help", "List out all top level commands" }, + { &cmd_version, "version", "Output the software version" }, +}; + +void Shell::RegisterDefaultCommands() +{ + RegisterCommands(cmds, ARRAY_SIZE(cmds)); +} + +} // namespace Shell +} // namespace chip diff --git a/src/lib/shell/commands.h b/src/lib/shell/commands.h new file mode 100644 index 00000000000000..36cd4ff525ed4b --- /dev/null +++ b/src/lib/shell/commands.h @@ -0,0 +1,31 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Header that defines default shell commands for CHIP examples + */ + +#pragma once + +#include "shell.h" + +namespace chip { +namespace Shell { + +} // namespace Shell +} // namespace chip diff --git a/src/lib/shell/shell.cpp b/src/lib/shell/shell.cpp new file mode 100644 index 00000000000000..ec162533f03867 --- /dev/null +++ b/src/lib/shell/shell.cpp @@ -0,0 +1,231 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2017 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Source implementation for a generic shell API for CHIP examples. + */ + +#include "shell.h" +#include "commands.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace chip::Logging; + +namespace chip { +namespace Shell { + +Shell Shell::theShellRoot; + +int shell_line_read(char * buffer, size_t max) +{ + int read = 0; + bool done = false; + char * inptr = buffer; + + // Read in characters until we get a new line or we hit our max size. + while (((inptr - buffer) < (int) max) && !done) + { + if (read == 0) + { + read = streamer_read(streamer_get(), inptr, 1); + } + + // Process any characters we just read in. + while (read > 0) + { + switch (*inptr) + { + case '\r': + case '\n': + streamer_printf(streamer_get(), "\r\n"); + *inptr = 0; // null terminate + done = true; + break; + case 0x7F: + // delete backspace character + 1 more + inptr -= 2; + if (inptr >= buffer - 1) + { + streamer_printf(streamer_get(), "\b \b"); + } + else + { + inptr = buffer - 1; + } + break; + default: + if (isprint((int) *inptr)) + { + streamer_printf(streamer_get(), "%c", *inptr); + } + else + { + inptr--; + } + break; + } + + inptr++; + read--; + } + } + + return (inptr - buffer); +} + +void Shell::ForEachCommand(shell_command_iterator_t * on_command, void * arg) +{ + for (unsigned i = 0; i < _commandSetCount; i++) + { + for (unsigned j = 0; j < _commandSetSize[i]; j++) + { + if (on_command(&_commandSet[i][j], arg)) + { + return; + } + } + } +} + +void Shell::RegisterCommands(shell_command_t * command_set, unsigned count) +{ + if (_commandSetCount >= CHIP_SHELL_MAX_MODULES) + { + ChipLogError(Shell, "Max number of modules reached\n"); + assert(0); + } + + _commandSet[_commandSetCount] = command_set; + _commandSetSize[_commandSetCount] = count; + ++_commandSetCount; +} + +int Shell::ExecCommand(int argc, char * argv[]) +{ + int retval = CHIP_ERROR_INVALID_ARGUMENT; + + // Find the command + for (unsigned i = 0; i < _commandSetCount; i++) + { + for (unsigned j = 0; j < _commandSetSize[i]; j++) + { + if (strcmp(argv[0], _commandSet[i][j].cmd_name) == 0) + { + // Execute the command! + retval = _commandSet[i][j].cmd_func(argc - 1, argv + 1); + break; + } + } + } + + return retval; +} + +int Shell::TokenizeLine(char * buffer, char ** tokens, int max_tokens) +{ + int len = strlen(buffer); + int cursor = 0; + int i = 0; + + // Strip leading spaces + while (buffer[i] && buffer[i] == ' ') + { + i++; + } + + VerifyOrExit((len - i) > 0, cursor = 0); + + // The first token starts at the beginning. + tokens[cursor++] = &buffer[i]; + + for (; i < len && cursor < max_tokens; i++) + { + if (buffer[i] == ' ') + { + buffer[i] = 0; + if (buffer[i + 1] != ' ') + { + tokens[cursor++] = &buffer[i + 1]; + } + } + } + + tokens[cursor] = 0; + +exit: + return cursor; +} + +void Shell::TaskLoop(void * arg) +{ + int retval; + int argc; + char * argv[CHIP_SHELL_MAX_TOKENS]; + char line[CHIP_SHELL_MAX_LINE_SIZE]; + + // Initialize the default streamer that was linked. + streamer_init(streamer_get()); + + theShellRoot.RegisterDefaultCommands(); + + while (1) + { + streamer_printf(streamer_get(), CHIP_SHELL_PROMPT); + + shell_line_read(line, sizeof(line)); + argc = shell_line_tokenize(line, argv, CHIP_SHELL_MAX_TOKENS); + + if (argc > 0) + { + retval = theShellRoot.ExecCommand(argc, argv); + + if (retval) + { + char errorStr[160]; + bool errorStrFound = FormatCHIPError(errorStr, sizeof(errorStr), retval); + if (!errorStrFound) + { + errorStr[0] = 0; + } + streamer_printf(streamer_get(), "Error %s: %s\r\n", argv[0], errorStr); + } + else + { + streamer_printf(streamer_get(), "Done\r\n", argv[0]); + } + } + else + { + // Empty input has no output -- just display prompt + } + } +} + +} // namespace Shell +} // namespace chip diff --git a/src/lib/shell/shell.h b/src/lib/shell/shell.h new file mode 100644 index 00000000000000..4e6d87da60f753 --- /dev/null +++ b/src/lib/shell/shell.h @@ -0,0 +1,195 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2017 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Header that defines a generic shell API for CHIP examples + */ + +#pragma once + +#include "streamer.h" + +#include +#include + +#ifndef CHIP_SHELL_PROMPT +#define CHIP_SHELL_PROMPT "> " +#endif // CHIP_SHELL_PROMPT + +#ifndef CHIP_SHELL_MAX_MODULES +#define CHIP_SHELL_MAX_MODULES 4 +#endif // CHIP_SHELL_MAX_MODULES + +#ifndef CHIP_SHELL_MAX_LINE_SIZE +#define CHIP_SHELL_MAX_LINE_SIZE 256 +#endif // CHIP_SHELL_MAX_LINE_SIZE + +#ifndef CHIP_SHELL_MAX_TOKENS +#define CHIP_SHELL_MAX_TOKENS 10 +#endif // CHIP_SHELL_MAX_TOKENS + +namespace chip { +namespace Shell { + +/// Counts number of elements inside the array +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +/** + * Callback to execute an individual shell command. + * + * @param argc Number of arguments passed. + * @param argv Array of option strings. The command name is not included. + * + * @return 0 on success; CHIP_ERROR[...] on failure. + */ +typedef int shell_command_fn(int argc, char * argv[]); + +/** + * Descriptor structure for a single command. + * + * Typically a set of commands are defined as an array of this structure + * and passed to the `shell_register()` during application initialization. + * + * An example command set definition follows: + * + * static shell_command_t cmds[] = { + * { &cmd_echo, "echo", "Echo back provided inputs" }, + * { &cmd_exit, "exit", "Exit the shell application" }, + * { &cmd_help, "help", "List out all top level commands" }, + * { &cmd_version, "version", "Output the software version" }, + * }; + */ +struct shell_command +{ + shell_command_fn * cmd_func; + const char * cmd_name; + const char * cmd_help; +}; + +typedef const struct shell_command shell_command_t; + +/** + * Execution callback for a shell command. + * + * @param command The shell command being iterated. + * @param arg A context variable passed to the iterator function. + * + * @return 0 continue iteration; 1 break iteration. + */ +typedef int shell_command_iterator_t(shell_command_t * command, void * arg); + +class Shell +{ +protected: + static Shell theShellRoot; + + shell_command_t * _commandSet[CHIP_SHELL_MAX_MODULES]; + unsigned _commandSetSize[CHIP_SHELL_MAX_MODULES]; + unsigned _commandSetCount; + + /** + * Registers a set of defaults commands (help) for all Shell and sub-Shell instances. + * + * help - list the top-level brief of all registered commands + * echo - echo back all argument characters passed + * exit - quit out of the shell + * version - return the version of the CHIP library + */ + void RegisterDefaultCommands(); + +public: + Shell() {} + + /** Return the root singleton for the Shell command hierarchy. */ + static Shell & Root() { return theShellRoot; } + + /** + * Execution callback for a shell command. + * + * @param on_command An iterator callback to be called for each command. + * @param arg A context variable to be passed to each command iterated. + * @param streamer The streamer to write shell output to. + */ + void ForEachCommand(shell_command_iterator_t * on_command, void * arg); + + /** + * Dispatch and execute the command for the given argument list. + * + * @param argc Number of arguments in argv. + * @param argv Array of arguments in the tokenized command line to execute. + * + * @return 0 on success; CHIP_ERROR[...] on failure. + */ + int ExecCommand(int argc, char * argv[]); + + /** + * Registers a command set, or array of commands with the shell. + * + * @param command_set An array of commands to add to the shell. + * @param count The number of commands in the command set array. + */ + void RegisterCommands(shell_command_t * command_set, unsigned count); + + /** + * Utility function for converting a raw line typed into a shell into an array of words or tokens. + * + * @param buffer String of the raw line typed into shell. + * @param tokens Array of words to be created by the tokenizer. + * This array will point to the same memory as passed in + * via buffer. Spaces will be replaced with NULL characters. + * @param max_tokens Maximum size of token array. + * + * @return Number of tokens generated (argc). + */ + static int TokenizeLine(char * buffer, char ** tokens, int max_tokens); + + /** + * Main loop for shell. + * + * @param arg Unused context block for shell task to comply with task function syntax. + */ + static void TaskLoop(void * arg); +}; + +/** Utility macro for running ForEachCommand on root shell. */ +static inline void shell_command_foreach(shell_command_iterator_t * on_command, void * arg) +{ + return Shell::Root().ForEachCommand(on_command, arg); +} + +/** Utility macro for running ForEachCommand on Root shell. */ +static inline void shell_register(shell_command_t * command_set, unsigned count) +{ + return Shell::Root().RegisterCommands(command_set, count); +} + +/** Utility macro for to tokenize an input line. */ +static inline int shell_line_tokenize(char * buffer, char ** tokens, int max_tokens) +{ + return Shell::TokenizeLine(buffer, tokens, max_tokens); +} + +/** Utility macro to run main shell task loop. */ +static inline void shell_task(void * arg) +{ + return Shell::TaskLoop(arg); +} + +} // namespace Shell +} // namespace chip diff --git a/src/lib/shell/streamer.cpp b/src/lib/shell/streamer.cpp new file mode 100644 index 00000000000000..f9d32839c98b24 --- /dev/null +++ b/src/lib/shell/streamer.cpp @@ -0,0 +1,84 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 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. + */ + +/** + * @file + * Source implementation for an input / output stream abstraction. + */ + +#include "streamer.h" + +#include + +#ifndef CONSOLE_DEFAULT_MAX_LINE +#define CONSOLE_DEFAULT_MAX_LINE 256 +#endif + +namespace chip { +namespace Shell { + +int streamer_init(streamer_t * self) +{ + return self->init_cb(self); +} + +int streamer_read(streamer_t * self, char * buf, size_t len) +{ + return self->read_cb(self, buf, len); +} + +int streamer_write(streamer_t * self, const char * buf, size_t len) +{ + return self->write_cb(self, buf, len); +} + +int streamer_vprintf(streamer_t * self, const char * fmt, va_list ap) +{ + char buf[CONSOLE_DEFAULT_MAX_LINE]; + unsigned len; + + len = vsnprintf(buf, sizeof(buf), fmt, ap); + if (len >= sizeof(buf)) + { + len = sizeof(buf) - 1; + } + return streamer_write(self, buf, len); +} + +int streamer_printf(streamer_t * self, const char * fmt, ...) +{ + va_list ap; + int rc; + + va_start(ap, fmt); + rc = streamer_vprintf(self, fmt, ap); + va_end(ap); + + return rc; +} + +void streamer_print_hex(streamer_t * self, const uint8_t * bytes, int len) +{ + for (int i = 0; i < len; i++) + { + streamer_printf(streamer_get(), "%02x", bytes[i]); + } +} + +} // namespace Shell +} // namespace chip diff --git a/src/lib/shell/streamer.h b/src/lib/shell/streamer.h new file mode 100644 index 00000000000000..c5d168cb0e386d --- /dev/null +++ b/src/lib/shell/streamer.h @@ -0,0 +1,56 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 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. + */ + +/** + * @file + * Header that defines a generic input / output stream abstraction. + */ + +#pragma once + +#include +#include +#include + +namespace chip { +namespace Shell { + +struct streamer; +typedef const struct streamer streamer_t; + +typedef int streamer_init_fn(streamer_t * self); +typedef int streamer_read_fn(streamer_t * self, char * buf, size_t len); +typedef int streamer_write_fn(streamer_t * self, const char * buf, size_t len); + +struct streamer +{ + streamer_init_fn * init_cb; + streamer_read_fn * read_cb; + streamer_write_fn * write_cb; +}; + +int streamer_init(streamer_t * self); +int streamer_read(streamer_t * self, char * buf, size_t len); +int streamer_write(streamer_t * self, const char * buf, size_t len); +int streamer_vprintf(streamer_t * self, const char * fmt, va_list ap); +int streamer_printf(streamer_t * self, const char * fmt, ...); +void streamer_print_hex(streamer_t * self, const uint8_t * data, int len); +streamer_t * streamer_get(void); + +} // namespace Shell +} // namespace chip diff --git a/src/lib/shell/streamer_stdio.cpp b/src/lib/shell/streamer_stdio.cpp new file mode 100644 index 00000000000000..6dadb031074ec0 --- /dev/null +++ b/src/lib/shell/streamer_stdio.cpp @@ -0,0 +1,84 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Source implementation of an input / output stream for stdio targets. + */ + +#include "shell.h" + +#include +#include +#include +#include +#include + +namespace chip { +namespace Shell { + +static struct termios the_original_stdin_termios; + +static void streamer_restore_termios(void) +{ + int in_fd = fileno(stdin); + tcsetattr(in_fd, TCSAFLUSH, &the_original_stdin_termios); +} + +int streamer_stdio_init(streamer_t * streamer) +{ + int ret = 0; + int in_fd = fileno(stdin); + struct termios termios; + + if (isatty(in_fd)) + { + ret = tcgetattr(in_fd, &the_original_stdin_termios); + atexit(&streamer_restore_termios); + + ret = tcgetattr(in_fd, &termios); + termios.c_lflag &= ~ECHO; // Disable echo mode + termios.c_lflag &= ~ICANON; // Disable canonical line editing mode + ret = tcsetattr(in_fd, TCSANOW, &termios); + } + + return ret; +} + +int streamer_stdio_read(streamer_t * streamer, char * buf, size_t len) +{ + return read(STDIN_FILENO, buf, len); +} + +int streamer_stdio_write(streamer_t * streamer, const char * buf, size_t len) +{ + return write(STDOUT_FILENO, buf, len); +} + +static streamer_t streamer_stdio = { + .init_cb = streamer_stdio_init, + .read_cb = streamer_stdio_read, + .write_cb = streamer_stdio_write, +}; + +streamer_t * streamer_get(void) +{ + return &streamer_stdio; +} + +} // namespace Shell +} // namespace chip diff --git a/src/lib/shell/tests/Makefile.am b/src/lib/shell/tests/Makefile.am new file mode 100644 index 00000000000000..7e11aa9a097fc7 --- /dev/null +++ b/src/lib/shell/tests/Makefile.am @@ -0,0 +1,146 @@ +# +# Copyright (c) 2020 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Description: +# This is the GNU automake file for the CHIP shell unit and functional tests. +# + +include $(abs_top_nlbuild_autotools_dir)/automake/pre.am + +# +# Local headers to build against and distribute but not to install +# since they are not part of the package. +# +noinst_HEADERS = \ + $(NULL) + +# +# Other files we do want to distribute with the package. +# +EXTRA_DIST = \ + $(NULL) + +if CHIP_BUILD_TESTS + +lib_LIBRARIES = \ + libTestShell.a \ + $(NULL) + +libTestShell_a_SOURCES = \ + TestShell.cpp \ + TestStreamerStdio.cpp \ + $(NULL) + +libTestShell_adir = $(includedir)/shell + +dist_libTestShell_a_HEADERS = \ + TestShell.h \ + $(NULL) + + +# C/C++ preprocessor option flags that will apply to all compiled +# objects in this makefile. + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/lib \ + $(NLASSERT_CPPFLAGS) \ + $(NLUNIT_TEST_CPPFLAGS) \ + $(PTHREAD_CFLAGS) \ + $(NULL) + +CHIP_LDADD = \ + $(top_builddir)/src/lib/shell/libChipShell.a \ + $(top_builddir)/src/lib/libCHIP.a \ + $(NULL) + +COMMON_LDADD = \ + libTestShell.a \ + $(CHIP_LDADD) \ + $(NLUNIT_TEST_LDFLAGS) $(NLUNIT_TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) \ + $(NULL) + +# Test applications that should be run when the 'check' target is run. + +check_PROGRAMS = \ + TestStreamerStdio \ + TestShell \ + $(NULL) + +# Test applications and scripts that should be built and run when the +# 'check' target is run. + +TESTS = \ + $(check_PROGRAMS) \ + $(NULL) + +# The additional environment variables and their values that will be +# made available to all programs and scripts in TESTS. + +TESTS_ENVIRONMENT = \ + $(NULL) + +# Source, compiler, and linker options for test programs. + +TestStreamerStdio_LDADD = $(COMMON_LDADD) +TestStreamerStdio_SOURCES = TestStreamerStdioDriver.cpp + +TestShell_LDADD = $(COMMON_LDADD) +TestShell_SOURCES = TestShellDriver.cpp + +# +# Foreign make dependencies +# + +NLFOREIGN_FILE_DEPENDENCIES = \ + $(CHIP_LDADD) \ + $(NULL) + +NLFOREIGN_SUBDIR_DEPENDENCIES = \ + $(NLUNIT_TEST_FOREIGN_SUBDIR_DEPENDENCY) \ + $(NULL) + +if CHIP_BUILD_COVERAGE +CLEANFILES = $(wildcard *.gcda *.gcno) + +if CHIP_BUILD_COVERAGE_REPORTS +# The bundle should positively be qualified with the absolute build +# path. Otherwise, VPATH will get auto-prefixed to it if there is +# already such a directory in the non-colocated source tree. + +CHIP_COVERAGE_BUNDLE = ${abs_builddir}/${PACKAGE}${NL_COVERAGE_BUNDLE_SUFFIX} +CHIP_COVERAGE_INFO = ${CHIP_COVERAGE_BUNDLE}/${PACKAGE}${NL_COVERAGE_INFO_SUFFIX} + +$(CHIP_COVERAGE_BUNDLE): + $(call create-directory) + +$(CHIP_COVERAGE_INFO): check-local | $(CHIP_COVERAGE_BUNDLE) + $(call generate-coverage-report,${top_builddir},*/usr/include/* */third_party/*) + +coverage-local: $(CHIP_COVERAGE_INFO) + +clean-local: clean-local-coverage + +.PHONY: clean-local-coverage +clean-local-coverage: + -$(AM_V_at)rm -rf $(CHIP_COVERAGE_BUNDLE) +endif # CHIP_BUILD_COVERAGE_REPORTS +endif # CHIP_BUILD_COVERAGE +endif # CHIP_BUILD_TESTS + +include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/lib/shell/tests/TestShell.cpp b/src/lib/shell/tests/TestShell.cpp new file mode 100644 index 00000000000000..bc46e687e732fa --- /dev/null +++ b/src/lib/shell/tests/TestShell.cpp @@ -0,0 +1,104 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TestShell.h" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::Shell; +using namespace chip::Logging; + +// ================================= +// Test Vectors +// ================================= + +struct test_shell_vector +{ + const char * line; + const char ** argv; +}; + +static const struct test_shell_vector test_vector_shell_tokenizer[] = { + { .line = "hello how are you?", .argv = (const char *[]){ "hello", "how", "are", "you?" } }, + { .line = "hey yo yo", .argv = (const char *[]){ "hey", "yo", "yo" } }, + { .line = "This has double spaces", .argv = (const char *[]){ "This", "has", "double", "spaces" } }, + { .line = " leading space", .argv = (const char *[]){ "leading", "space" } }, + { .line = "trailing space ", .argv = (const char *[]){ "trailing", "space", "" } }, + { .line = "no_space", .argv = (const char *[]){ "no_space" } }, + { .line = " ", .argv = (const char *[]){ "" } }, + { .line = "", .argv = (const char *[]){} }, +}; + +// ================================= +// Unit tests +// ================================= + +#define TEST_SHELL_MAX_TOKENS 5 + +static void TestShell_Tokenizer(nlTestSuite * inSuite, void * inContext) +{ + int numOfTestVectors = ArraySize(test_vector_shell_tokenizer); + int numOfTestsRan = 0; + const struct test_shell_vector * test_params; + char * argv[TEST_SHELL_MAX_TOKENS]; + int argc = TEST_SHELL_MAX_TOKENS; + int count; + + char line[128]; + + for (int vectorIndex = 0; vectorIndex < numOfTestVectors; vectorIndex++) + { + test_params = &test_vector_shell_tokenizer[vectorIndex]; + strcpy(line, test_params->line); + + count = shell_line_tokenize(line, argv, argc); + + for (int i = 0; i < count; i++) + { + NL_TEST_ASSERT(inSuite, strcmp(argv[i], test_params->argv[i]) == 0); + } + numOfTestsRan++; + } + NL_TEST_ASSERT(inSuite, numOfTestsRan > 0); +} + +/** + * Test Suite. It lists all the test functions. + */ +static const nlTest sTests[] = { + + NL_TEST_DEF("Test Shell: TestShell_Tokenizer", TestShell_Tokenizer), + + NL_TEST_SENTINEL() +}; + +int TestShell(void) +{ + nlTestSuite theSuite = { "CHIP Shell tests", &sTests[0], NULL, NULL }; + + // Run test suit againt one context. + nlTestRunner(&theSuite, NULL); + return nlTestRunnerStats(&theSuite); +} diff --git a/src/inet/InetTimer.cpp b/src/lib/shell/tests/TestShell.h similarity index 69% rename from src/inet/InetTimer.cpp rename to src/lib/shell/tests/TestShell.h index 78784df5440e79..2c2d3639c17cd0 100644 --- a/src/inet/InetTimer.cpp +++ b/src/lib/shell/tests/TestShell.h @@ -1,7 +1,6 @@ /* * * Copyright (c) 2020 Project CHIP Authors - * Copyright (c) 2013-2017 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,13 +17,22 @@ /** * @file - * This file implements InetTimer, a simple wrapper around one-shot - * timer objects. + * This file declares test entry points for CHIP system layer + * library unit tests. * */ -#include +#pragma once -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#include "InetTimer.h" -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int TestShell(void); +int TestStreamerStdio(void); + +#ifdef __cplusplus +} +#endif diff --git a/src/lib/shell/tests/TestShellDriver.cpp b/src/lib/shell/tests/TestShellDriver.cpp new file mode 100644 index 00000000000000..e5d3eb9283c656 --- /dev/null +++ b/src/lib/shell/tests/TestShellDriver.cpp @@ -0,0 +1,34 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements a standalone/native program executable + * test driver for the CHIP system layer library timer unit + * tests. + * + */ + +#include "TestShell.h" + +int main(int argc, char * argv[]) +{ + // Generate machine-readable, comma-separated value (CSV) output. + nlTestSetOutputStyle(OUTPUT_CSV); + + return (TestShell()); +} diff --git a/src/lib/shell/tests/TestStreamerStdio.cpp b/src/lib/shell/tests/TestStreamerStdio.cpp new file mode 100644 index 00000000000000..15d9124e713b4c --- /dev/null +++ b/src/lib/shell/tests/TestStreamerStdio.cpp @@ -0,0 +1,88 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TestShell.h" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::Shell; +using namespace chip::Logging; + +// ================================= +// Test Vectors +// ================================= + +struct test_streamer_vector +{ + const char * output; +}; + +static const struct test_streamer_vector test_vector_streamer_out[] = { + { .output = "prompt>\n" }, { .output = "T123 " }, { .output = "T456\n" }, { .output = "T789 " }, { .output = "T10\n" }, +}; + +// ================================= +// Unit tests +// ================================= + +static void TestStreamer_Output(nlTestSuite * inSuite, void * inContext) +{ + int numOfTestVectors = ArraySize(test_vector_streamer_out); + int numOfTestsRan = 0; + const struct test_streamer_vector * test_params; + + const char * output; + int num_chars; + + for (int vectorIndex = 0; vectorIndex < numOfTestVectors; vectorIndex++) + { + test_params = &test_vector_streamer_out[vectorIndex]; + output = test_params->output; + + num_chars = streamer_write(streamer_get(), output, strlen(output)); + NL_TEST_ASSERT(inSuite, num_chars == (int) strlen(output)); + numOfTestsRan++; + } + NL_TEST_ASSERT(inSuite, numOfTestsRan > 0); +} + +/** + * Test Suite. It lists all the test functions. + */ +static const nlTest sTests[] = { + + NL_TEST_DEF("Test Streamer: TestStreamer_Output", TestStreamer_Output), + + NL_TEST_SENTINEL() +}; + +int TestStreamerStdio(void) +{ + nlTestSuite theSuite = { "CHIP Streamer tests", &sTests[0], NULL, NULL }; + + // Run test suit againt one context. + nlTestRunner(&theSuite, NULL); + return nlTestRunnerStats(&theSuite); +} diff --git a/src/lib/shell/tests/TestStreamerStdioDriver.cpp b/src/lib/shell/tests/TestStreamerStdioDriver.cpp new file mode 100644 index 00000000000000..cbbc162ade9994 --- /dev/null +++ b/src/lib/shell/tests/TestStreamerStdioDriver.cpp @@ -0,0 +1,34 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements a standalone/native program executable + * test driver for the CHIP system layer library timer unit + * tests. + * + */ + +#include "TestShell.h" + +int main(int argc, char * argv[]) +{ + // Generate machine-readable, comma-separated value (CSV) output. + nlTestSetOutputStyle(OUTPUT_CSV); + + return (TestStreamerStdio()); +} diff --git a/src/lib/support/CodeUtils.h b/src/lib/support/CodeUtils.h index 5d4eae1cb00305..131795c63ae9ed 100644 --- a/src/lib/support/CodeUtils.h +++ b/src/lib/support/CodeUtils.h @@ -404,4 +404,12 @@ inline void chipDie(void) #endif // defined(__cplusplus) && (__cplusplus >= 201103L) +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define CHECK_RETURN_VALUE __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1700) +#define CHECK_RETURN_VALUE _Check_return_ +#else +#define CHECK_RETURN_VALUE +#endif + #endif /* CODEUTILS_H_ */ diff --git a/src/lib/support/Makefile.am b/src/lib/support/Makefile.am index 86bbceca4ecdea..0244ae376f87cf 100644 --- a/src/lib/support/Makefile.am +++ b/src/lib/support/Makefile.am @@ -28,7 +28,6 @@ SUBDIRS = \ include SupportLayer.am - lib_LIBRARIES = libSupportLayer.a libSupportLayer_a_CPPFLAGS = \ diff --git a/src/lib/support/SupportLayer.am b/src/lib/support/SupportLayer.am index 0fae8d17572458..1ebcf5eb3aabb6 100644 --- a/src/lib/support/SupportLayer.am +++ b/src/lib/support/SupportLayer.am @@ -22,48 +22,46 @@ # must be anchored relative to the top build directory. # -AM_CXXFLAGS = \ - $(NLUNIT_TEST_CPPFLAGS) \ - $(NULL) -CHIP_BUILD_SUPPORT_LAYER_SOURCE_FILES = \ - @top_builddir@/src/lib/support/Base64.cpp \ - @top_builddir@/src/lib/support/CHIPArgParser.cpp \ - @top_builddir@/src/lib/support/CHIPCounter.cpp \ - @top_builddir@/src/lib/support/CHIPFaultInjection.cpp \ - @top_builddir@/src/lib/support/ErrorStr.cpp \ - @top_builddir@/src/lib/support/FibonacciUtils.cpp \ - @top_builddir@/src/lib/support/logging/CHIPLogging.cpp \ - @top_builddir@/src/lib/support/PersistedCounter.cpp \ - @top_builddir@/src/lib/support/RandUtils.cpp \ - @top_builddir@/src/lib/support/TestUtils.cpp \ - @top_builddir@/src/lib/support/TimeUtils.cpp \ - @top_builddir@/src/lib/support/verhoeff/Verhoeff.cpp \ - @top_builddir@/src/lib/support/verhoeff/Verhoeff10.cpp \ - @top_builddir@/src/lib/support/verhoeff/Verhoeff16.cpp \ - @top_builddir@/src/lib/support/verhoeff/Verhoeff32.cpp \ - @top_builddir@/src/lib/support/verhoeff/Verhoeff36.cpp \ +CHIP_BUILD_SUPPORT_LAYER_SOURCE_FILES = \ + @top_builddir@/src/lib/support/Base64.cpp \ + @top_builddir@/src/lib/support/CHIPArgParser.cpp \ + @top_builddir@/src/lib/support/CHIPCounter.cpp \ + @top_builddir@/src/lib/support/CHIPFaultInjection.cpp \ + @top_builddir@/src/lib/support/ErrorStr.cpp \ + @top_builddir@/src/lib/support/FibonacciUtils.cpp \ + @top_builddir@/src/lib/support/logging/CHIPLogging.cpp \ + @top_builddir@/src/lib/support/logging/CHIPLoggingLogV.cpp \ + @top_builddir@/src/lib/support/PersistedCounter.cpp \ + @top_builddir@/src/lib/support/RandUtils.cpp \ + @top_builddir@/src/lib/support/TestUtils.cpp \ + @top_builddir@/src/lib/support/TimeUtils.cpp \ + @top_builddir@/src/lib/support/verhoeff/Verhoeff.cpp \ + @top_builddir@/src/lib/support/verhoeff/Verhoeff10.cpp \ + @top_builddir@/src/lib/support/verhoeff/Verhoeff16.cpp \ + @top_builddir@/src/lib/support/verhoeff/Verhoeff32.cpp \ + @top_builddir@/src/lib/support/verhoeff/Verhoeff36.cpp \ $(NULL) -CHIP_BUILD_SUPPORT_LAYER_HEADER_FILES = \ - @top_builddir@/src/lib/support/CHIPArgParser.hpp \ - @top_builddir@/src/lib/support/CHIPCounter.h \ - @top_builddir@/src/lib/support/CHIPFaultInjection.h \ - @top_builddir@/src/lib/support/CodeUtils.h \ - @top_builddir@/src/lib/support/DLLUtil.h \ - @top_builddir@/src/lib/support/ErrorStr.h \ - @top_builddir@/src/lib/support/FibonacciUtils.h \ - @top_builddir@/src/lib/support/FlagUtils.hpp \ - @top_builddir@/src/lib/support/logging/CHIPLogging.h \ - @top_builddir@/src/lib/support/Base64.h \ - @top_builddir@/src/lib/support/PersistedCounter.h \ - @top_builddir@/src/lib/support/RandUtils.h \ - @top_builddir@/src/lib/support/TestUtils.h \ - @top_builddir@/src/lib/support/TimeUtils.h \ +CHIP_BUILD_SUPPORT_LAYER_HEADER_FILES = \ + @top_builddir@/src/lib/support/CHIPArgParser.hpp \ + @top_builddir@/src/lib/support/CHIPCounter.h \ + @top_builddir@/src/lib/support/CHIPFaultInjection.h \ + @top_builddir@/src/lib/support/CodeUtils.h \ + @top_builddir@/src/lib/support/DLLUtil.h \ + @top_builddir@/src/lib/support/ErrorStr.h \ + @top_builddir@/src/lib/support/FibonacciUtils.h \ + @top_builddir@/src/lib/support/FlagUtils.hpp \ + @top_builddir@/src/lib/support/logging/CHIPLogging.h \ + @top_builddir@/src/lib/support/Base64.h \ + @top_builddir@/src/lib/support/PersistedCounter.h \ + @top_builddir@/src/lib/support/RandUtils.h \ + @top_builddir@/src/lib/support/TestUtils.h \ + @top_builddir@/src/lib/support/TimeUtils.h \ $(NULL) -CHIP_BUILD_SUPPORT_LAYER_VERHOEFF_HEADER_FILES = \ - @top_builddir@/src/lib/support/verhoeff/Verhoeff.h \ +CHIP_BUILD_SUPPORT_LAYER_VERHOEFF_HEADER_FILES = \ + @top_builddir@/src/lib/support/verhoeff/Verhoeff.h \ $(NULL) if CHIP_WITH_NLFAULTINJECTION diff --git a/src/lib/support/logging/CHIPLogging.cpp b/src/lib/support/logging/CHIPLogging.cpp index 71d37ff9f50992..4caf5c562ae0ce 100644 --- a/src/lib/support/logging/CHIPLogging.cpp +++ b/src/lib/support/logging/CHIPLogging.cpp @@ -65,7 +65,7 @@ static const char ModuleNames[] = "-\0\0" // None "TLV" // TLV "ASN" // ASN1 "CR\0" // Crypto - "CTL\0" // Controller + "CTL" // Controller "AL\0" // Alarm "BDX" // BulkDataTransfer "DMG" // DataManagement @@ -79,11 +79,16 @@ static const char ModuleNames[] = "-\0\0" // None "SWU" // SoftwareUpdate "TP\0" // TokenPairing "TS\0" // TimeServices - "WT\0" // chipTunnel + "TUN" // chipTunnel "HB\0" // Heartbeat - "WSL" // chipSystemLayer + "CSL" // chipSystemLayer "EVL" // Event Logging "SPT" // Support + "TOO" // chipTool + "ZCL" // Zcl + "SH\0" // Shell + "DL\0" // DeviceLayer + "SPL" // SetupPayload ; #define ModuleNamesCount ((sizeof(ModuleNames) - 1) / ChipLoggingModuleNameLen) @@ -150,17 +155,6 @@ uint8_t gLogFilter = kLogCategory_Max; #endif // CHIP_LOG_FILTERING -#if !CHIP_LOGGING_STYLE_EXTERNAL -/* - * Only enable an in-package implementation of the logging interface - * if external logging was not requested. Within that, the package - * supports either Android-style or C Standard I/O-style logging. - * - * In the event a "weak" variant is specified, i.e - * CHIP_LOGGING_STYLE_STDIO_WEAK, the in-package implementation will - * be provided but with "weak" linkage - */ - /** * Log, to the platform-specified mechanism, the specified log * message, @a msg, for the specified module, @a module, in the @@ -183,47 +177,16 @@ uint8_t gLogFilter = kLogCategory_Max; * correspond to the format specifiers in @a msg. * */ - -#if CHIP_LOGGING_STYLE_STDIO_WEAK -#define __CHIP_LOGGING_LINK_ATTRIBUTE __attribute__((weak)) -#else -#define __CHIP_LOGGING_LINK_ATTRIBUTE -#endif - -DLL_EXPORT __CHIP_LOGGING_LINK_ATTRIBUTE void Log(uint8_t module, uint8_t category, const char * msg, ...) +DLL_EXPORT void Log(uint8_t module, uint8_t category, const char * msg, ...) { va_list v; va_start(v, msg); - if (IsCategoryEnabled(category)) - { - -#if CHIP_LOGGING_STYLE_ANDROID - - char moduleName[ChipLoggingModuleNameLen + 1]; - GetModuleName(moduleName, module); - - int priority = (category == kLogCategory_Error) ? ANDROID_LOG_ERROR : ANDROID_LOG_DEBUG; - - __android_log_vprint(priority, moduleName, msg, v); - -#elif CHIP_LOGGING_STYLE_STDIO || CHIP_LOGGING_STYLE_STDIO_WEAK - - PrintMessagePrefix(module); - vprintf(msg, v); - printf("\n"); - -#else - -#error "Undefined platform-specific implementation for non-externnal chip logging style!" - -#endif /* CHIP_LOGGING_STYLE_ANDROID */ - } + LogV(module, category, msg, v); va_end(v); } -#endif /* !CHIP_LOGGING_STYLE_EXTERNAL */ DLL_EXPORT uint8_t GetLogFilter() { diff --git a/src/lib/support/logging/CHIPLogging.h b/src/lib/support/logging/CHIPLogging.h index c3a86f83b4e8bd..e2728f2586e92a 100644 --- a/src/lib/support/logging/CHIPLogging.h +++ b/src/lib/support/logging/CHIPLogging.h @@ -37,6 +37,7 @@ #include +#include #include /** @@ -106,6 +107,10 @@ enum LogModule kLogModule_EventLogging, kLogModule_Support, kLogModule_chipTool, + kLogModule_Zcl, + kLogModule_Shell, + kLogModule_DeviceLayer, + kLogModule_SetupPayload, kLogModule_Max }; @@ -184,6 +189,7 @@ enum LogCategory kLogCategory_Max = kLogCategory_Retain }; +extern void LogV(uint8_t module, uint8_t category, const char * msg, va_list args); extern void Log(uint8_t module, uint8_t category, const char * msg, ...); extern uint8_t GetLogFilter(void); extern void SetLogFilter(uint8_t category); diff --git a/src/lib/support/logging/CHIPLoggingLogV.cpp b/src/lib/support/logging/CHIPLoggingLogV.cpp new file mode 100644 index 00000000000000..dd8b869a9219b3 --- /dev/null +++ b/src/lib/support/logging/CHIPLoggingLogV.cpp @@ -0,0 +1,122 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2013-2017 Nest Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements macros, constants, and interfaces for a + * platform-independent logging interface for the chip SDK. + * + */ + +#include "CHIPLogging.h" + +#include +#include +#include + +#if CHIP_LOGGING_STYLE_ANDROID && defined(__ANDROID__) +#include +#endif + +#if HAVE_SYS_TIME_H && CHIP_LOGGING_STYLE_STDIO_WITH_TIMESTAMPS +#include +#endif // HAVE_SYS_TIME_H && CHIP_LOGGING_STYLE_STDIO_WITH_TIMESTAMPS + +#include +#include +#include + +namespace chip { +namespace Logging { + +#if _CHIP_USE_LOGGING + +#if !CHIP_LOGGING_STYLE_EXTERNAL +/* + * Only enable an in-package implementation of the logging interface + * if external logging was not requested. Within that, the package + * supports either Android-style or C Standard I/O-style logging. + * + * In the event a "weak" variant is specified, i.e + * CHIP_LOGGING_STYLE_STDIO_WEAK, the in-package implementation will + * be provided but with "weak" linkage + */ + +/** + * Log, to the platform-specified mechanism, the specified log + * message, @a msg, for the specified module, @a module, in the + * provided category, @a category. + * + * @param[in] module A LogModule enumeration indicating the + * source of the chip package module that + * generated the log message. This must be + * translated within the function to a module + * name for inclusion in the log message. + * @param[in] category A LogCategory enumeration indicating the + * category of the log message. The category + * may be filtered in or out if + * CHIP_LOG_FILTERING was asserted. + * @param[in] msg A pointer to a NULL-terminated C string with + * C Standard Library-style format specifiers + * containing the log message to be formatted and + * logged. + * @param[in] v A variadic argument list whose elements should + * correspond to the format specifiers in @a msg. + * + */ + +#if CHIP_LOGGING_STYLE_STDIO_WEAK +#define __CHIP_LOGGING_LINK_ATTRIBUTE __attribute__((weak)) +#else +#define __CHIP_LOGGING_LINK_ATTRIBUTE +#endif + +DLL_EXPORT __CHIP_LOGGING_LINK_ATTRIBUTE void LogV(uint8_t module, uint8_t category, const char * msg, va_list v) +{ + if (IsCategoryEnabled(category)) + { + +#if CHIP_LOGGING_STYLE_ANDROID + + char moduleName[ChipLoggingModuleNameLen + 1]; + GetModuleName(moduleName, module); + + int priority = (category == kLogCategory_Error) ? ANDROID_LOG_ERROR : ANDROID_LOG_DEBUG; + + __android_log_vprint(priority, moduleName, msg, v); + +#elif CHIP_LOGGING_STYLE_STDIO || CHIP_LOGGING_STYLE_STDIO_WEAK + + PrintMessagePrefix(module); + vprintf(msg, v); + printf("\n"); + +#else + +#error "Undefined platform-specific implementation for non-externnal chip logging style!" + +#endif /* CHIP_LOGGING_STYLE_ANDROID */ + } +} + +#endif /* !CHIP_LOGGING_STYLE_EXTERNAL */ + +#endif /* _CHIP_USE_LOGGING */ + +} // namespace Logging +} // namespace chip diff --git a/src/lwip/freertos/sys_arch.c b/src/lwip/freertos/sys_arch.c index 49ada9f4b96518..ebe0c9a3045d8b 100644 --- a/src/lwip/freertos/sys_arch.c +++ b/src/lwip/freertos/sys_arch.c @@ -274,6 +274,15 @@ sys_thread_t sys_thread_new(const char * name, lwip_thread_fn thread, void * arg return taskH; } +err_t sys_thread_finish(sys_thread_t t) +{ +#if INCLUDE_vTaskDelete + TaskHandle_t taskH = (TaskHandle_t) t; + vTaskDelete(taskH); +#endif + return ERR_OK; +} + u32_t sys_now(void) { return TicksToMS(xTaskGetTickCount()); diff --git a/src/platform/EFR32/Logging.cpp b/src/platform/EFR32/Logging.cpp index 70ec4775225292..9764a5c5d09a6d 100644 --- a/src/platform/EFR32/Logging.cpp +++ b/src/platform/EFR32/Logging.cpp @@ -161,13 +161,10 @@ namespace chip { namespace Logging { /** - * CHIP log output function. + * CHIP log output functions. */ -void Log(uint8_t module, uint8_t category, const char * aFormat, ...) +void LogV(uint8_t module, uint8_t category, const char * aFormat, va_list v) { - va_list v; - - va_start(v, aFormat); #if EFR32_LOG_ENABLED && _CHIP_USE_LOGGING if (IsCategoryEnabled(category)) { @@ -214,7 +211,6 @@ void Log(uint8_t module, uint8_t category, const char * aFormat, ...) // Let the application know that a log message has been emitted. DeviceLayer::OnLogOutput(); #endif // EFR32_LOG_ENABLED && _CHIP_USE_LOGGING - va_end(v); } } // namespace Logging diff --git a/src/platform/ESP32/ConnectivityManagerImpl.cpp b/src/platform/ESP32/ConnectivityManagerImpl.cpp index e97a329e23e38f..32d4819c07bee3 100644 --- a/src/platform/ESP32/ConnectivityManagerImpl.cpp +++ b/src/platform/ESP32/ConnectivityManagerImpl.cpp @@ -981,7 +981,14 @@ void ConnectivityManagerImpl::UpdateInternetConnectivityState(void) { if (ip6_addr_isglobal(netif_ip6_addr(netif, i)) && ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) { - // TODO Determine if there is a default IPv6 router that is currently reachable + // Determine if there is a default IPv6 router that is currently reachable + // via the station interface. If so, presume for now that the device has + // IPv6 connectivity. + struct netif * found_if = nd6_find_route(IP6_ADDR_ANY6); + if (found_if && netif->num == found_if->num) + { + haveIPv6Conn = true; + } } } } diff --git a/src/platform/ESP32/Logging.cpp b/src/platform/ESP32/Logging.cpp index ba089df11483ed..d680256bf8af9a 100644 --- a/src/platform/ESP32/Logging.cpp +++ b/src/platform/ESP32/Logging.cpp @@ -51,12 +51,8 @@ void GetModuleName(char * buf, uint8_t module) namespace chip { namespace Logging { -void Log(uint8_t module, uint8_t category, const char * msg, ...) +void LogV(uint8_t module, uint8_t category, const char * msg, va_list v) { - va_list v; - - va_start(v, msg); - if (IsCategoryEnabled(category)) { enum @@ -91,9 +87,8 @@ void Log(uint8_t module, uint8_t category, const char * msg, ...) break; } } - - va_end(v); } } // namespace Logging + } // namespace chip diff --git a/src/platform/FreeRTOS/GenericThreadStackManagerImpl_FreeRTOS.h b/src/platform/FreeRTOS/GenericThreadStackManagerImpl_FreeRTOS.h new file mode 100644 index 00000000000000..b9b5b098d2fd98 --- /dev/null +++ b/src/platform/FreeRTOS/GenericThreadStackManagerImpl_FreeRTOS.h @@ -0,0 +1,89 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2018 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Provides an generic implementation of ThreadStackManager features + * for use on FreeRTOS platforms. + */ + +#ifndef GENERIC_THREAD_STACK_MANAGER_IMPL_FREERTOS_H +#define GENERIC_THREAD_STACK_MANAGER_IMPL_FREERTOS_H + +#if defined(ESP_PLATFORM) +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" +#else +#include "FreeRTOS.h" +#include "semphr.h" +#include "task.h" +#endif + +namespace chip { +namespace DeviceLayer { + +class ThreadStackManagerImpl; + +namespace Internal { + +/** + * Provides a generic implementation of ThreadStackManager features that works on FreeRTOS platforms. + * + * This template contains implementations of select features from the ThreadStackManager abstract + * interface that are suitable for use on FreeRTOS-based platforms. It is intended to be + * inherited, directly or indirectly, by the ThreadStackManagerImpl class, which also appears as + * the template's ImplClass parameter. + */ +template +class GenericThreadStackManagerImpl_FreeRTOS +{ +protected: + // ===== Methods that implement the ThreadStackManager abstract interface. + + CHIP_ERROR _StartThreadTask(void); + void _LockThreadStack(void); + bool _TryLockThreadStack(void); + void _UnlockThreadStack(void); + + // ===== Members available to the implementation subclass. + + SemaphoreHandle_t mThreadStackLock; + TaskHandle_t mThreadTask; + + CHIP_ERROR DoInit(); + void SignalThreadActivityPending(); + BaseType_t SignalThreadActivityPendingFromISR(); + +private: + // ===== Private members for use by this class only. + + inline ImplClass * Impl() { return static_cast(this); } + + static void ThreadTaskMain(void * arg); +}; + +// Instruct the compiler to instantiate the template only when explicitly told to do so. +extern template class GenericThreadStackManagerImpl_FreeRTOS; + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif // GENERIC_THREAD_STACK_MANAGER_IMPL_FREERTOS_H diff --git a/src/platform/FreeRTOS/GenericThreadStackManagerImpl_FreeRTOS.ipp b/src/platform/FreeRTOS/GenericThreadStackManagerImpl_FreeRTOS.ipp new file mode 100644 index 00000000000000..6a2276583ad454 --- /dev/null +++ b/src/platform/FreeRTOS/GenericThreadStackManagerImpl_FreeRTOS.ipp @@ -0,0 +1,150 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Contains non-inline method definitions for the + * GenericThreadStackManagerImpl_FreeRTOS<> template. + */ + +#ifndef GENERIC_THREAD_STACK_MANAGER_IMPL_FREERTOS_IPP +#define GENERIC_THREAD_STACK_MANAGER_IMPL_FREERTOS_IPP + +#include +#include +#include +#include +#include + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +// Fully instantiate the generic implementation class in whatever compilation unit includes this file. +template class GenericThreadStackManagerImpl_FreeRTOS; + +template +CHIP_ERROR GenericThreadStackManagerImpl_FreeRTOS::DoInit(void) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + mThreadStackLock = xSemaphoreCreateMutex(); + if (mThreadStackLock == NULL) + { + ChipLogError(DeviceLayer, "Failed to create Thread stack lock"); + ExitNow(err = CHIP_ERROR_NO_MEMORY); + } + + mThreadTask = NULL; + +exit: + return err; +} + +template +CHIP_ERROR GenericThreadStackManagerImpl_FreeRTOS::_StartThreadTask(void) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + BaseType_t res; + + VerifyOrExit(mThreadTask == NULL, err = CHIP_ERROR_INCORRECT_STATE); + + res = xTaskCreate(ThreadTaskMain, CHIP_DEVICE_CONFIG_THREAD_TASK_NAME, + CHIP_DEVICE_CONFIG_THREAD_TASK_STACK_SIZE / sizeof(StackType_t), this, + CHIP_DEVICE_CONFIG_THREAD_TASK_PRIORITY, NULL); + VerifyOrExit(res == pdPASS, err = CHIP_ERROR_NO_MEMORY); + +exit: + return err; +} + +template +void GenericThreadStackManagerImpl_FreeRTOS::_LockThreadStack(void) +{ + xSemaphoreTake(mThreadStackLock, portMAX_DELAY); +} + +template +bool GenericThreadStackManagerImpl_FreeRTOS::_TryLockThreadStack(void) +{ + return xSemaphoreTake(mThreadStackLock, 0) == pdTRUE; +} + +template +void GenericThreadStackManagerImpl_FreeRTOS::_UnlockThreadStack(void) +{ + xSemaphoreGive(mThreadStackLock); +} + +template +void GenericThreadStackManagerImpl_FreeRTOS::SignalThreadActivityPending() +{ + if (mThreadTask != NULL) + { + xTaskNotifyGive(mThreadTask); + } +} + +template +BaseType_t GenericThreadStackManagerImpl_FreeRTOS::SignalThreadActivityPendingFromISR() +{ + BaseType_t yieldRequired = pdFALSE; + + if (mThreadTask != NULL) + { + vTaskNotifyGiveFromISR(mThreadTask, &yieldRequired); + } + + return yieldRequired; +} + +template +void GenericThreadStackManagerImpl_FreeRTOS::ThreadTaskMain(void * arg) +{ + GenericThreadStackManagerImpl_FreeRTOS * self = + static_cast *>(arg); + + VerifyOrDie(self->mThreadTask == NULL); + + ChipLogDetail(DeviceLayer, "Thread task running"); + + // Capture the Thread task handle. + self->mThreadTask = xTaskGetCurrentTaskHandle(); + + while (true) + { + // Lock the Thread stack. + self->Impl()->LockThreadStack(); + + // Process any pending Thread activity. + self->Impl()->ProcessThreadActivity(); + + // Unlock the Thread stack. + self->Impl()->UnlockThreadStack(); + + // Wait for a signal that more activity is pending. + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + } +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif // GENERIC_THREAD_STACK_MANAGER_IMPL_FREERTOS_IPP diff --git a/src/platform/GeneralUtils.cpp b/src/platform/GeneralUtils.cpp index 5c2e5809e5f505..5c78a157f2dcb1 100644 --- a/src/platform/GeneralUtils.cpp +++ b/src/platform/GeneralUtils.cpp @@ -125,5 +125,22 @@ bool FormatDeviceLayerError(char * buf, uint16_t bufSize, int32_t err) return true; } +const char * CharacterizeIPv6Address(const IPAddress & ipAddr) +{ + if (ipAddr.IsIPv6LinkLocal()) + { + return "IPv6 link-local address"; + } + else if (ipAddr.IsIPv6ULA()) + { + return "IPv6 unique local address"; + } + else if (ipAddr.IsIPv6GlobalUnicast()) + { + return "IPv6 global unicast address"; + } + return "IPv6 address"; +} + } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/Linux/CHIPDevicePlatformConfig.h b/src/platform/Linux/CHIPDevicePlatformConfig.h index e1bdb6c1ef12bd..3daef2dd670ec5 100644 --- a/src/platform/Linux/CHIPDevicePlatformConfig.h +++ b/src/platform/Linux/CHIPDevicePlatformConfig.h @@ -81,4 +81,7 @@ #define CHIP_DEVICE_CONFIG_ENABLE_THREAD_TELEMETRY_FULL 0 #define CHIP_DEVICE_CONFIG_ENABLE_TUNNEL_TELEMETRY 0 +// TODO: CHIP has redesigned the crypto interface, pending on the final version of CHIP HASH APIs +#define CHIP_DEVICE_CONFIG_LOG_PROVISIONING_HASH 0 + #endif // CHIP_DEVICE_PLATFORM_CONFIG_H diff --git a/src/platform/Linux/CHIPLinuxStorage.cpp b/src/platform/Linux/CHIPLinuxStorage.cpp new file mode 100644 index 00000000000000..0e386bca019a7b --- /dev/null +++ b/src/platform/Linux/CHIPLinuxStorage.cpp @@ -0,0 +1,323 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements a class for managing client application + * user-editable settings on Linux platform. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +ChipLinuxStorage::ChipLinuxStorage() +{ + mDirty = false; +} + +ChipLinuxStorage::~ChipLinuxStorage() {} + +CHIP_ERROR ChipLinuxStorage::Init(const char * configFile) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + mConfigPath.assign(configFile); + retval = ChipLinuxStorageIni::Init(); + + if (retval == CHIP_NO_ERROR) + { + std::ifstream ifs; + + ifs.open(configFile, std::ifstream::in); + + // Create default setting file if not exist. + if (!ifs.good()) + { + mDirty = true; + retval = Commit(); + mDirty = false; + } + } + + if (retval == CHIP_NO_ERROR) + { + retval = ChipLinuxStorageIni::AddConfig(mConfigPath); + } + + return retval; +} + +CHIP_ERROR ChipLinuxStorage::ReadValue(const char * key, bool & val) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + uint32_t result; + + mLock.lock(); + + retval = ChipLinuxStorageIni::GetUIntValue(key, result); + val = (result == 0 ? false : true); + + mLock.unlock(); + + return retval; +} + +CHIP_ERROR ChipLinuxStorage::ReadValue(const char * key, uint32_t & val) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + mLock.lock(); + + retval = ChipLinuxStorageIni::GetUIntValue(key, val); + + mLock.unlock(); + + return retval; +} + +CHIP_ERROR ChipLinuxStorage::ReadValue(const char * key, uint64_t & val) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + mLock.lock(); + + retval = ChipLinuxStorageIni::GetUInt64Value(key, val); + + mLock.unlock(); + + return retval; +} + +CHIP_ERROR ChipLinuxStorage::ReadValueStr(const char * key, char * buf, size_t bufSize, size_t & outLen) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + mLock.lock(); + + retval = ChipLinuxStorageIni::GetStringValue(key, buf, bufSize, outLen); + + mLock.unlock(); + + return retval; +} + +CHIP_ERROR ChipLinuxStorage::ReadValueBin(const char * key, uint8_t * buf, size_t bufSize, size_t & outLen) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + mLock.lock(); + + retval = ChipLinuxStorageIni::GetBinaryBlobValue(key, buf, bufSize, outLen); + + mLock.unlock(); + + return retval; +} + +CHIP_ERROR ChipLinuxStorage::WriteValue(const char * key, bool val) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + if (val) + { + retval = WriteValue(key, (uint32_t) 1); + } + else + { + retval = WriteValue(key, (uint32_t) 0); + } + + return retval; +} + +CHIP_ERROR ChipLinuxStorage::WriteValue(const char * key, uint32_t val) +{ + char buf[32]; + + snprintf(buf, sizeof(buf), "%d", val); + + return WriteValueStr(key, buf); +} + +CHIP_ERROR ChipLinuxStorage::WriteValue(const char * key, uint64_t val) +{ + char buf[64]; + + snprintf(buf, sizeof(buf), "%llu", val); + + return WriteValueStr(key, buf); +} + +CHIP_ERROR ChipLinuxStorage::WriteValueStr(const char * key, const char * val) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + mLock.lock(); + + retval = ChipLinuxStorageIni::AddEntry(key, val); + + mDirty = true; + + mLock.unlock(); + + return retval; +} + +CHIP_ERROR ChipLinuxStorage::WriteValueBin(const char * key, const uint8_t * data, size_t dataLen) +{ + static const size_t kMaxBlobSize = 5 * 1024; + + CHIP_ERROR retval = CHIP_NO_ERROR; + char * encodedData = NULL; + size_t encodedDataLen = 0; + size_t expectedEncodedLen = ((dataLen + 3) * 4) / 3; + + // We only support encoding blobs up to 5kb + if (dataLen > kMaxBlobSize) + { + retval = CHIP_ERROR_INVALID_ARGUMENT; + } + + // Compute our expectedEncodedLen + // Allocate just enough space for the encoded data, and the NULL terminator + if (retval == CHIP_NO_ERROR) + { + encodedData = (char *) malloc(expectedEncodedLen + 1); + if (encodedData == NULL) + { + retval = CHIP_ERROR_NO_MEMORY; + } + } + + // Encode it + if (retval == CHIP_NO_ERROR) + { + encodedDataLen = Base64Encode(data, dataLen, encodedData); + encodedData[encodedDataLen] = 0; + } + + // Store it + if (retval == CHIP_NO_ERROR) + { + WriteValueStr(key, (const char *) encodedData); + } + + // Free memory + if (encodedData) + { + free(encodedData); + } + + return retval; +} + +CHIP_ERROR ChipLinuxStorage::ClearValue(const char * key) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + mLock.lock(); + + retval = ChipLinuxStorageIni::RemoveEntry(key); + + if (retval == CHIP_NO_ERROR) + { + mDirty = true; + } + else + { + retval = CHIP_ERROR_KEY_NOT_FOUND; + } + + mLock.unlock(); + + return retval; +} + +CHIP_ERROR ChipLinuxStorage::ClearAll(void) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + mLock.lock(); + + retval = ChipLinuxStorageIni::RemoveAll(); + + mLock.unlock(); + + if (retval == CHIP_NO_ERROR) + { + mDirty = true; + retval = Commit(); + } + else + { + retval = CHIP_ERROR_PERSISTED_STORAGE_FAIL; + } + + return retval; +} + +bool ChipLinuxStorage::HasValue(const char * key) +{ + bool retval; + + mLock.lock(); + + retval = ChipLinuxStorageIni::HasValue(key); + + mLock.unlock(); + + return retval; +} + +CHIP_ERROR ChipLinuxStorage::Commit(void) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + if (mDirty && !mConfigPath.empty()) + { + mLock.lock(); + + retval = ChipLinuxStorageIni::CommitConfig(mConfigPath); + + mLock.unlock(); + } + else + { + retval = CHIP_ERROR_PERSISTED_STORAGE_FAIL; + } + + return retval; +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/Linux/CHIPLinuxStorage.h b/src/platform/Linux/CHIPLinuxStorage.h new file mode 100644 index 00000000000000..cf46b59fe054c6 --- /dev/null +++ b/src/platform/Linux/CHIPLinuxStorage.h @@ -0,0 +1,98 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file defines a class for managing client application + * user-editable settings. CHIP settings are partitioned into two + * distinct areas: + * + * 1. immutable / durable: factory parameters (CHIP_DEFAULT_FACTORY_PATH) + * 2. mutable / ephemeral: user parameters (CHIP_DEFAULT_CONFIG_PATH/CHIP_DEFAULT_DATA_PATH) + * + * The ephemeral partition should be erased during factory reset. + * + * ChipLinuxStorage wraps the storage class ChipLinuxStorageIni with mutex. + * + */ + +#ifndef CHIP_LINUX_STORAGE_H +#define CHIP_LINUX_STORAGE_H + +#include +#include + +#ifndef FATCONFDIR +#define FATCONFDIR "/tmp" +#endif + +#ifndef SYSCONFDIR +#define SYSCONFDIR "/tmp" +#endif + +#ifndef LOCALSTATEDIR +#define LOCALSTATEDIR "/tmp" +#endif + +#define CHIP_DEFAULT_FACTORY_PATH \ + FATCONFDIR "/" \ + "chip_factory.ini" +#define CHIP_DEFAULT_CONFIG_PATH \ + SYSCONFDIR "/" \ + "chip_config.ini" +#define CHIP_DEFAULT_DATA_PATH \ + LOCALSTATEDIR "/" \ + "chip_counters.ini" + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +class ChipLinuxStorage : private ChipLinuxStorageIni +{ +public: + ChipLinuxStorage(); + ~ChipLinuxStorage(); + + CHIP_ERROR Init(const char * configFile); + CHIP_ERROR ReadValue(const char * key, bool & val); + CHIP_ERROR ReadValue(const char * key, uint32_t & val); + CHIP_ERROR ReadValue(const char * key, uint64_t & val); + CHIP_ERROR ReadValueStr(const char * key, char * buf, size_t bufSize, size_t & outLen); + CHIP_ERROR ReadValueBin(const char * key, uint8_t * buf, size_t bufSize, size_t & outLen); + CHIP_ERROR WriteValue(const char * key, bool val); + CHIP_ERROR WriteValue(const char * key, uint32_t val); + CHIP_ERROR WriteValue(const char * key, uint64_t val); + CHIP_ERROR WriteValueStr(const char * key, const char * val); + CHIP_ERROR WriteValueBin(const char * key, const uint8_t * data, size_t dataLen); + CHIP_ERROR ClearValue(const char * key); + CHIP_ERROR ClearAll(void); + CHIP_ERROR Commit(void); + bool HasValue(const char * key); + +private: + std::mutex mLock; + bool mDirty; + std::string mConfigPath; +}; + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif /* CHIP_LINUX_STORAGE_H */ diff --git a/src/platform/Linux/CHIPLinuxStorageIni.cpp b/src/platform/Linux/CHIPLinuxStorageIni.cpp new file mode 100644 index 00000000000000..534c9dbcc0a4ea --- /dev/null +++ b/src/platform/Linux/CHIPLinuxStorageIni.cpp @@ -0,0 +1,371 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Provides an implementation of the Configuration key-value store object + * using IniPP on Linux platform. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +CHIP_ERROR ChipLinuxStorageIni::Init(void) +{ + return RemoveAll(); +} + +CHIP_ERROR ChipLinuxStorageIni::GetDefaultSection(std::map & section) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + auto it = mConfigStore.sections.find("DEFAULT"); + + if (it != mConfigStore.sections.end()) + { + section = mConfigStore.sections["DEFAULT"]; + } + else + { + retval = CHIP_ERROR_KEY_NOT_FOUND; + } + + return retval; +} + +CHIP_ERROR ChipLinuxStorageIni::AddConfig(const std::string & configFile) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + std::ifstream ifs; + + ifs.open(configFile, std::ifstream::in); + + if (ifs.is_open()) + { + mConfigStore.parse(ifs); + ifs.close(); + } + else + { + ChipLogError(DeviceLayer, "Failed to open config file: %s", configFile); + retval = CHIP_ERROR_PERSISTED_STORAGE_FAIL; + } + + return retval; +} + +// Updating a file atomically and durably on Linux requires: +// 1. Writing to a temporary file +// 2. Sync'ing the temp file to commit updated data +// 3. Using rename() to overwrite the existing file +CHIP_ERROR ChipLinuxStorageIni::CommitConfig(const std::string & configFile) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + std::ofstream ofs; + std::string tmpPath = configFile; + + tmpPath.append(".tmp"); + + ofs.open(tmpPath, std::ofstream::out | std::ofstream::trunc); + + if (ofs.is_open()) + { + ChipLogProgress(DeviceLayer, "writing settings to file (%s)", tmpPath.c_str()); + + mConfigStore.generate(ofs); + ofs.close(); + + if (rename(tmpPath.c_str(), configFile.c_str()) == 0) + { + ChipLogError(DeviceLayer, "renamed tmp file to file (%s)", configFile.c_str()); + } + else + { + ChipLogError(DeviceLayer, "failed to rename (%s), %s (%d)", tmpPath.c_str(), strerror(errno), errno); + retval = CHIP_ERROR_PERSISTED_STORAGE_FAIL; + } + } + else + { + ChipLogError(DeviceLayer, "failed to open file (%s) for writing", tmpPath.c_str()); + retval = CHIP_ERROR_PERSISTED_STORAGE_FAIL; + } + + return retval; +} + +CHIP_ERROR ChipLinuxStorageIni::GetUIntValue(const char * key, uint32_t & val) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + std::map section; + + retval = GetDefaultSection(section); + + if (retval == CHIP_NO_ERROR) + { + auto it = section.find(key); + + if (it != section.end()) + { + if (!inipp::extract(section[key], val)) + { + retval = CHIP_ERROR_INVALID_ARGUMENT; + } + } + else + { + retval = CHIP_ERROR_KEY_NOT_FOUND; + } + } + + return retval; +} + +CHIP_ERROR ChipLinuxStorageIni::GetUInt64Value(const char * key, uint64_t & val) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + std::map section; + + retval = GetDefaultSection(section); + + if (retval == CHIP_NO_ERROR) + { + auto it = section.find(key); + + if (it != section.end()) + { + if (!inipp::extract(section[key], val)) + { + retval = CHIP_ERROR_INVALID_ARGUMENT; + } + } + else + { + retval = CHIP_ERROR_KEY_NOT_FOUND; + } + } + + return retval; +} + +CHIP_ERROR ChipLinuxStorageIni::GetStringValue(const char * key, char * buf, size_t bufSize, size_t & outLen) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + std::map section; + + retval = GetDefaultSection(section); + + if (retval == CHIP_NO_ERROR) + { + auto it = section.find(key); + + if (it != section.end()) + { + std::string value; + if (inipp::extract(section[key], value)) + { + size_t len = value.size(); + + if (len > bufSize - 1) + { + retval = CHIP_ERROR_BUFFER_TOO_SMALL; + } + else + { + outLen = value.copy(buf, len); + buf[outLen] = '\0'; + } + } + else + { + retval = CHIP_ERROR_INVALID_ARGUMENT; + } + } + else + { + retval = CHIP_ERROR_KEY_NOT_FOUND; + } + } + + return retval; +} + +CHIP_ERROR ChipLinuxStorageIni::GetBinaryBlobDataAndLengths(const char * key, char *& encodedData, size_t & encodedDataLen, + size_t & decodedDataLen) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + size_t encodedDataPaddingLen = 0; + std::map section; + retval = GetDefaultSection(section); + + if (retval == CHIP_NO_ERROR) + { + auto it = section.find(key); + + if (it != section.end()) + { + std::string value; + + // Compute the expectedDecodedLen + if (inipp::extract(section[key], value)) + { + size_t len = value.size(); + + encodedData = (char *) malloc(len + 1); + encodedDataLen = value.copy(encodedData, len); + encodedData[encodedDataLen] = '\0'; + + // Check if encoded data was padded. Only "=" or "==" padding combinations are allowed. + if ((encodedDataLen > 0) && (encodedData[encodedDataLen - 1] == '=')) + { + encodedDataPaddingLen++; + if ((encodedDataLen > 1) && (encodedData[encodedDataLen - 2] == '=')) + encodedDataPaddingLen++; + } + + decodedDataLen = ((encodedDataLen - encodedDataPaddingLen) * 3) / 4; + } + else + { + retval = CHIP_ERROR_INVALID_ARGUMENT; + } + } + else + { + retval = CHIP_ERROR_KEY_NOT_FOUND; + } + } + + return retval; +} + +CHIP_ERROR ChipLinuxStorageIni::GetBinaryBlobValue(const char * key, uint8_t * decodedData, size_t bufSize, size_t & decodedDataLen) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + char * encodedData = NULL; + size_t encodedDataLen; + size_t expectedDecodedLen = 0; + + retval = GetBinaryBlobDataAndLengths(key, encodedData, encodedDataLen, expectedDecodedLen); + + // Check the size + if (retval == CHIP_NO_ERROR) + { + if (expectedDecodedLen > bufSize) + { + retval = CHIP_ERROR_BUFFER_TOO_SMALL; + } + } + + // Decode it + if (retval == CHIP_NO_ERROR) + { + decodedDataLen = Base64Decode(encodedData, encodedDataLen, (uint8_t *) decodedData); + if (decodedDataLen == UINT16_MAX || decodedDataLen > expectedDecodedLen) + { + retval = CHIP_ERROR_NOT_IMPLEMENTED; + } + + if (encodedData) + { + free(encodedData); + } + } + + return retval; +} + +bool ChipLinuxStorageIni::HasValue(const char * key) +{ + std::map section; + + if (GetDefaultSection(section) != CHIP_NO_ERROR) + return false; + + auto it = section.find(key); + + if (it != section.end()) + { + return true; + } + else + { + return false; + } +} + +CHIP_ERROR ChipLinuxStorageIni::AddEntry(const char * key, const char * value) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + if ((key != NULL) && (value != NULL)) + { + std::map & section = mConfigStore.sections["DEFAULT"]; + section[key] = std::string(value); + } + else + { + ChipLogError(DeviceLayer, "Invalid input argument, failed to add entry"); + retval = CHIP_ERROR_INVALID_ARGUMENT; + } + + return retval; +} + +CHIP_ERROR ChipLinuxStorageIni::RemoveEntry(const char * key) +{ + CHIP_ERROR retval = CHIP_NO_ERROR; + + std::map & section = mConfigStore.sections["DEFAULT"]; + + auto it = section.find(key); + + if (it != section.end()) + { + section.erase(it); + } + else + { + retval = CHIP_ERROR_KEY_NOT_FOUND; + } + + return retval; +} + +CHIP_ERROR ChipLinuxStorageIni::RemoveAll() +{ + mConfigStore.clear(); + + return CHIP_NO_ERROR; +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/Linux/CHIPLinuxStorageIni.h b/src/platform/Linux/CHIPLinuxStorageIni.h new file mode 100644 index 00000000000000..0ea616a9684464 --- /dev/null +++ b/src/platform/Linux/CHIPLinuxStorageIni.h @@ -0,0 +1,63 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Provides an implementation of the Configuration key-value store interface + * using IniPP. + * + */ + +#ifndef CHIP_LINUX_STORAGE_INI_H +#define CHIP_LINUX_STORAGE_INI_H + +#include +#include + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +class ChipLinuxStorageIni +{ +public: + CHIP_ERROR Init(void); + CHIP_ERROR AddConfig(const std::string & configFile); + CHIP_ERROR CommitConfig(const std::string & configFile); + CHIP_ERROR GetUIntValue(const char * key, uint32_t & val); + CHIP_ERROR GetUInt64Value(const char * key, uint64_t & val); + CHIP_ERROR GetStringValue(const char * key, char * buf, size_t bufSize, size_t & outLen); + CHIP_ERROR GetBinaryBlobValue(const char * key, uint8_t * decodedData, size_t bufSize, size_t & decodedDataLen); + bool HasValue(const char * key); + +protected: + CHIP_ERROR AddEntry(const char * key, const char * value); + CHIP_ERROR RemoveEntry(const char * key); + CHIP_ERROR RemoveAll(void); + +private: + CHIP_ERROR GetDefaultSection(std::map & section); + CHIP_ERROR GetBinaryBlobDataAndLengths(const char * key, char *& encodedData, size_t & encodedDataLen, size_t & decodedDataLen); + inipp::Ini mConfigStore; +}; + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif // CHIP_LINUX_STORAGE_INI_H diff --git a/src/platform/Linux/CHIPPlatformConfig.h b/src/platform/Linux/CHIPPlatformConfig.h index 9addba9c240c79..aee85f9510fae7 100644 --- a/src/platform/Linux/CHIPPlatformConfig.h +++ b/src/platform/Linux/CHIPPlatformConfig.h @@ -33,9 +33,11 @@ #define CHIP_CONFIG_ENABLE_SERVICE_DIRECTORY 0 -#define CHIP_CONFIG_PERSISTED_STORAGE_KEY_TYPE uint16_t -#define CHIP_CONFIG_PERSISTED_STORAGE_ENC_MSG_CNTR_ID 1 -#define CHIP_CONFIG_PERSISTED_STORAGE_MAX_KEY_LENGTH 2 +// TODO:(#756) Add FabricState support +#define CHIP_CONFIG_ENABLE_FABRIC_STATE 0 + +using CHIP_CONFIG_PERSISTED_STORAGE_KEY_TYPE = const char *; +#define CHIP_CONFIG_PERSISTED_STORAGE_MAX_KEY_LENGTH 16 #define CHIP_CONFIG_TIME_ENABLE_CLIENT 1 #define CHIP_CONFIG_TIME_ENABLE_SERVER 0 diff --git a/src/platform/Linux/ConfigurationManagerImpl.cpp b/src/platform/Linux/ConfigurationManagerImpl.cpp index fdbd1a8b3c83c9..afc87131a47aea 100644 --- a/src/platform/Linux/ConfigurationManagerImpl.cpp +++ b/src/platform/Linux/ConfigurationManagerImpl.cpp @@ -45,6 +45,15 @@ ConfigurationManagerImpl ConfigurationManagerImpl::sInstance; CHIP_ERROR ConfigurationManagerImpl::_Init() { CHIP_ERROR err; + bool failSafeArmed; + + // Force initialization of NVS namespaces if they doesn't already exist. + err = EnsureNamespace(kConfigNamespace_ChipFactory); + SuccessOrExit(err); + err = EnsureNamespace(kConfigNamespace_ChipConfig); + SuccessOrExit(err); + err = EnsureNamespace(kConfigNamespace_ChipCounters); + SuccessOrExit(err); // Initialize the generic implementation base class. err = Internal::GenericConfigurationManagerImpl::_Init(); @@ -63,38 +72,12 @@ CHIP_ERROR ConfigurationManagerImpl::_Init() return err; } -void ConnectivityManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) -{ - // Forward the event to the generic base classes as needed. -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD - GenericConnectivityManagerImpl_Thread::_OnPlatformEvent(event); -#endif -} - CHIP_ERROR ConfigurationManagerImpl::_GetPrimaryWiFiMACAddress(uint8_t * buf) { // TODO(#739): add WiFi support return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; } -CHIP_ERROR -ConfigurationManagerImpl::_GetDeviceDescriptor(::chip::Profiles::DeviceDescription::ChipDeviceDescriptor & deviceDesc) -{ - CHIP_ERROR err; - - // Call the generic version of _GetDeviceDescriptor() supplied by the base class. - err = Internal::GenericConfigurationManagerImpl::_GetDeviceDescriptor(deviceDesc); - SuccessOrExit(err); - -exit: - return err; -} - -::chip::Profiles::Security::AppKeys::GroupKeyStoreBase * ConfigurationManagerImpl::_GetGroupKeyStore() -{ - return &gGroupKeyStore; -} - bool ConfigurationManagerImpl::_CanFactoryReset() { // TODO(#742): query the application to determine if factory reset is allowed. @@ -124,6 +107,7 @@ CHIP_ERROR ConfigurationManagerImpl::_WritePersistedStorageValue(::chip::Platfor return WriteConfigValue(configKey, value); } +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION CHIP_ERROR ConfigurationManagerImpl::GetWiFiStationSecurityType(Profiles::NetworkProvisioning::WiFiSecurityType & secType) { CHIP_ERROR err; @@ -161,6 +145,7 @@ CHIP_ERROR ConfigurationManagerImpl::UpdateWiFiStationSecurityType(Profiles::Net exit: return err; } +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION void ConfigurationManagerImpl::DoFactoryReset(intptr_t arg) { diff --git a/src/platform/Linux/ConfigurationManagerImpl.h b/src/platform/Linux/ConfigurationManagerImpl.h index 652f148aa64aaa..88376d8a7d2934 100644 --- a/src/platform/Linux/ConfigurationManagerImpl.h +++ b/src/platform/Linux/ConfigurationManagerImpl.h @@ -92,11 +92,6 @@ inline ConfigurationManagerImpl & ConfigurationMgrImpl(void) return ConfigurationManagerImpl::sInstance; } -inline CHIP_ERROR ConfigurationManagerImpl::_GetPrimaryWiFiMACAddress(uint8_t * buf) -{ - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -} - } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/Linux/Logging.cpp b/src/platform/Linux/Logging.cpp index e419b0d9c9797f..0befbfde9fe48e 100644 --- a/src/platform/Linux/Logging.cpp +++ b/src/platform/Linux/Logging.cpp @@ -55,13 +55,10 @@ namespace chip { namespace Logging { /** - * CHIP log output function. + * CHIP log output functions. */ -void Log(uint8_t module, uint8_t category, const char * msg, ...) +void LogV(uint8_t module, uint8_t category, const char * msg, va_list v) { - va_list v; - va_start(v, msg); - if (IsCategoryEnabled(category)) { vsyslog(LOG_INFO, msg, v); @@ -69,8 +66,6 @@ void Log(uint8_t module, uint8_t category, const char * msg, ...) // Let the application know that a log message has been emitted. DeviceLayer::OnLogOutput(); } - - va_end(v); } } // namespace Logging diff --git a/src/platform/Linux/PosixConfig.cpp b/src/platform/Linux/PosixConfig.cpp index 5f2bdc16dc5776..6b8475f6584829 100644 --- a/src/platform/Linux/PosixConfig.cpp +++ b/src/platform/Linux/PosixConfig.cpp @@ -20,12 +20,15 @@ /** * @file - * Utilities for interacting with the the ESP32 "NVS" key-value store. + * Utilities for interacting with multiple file partitions and maps + * key-value config calls to the correct partition. */ #include +#include #include +#include #include #include @@ -33,6 +36,59 @@ namespace chip { namespace DeviceLayer { namespace Internal { +static ChipLinuxStorage gChipLinuxFactoryStorage; +static ChipLinuxStorage gChipLinuxConfigStorage; +static ChipLinuxStorage gChipLinuxCountersStorage; + +// *** CAUTION ***: Changing the names or namespaces of these values will *break* existing devices. + +// NVS namespaces used to store device configuration information. +const char PosixConfig::kConfigNamespace_ChipFactory[] = "chip-factory"; +const char PosixConfig::kConfigNamespace_ChipConfig[] = "chip-config"; +const char PosixConfig::kConfigNamespace_ChipCounters[] = "chip-counters"; + +// Keys stored in the Chip-factory namespace +const PosixConfig::Key PosixConfig::kConfigKey_SerialNum = { kConfigNamespace_ChipFactory, "serial-num" }; +const PosixConfig::Key PosixConfig::kConfigKey_MfrDeviceId = { kConfigNamespace_ChipFactory, "device-id" }; +const PosixConfig::Key PosixConfig::kConfigKey_MfrDeviceCert = { kConfigNamespace_ChipFactory, "device-cert" }; +const PosixConfig::Key PosixConfig::kConfigKey_MfrDeviceICACerts = { kConfigNamespace_ChipFactory, "device-ca-certs" }; +const PosixConfig::Key PosixConfig::kConfigKey_MfrDevicePrivateKey = { kConfigNamespace_ChipFactory, "device-key" }; +const PosixConfig::Key PosixConfig::kConfigKey_ProductRevision = { kConfigNamespace_ChipFactory, "product-rev" }; +const PosixConfig::Key PosixConfig::kConfigKey_ManufacturingDate = { kConfigNamespace_ChipFactory, "mfg-date" }; +const PosixConfig::Key PosixConfig::kConfigKey_PairingCode = { kConfigNamespace_ChipFactory, "pairing-code" }; + +// Keys stored in the Chip-config namespace +const PosixConfig::Key PosixConfig::kConfigKey_FabricId = { kConfigNamespace_ChipConfig, "fabric-id" }; +const PosixConfig::Key PosixConfig::kConfigKey_ServiceConfig = { kConfigNamespace_ChipConfig, "service-config" }; +const PosixConfig::Key PosixConfig::kConfigKey_PairedAccountId = { kConfigNamespace_ChipConfig, "account-id" }; +const PosixConfig::Key PosixConfig::kConfigKey_ServiceId = { kConfigNamespace_ChipConfig, "service-id" }; +const PosixConfig::Key PosixConfig::kConfigKey_FabricSecret = { kConfigNamespace_ChipConfig, "fabric-secret" }; +const PosixConfig::Key PosixConfig::kConfigKey_GroupKeyIndex = { kConfigNamespace_ChipConfig, "group-key-index" }; +const PosixConfig::Key PosixConfig::kConfigKey_LastUsedEpochKeyId = { kConfigNamespace_ChipConfig, "last-ek-id" }; +const PosixConfig::Key PosixConfig::kConfigKey_FailSafeArmed = { kConfigNamespace_ChipConfig, "fail-safe-armed" }; +const PosixConfig::Key PosixConfig::kConfigKey_WiFiStationSecType = { kConfigNamespace_ChipConfig, "sta-sec-type" }; +const PosixConfig::Key PosixConfig::kConfigKey_OperationalDeviceId = { kConfigNamespace_ChipConfig, "op-device-id" }; +const PosixConfig::Key PosixConfig::kConfigKey_OperationalDeviceCert = { kConfigNamespace_ChipConfig, "op-device-cert" }; +const PosixConfig::Key PosixConfig::kConfigKey_OperationalDeviceICACerts = { kConfigNamespace_ChipConfig, "op-device-ca-certs" }; +const PosixConfig::Key PosixConfig::kConfigKey_OperationalDevicePrivateKey = { kConfigNamespace_ChipConfig, "op-device-key" }; + +// Prefix used for NVS keys that contain Chip group encryption keys. +const char PosixConfig::kGroupKeyNamePrefix[] = "gk-"; + +ChipLinuxStorage * PosixConfig::GetStorageForNamespace(Key key) +{ + if (strcmp(key.Namespace, kConfigNamespace_ChipFactory) == 0) + return &gChipLinuxFactoryStorage; + + if (strcmp(key.Namespace, kConfigNamespace_ChipConfig) == 0) + return &gChipLinuxConfigStorage; + + if (strcmp(key.Namespace, kConfigNamespace_ChipCounters) == 0) + return &gChipLinuxCountersStorage; + + return NULL; +} + CHIP_ERROR PosixConfig::Init() { CHIP_ERROR err = CHIP_NO_ERROR; @@ -41,98 +97,403 @@ CHIP_ERROR PosixConfig::Init() CHIP_ERROR PosixConfig::ReadConfigValue(Key key, bool & val) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err; + ChipLinuxStorage * storage; + uint32_t intVal; + + storage = GetStorageForNamespace(key); + VerifyOrExit(storage != NULL, err = CHIP_ERROR_PERSISTED_STORAGE_FAIL); + + err = storage->ReadValue(key.Name, intVal); + if (err == CHIP_ERROR_KEY_NOT_FOUND) + { + err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; + } + SuccessOrExit(err); + + val = (intVal != 0); + +exit: + return err; } CHIP_ERROR PosixConfig::ReadConfigValue(Key key, uint32_t & val) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err; + ChipLinuxStorage * storage; + + storage = GetStorageForNamespace(key); + VerifyOrExit(storage != NULL, err = CHIP_ERROR_PERSISTED_STORAGE_FAIL); + + err = storage->ReadValue(key.Name, val); + if (err == CHIP_ERROR_KEY_NOT_FOUND) + { + err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; + } + SuccessOrExit(err); + +exit: + return err; } CHIP_ERROR PosixConfig::ReadConfigValue(Key key, uint64_t & val) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err; + ChipLinuxStorage * storage; + + storage = GetStorageForNamespace(key); + VerifyOrExit(storage != NULL, err = CHIP_ERROR_PERSISTED_STORAGE_FAIL); + + // Special case the MfrDeviceId value, optionally allowing it to be read as a blob containing + // a 64-bit big-endian integer, instead of a u64 value. + if (key == kConfigKey_MfrDeviceId) + { + uint8_t deviceIdBytes[sizeof(uint64_t)]; + size_t deviceIdLen = sizeof(deviceIdBytes); + size_t deviceIdOutLen; + err = storage->ReadValueBin(key.Name, deviceIdBytes, deviceIdLen, deviceIdOutLen); + if (err == CHIP_NO_ERROR) + { + VerifyOrExit(deviceIdOutLen == sizeof(deviceIdBytes), err = CHIP_ERROR_INCORRECT_STATE); + val = Encoding::BigEndian::Get64(deviceIdBytes); + ExitNow(); + } + } + + err = storage->ReadValue(key.Name, val); + if (err == CHIP_ERROR_KEY_NOT_FOUND) + { + err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; + } + SuccessOrExit(err); + +exit: + return err; } CHIP_ERROR PosixConfig::ReadConfigValueStr(Key key, char * buf, size_t bufSize, size_t & outLen) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err; + ChipLinuxStorage * storage; + + storage = GetStorageForNamespace(key); + VerifyOrExit(storage != NULL, err = CHIP_ERROR_PERSISTED_STORAGE_FAIL); + + err = storage->ReadValueStr(key.Name, buf, bufSize, outLen); + if (err == CHIP_ERROR_KEY_NOT_FOUND) + { + outLen = 0; + err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; + } + else if (err == CHIP_ERROR_BUFFER_TOO_SMALL) + { + err = (buf == NULL) ? CHIP_NO_ERROR : CHIP_ERROR_BUFFER_TOO_SMALL; + } + SuccessOrExit(err); + +exit: + return err; } CHIP_ERROR PosixConfig::ReadConfigValueBin(Key key, uint8_t * buf, size_t bufSize, size_t & outLen) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err; + ChipLinuxStorage * storage; + + storage = GetStorageForNamespace(key); + VerifyOrExit(storage != NULL, err = CHIP_ERROR_PERSISTED_STORAGE_FAIL); + + err = storage->ReadValueBin(key.Name, buf, bufSize, outLen); + if (err == CHIP_ERROR_KEY_NOT_FOUND) + { + outLen = 0; + err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; + } + else if (err == CHIP_ERROR_BUFFER_TOO_SMALL) + { + err = (buf == NULL) ? CHIP_NO_ERROR : CHIP_ERROR_BUFFER_TOO_SMALL; + } + SuccessOrExit(err); + +exit: + return err; } CHIP_ERROR PosixConfig::WriteConfigValue(Key key, bool val) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err; + ChipLinuxStorage * storage; + + storage = GetStorageForNamespace(key); + VerifyOrExit(storage != NULL, err = CHIP_ERROR_PERSISTED_STORAGE_FAIL); + + err = storage->WriteValue(key.Name, val ? true : false); + SuccessOrExit(err); + + // Commit the value to the persistent store. + err = storage->Commit(); + SuccessOrExit(err); + + ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %s", key.Namespace, key.Name, val ? "true" : "false"); + +exit: + return err; } CHIP_ERROR PosixConfig::WriteConfigValue(Key key, uint32_t val) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err; + ChipLinuxStorage * storage; + + storage = GetStorageForNamespace(key); + VerifyOrExit(storage != NULL, err = CHIP_ERROR_PERSISTED_STORAGE_FAIL); + + err = storage->WriteValue(key.Name, val); + SuccessOrExit(err); + + // Commit the value to the persistent store. + err = storage->Commit(); + SuccessOrExit(err); + + ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %" PRIu32 " (0x%" PRIX32 ")", key.Namespace, key.Name, val, val); + +exit: + return err; } CHIP_ERROR PosixConfig::WriteConfigValue(Key key, uint64_t val) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err; + ChipLinuxStorage * storage; + + storage = GetStorageForNamespace(key); + VerifyOrExit(storage != NULL, err = CHIP_ERROR_PERSISTED_STORAGE_FAIL); + + err = storage->WriteValue(key.Name, val); + SuccessOrExit(err); + + // Commit the value to the persistent store. + err = storage->Commit(); + SuccessOrExit(err); + + ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %" PRIu64 " (0x%" PRIX64 ")", key.Namespace, key.Name, val, val); + +exit: + return err; } CHIP_ERROR PosixConfig::WriteConfigValueStr(Key key, const char * str) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err; + ChipLinuxStorage * storage; + + if (str != NULL) + { + storage = GetStorageForNamespace(key); + VerifyOrExit(storage != NULL, err = CHIP_ERROR_PERSISTED_STORAGE_FAIL); + + err = storage->WriteValueStr(key.Name, str); + SuccessOrExit(err); + + // Commit the value to the persistent store. + err = storage->Commit(); + SuccessOrExit(err); + + ChipLogProgress(DeviceLayer, "NVS set: %s/%s = \"%s\"", key.Namespace, key.Name, str); + } + + else + { + err = ClearConfigValue(key); + SuccessOrExit(err); + } + +exit: + return err; } CHIP_ERROR PosixConfig::WriteConfigValueStr(Key key, const char * str, size_t strLen) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err; + char * strCopy = NULL; + + if (str != NULL) + { + strCopy = strndup(str, strLen); + VerifyOrExit(strCopy != NULL, err = CHIP_ERROR_NO_MEMORY); + } + + err = PosixConfig::WriteConfigValueStr(key, strCopy); + +exit: + if (strCopy != NULL) + { + free(strCopy); + } + return err; } CHIP_ERROR PosixConfig::WriteConfigValueBin(Key key, const uint8_t * data, size_t dataLen) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err; + ChipLinuxStorage * storage; + + if (data != NULL) + { + storage = GetStorageForNamespace(key); + VerifyOrExit(storage != NULL, err = CHIP_ERROR_PERSISTED_STORAGE_FAIL); + + err = storage->WriteValueBin(key.Name, data, dataLen); + SuccessOrExit(err); + + // Commit the value to the persistent store. + err = storage->Commit(); + SuccessOrExit(err); + + ChipLogProgress(DeviceLayer, "NVS set: %s/%s = (blob length %" PRId32 ")", key.Namespace, key.Name, dataLen); + } + else + { + err = ClearConfigValue(key); + SuccessOrExit(err); + } + +exit: + return err; } CHIP_ERROR PosixConfig::ClearConfigValue(Key key) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err; + ChipLinuxStorage * storage; + + storage = GetStorageForNamespace(key); + VerifyOrExit(storage != NULL, err = CHIP_ERROR_PERSISTED_STORAGE_FAIL); + + err = storage->ClearValue(key.Name); + if (err == CHIP_ERROR_KEY_NOT_FOUND) + { + ExitNow(err = CHIP_NO_ERROR); + } + SuccessOrExit(err); + + // Commit the value to the persistent store. + err = storage->Commit(); + SuccessOrExit(err); + + ChipLogProgress(DeviceLayer, "NVS erase: %s/%s", key.Namespace, key.Name); + +exit: + return err; } bool PosixConfig::ConfigValueExists(Key key) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + ChipLinuxStorage * storage; + + storage = GetStorageForNamespace(key); + if (storage == NULL) + return false; + + return storage->HasValue(key.Name); } CHIP_ERROR PosixConfig::EnsureNamespace(const char * ns) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err = CHIP_NO_ERROR; + ChipLinuxStorage * storage = NULL; + + if (strcmp(ns, kConfigNamespace_ChipFactory) == 0) + { + storage = &gChipLinuxFactoryStorage; + err = storage->Init(CHIP_DEFAULT_FACTORY_PATH); + } + else if (strcmp(ns, kConfigNamespace_ChipConfig) == 0) + { + storage = &gChipLinuxConfigStorage; + err = storage->Init(CHIP_DEFAULT_CONFIG_PATH); + } + else if (strcmp(ns, kConfigNamespace_ChipCounters) == 0) + { + storage = &gChipLinuxCountersStorage; + err = storage->Init(CHIP_DEFAULT_DATA_PATH); + } + + SuccessOrExit(err); + +exit: + return err; } CHIP_ERROR PosixConfig::ClearNamespace(const char * ns) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err = CHIP_NO_ERROR; + ChipLinuxStorage * storage = NULL; + + if (strcmp(ns, kConfigNamespace_ChipConfig) == 0) + { + storage = &gChipLinuxConfigStorage; + } + else if (strcmp(ns, kConfigNamespace_ChipCounters) == 0) + { + storage = &gChipLinuxCountersStorage; + } + + VerifyOrExit(storage != NULL, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); + + err = storage->ClearAll(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Storage ClearAll failed: %s", ErrorStr(err)); + } + SuccessOrExit(err); + + err = storage->Commit(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Storage Commit failed: %s", ErrorStr(err)); + } + +exit: + return err; } CHIP_ERROR PosixConfig::FactoryResetConfig(void) { - // TODO(#738) - return CHIP_ERROR_NOT_IMPLEMENTED; + CHIP_ERROR err = CHIP_NO_ERROR; + ChipLinuxStorage * storage; + + ChipLogProgress(DeviceLayer, "Performing factory reset"); + + storage = &gChipLinuxConfigStorage; + if (storage == NULL) + { + ChipLogError(DeviceLayer, "Storage get failed"); + err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; + } + SuccessOrExit(err); + + err = storage->ClearAll(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Storage ClearAll failed: %s", ErrorStr(err)); + } + SuccessOrExit(err); + + err = storage->Commit(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Storage Commit failed: %s", ErrorStr(err)); + } + +exit: + return err; +} + +void PosixConfig::RunConfigUnitTest() +{ + // Run common unit test. + ::chip::DeviceLayer::Internal::RunConfigUnitTest(); } } // namespace Internal diff --git a/src/platform/Linux/PosixConfig.h b/src/platform/Linux/PosixConfig.h index 2f9efc753cd559..320076d3880547 100644 --- a/src/platform/Linux/PosixConfig.h +++ b/src/platform/Linux/PosixConfig.h @@ -33,6 +33,8 @@ namespace chip { namespace DeviceLayer { namespace Internal { +class ChipLinuxStorage; + /** * Provides functions and definitions for accessing device configuration information on the Posix. * @@ -44,6 +46,39 @@ class PosixConfig public: struct Key; + // Maximum length of an NVS key name. + static constexpr size_t kMaxConfigKeyNameLength = 15; + + // NVS namespaces used to store device configuration information. + static const char kConfigNamespace_ChipFactory[]; + static const char kConfigNamespace_ChipConfig[]; + static const char kConfigNamespace_ChipCounters[]; + + // Key definitions for well-known keys. + static const Key kConfigKey_SerialNum; + static const Key kConfigKey_MfrDeviceId; + static const Key kConfigKey_MfrDeviceCert; + static const Key kConfigKey_MfrDeviceICACerts; + static const Key kConfigKey_MfrDevicePrivateKey; + static const Key kConfigKey_ProductRevision; + static const Key kConfigKey_ManufacturingDate; + static const Key kConfigKey_PairingCode; + static const Key kConfigKey_FabricId; + static const Key kConfigKey_ServiceConfig; + static const Key kConfigKey_PairedAccountId; + static const Key kConfigKey_ServiceId; + static const Key kConfigKey_FabricSecret; + static const Key kConfigKey_GroupKeyIndex; + static const Key kConfigKey_LastUsedEpochKeyId; + static const Key kConfigKey_FailSafeArmed; + static const Key kConfigKey_WiFiStationSecType; + static const Key kConfigKey_OperationalDeviceId; + static const Key kConfigKey_OperationalDeviceCert; + static const Key kConfigKey_OperationalDeviceICACerts; + static const Key kConfigKey_OperationalDevicePrivateKey; + + static const char kGroupKeyNamePrefix[]; + static CHIP_ERROR Init(void); // Config value accessors. @@ -62,10 +97,15 @@ class PosixConfig static bool ConfigValueExists(Key key); static CHIP_ERROR FactoryResetConfig(void); + static void RunConfigUnitTest(void); + protected: // NVS Namespace helper functions. static CHIP_ERROR EnsureNamespace(const char * ns); static CHIP_ERROR ClearNamespace(const char * ns); + +private: + static ChipLinuxStorage * GetStorageForNamespace(Key key); }; struct PosixConfig::Key diff --git a/src/platform/Makefile.am b/src/platform/Makefile.am index 8f3f1c5b18b35e..58cc529d1b51bf 100644 --- a/src/platform/Makefile.am +++ b/src/platform/Makefile.am @@ -48,6 +48,8 @@ noinst_HEADERS = \ @top_srcdir@/src/include/platform/CHIPDeviceLayer.h \ @top_srcdir@/src/include/platform/internal/BLEManager.h \ @top_srcdir@/src/include/platform/internal/EventLogging.h \ + @top_srcdir@/src/include/platform/internal/GenericConfigurationManagerImpl.h \ + @top_srcdir@/src/include/platform/internal/GenericConfigurationManagerImpl.ipp \ @top_srcdir@/src/include/platform/internal/GenericPlatformManagerImpl.h \ @top_srcdir@/src/include/platform/internal/GenericPlatformManagerImpl.ipp \ @top_srcdir@/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.h \ @@ -87,6 +89,7 @@ noinst_HEADERS = \ @top_srcdir@/src/platform/ESP32/ESP32Config.h \ @top_srcdir@/src/platform/Linux/CHIPDevicePlatformConfig.h \ @top_srcdir@/src/platform/Linux/CHIPDevicePlatformEvent.h \ + @top_srcdir@/src/platform/Linux/ConfigurationManagerImpl.h \ @top_srcdir@/src/platform/Linux/PlatformManagerImpl.h \ $(NULL) @@ -113,15 +116,28 @@ libDeviceLayer_a_SOURCES = \ Globals.cpp \ $(NULL) +if CHIP_ENABLE_OPENTHREAD +libDeviceLayer_a_SOURCES += \ + OpenThread/OpenThreadUtils.cpp \ + $(NULL) +endif + if CHIP_DEVICE_LAYER_TARGET_LINUX SUBDIRS += tests +libDeviceLayer_a_CPPFLAGS += \ + $(INIPP_CPPFLAGS) \ + $(NULL) + libDeviceLayer_a_SOURCES += \ Linux/BLEManagerImpl.cpp \ + Linux/ConfigurationManagerImpl.cpp \ Linux/ConnectivityManagerImpl.cpp \ Linux/Logging.cpp \ Linux/PosixConfig.cpp \ + Linux/CHIPLinuxStorage.cpp \ + Linux/CHIPLinuxStorageIni.cpp \ Linux/PlatformManagerImpl.cpp \ Linux/SystemTimeSupport.cpp \ $(NULL) @@ -140,6 +156,12 @@ libDeviceLayer_a_SOURCES += \ FreeRTOS/SystemTimeSupport.cpp \ $(NULL) +if CHIP_ENABLE_OPENTHREAD +libDeviceLayer_a_SOURCES += \ + nRF5/ThreadStackManagerImpl.cpp \ + $(NULL) +endif + endif # CHIP_DEVICE_LAYER_TARGET_NRF5 if CHIP_DEVICE_LAYER_TARGET_EFR32 diff --git a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h new file mode 100644 index 00000000000000..5389c7313cef66 --- /dev/null +++ b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h @@ -0,0 +1,130 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Provides a generic implementation of ThreadStackManager features + * for use on platforms that use OpenThread. + */ + +#ifndef GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_H +#define GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_H + +#include + +namespace chip { +namespace DeviceLayer { + +class ThreadStackManagerImpl; + +namespace Internal { + +class DeviceNetworkInfo; + +/** + * Provides a generic implementation of ThreadStackManager features that works in conjunction + * with OpenThread. + * + * This class contains implementations of select features from the ThreadStackManager abstract + * interface that are suitable for use on devices that employ OpenThread. It is intended to + * be inherited, directly or indirectly, by the ThreadStackManagerImpl class, which also appears + * as the template's ImplClass parameter. + * + * The class is designed to be independent of the choice of host OS (e.g. RTOS or posix) and + * network stack (e.g. LwIP or other IP stack). + */ +template +class GenericThreadStackManagerImpl_OpenThread +{ +public: + // ===== Platform-specific methods directly callable by the application. + + otInstance * OTInstance() const; + static void OnOpenThreadStateChange(uint32_t flags, void * context); + +protected: + // ===== Methods that implement the ThreadStackManager abstract interface. + + void _ProcessThreadActivity(void); + bool _HaveRouteToAddress(const IPAddress & destAddr); + void _OnPlatformEvent(const ChipDeviceEvent * event); + bool _IsThreadEnabled(void); + CHIP_ERROR _SetThreadEnabled(bool val); + bool _IsThreadProvisioned(void); + bool _IsThreadAttached(void); + CHIP_ERROR _GetThreadProvision(DeviceNetworkInfo & netInfo, bool includeCredentials); + CHIP_ERROR _SetThreadProvision(const DeviceNetworkInfo & netInfo); + void _ClearThreadProvision(void); + ConnectivityManager::ThreadDeviceType _GetThreadDeviceType(void); + CHIP_ERROR _SetThreadDeviceType(ConnectivityManager::ThreadDeviceType deviceType); + void _GetThreadPollingConfig(ConnectivityManager::ThreadPollingConfig & pollingConfig); + CHIP_ERROR _SetThreadPollingConfig(const ConnectivityManager::ThreadPollingConfig & pollingConfig); + bool _HaveMeshConnectivity(void); + void _OnMessageLayerActivityChanged(bool messageLayerIsActive); + CHIP_ERROR _GetAndLogThreadStatsCounters(void); + CHIP_ERROR _GetAndLogThreadTopologyMinimal(void); + CHIP_ERROR _GetAndLogThreadTopologyFull(void); + CHIP_ERROR _GetPrimary802154MACAddress(uint8_t * buf); + void _OnWoBLEAdvertisingStart(void); + void _OnWoBLEAdvertisingStop(void); + + // ===== Members available to the implementation subclass. + + CHIP_ERROR DoInit(otInstance * otInst); + bool IsThreadAttachedNoLock(void); + CHIP_ERROR AdjustPollingInterval(void); + +private: + // ===== Private members for use by this class only. + + otInstance * mOTInst; + ConnectivityManager::ThreadPollingConfig mPollingConfig; + + inline ImplClass * Impl() { return static_cast(this); } +}; + +// Instruct the compiler to instantiate the template only when explicitly told to do so. +extern template class GenericThreadStackManagerImpl_OpenThread; + +/** + * Returns the underlying OpenThread instance object. + */ +template +inline otInstance * GenericThreadStackManagerImpl_OpenThread::OTInstance() const +{ + return mOTInst; +} + +template +inline void GenericThreadStackManagerImpl_OpenThread::_OnWoBLEAdvertisingStart(void) +{ + // Do nothing by default. +} + +template +inline void GenericThreadStackManagerImpl_OpenThread::_OnWoBLEAdvertisingStop(void) +{ + // Do nothing by default. +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif // GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_H diff --git a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.ipp b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.ipp new file mode 100644 index 00000000000000..3af3b4271a0235 --- /dev/null +++ b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.ipp @@ -0,0 +1,816 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Contains non-inline method definitions for the + * GenericThreadStackManagerImpl_OpenThread<> template. + */ + +#ifndef GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_IPP +#define GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_IPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" void otSysProcessDrivers(otInstance * aInstance); + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +// Fully instantiate the generic implementation class in whatever compilation unit includes this file. +template class GenericThreadStackManagerImpl_OpenThread; + +/** + * Called by OpenThread to alert the ThreadStackManager of a change in the state of the Thread stack. + * + * By default, applications never need to call this method directly. However, applications that + * wish to receive OpenThread state change call-backs directly from OpenThread (e.g. by calling + * otSetStateChangedCallback() with their own callback function) can call this method to pass + * state change events to the ThreadStackManager. + */ +template +void GenericThreadStackManagerImpl_OpenThread::OnOpenThreadStateChange(uint32_t flags, void * context) +{ + ChipDeviceEvent event; + event.Type = DeviceEventType::kThreadStateChange; + event.ThreadStateChange.RoleChanged = (flags & OT_CHANGED_THREAD_ROLE) != 0; + event.ThreadStateChange.AddressChanged = (flags & (OT_CHANGED_IP6_ADDRESS_ADDED | OT_CHANGED_IP6_ADDRESS_REMOVED)) != 0; + event.ThreadStateChange.NetDataChanged = (flags & OT_CHANGED_THREAD_NETDATA) != 0; + event.ThreadStateChange.ChildNodesChanged = (flags & (OT_CHANGED_THREAD_CHILD_ADDED | OT_CHANGED_THREAD_CHILD_REMOVED)) != 0; + event.ThreadStateChange.OpenThread.Flags = flags; + PlatformMgr().PostEvent(&event); +} + +template +void GenericThreadStackManagerImpl_OpenThread::_ProcessThreadActivity(void) +{ + otTaskletsProcess(mOTInst); + otSysProcessDrivers(mOTInst); +} + +template +bool GenericThreadStackManagerImpl_OpenThread::_HaveRouteToAddress(const IPAddress & destAddr) +{ + bool res = false; + + // Lock OpenThread + Impl()->LockThreadStack(); + + // No routing of IPv4 over Thread. + VerifyOrExit(!destAddr.IsIPv4(), res = false); + + // If the device is attached to a Thread network... + if (IsThreadAttachedNoLock()) + { + // Link-local addresses are always presumed to be routable, provided the device is attached. + if (destAddr.IsIPv6LinkLocal()) + { + ExitNow(res = true); + } + + // Iterate over the routes known to the OpenThread stack looking for a route that covers the + // destination address. If found, consider the address routable. + // Ignore any routes advertised by this device. + // If the destination address is a ULA, ignore default routes. Border routers advertising + // default routes are not expected to be capable of routing CHIP fabric ULAs unless they + // advertise those routes specifically. + { + otError otErr; + otNetworkDataIterator routeIter = OT_NETWORK_DATA_ITERATOR_INIT; + otExternalRouteConfig routeConfig; + const bool destIsULA = destAddr.IsIPv6ULA(); + + while ((otErr = otNetDataGetNextRoute(Impl()->OTInstance(), &routeIter, &routeConfig)) == OT_ERROR_NONE) + { + const IPPrefix prefix = ToIPPrefix(routeConfig.mPrefix); + char addrStr[64]; + prefix.IPAddr.ToString(addrStr, sizeof(addrStr)); + if (!routeConfig.mNextHopIsThisDevice && (!destIsULA || routeConfig.mPrefix.mLength > 0) && + ToIPPrefix(routeConfig.mPrefix).MatchAddress(destAddr)) + { + ExitNow(res = true); + } + } + } + } + +exit: + + // Unlock OpenThread + Impl()->UnlockThreadStack(); + + return res; +} + +template +void GenericThreadStackManagerImpl_OpenThread::_OnPlatformEvent(const ChipDeviceEvent * event) +{ + if (event->Type == DeviceEventType::kThreadStateChange) + { + Impl()->LockThreadStack(); + +#if CHIP_DETAIL_LOGGING + + LogOpenThreadStateChange(mOTInst, event->ThreadStateChange.OpenThread.Flags); + +#endif // CHIP_DETAIL_LOGGING + + Impl()->UnlockThreadStack(); + } +} + +template +bool GenericThreadStackManagerImpl_OpenThread::_IsThreadEnabled(void) +{ + otDeviceRole curRole; + + Impl()->LockThreadStack(); + curRole = otThreadGetDeviceRole(mOTInst); + Impl()->UnlockThreadStack(); + + return (curRole != OT_DEVICE_ROLE_DISABLED); +} + +template +CHIP_ERROR GenericThreadStackManagerImpl_OpenThread::_SetThreadEnabled(bool val) +{ + otError otErr = OT_ERROR_NONE; + + Impl()->LockThreadStack(); + + bool isEnabled = (otThreadGetDeviceRole(mOTInst) != OT_DEVICE_ROLE_DISABLED); + bool isIp6Enabled = otIp6IsEnabled(mOTInst); + + if (val && !isIp6Enabled) + { + otErr = otIp6SetEnabled(mOTInst, val); + VerifyOrExit(otErr == OT_ERROR_NONE, ); + } + + if (val != isEnabled) + { + otErr = otThreadSetEnabled(mOTInst, val); + VerifyOrExit(otErr == OT_ERROR_NONE, ); + } + + if (!val && isIp6Enabled) + { + otErr = otIp6SetEnabled(mOTInst, val); + VerifyOrExit(otErr == OT_ERROR_NONE, ); + } + +exit: + Impl()->UnlockThreadStack(); + + return MapOpenThreadError(otErr); +} + +template +bool GenericThreadStackManagerImpl_OpenThread::_IsThreadProvisioned(void) +{ + bool provisioned; + + Impl()->LockThreadStack(); + provisioned = otDatasetIsCommissioned(mOTInst); + Impl()->UnlockThreadStack(); + + return provisioned; +} + +template +bool GenericThreadStackManagerImpl_OpenThread::_IsThreadAttached(void) +{ + otDeviceRole curRole; + + Impl()->LockThreadStack(); + curRole = otThreadGetDeviceRole(mOTInst); + Impl()->UnlockThreadStack(); + + return (curRole != OT_DEVICE_ROLE_DISABLED && curRole != OT_DEVICE_ROLE_DETACHED); +} + +template +ConnectivityManager::ThreadDeviceType GenericThreadStackManagerImpl_OpenThread::_GetThreadDeviceType(void) +{ + otLinkModeConfig linkMode; + ConnectivityManager::ThreadDeviceType deviceType; + + Impl()->LockThreadStack(); + + linkMode = otThreadGetLinkMode(mOTInst); + + if (linkMode.mDeviceType) + { + if (otThreadIsRouterEligible(mOTInst)) + { + deviceType = ConnectivityManager::kThreadDeviceType_Router; + } + else + { + deviceType = ConnectivityManager::kThreadDeviceType_FullEndDevice; + } + } + else + { + if (linkMode.mRxOnWhenIdle) + { + deviceType = ConnectivityManager::kThreadDeviceType_MinimalEndDevice; + } + else + { + deviceType = ConnectivityManager::kThreadDeviceType_SleepyEndDevice; + } + } + + Impl()->UnlockThreadStack(); + + return deviceType; +} + +template +CHIP_ERROR +GenericThreadStackManagerImpl_OpenThread::_SetThreadDeviceType(ConnectivityManager::ThreadDeviceType deviceType) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + otLinkModeConfig linkMode; + + VerifyOrExit(deviceType == ConnectivityManager::kThreadDeviceType_Router || + deviceType == ConnectivityManager::kThreadDeviceType_FullEndDevice || + deviceType == ConnectivityManager::kThreadDeviceType_MinimalEndDevice || + deviceType == ConnectivityManager::kThreadDeviceType_SleepyEndDevice, + err = CHIP_ERROR_INVALID_ARGUMENT); + +#if CHIP_PROGRESS_LOGGING + + { + const char * deviceTypeStr; + switch (deviceType) + { + case ConnectivityManager::kThreadDeviceType_Router: + deviceTypeStr = "ROUTER"; + break; + case ConnectivityManager::kThreadDeviceType_FullEndDevice: + deviceTypeStr = "FULL END DEVICE"; + break; + case ConnectivityManager::kThreadDeviceType_MinimalEndDevice: + deviceTypeStr = "MINIMAL END DEVICE"; + break; + case ConnectivityManager::kThreadDeviceType_SleepyEndDevice: + deviceTypeStr = "SLEEPY END DEVICE"; + break; + default: + deviceTypeStr = "(unknown)"; + break; + } + ChipLogProgress(DeviceLayer, "Setting OpenThread device type to %s", deviceTypeStr); + } + +#endif // CHIP_PROGRESS_LOGGING + + Impl()->LockThreadStack(); + + linkMode = otThreadGetLinkMode(mOTInst); + + switch (deviceType) + { + case ConnectivityManager::kThreadDeviceType_Router: + case ConnectivityManager::kThreadDeviceType_FullEndDevice: + linkMode.mDeviceType = true; + linkMode.mRxOnWhenIdle = true; + otThreadSetRouterEligible(mOTInst, deviceType == ConnectivityManager::kThreadDeviceType_Router); + break; + case ConnectivityManager::kThreadDeviceType_MinimalEndDevice: + linkMode.mDeviceType = false; + linkMode.mRxOnWhenIdle = true; + break; + case ConnectivityManager::kThreadDeviceType_SleepyEndDevice: + linkMode.mDeviceType = false; + linkMode.mRxOnWhenIdle = false; + break; + default: + break; + } + + otThreadSetLinkMode(mOTInst, linkMode); + + Impl()->UnlockThreadStack(); + +exit: + return err; +} + +template +void GenericThreadStackManagerImpl_OpenThread::_GetThreadPollingConfig( + ConnectivityManager::ThreadPollingConfig & pollingConfig) +{ + pollingConfig = mPollingConfig; +} + +template +CHIP_ERROR GenericThreadStackManagerImpl_OpenThread::_SetThreadPollingConfig( + const ConnectivityManager::ThreadPollingConfig & pollingConfig) +{ + mPollingConfig = pollingConfig; + return Impl()->AdjustPollingInterval(); +} + +template +bool GenericThreadStackManagerImpl_OpenThread::_HaveMeshConnectivity(void) +{ + bool res; + otDeviceRole curRole; + + Impl()->LockThreadStack(); + + // Get the current Thread role. + curRole = otThreadGetDeviceRole(mOTInst); + + // If Thread is disabled, or the node is detached, then the node has no mesh connectivity. + if (curRole == OT_DEVICE_ROLE_DISABLED || curRole == OT_DEVICE_ROLE_DETACHED) + { + res = false; + } + + // If the node is a child, that implies the existence of a parent node which provides connectivity + // to the mesh. + else if (curRole == OT_DEVICE_ROLE_CHILD) + { + res = true; + } + + // Otherwise, if the node is acting as a router, scan the Thread neighbor table looking for at least + // one other node that is also acting as router. + else + { + otNeighborInfoIterator neighborIter = OT_NEIGHBOR_INFO_ITERATOR_INIT; + otNeighborInfo neighborInfo; + + res = false; + + while (otThreadGetNextNeighborInfo(mOTInst, &neighborIter, &neighborInfo) == OT_ERROR_NONE) + { + if (!neighborInfo.mIsChild) + { + res = true; + break; + } + } + } + + Impl()->UnlockThreadStack(); + + return res; +} + +template +void GenericThreadStackManagerImpl_OpenThread::_OnMessageLayerActivityChanged(bool messageLayerIsActive) +{ + Impl()->AdjustPollingInterval(); +} + +template +CHIP_ERROR GenericThreadStackManagerImpl_OpenThread::_GetAndLogThreadStatsCounters(void) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + otError otErr; + const otMacCounters * macCounters; + const otIpCounters * ipCounters; + otOperationalDataset activeDataset; + otDeviceRole role; + + Impl()->LockThreadStack(); + + role = otThreadGetDeviceRole(mOTInst); + ChipLogProgress(DeviceLayer, "Thread Role: %d\n", role); + + if (otDatasetIsCommissioned(mOTInst)) + { + otErr = otDatasetGetActive(mOTInst, &activeDataset); + VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); + + if (activeDataset.mComponents.mIsChannelPresent) + { + ChipLogProgress(DeviceLayer, "Thread Channel: %d\n", activeDataset.mChannel); + } + } + + macCounters = otLinkGetCounters(mOTInst); + + ChipLogProgress(DeviceLayer, + "Rx Counters:\n" + "PHY Rx Total: %d\n" + "MAC Rx Unicast: %d\n" + "MAC Rx Broadcast: %d\n" + "MAC Rx Data: %d\n" + "MAC Rx Data Polls: %d\n" + "MAC Rx Beacons: %d\n" + "MAC Rx Beacon Reqs: %d\n" + "MAC Rx Other: %d\n" + "MAC Rx Filtered Whitelist: %d\n" + "MAC Rx Filtered DestAddr: %d\n", + macCounters->mRxTotal, macCounters->mRxUnicast, macCounters->mRxBroadcast, macCounters->mRxData, + macCounters->mRxDataPoll, macCounters->mRxBeacon, macCounters->mRxBeaconRequest, macCounters->mRxOther, + macCounters->mRxAddressFiltered, macCounters->mRxDestAddrFiltered); + + ChipLogProgress(DeviceLayer, + "Tx Counters:\n" + "PHY Tx Total: %d\n" + "MAC Tx Unicast: %d\n" + "MAC Tx Broadcast: %d\n" + "MAC Tx Data: %d\n" + "MAC Tx Data Polls: %d\n" + "MAC Tx Beacons: %d\n" + "MAC Tx Beacon Reqs: %d\n" + "MAC Tx Other: %d\n" + "MAC Tx Retry: %d\n" + "MAC Tx CCA Fail: %d\n", + macCounters->mTxTotal, macCounters->mTxUnicast, macCounters->mTxBroadcast, macCounters->mTxData, + macCounters->mTxDataPoll, macCounters->mTxBeacon, macCounters->mTxBeaconRequest, macCounters->mTxOther, + macCounters->mTxRetry, macCounters->mTxErrCca); + + ChipLogProgress(DeviceLayer, + "Failure Counters:\n" + "MAC Rx Decrypt Fail: %d\n" + "MAC Rx No Frame Fail: %d\n" + "MAC Rx Unknown Neighbor Fail: %d\n" + "MAC Rx Invalid Src Addr Fail: %d\n" + "MAC Rx FCS Fail: %d\n" + "MAC Rx Other Fail: %d\n", + macCounters->mRxErrSec, macCounters->mRxErrNoFrame, macCounters->mRxErrUnknownNeighbor, + macCounters->mRxErrInvalidSrcAddr, macCounters->mRxErrFcs, macCounters->mRxErrOther); + + ipCounters = otThreadGetIp6Counters(mOTInst); + + ChipLogProgress(DeviceLayer, + "IP Counters:\n" + "IP Tx Success: %d\n" + "IP Rx Success: %d\n" + "IP Tx Fail: %d\n" + "IP Rx Fail: %d\n", + ipCounters->mTxSuccess, ipCounters->mRxSuccess, ipCounters->mTxFailure, ipCounters->mRxFailure); + + Impl()->UnlockThreadStack(); + +exit: + return err; +} + +template +CHIP_ERROR GenericThreadStackManagerImpl_OpenThread::_GetAndLogThreadTopologyMinimal(void) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + otError otErr; + const otExtAddress * extAddress; + uint16_t rloc16; + uint16_t routerId; + uint16_t leaderRouterId; + uint32_t partitionId; + int8_t parentAverageRssi; + int8_t parentLastRssi; + int8_t instantRssi; + + Impl()->LockThreadStack(); + + rloc16 = otThreadGetRloc16(mOTInst); + + // Router ID is the top 6 bits of the RLOC + routerId = (rloc16 >> 10) & 0x3f; + + leaderRouterId = otThreadGetLeaderRouterId(mOTInst); + + otErr = otThreadGetParentAverageRssi(mOTInst, &parentAverageRssi); + VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); + + otErr = otThreadGetParentLastRssi(mOTInst, &parentLastRssi); + VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); + + partitionId = otThreadGetPartitionId(mOTInst); + + extAddress = otLinkGetExtendedAddress(mOTInst); + + instantRssi = otPlatRadioGetRssi(mOTInst); + + ChipLogProgress(DeviceLayer, + "Thread Topology:\n" + "RLOC16: %04X\n" + "Router ID: %u\n" + "Leader Router ID: %u\n" + "Parent Avg RSSI: %d\n" + "Parent Last RSSI: %d\n" + "Partition ID: %d\n" + "Extended Address: %02X%02X:%02X%02X:%02X%02X:%02X%02X\n" + "Instant RSSI: %d\n", + rloc16, routerId, leaderRouterId, parentAverageRssi, parentLastRssi, partitionId, extAddress->m8[0], + extAddress->m8[1], extAddress->m8[2], extAddress->m8[3], extAddress->m8[4], extAddress->m8[5], + extAddress->m8[6], extAddress->m8[7], instantRssi); + + Impl()->UnlockThreadStack(); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "GetAndLogThreadTopologyMinimul failed: %d", err); + } + + return err; +} + +#define TELEM_NEIGHBOR_TABLE_SIZE (64) +#define TELEM_PRINT_BUFFER_SIZE (64) + +template +CHIP_ERROR GenericThreadStackManagerImpl_OpenThread::_GetAndLogThreadTopologyFull(void) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + otError otErr; + otIp6Address * leaderAddr = NULL; + uint8_t * networkData = NULL; + uint8_t * stableNetworkData = NULL; + uint8_t networkDataLen = 0; + uint8_t stableNetworkDataLen = 0; + const otExtAddress * extAddress; + otNeighborInfo neighborInfo[TELEM_NEIGHBOR_TABLE_SIZE]; + otNeighborInfoIterator iter; + otNeighborInfoIterator iterCopy; + char printBuf[TELEM_PRINT_BUFFER_SIZE]; + uint16_t rloc16; + uint16_t routerId; + uint16_t leaderRouterId; + uint8_t leaderWeight; + uint8_t leaderLocalWeight; + uint32_t partitionId; + int8_t instantRssi; + uint8_t networkDataVersion; + uint8_t stableNetworkDataVersion; + uint16_t neighborTableSize = 0; + uint16_t childTableSize = 0; + + Impl()->LockThreadStack(); + + rloc16 = otThreadGetRloc16(mOTInst); + + // Router ID is the top 6 bits of the RLOC + routerId = (rloc16 >> 10) & 0x3f; + + leaderRouterId = otThreadGetLeaderRouterId(mOTInst); + + otErr = otThreadGetLeaderRloc(mOTInst, leaderAddr); + VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); + + leaderWeight = otThreadGetLeaderWeight(mOTInst); + + leaderLocalWeight = otThreadGetLocalLeaderWeight(mOTInst); + + otErr = otNetDataGet(mOTInst, false, networkData, &networkDataLen); + VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); + + networkDataVersion = otNetDataGetVersion(mOTInst); + + otErr = otNetDataGet(mOTInst, true, stableNetworkData, &stableNetworkDataLen); + VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); + + stableNetworkDataVersion = otNetDataGetStableVersion(mOTInst); + + extAddress = otLinkGetExtendedAddress(mOTInst); + + partitionId = otThreadGetPartitionId(mOTInst); + + instantRssi = otPlatRadioGetRssi(mOTInst); + + iter = OT_NEIGHBOR_INFO_ITERATOR_INIT; + iterCopy = OT_NEIGHBOR_INFO_ITERATOR_INIT; + neighborTableSize = 0; + childTableSize = 0; + + while (otThreadGetNextNeighborInfo(mOTInst, &iter, &neighborInfo[iter]) == OT_ERROR_NONE) + { + neighborTableSize++; + if (neighborInfo[iterCopy].mIsChild) + { + childTableSize++; + } + iterCopy = iter; + } + + ChipLogProgress(DeviceLayer, + "Thread Topology:\n" + "RLOC16: %04X\n" + "Router ID: %u\n" + "Leader Router ID: %u\n" + "Leader Address: %02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X\n" + "Leader Weight: %d\n" + "Local Leader Weight: %d\n" + "Network Data Len: %d\n" + "Network Data Version: %d\n" + "Stable Network Data Version: %d\n" + "Extended Address: %02X%02X:%02X%02X:%02X%02X:%02X%02X\n" + "Partition ID: %X\n" + "Instant RSSI: %d\n" + "Neighbor Table Length: %d\n" + "Child Table Length: %d\n", + rloc16, routerId, leaderRouterId, leaderAddr->mFields.m8[0], leaderAddr->mFields.m8[1], + leaderAddr->mFields.m8[2], leaderAddr->mFields.m8[3], leaderAddr->mFields.m8[4], leaderAddr->mFields.m8[5], + leaderAddr->mFields.m8[6], leaderAddr->mFields.m8[7], leaderAddr->mFields.m8[8], leaderAddr->mFields.m8[9], + leaderAddr->mFields.m8[10], leaderAddr->mFields.m8[11], leaderAddr->mFields.m8[12], leaderAddr->mFields.m8[13], + leaderAddr->mFields.m8[14], leaderAddr->mFields.m8[15], leaderWeight, leaderLocalWeight, networkDataLen, + networkDataVersion, stableNetworkDataVersion, extAddress->m8[0], extAddress->m8[1], extAddress->m8[2], + extAddress->m8[3], extAddress->m8[4], extAddress->m8[5], extAddress->m8[6], extAddress->m8[7], partitionId, + instantRssi, neighborTableSize, childTableSize); + + // Handle each neighbor event seperatly. + for (uint32_t i = 0; i < neighborTableSize; i++) + { + otNeighborInfo * neighbor = &neighborInfo[i]; + + if (neighbor->mIsChild) + { + otChildInfo * child = NULL; + otErr = otThreadGetChildInfoById(mOTInst, neighbor->mRloc16, child); + VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); + + snprintf(printBuf, TELEM_PRINT_BUFFER_SIZE, ", Timeout: %10lu NetworkDataVersion: %3d", child->mTimeout, + child->mNetworkDataVersion); + } + else + { + printBuf[0] = 0; + } + + ChipLogProgress(DeviceLayer, + "TopoEntry[%u]: %02X%02X:%02X%02X:%02X%02X:%02X%02X\n" + "RLOC: %04X\n" + "Age: %3d\n" + "LQI: %1d\n" + "AvgRSSI: %3d\n" + "LastRSSI: %3d\n" + "LinkFrameCounter: %10d\n" + "MleFrameCounter: %10d\n" + "RxOnWhenIdle: %c\n" + "SecureDataRequest: %c\n" + "FullFunction: %c\n" + "FullNetworkData: %c\n" + "IsChild: %c%s\n", + i, neighbor->mExtAddress.m8[0], neighbor->mExtAddress.m8[1], neighbor->mExtAddress.m8[2], + neighbor->mExtAddress.m8[3], neighbor->mExtAddress.m8[4], neighbor->mExtAddress.m8[5], + neighbor->mExtAddress.m8[6], neighbor->mExtAddress.m8[7], neighbor->mRloc16, neighbor->mAge, + neighbor->mLinkQualityIn, neighbor->mAverageRssi, neighbor->mLastRssi, neighbor->mLinkFrameCounter, + neighbor->mMleFrameCounter, neighbor->mRxOnWhenIdle ? 'Y' : 'n', neighbor->mSecureDataRequest ? 'Y' : 'n', +#if OPENTHREA_API_VERSION + neighbor->mFullThreadDevice ? 'Y' : 'n', +#else + 'n', +#endif + + neighbor->mFullNetworkData ? 'Y' : 'n', neighbor->mIsChild ? 'Y' : 'n', printBuf); + } + + Impl()->UnlockThreadStack(); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "GetAndLogThreadTopologyFull failed: %s", err); + } + return err; +} + +template +CHIP_ERROR GenericThreadStackManagerImpl_OpenThread::_GetPrimary802154MACAddress(uint8_t * buf) +{ + const otExtAddress * extendedAddr = otLinkGetExtendedAddress(mOTInst); + memcpy(buf, extendedAddr, sizeof(otExtAddress)); + return CHIP_NO_ERROR; +}; + +template +CHIP_ERROR GenericThreadStackManagerImpl_OpenThread::DoInit(otInstance * otInst) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + otError otErr = OT_ERROR_NONE; + + // Arrange for OpenThread errors to be translated to text. + RegisterOpenThreadErrorFormatter(); + + mOTInst = NULL; + mPollingConfig.Clear(); + + // If an OpenThread instance hasn't been supplied, call otInstanceInitSingle() to + // create or acquire a singleton instance of OpenThread. + if (otInst == NULL) + { + otInst = otInstanceInitSingle(); + VerifyOrExit(otInst != NULL, err = MapOpenThreadError(OT_ERROR_FAILED)); + } + + otCliUartInit(otInst); + + mOTInst = otInst; + + // Arrange for OpenThread to call the OnOpenThreadStateChange method whenever a + // state change occurs. Note that we reference the OnOpenThreadStateChange method + // on the concrete implementation class so that that class can override the default + // method implementation if it chooses to. + otErr = otSetStateChangedCallback(otInst, ImplClass::OnOpenThreadStateChange, NULL); + VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); + + // Enable use of secure data requests. + { + otLinkModeConfig linkMode = otThreadGetLinkMode(otInst); + linkMode.mSecureDataRequests = true; + otErr = otThreadSetLinkMode(otInst, linkMode); + VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); + } + + // Disable automatic assignment of Thread advertised addresses. +#if OPENTHREAD_CONFIG_ENABLE_SLAAC + otIp6SetSlaacEnabled(otInst, false); +#endif + + // If the Thread stack has been provisioned, but is not currently enabled, enable it now. + if (otThreadGetDeviceRole(mOTInst) == OT_DEVICE_ROLE_DISABLED && otDatasetIsCommissioned(otInst)) + { + // Enable the Thread IPv6 interface. + otErr = otIp6SetEnabled(otInst, true); + VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); + + otErr = otThreadSetEnabled(otInst, true); + VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); + + ChipLogProgress(DeviceLayer, "OpenThread ifconfig up and thread start"); + } + +exit: + ChipLogProgress(DeviceLayer, "OpenThread started: %s", otThreadErrorToString(otErr)); + return err; +} + +template +bool GenericThreadStackManagerImpl_OpenThread::IsThreadAttachedNoLock(void) +{ + otDeviceRole curRole = otThreadGetDeviceRole(mOTInst); + return (curRole != OT_DEVICE_ROLE_DISABLED && curRole != OT_DEVICE_ROLE_DETACHED); +} + +template +CHIP_ERROR GenericThreadStackManagerImpl_OpenThread::AdjustPollingInterval(void) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + uint32_t newPollingIntervalMS = mPollingConfig.InactivePollingIntervalMS; + + if (newPollingIntervalMS != 0) + { + Impl()->LockThreadStack(); + + uint32_t curPollingIntervalMS = otLinkGetPollPeriod(mOTInst); + + if (newPollingIntervalMS != curPollingIntervalMS) + { + otError otErr = otLinkSetPollPeriod(mOTInst, newPollingIntervalMS); + err = MapOpenThreadError(otErr); + } + + Impl()->UnlockThreadStack(); + + if (newPollingIntervalMS != curPollingIntervalMS) + { + ChipLogProgress(DeviceLayer, "OpenThread polling interval set to %" PRId32 "ms", newPollingIntervalMS); + } + } + + return err; +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif // GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_IPP diff --git a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread_LwIP.h b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread_LwIP.h new file mode 100644 index 00000000000000..86f42f63825685 --- /dev/null +++ b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread_LwIP.h @@ -0,0 +1,100 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Provides a generic implementation of ThreadStackManager features + * for use on platforms that use OpenThread with LwIP. + */ + +#ifndef GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_LWIP_H +#define GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_LWIP_H + +#include + +#include +#include + +#include + +namespace chip { +namespace DeviceLayer { + +class ThreadStackManagerImpl; + +namespace Internal { + +/** + * Provides a generic implementation of ThreadStackManager features that works in conjunction + * with OpenThread and LwIP. + * + * This template contains implementations of select features from the ThreadStackManager abstract + * interface that are suitable for use on devices that employ OpenThread and LwIP together. It is + * intended to be inherited, directly or indirectly, by the ThreadStackManagerImpl class, which + * also appears as the template's ImplClass parameter. + */ +template +class GenericThreadStackManagerImpl_OpenThread_LwIP : public GenericThreadStackManagerImpl_OpenThread +{ +public: + // ===== Platform-specific methods directly callable by the application. + + struct netif * ThreadNetIf() const; + +protected: + // ===== Methods that implement the ThreadStackManager abstract interface. + + void _OnPlatformEvent(const ChipDeviceEvent * event); + + // ===== Members available to the implementation subclass. + + CHIP_ERROR DoInit(otInstance * otInst); + void UpdateThreadInterface(bool addrChange); + +private: + // ===== Private members for use by this class only. + + struct netif * mNetIf; + bool mAddrAssigned[LWIP_IPV6_NUM_ADDRESSES]; + + static err_t DoInitThreadNetIf(struct netif * netif); +#if LWIP_VERSION_MAJOR < 2 + static err_t SendPacket(struct netif * netif, struct pbuf * pkt, struct ip6_addr * ipaddr); +#else + static err_t SendPacket(struct netif * netif, struct pbuf * pkt, const struct ip6_addr * ipaddr); +#endif + static void ReceivePacket(otMessage * pkt, void * context); + + inline ImplClass * Impl() { return static_cast(this); } +}; + +// Instruct the compiler to instantiate the template only when explicitly told to do so. +extern template class GenericThreadStackManagerImpl_OpenThread_LwIP; + +template +inline struct netif * GenericThreadStackManagerImpl_OpenThread_LwIP::ThreadNetIf() const +{ + return mNetIf; +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif // GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_LWIP_H diff --git a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread_LwIP.ipp b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread_LwIP.ipp new file mode 100644 index 00000000000000..b4a8a45448247b --- /dev/null +++ b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread_LwIP.ipp @@ -0,0 +1,419 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2018 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Contains non-inline method definitions for the + * GenericThreadStackManagerImpl_OpenThread_LwIP<> template. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +// Fully instantiate the generic implementation class in whatever compilation unit includes this file. +template class GenericThreadStackManagerImpl_OpenThread_LwIP; + +template +void GenericThreadStackManagerImpl_OpenThread_LwIP::_OnPlatformEvent(const ChipDeviceEvent * event) +{ + // Pass the event to the base class first. + GenericThreadStackManagerImpl_OpenThread::_OnPlatformEvent(event); + + if (event->Type == DeviceEventType::kThreadStateChange) + { + // If the Thread device role has changed, or if an IPv6 address has been added or + // removed in the Thread stack, update the state and configuration of the LwIP Thread interface. + if (event->ThreadStateChange.RoleChanged || event->ThreadStateChange.AddressChanged) + { + UpdateThreadInterface(event->ThreadStateChange.AddressChanged); + } + } +} + +template +CHIP_ERROR GenericThreadStackManagerImpl_OpenThread_LwIP::DoInit(otInstance * otInst) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + static struct netif sThreadNetIf; + + // Initialize member data. + memset(mAddrAssigned, 0, sizeof(mAddrAssigned)); + + // Initialize the base class. + err = GenericThreadStackManagerImpl_OpenThread::DoInit(otInst); + SuccessOrExit(err); + + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + // Initialize a LwIP netif structure for the OpenThread interface and + // add it to the list of interfaces known to LwIP. + mNetIf = netif_add(&sThreadNetIf, +#if LWIP_IPV4 + NULL, NULL, NULL, +#endif // LWIP_IPV4 + NULL, DoInitThreadNetIf, tcpip_input); + + // Start with the interface in the down state. + netif_set_link_down(mNetIf); + + // Unkock LwIP stack + UNLOCK_TCPIP_CORE(); + + VerifyOrExit(mNetIf != NULL, err = INET_ERROR_INTERFACE_INIT_FAILURE); + + // Lock OpenThread + Impl()->LockThreadStack(); + + // Arrange for OpenThread to call our ReceivePacket() method whenever an + // IPv6 packet is received. + otIp6SetReceiveCallback(Impl()->OTInstance(), ReceivePacket, NULL); + + // Disable automatic echo mode in OpenThread. + otIcmp6SetEchoMode(Impl()->OTInstance(), OT_ICMP6_ECHO_HANDLER_DISABLED); + + // Enable the receive filter for Thread control traffic. + otIp6SetReceiveFilterEnabled(Impl()->OTInstance(), true); + + // Unlock OpenThread + Impl()->UnlockThreadStack(); + +exit: + return err; +} + +template +void GenericThreadStackManagerImpl_OpenThread_LwIP::UpdateThreadInterface(bool addrChange) +{ + err_t lwipErr = ERR_OK; + bool isAttached; + bool addrAssigned[LWIP_IPV6_NUM_ADDRESSES]; + + memset(addrAssigned, 0, sizeof(addrAssigned)); + + // Lock LwIP stack first, then OpenThread. + LOCK_TCPIP_CORE(); + Impl()->LockThreadStack(); + + // Determine whether the device is attached to a Thread network. + isAttached = GenericThreadStackManagerImpl_OpenThread::IsThreadAttachedNoLock(); + + // If needed, adjust the link state of the LwIP netif to reflect the state of the OpenThread stack. + // Set ifConnectivity to indicate the change in the link state. + if (isAttached != (bool) netif_is_link_up(mNetIf)) + { + ChipLogDetail(DeviceLayer, "LwIP Thread interface %s", isAttached ? "UP" : "DOWN"); + + if (isAttached) + { + netif_set_link_up(mNetIf); + } + else + { + netif_set_link_down(mNetIf); + } + + // Post an event signaling the change in Thread interface connectivity state. + { + ChipDeviceEvent event; + event.Clear(); + event.Type = DeviceEventType::kThreadConnectivityChange; + event.ThreadConnectivityChange.Result = (isAttached) ? kConnectivity_Established : kConnectivity_Lost; + PlatformMgr().PostEvent(&event); + } + + // Presume the interface addresses are also changing. + addrChange = true; + } + + // If needed, adjust the set of addresses associated with the LwIP netif to reflect those + // known to the Thread stack. + if (addrChange) + { + // If attached to a Thread network, add addresses to the LwIP netif to match those + // configured in the Thread stack... + if (isAttached) + { + // Enumerate the list of unicast IPv6 addresses known to OpenThread... + const otNetifAddress * otAddrs = otIp6GetUnicastAddresses(Impl()->OTInstance()); + for (const otNetifAddress * otAddr = otAddrs; otAddr != NULL; otAddr = otAddr->mNext) + { + IPAddress addr = ToIPAddress(otAddr->mAddress); + + // Assign the following OpenThread addresses to LwIP's address table: + // - link-local addresses. + // - mesh-local addresses that are NOT RLOC addresses. + // - global unicast addresses. + // + // This logic purposefully leaves out CHIP fabric ULAs, as well as other non-fabric ULAs that the + // Thread stack assigns due to Thread SLAAC. + // + // Assignments of CHIP fabric ULAs to the netif address table are handled separately by the WARM module. + // + // Non-fabric ULAs are ignored entirely as they are presumed to not be of interest to CHIP-enabled + // devices, and would otherwise consume slots in the LwIP address table, potentially leading to + // starvation. + if (otAddr->mValid && !otAddr->mRloc && + (!addr.IsIPv6ULA() || IsOpenThreadMeshLocalAddress(Impl()->OTInstance(), addr))) + { + ip_addr_t lwipAddr = addr.ToLwIPAddr(); + s8_t addrIdx; + + // Add the address to the LwIP netif. If the address is a link-local, and the primary + // link-local address* for the LwIP netif has not been set already, then use netif_ip6_addr_set() + // to set the primary address. Otherwise use netif_add_ip6_address(). This special case is + // required because LwIP's netif_add_ip6_address() will never set the primary link-local address. + // + // * -- The primary link-local address always appears in the first slot in the netif address table. + // + if (addr.IsIPv6LinkLocal() && !addrAssigned[0]) + { + netif_ip6_addr_set(mNetIf, 0, ip_2_ip6(&lwipAddr)); + addrIdx = 0; + } + else + { + // Add the address to the LwIP netif. If the address table fills (ERR_VAL), simply stop + // adding addresses. If something else fails, log it and soldier on. + lwipErr = netif_add_ip6_address(mNetIf, ip_2_ip6(&lwipAddr), &addrIdx); + if (lwipErr == ERR_VAL) + { + break; + } + else if (lwipErr != ERR_OK) + { + ChipLogProgress(DeviceLayer, "netif_add_ip6_address) failed: %s", + ErrorStr(chip::System::MapErrorLwIP(lwipErr))); + } + } + + // Set the address state to PREFERRED or ACTIVE depending on the state in OpenThread. + netif_ip6_addr_set_state(mNetIf, addrIdx, (otAddr->mPreferred) ? IP6_ADDR_PREFERRED : IP6_ADDR_VALID); + + // Record that the netif address slot was assigned during this loop. + addrAssigned[addrIdx] = true; + } + } + } + + ChipLogDetail(DeviceLayer, "LwIP Thread interface addresses %s", isAttached ? "updated" : "cleared"); + + // For each address associated with the netif that was *not* assigned above, remove the address + // from the netif if the address is one that was previously assigned by this method. + // In the case where the device is no longer attached to a Thread network, remove all addresses + // from the netif. + for (u8_t addrIdx = 0; addrIdx < LWIP_IPV6_NUM_ADDRESSES; addrIdx++) + { + if (!isAttached || (mAddrAssigned[addrIdx] && !addrAssigned[addrIdx])) + { + // Remove the address from the netif by setting its state to INVALID + netif_ip6_addr_set_state(mNetIf, addrIdx, IP6_ADDR_INVALID); + } + +#if CHIP_DETAIL_LOGGING + else + { + uint8_t state = netif_ip6_addr_state(mNetIf, addrIdx); + if (state != IP6_ADDR_INVALID) + { + IPAddress addr = IPAddress::FromLwIPAddr(*netif_ip6_addr(mNetIf, addrIdx)); + char addrStr[50]; + addr.ToString(addrStr, sizeof(addrStr)); + const char * typeStr; + if (IsOpenThreadMeshLocalAddress(Impl()->OTInstance(), addr)) + typeStr = "Thread mesh-local address"; + else + typeStr = CharacterizeIPv6Address(addr); + ChipLogDetail(DeviceLayer, " %s %s%s)", addrStr, typeStr, (state == IP6_ADDR_PREFERRED) ? ", preferred" : ""); + } + } +#endif // CHIP_DETAIL_LOGGING + } + + // Remember the set of assigned addresses. + memcpy(mAddrAssigned, addrAssigned, sizeof(mAddrAssigned)); + } + + Impl()->UnlockThreadStack(); + UNLOCK_TCPIP_CORE(); +} + +template +err_t GenericThreadStackManagerImpl_OpenThread_LwIP::DoInitThreadNetIf(struct netif * netif) +{ + netif->name[0] = CHIP_DEVICE_CONFIG_LWIP_THREAD_IF_NAME[0]; + netif->name[1] = CHIP_DEVICE_CONFIG_LWIP_THREAD_IF_NAME[1]; + netif->output_ip6 = SendPacket; +#if LWIP_IPV4 || LWIP_VERSION_MAJOR < 2 + netif->output = NULL; +#endif /* LWIP_IPV4 || LWIP_VERSION_MAJOR < 2 */ + netif->linkoutput = NULL; + netif->flags = NETIF_FLAG_UP | NETIF_FLAG_LINK_UP | NETIF_FLAG_BROADCAST | NETIF_FLAG_MLD6; + netif->mtu = CHIP_DEVICE_CONFIG_THREAD_IF_MTU; + return ERR_OK; +} + +/** + * Send an outbound packet via the LwIP Thread interface. + * + * This method is called by LwIP, via a pointer in the netif structure, whenever + * an IPv6 packet is to be sent out the Thread interface. + * + * NB: This method is called in the LwIP TCPIP thread with the LwIP core lock held. + */ +template +#if LWIP_VERSION_MAJOR < 2 +err_t GenericThreadStackManagerImpl_OpenThread_LwIP::SendPacket(struct netif * netif, struct pbuf * pktPBuf, + struct ip6_addr * ipaddr) +#else +err_t GenericThreadStackManagerImpl_OpenThread_LwIP::SendPacket(struct netif * netif, struct pbuf * pktPBuf, + const struct ip6_addr * ipaddr) +#endif +{ + err_t lwipErr = ERR_OK; + otError otErr; + otMessage * pktMsg = NULL; + const otMessageSettings msgSettings = { true, OT_MESSAGE_PRIORITY_NORMAL }; + uint16_t remainingLen; + + // Lock the OpenThread stack. + // Note that at this point the LwIP core lock is also held. + ThreadStackMgrImpl().LockThreadStack(); + + // Allocate an OpenThread message + pktMsg = otIp6NewMessage(ThreadStackMgrImpl().OTInstance(), &msgSettings); + VerifyOrExit(pktMsg != NULL, lwipErr = ERR_MEM); + + // Copy data from LwIP's packet buffer chain into the OpenThread message. + remainingLen = pktPBuf->tot_len; + for (struct pbuf * partialPkt = pktPBuf; partialPkt != NULL; partialPkt = partialPkt->next) + { + VerifyOrExit(partialPkt->len <= remainingLen, lwipErr = ERR_VAL); + + otErr = otMessageAppend(pktMsg, partialPkt->payload, partialPkt->len); + VerifyOrExit(otErr == OT_ERROR_NONE, lwipErr = ERR_MEM); + + remainingLen -= partialPkt->len; + } + VerifyOrExit(remainingLen == 0, lwipErr = ERR_VAL); + +#if CHIP_DETAIL_LOGGING + LogOpenThreadPacket("Thread packet SENT", pktMsg); +#endif // CHIP_DETAIL_LOGGING + + // Pass the packet to OpenThread to be sent. Note that OpenThread takes care of releasing + // the otMessage object regardless of whether otIp6Send() succeeds or fails. + // Propagate the error back up the stack UNLESS it is a transient error. + otErr = otIp6Send(ThreadStackMgrImpl().OTInstance(), pktMsg); + pktMsg = NULL; +#if CHIP_DETAIL_LOGGING + if (otErr != OT_ERROR_NONE) + { + ChipLogDetail(DeviceLayer, "ThreadStackManagerImpl: otIp6Send() error: %s", otThreadErrorToString(otErr)); + } +#endif // CHIP_DETAIL_LOGGING + VerifyOrExit(otErr == OT_ERROR_NONE || otErr == OT_ERROR_DROP || otErr == OT_ERROR_NO_BUFS || otErr == OT_ERROR_NO_ROUTE, + lwipErr = ERR_IF); + +exit: + + if (pktMsg != NULL) + { + otMessageFree(pktMsg); + } + + // Unlock the OpenThread stack. + ThreadStackMgrImpl().UnlockThreadStack(); + + return lwipErr; +} + +/** + * Receive an inbound packet from the LwIP Thread interface. + * + * This method is called by OpenThread whenever an IPv6 packet has been received destined + * to the local node has been received from the Thread interface. + * + * NB: This method is called with the OpenThread stack lock held. To ensure proper + * lock ordering, it must *not* attempt to acquire the LwIP TCPIP core lock, or the + * CHIP stack lock. + */ +template +void GenericThreadStackManagerImpl_OpenThread_LwIP::ReceivePacket(otMessage * pkt, void *) +{ + struct pbuf * pbuf = NULL; + err_t lwipErr = ERR_OK; + uint16_t pktLen = otMessageGetLength(pkt); + struct netif * threadNetIf = ThreadStackMgrImpl().ThreadNetIf(); + + // Allocate an LwIP pbuf to hold the inbound packet. + pbuf = pbuf_alloc(PBUF_RAW, pktLen, PBUF_POOL); + VerifyOrExit(pbuf != NULL, lwipErr = ERR_MEM); + + // Copy the packet data from the OpenThread message object to the pbuf. + if (otMessageRead(pkt, 0, pbuf->payload, pktLen) != pktLen) + { + ExitNow(lwipErr = ERR_IF); + } + +#if CHIP_DETAIL_LOGGING + LogOpenThreadPacket("Thread packet RCVD", pkt); +#endif // CHIP_DETAIL_LOGGING + + // Deliver the packet to the input function associated with the LwIP netif. + // NOTE: The input function posts the inbound packet to LwIP's TCPIP thread. + // Thus there's no need to acquire the LwIP TCPIP core lock here. + lwipErr = threadNetIf->input(pbuf, threadNetIf); + +exit: + + // Always free the OpenThread message. + otMessageFree(pkt); + + if (lwipErr != ERR_OK) + { + // If an error occurred, make sure the pbuf gets freed. + if (pbuf != NULL) + { + pbuf_free(pbuf); + } + + // TODO: log packet reception error + // TODO: deliver CHIP platform event signaling loss of inbound packet. + } +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/OpenThread/OpenThreadUtils.cpp b/src/platform/OpenThread/OpenThreadUtils.cpp new file mode 100644 index 00000000000000..d4fd979b5def10 --- /dev/null +++ b/src/platform/OpenThread/OpenThreadUtils.cpp @@ -0,0 +1,267 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Utility functions for working with OpenThread. + */ + +#include "OpenThreadUtils.h" + +#include +#include +#include +#include + +#include + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +/** + * Map an OpenThread error into the OpenChip error space. + */ +CHIP_ERROR MapOpenThreadError(otError otErr) +{ + return (otErr == OT_ERROR_NONE) ? CHIP_NO_ERROR : CHIP_CONFIG_OPENTHREAD_ERROR_MIN + (CHIP_ERROR) otErr; +} + +/** + * Given an OpenChip error value that represents an OpenThread error, returns a + * human-readable NULL-terminated C string describing the error. + * + * @param[in] buf Buffer into which the error string will be placed. + * @param[in] bufSize Size of the supplied buffer in bytes. + * @param[in] err The error to be described. + * + * @return true If a description string was written into the supplied buffer. + * @return false If the supplied error was not an OpenThread error. + * + */ +bool FormatOpenThreadError(char * buf, uint16_t bufSize, int32_t err) +{ + if (err < CHIP_CONFIG_OPENTHREAD_ERROR_MIN || err > CHIP_CONFIG_OPENTHREAD_ERROR_MAX) + { + return false; + } + +#if CHIP_CONFIG_SHORT_ERROR_STR + const char * desc = NULL; +#else // CHIP_CONFIG_SHORT_ERROR_STR + otError otErr = (otError)(err - CHIP_CONFIG_OPENTHREAD_ERROR_MIN); + const char * desc = otThreadErrorToString(otErr); +#endif // CHIP_CONFIG_SHORT_ERROR_STR + + chip::FormatError(buf, bufSize, "OpenThread", err, desc); + + return true; +} + +/** + * Register a text error formatter for OpenThread errors. + */ +void RegisterOpenThreadErrorFormatter(void) +{ + static ErrorFormatter sOpenThreadErrorFormatter = { FormatOpenThreadError, NULL }; + + RegisterErrorFormatter(&sOpenThreadErrorFormatter); +} + +/** + * Log information related to a state change in the OpenThread stack. + * + * NB: This function *must* be called with the Thread stack lock held. + */ +void LogOpenThreadStateChange(otInstance * otInst, uint32_t flags) +{ +#if CHIP_DETAIL_LOGGING + + const uint32_t kParamsChanged = (OT_CHANGED_THREAD_NETWORK_NAME | OT_CHANGED_THREAD_PANID | OT_CHANGED_THREAD_EXT_PANID | + OT_CHANGED_THREAD_CHANNEL | OT_CHANGED_MASTER_KEY | OT_CHANGED_PSKC); + + static char strBuf[64]; + + ChipLogDetail(DeviceLayer, "OpenThread State Changed (Flags: 0x%08x)", flags); + if ((flags & OT_CHANGED_THREAD_ROLE) != 0) + { + ChipLogDetail(DeviceLayer, " Device Role: %s", OpenThreadRoleToStr(otThreadGetDeviceRole(otInst))); + } + if ((flags & kParamsChanged) != 0) + { + ChipLogDetail(DeviceLayer, " Network Name: %s", otThreadGetNetworkName(otInst)); + ChipLogDetail(DeviceLayer, " PAN Id: 0x%04X", otLinkGetPanId(otInst)); + { + const otExtendedPanId * exPanId = otThreadGetExtendedPanId(otInst); + snprintf(strBuf, sizeof(strBuf), "0x%02X%02X%02X%02X%02X%02X%02X%02X", exPanId->m8[0], exPanId->m8[1], exPanId->m8[2], + exPanId->m8[3], exPanId->m8[4], exPanId->m8[5], exPanId->m8[6], exPanId->m8[7]); + ChipLogDetail(DeviceLayer, " Extended PAN Id: %s", strBuf); + } + ChipLogDetail(DeviceLayer, " Channel: %d", otLinkGetChannel(otInst)); + { + const otMeshLocalPrefix * otMeshPrefix = otThreadGetMeshLocalPrefix(otInst); + chip::Inet::IPAddress meshPrefix; + memset(meshPrefix.Addr, 0, sizeof(meshPrefix.Addr)); + memcpy(meshPrefix.Addr, otMeshPrefix->m8, sizeof(otMeshPrefix->m8)); + meshPrefix.ToString(strBuf, sizeof(strBuf)); + ChipLogDetail(DeviceLayer, " Mesh Prefix: %s/64", strBuf); + } +#if CHIP_CONFIG_SECURITY_TEST_MODE + { + const otMasterKey * otKey = otThreadGetMasterKey(otInst); + for (int i = 0; i < OT_MASTER_KEY_SIZE; i++) + snprintf(&strBuf[i * 2], 3, "%02X", otKey->m8[i]); + ChipLogDetail(DeviceLayer, " Master Key: %s", strBuf); + } +#endif // CHIP_CONFIG_SECURITY_TEST_MODE + } + if ((flags & OT_CHANGED_THREAD_PARTITION_ID) != 0) + { + ChipLogDetail(DeviceLayer, " Partition Id: 0x%" PRIX32, otThreadGetPartitionId(otInst)); + } + if ((flags & (OT_CHANGED_IP6_ADDRESS_ADDED | OT_CHANGED_IP6_ADDRESS_REMOVED)) != 0) + { + ChipLogDetail(DeviceLayer, " Thread Unicast Addresses:"); + for (const otNetifAddress * addr = otIp6GetUnicastAddresses(otInst); addr != NULL; addr = addr->mNext) + { + chip::Inet::IPAddress ipAddr; + memcpy(ipAddr.Addr, addr->mAddress.mFields.m32, sizeof(ipAddr.Addr)); + + ipAddr.ToString(strBuf, sizeof(strBuf)); + + ChipLogDetail(DeviceLayer, " %s/%d%s%s%s", strBuf, addr->mPrefixLength, addr->mValid ? " valid" : "", + addr->mPreferred ? " preferred" : "", addr->mRloc ? " rloc" : ""); + } + } + +#endif // CHIP_DETAIL_LOGGING +} + +void LogOpenThreadPacket(const char * titleStr, otMessage * pkt) +{ +#if CHIP_DETAIL_LOGGING + + char srcStr[50], destStr[50], typeBuf[20]; + const char * type = typeBuf; + IPAddress addr; + uint8_t headerData[44]; + uint16_t pktLen; + + const uint8_t & IPv6_NextHeader = headerData[6]; + const uint8_t * const IPv6_SrcAddr = headerData + 8; + const uint8_t * const IPv6_DestAddr = headerData + 24; + const uint8_t * const IPv6_SrcPort = headerData + 40; + const uint8_t * const IPv6_DestPort = headerData + 42; + const uint8_t & ICMPv6_Type = headerData[40]; + const uint8_t & ICMPv6_Code = headerData[41]; + + constexpr uint8_t kIPProto_UDP = 17; + constexpr uint8_t kIPProto_TCP = 6; + constexpr uint8_t kIPProto_ICMPv6 = 58; + + constexpr uint8_t kICMPType_EchoRequest = 128; + constexpr uint8_t kICMPType_EchoResponse = 129; + + pktLen = otMessageGetLength(pkt); + + if (pktLen >= sizeof(headerData)) + { + otMessageRead(pkt, 0, headerData, sizeof(headerData)); + + memcpy(addr.Addr, IPv6_SrcAddr, 16); + addr.ToString(srcStr, sizeof(srcStr)); + + memcpy(addr.Addr, IPv6_DestAddr, 16); + addr.ToString(destStr, sizeof(destStr)); + + if (IPv6_NextHeader == kIPProto_UDP) + { + type = "UDP"; + } + else if (IPv6_NextHeader == kIPProto_TCP) + { + type = "TCP"; + } + else if (IPv6_NextHeader == kIPProto_ICMPv6) + { + if (ICMPv6_Type == kICMPType_EchoRequest) + { + type = "ICMPv6 Echo Request"; + } + else if (ICMPv6_Type == kICMPType_EchoResponse) + { + type = "ICMPv6 Echo Response"; + } + else + { + snprintf(typeBuf, sizeof(typeBuf), "ICMPv6 %" PRIu8 ",%" PRIu8, ICMPv6_Type, ICMPv6_Code); + } + } + else + { + snprintf(typeBuf, sizeof(typeBuf), "IP proto %" PRIu8, IPv6_NextHeader); + } + + if (IPv6_NextHeader == kIPProto_UDP || IPv6_NextHeader == kIPProto_TCP) + { + snprintf(srcStr + strlen(srcStr), 13, ", port %" PRIu16, Encoding::BigEndian::Get16(IPv6_SrcPort)); + snprintf(destStr + strlen(destStr), 13, ", port %" PRIu16, Encoding::BigEndian::Get16(IPv6_DestPort)); + } + + ChipLogDetail(DeviceLayer, "%s: %s, len %" PRIu16, titleStr, type, pktLen); + ChipLogDetail(DeviceLayer, " src %s", srcStr); + ChipLogDetail(DeviceLayer, " dest %s", destStr); + } + else + { + ChipLogDetail(DeviceLayer, "%s: %s, len %" PRIu16, titleStr, "(decode error)", pktLen); + } + +#endif // CHIP_DETAIL_LOGGING +} + +bool IsOpenThreadMeshLocalAddress(otInstance * otInst, const IPAddress & addr) +{ + const otMeshLocalPrefix * otMeshPrefix = otThreadGetMeshLocalPrefix(otInst); + + return otMeshPrefix != NULL && memcmp(otMeshPrefix->m8, addr.Addr, OT_MESH_LOCAL_PREFIX_SIZE) == 0; +} + +const char * OpenThreadRoleToStr(otDeviceRole role) +{ + switch (role) + { + case OT_DEVICE_ROLE_DISABLED: + return "DISABLED"; + case OT_DEVICE_ROLE_DETACHED: + return "DETACHED"; + case OT_DEVICE_ROLE_CHILD: + return "CHILD"; + case OT_DEVICE_ROLE_ROUTER: + return "ROUTER"; + case OT_DEVICE_ROLE_LEADER: + return "LEADER"; + default: + return "(unknown)"; + } +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/OpenThread/OpenThreadUtils.h b/src/platform/OpenThread/OpenThreadUtils.h new file mode 100644 index 00000000000000..8787ddf02a4f22 --- /dev/null +++ b/src/platform/OpenThread/OpenThreadUtils.h @@ -0,0 +1,95 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * Utility functions for working with OpenThread. + */ + +#ifndef OPENTHREAD_UTILS_H +#define OPENTHREAD_UTILS_H + +#include + +#include +#include +#include + +#include +#include +#include + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +/** + * @def CHIP_CONFIG_OPENTHREAD_ERROR_MIN + * + * @brief + * The base value for OpenThread errors when mapped into the CHIP error space. + */ +#ifndef CHIP_CONFIG_OPENTHREAD_ERROR_MIN +#define CHIP_CONFIG_OPENTHREAD_ERROR_MIN 9000000 +#endif // CHIP_CONFIG_OPENTHREAD_ERROR_MIN + +/** + * @def CHIP_CONFIG_OPENTHREAD_ERROR_MAX + * + * @brief + * The max value for OpenThread errors when mapped into the CHIP error space. + */ +#ifndef CHIP_CONFIG_OPENTHREAD_ERROR_MAX +#define CHIP_CONFIG_OPENTHREAD_ERROR_MAX 9000999 +#endif // CHIP_CONFIG_OPENTHREAD_ERROR_MAX + +extern CHIP_ERROR MapOpenThreadError(otError otErr); +extern void RegisterOpenThreadErrorFormatter(void); +extern void LogOpenThreadStateChange(otInstance * otInst, uint32_t flags); +extern void LogOpenThreadPacket(const char * titleStr, otMessage * pkt); +extern bool IsOpenThreadMeshLocalAddress(otInstance * otInst, const Inet::IPAddress & addr); +extern const char * OpenThreadRoleToStr(otDeviceRole role); + +inline otIp6Address ToOpenThreadIP6Address(const Inet::IPAddress & addr) +{ + otIp6Address otAddr; + memcpy(otAddr.mFields.m32, addr.Addr, sizeof(otAddr.mFields.m32)); + return otAddr; +} + +inline Inet::IPAddress ToIPAddress(const otIp6Address & otAddr) +{ + Inet::IPAddress addr; + memcpy(addr.Addr, otAddr.mFields.m32, sizeof(addr.Addr)); + return addr; +} + +inline Inet::IPPrefix ToIPPrefix(const otIp6Prefix & otPrefix) +{ + Inet::IPPrefix prefix; + prefix.IPAddr = ToIPAddress(otPrefix.mPrefix); + prefix.Length = otPrefix.mLength; + return prefix; +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif // OPENTHREAD_UTILS_H diff --git a/src/platform/nRF5/CHIPDevicePlatformConfig.h b/src/platform/nRF5/CHIPDevicePlatformConfig.h index ed4d1efad2c020..4fda568645c10b 100644 --- a/src/platform/nRF5/CHIPDevicePlatformConfig.h +++ b/src/platform/nRF5/CHIPDevicePlatformConfig.h @@ -36,7 +36,9 @@ #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION 0 #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_AP 0 -#define CHIP_DEVICE_CONFIG_ENABLE_THREAD 0 +#if CHIP_ENABLE_OPENTHREAD +#define CHIP_DEVICE_CONFIG_ENABLE_THREAD 1 +#endif #define CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE 1 diff --git a/src/platform/nRF5/Logging.cpp b/src/platform/nRF5/Logging.cpp index 197cfe75f2afc4..4c524e7472a5eb 100644 --- a/src/platform/nRF5/Logging.cpp +++ b/src/platform/nRF5/Logging.cpp @@ -79,17 +79,14 @@ namespace chip { namespace Logging { /** - * Openchip log output function. + * CHIP log output function. */ -void Log(uint8_t module, uint8_t category, const char * msg, ...) -{ - va_list v; +void LogV(uint8_t module, uint8_t category, const char * msg, va_list v) +{ (void) module; (void) category; - va_start(v, msg); - #if NRF_LOG_ENABLED if (IsCategoryEnabled(category)) @@ -131,10 +128,10 @@ void Log(uint8_t module, uint8_t category, const char * msg, ...) // Let the application know that a log message has been emitted. DeviceLayer::OnLogOutput(); } - +#else // NRF_LOG_ENABLED + (void) msg; + (void) v; #endif // NRF_LOG_ENABLED - - va_end(v); } } // namespace Logging diff --git a/src/platform/tests/Makefile.am b/src/platform/tests/Makefile.am index f9230c12b04237..f03b7df3eccff0 100644 --- a/src/platform/tests/Makefile.am +++ b/src/platform/tests/Makefile.am @@ -36,6 +36,24 @@ EXTRA_DIST = \ $(NULL) if CHIP_BUILD_TESTS +lib_LIBRARIES = \ + libPlatformTests.a \ + $(NULL) + +libPlatformTests_a_SOURCES = \ + TestPlatformMgr.cpp \ + TestPlatformTime.cpp \ + TestConfigurationMgr.cpp \ + $(NULL) + +libPlatformTests_adir = $(includedir)/platform + +dist_libPlatformTests_a_HEADERS = \ + TestPlatformMgr.h \ + TestPlatformTime.h \ + TestConfigurationMgr.h \ + $(NULL) + # C/C++ preprocessor option flags that will apply to all compiled # objects in this makefile. @@ -57,8 +75,9 @@ CHIP_LDADD = \ $(top_builddir)/src/lib/support/libSupportLayer.a \ $(NULL) -COMMON_LDADD = \ - $(CHIP_LDADD) \ +COMMON_LDADD = \ + libPlatformTests.a \ + $(CHIP_LDADD) \ $(NLFAULTINJECTION_LDFLAGS) $(NLFAULTINJECTION_LIBS)\ $(NLUNIT_TEST_LDFLAGS) $(NLUNIT_TEST_LIBS) \ $(LWIP_LDFLAGS) $(LWIP_LIBS) \ @@ -71,8 +90,11 @@ COMMON_LDADD = \ check_PROGRAMS = \ TestPlatformTime \ TestPlatformMgr \ + TestConfigurationMgr \ $(NULL) +# TODO Add TestConfigurationMgr to check_PROGRAMS when dependent core profile are merged + # Test applications and scripts that should be built and run when the # 'check' target is run. @@ -89,10 +111,13 @@ TESTS_ENVIRONMENT = \ # Source, compiler, and linker options for test programs. TestPlatformTime_LDADD = $(COMMON_LDADD) -TestPlatformTime_SOURCES = TestPlatformTime.cpp +TestPlatformTime_SOURCES = TestPlatformTimeDriver.cpp TestPlatformMgr_LDADD = $(COMMON_LDADD) -TestPlatformMgr_SOURCES = TestPlatformMgr.cpp +TestPlatformMgr_SOURCES = TestPlatformMgrDriver.cpp + +TestConfigurationMgr_LDADD = $(COMMON_LDADD) +TestConfigurationMgr_SOURCES = TestConfigurationMgrDriver.cpp # # Foreign make dependencies diff --git a/src/platform/tests/TestConfigurationMgr.cpp b/src/platform/tests/TestConfigurationMgr.cpp new file mode 100644 index 00000000000000..36e7bc89a4cd05 --- /dev/null +++ b/src/platform/tests/TestConfigurationMgr.cpp @@ -0,0 +1,133 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements a unit test suite for the Configuration Manager + * code functionality. + * + */ + +#include "TestConfigurationMgr.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace chip; +using namespace chip::Logging; +using namespace chip::Inet; +using namespace chip::DeviceLayer; + +// ================================= +// Unit tests +// ================================= + +static void TestPlatformMgr_Init(nlTestSuite * inSuite, void * inContext) +{ + // ConfigurationManager is initialized from PlatformManager indirectly + CHIP_ERROR err = PlatformMgr().InitChipStack(); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); +} + +static void TestConfigurationMgr_FabricId(nlTestSuite * inSuite, void * inContext) +{ +#if CHIP_CONFIG_ENABLE_FABRIC_STATE + CHIP_ERROR err = CHIP_NO_ERROR; + uint64_t fabricId = 0; + + err = ConfigurationMgr().StoreFabricId(2006255910626445ULL); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = ConfigurationMgr().GetFabricId(fabricId); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + NL_TEST_ASSERT(inSuite, fabricId == 2006255910626445ULL); +#endif +} + +static void TestConfigurationMgr_ServiceConfig(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + uint8_t buf[1024]; + size_t serviceConfigLen = 0; + const char * serviceConfig = "1QAADwABADYBFTABCQCoNCLp2XXkVSQCBDcDJxMBAADu7jC0GBgmBJUjqRkmBRXB0iw3BicTAQAA7" + "u4wtBgYJAcCJggVAFojMAoxBHhS4pySunAZWEZtrhhySvtDDfYHKTMNYVXlZUaOug2lP7UXwEdkRA" + "IYT6gRJFDUezWDKQEpAhg1gikBJAJgGDWBMAIIQgys9rRkceYYNYAwAghCDKz2tGRx5hg1DDABGQC" + "+DtqhY1qO8VIXRYC93JQS1MwcLDNOKdwwAhkAi+fuLhEXFK6S2is7bS/XXZ5fzbi6L2V2GBgVMAEI" + "cQIn0yh6aI8kAgQ3AywBCDMwMzI0OTUzGCYEeo6eJSYFejfEODcGLAEIMzAzMjQ5NTMYJAcCJgglA" + "FojMAo5BLZ6ok8Qck/nD1V/uwxBHadUxpBwP+AtAjO9iBxp+CrnCdp8iT1k0rpICd/yVB5J79FpKN" + "Zk81aVNYMpARg1gikBJAIFGDWEKQE2AgQCBAEYGDWBMAIITs6h0PVqSKQYNYAwAghOzqHQ9WpIpBg" + "1DDABHGd5WOwcKPLXFHW97Y3skfyC8PHj0LgiadIPQx4wAh0AnzaRJWzznB07Q0cghoIGZTIPZjZA" + "0kFZ3uvMPxgYGDUCJwEBAAAAAjC0GDYCFSwBGWZyb250ZG9vci5xYS5uZXN0bGFicy5jb20lAlcrG" + "BgYGA=="; + + err = ConfigurationMgr().StoreServiceConfig((uint8_t *) serviceConfig, strlen(serviceConfig)); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = ConfigurationMgr().GetServiceConfig(buf, 1024, serviceConfigLen); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + NL_TEST_ASSERT(inSuite, serviceConfigLen == strlen(serviceConfig)); + NL_TEST_ASSERT(inSuite, memcmp(buf, serviceConfig, serviceConfigLen) == 0); +} + +static void TestConfigurationMgr_PairedAccountId(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + char buf[64]; + size_t accountIdLen = 0; + const char * pairedAccountId = "USER_016CB664A86A888D"; + + err = ConfigurationMgr().StorePairedAccountId(pairedAccountId, strlen(pairedAccountId)); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = ConfigurationMgr().GetPairedAccountId(buf, 64, accountIdLen); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + NL_TEST_ASSERT(inSuite, accountIdLen == strlen(pairedAccountId)); + NL_TEST_ASSERT(inSuite, strcmp(buf, "USER_016CB664A86A888D") == 0); +} + +/** + * Test Suite. It lists all the test functions. + */ +static const nlTest sTests[] = { + + NL_TEST_DEF("Test PlatformMgr::Init", TestPlatformMgr_Init), + NL_TEST_DEF("Test ConfigurationMgr::FabricId", TestConfigurationMgr_FabricId), + NL_TEST_DEF("Test ConfigurationMgr::ServiceConfig", TestConfigurationMgr_ServiceConfig), + NL_TEST_DEF("Test ConfigurationMgr::PairedAccountId", TestConfigurationMgr_PairedAccountId), NL_TEST_SENTINEL() +}; + +int TestConfigurationMgr(void) +{ + nlTestSuite theSuite = { "CHIP DeviceLayer time tests", &sTests[0], NULL, NULL }; + + // Run test suit againt one context. + nlTestRunner(&theSuite, NULL); + return nlTestRunnerStats(&theSuite); +} diff --git a/src/platform/tests/TestConfigurationMgr.h b/src/platform/tests/TestConfigurationMgr.h new file mode 100644 index 00000000000000..f150dc0a3d81db --- /dev/null +++ b/src/platform/tests/TestConfigurationMgr.h @@ -0,0 +1,29 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file declares test entry point for CHIP Configuration Manager code unit tests. + * + */ + +#ifndef TESTCONFIGURATIONMGR_H +#define TESTCONFIGURATIONMGR_H + +int TestConfigurationMgr(void); + +#endif // TESTCONFIGURATIONMGR_H diff --git a/src/platform/tests/TestConfigurationMgrDriver.cpp b/src/platform/tests/TestConfigurationMgrDriver.cpp new file mode 100644 index 00000000000000..1af1df458ccbdb --- /dev/null +++ b/src/platform/tests/TestConfigurationMgrDriver.cpp @@ -0,0 +1,30 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements a standalone/native program executable + * test driver for the Configuration Manager code code unit tests. + * + */ + +#include "TestConfigurationMgr.h" + +int main(void) +{ + return (TestConfigurationMgr()); +} diff --git a/src/platform/tests/TestPlatformMgr.cpp b/src/platform/tests/TestPlatformMgr.cpp index f316494b19614f..ed255a036cb3ae 100644 --- a/src/platform/tests/TestPlatformMgr.cpp +++ b/src/platform/tests/TestPlatformMgr.cpp @@ -15,6 +15,15 @@ * limitations under the License. */ +/** + * @file + * This file implements a unit test suite for the Platform Manager + * code functionality. + * + */ + +#include "TestPlatformMgr.h" + #include #include #include @@ -95,7 +104,7 @@ static const nlTest sTests[] = { NL_TEST_SENTINEL() }; -int main(void) +int TestPlatformMgr(void) { nlTestSuite theSuite = { "CHIP DeviceLayer time tests", &sTests[0], NULL, NULL }; diff --git a/src/platform/tests/TestPlatformMgr.h b/src/platform/tests/TestPlatformMgr.h new file mode 100644 index 00000000000000..01a94df5cb93f0 --- /dev/null +++ b/src/platform/tests/TestPlatformMgr.h @@ -0,0 +1,29 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file declares test entry point for CHIP Platform Manager code unit tests. + * + */ + +#ifndef TESTPLATFORMMGR_H +#define TESTPLATFORMMGR_H + +int TestPlatformMgr(void); + +#endif // TESTPLATFORMMGR_H diff --git a/src/platform/tests/TestPlatformMgrDriver.cpp b/src/platform/tests/TestPlatformMgrDriver.cpp new file mode 100644 index 00000000000000..aa4eb72595930c --- /dev/null +++ b/src/platform/tests/TestPlatformMgrDriver.cpp @@ -0,0 +1,30 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements a standalone/native program executable + * test driver for the Platform Manager code code unit tests. + * + */ + +#include "TestPlatformMgr.h" + +int main(void) +{ + return (TestPlatformMgr()); +} diff --git a/src/platform/tests/TestPlatformTime.cpp b/src/platform/tests/TestPlatformTime.cpp index 2ae5927f4de9e9..d05a2881aa23fe 100644 --- a/src/platform/tests/TestPlatformTime.cpp +++ b/src/platform/tests/TestPlatformTime.cpp @@ -15,6 +15,15 @@ * limitations under the License. */ +/** + * @file + * This file implements a unit test suite for the Platform Time + * code functionality. + * + */ + +#include "TestPlatformTime.h" + #include #include #include @@ -112,8 +121,10 @@ static void TestDevice_GetClock_Monotonic(nlTestSuite * inSuite, void * inContex ChipLogProgress(DeviceLayer, "Start=%" PRIu64 " End=%" PRIu64 " Delta=%" PRIu64 " Expected=%" PRIu64, Tstart, Tend, Tdelta, Tdelay); + // verify that timers don't fire early NL_TEST_ASSERT(inSuite, Tdelta > (Tdelay - margin)); - NL_TEST_ASSERT(inSuite, Tdelta < (Tdelay + margin)); + // verify they're not too late + // NL_TEST_ASSERT(inSuite, Tdelta < (Tdelay + margin)); numOfTestsRan++; } NL_TEST_ASSERT(inSuite, numOfTestsRan > 0); @@ -141,8 +152,10 @@ static void TestDevice_GetClock_MonotonicMS(nlTestSuite * inSuite, void * inCont ChipLogProgress(DeviceLayer, "Start=%" PRIu64 " End=%" PRIu64 " Delta=%" PRIu64 " Expected=%" PRIu64, Tstart, Tend, Tdelta, Tdelay); + // verify that timers don't fire early NL_TEST_ASSERT(inSuite, Tdelta > (Tdelay - margin)); - NL_TEST_ASSERT(inSuite, Tdelta < (Tdelay + margin)); + // verify they're not too late + // NL_TEST_ASSERT(inSuite, Tdelta < (Tdelay + margin)); numOfTestsRan++; } NL_TEST_ASSERT(inSuite, numOfTestsRan > 0); @@ -189,7 +202,7 @@ static const nlTest sTests[] = { NL_TEST_SENTINEL() }; -int main(void) +int TestPlatformTime(void) { nlTestSuite theSuite = { "CHIP DeviceLayer tests", &sTests[0], NULL, NULL }; diff --git a/src/platform/tests/TestPlatformTime.h b/src/platform/tests/TestPlatformTime.h new file mode 100644 index 00000000000000..66f52f1edd35dc --- /dev/null +++ b/src/platform/tests/TestPlatformTime.h @@ -0,0 +1,29 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file declares test entry point for CHIP Platform Time code unit tests. + * + */ + +#ifndef TESTPLATFORMTIME_H +#define TESTPLATFORMTIME_H + +int TestPlatformTime(void); + +#endif // TESTCONFIGURATIONMGR_H diff --git a/src/platform/tests/TestPlatformTimeDriver.cpp b/src/platform/tests/TestPlatformTimeDriver.cpp new file mode 100644 index 00000000000000..f301a0b7820faf --- /dev/null +++ b/src/platform/tests/TestPlatformTimeDriver.cpp @@ -0,0 +1,30 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements a standalone/native program executable + * test driver for the Platform Time code code unit tests. + * + */ + +#include "TestPlatformTime.h" + +int main(void) +{ + return (TestPlatformTime()); +} diff --git a/src/qrcodetool/setup_payload_commands.cpp b/src/qrcodetool/setup_payload_commands.cpp index b9a3330fce094b..19eae22fc9971a 100644 --- a/src/qrcodetool/setup_payload_commands.cpp +++ b/src/qrcodetool/setup_payload_commands.cpp @@ -18,8 +18,6 @@ #include "setup_payload_commands.h" #include -#include - #include #include #include diff --git a/src/setup_payload/Base41.cpp b/src/setup_payload/Base41.cpp index e81f78c7b7975d..4c0af799da6050 100644 --- a/src/setup_payload/Base41.cpp +++ b/src/setup_payload/Base41.cpp @@ -40,7 +40,7 @@ static const char codes[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8' 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '*', '+', '-', '.' }; static const int kBase41ChunkLen = 3; static const int kBytesChunkLen = 2; -static const int radix = sizeof(codes) / sizeof(codes[0]); +static const int kRadix = sizeof(codes) / sizeof(codes[0]); string base41Encode(const uint8_t * buf, size_t buf_len) { @@ -51,24 +51,62 @@ string base41Encode(const uint8_t * buf, size_t buf_len) while (buf_len >= kBytesChunkLen) { - int next = buf[0] + buf[1] * 256; - - for (int _ = 0; _ < kBase41ChunkLen; _++) + int value = 0; + for (int i = kBytesChunkLen - 1; i >= 0; i--) { - result += codes[next % radix]; - next /= radix; + value *= 256; + value += buf[i]; } - buf += kBytesChunkLen; buf_len -= kBytesChunkLen; + buf += kBytesChunkLen; + + // This code needs to correctly convey to the decoder + // the length of data encoded, so normally emits 3 chars for + // 2 bytes. + // But there's a special case possible where the last + // two bytes would fit in exactly 2 chars. + // The following conditions must be met: + // 1. this must be the last value + // 2. the value doesn't fit in a single byte, if we encode a + // small value at the end of the encoded string with a + // shortened chunk, the decoder will think only one byte + // was encoded + // 3. the value can be encoded in 2 base41 chars + // + int encodeLen = kBase41ChunkLen; + if (buf_len == 0 && // the very last value, i.e. not an odd byte + (value > UINT8_MAX) && // the value wouldn't fit in one byte + (value < kRadix * kRadix)) // the value can be encoded with 2 base41 chars + { + encodeLen--; + } + for (int _ = 0; _ < encodeLen; _++) + { + result += codes[value % kRadix]; + value /= kRadix; + } } - // handle leftover byte, if any + + // handle leftover bytes, if any if (buf_len != 0) { - int next = buf[0]; - for (int _ = 0; _ < kBase41ChunkLen - 1; _++) + int value = 0; + + for (int i = buf_len - 1; i >= 0; i--) { - result += codes[next % radix]; - next /= radix; + value *= 256; + value += buf[i]; + } + + // need to indicate there are leftover bytes, so append at least one encoding char + result += codes[value % kRadix]; + value /= kRadix; + + // if there's still value, encode with more chars + while (value != 0) + { + result += codes[value % kRadix]; + value /= kRadix; } } return result; @@ -155,12 +193,6 @@ static inline CHIP_ERROR decodeChar(char c, uint8_t & value) CHIP_ERROR base41Decode(string base41, vector & result) { - // not long enough, there are always at least 2 - // base41 characters - if (base41.length() % kBase41ChunkLen == 1) - { - return CHIP_ERROR_INVALID_MESSAGE_LENGTH; - } result.clear(); for (int i = 0; base41.length() - i >= kBase41ChunkLen; i += kBase41ChunkLen) @@ -177,19 +209,21 @@ CHIP_ERROR base41Decode(string base41, vector & result) return err; } - value *= radix; + value *= kRadix; value += v; } result.push_back(value % 256); result.push_back(value / 256); } - if (base41.length() % kBase41ChunkLen == kBase41ChunkLen - 1) + + if (base41.length() % kBase41ChunkLen != 0) // only 1 or 2 chars left { - int i = base41.length() - (kBase41ChunkLen - 1); + int tail = (base41.length() % kBase41ChunkLen); + int i = base41.length() - tail; uint16_t value = 0; - for (int iv = i + (kBase41ChunkLen - 2); iv >= i; iv--) + for (int iv = base41.length() - 1; iv >= i; iv--) { uint8_t v; CHIP_ERROR err = decodeChar(base41[iv], v); @@ -199,12 +233,15 @@ CHIP_ERROR base41Decode(string base41, vector & result) return err; } - value *= radix; + value *= kRadix; value += v; } - result.push_back(value % 256); - result.push_back(value / 256); + value /= 256; + if (value != 0) + { + result.push_back(value); + } } return CHIP_NO_ERROR; } diff --git a/src/setup_payload/ManualSetupPayloadGenerator.cpp b/src/setup_payload/ManualSetupPayloadGenerator.cpp index 054595d6dec756..ea782339f77055 100644 --- a/src/setup_payload/ManualSetupPayloadGenerator.cpp +++ b/src/setup_payload/ManualSetupPayloadGenerator.cpp @@ -23,6 +23,7 @@ #include "ManualSetupPayloadGenerator.h" +#include #include using namespace chip; @@ -31,10 +32,9 @@ static uint32_t shortPayloadRepresentation(SetupPayload payload) { int offset = 1; uint32_t result = payload.requiresCustomFlow ? 1 : 0; - result |= payload.setUpPINCode << offset; - offset += kSetupPINCodeFieldLengthInBits; - result |= payload.discriminator << offset; + offset += kManualSetupDiscriminatorFieldLengthInBits; + result |= payload.setUpPINCode << offset; return result; } @@ -49,7 +49,7 @@ CHIP_ERROR ManualSetupPayloadGenerator::payloadDecimalStringRepresentation(strin { if (!mSetupPayload.isValidManualCode()) { - fprintf(stderr, "\nFailed encoding invalid payload\n"); + ChipLogError(SetupPayload, "Failed encoding invalid payload"); return CHIP_ERROR_INVALID_ARGUMENT; } diff --git a/src/setup_payload/ManualSetupPayloadParser.cpp b/src/setup_payload/ManualSetupPayloadParser.cpp index cd9b46a406b9ba..0db56ac00e2b69 100644 --- a/src/setup_payload/ManualSetupPayloadParser.cpp +++ b/src/setup_payload/ManualSetupPayloadParser.cpp @@ -23,6 +23,7 @@ #include "ManualSetupPayloadParser.h" +#include #include #include @@ -37,7 +38,7 @@ static CHIP_ERROR checkDecimalStringValidity(string decimalString, string & deci { if (decimalString.length() < 2) { - fprintf(stderr, "\nFailed decoding base10. Input was empty. %zu\n", decimalString.length()); + ChipLogError(SetupPayload, "Failed decoding base10. Input was empty. %zu", decimalString.length()); return CHIP_ERROR_INVALID_STRING_LENGTH; } string repWithoutCheckChar = decimalString.substr(0, decimalString.length() - 1); @@ -56,8 +57,8 @@ static CHIP_ERROR checkCodeLengthValidity(string decimalString, bool isLongCode) size_t expectedCharLength = isLongCode ? kManualSetupLongCodeCharLength : kManualSetupShortCodeCharLength; if (decimalString.length() != expectedCharLength) { - fprintf(stderr, "\nFailed decoding base10. Input length %zu was not expected length %zu\n", decimalString.length(), - expectedCharLength); + ChipLogError(SetupPayload, "Failed decoding base10. Input length %zu was not expected length %zu", decimalString.length(), + expectedCharLength); return CHIP_ERROR_INVALID_STRING_LENGTH; } return CHIP_NO_ERROR; @@ -68,7 +69,7 @@ static CHIP_ERROR extractBits(uint32_t number, uint64_t & dest, int index, int n { if ((index + numberBits) > maxBits) { - fprintf(stderr, "\nNumber %u maxBits %d index %d n %d\n", number, maxBits, index, numberBits); + ChipLogError(SetupPayload, "Number %u maxBits %d index %d n %d", number, maxBits, index, numberBits); return CHIP_ERROR_INVALID_STRING_LENGTH; } dest = (((1 << numberBits) - 1) & (number >> index)); @@ -82,7 +83,7 @@ static CHIP_ERROR toNumber(string decimalString, uint64_t & dest) { if (!isdigit(decimalString[i])) { - fprintf(stderr, "\nFailed decoding base10. Character was invalid %c\n", decimalString[i]); + ChipLogError(SetupPayload, "Failed decoding base10. Character was invalid %c", decimalString[i]); return CHIP_ERROR_INVALID_INTEGER_VALUE; } number *= 10; @@ -97,12 +98,12 @@ static CHIP_ERROR readDigitsFromDecimalString(string decimalString, int & index, { if (decimalString.length() < numberOfCharsToRead || (numberOfCharsToRead + index > decimalString.length())) { - fprintf(stderr, "\nFailed decoding base10. Input was too short. %zu\n", decimalString.length()); + ChipLogError(SetupPayload, "Failed decoding base10. Input was too short. %zu", decimalString.length()); return CHIP_ERROR_INVALID_STRING_LENGTH; } else if (index < 0) { - fprintf(stderr, "\nFailed decoding base10. Index was negative. %d\n", index); + ChipLogError(SetupPayload, "Failed decoding base10. Index was negative. %d", index); return CHIP_ERROR_INVALID_ARGUMENT; } @@ -156,24 +157,25 @@ CHIP_ERROR ManualSetupPayloadParser::populatePayload(SetupPayload & outPayload) int numberOffset = 1; uint64_t setUpPINCode; size_t maxShortCodeBitsLength = 1 + kSetupPINCodeFieldLengthInBits + kManualSetupDiscriminatorFieldLengthInBits; - result = readBitsFromNumber(shortCode, numberOffset, setUpPINCode, kSetupPINCodeFieldLengthInBits, maxShortCodeBitsLength); + + uint64_t discriminator; + result = readBitsFromNumber(shortCode, numberOffset, discriminator, kManualSetupDiscriminatorFieldLengthInBits, + maxShortCodeBitsLength); if (result != CHIP_NO_ERROR) { return result; } - else if (setUpPINCode == 0) - { - fprintf(stderr, "\nFailed decoding base10. SetUpPINCode was 0.\n"); - return CHIP_ERROR_INVALID_ARGUMENT; - } - uint64_t discriminator; - result = readBitsFromNumber(shortCode, numberOffset, discriminator, kManualSetupDiscriminatorFieldLengthInBits, - maxShortCodeBitsLength); + result = readBitsFromNumber(shortCode, numberOffset, setUpPINCode, kSetupPINCodeFieldLengthInBits, maxShortCodeBitsLength); if (result != CHIP_NO_ERROR) { return result; } + else if (setUpPINCode == 0) + { + ChipLogError(SetupPayload, "Failed decoding base10. SetUpPINCode was 0."); + return CHIP_ERROR_INVALID_ARGUMENT; + } if (isLongCode) { diff --git a/src/setup_payload/QRCodeSetupPayloadGenerator.cpp b/src/setup_payload/QRCodeSetupPayloadGenerator.cpp index ab06a24d94993b..8e4b1ead78bbb2 100644 --- a/src/setup_payload/QRCodeSetupPayloadGenerator.cpp +++ b/src/setup_payload/QRCodeSetupPayloadGenerator.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -87,29 +88,25 @@ static void addCHIPInfoToOptionalData(SetupPayload & outPayload) { OptionalQRCodeInfo info; info.type = optionalQRCodeInfoTypeString; - info.tag = ContextTag(kSerialNumberTag); + info.tag = kSerialNumberTag; info.data = outPayload.serialNumber; - outPayload.addOptionalData(info); + outPayload.addCHIPOptionalData(info); } } -CHIP_ERROR writeOptionaData(TLVWriter & writer, vector optionalData) +CHIP_ERROR writeTag(TLVWriter & writer, uint64_t tag, OptionalQRCodeInfo & info) { CHIP_ERROR err = CHIP_NO_ERROR; - for (OptionalQRCodeInfo info : optionalData) + + if (info.type == optionalQRCodeInfoTypeString) { - if (info.type == optionalQRCodeInfoTypeString) - { - err = writer.PutString(info.tag, info.data.c_str()); - SuccessOrExit(err); - } - else if (info.type == optionalQRCodeInfoTypeInt) - { - err = writer.Put(info.tag, static_cast(info.integer)); - SuccessOrExit(err); - } + err = writer.PutString(tag, info.data.c_str()); } -exit: + else if (info.type == optionalQRCodeInfoTypeInt) + { + err = writer.Put(tag, static_cast(info.integer)); + } + return err; } @@ -123,19 +120,35 @@ CHIP_ERROR generateTLVFromOptionalData(SetupPayload & outPayload, uint8_t * tlvD TLVWriter rootWriter; rootWriter.Init(tlvDataStart, maxLen); + rootWriter.ImplicitProfileId = chip::Profiles::kChipProfile_ServiceProvisioning; - TLVWriter innerStructureWritter; + // The cost (in bytes) of the top-level container is amortized as soon as there is at least 4 optionals elements. + if (optionalData.size() >= 4) + { - err = rootWriter.OpenContainer(ProfileTag(2, 1), kTLVType_Structure, - innerStructureWritter); // TODO: Remove outer nest of QR code TLV encoding #728 - SuccessOrExit(err); + TLVWriter innerStructureWriter; - err = writeOptionaData(innerStructureWritter, optionalData); - SuccessOrExit(err); + err = rootWriter.OpenContainer(ProfileTag(rootWriter.ImplicitProfileId, kTag_QRCodeExensionDescriptor), kTLVType_Structure, + innerStructureWriter); + SuccessOrExit(err); - err = rootWriter.CloseContainer(innerStructureWritter); - SuccessOrExit(err); + for (OptionalQRCodeInfo info : optionalData) + { + err = writeTag(innerStructureWriter, ContextTag(info.tag), info); + SuccessOrExit(err); + } + err = rootWriter.CloseContainer(innerStructureWriter); + SuccessOrExit(err); + } + else + { + for (OptionalQRCodeInfo info : optionalData) + { + err = writeTag(rootWriter, ProfileTag(rootWriter.ImplicitProfileId, info.tag), info); + SuccessOrExit(err); + } + } err = rootWriter.Finalize(); SuccessOrExit(err); @@ -157,57 +170,11 @@ static CHIP_ERROR generateBitSet(SetupPayload & payload, uint8_t * bits, uint8_t err = populateBits(bits, offset, payload.rendezvousInformation, kRendezvousInfoFieldLengthInBits, kTotalPayloadDataSizeInBits); err = populateBits(bits, offset, payload.discriminator, kPayloadDiscriminatorFieldLengthInBits, kTotalPayloadDataSizeInBits); err = populateBits(bits, offset, payload.setUpPINCode, kSetupPINCodeFieldLengthInBits, kTotalPayloadDataSizeInBits); - err = populateBits(bits, offset, 0, kReservedFieldLengthInBits, kTotalPayloadDataSizeInBits); + err = populateBits(bits, offset, 0, kPaddingFieldLengthInBits, kTotalPayloadDataSizeInBits); err = populateTLVBits(bits, offset, tlvDataStart, tlvDataLengthInBytes, totalPayloadSizeInBits); return err; } -static CHIP_ERROR payloadBinaryRepresentationWithTLV(SetupPayload & setupPayload, string & binaryRepresentation, size_t bitsetSize, - uint8_t * tlvDataStart, size_t tlvDataLengthInBytes) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - string binary; - uint8_t bits[bitsetSize]; - memset(bits, 0, bitsetSize); - err = generateBitSet(setupPayload, bits, tlvDataStart, tlvDataLengthInBytes); - SuccessOrExit(err); - - for (int i = ArraySize(bits) - 1; i >= 0; i--) - { - for (unsigned j = 1 << 7; j != 0;) - { - binary += bits[i] & j ? "1" : "0"; - j >>= 1; - } - } - binaryRepresentation = binary; -exit: - return err; -} - -CHIP_ERROR QRCodeSetupPayloadGenerator::payloadBinaryRepresentation(string & binaryRepresentation) -{ - return payloadBinaryRepresentation(binaryRepresentation, NULL, 0); -} - -CHIP_ERROR QRCodeSetupPayloadGenerator::payloadBinaryRepresentation(string & binaryRepresentation, uint8_t * tlvDataStart, - size_t tlvDataStartSize) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - uint32_t tlvDataLengthInBytes = 0; - - VerifyOrExit(mPayload.isValidQRCodePayload(), err = CHIP_ERROR_INVALID_ARGUMENT); - - err = generateTLVFromOptionalData(mPayload, tlvDataStart, tlvDataStartSize, tlvDataLengthInBytes); - SuccessOrExit(err); - - err = payloadBinaryRepresentationWithTLV(mPayload, binaryRepresentation, kTotalPayloadDataSizeInBytes + tlvDataLengthInBytes, - tlvDataStart, tlvDataLengthInBytes); - SuccessOrExit(err); -exit: - return err; -} - static CHIP_ERROR payloadBase41RepresentationWithTLV(SetupPayload & setupPayload, string & base41Representation, size_t bitsetSize, uint8_t * tlvDataStart, size_t tlvDataLengthInBytes) { diff --git a/src/setup_payload/QRCodeSetupPayloadGenerator.h b/src/setup_payload/QRCodeSetupPayloadGenerator.h index 94e65675904fdc..9007487c59d1f7 100644 --- a/src/setup_payload/QRCodeSetupPayloadGenerator.h +++ b/src/setup_payload/QRCodeSetupPayloadGenerator.h @@ -22,9 +22,9 @@ * * The encoding of the binary data to a base41 string is as follows: * - Every 2 bytes (16 bits) of binary source data are encoded to 3 - * characters of the Base-45 alphabet. + * characters of the Base-41 alphabet. * - If an odd number of bytes are to be encoded, the remaining - * single byte is encoded to 2 characters of the Base-45 alphabet. + * single byte is encoded to 2 characters of the Base-41 alphabet. */ #include "SetupPayload.h" @@ -83,10 +83,6 @@ class QRCodeSetupPayloadGenerator * producing the requested string. */ CHIP_ERROR payloadBase41Representation(string & base41Representation, uint8_t * tlvDataStart, size_t tlvDataStartSize); - - // These methods are only used for testing purposes. - CHIP_ERROR payloadBinaryRepresentation(string & binaryRepresentation); - CHIP_ERROR payloadBinaryRepresentation(string & binaryRepresentation, uint8_t * tlvDataStart, size_t tlvDataStartSize); }; }; // namespace chip diff --git a/src/setup_payload/QRCodeSetupPayloadParser.cpp b/src/setup_payload/QRCodeSetupPayloadParser.cpp index f4bab1652a156a..b46b52ff1af49e 100644 --- a/src/setup_payload/QRCodeSetupPayloadParser.cpp +++ b/src/setup_payload/QRCodeSetupPayloadParser.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #include #include @@ -48,8 +50,8 @@ static CHIP_ERROR readBits(vector buf, int & index, uint64_t & dest, si dest = 0; if (index + numberOfBitsToRead > buf.size() * 8 || numberOfBitsToRead > sizeof(uint64_t) * 8) { - fprintf(stderr, "Error parsing QR code. startIndex %d numberOfBitsToLoad %zu buf_len %zu ", index, numberOfBitsToRead, - buf.size()); + ChipLogError(SetupPayload, "Error parsing QR code. startIndex %d numberOfBitsToLoad %zu buf_len %zu ", index, + numberOfBitsToRead, buf.size()); return CHIP_ERROR_INVALID_ARGUMENT; } @@ -84,80 +86,109 @@ static CHIP_ERROR openTLVContainer(TLVReader & reader, TLVType type, uint64_t ta static CHIP_ERROR retrieveStringOptionalInfo(TLVReader & reader, OptionalQRCodeInfo & info) { CHIP_ERROR err = CHIP_NO_ERROR; + uint64_t tag = reader.GetTag(); uint32_t valLength = reader.GetLength(); - char * val = new char[valLength + 1]; - err = reader.GetString(val, valLength + 1); + unique_ptr val(new char[valLength + 1]); + err = reader.GetString(val.get(), valLength + 1); + SuccessOrExit(err); + + VerifyOrExit(IsContextTag(tag) == true || IsProfileTag(tag) == true, err = CHIP_ERROR_INVALID_TLV_TAG); info.type = optionalQRCodeInfoTypeString; - info.tag = reader.GetTag(); - info.data = string(val); + info.tag = (uint8_t) TagNumFromTag(tag); + info.data = string(val.get()); + exit: - delete[] val; return err; } static CHIP_ERROR retrieveIntegerOptionalInfo(TLVReader & reader, OptionalQRCodeInfo & info) { CHIP_ERROR err = CHIP_NO_ERROR; + uint64_t tag = reader.GetTag(); int64_t storedInteger; err = reader.Get(storedInteger); - if (err != CHIP_NO_ERROR) - { - return err; - } + SuccessOrExit(err); + VerifyOrExit(IsContextTag(tag) == true || IsProfileTag(tag) == true, err = CHIP_ERROR_INVALID_TLV_TAG); info.type = optionalQRCodeInfoTypeInt; - info.tag = reader.GetTag(); + info.tag = (uint8_t) TagNumFromTag(tag); info.integer = storedInteger; +exit: return err; } static void populatePayloadTLVField(SetupPayload & outPayload, OptionalQRCodeInfo info) { - if (info.tag == ContextTag(kSerialNumberTag)) + if (info.tag == kSerialNumberTag) { outPayload.serialNumber = info.data; } else { - outPayload.addOptionalData(info); + outPayload.addVendorOptionalData(info); } } +static CHIP_ERROR retrieveOptionalInfos(SetupPayload & outPayload, TLVReader & reader) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TLVType type; + while (err == CHIP_NO_ERROR) + { + OptionalQRCodeInfo info; + + type = reader.GetType(); + if (type != kTLVType_UTF8String && type != kTLVType_SignedInteger) + { + err = reader.Next(); + continue; + } + if (type == kTLVType_UTF8String) + { + err = retrieveStringOptionalInfo(reader, info); + } + else if (type == kTLVType_SignedInteger) + { + err = retrieveIntegerOptionalInfo(reader, info); + } + SuccessOrExit(err); + + populatePayloadTLVField(outPayload, info); + err = reader.Next(); + } + if (err == CHIP_END_OF_TLV) + { + err = CHIP_NO_ERROR; + } + +exit: + return err; +} + static CHIP_ERROR parseTLVFields(SetupPayload & outPayload, uint8_t * tlvDataStart, uint32_t tlvDataLengthInBytes) { CHIP_ERROR err = CHIP_NO_ERROR; TLVReader rootReader; rootReader.Init(tlvDataStart, tlvDataLengthInBytes); - rootReader.ImplicitProfileId = outPayload.productID; + rootReader.ImplicitProfileId = chip::Profiles::kChipProfile_ServiceProvisioning; err = rootReader.Next(); SuccessOrExit(err); + + if (rootReader.GetType() == kTLVType_Structure) { TLVReader innerStructureReader; - err = openTLVContainer(rootReader, kTLVType_Structure, ProfileTag(2, 1), innerStructureReader); + err = openTLVContainer(rootReader, kTLVType_Structure, + ProfileTag(rootReader.ImplicitProfileId, kTag_QRCodeExensionDescriptor), innerStructureReader); SuccessOrExit(err); err = innerStructureReader.Next(); - while (err == CHIP_NO_ERROR) - { - TLVType type = innerStructureReader.GetType(); - OptionalQRCodeInfo info; - if (type != kTLVType_UTF8String && type != kTLVType_SignedInteger) - { - err = innerStructureReader.Next(); - continue; - } - if (type == kTLVType_UTF8String) - { - err = retrieveStringOptionalInfo(innerStructureReader, info); - } - else if (type == kTLVType_SignedInteger) - { - err = retrieveIntegerOptionalInfo(innerStructureReader, info); - } - SuccessOrExit(err); - populatePayloadTLVField(outPayload, info); - err = innerStructureReader.Next(); - } + SuccessOrExit(err); + err = retrieveOptionalInfos(outPayload, innerStructureReader); + } + else + { + err = retrieveOptionalInfos(outPayload, rootReader); } + if (err == CHIP_END_OF_TLV) { err = CHIP_NO_ERROR; @@ -168,13 +199,14 @@ static CHIP_ERROR parseTLVFields(SetupPayload & outPayload, uint8_t * tlvDataSta static CHIP_ERROR populateTLV(SetupPayload & outPayload, const vector & buf, int & index) { - if (buf.size() * 8 == (uint) index) - { - return CHIP_NO_ERROR; - } + CHIP_ERROR err = CHIP_NO_ERROR; size_t bitsLeftToRead = (buf.size() * 8) - index; size_t tlvBytesLength = ceil(double(bitsLeftToRead) / 8); - uint8_t * tlvArray = new uint8_t[tlvBytesLength]; + unique_ptr tlvArray; + + SuccessOrExit(tlvBytesLength == 0); + + tlvArray = unique_ptr(new uint8_t[tlvBytesLength]); for (size_t i = 0; i < tlvBytesLength; i++) { uint64_t dest; @@ -182,7 +214,11 @@ static CHIP_ERROR populateTLV(SetupPayload & outPayload, const vector & tlvArray[i] = static_cast(dest); } - return parseTLVFields(outPayload, tlvArray, tlvBytesLength); + err = parseTLVFields(outPayload, tlvArray.get(), tlvBytesLength); + SuccessOrExit(err); + +exit: + return err; } static string extractPayload(string inString) @@ -267,7 +303,7 @@ CHIP_ERROR QRCodeSetupPayloadParser::populatePayload(SetupPayload & outPayload) SuccessOrExit(err); outPayload.setUpPINCode = dest; - err = readBits(buf, indexToReadFrom, dest, kReservedFieldLengthInBits); + err = readBits(buf, indexToReadFrom, dest, kPaddingFieldLengthInBits); SuccessOrExit(err); err = populateTLV(outPayload, buf, indexToReadFrom); diff --git a/src/setup_payload/SetupPayload.cpp b/src/setup_payload/SetupPayload.cpp index 3e693d43b0c716..25be09633d764c 100644 --- a/src/setup_payload/SetupPayload.cpp +++ b/src/setup_payload/SetupPayload.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include using namespace chip; @@ -45,14 +46,18 @@ bool SetupPayload::isValidQRCodePayload() { return false; } - if (rendezvousInformation >= 1 << kRendezvousInfoFieldLengthInBits) + + size_t rendezvousInfoAllowedFieldLengthInBits = kRendezvousInfoFieldLengthInBits - kRendezvousInfoReservedFieldLengthInBits; + if (rendezvousInformation >= 1 << rendezvousInfoAllowedFieldLengthInBits) { return false; } + if (discriminator >= 1 << kPayloadDiscriminatorFieldLengthInBits) { return false; } + if (setUpPINCode >= 1 << kSetupPINCodeFieldLengthInBits) { return false; @@ -85,10 +90,22 @@ bool SetupPayload::isValidManualCode() return true; } -CHIP_ERROR SetupPayload::addOptionalData(OptionalQRCodeInfo info) +CHIP_ERROR SetupPayload::addVendorOptionalData(OptionalQRCodeInfo info) { + CHIP_ERROR err = CHIP_NO_ERROR; + VerifyOrExit(info.tag < 1 << kRawVendorTagLengthInBits, err = CHIP_ERROR_INVALID_ARGUMENT); optionalData[info.tag] = info; - return CHIP_NO_ERROR; +exit: + return err; +} + +CHIP_ERROR SetupPayload::addCHIPOptionalData(OptionalQRCodeInfo info) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + VerifyOrExit(info.tag >= 1 << kRawVendorTagLengthInBits, err = CHIP_ERROR_INVALID_ARGUMENT); + optionalData[info.tag] = info; +exit: + return err; } vector SetupPayload::getAllOptionalData() @@ -101,7 +118,7 @@ vector SetupPayload::getAllOptionalData() return returnedOptionalInfo; } -CHIP_ERROR SetupPayload::removeOptionalData(uint64_t tag) +CHIP_ERROR SetupPayload::removeOptionalData(uint8_t tag) { if (optionalData.find(tag) == optionalData.end()) { @@ -111,16 +128,6 @@ CHIP_ERROR SetupPayload::removeOptionalData(uint64_t tag) return CHIP_NO_ERROR; } -CHIP_ERROR VendorTag(uint16_t tagNumber, uint64_t & outVendorTag) -{ - if (tagNumber >= 1 << kRawVendorTagLengthInBits) - { - return CHIP_ERROR_INVALID_ARGUMENT; - } - outVendorTag = ContextTag(tagNumber); - return CHIP_NO_ERROR; -} - bool SetupPayload::operator==(const SetupPayload & input) { return this->version == input.version && this->vendorID == input.vendorID && this->productID == input.productID && diff --git a/src/setup_payload/SetupPayload.h b/src/setup_payload/SetupPayload.h index 4c0e19649d81a5..c16adc420dea3c 100644 --- a/src/setup_payload/SetupPayload.h +++ b/src/setup_payload/SetupPayload.h @@ -41,24 +41,34 @@ const int kVendorIDFieldLengthInBits = 16; const int kProductIDFieldLengthInBits = 16; const int kCustomFlowRequiredFieldLengthInBits = 1; const int kRendezvousInfoFieldLengthInBits = 8; -const int kPayloadDiscriminatorFieldLengthInBits = 8; +const int kPayloadDiscriminatorFieldLengthInBits = 12; const int kManualSetupDiscriminatorFieldLengthInBits = 4; const int kSetupPINCodeFieldLengthInBits = 27; -const int kReservedFieldLengthInBits = 1; +const int kPaddingFieldLengthInBits = 5; -const int kRawVendorTagLengthInBits = 7; -const uint16_t kSerialNumberTag = 128; +const int kRendezvousInfoReservedFieldLengthInBits = 4; +const int kRawVendorTagLengthInBits = 7; +const uint16_t kSerialNumberTag = 128; +const uint32_t kTag_QRCodeExensionDescriptor = 0x00; const int kManualSetupShortCodeCharLength = 10; const int kManualSetupLongCodeCharLength = 20; const int kManualSetupVendorIdCharLength = 5; const int kManualSetupProductIdCharLength = 5; +// clang-format off const int kTotalPayloadDataSizeInBits = - (kVersionFieldLengthInBits + kVendorIDFieldLengthInBits + kProductIDFieldLengthInBits + kCustomFlowRequiredFieldLengthInBits + - kRendezvousInfoFieldLengthInBits + kPayloadDiscriminatorFieldLengthInBits + kSetupPINCodeFieldLengthInBits + - kReservedFieldLengthInBits); -const int kTotalPayloadDataSizeInBytes = (kTotalPayloadDataSizeInBits + 7) / 8; + kVersionFieldLengthInBits + + kVendorIDFieldLengthInBits + + kProductIDFieldLengthInBits + + kCustomFlowRequiredFieldLengthInBits + + kRendezvousInfoFieldLengthInBits + + kPayloadDiscriminatorFieldLengthInBits + + kSetupPINCodeFieldLengthInBits + + kPaddingFieldLengthInBits; +// clang-format on + +const int kTotalPayloadDataSizeInBytes = kTotalPayloadDataSizeInBits / 8; const char * const kQRCodePrefix = "CH:"; @@ -81,21 +91,12 @@ enum optionalQRCodeInfoType **/ struct OptionalQRCodeInfo { - uint64_t tag; + uint8_t tag; enum optionalQRCodeInfoType type; int integer; string data; }; -/** - * @brief A function to retrieve a vendor tag - * @param tagNumber Tag number is 7 bits long - * @param outVendorTag A 64-bit integer representing the tag - * to use in OptionalQRCodeInfo - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ -CHIP_ERROR VendorTag(uint16_t tagNumber, uint64_t & outVendorTag); - class SetupPayload { public: @@ -114,18 +115,24 @@ class SetupPayload **/ vector getAllOptionalData(); - /** @brief A function to add an optional QR Code info object + /** @brief A function to add an optional QR Code info vendor object + * @param info Optional QR code info object to add + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR addVendorOptionalData(OptionalQRCodeInfo info); + + /** @brief A function to add an optional QR Code info CHIP object * @param info Optional QR code info object to add * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise **/ - CHIP_ERROR addOptionalData(OptionalQRCodeInfo info); + CHIP_ERROR addCHIPOptionalData(OptionalQRCodeInfo info); /** @brief A function to remove an optional QR Code info object * @param tag Optional QR code info tag number to remove * @return Returns CHIP_ERROR_KEY_NOT_FOUND if info could not be found in existing optional data structs, * CHIP_NO_ERROR otherwise **/ - CHIP_ERROR removeOptionalData(uint64_t tag); + CHIP_ERROR removeOptionalData(uint8_t tag); // Test that the Setup Payload is within expected value ranges SetupPayload() : diff --git a/src/setup_payload/SetupPayloadHelper.cpp b/src/setup_payload/SetupPayloadHelper.cpp index 34aca86c5dfe34..5fc2f390e62921 100644 --- a/src/setup_payload/SetupPayloadHelper.cpp +++ b/src/setup_payload/SetupPayloadHelper.cpp @@ -22,6 +22,7 @@ #include #include +#include using namespace chip; @@ -105,31 +106,31 @@ static CHIP_ERROR addParameter(SetupPayload & setupPayload, SetupPayloadParamete switch (parameter.key) { case SetupPayloadKey_Version: - printf("Loaded version: %u\n", (uint8_t) parameter.uintValue); + ChipLogDetail(SetupPayload, "Loaded version: %u", (uint8_t) parameter.uintValue); setupPayload.version = (uint8_t) parameter.uintValue; break; case SetupPayloadKey_VendorID: - printf("Loaded vendorID: %u\n", (uint16_t) parameter.uintValue); + ChipLogDetail(SetupPayload, "Loaded vendorID: %u", (uint16_t) parameter.uintValue); setupPayload.vendorID = (uint16_t) parameter.uintValue; break; case SetupPayloadKey_ProductID: - printf("Loaded productID: %u\n", (uint16_t) parameter.uintValue); + ChipLogDetail(SetupPayload, "Loaded productID: %u", (uint16_t) parameter.uintValue); setupPayload.productID = (uint16_t) parameter.uintValue; break; case SetupPayloadKey_RequiresCustomFlowTrue: - printf("Requires custom flow was set to true\n"); + ChipLogDetail(SetupPayload, "Requires custom flow was set to true"); setupPayload.requiresCustomFlow = true; break; case SetupPayloadKey_RendezVousInformation: - printf("Loaded rendezvousInfo: %u\n", (uint16_t) parameter.uintValue); + ChipLogDetail(SetupPayload, "Loaded rendezvousInfo: %u", (uint16_t) parameter.uintValue); setupPayload.rendezvousInformation = (uint16_t) parameter.uintValue; break; case SetupPayloadKey_Discriminator: - printf("Loaded discriminator: %u\n", (uint16_t) parameter.uintValue); + ChipLogDetail(SetupPayload, "Loaded discriminator: %u", (uint16_t) parameter.uintValue); setupPayload.discriminator = (uint16_t) parameter.uintValue; break; case SetupPayloadKey_SetupPINCode: - printf("Loaded setupPinCode: %lu\n", (unsigned long) parameter.uintValue); + ChipLogDetail(SetupPayload, "Loaded setupPinCode: %lu", (unsigned long) parameter.uintValue); setupPayload.setUpPINCode = (uint32_t) parameter.uintValue; break; default: diff --git a/src/setup_payload/tests/Makefile.am b/src/setup_payload/tests/Makefile.am index 2ea880d0a9c25b..66c0d906d17eb8 100644 --- a/src/setup_payload/tests/Makefile.am +++ b/src/setup_payload/tests/Makefile.am @@ -49,8 +49,9 @@ libSetupPayloadTests_a_SOURCES = \ libSetupPayloadTests_adir = $(includedir)/setup_payload dist_libSetupPayloadTests_a_HEADERS = \ - TestQRCode.h \ + TestHelpers.h \ TestManualCode.h \ + TestQRCode.h \ TestQRCodeTLV.h \ $(NULL) @@ -86,7 +87,7 @@ COMMON_LDADD = \ $(SOCKETS_LDFLAGS) $(SOCKETS_LIBS) \ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) \ $(NULL) - + # Test applications that should be run when the 'check' target is run. check_PROGRAMS = \ diff --git a/src/setup_payload/tests/TestHelpers.h b/src/setup_payload/tests/TestHelpers.h new file mode 100644 index 00000000000000..7ddb992fc118ac --- /dev/null +++ b/src/setup_payload/tests/TestHelpers.h @@ -0,0 +1,158 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "Base41.cpp" +#include "QRCodeSetupPayloadGenerator.cpp" +#include "QRCodeSetupPayloadParser.cpp" +#include "SetupPayload.cpp" + +const uint16_t kSmallBufferSizeInBytes = 1; +const uint16_t kDefaultBufferSizeInBytes = 512; + +SetupPayload GetDefaultPayload() +{ + SetupPayload payload; + + payload.version = 5; + payload.vendorID = 12; + payload.productID = 1; + payload.requiresCustomFlow = 0; + payload.rendezvousInformation = 1; + payload.discriminator = 128; + payload.setUpPINCode = 2048; + + return payload; +} + +SetupPayload GetDefaultPayloadWithSerialNumber() +{ + SetupPayload payload = GetDefaultPayload(); + payload.serialNumber = "123456789QWDHANTYUIOP"; + + return payload; +} + +OptionalQRCodeInfo GetOptionalDefaultString() +{ + OptionalQRCodeInfo info; + info.tag = 2; + info.type = optionalQRCodeInfoTypeString; + info.data = "myData"; + + return info; +} + +OptionalQRCodeInfo GetOptionalDefaultInt() +{ + OptionalQRCodeInfo info; + info.tag = 3; + info.type = optionalQRCodeInfoTypeInt; + info.integer = 12; + + return info; +} + +SetupPayload GetDefaultPayloadWithOptionalDefaults() +{ + SetupPayload payload = GetDefaultPayloadWithSerialNumber(); + + OptionalQRCodeInfo stringInfo = GetOptionalDefaultString(); + OptionalQRCodeInfo intInfo = GetOptionalDefaultInt(); + + payload.addVendorOptionalData(stringInfo); + payload.addVendorOptionalData(intInfo); + + return payload; +} + +string toBinaryRepresentation(string base41Result) +{ + // Remove the kQRCodePrefix + base41Result.erase(0, strlen(kQRCodePrefix)); + + // Decode the base41 encoded string + vector buffer; + base41Decode(base41Result, buffer); + + // Convert it to binary + string binaryResult; + for (int i = buffer.size() - 1; i >= 0; i--) + { + binaryResult += bitset<8>(buffer[i]).to_string(); + } + + // Insert spaces after each block + size_t pos = binaryResult.size(); + + pos -= kVersionFieldLengthInBits; + binaryResult.insert(pos, " "); + + pos -= kVendorIDFieldLengthInBits; + binaryResult.insert(pos, " "); + + pos -= kProductIDFieldLengthInBits; + binaryResult.insert(pos, " "); + + pos -= kCustomFlowRequiredFieldLengthInBits; + binaryResult.insert(pos, " "); + + pos -= kRendezvousInfoFieldLengthInBits; + binaryResult.insert(pos, " "); + + pos -= kPayloadDiscriminatorFieldLengthInBits; + binaryResult.insert(pos, " "); + + pos -= kSetupPINCodeFieldLengthInBits; + binaryResult.insert(pos, " "); + + pos -= kPaddingFieldLengthInBits; + binaryResult.insert(pos, " "); + + return binaryResult; +} + +bool CompareBinary(SetupPayload & payload, string & expectedBinary) +{ + QRCodeSetupPayloadGenerator generator(payload); + + string result; + uint8_t optionalInfo[kDefaultBufferSizeInBytes]; + generator.payloadBase41Representation(result, optionalInfo, sizeof(optionalInfo)); + + string resultBinary = toBinaryRepresentation(result); + return (expectedBinary == resultBinary); +} + +bool CompareBinaryLength(SetupPayload & payload, size_t expectedTLVLengthInBytes) +{ + string result; + uint8_t optionalInfo[kDefaultBufferSizeInBytes]; + + SetupPayload basePayload = GetDefaultPayload(); + QRCodeSetupPayloadGenerator baseGenerator(basePayload); + baseGenerator.payloadBase41Representation(result, optionalInfo, sizeof(optionalInfo)); + string baseBinary = toBinaryRepresentation(result); + + QRCodeSetupPayloadGenerator generator(payload); + generator.payloadBase41Representation(result, optionalInfo, sizeof(optionalInfo)); + + string resultBinary = toBinaryRepresentation(result); + size_t resultTLVLengthInBytes = (resultBinary.size() - baseBinary.size()) / 8; + return (expectedTLVLengthInBytes == resultTLVLengthInBytes); +} diff --git a/src/setup_payload/tests/TestManualCode.cpp b/src/setup_payload/tests/TestManualCode.cpp index 73031edbb04079..d378bdadb3fc5c 100644 --- a/src/setup_payload/tests/TestManualCode.cpp +++ b/src/setup_payload/tests/TestManualCode.cpp @@ -24,9 +24,8 @@ #include "TestManualCode.h" -#include -#include #include +#include #include "ManualSetupPayloadGenerator.cpp" #include "ManualSetupPayloadParser.cpp" @@ -35,124 +34,90 @@ #include -#include -#include -#include - using namespace chip; -void TestDecimalRepresentation_PartialPayload(nlTestSuite * inSuite, void * inContext) +bool CheckGenerator(SetupPayload payload, string expectedResult) { - SetupPayload payload; - payload.setUpPINCode = 2345; - payload.discriminator = 13; - - ManualSetupPayloadGenerator generator(payload); string result; - NL_TEST_ASSERT(inSuite, generator.payloadDecimalStringRepresentation(result) == CHIP_NO_ERROR); - string expectedResult = "3489665618"; - expectedResult += Verhoeff10::ComputeCheckChar(expectedResult.c_str()); + ManualSetupPayloadGenerator generator(payload); + generator.payloadDecimalStringRepresentation(result); - bool succeeded = result.compare(expectedResult) == 0; - if (!succeeded) + if (expectedResult.size() != 0) + { + expectedResult += Verhoeff10::ComputeCheckChar(expectedResult.c_str()); + } + + bool same = result.compare(expectedResult) == 0; + if (!same) { - printf("Expected result: %s\n", expectedResult.c_str()); printf("Actual result: %s\n", result.c_str()); + printf("Expected result: %s\n", expectedResult.c_str()); } - NL_TEST_ASSERT(inSuite, succeeded); + + return same; } -void TestDecimalRepresentation_PartialPayload_RequiresCustomFlow(nlTestSuite * inSuite, void * inContext) +SetupPayload GetDefaultPayload() { SetupPayload payload; - payload.setUpPINCode = 2345; - payload.discriminator = 13; - payload.requiresCustomFlow = true; - - ManualSetupPayloadGenerator generator(payload); - string result; - NL_TEST_ASSERT(inSuite, generator.payloadDecimalStringRepresentation(result) == CHIP_NO_ERROR); - string expectedResult = "34896656190000000000"; - expectedResult += Verhoeff10::ComputeCheckChar(expectedResult.c_str()); + payload.setUpPINCode = 1234; + payload.discriminator = 1; - bool succeeded = result.compare(expectedResult) == 0; - if (!succeeded) - { - printf("Expected result: %s\n", expectedResult.c_str()); - printf("Actual result: %s\n", result.c_str()); - } - NL_TEST_ASSERT(inSuite, succeeded); + return payload; } -void TestDecimalRepresentation_FullPayloadWithoutZeros(nlTestSuite * inSuite, void * inContext) +void TestDecimalRepresentation_PartialPayload(nlTestSuite * inSuite, void * inContext) { - SetupPayload payload; - payload.setUpPINCode = 2345; - payload.discriminator = 13; - payload.vendorID = 45367; - payload.productID = 14526; - payload.requiresCustomFlow = true; + SetupPayload payload = GetDefaultPayload(); - ManualSetupPayloadGenerator generator(payload); - string result; - NL_TEST_ASSERT(inSuite, generator.payloadDecimalStringRepresentation(result) == CHIP_NO_ERROR); - string expectedResult = "34896656194536714526"; - expectedResult += Verhoeff10::ComputeCheckChar(expectedResult.c_str()); + string expectedResult = "0000039490"; - bool succeeded = result.compare(expectedResult) == 0; - if (!succeeded) - { - printf("Expected result: %s\n", expectedResult.c_str()); - printf("Actual result: %s\n", result.c_str()); - } - NL_TEST_ASSERT(inSuite, succeeded); + NL_TEST_ASSERT(inSuite, CheckGenerator(payload, expectedResult)); } -void TestDecimalRepresentation_FullPayloadWithoutZeros_DoesNotRequireCustomFlow(nlTestSuite * inSuite, void * inContext) +void TestDecimalRepresentation_PartialPayload_RequiresCustomFlow(nlTestSuite * inSuite, void * inContext) { - SetupPayload payload; - payload.setUpPINCode = 2345; - payload.discriminator = 13; - payload.vendorID = 45367; - payload.productID = 14526; + SetupPayload payload = GetDefaultPayload(); + payload.requiresCustomFlow = true; - ManualSetupPayloadGenerator generator(payload); - string result; - NL_TEST_ASSERT(inSuite, generator.payloadDecimalStringRepresentation(result) == CHIP_NO_ERROR); - string expectedResult = "3489665618"; - expectedResult += Verhoeff10::ComputeCheckChar(expectedResult.c_str()); + string expectedResult = "00000394910000000000"; - bool succeeded = result.compare(expectedResult) == 0; - if (!succeeded) - { - printf("Expected result: %s\n", expectedResult.c_str()); - printf("Actual result: %s\n", result.c_str()); - } - NL_TEST_ASSERT(inSuite, succeeded); + NL_TEST_ASSERT(inSuite, CheckGenerator(payload, expectedResult)); } void TestDecimalRepresentation_FullPayloadWithZeros(nlTestSuite * inSuite, void * inContext) { - SetupPayload payload; - payload.setUpPINCode = 2345; - payload.discriminator = 13; + SetupPayload payload = GetDefaultPayload(); + payload.requiresCustomFlow = true; payload.vendorID = 1; payload.productID = 1; + + string expectedResult = "00000394910000100001"; + + NL_TEST_ASSERT(inSuite, CheckGenerator(payload, expectedResult)); +} + +void TestDecimalRepresentation_FullPayloadWithoutZeros(nlTestSuite * inSuite, void * inContext) +{ + SetupPayload payload = GetDefaultPayload(); payload.requiresCustomFlow = true; + payload.vendorID = 45367; + payload.productID = 14526; - ManualSetupPayloadGenerator generator(payload); - string result; - NL_TEST_ASSERT(inSuite, generator.payloadDecimalStringRepresentation(result) == CHIP_NO_ERROR); - string expectedResult = "34896656190000100001"; - expectedResult += Verhoeff10::ComputeCheckChar(expectedResult.c_str()); + string expectedResult = "00000394914536714526"; - bool succeeded = result.compare(expectedResult) == 0; - if (!succeeded) - { - printf("Expected result: %s\n", expectedResult.c_str()); - printf("Actual result: %s\n", result.c_str()); - } - NL_TEST_ASSERT(inSuite, succeeded); + NL_TEST_ASSERT(inSuite, CheckGenerator(payload, expectedResult)); +} + +void TestDecimalRepresentation_FullPayloadWithoutZeros_DoesNotRequireCustomFlow(nlTestSuite * inSuite, void * inContext) +{ + SetupPayload payload = GetDefaultPayload(); + payload.vendorID = 45367; + payload.productID = 14526; + + string expectedResult = "0000039490"; + + NL_TEST_ASSERT(inSuite, CheckGenerator(payload, expectedResult)); } void TestDecimalRepresentation_AllZeros(nlTestSuite * inSuite, void * inContext) @@ -161,25 +126,15 @@ void TestDecimalRepresentation_AllZeros(nlTestSuite * inSuite, void * inContext) payload.setUpPINCode = 0; payload.discriminator = 0; - ManualSetupPayloadGenerator generator(payload); - string result; - NL_TEST_ASSERT(inSuite, generator.payloadDecimalStringRepresentation(result) == CHIP_ERROR_INVALID_ARGUMENT); string expectedResult = ""; - bool succeeded = result.compare(expectedResult) == 0; - if (!succeeded) - { - printf("Expected result: %s\n", expectedResult.c_str()); - printf("Actual result: %s\n", result.c_str()); - } - NL_TEST_ASSERT(inSuite, succeeded); + NL_TEST_ASSERT(inSuite, CheckGenerator(payload, expectedResult)); } void TestDecimalRepresentation_InvalidPayload(nlTestSuite * inSuite, void * inContext) { - SetupPayload payload; - payload.setUpPINCode = 2345; - payload.discriminator = 18; + SetupPayload payload = GetDefaultPayload(); + payload.discriminator = 16; ManualSetupPayloadGenerator generator(payload); string result; @@ -206,17 +161,19 @@ void assertEmptyPayloadWithError(nlTestSuite * inSuite, CHIP_ERROR actualError, void TestPayloadParser_FullPayload(nlTestSuite * inSuite, void * inContext) { SetupPayload payload; - string decimalString = "34896656194536714526"; + string decimalString; + + decimalString = "00000394914536714526"; decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); CHIP_ERROR err = ManualSetupPayloadParser(decimalString).populatePayload(payload); - assertPayloadValues(inSuite, err, CHIP_NO_ERROR, payload, 2345, 13, 45367, 14526); + assertPayloadValues(inSuite, err, CHIP_NO_ERROR, payload, 1234, 1, 45367, 14526); - decimalString = "41039884090456200032"; + decimalString = "12393051190456200032"; decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); err = ManualSetupPayloadParser(decimalString).populatePayload(payload); assertPayloadValues(inSuite, err, CHIP_NO_ERROR, payload, 38728284, 15, 4562, 32); - decimalString = "02684354590000100001"; + decimalString = "00000000350000100001"; decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); err = ManualSetupPayloadParser(decimalString).populatePayload(payload); assertPayloadValues(inSuite, err, CHIP_NO_ERROR, payload, 1, 1, 1, 1); @@ -224,9 +181,7 @@ void TestPayloadParser_FullPayload(nlTestSuite * inSuite, void * inContext) void TestGenerateAndParser_FullPayload(nlTestSuite * inSuite, void * inContext) { - SetupPayload payload; - payload.setUpPINCode = 2345; - payload.discriminator = 13; + SetupPayload payload = GetDefaultPayload(); payload.vendorID = 1; payload.productID = 1; payload.requiresCustomFlow = true; @@ -237,15 +192,13 @@ void TestGenerateAndParser_FullPayload(nlTestSuite * inSuite, void * inContext) SetupPayload outPayload; CHIP_ERROR err = ManualSetupPayloadParser(result).populatePayload(outPayload); - assertPayloadValues(inSuite, err, CHIP_NO_ERROR, outPayload, 2345, 13, 1, 1); + assertPayloadValues(inSuite, err, CHIP_NO_ERROR, outPayload, payload.setUpPINCode, payload.discriminator, payload.vendorID, + payload.productID); } void TestGenerateAndParser_PartialPayload(nlTestSuite * inSuite, void * inContext) { - SetupPayload payload; - payload.setUpPINCode = 2341; - payload.discriminator = 9; - payload.requiresCustomFlow = false; + SetupPayload payload = GetDefaultPayload(); ManualSetupPayloadGenerator generator(payload); string result; @@ -253,53 +206,54 @@ void TestGenerateAndParser_PartialPayload(nlTestSuite * inSuite, void * inContex SetupPayload outPayload; CHIP_ERROR err = ManualSetupPayloadParser(result).populatePayload(outPayload); - assertPayloadValues(inSuite, err, CHIP_NO_ERROR, outPayload, 2341, 9, 0, 0); + assertPayloadValues(inSuite, err, CHIP_NO_ERROR, outPayload, payload.setUpPINCode, payload.discriminator, payload.vendorID, + payload.productID); } void TestPayloadParser_PartialPayload(nlTestSuite * inSuite, void * inContext) { + CHIP_ERROR err; SetupPayload payload; - string decimalString = "2418402196"; - decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); - NL_TEST_ASSERT(inSuite, decimalString.length() == 11); - CHIP_ERROR err = ManualSetupPayloadParser(decimalString).populatePayload(payload); - assertPayloadValues(inSuite, err, CHIP_NO_ERROR, payload, 1241546, 9, 0, 0); + string decimalString; - decimalString = "3669676692"; + decimalString = "0000039490"; decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); NL_TEST_ASSERT(inSuite, decimalString.length() == 11); err = ManualSetupPayloadParser(decimalString).populatePayload(payload); - assertPayloadValues(inSuite, err, CHIP_NO_ERROR, payload, 90007882, 13, 0, 0); + assertPayloadValues(inSuite, err, CHIP_NO_ERROR, payload, 1234, 1, 0, 0); - decimalString = "0268435458"; + decimalString = "0000000034"; decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); NL_TEST_ASSERT(inSuite, decimalString.length() == 11); err = ManualSetupPayloadParser(decimalString).populatePayload(payload); assertPayloadValues(inSuite, err, CHIP_NO_ERROR, payload, 1, 1, 0, 0); - decimalString = "00000000030000000000"; + decimalString = "00000000330000000000"; decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); NL_TEST_ASSERT(inSuite, decimalString.length() == 21); err = ManualSetupPayloadParser(decimalString).populatePayload(payload); assertPayloadValues(inSuite, err, CHIP_NO_ERROR, payload, 1, 0, 0, 0); // no discriminator (= 0) - decimalString = "0000070548"; + decimalString = "0000000032"; decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); NL_TEST_ASSERT(inSuite, decimalString.length() == 11); - NL_TEST_ASSERT(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload) == CHIP_NO_ERROR); + err = ManualSetupPayloadParser(decimalString).populatePayload(payload); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); // no vid (= 0) - decimalString = "40321242450000014536"; + decimalString = "00000000330000014536"; decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); NL_TEST_ASSERT(inSuite, decimalString.length() == 21); - NL_TEST_ASSERT(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload) == CHIP_NO_ERROR); + err = ManualSetupPayloadParser(decimalString).populatePayload(payload); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); // no pid (= 0) - decimalString = "40321242452645300000"; + decimalString = "00000000332645300000"; decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); NL_TEST_ASSERT(inSuite, decimalString.length() == 21); - NL_TEST_ASSERT(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload) == CHIP_NO_ERROR); + err = ManualSetupPayloadParser(decimalString).populatePayload(payload); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); } void TestExtractBits(nlTestSuite * inSuite, void * inContext) @@ -322,9 +276,10 @@ void TestExtractBits(nlTestSuite * inSuite, void * inContext) void TestPayloadParser_InvalidEntry(nlTestSuite * inSuite, void * inContext) { SetupPayload payload; + string decimalString; // Empty input - string decimalString = ""; + decimalString = ""; decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); assertEmptyPayloadWithError(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload), CHIP_ERROR_INVALID_STRING_LENGTH, payload); @@ -359,11 +314,10 @@ void TestPayloadParser_InvalidEntry(nlTestSuite * inSuite, void * inContext) assertEmptyPayloadWithError(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload), CHIP_ERROR_INVALID_STRING_LENGTH, payload); // no pin code (= 0) - decimalString = "3221225472"; + decimalString = "0000000016"; decimalString += Verhoeff10::ComputeCheckChar(decimalString.c_str()); assertEmptyPayloadWithError(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload), CHIP_ERROR_INVALID_ARGUMENT, payload); - // wrong check digit decimalString = "02684354589"; assertEmptyPayloadWithError(inSuite, ManualSetupPayloadParser(decimalString).populatePayload(payload), @@ -535,7 +489,6 @@ static const nlTest sTests[] = NL_TEST_DEF("Generate Full Payload and Parse it", TestGenerateAndParser_FullPayload), NL_TEST_DEF("Generate Partial Payload and Parse it", TestGenerateAndParser_PartialPayload), - NL_TEST_SENTINEL() }; // clang-format on diff --git a/src/setup_payload/tests/TestQRCode.cpp b/src/setup_payload/tests/TestQRCode.cpp index bd338c8067b50e..0b5d4fe88b2109 100644 --- a/src/setup_payload/tests/TestQRCode.cpp +++ b/src/setup_payload/tests/TestQRCode.cpp @@ -23,56 +23,26 @@ */ #include "TestQRCode.h" +#include "TestHelpers.h" + #include #include #include -#include "Base41.cpp" -#include "QRCodeSetupPayloadGenerator.cpp" -#include "QRCodeSetupPayloadParser.cpp" -#include "SetupPayload.cpp" - -#include -#include -#include -#include - using namespace chip; using namespace std; void TestPayloadByteArrayRep(nlTestSuite * inSuite, void * inContext) { - SetupPayload payload; - - payload.version = 5; - payload.vendorID = 12; - payload.productID = 1; - payload.requiresCustomFlow = 0; - payload.rendezvousInformation = 1; - payload.discriminator = 128; - payload.setUpPINCode = 2048; - - QRCodeSetupPayloadGenerator generator(payload); - string result; - CHIP_ERROR err = generator.payloadBinaryRepresentation(result); - bool didSucceed = err == CHIP_NO_ERROR; - NL_TEST_ASSERT(inSuite, didSucceed == true); + SetupPayload payload = GetDefaultPayload(); - string expected = "00000000000000001000000000001000000000000001000000000000000010000000000001100101"; - NL_TEST_ASSERT(inSuite, result == expected); + string expected = " 00000 000000000000000100000000000 000010000000 00000001 0 0000000000000001 0000000000001100 101"; + NL_TEST_ASSERT(inSuite, CompareBinary(payload, expected)); } void TestPayloadBase41Rep(nlTestSuite * inSuite, void * inContext) { - SetupPayload payload; - - payload.version = 5; - payload.vendorID = 12; - payload.productID = 1; - payload.requiresCustomFlow = 0; - payload.rendezvousInformation = 1; - payload.discriminator = 128; - payload.setUpPINCode = 2048; + SetupPayload payload = GetDefaultPayload(); QRCodeSetupPayloadGenerator generator(payload); string result; @@ -80,7 +50,7 @@ void TestPayloadBase41Rep(nlTestSuite * inSuite, void * inContext) bool didSucceed = err == CHIP_NO_ERROR; NL_TEST_ASSERT(inSuite, didSucceed == true); - string expected = "CH:J20800G00HKJ000"; + string expected = "CH:J20800G008008000"; NL_TEST_ASSERT(inSuite, result == expected); } @@ -88,14 +58,41 @@ void TestBase41(nlTestSuite * inSuite, void * inContext) { uint8_t input[] = { 10, 10, 10 }; + // basic stuff NL_TEST_ASSERT(inSuite, base41Encode(input, 0).compare("") == 0); - - NL_TEST_ASSERT(inSuite, base41Encode(input, 1).compare("A0") == 0); - + NL_TEST_ASSERT(inSuite, base41Encode(input, 1).compare("A") == 0); NL_TEST_ASSERT(inSuite, base41Encode(input, 2).compare("SL1") == 0); - - NL_TEST_ASSERT(inSuite, base41Encode(input, 3).compare("SL1A0") == 0); - + NL_TEST_ASSERT(inSuite, base41Encode(input, 3).compare("SL1A") == 0); + + // test single odd byte corner conditions + input[2] = 0; + NL_TEST_ASSERT(inSuite, base41Encode(input, 3).compare("SL10") == 0); + input[2] = 40; + NL_TEST_ASSERT(inSuite, base41Encode(input, 3).compare("SL1.") == 0); + input[2] = 41; + NL_TEST_ASSERT(inSuite, base41Encode(input, 3).compare("SL101") == 0); + input[2] = 255; + NL_TEST_ASSERT(inSuite, base41Encode(input, 3).compare("SL196") == 0); + + // testing optimized encoding + // verify that we can't optimize a low value, need 3 chars + input[0] = 255; + input[1] = 0; + NL_TEST_ASSERT(inSuite, base41Encode(input, 2).compare("960") == 0); + // smallest optimized encoding, 256 + input[0] = 256 % 256; + input[1] = 256 / 256; + NL_TEST_ASSERT(inSuite, base41Encode(input, 2).compare("A6") == 0); + // largest optimizated encoding value + input[0] = ((kRadix * kRadix) - 1) % 256; + input[1] = ((kRadix * kRadix) - 1) / 256; + NL_TEST_ASSERT(inSuite, base41Encode(input, 2).compare("..") == 0); + // can't optimize + input[0] = ((kRadix * kRadix)) % 256; + input[1] = ((kRadix * kRadix)) / 256; + NL_TEST_ASSERT(inSuite, base41Encode(input, 2).compare("001") == 0); + + // fun with strings NL_TEST_ASSERT(inSuite, base41Encode((uint8_t *) "Hello World!", sizeof("Hello World!") - 1).compare("GHF.KGL+48-G5LGK35") == 0); @@ -111,13 +108,16 @@ void TestBase41(nlTestSuite * inSuite, void * inContext) // short input NL_TEST_ASSERT(inSuite, base41Decode("A0", decoded) == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, decoded.size() == 2); + NL_TEST_ASSERT(inSuite, decoded.size() == 1); // empty == empty NL_TEST_ASSERT(inSuite, base41Decode("", decoded) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, decoded.size() == 0); - // too short - NL_TEST_ASSERT(inSuite, base41Decode("A", decoded) == CHIP_ERROR_INVALID_MESSAGE_LENGTH); + + // single base41 means one byte of output + NL_TEST_ASSERT(inSuite, base41Decode("A", decoded) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, decoded.size() == 1); + NL_TEST_ASSERT(inSuite, decoded[0] == 10); // outside valid chars NL_TEST_ASSERT(inSuite, base41Decode("0\001", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE); @@ -139,6 +139,14 @@ void TestBase41(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, base41Decode("=0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE); NL_TEST_ASSERT(inSuite, base41Decode(">0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE); NL_TEST_ASSERT(inSuite, base41Decode("@0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE); + + // odd byte(s) cases + NL_TEST_ASSERT(inSuite, base41Decode("96", decoded) == CHIP_NO_ERROR); // this is 255 + NL_TEST_ASSERT(inSuite, decoded.size() == 1 && decoded[0] == 255); + NL_TEST_ASSERT(inSuite, base41Decode("A6", decoded) == CHIP_NO_ERROR); // this is 256, needs 2 output bytes + NL_TEST_ASSERT(inSuite, decoded.size() == 2 && decoded[0] + decoded[1] * 256 == 256); + NL_TEST_ASSERT(inSuite, base41Decode("..", decoded) == CHIP_NO_ERROR); // this is (41*41)-1, or 1680, needs 2 output bytes + NL_TEST_ASSERT(inSuite, decoded.size() == 2 && decoded[0] + decoded[1] * 256 == (kRadix * kRadix) - 1); } void TestBitsetLen(nlTestSuite * inSuite, void * inContext) @@ -148,35 +156,32 @@ void TestBitsetLen(nlTestSuite * inSuite, void * inContext) void TestSetupPayloadVerify(nlTestSuite * inSuite, void * inContext) { - SetupPayload payload; - - payload.version = 5; - payload.vendorID = 12; - payload.productID = 1; - payload.requiresCustomFlow = 0; - payload.rendezvousInformation = 1; - payload.discriminator = 128; - payload.setUpPINCode = 2048; + SetupPayload payload = GetDefaultPayload(); NL_TEST_ASSERT(inSuite, payload.isValidQRCodePayload() == true); // test invalid version SetupPayload test_payload = payload; - test_payload.version = 2 << kVersionFieldLengthInBits; + test_payload.version = 1 << kVersionFieldLengthInBits; NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false); // test invalid rendezvousInformation test_payload = payload; - test_payload.rendezvousInformation = 512; + test_payload.rendezvousInformation = 1 << kRendezvousInfoFieldLengthInBits; + NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false); + + // test invalid rendezvousInformation + test_payload = payload; + test_payload.rendezvousInformation = 1 << (kRendezvousInfoFieldLengthInBits - kRendezvousInfoReservedFieldLengthInBits); NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false); // test invalid discriminator test_payload = payload; - test_payload.discriminator = 2 << kPayloadDiscriminatorFieldLengthInBits; + test_payload.discriminator = 1 << kPayloadDiscriminatorFieldLengthInBits; NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false); // test invalid stetup PIN test_payload = payload; - test_payload.setUpPINCode = 2 << kSetupPINCodeFieldLengthInBits; + test_payload.setUpPINCode = 1 << kSetupPINCodeFieldLengthInBits; NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false); } @@ -205,24 +210,8 @@ void TestInvalidQRCodePayload_WrongLength(nlTestSuite * inSuite, void * inContex void TestPayloadEquality(nlTestSuite * inSuite, void * inContext) { - SetupPayload payload; - - payload.version = 5; - payload.vendorID = 12; - payload.productID = 1; - payload.requiresCustomFlow = 0; - payload.rendezvousInformation = 1; - payload.discriminator = 128; - payload.setUpPINCode = 2048; - - SetupPayload equalPayload; - equalPayload.version = 5; - equalPayload.vendorID = 12; - equalPayload.productID = 1; - equalPayload.requiresCustomFlow = 0; - equalPayload.rendezvousInformation = 1; - equalPayload.discriminator = 128; - equalPayload.setUpPINCode = 2048; + SetupPayload payload = GetDefaultPayload(); + SetupPayload equalPayload = GetDefaultPayload(); bool result = payload == equalPayload; NL_TEST_ASSERT(inSuite, result == true); @@ -230,24 +219,11 @@ void TestPayloadEquality(nlTestSuite * inSuite, void * inContext) void TestPayloadInEquality(nlTestSuite * inSuite, void * inContext) { - SetupPayload payload; + SetupPayload payload = GetDefaultPayload(); - payload.version = 5; - payload.vendorID = 12; - payload.productID = 1; - payload.requiresCustomFlow = 0; - payload.rendezvousInformation = 1; - payload.discriminator = 128; - payload.setUpPINCode = 2048; - - SetupPayload unequalPayload; - unequalPayload.version = 5; - unequalPayload.vendorID = 12; - unequalPayload.productID = 1; - unequalPayload.requiresCustomFlow = 0; - unequalPayload.rendezvousInformation = 1; - unequalPayload.discriminator = 28; - unequalPayload.setUpPINCode = 121233; + SetupPayload unequalPayload = GetDefaultPayload(); + unequalPayload.discriminator = 28; + unequalPayload.setUpPINCode = 121233; bool result = payload == unequalPayload; NL_TEST_ASSERT(inSuite, result == false); @@ -255,14 +231,7 @@ void TestPayloadInEquality(nlTestSuite * inSuite, void * inContext) void TestQRCodeToPayloadGeneration(nlTestSuite * inSuite, void * inContext) { - SetupPayload payload; - payload.version = 3; - payload.vendorID = 100; - payload.productID = 12; - payload.requiresCustomFlow = 1; - payload.rendezvousInformation = 4; - payload.discriminator = 233; - payload.setUpPINCode = 5221133; + SetupPayload payload = GetDefaultPayload(); QRCodeSetupPayloadGenerator generator(payload); string base41Rep; @@ -311,10 +280,11 @@ void TestExtractPayload(nlTestSuite * inSuite, void * inContext) // clang-format off static const nlTest sTests[] = { + + NL_TEST_DEF("Test Base 41", TestBase41), NL_TEST_DEF("Test Bitset Length", TestBitsetLen), NL_TEST_DEF("Test Payload Byte Array Representation", TestPayloadByteArrayRep), NL_TEST_DEF("Test Payload Base 41 Representation", TestPayloadBase41Rep), - NL_TEST_DEF("Test Payload Base 41", TestBase41), NL_TEST_DEF("Test Setup Payload Verify", TestSetupPayloadVerify), NL_TEST_DEF("Test Payload Equality", TestPayloadEquality), NL_TEST_DEF("Test Payload Inequality", TestPayloadInEquality), diff --git a/src/setup_payload/tests/TestQRCodeTLV.cpp b/src/setup_payload/tests/TestQRCodeTLV.cpp index 9a75b80173c923..54df5b0aab723d 100644 --- a/src/setup_payload/tests/TestQRCodeTLV.cpp +++ b/src/setup_payload/tests/TestQRCodeTLV.cpp @@ -15,86 +15,15 @@ * limitations under the License. */ #include "TestQRCodeTLV.h" - -#include -#include -#include +#include "TestHelpers.h" #include #include #include -#include "Base41.cpp" -#include "QRCodeSetupPayloadGenerator.cpp" -#include "QRCodeSetupPayloadParser.cpp" -#include "SetupPayload.cpp" - using namespace chip; using namespace std; -const uint16_t kDefaultBufferSizeInBytes = 512; - -SetupPayload GetDefaultPayload() -{ - SetupPayload payload; - payload.version = 1; - payload.vendorID = 2; - payload.productID = 3; - payload.requiresCustomFlow = 1; - payload.rendezvousInformation = 1; - payload.discriminator = 5; - payload.setUpPINCode = 13; - - return payload; -} - -SetupPayload GetDefaultPayloadWithSerialNumber() -{ - SetupPayload payload = GetDefaultPayload(); - payload.serialNumber = "123456789QWDHANTYUIOP"; - - return payload; -} - -OptionalQRCodeInfo GetOptionalDefaultString() -{ - uint64_t tag; - VendorTag(2, tag); - - OptionalQRCodeInfo info; - info.tag = tag; - info.type = optionalQRCodeInfoTypeString; - info.data = "myData"; - - return info; -} - -OptionalQRCodeInfo GetOptionalDefaultInt() -{ - uint64_t tag; - VendorTag(3, tag); - - OptionalQRCodeInfo info; - info.tag = tag; - info.type = optionalQRCodeInfoTypeInt; - info.integer = 12; - - return info; -} - -SetupPayload GetDefaultPayloadWithOptionalDefaults() -{ - SetupPayload payload = GetDefaultPayloadWithSerialNumber(); - - OptionalQRCodeInfo stringInfo = GetOptionalDefaultString(); - OptionalQRCodeInfo intInfo = GetOptionalDefaultInt(); - - payload.addOptionalData(stringInfo); - payload.addOptionalData(intInfo); - - return payload; -} - void ComparePayloads(nlTestSuite * inSuite, void * inContext, SetupPayload & inPayload, SetupPayload & outPayload) { NL_TEST_ASSERT(inSuite, inPayload.version == outPayload.version); @@ -118,14 +47,59 @@ void ComparePayloads(nlTestSuite * inSuite, void * inContext, SetupPayload & inP NL_TEST_ASSERT(inSuite, in[i].data.compare(out[i].data) == 0); } } +void CompareWriteRead(nlTestSuite * inSuite, void * inContext, SetupPayload & inPayload) +{ + SetupPayload outPayload; -void TestVendorTag(nlTestSuite * inSuite, void * inContext) + QRCodeSetupPayloadGenerator generator(inPayload); + string result; + uint8_t optionalInfo[kDefaultBufferSizeInBytes]; + CHIP_ERROR err = generator.payloadBase41Representation(result, optionalInfo, sizeof(optionalInfo)); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + QRCodeSetupPayloadParser parser = QRCodeSetupPayloadParser(result); + err = parser.populatePayload(outPayload); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + ComparePayloads(inSuite, inContext, inPayload, outPayload); +} + +void TestOptionalTagValues(nlTestSuite * inSuite, void * inContext) { - uint64_t tag; - NL_TEST_ASSERT(inSuite, VendorTag(128, tag) == CHIP_ERROR_INVALID_ARGUMENT); - NL_TEST_ASSERT(inSuite, VendorTag(255, tag) == CHIP_ERROR_INVALID_ARGUMENT); - NL_TEST_ASSERT(inSuite, VendorTag(127, tag) == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, tag == ContextTag(127)); + SetupPayload payload = GetDefaultPayload(); + OptionalQRCodeInfo stringInfo = GetOptionalDefaultString(); + CHIP_ERROR err; + + err = payload.addVendorOptionalData(stringInfo); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + stringInfo.tag = 0; + err = payload.addVendorOptionalData(stringInfo); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + stringInfo.tag = 128; + err = payload.addVendorOptionalData(stringInfo); + NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INVALID_ARGUMENT); + + stringInfo.tag = 255; + err = payload.addVendorOptionalData(stringInfo); + NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INVALID_ARGUMENT); + + stringInfo.tag = 127; + err = payload.addVendorOptionalData(stringInfo); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + stringInfo.tag = 128; + err = payload.addCHIPOptionalData(stringInfo); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + stringInfo.tag = 127; + err = payload.addCHIPOptionalData(stringInfo); + NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INVALID_ARGUMENT); + + stringInfo.tag = 255; + err = payload.addCHIPOptionalData(stringInfo); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); } void TestOptionalDataAddRemove(nlTestSuite * inSuite, void * inContext) @@ -139,13 +113,13 @@ void TestOptionalDataAddRemove(nlTestSuite * inSuite, void * inContext) optionalData = payload.getAllOptionalData(); NL_TEST_ASSERT(inSuite, optionalData.size() == 0); - err = payload.addOptionalData(stringInfo); + err = payload.addVendorOptionalData(stringInfo); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); optionalData = payload.getAllOptionalData(); NL_TEST_ASSERT(inSuite, optionalData.size() == 1); - err = payload.addOptionalData(intInfo); + err = payload.addVendorOptionalData(intInfo); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); optionalData = payload.getAllOptionalData(); @@ -229,22 +203,37 @@ void TestOptionalDataWrite(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); } -void TestOptionalDataRead(nlTestSuite * inSuite, void * inContext) +void TestOptionalDataReadSerial(nlTestSuite * inSuite, void * inContext) { - SetupPayload inPayload = GetDefaultPayloadWithOptionalDefaults(); - SetupPayload outPayload; + SetupPayload inPayload = GetDefaultPayload(); + inPayload.serialNumber = "1"; - QRCodeSetupPayloadGenerator generator(inPayload); - string result; - uint8_t optionalInfo[kDefaultBufferSizeInBytes]; - CHIP_ERROR err = generator.payloadBase41Representation(result, optionalInfo, sizeof(optionalInfo)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + CompareWriteRead(inSuite, inContext, inPayload); +} - QRCodeSetupPayloadParser parser = QRCodeSetupPayloadParser(result); - err = parser.populatePayload(outPayload); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); +void TestOptionalDataReadVendorInt(nlTestSuite * inSuite, void * inContext) +{ + SetupPayload inPayload = GetDefaultPayload(); + OptionalQRCodeInfo intInfo = GetOptionalDefaultInt(); + inPayload.addVendorOptionalData(intInfo); - ComparePayloads(inSuite, inContext, inPayload, outPayload); + CompareWriteRead(inSuite, inContext, inPayload); +} + +void TestOptionalDataReadVendorString(nlTestSuite * inSuite, void * inContext) +{ + SetupPayload inPayload = GetDefaultPayload(); + OptionalQRCodeInfo stringInfo = GetOptionalDefaultString(); + inPayload.addVendorOptionalData(stringInfo); + + CompareWriteRead(inSuite, inContext, inPayload); +} + +void TestOptionalDataRead(nlTestSuite * inSuite, void * inContext) +{ + SetupPayload inPayload = GetDefaultPayloadWithOptionalDefaults(); + + CompareWriteRead(inSuite, inContext, inPayload); } void TestOptionalDataWriteNoBuffer(nlTestSuite * inSuite, void * inContext) @@ -263,11 +252,47 @@ void TestOptionalDataWriteSmallBuffer(nlTestSuite * inSuite, void * inContext) QRCodeSetupPayloadGenerator generator(inPayload); string result; - uint8_t optionalInfo[kTotalPayloadDataSizeInBytes]; + uint8_t optionalInfo[kSmallBufferSizeInBytes]; CHIP_ERROR err = generator.payloadBase41Representation(result, optionalInfo, sizeof(optionalInfo)); NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); } +void TestPayloadBinary(nlTestSuite * inSuite, void * inContext) +{ + SetupPayload payload = GetDefaultPayload(); + NL_TEST_ASSERT(inSuite, CompareBinaryLength(payload, 0)); + + OptionalQRCodeInfo info = GetOptionalDefaultString(); + info.data = "1"; + + payload.addVendorOptionalData(info); + NL_TEST_ASSERT(inSuite, CompareBinaryLength(payload, 5)); + payload.removeOptionalData(info.tag); + + info = GetOptionalDefaultInt(); + info.integer = 1; + + info.tag = 1; + payload.addVendorOptionalData(info); + NL_TEST_ASSERT(inSuite, CompareBinaryLength(payload, 4)); + + info.tag = 2; + payload.addVendorOptionalData(info); + NL_TEST_ASSERT(inSuite, CompareBinaryLength(payload, 8)); + + info.tag = 3; + payload.addVendorOptionalData(info); + NL_TEST_ASSERT(inSuite, CompareBinaryLength(payload, 12)); + + info.tag = 4; + payload.addVendorOptionalData(info); + NL_TEST_ASSERT(inSuite, CompareBinaryLength(payload, 16)); + + info.tag = 5; + payload.addVendorOptionalData(info); + NL_TEST_ASSERT(inSuite, CompareBinaryLength(payload, 19)); +} + // Test Suite /** @@ -276,7 +301,6 @@ void TestOptionalDataWriteSmallBuffer(nlTestSuite * inSuite, void * inContext) // clang-format off static const nlTest sTests[] = { - NL_TEST_DEF("Test Vendor Tag", TestVendorTag), NL_TEST_DEF("Test Simple Write", TestSimpleWrite), NL_TEST_DEF("Test Simple Read", TestSimpleRead), NL_TEST_DEF("Test Optional Add Remove", TestOptionalDataAddRemove), @@ -284,7 +308,12 @@ static const nlTest sTests[] = NL_TEST_DEF("Test Optional Write Serial", TestOptionalDataWriteSerial), NL_TEST_DEF("Test Optional Write No Buffer", TestOptionalDataWriteNoBuffer), NL_TEST_DEF("Test Optional Write Small Buffer", TestOptionalDataWriteSmallBuffer), + NL_TEST_DEF("Test Optional Read Serial", TestOptionalDataReadSerial), + NL_TEST_DEF("Test Optional Read Vendor String", TestOptionalDataReadVendorString), + NL_TEST_DEF("Test Optional Read Vendor Int", TestOptionalDataReadVendorInt), NL_TEST_DEF("Test Optional Read", TestOptionalDataRead), + NL_TEST_DEF("Test Optional Tag Values", TestOptionalTagValues), + NL_TEST_DEF("Test Payload Binary", TestPayloadBinary), NL_TEST_SENTINEL() }; diff --git a/src/system/SystemConfig.h b/src/system/SystemConfig.h index 02c997c247d466..7dbb4444c65f62 100644 --- a/src/system/SystemConfig.h +++ b/src/system/SystemConfig.h @@ -94,25 +94,23 @@ /*--- Sanity check on the build configuration logic. ---*/ -#if !(CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_SOCKETS) -#error "REQUIRED: CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_SOCKETS" -#endif // !(CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_SOCKETS) +#if !(CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK) +#error "REQUIRED: CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK" +#endif // !(CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_USE_NETWORK_FRAMEWORK) #if CHIP_SYSTEM_CONFIG_USE_LWIP && CHIP_SYSTEM_CONFIG_USE_SOCKETS #error "FORBIDDEN: CHIP_SYSTEM_CONFIG_USE_LWIP && CHIP_SYSTEM_CONFIG_USE_SOCKETS" #endif // CHIP_SYSTEM_CONFIG_USE_LWIP && CHIP_SYSTEM_CONFIG_USE_SOCKETS -// clang-format off +#if CHIP_SYSTEM_CONFIG_USE_LWIP && CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#error "FORBIDDEN: CHIP_SYSTEM_CONFIG_USE_LWIP && CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK" +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP && CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK -/** - * @def CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - * - * @brief - * This boolean configuration option is (1) if the obsolescent features of the CHIP System Layer are provided. - */ -#ifndef CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -#define CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES 0 -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES +#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK && CHIP_SYSTEM_CONFIG_USE_SOCKETS +#error "FORBIDDEN: CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK && CHIP_SYSTEM_CONFIG_USE_SOCKETS" +#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK && CHIP_SYSTEM_CONFIG_USE_SOCKETS + +// clang-format off /** * @def CHIP_SYSTEM_CONFIG_TRANSFER_INETLAYER_PROJECT_CONFIGURATION @@ -127,23 +125,6 @@ #if CHIP_SYSTEM_CONFIG_TRANSFER_INETLAYER_PROJECT_CONFIGURATION #ifdef INET_PROJECT_CONFIG_INCLUDE -#if CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -/* - * NOTE WELL: the `INET_LWIP` and `INET_SOCKETS` configuration parameters used to be generated directly by the `autoconf` system. - * Historically, those definitions appeared in `$CHIP/src/include/BuildConfig.h` and the build configuration logic in some systems - * that have `InetProjectConfig.h` may still be relying on these definitions already being present in the logic prior to the - * inclusion of and they must accordingly be defined here to provide for transferring the contents of the - * INET layer configuration properly. - */ -#ifndef INET_LWIP -#define INET_LWIP CHIP_SYSTEM_CONFIG_USE_LWIP -#endif // !defined(INET_LWIP) - -#ifndef INET_SOCKETS -#define INET_SOCKETS CHIP_SYSTEM_CONFIG_USE_SOCKETS -#endif // !defined(INET_SOCKETS) -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - #include INET_PROJECT_CONFIG_INCLUDE #endif // INET_PROJECT_CONFIG_INCLUDE @@ -291,6 +272,26 @@ #endif /* CHIP_SYSTEM_CONFIG_ERROR_TYPE */ +/** + * @def CHIP_SYSTEM_HEADER_RESERVE_SIZE + * + * @brief + * The number of bytes to reserve in a network packet buffer to contain + * the CHIP message and exchange headers. + * + * This number was calculated as follows: + * + * CHIP Crypto Header: + * + * 4 -- Length of encrypted block + * 4 -- Reserve + * 8 -- Initialization Vector + * 8 -- Encryption Tag + */ +#ifndef CHIP_SYSTEM_CRYPTO_HEADER_RESERVE_SIZE +#define CHIP_SYSTEM_CRYPTO_HEADER_RESERVE_SIZE 24 +#endif + /** * @def CHIP_SYSTEM_HEADER_RESERVE_SIZE * @@ -320,7 +321,7 @@ * @note A number of these fields are optional or not presently used. So most headers will be considerably smaller than this. */ #ifndef CHIP_SYSTEM_HEADER_RESERVE_SIZE -#define CHIP_SYSTEM_HEADER_RESERVE_SIZE 38 +#define CHIP_SYSTEM_HEADER_RESERVE_SIZE (38 + CHIP_SYSTEM_CRYPTO_HEADER_RESERVE_SIZE) #endif /* CHIP_SYSTEM_HEADER_RESERVE_SIZE */ /** @@ -567,11 +568,11 @@ struct LwIPEvent; * This configuration is overridden if CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME is set. */ #ifndef CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK #define CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS 1 -#else // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#else // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK #define CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS 0 -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK #endif // CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS /** diff --git a/src/system/SystemLayer.am b/src/system/SystemLayer.am index e09867a7a23ac9..68ff1d89b8db43 100644 --- a/src/system/SystemLayer.am +++ b/src/system/SystemLayer.am @@ -38,16 +38,17 @@ CHIP_BUILD_SYSTEM_LAYER_SOURCE_FILES += @top_builddir@/src/system/SystemFaultInj endif # CHIP_WITH_NLFAULTINJECTION CHIP_BUILD_SYSTEM_LAYER_HEADER_FILES = \ - SystemAlignSize.h \ - SystemClock.h \ - SystemConfig.h \ - SystemError.h \ - SystemEvent.h \ - SystemFaultInjection.h \ - SystemStats.h \ - SystemLayer.h \ - SystemMutex.h \ - SystemObject.h \ - SystemTimer.h \ - SystemPacketBuffer.h \ + @top_builddir@/src/system/SystemAlignSize.h \ + @top_builddir@/src/system/SystemClock.h \ + @top_builddir@/src/system/SystemConfig.h \ + @top_builddir@/src/system/SystemError.h \ + @top_builddir@/src/system/SystemEvent.h \ + @top_builddir@/src/system/SystemFaultInjection.h \ + @top_builddir@/src/system/SystemStats.h \ + @top_builddir@/src/system/SystemLayer.h \ + @top_builddir@/src/system/SystemMutex.h \ + @top_builddir@/src/system/SystemObject.h \ + @top_builddir@/src/system/SystemTimer.h \ + @top_builddir@/src/system/SystemPacketBuffer.h \ + @top_builddir@/src/system/TimeSource.h \ $(NULL) diff --git a/src/system/SystemLayer.cpp b/src/system/SystemLayer.cpp index 28249f2861bdad..5ffbfbcf27e275 100644 --- a/src/system/SystemLayer.cpp +++ b/src/system/SystemLayer.cpp @@ -304,23 +304,6 @@ void Layer::CancelTimer(Layer::TimerCompleteFunct aOnComplete, void * aAppState) } } -#if CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -void Layer::CancelAllMatchingInetTimers(Inet::InetLayer & aInetLayer, void * aOnCompleteInetLayer, void * aAppState) -{ - for (size_t i = 0; i < Timer::sPool.Size(); ++i) - { - Timer * lTimer = Timer::sPool.Get(*this, i); - - if (lTimer != NULL && lTimer->mInetLayer == &aInetLayer && lTimer->mOnCompleteInetLayer == aOnCompleteInetLayer && - lTimer->mAppStateInetLayer == aAppState) - { - lTimer->Cancel(); - break; - } - } -} -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - /** * @brief * Schedules a function with a signature identical to diff --git a/src/system/SystemLayer.h b/src/system/SystemLayer.h index d45eac3c4ba380..fe55c342019dec 100644 --- a/src/system/SystemLayer.h +++ b/src/system/SystemLayer.h @@ -43,18 +43,6 @@ #include #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING -#if CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -namespace chip { -namespace Inet { - -class InetLayer; - -} // namespace Inet -} // namespace chip - -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - namespace chip { namespace System { @@ -219,11 +207,6 @@ class DLL_EXPORT Layer Layer & operator=(const Layer &); friend class Timer; - -#if CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - friend class Inet::InetLayer; - void CancelAllMatchingInetTimers(Inet::InetLayer & aInetLayer, void * aOnCompleteInetLayer, void * aAppState); -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES }; /** diff --git a/src/system/SystemTimer.h b/src/system/SystemTimer.h index b0320b89f824d6..dbb18729c18452 100644 --- a/src/system/SystemTimer.h +++ b/src/system/SystemTimer.h @@ -37,18 +37,6 @@ #include #include -#if CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - -namespace chip { -namespace Inet { - -class InetLayer; - -} // namespace Inet -} // namespace chip - -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - namespace chip { namespace System { @@ -86,24 +74,11 @@ class DLL_EXPORT Timer : public Object static void GetStatistics(chip::System::Stats::count_t & aNumInUse, chip::System::Stats::count_t & aHighWatermark); -#if CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - void AttachInetLayer(Inet::InetLayer & aInetLayer, void * aOnCompleteInetLayer, void * aAppStateInetLayer); - Inet::InetLayer * InetLayer(void) const; - void * OnCompleteInetLayer(void) const; - void * AppStateInetLayer(void) const; -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - private: static ObjectPool sPool; Epoch mAwakenEpoch; -#if CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - Inet::InetLayer * mInetLayer; - void * mOnCompleteInetLayer; - void * mAppStateInetLayer; -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - void HandleComplete(void); Error ScheduleWork(OnCompleteFunct aOnComplete, void * aAppState); @@ -124,30 +99,6 @@ inline void Timer::GetStatistics(chip::System::Stats::count_t & aNumInUse, chip: sPool.GetStatistics(aNumInUse, aHighWatermark); } -#if CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES -inline void Timer::AttachInetLayer(Inet::InetLayer & aInetLayer, void * aOnCompleteInetLayer, void * aAppStateInetLayer) -{ - this->mInetLayer = &aInetLayer; - this->mOnCompleteInetLayer = aOnCompleteInetLayer; - this->mAppStateInetLayer = aAppStateInetLayer; -} - -inline Inet::InetLayer * Timer::InetLayer(void) const -{ - return this->mInetLayer; -} - -inline void * Timer::OnCompleteInetLayer(void) const -{ - return this->mOnCompleteInetLayer; -} - -inline void * Timer::AppStateInetLayer(void) const -{ - return this->mAppStateInetLayer; -} -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - } // namespace System } // namespace chip diff --git a/src/system/TimeSource.h b/src/system/TimeSource.h new file mode 100644 index 00000000000000..bdf867555676b8 --- /dev/null +++ b/src/system/TimeSource.h @@ -0,0 +1,90 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief defines a generic time source interface that uses a real clock + * at runtime but can be substituted by a test one for unit tests. + */ +#ifndef TIME_SOURCE_H_ +#define TIME_SOURCE_H_ + +#include + +namespace chip { +namespace Time { + +enum class Source +{ + kSystem, // System time source + kTest, // Test time source +}; + +/** + * Defines a generic time source within a system. System time and test times + * are available. + */ +template +class TimeSource +{ +public: + /** + * Returns a monotonically increasing time in milliseconds since an arbitrary, platform-defined + * epoch. + * + * Maintains requirements for the System::Platform::Layer clock implementation: + * + * - Return a value that is ever-increasing (i.e. never * wraps) between reboots of the system. + * - The underlying time source is required to tick continuously during any system sleep modes + * such that the values do not entail a restart upon wake. + * - This function is expected to be thread-safe on any platform that employs threading. + */ + uint64_t GetCurrentMonotonicTimeMs(); +}; + +/** + * A system time source, based on the system platform layer. + */ +template <> +class TimeSource +{ +public: + uint64_t GetCurrentMonotonicTimeMs() { return System::Platform::Layer::GetClock_MonotonicMS(); } +}; + +/** + * A test time source. Allows setting the current time. + */ +template <> +class TimeSource +{ +public: + uint64_t GetCurrentMonotonicTimeMs() { return mCurrentTimeMs; } + + void SetCurrentMonotonicTimeMs(uint64_t value) + { + VerifyOrDie(value >= mCurrentTimeMs); // required contract + mCurrentTimeMs = value; + } + +private: + uint64_t mCurrentTimeMs = 0; +}; + +} // namespace Time +} // namespace chip + +#endif // TIME_SOURCE_H_ diff --git a/src/system/tests/Makefile.am b/src/system/tests/Makefile.am index 9e3c29363b9e73..c7447ef7f4e2c9 100644 --- a/src/system/tests/Makefile.am +++ b/src/system/tests/Makefile.am @@ -47,6 +47,7 @@ libSystemLayerTests_a_SOURCES = \ TestSystemObject.cpp \ TestSystemPacketBuffer.cpp \ TestSystemTimer.cpp \ + TestTimeSource.cpp \ $(NULL) libSystemLayerTests_adir = $(includedir)/system @@ -89,23 +90,46 @@ COMMON_LDADD = \ # Test applications that should be run when the 'check' target is run. check_PROGRAMS = \ + $(NULL) + +check_SCRIPTS = \ + $(NULL) + +# The additional environment variables and their values that will be +# made available to all programs and scripts in TESTS. +TESTS_ENVIRONMENT = \ + $(NULL) + +if CHIP_DEVICE_LAYER_TARGET_ESP32 + +check_SCRIPTS += \ + qemu_system_tests.sh \ + $(NULL) + +TESTS_ENVIRONMENT += \ + abs_top_srcdir='$(abs_top_srcdir)' \ + abs_top_builddir='$(abs_top_builddir)' \ + QEMU_TEST_TARGET=libSystemLayerTests.a \ + $(NULL) + +else # CHIP_DEVICE_LAYER_TARGET_ESP32 + +check_PROGRAMS += \ TestSystemErrorStr \ TestSystemObject \ TestSystemPacketBuffer \ TestSystemTimer \ + TestTimeSource \ $(NULL) +endif # CHIP_DEVICE_LAYER_TARGET_ESP32 + # Test applications and scripts that should be built and run when the # 'check' target is run. TESTS = \ $(check_PROGRAMS) \ - $(NULL) - -# The additional environment variables and their values that will be -# made available to all programs and scripts in TESTS. - -TESTS_ENVIRONMENT = \ + $(check_SCRIPTS) \ $(NULL) # Source, compiler, and linker options for test programs. @@ -122,6 +146,9 @@ TestSystemPacketBuffer_LDADD = $(COMMON_LDADD) TestSystemTimer_SOURCES = TestSystemTimerDriver.cpp TestSystemTimer_LDADD = $(COMMON_LDADD) +TestTimeSource_SOURCES = TestTimeSourceDriver.cpp +TestTimeSource_LDADD = $(COMMON_LDADD) + # # Foreign make dependencies # diff --git a/src/system/tests/TestSystemErrorStr.cpp b/src/system/tests/TestSystemErrorStr.cpp index 66ab4233b87f1e..9deeba137f9f9d 100644 --- a/src/system/tests/TestSystemErrorStr.cpp +++ b/src/system/tests/TestSystemErrorStr.cpp @@ -39,7 +39,9 @@ #include #include +#include #include +#include #include @@ -118,3 +120,8 @@ int TestSystemErrorStr(void) return (nlTestRunnerStats(&theSuite)); } + +static void __attribute__((constructor)) TestSystemErrorStrCtor(void) +{ + VerifyOrDie(RegisterUnitTests(&TestSystemErrorStr) == CHIP_NO_ERROR); +} diff --git a/src/system/tests/TestSystemLayer.h b/src/system/tests/TestSystemLayer.h index 43df4cdd3e98da..1736075cbd8c49 100644 --- a/src/system/tests/TestSystemLayer.h +++ b/src/system/tests/TestSystemLayer.h @@ -33,6 +33,7 @@ int TestSystemErrorStr(void); int TestSystemObject(void); int TestSystemPacketBuffer(void); int TestSystemTimer(void); +int TestTimeSource(void); #ifdef __cplusplus } diff --git a/src/system/tests/TestSystemObject.cpp b/src/system/tests/TestSystemObject.cpp index 76f0f57f7bae09..000cc1dbe7150e 100644 --- a/src/system/tests/TestSystemObject.cpp +++ b/src/system/tests/TestSystemObject.cpp @@ -39,11 +39,14 @@ #include +#include #include +#include #include #if CHIP_SYSTEM_CONFIG_USE_LWIP +#include #include #include #endif // CHIP_SYSTEM_CONFIG_USE_LWIP @@ -490,7 +493,7 @@ static int Initialize(void * aContext) TestContext & lContext = *reinterpret_cast(aContext); void * lLayerContext = NULL; -#if CHIP_SYSTEM_CONFIG_USE_LWIP +#if CHIP_SYSTEM_CONFIG_USE_LWIP && LWIP_VERSION_MAJOR <= 2 && LWIP_VERSION_MINOR < 1 static sys_mbox_t * sLwIPEventQueue = NULL; if (sLwIPEventQueue == NULL) @@ -531,3 +534,8 @@ int TestSystemObject(void) return nlTestRunnerStats(&sTestSuite); } + +static void __attribute__((constructor)) TestSystemObjectCtor(void) +{ + VerifyOrDie(chip::RegisterUnitTests(&TestSystemObject) == CHIP_NO_ERROR); +} diff --git a/src/system/tests/TestSystemPacketBuffer.cpp b/src/system/tests/TestSystemPacketBuffer.cpp index 5b96d6b4df57c2..18086742015979 100644 --- a/src/system/tests/TestSystemPacketBuffer.cpp +++ b/src/system/tests/TestSystemPacketBuffer.cpp @@ -34,6 +34,8 @@ #include #include +#include +#include #include #if CHIP_SYSTEM_CONFIG_USE_LWIP @@ -1207,3 +1209,8 @@ int TestSystemPacketBuffer(void) return (nlTestRunnerStats(&theSuite)); } + +static void __attribute__((constructor)) TestSystemPacketBufferCtor(void) +{ + VerifyOrDie(chip::RegisterUnitTests(&TestSystemPacketBuffer) == CHIP_NO_ERROR); +} diff --git a/src/system/tests/TestSystemTimer.cpp b/src/system/tests/TestSystemTimer.cpp index f8a3d0bb3521eb..37d74f65948ee2 100644 --- a/src/system/tests/TestSystemTimer.cpp +++ b/src/system/tests/TestSystemTimer.cpp @@ -33,7 +33,9 @@ #include "TestSystemLayer.h" #include +#include #include +#include #include #include #include @@ -224,11 +226,13 @@ static int TestSetup(void * aContext) void * lLayerContext = NULL; #if CHIP_SYSTEM_CONFIG_USE_LWIP +#if LWIP_VERSION_MAJOR <= 2 && LWIP_VERSION_MINOR < 1 static sys_mbox_t * sLwIPEventQueue = NULL; sys_mbox_new(sLwIPEventQueue, 100); - tcpip_init(NULL, NULL); lLayerContext = &sLwIPEventQueue; +#endif + tcpip_init(NULL, NULL); #endif // CHIP_SYSTEM_CONFIG_USE_LWIP sLayer.Init(lLayerContext); @@ -265,3 +269,8 @@ int TestSystemTimer(void) return nlTestRunnerStats(&kTheSuite); } + +static void __attribute__((constructor)) TestSystemTimerCtor(void) +{ + VerifyOrDie(chip::RegisterUnitTests(&TestSystemTimer) == CHIP_NO_ERROR); +} diff --git a/src/system/tests/TestTimeSource.cpp b/src/system/tests/TestTimeSource.cpp new file mode 100644 index 00000000000000..b1c571ee0fe9d1 --- /dev/null +++ b/src/system/tests/TestTimeSource.cpp @@ -0,0 +1,98 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This is a unit test suite for chip::Time::TimeSource. Tests mainly + * the ability to compile and use the test implementation of the time source. + */ + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +// config +#include + +// module header +#include "TestSystemLayer.h" + +#include +#include +#include +#include +#include + +namespace { + +void TestTimeSourceSetAndGet(nlTestSuite * inSuite, void * inContext) +{ + + chip::Time::TimeSource source; + + NL_TEST_ASSERT(inSuite, source.GetCurrentMonotonicTimeMs() == 0); + + source.SetCurrentMonotonicTimeMs(1234); + NL_TEST_ASSERT(inSuite, source.GetCurrentMonotonicTimeMs() == 1234); +} + +void SystemTimeSourceGet(nlTestSuite * inSuite, void * inContext) +{ + + chip::Time::TimeSource source; + + uint64_t oldValue = source.GetCurrentMonotonicTimeMs(); + + // a basic monotonic test. This is likely to take less than 1ms, so the + // actual test value lies mostly in ensuring things compile. + for (int i = 0; i < 100; i++) + { + uint64_t newValue = source.GetCurrentMonotonicTimeMs(); + NL_TEST_ASSERT(inSuite, newValue >= oldValue); + oldValue = newValue; + } +} + +} // namespace + +/** + * Test Suite. It lists all the test functions. + */ +// clang-format off +static const nlTest sTests[] = +{ + NL_TEST_DEF("TimeSource::SetAndGet", TestTimeSourceSetAndGet), + NL_TEST_DEF("TimeSource::SetAndGet", SystemTimeSourceGet), + NL_TEST_SENTINEL() +}; +// clang-format on + +int TestTimeSource(void) +{ + nlTestSuite theSuite = { + "chip-timesource", &sTests[0], NULL /* setup */, NULL /* teardown */ + }; + + // Run test suit againt one context. + nlTestRunner(&theSuite, NULL /* context */); + + return (nlTestRunnerStats(&theSuite)); +} + +static void __attribute__((constructor)) TestTimeSourceCtor(void) +{ + VerifyOrDie(chip::RegisterUnitTests(&TestTimeSource) == CHIP_NO_ERROR); +} diff --git a/src/inet/tests/TestInetTimerDriver.cpp b/src/system/tests/TestTimeSourceDriver.cpp similarity index 88% rename from src/inet/tests/TestInetTimerDriver.cpp rename to src/system/tests/TestTimeSourceDriver.cpp index 6fff2d9c6fc7fd..b143dbcbd3e30a 100644 --- a/src/inet/tests/TestInetTimerDriver.cpp +++ b/src/system/tests/TestTimeSourceDriver.cpp @@ -18,12 +18,12 @@ /** * @file * This file implements a standalone/native program executable - * test driver for the CHIP Internet (inet) library timer unit + * test driver for the CHIP system layer library timer unit * tests. * */ -#include "TestInetLayer.h" +#include "TestSystemLayer.h" #include @@ -32,5 +32,5 @@ int main(int argc, char * argv[]) // Generate machine-readable, comma-separated value (CSV) output. nlTestSetOutputStyle(OUTPUT_CSV); - return (TestInetTimer()); + return TestTimeSource(); } diff --git a/src/system/tests/qemu_system_tests.sh b/src/system/tests/qemu_system_tests.sh new file mode 120000 index 00000000000000..8fbe617c97a267 --- /dev/null +++ b/src/system/tests/qemu_system_tests.sh @@ -0,0 +1 @@ +../../../scripts/tools/qemu_run_test.sh \ No newline at end of file diff --git a/src/test_driver/esp32/Makefile b/src/test_driver/esp32/Makefile index 5d71484787c1c2..3f2d23d8cfa374 100644 --- a/src/test_driver/esp32/Makefile +++ b/src/test_driver/esp32/Makefile @@ -14,7 +14,7 @@ include $(IDF_PATH)/make/project.mk esp32_elf_builder: all mkdir -p build/chip/ echo $(CC) -L$(PROJECT_PATH)/build/chip/lib -Wl,--whole-archive '$$1' -Wl,--no-whole-archive \ - -lnlunit-test $(LDFLAGS) -o $(PROJECT_PATH)/build/chip-tests.elf -Wl,-Map=$(APP_MAP) > build/chip/esp32_elf_builder.sh + -lnlunit-test $(LDFLAGS) -lnlfaultinjection -o $(PROJECT_PATH)/build/chip-tests.elf -Wl,-Map=$(APP_MAP) > build/chip/esp32_elf_builder.sh echo $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) \ -o $(PROJECT_PATH)/build/chip/chip-tests.bin $(PROJECT_PATH)/build/chip-tests.elf >> build/chip/esp32_elf_builder.sh ln -sf $(PROJECT_PATH)/build/partitions_singleapp.bin $(PROJECT_PATH)/build/chip/partitions_singleapp.bin diff --git a/src/test_driver/esp32/idf.sh b/src/test_driver/esp32/idf.sh index b72c2665230a62..c78a9c1213f57a 100644 --- a/src/test_driver/esp32/idf.sh +++ b/src/test_driver/esp32/idf.sh @@ -20,16 +20,25 @@ # a command presented as arguments # # This file can also be used as an executable -me=${0##*/} -die() { + +error() { echo "$me: *** ERROR: " "${*}" - exit 1 } idf() { - [[ -d $IDF_PATH && -r $IDF_PATH/export.sh ]] || die "can't find IDF's export.sh" - . "$IDF_PATH/export.sh" - "$@" + [[ -d $IDF_PATH && -r $IDF_PATH/export.sh ]] || { + error "can't find IDF's export.sh, please set IDF_PATH" + return 1 + } + ( + . "$IDF_PATH/export.sh" + export IDF_PATH + "$@" + ) } if [[ ${0} == ${BASH_SOURCE[0]} ]]; then + me=${0##*/} idf "${@}" +else + me=idf + [[ $PS1 =~ \[idf\].* ]] || PS1="[idf]$PS1" fi diff --git a/src/transport/Base.h b/src/transport/Base.h new file mode 100644 index 00000000000000..1889a548cdcd58 --- /dev/null +++ b/src/transport/Base.h @@ -0,0 +1,110 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 Defines base properties and constants valid across all transport + * classes (UDP, TCP, BLE, ....) + */ + +#ifndef TRANSPORT_BASE_H_ +#define TRANSPORT_BASE_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace Transport { + +/** + * Transport class base, defining common methods among transports (message + * packing by encoding and decoding headers) and generic message transport + * methods. + */ +class Base : public ReferenceCounted +{ +public: + virtual ~Base() {} + + /** + * Sets the message receive handler and associated argument + * + * @param[in] handler The callback to call when a message is received + * @param[in] param The argument to pass in to the handler function + * + */ + template + void SetMessageReceiveHandler(void (*handler)(const MessageHeader &, const Inet::IPPacketInfo &, System::PacketBuffer *, T *), + T * param) + { + mMessageReceivedArgument = param; + OnMessageReceived = reinterpret_cast(handler); + } + + /** + * @brief Send a message to the specified target. + * + * @details + * This method calls chip::System::PacketBuffer::Free on + * behalf of the caller regardless of the return status. + */ + virtual CHIP_ERROR SendMessage(const MessageHeader & header, const Transport::PeerAddress & address, + System::PacketBuffer * msgBuf) = 0; + + /** + * Get the path that this transport is associated with. + * + * Within a system, only one transport should be associated with a path. + */ + virtual Type GetType() = 0; + +protected: + /** + * Method used by subclasses to notify that a packet has been received after + * any associated headers have been decoded. + */ + void HandleMessageReceived(const MessageHeader & header, const Inet::IPPacketInfo & source, System::PacketBuffer * buffer) + { + if (OnMessageReceived) + { + OnMessageReceived(header, source, buffer, mMessageReceivedArgument); + } + } + + /** + * This function is the application callback that is invoked when a message is received over a + * Chip connection. + * + * @param[in] msgBuf A pointer to the PacketBuffer object holding the message. + */ + typedef void (*MessageReceiveHandler)(const MessageHeader & header, const Inet::IPPacketInfo & source, + System::PacketBuffer * msgBuf, void * param); + + MessageReceiveHandler OnMessageReceived = nullptr; ///< Callback on message receiving + void * mMessageReceivedArgument = nullptr; ///< Argument for callback + + uint8_t mRefCount = 0; +}; + +} // namespace Transport +} // namespace chip + +#endif // TRANSPORT_BASE_H_ diff --git a/src/transport/MessageHeader.cpp b/src/transport/MessageHeader.cpp index 780f18b2056eb0..f55729d341a06a 100644 --- a/src/transport/MessageHeader.cpp +++ b/src/transport/MessageHeader.cpp @@ -31,7 +31,11 @@ * Header format (little endian): * * 16 bit: | VERSION: 4 bit | FLAGS: 4 bit | RESERVED: 8 bit | + * 16 bit: | Secure message type | * 32 bit: | MESSAGE_ID | + * 32 bit: | Secure Session ID | + * 64 bit: | Encryption Initialization Vector (nonce) | + * 64 bit: | Message Authentication Tag | * 64 bit: | SOURCE_NODE_ID (iff source node flag is set) | * 64 bit: | DEST_NODE_ID (iff destination node flag is set) | * @@ -43,7 +47,7 @@ namespace { using namespace chip::Encoding; /// size of the fixed portion of the header -constexpr size_t kFixedHeaderSizeBytes = 6; +constexpr size_t kFixedHeaderSizeBytes = 28; /// size of a serialized node id inside a header constexpr size_t kNodeIdSizeBytes = 8; @@ -90,7 +94,11 @@ CHIP_ERROR MessageHeader::Decode(const uint8_t * data, size_t size, size_t * dec version = ((header & kVersionMask) >> kVersionShift); VerifyOrExit(version == kHeaderVersion, err = CHIP_ERROR_VERSION_MISMATCH); - mMessageId = LittleEndian::Read32(p); + mSecureMsgType = LittleEndian::Read16(p); + mMessageId = LittleEndian::Read32(p); + mSecureSessionID = LittleEndian::Read32(p); + mIV = LittleEndian::Read64(p); + mTag = LittleEndian::Read64(p); assert(p - data == kFixedHeaderSizeBytes); size -= kFixedHeaderSizeBytes; @@ -99,7 +107,7 @@ CHIP_ERROR MessageHeader::Decode(const uint8_t * data, size_t size, size_t * dec { VerifyOrExit(size >= kNodeIdSizeBytes, err = CHIP_ERROR_INVALID_ARGUMENT); mSourceNodeId.SetValue(LittleEndian::Read64(p)); - size -= kFixedHeaderSizeBytes; + size -= kNodeIdSizeBytes; } else { @@ -110,7 +118,7 @@ CHIP_ERROR MessageHeader::Decode(const uint8_t * data, size_t size, size_t * dec { VerifyOrExit(size >= kNodeIdSizeBytes, err = CHIP_ERROR_INVALID_ARGUMENT); mDestinationNodeId.SetValue(LittleEndian::Read64(p)); - size -= kFixedHeaderSizeBytes; + size -= kNodeIdSizeBytes; } else { @@ -124,7 +132,7 @@ CHIP_ERROR MessageHeader::Decode(const uint8_t * data, size_t size, size_t * dec return err; } -CHIP_ERROR MessageHeader::Encode(uint8_t * data, size_t size, size_t * encode_size) +CHIP_ERROR MessageHeader::Encode(uint8_t * data, size_t size, size_t * encode_size) const { CHIP_ERROR err = CHIP_NO_ERROR; uint8_t * p = data; @@ -142,7 +150,11 @@ CHIP_ERROR MessageHeader::Encode(uint8_t * data, size_t size, size_t * encode_si } LittleEndian::Write16(p, header); + LittleEndian::Write16(p, mSecureMsgType); LittleEndian::Write32(p, mMessageId); + LittleEndian::Write32(p, mSecureSessionID); + LittleEndian::Write64(p, mIV); + LittleEndian::Write64(p, mTag); if (mSourceNodeId.HasValue()) { LittleEndian::Write64(p, mSourceNodeId.Value()); diff --git a/src/transport/MessageHeader.h b/src/transport/MessageHeader.h index 9c467bd892575b..b4df39ecbc7ed3 100644 --- a/src/transport/MessageHeader.h +++ b/src/transport/MessageHeader.h @@ -30,6 +30,11 @@ namespace chip { +/// Convenience type to make it clear a number represents a node id. +typedef uint64_t NodeId; + +constexpr NodeId kUndefinedNodeId = 0xFFFFFFFFFFFFFFFFll; + /** Handles encoding/decoding of CHIP message headers */ class MessageHeader { @@ -39,14 +44,14 @@ class MessageHeader * * NOTE: the source node id is optional and may be missing. */ - const Optional & GetSourceNodeId() const { return mSourceNodeId; } + const Optional & GetSourceNodeId() const { return mSourceNodeId; } /** * Gets the destination node id in the current message. * * NOTE: the destination node id is optional and may be missing. */ - const Optional & GetDestinationNodeId() const { return mDestinationNodeId; } + const Optional & GetDestinationNodeId() const { return mDestinationNodeId; } /** * Gets the message id set in the header. @@ -56,6 +61,18 @@ class MessageHeader */ uint32_t GetMessageId() const { return mMessageId; } + /** Get the secure msg type from this header. */ + uint16_t GetSecureMsgType(void) const { return mSecureMsgType; } + + /** Get the Session ID from this header. */ + uint32_t GetSecureSessionID(void) const { return mSecureSessionID; } + + /** Get the initialization vector from this header. */ + uint64_t GetIV(void) const { return mIV; } + + /** Get the message auth tag from this header. */ + uint64_t GetTag(void) const { return mTag; } + /** Set the message id for this header. */ MessageHeader & SetMessageId(uint32_t id) { @@ -65,13 +82,21 @@ class MessageHeader } /** Set the source node id for this header. */ - MessageHeader & SetSourceNodeId(uint64_t id) + MessageHeader & SetSourceNodeId(NodeId id) { mSourceNodeId.SetValue(id); return *this; } + /** Set the source node id for this header. */ + MessageHeader & SetSourceNodeId(Optional id) + { + mSourceNodeId = id; + + return *this; + } + /** Clear the source node id for this header. */ MessageHeader & ClearSourceNodeId() { @@ -81,13 +106,20 @@ class MessageHeader } /** Set the destination node id for this header. */ - MessageHeader & SetDestinationNodeId(uint64_t id) + MessageHeader & SetDestinationNodeId(NodeId id) { mDestinationNodeId.SetValue(id); return *this; } + /** Set the destination node id for this header. */ + MessageHeader & SetDestinationNodeId(Optional id) + { + mDestinationNodeId = id; + return *this; + } + /** Clear the destination node id for this header. */ MessageHeader & ClearDestinationNodeId() { @@ -96,6 +128,34 @@ class MessageHeader return *this; } + /** Set the secure message type for this header. */ + MessageHeader & SetSecureMsgType(uint16_t type) + { + mSecureMsgType = type; + return *this; + } + + /** Set the security session ID for this header. */ + MessageHeader & SetSessionID(uint32_t id) + { + mSecureSessionID = id; + return *this; + } + + /** Set the initialization vector for this header. */ + MessageHeader & SetIV(uint64_t IV) + { + mIV = IV; + return *this; + } + + /** Set the message auth tag for this header. */ + MessageHeader & SetTag(uint64_t tag) + { + mTag = tag; + return *this; + } + /** * A call to `Encode` will require at least this many bytes on the current * object to be successful. @@ -132,7 +192,7 @@ class MessageHeader * Possible failures: * CHIP_ERROR_INVALID_ARGUMENT on insufficient buffer size */ - CHIP_ERROR Encode(uint8_t * data, size_t size, size_t * encode_size); + CHIP_ERROR Encode(uint8_t * data, size_t size, size_t * encode_size) const; private: /// Represents the current encode/decode header version @@ -142,10 +202,22 @@ class MessageHeader uint32_t mMessageId = 0; /// What node the message originated from - Optional mSourceNodeId; + Optional mSourceNodeId; /// Intended recipient of the message. - Optional mDestinationNodeId; + Optional mDestinationNodeId; + + /// Packet type (application data, security control packets, e.g. pairing, configuration, rekey etc) + uint16_t mSecureMsgType = 0; + + /// Security session identifier + uint32_t mSecureSessionID = 0; + + /// Initialization vector used for encryption of the message. + uint64_t mIV = 0; + + /// Message authentication tag generated at encryption of the message. + uint64_t mTag = 0; }; } // namespace chip diff --git a/src/transport/PeerAddress.h b/src/transport/PeerAddress.h new file mode 100644 index 00000000000000..b673565c9f37c4 --- /dev/null +++ b/src/transport/PeerAddress.h @@ -0,0 +1,107 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief + * File contains definitions on how a connection to a peer can be defined. + * + */ + +#ifndef PEER_ADDRESS_H_ +#define PEER_ADDRESS_H_ + +#include + +namespace chip { +namespace Transport { + +/** + * Communication path defines how two peers communicate. + * + * When a peer contacts another peer, it defines how the peers communicate. + * + * Once communication between two peers is established, the same transport + * path should be used: a peer contacting another peer over UDP will receive + * messages back over UDP. A communication channel established over TCP + * will keep the same TCP channel. + * + */ +enum class Type +{ + kUndefined, + kUdp, + // More constants to be added later, such as TCP and BLE +}; + +/** + * Describes how a peer on a CHIP network can be addressed. + */ +class PeerAddress +{ +public: + PeerAddress(const Inet::IPAddress & addr, Type type) : mIPAddress(addr), mTransportType(type) {} + + PeerAddress(PeerAddress &&) = default; + PeerAddress(const PeerAddress &) = default; + PeerAddress & operator=(const PeerAddress &) = default; + PeerAddress & operator=(PeerAddress &&) = default; + + const Inet::IPAddress & GetIPAddress() const { return mIPAddress; } + PeerAddress & SetIPAddress(const Inet::IPAddress & addr) + { + mIPAddress = addr; + return *this; + } + + Type GetTransportType() const { return mTransportType; } + PeerAddress & SetTransportType(Type type) + { + mTransportType = type; + return *this; + } + + uint16_t GetPort() const { return mPort; } + PeerAddress & SetPort(uint16_t port) + { + mPort = port; + return *this; + } + + bool IsInitialized() const { return mTransportType != Type::kUndefined; } + + bool operator==(const PeerAddress & other) + { + return (mTransportType == other.mTransportType) && (mIPAddress == other.mIPAddress) && (mPort == other.mPort); + } + + /****** Factory methods for convenience ******/ + + static PeerAddress Uninitialized() { return PeerAddress(Inet::IPAddress::Any, Type::kUndefined); } + + static PeerAddress UDP(const Inet::IPAddress & addr) { return PeerAddress(addr, Type::kUdp); } + static PeerAddress UDP(const Inet::IPAddress & addr, uint16_t port) { return UDP(addr).SetPort(port); } + +private: + Inet::IPAddress mIPAddress; + Type mTransportType; + uint16_t mPort = CHIP_PORT; ///< Relevant for UDP data sending. +}; + +} // namespace Transport +} // namespace chip + +#endif // PEER_ADDRESS_H_ diff --git a/src/transport/PeerConnectionState.h b/src/transport/PeerConnectionState.h new file mode 100644 index 00000000000000..90b0a83cc82586 --- /dev/null +++ b/src/transport/PeerConnectionState.h @@ -0,0 +1,78 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief Defines state relevant for an active connection to a peer. + */ + +#ifndef PEER_CONNCTION_STATE_H_ +#define PEER_CONNCTION_STATE_H_ + +#include +#include + +namespace chip { +namespace Transport { + +/** + * Defines state of a peer connection at a transport layer. + * + * Information contained within the state: + * - PeerAddress represents how to talk to the peer + * - PeerNodeId is the unique ID of the peer + * - SendMessageIndex is an ever increasing index for sending messages + * - LastActivityTimeMs is a monotonic timestamp of when this connection was + * last used. Inactive connections can expire. + * + * TODO: to add encryption data and any message ACK information + */ +class PeerConnectionState +{ +public: + PeerConnectionState() : mPeerAddress(PeerAddress::Uninitialized()) {} + PeerConnectionState(const PeerAddress & addr) : mPeerAddress(addr) {} + PeerConnectionState(PeerAddress && addr) : mPeerAddress(addr) {} + + PeerConnectionState(PeerConnectionState &&) = default; + PeerConnectionState(const PeerConnectionState &) = default; + PeerConnectionState & operator=(const PeerConnectionState &) = default; + PeerConnectionState & operator=(PeerConnectionState &&) = default; + + const PeerAddress & GetPeerAddress() const { return mPeerAddress; } + PeerAddress & GetPeerAddress() { return mPeerAddress; } + void SetPeerAddress(const PeerAddress & address) { mPeerAddress = address; } + + NodeId GetPeerNodeId() const { return mPeerNodeId; } + void SetPeerNodeId(NodeId peerNodeId) { mPeerNodeId = peerNodeId; } + + uint32_t GetSendMessageIndex() const { return mSendMessageIndex; } + void IncrementSendMessageIndex() { mSendMessageIndex++; } + + uint64_t GetLastActivityTimeMs() const { return mLastActityTimeMs; } + void SetLastActivityTimeMs(uint64_t value) { mLastActityTimeMs = value; } + +private: + PeerAddress mPeerAddress; + NodeId mPeerNodeId = kUndefinedNodeId; + uint32_t mSendMessageIndex = 0; + uint64_t mLastActityTimeMs = 0; +}; + +} // namespace Transport +} // namespace chip + +#endif // PEER_CONNCTION_STATE_H_ diff --git a/src/transport/PeerConnections.cpp b/src/transport/PeerConnections.cpp new file mode 100644 index 00000000000000..896a9e823c341a --- /dev/null +++ b/src/transport/PeerConnections.cpp @@ -0,0 +1,112 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace chip { +namespace Transport { + +CHIP_ERROR PeerConnectionsBase::CreateNewPeerConnectionState(const PeerAddress & address, PeerConnectionState ** state) +{ + CHIP_ERROR err = CHIP_ERROR_NO_MEMORY; + + if (state) + { + *state = nullptr; + } + + for (size_t i = 0; i < mConnectionStateArraySize; i++) + { + if (!mConnectionStateArray[i].GetPeerAddress().IsInitialized()) + { + mConnectionStateArray[i] = PeerConnectionState(address); + mConnectionStateArray[i].SetLastActivityTimeMs(GetCurrentMonotonicTimeMs()); + + if (state) + { + *state = &mConnectionStateArray[i]; + } + + err = CHIP_NO_ERROR; + break; + } + } + + return err; +} + +bool PeerConnectionsBase::FindPeerConnectionState(const PeerAddress & address, PeerConnectionState ** state) +{ + *state = nullptr; + for (size_t i = 0; i < mConnectionStateArraySize; i++) + { + if (mConnectionStateArray[i].GetPeerAddress() == address) + { + *state = &mConnectionStateArray[i]; + break; + } + } + return *state != nullptr; +} + +bool PeerConnectionsBase::FindPeerConnectionState(NodeId nodeId, PeerConnectionState ** state) +{ + *state = nullptr; + for (size_t i = 0; i < mConnectionStateArraySize; i++) + { + if (!mConnectionStateArray[i].GetPeerAddress().IsInitialized()) + { + continue; + } + if (mConnectionStateArray[i].GetPeerNodeId() == nodeId) + { + *state = &mConnectionStateArray[i]; + break; + } + } + return *state != nullptr; +} + +void PeerConnectionsBase::ExpireInactiveConnections(uint64_t maxIdleTimeMs) +{ + const uint64_t currentTime = GetCurrentMonotonicTimeMs(); + + for (size_t i = 0; i < mConnectionStateArraySize; i++) + { + if (!mConnectionStateArray[i].GetPeerAddress().IsInitialized()) + { + continue; // not an active connection + } + + uint64_t connectionActiveTime = mConnectionStateArray[i].GetLastActivityTimeMs(); + if (connectionActiveTime + maxIdleTimeMs >= currentTime) + { + continue; // not expired + } + + if (OnConnectionExpired) + { + OnConnectionExpired(mConnectionStateArray[i], mConnectionExpiredArgument); + } + + // Connection is assumed expired, marking it as invalid + mConnectionStateArray[i] = PeerConnectionState(PeerAddress::Uninitialized()); + } +} + +} // namespace Transport +} // namespace chip diff --git a/src/transport/PeerConnections.h b/src/transport/PeerConnections.h new file mode 100644 index 00000000000000..f1292bf1f968e5 --- /dev/null +++ b/src/transport/PeerConnections.h @@ -0,0 +1,142 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 PEER_CONNECTIONS_H_ +#define PEER_CONNECTIONS_H_ + +#include +#include +#include +#include +#include + +namespace chip { +namespace Transport { + +/** + * Handles a set of peer connection states. + * + * Intended for: + * - handle connection active time and expiration + * - allocate and free space for connection states. + */ +class PeerConnectionsBase +{ +public: + /** + * Construct a PeerConnectionsBase object using a preallocated array used for connection state storage. + */ + PeerConnectionsBase(PeerConnectionState * storageArray, size_t arraySize) : + mConnectionStateArray(storageArray), mConnectionStateArraySize(arraySize) + {} + virtual ~PeerConnectionsBase() {} + + /** + * Allocates a new peer connection state state object out of the internal resource pool. + * + * @param address represents the connection state address + * @param state [out] will contain the connection state if one was available. May be null if no return value is desired. + * + * @note the newly created state will have an 'active' time set based on the current time source. + * + * @returns CHIP_NO_ERROR if state could be initialized. May fail if maximum connection count + * has been reached (with CHIP_ERROR_NO_MEMORY). + */ + CHECK_RETURN_VALUE + CHIP_ERROR CreateNewPeerConnectionState(const PeerAddress & address, PeerConnectionState ** state); + + /** + * Get a peer connection state given a peer address. + * + * @param address is the connection to find (based on address) + * @param state [out] the connection if found, null otherwise. MUST not be null. + * + * @return true if a corresponding state was found. + */ + CHECK_RETURN_VALUE + bool FindPeerConnectionState(const PeerAddress & address, PeerConnectionState ** state); + + /** + * Get a peer connection state given a Node Id. + * + * @param nodeId is the connection to find (based on nodeId). Note that initial connections + * do not have a node id set. Use this if you know the node id should be set. + * @param state [out] the connection if found, null otherwise. MUST not be null. + * + * @return true if a corresponding state was found. + */ + CHECK_RETURN_VALUE + bool FindPeerConnectionState(NodeId nodeId, PeerConnectionState ** state); + + /// Convenience method to mark a peer connection state as active + void MarkConnectionActive(PeerConnectionState * state) { state->SetLastActivityTimeMs(GetCurrentMonotonicTimeMs()); } + + /** + * Iterates through all active connections and expires any connection with an idle time + * larger than the given amount. + * + * Expiring a connection involves callback execution and then clearing the internal state. + */ + void ExpireInactiveConnections(uint64_t maxIdleTimeMs); + + /** + * Sets the handler for expired connections + * + * @param[in] handler The callback to call when a connection is marked as expired + * @param[in] param The argument to pass in to the handler function + * + */ + template + void SetConnectionExpiredHandler(void (*handler)(const PeerConnectionState &, T *), T * param) + { + mConnectionExpiredArgument = param; + OnConnectionExpired = reinterpret_cast(handler); + } + +protected: + /// Get the current time from a Time::TimeSource or equivalent + virtual uint64_t GetCurrentMonotonicTimeMs() = 0; + +private: + PeerConnectionState * mConnectionStateArray; + const size_t mConnectionStateArraySize; + + typedef void (*ConnectionExpiredHandler)(const PeerConnectionState & state, void * param); + + ConnectionExpiredHandler OnConnectionExpired = nullptr; ///< Callback for when a connection expires + void * mConnectionExpiredArgument = nullptr; ///< Argument for callback +}; + +/** + * Concrete peer connections implementation based on system sizes and timers. + */ +class PeerConnections : public PeerConnectionsBase +{ +public: + PeerConnections() : PeerConnectionsBase(mState, ArraySize(mState)) {} + +protected: + uint64_t GetCurrentMonotonicTimeMs() override { return mTimeSource.GetCurrentMonotonicTimeMs(); } + +private: + PeerConnectionState mState[CHIP_CONFIG_PEER_CONNECTION_POOL_SIZE]; + Time::TimeSource mTimeSource; +}; + +} // namespace Transport +} // namespace chip + +#endif // PEER_CONNECTIONS_H_ diff --git a/src/lib/core/CHIPSecureChannel.cpp b/src/transport/SecureSession.cpp similarity index 51% rename from src/lib/core/CHIPSecureChannel.cpp rename to src/transport/SecureSession.cpp index fd623b981731d0..b11e81af37ab7f 100644 --- a/src/lib/core/CHIPSecureChannel.cpp +++ b/src/transport/SecureSession.cpp @@ -18,13 +18,14 @@ /** * @file - * This file implements the CHIP Secure Channel object. + * This file implements the CHIP Secure Session object. * */ -#include +#include #include #include +#include #include @@ -32,12 +33,11 @@ namespace chip { using namespace Crypto; -ChipSecureChannel::ChipSecureChannel() : mKeyAvailable(false), mNextIV(0) {} +SecureSession::SecureSession() : mKeyAvailable(false), mNextIV(0) {} -CHIP_ERROR ChipSecureChannel::Init(const unsigned char * remote_public_key, const size_t public_key_length, - const unsigned char * local_private_key, const size_t private_key_length, - const unsigned char * salt, const size_t salt_length, const unsigned char * info, - const size_t info_length) +CHIP_ERROR SecureSession::Init(const unsigned char * remote_public_key, const size_t public_key_length, + const unsigned char * local_private_key, const size_t private_key_length, const unsigned char * salt, + const size_t salt_length, const unsigned char * info, const size_t info_length) { CHIP_ERROR error = CHIP_NO_ERROR; uint8_t secret[kMax_ECDH_Secret_Length]; @@ -69,72 +69,50 @@ CHIP_ERROR ChipSecureChannel::Init(const unsigned char * remote_public_key, cons return error; } -CHIP_ERROR ChipSecureChannel::Encrypt(const unsigned char * input, size_t input_length, unsigned char * output, - size_t output_length) +void SecureSession::Close(void) +{ + mKeyAvailable = false; + mNextIV = 0; +} + +CHIP_ERROR SecureSession::Encrypt(const unsigned char * input, size_t input_length, unsigned char * output, MessageHeader & header) { CHIP_ERROR error = CHIP_NO_ERROR; uint64_t tag = 0; - security_header_t header; - size_t overhead = EncryptionOverhead(); VerifyOrExit(mKeyAvailable, error = CHIP_ERROR_INVALID_USE_OF_SESSION_KEY); VerifyOrExit(input != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(input_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(output != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(output_length >= overhead + input_length, error = CHIP_ERROR_INVALID_ARGUMENT); - header.payload_length = input_length; - header.IV = mNextIV; - header.tag = 0; + error = AES_CCM_encrypt(input, input_length, NULL, 0, mKey, sizeof(mKey), (const unsigned char *) &mNextIV, sizeof(mNextIV), + output, (unsigned char *) &tag, sizeof(tag)); + SuccessOrExit(error); - error = AES_CCM_encrypt(input, input_length, (const unsigned char *) &header, sizeof(header), mKey, sizeof(mKey), - (const unsigned char *) &header.IV, sizeof(header.IV), &output[overhead], (unsigned char *) &tag, - sizeof(tag)); - if (error == CHIP_NO_ERROR) - { - header.tag = tag; - memcpy(output, &header, sizeof(header)); - mNextIV++; - } + header.SetIV(mNextIV).SetTag(tag); + + mNextIV++; exit: return error; } -CHIP_ERROR ChipSecureChannel::Decrypt(const unsigned char * input, size_t input_length, unsigned char * output, - size_t & output_length) +CHIP_ERROR SecureSession::Decrypt(const unsigned char * input, size_t input_length, unsigned char * output, + const MessageHeader & header) { CHIP_ERROR error = CHIP_NO_ERROR; - uint64_t tag = 0; - security_header_t header; - size_t overhead = EncryptionOverhead(); + uint64_t tag = header.GetTag(); + uint64_t IV = header.GetIV(); VerifyOrExit(mKeyAvailable, error = CHIP_ERROR_INVALID_USE_OF_SESSION_KEY); VerifyOrExit(input != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(input_length > overhead, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(input_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(output != NULL, error = CHIP_ERROR_INVALID_ARGUMENT); - memcpy(&header, input, sizeof(header)); - tag = header.tag; - header.tag = 0; - - VerifyOrExit(output_length >= header.payload_length, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(input_length >= header.payload_length + overhead, error = CHIP_ERROR_INVALID_ARGUMENT); - - error = AES_CCM_decrypt(&input[overhead], header.payload_length, (const unsigned char *) &header, sizeof(header), - (const unsigned char *) &tag, sizeof(tag), mKey, sizeof(mKey), (const unsigned char *) &header.IV, - sizeof(header.IV), output); - if (error == CHIP_NO_ERROR) - { - output_length = header.payload_length; - } + error = AES_CCM_decrypt(input, input_length, NULL, 0, (const unsigned char *) &tag, sizeof(tag), mKey, sizeof(mKey), + (const unsigned char *) &IV, sizeof(IV), output); exit: return error; } -size_t ChipSecureChannel::EncryptionOverhead(void) -{ - return sizeof(security_header_t); -} - } // namespace chip diff --git a/src/lib/core/CHIPSecureChannel.h b/src/transport/SecureSession.h similarity index 83% rename from src/lib/core/CHIPSecureChannel.h rename to src/transport/SecureSession.h index e25920d3e94faa..fdfdca0cce8c23 100644 --- a/src/lib/core/CHIPSecureChannel.h +++ b/src/transport/SecureSession.h @@ -18,19 +18,20 @@ /** * @file - * This file defines the CHIP Secure Channel object that provides + * This file defines the CHIP Secure Session object that provides * APIs for encrypting/decryting data using cryptographic keys. * */ -#ifndef __CHIPSECURECHANNEL_H__ -#define __CHIPSECURECHANNEL_H__ +#ifndef __SECURESESSION_H__ +#define __SECURESESSION_H__ #include +#include namespace chip { -class DLL_EXPORT ChipSecureChannel +class DLL_EXPORT SecureSession { public: /** @@ -55,10 +56,10 @@ class DLL_EXPORT ChipSecureChannel * @param input Unencrypted input data * @param input_length Length of the input data * @param output Output buffer for encrypted data - * @param output_length Length of the output buffer + * @param header message header structure * @return CHIP_ERROR The result of encryption */ - CHIP_ERROR Encrypt(const unsigned char * input, size_t input_length, unsigned char * output, size_t output_length); + CHIP_ERROR Encrypt(const unsigned char * input, size_t input_length, unsigned char * output, MessageHeader & header); /** * @brief @@ -67,10 +68,10 @@ class DLL_EXPORT ChipSecureChannel * @param input Encrypted input data * @param input_length Length of the input data * @param output Output buffer for decrypted data - * @param output_length Length of the output buffer + * @param header message header structure * @return CHIP_ERROR The result of decryption */ - CHIP_ERROR Decrypt(const unsigned char * input, size_t input_length, unsigned char * output, size_t & output_length); + CHIP_ERROR Decrypt(const unsigned char * input, size_t input_length, unsigned char * output, const MessageHeader & header); /** * @brief @@ -81,18 +82,13 @@ class DLL_EXPORT ChipSecureChannel */ size_t EncryptionOverhead(void); - ChipSecureChannel(void); + void Close(void); + + SecureSession(void); private: static const size_t kAES_CCM128_Key_Length = 16; - typedef struct - { - size_t payload_length; - uint64_t IV; - uint64_t tag; - } security_header_t; - bool mKeyAvailable; uint64_t mNextIV; uint8_t mKey[kAES_CCM128_Key_Length]; @@ -100,4 +96,4 @@ class DLL_EXPORT ChipSecureChannel } // namespace chip -#endif // __CHIPSECURECHANNEL_H__ +#endif // __SECURESESSION_H__ diff --git a/src/transport/SecureSessionMgr.cpp b/src/transport/SecureSessionMgr.cpp new file mode 100644 index 00000000000000..bed80559b98678 --- /dev/null +++ b/src/transport/SecureSessionMgr.cpp @@ -0,0 +1,202 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2013-2017 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements the CHIP Connection object that maintains a UDP connection. + * TODO This class should be extended to support TCP as well... + * + */ + +#include +#include +#include +#include + +#include + +namespace chip { + +// Maximum length of application data that can be encrypted as one block. +// The limit is derived from IPv6 MTU (1280 bytes) - expected header overheads. +// This limit would need additional reviews once we have formalized Secure Transport header. +static const size_t kMax_SecureSDU_Length = 1024; +static const char * kManualKeyExchangeChannelInfo = "Manual Key Exchanged Channel"; + +SecureSessionMgr::SecureSessionMgr() : mConnectionState(Transport::PeerAddress::Uninitialized()), mState(State::kNotReady) +{ + OnMessageReceived = NULL; +} + +CHIP_ERROR SecureSessionMgr::Init(NodeId localNodeId, Inet::InetLayer * inet, const Transport::UdpListenParameters & listenParams) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + VerifyOrExit(mState == State::kNotReady, err = CHIP_ERROR_INCORRECT_STATE); + + err = mTransport.Init(inet, listenParams); + SuccessOrExit(err); + + mTransport.SetMessageReceiveHandler(HandleDataReceived, this); + mState = State::kInitialized; + mLocalNodeId = localNodeId; + +exit: + return err; +} + +CHIP_ERROR SecureSessionMgr::Connect(NodeId peerNodeId, const Transport::PeerAddress & peerAddress) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrExit(mState == State::kInitialized, err = CHIP_ERROR_INCORRECT_STATE); + + mConnectionState.SetPeerNodeId(peerNodeId); + mConnectionState.SetPeerAddress(peerAddress); + + mState = State::kConnected; + +exit: + return err; +} + +CHIP_ERROR SecureSessionMgr::ManualKeyExchange(const unsigned char * remote_public_key, const size_t public_key_length, + const unsigned char * local_private_key, const size_t private_key_length) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + size_t info_len = strlen(kManualKeyExchangeChannelInfo); + + VerifyOrExit(mState == State::kConnected, err = CHIP_ERROR_INCORRECT_STATE); + + err = mSecureChannel.Init(remote_public_key, public_key_length, local_private_key, private_key_length, NULL, 0, + (const unsigned char *) kManualKeyExchangeChannelInfo, info_len); + SuccessOrExit(err); + mState = State::kSecureConnected; + +exit: + return err; +} + +CHIP_ERROR SecureSessionMgr::SendMessage(NodeId peerNodeId, System::PacketBuffer * msgBuf) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrExit(StateAllowsSend(), err = CHIP_ERROR_INCORRECT_STATE); + + VerifyOrExit(msgBuf != NULL, err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(msgBuf->Next() == NULL, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + VerifyOrExit(msgBuf->TotalLength() < kMax_SecureSDU_Length, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + + { + uint8_t * data = msgBuf->Start(); + MessageHeader header; + + err = mSecureChannel.Encrypt(data, msgBuf->TotalLength(), data, header); + SuccessOrExit(err); + + ChipLogProgress(Inet, "Secure transport transmitting msg %u after encryption", mConnectionState.GetSendMessageIndex()); + + header + .SetSourceNodeId(mLocalNodeId) // + .SetDestinationNodeId(peerNodeId) // + .SetMessageId(mConnectionState.GetSendMessageIndex()); + + err = mTransport.SendMessage(header, mConnectionState.GetPeerAddress(), msgBuf); + msgBuf = NULL; + } + SuccessOrExit(err); + + mConnectionState.IncrementSendMessageIndex(); + +exit: + if (msgBuf != NULL) + { + ChipLogProgress(Inet, "Secure transport failed to encrypt msg %u: %s", mConnectionState.GetSendMessageIndex(), + ErrorStr(err)); + PacketBuffer::Free(msgBuf); + msgBuf = NULL; + } + + return err; +} + +void SecureSessionMgr::HandleDataReceived(const MessageHeader & header, const IPPacketInfo & pktInfo, System::PacketBuffer * msg, + SecureSessionMgr * connection) +{ + CHIP_ERROR err = CHIP_ERROR_INVALID_ARGUMENT; + + System::PacketBuffer * origMsg = nullptr; + + // TODO: actual key exchange should happen here + if (!connection->StateAllowsReceive()) + { + if (connection->OnNewConnection) + { + connection->OnNewConnection(header, pktInfo, connection->mNewConnectionArgument); + } + err = CHIP_NO_ERROR; + ExitNow(ChipLogProgress(Inet, "Secure transport failed: state does not allow receive")); + } + + VerifyOrExit(msg != nullptr, ChipLogError(Inet, "Secure transport received NULL packet, discarding")); + + // TODO this is where messages should be decoded + { + uint8_t * data = msg->Start(); + uint8_t * plainText = nullptr; + uint16_t len = msg->TotalLength(); +#if CHIP_SYSTEM_CONFIG_USE_LWIP + /* This is a workaround for the case where PacketBuffer payload is not allocated + as an inline buffer to PacketBuffer structure */ + origMsg = msg; + msg = PacketBuffer::NewWithAvailableSize(len); + msg->SetDataLength(len, msg); +#endif + plainText = msg->Start(); + + err = connection->mSecureChannel.Decrypt(data, len, plainText, header); + VerifyOrExit(err == CHIP_NO_ERROR, ChipLogProgress(Inet, "Secure transport failed to decrypt msg: err %d", err)); + + if (connection->OnMessageReceived) + { + connection->OnMessageReceived(header, pktInfo, msg, connection->mMessageReceivedArgument); + msg = nullptr; + } + } + +exit: + if (origMsg != nullptr) + { + PacketBuffer::Free(origMsg); + } + + if (msg != nullptr) + { + PacketBuffer::Free(msg); + } + + if (err != CHIP_NO_ERROR) + { + if (connection->OnReceiveError) + { + connection->OnReceiveError(err, pktInfo); + } + } +} + +} // namespace chip diff --git a/src/transport/SecureSessionMgr.h b/src/transport/SecureSessionMgr.h new file mode 100644 index 00000000000000..eaea6c6502f783 --- /dev/null +++ b/src/transport/SecureSessionMgr.h @@ -0,0 +1,198 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file defines a secure transport layer which adds encryption to data + * sent over a transport. + * + */ + +#ifndef __SECURESESSIONMGR_H__ +#define __SECURESESSIONMGR_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace chip { + +using namespace System; + +class DLL_EXPORT SecureSessionMgr : public ReferenceCounted +{ +public: + /** + * The State of a secure transport object. + */ + enum class State + { + kNotReady, /**< State before initialization. */ + kInitialized, /**< State when the object is ready to connect. */ + kConnected, /**< State when the remte peer is connected. */ + kSecureConnected, /**< State when the security of the connection has been established. */ + }; + + /** + * @brief + * Initialize a Secure Transport + * + * @param inet Inet layer to use + * @param listenParams Listen settings for the transport + * + * @note This is not a final API as it is UDP specific. Class will be updated to support + * separate Transports (UDP, BLE, TCP, optional ipv4 for testing etc.). This API is currently + * UDP-specific and that will change. + */ + CHIP_ERROR Init(NodeId localNodeId, Inet::InetLayer * inet, const Transport::UdpListenParameters & listenParams); + + /** + * @brief + * The keypair for the secure channel. This is a utility function that will be used + * until we have automatic key exchange in place. The function is useful only for + * example applications for now. It will eventually be removed. + * + * @param remote_public_key A pointer to peer's public key + * @param public_key_length Length of remote_public_key + * @param local_private_key A pointer to local private key + * @param private_key_length Length of local_private_key + * @return CHIP_ERROR The result of key derivation + */ + CHIP_ERROR ManualKeyExchange(const unsigned char * remote_public_key, const size_t public_key_length, + const unsigned char * local_private_key, const size_t private_key_length); + + /** + * Establishes a connection to the given peer node. + * + * A connection needs to be established before SendMessage can be called. + */ + CHIP_ERROR Connect(NodeId peerNodeId, const Transport::PeerAddress & peerAddress); + + /** + * @brief + * Send a message to a currently connected peer + * + * @details + * This method calls chip::System::PacketBuffer::Free on + * behalf of the caller regardless of the return status. + */ + CHIP_ERROR SendMessage(NodeId peerNodeId, System::PacketBuffer * msgBuf); + + SecureSessionMgr(); + virtual ~SecureSessionMgr() {} + + /** + * Sets the message receive handler and associated argument + * + * @param[in] handler The callback to call when a message is received + * @param[in] param The argument to pass in to the handler function + * + */ + template + void SetMessageReceiveHandler(void (*handler)(const MessageHeader &, const Inet::IPPacketInfo &, System::PacketBuffer *, T *), + T * param) + { + mMessageReceivedArgument = param; + OnMessageReceived = reinterpret_cast(handler); + } + + /** + * Sets the receive error handler and associated argument + * + * @param[in] handler The callback to call on receive error + * + */ + void SetReceiveErrorHandler(void (*handler)(CHIP_ERROR, const Inet::IPPacketInfo &)) + { + OnReceiveError = reinterpret_cast(handler); + } + + /** + * Sets the new connection handler and associated argument + * + * @param[in] handler The callback to call when a message is received + * @param[in] param The argument to pass in to the handler function + * + */ + template + void SetNewConnectionHandler(void (*handler)(const MessageHeader &, const Inet::IPPacketInfo &, T *), T * param) + { + mNewConnectionArgument = param; + OnNewConnection = reinterpret_cast(handler); + } + + /** + * TEMPORARY method for current connection until multi-session handling support + * is added to this class. + */ + NodeId GetPeerNodeId() const { return mConnectionState.GetPeerNodeId(); }; + +private: + // TODO: settings below are single-transport and single-peer. Long term + // intent for the class is to do session management and there will be + // multiple transports and multiple peers supported. + // Expected changes: + // - Transports including UDP, TCP, BLE as needed + // - Multiple peers with separate states (encryption settings and handshake state) + // - Global state is only a ready/notready and connection state is deferred into + // individual connections. + + Transport::UDP mTransport; + + Transport::PeerConnectionState mConnectionState; + NodeId mLocalNodeId; + State mState; + SecureSession mSecureChannel; + + bool StateAllowsSend(void) const { return mState != State::kNotReady; } + bool StateAllowsReceive(void) const { return mState == State::kSecureConnected; } + + /** + * This function is the application callback that is invoked when a message is received over a + * Chip connection. + * + * @param[in] msgBuf A pointer to the PacketBuffer object holding the message. + */ + typedef void (*MessageReceiveHandler)(const MessageHeader & header, const Inet::IPPacketInfo & source, + System::PacketBuffer * msgBuf, void * param); + + MessageReceiveHandler OnMessageReceived = nullptr; ///< Callback on message receiving + void * mMessageReceivedArgument = nullptr; ///< Argument for callback + + typedef void (*ReceiveErrorHandler)(CHIP_ERROR error, const Inet::IPPacketInfo & source); + + ReceiveErrorHandler OnReceiveError = nullptr; ///< Callback on error in message receiving + + typedef void (*NewConnectionHandler)(const MessageHeader & header, const Inet::IPPacketInfo & source, void * param); + + NewConnectionHandler OnNewConnection = nullptr; ///< Callback for new connection received + void * mNewConnectionArgument = nullptr; ///< Argument for callback + + static void HandleDataReceived(const MessageHeader & header, const Inet::IPPacketInfo & source, System::PacketBuffer * msgBuf, + SecureSessionMgr * transport); +}; + +} // namespace chip + +#endif // __SECURESESSIONMGR_H__ diff --git a/src/transport/TransportLayer.am b/src/transport/TransportLayer.am index 5e4fd327cf66ab..30dbea2a1d9134 100644 --- a/src/transport/TransportLayer.am +++ b/src/transport/TransportLayer.am @@ -25,11 +25,20 @@ # CHIP_BUILD_TRANSPORT_LAYER_SOURCE_FILES = \ + @top_builddir@/src/transport/SecureSession.cpp \ @top_builddir@/src/transport/MessageHeader.cpp \ - @top_builddir@/src/transport/UdpTransport.cpp \ + @top_builddir@/src/transport/PeerConnections.cpp \ + @top_builddir@/src/transport/SecureSessionMgr.cpp \ + @top_builddir@/src/transport/UDP.cpp \ $(NULL) -CHIP_BUILD_TRANSPORT_LAYER_HEADER_FILES = \ - MessageHeader.h \ - UdpTransport.h \ +CHIP_BUILD_TRANSPORT_LAYER_HEADER_FILES = \ + @top_builddir@/src/transport/Base.h \ + @top_builddir@/src/transport/SecureSession.h \ + @top_builddir@/src/transport/MessageHeader.h \ + @top_builddir@/src/transport/PeerAddress.h \ + @top_builddir@/src/transport/PeerConnectionState.h \ + @top_builddir@/src/transport/PeerConnections.h \ + @top_builddir@/src/transport/SecureSessionMgr.h \ + @top_builddir@/src/transport/UDP.h \ $(NULL) diff --git a/src/transport/UDP.cpp b/src/transport/UDP.cpp new file mode 100644 index 00000000000000..ffa42a1262b958 --- /dev/null +++ b/src/transport/UDP.cpp @@ -0,0 +1,144 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2013-2017 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements the CHIP Connection object that maintains a UDP connection. + * TODO This class should be extended to support TCP as well... + * + */ +#include + +#include +#include +#include + +#include + +namespace chip { +namespace Transport { + +UDP::~UDP() +{ + if (mUDPEndPoint) + { + // Udp endpoint is only non null if udp endpoint is initialized and listening + mUDPEndPoint->Close(); + mUDPEndPoint->Free(); + mUDPEndPoint = nullptr; + } +} + +CHIP_ERROR UDP::Init(Inet::InetLayer * inetLayer, const UdpListenParameters & params) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrExit(mState == State::kNotReady, err = CHIP_ERROR_INCORRECT_STATE); + + mSendPort = params.GetMessageSendPort(); + + err = inetLayer->NewUDPEndPoint(&mUDPEndPoint); + SuccessOrExit(err); + + err = mUDPEndPoint->Bind(params.GetAddressType(), IPAddress::Any, params.GetListenPort(), params.GetInterfaceId()); + SuccessOrExit(err); + + err = mUDPEndPoint->Listen(); + SuccessOrExit(err); + + mUDPEndPoint->AppState = reinterpret_cast(this); + mUDPEndPoint->OnMessageReceived = OnUdpReceive; + + mState = State::kInitialized; + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogProgress(Inet, "Failed to initialize Udp transport: %s", ErrorStr(err)); + if (mUDPEndPoint) + { + mUDPEndPoint->Free(); + mUDPEndPoint = nullptr; + } + } + + return err; +} + +CHIP_ERROR UDP::SendMessage(const MessageHeader & header, const Transport::PeerAddress & address, System::PacketBuffer * msgBuf) +{ + const size_t headerSize = header.EncodeSizeBytes(); + size_t actualEncodedHeaderSize; + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrExit(address.GetTransportType() == Type::kUdp, err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(mState == State::kInitialized, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mUDPEndPoint != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + IPPacketInfo addrInfo; + addrInfo.Clear(); + + addrInfo.DestAddress = address.GetIPAddress(); + addrInfo.DestPort = address.GetPort(); + + VerifyOrExit(msgBuf->EnsureReservedSize(headerSize), err = CHIP_ERROR_NO_MEMORY); + + msgBuf->SetStart(msgBuf->Start() - headerSize); + err = header.Encode(msgBuf->Start(), msgBuf->DataLength(), &actualEncodedHeaderSize); + SuccessOrExit(err); + + // This is unexpected and means header changed while encoding + VerifyOrExit(headerSize == actualEncodedHeaderSize, err = CHIP_ERROR_INTERNAL); + + err = mUDPEndPoint->SendMsg(&addrInfo, msgBuf); + msgBuf = nullptr; + SuccessOrExit(err); + +exit: + if (msgBuf != NULL) + { + System::PacketBuffer::Free(msgBuf); + msgBuf = NULL; + } + + return err; +} + +void UDP::OnUdpReceive(Inet::IPEndPointBasis * endPoint, System::PacketBuffer * buffer, const IPPacketInfo * pktInfo) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + UDP * udp = reinterpret_cast(endPoint->AppState); + size_t headerSize = 0; + + MessageHeader header; + err = header.Decode(buffer->Start(), buffer->DataLength(), &headerSize); + SuccessOrExit(err); + + buffer->ConsumeHead(headerSize); + udp->HandleMessageReceived(header, *pktInfo, buffer); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Inet, "Failed to receive UDP message: %s", ErrorStr(err)); + } +} + +} // namespace Transport +} // namespace chip diff --git a/src/transport/UDP.h b/src/transport/UDP.h new file mode 100644 index 00000000000000..028602391a4aa2 --- /dev/null +++ b/src/transport/UDP.h @@ -0,0 +1,138 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file defines the CHIP Connection object that maintains a UDP connection. + * It binds to any avaiable local addr and port and begins listening. + * TODO This class should be extended to support TCP as well... + * + */ + +#ifndef __UDPTRANSPORT_H__ +#define __UDPTRANSPORT_H__ + +#include + +#include +#include +#include +#include +#include + +namespace chip { +namespace Transport { + +/** Defines listening parameters for setting up a UDP transport */ +class UdpListenParameters +{ +public: + UdpListenParameters() {} + UdpListenParameters(const UdpListenParameters &) = default; + UdpListenParameters(UdpListenParameters &&) = default; + + Inet::IPAddressType GetAddressType() const { return mAddressType; } + UdpListenParameters & SetAddressType(Inet::IPAddressType type) + { + mAddressType = type; + + return *this; + } + + uint16_t GetMessageSendPort() const { return mMessageSendPort; } + UdpListenParameters & SetMessageSendPort(uint16_t port) + { + mMessageSendPort = port; + + return *this; + } + + uint16_t GetListenPort() const { return mListenPort; } + UdpListenParameters & SetListenPort(uint16_t port) + { + mListenPort = port; + + return *this; + } + + InterfaceId GetInterfaceId() const { return mInterfaceId; } + UdpListenParameters & SetInterfaceId(InterfaceId id) + { + mInterfaceId = id; + + return *this; + } + +private: + Inet::IPAddressType mAddressType = kIPAddressType_IPv6; ///< type of listening socket + uint16_t mMessageSendPort = CHIP_PORT; ///< over what port to send requests + uint16_t mListenPort = CHIP_PORT; ///< UDP listen port + InterfaceId mInterfaceId = INET_NULL_INTERFACEID; ///< Interface to listen on +}; + +/** Implements a transport using UDP. */ +class DLL_EXPORT UDP : public Base +{ + /** + * The State of the UDP connection + * + */ + enum class State + { + kNotReady = 0, /**< State before initialization. */ + kInitialized = 1, /**< State after class is listening and ready. */ + }; + +public: + virtual ~UDP(); + + /** + * Initialize a UDP transport on a given port. + * + * @param inetLayer underlying communication channel + * @param parms UDP configuration parameters for this transport + * + * @details + * Generally send and receive ports should be the same and equal to CHIP_PORT. + * The class allows separate definitions to allow local execution of several + * Nodes. + */ + CHIP_ERROR Init(Inet::InetLayer * inetLayer, const UdpListenParameters & params); + + /** + * Convenience method to listen on IPv6 on chip standard ports + */ + CHIP_ERROR Init(Inet::InetLayer * inetLayer) { return Init(inetLayer, UdpListenParameters()); } + + Type GetType() override { return Type::kUdp; } + CHIP_ERROR SendMessage(const MessageHeader & header, const Transport::PeerAddress & address, + System::PacketBuffer * msgBuf) override; + +private: + // UDP message receive handler. + static void OnUdpReceive(Inet::IPEndPointBasis * endPoint, System::PacketBuffer * buffer, const IPPacketInfo * pktInfo); + + Inet::UDPEndPoint * mUDPEndPoint = nullptr; ///< UDP socket used by the transport + State mState = State::kNotReady; ///< State of the UDP transport + uint16_t mSendPort = 0; ///< Port where packets are sent by default +}; + +} // namespace Transport +} // namespace chip + +#endif // __UDPTRANSPORT_H__ diff --git a/src/transport/UdpTransport.cpp b/src/transport/UdpTransport.cpp deleted file mode 100644 index 37e7e591765c46..00000000000000 --- a/src/transport/UdpTransport.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/* - * - * Copyright (c) 2020 Project CHIP Authors - * Copyright (c) 2013-2017 Nest Labs, Inc. - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 - * This file implements the CHIP Connection object that maintains a UDP connection. - * TODO This class should be extended to support TCP as well... - * - */ - -#include -#include -#include - -#include - -namespace chip { - -UdpTransport::UdpTransport() : mState(kState_NotReady), mRefCount(1) -{ - mState = kState_NotReady; - mRefCount = 1; - mUDPEndPoint = NULL; - mRefCount = 1; - OnMessageReceived = NULL; - OnReceiveError = NULL; - mPeerAddr = IPAddress::Any; - mPeerPort = 0; -} - -void UdpTransport::Init(Inet::InetLayer * inetLayer) -{ - if (mState != kState_NotReady) - { - return; - } - - mInetLayer = inetLayer; - mState = kState_ReadyToConnect; - -} // namespace chip - -CHIP_ERROR UdpTransport::Connect(const IPAddress & peerAddr, uint16_t peerPort) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - - VerifyOrExit(mState == kState_ReadyToConnect, err = CHIP_ERROR_INCORRECT_STATE); - - mPeerAddr = peerAddr; - mPeerPort = (peerPort != 0) ? peerPort : CHIP_PORT; - - // Bump the reference count when we start the connection process. The corresponding decrement happens when the - // DoClose() method is called. This ensures the object stays live while there's the possibility of a callback - // happening from an underlying layer. - mRefCount++; - - ChipLogProgress(Inet, "Connection start"); - err = DoConnect(); -exit: - return err; -} - -CHIP_ERROR UdpTransport::DoConnect() -{ - CHIP_ERROR err = CHIP_NO_ERROR; - - err = mInetLayer->NewUDPEndPoint(&mUDPEndPoint); - if (err != CHIP_NO_ERROR) - { - ChipLogProgress(Inet, "Error: %s\n Couldn't create connection\n", ErrorStr(err)); - return err; - } - - err = mUDPEndPoint->Bind(mPeerAddr.Type(), IPAddress::Any, CHIP_PORT); - if (err != CHIP_NO_ERROR) - { - ChipLogProgress(Inet, "Error: %s\n Bind failed\n", ErrorStr(err)); - return err; - } - - err = mUDPEndPoint->Listen(); - if (err != CHIP_NO_ERROR) - { - ChipLogProgress(Inet, "Error: %s\n Listen failed\n", ErrorStr(err)); - return err; - } - - mUDPEndPoint->AppState = this; - mUDPEndPoint->OnMessageReceived = HandleDataReceived; - mUDPEndPoint->OnReceiveError = HandleReceiveError; - -#if CHIP_PROGRESS_LOGGING - { - char ipAddrStr[64]; - mPeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr)); - ChipLogProgress(Inet, "Connection started %s %d", ipAddrStr, (int) mPeerPort); - } -#endif - mState = kState_Connected; - - return err; -} - -CHIP_ERROR UdpTransport::SendMessage(PacketBuffer * msgBuf) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - - VerifyOrExit(StateAllowsSend(), err = CHIP_ERROR_INCORRECT_STATE); - - IPPacketInfo addrInfo; - addrInfo.Clear(); - addrInfo.DestAddress = mPeerAddr; - addrInfo.DestPort = mPeerPort; - - err = mUDPEndPoint->SendMsg(&addrInfo, msgBuf); - msgBuf = NULL; - -exit: - if (msgBuf != NULL) - { - PacketBuffer::Free(msgBuf); - msgBuf = NULL; - } - - return err; -} - -void UdpTransport::HandleDataReceived(IPEndPointBasis * endPoint, chip::System::PacketBuffer * msg, const IPPacketInfo * pktInfo) -{ - UDPEndPoint * udpEndPoint = static_cast(endPoint); - UdpTransport * connection = (UdpTransport *) udpEndPoint->AppState; - - // TODO this where where messages should be decoded - if (connection->StateAllowsReceive() && msg != NULL) - { - connection->OnMessageReceived(connection, msg, pktInfo); - } -} - -void UdpTransport::HandleReceiveError(IPEndPointBasis * endPoint, CHIP_ERROR err, const IPPacketInfo * pktInfo) -{ - UDPEndPoint * udpEndPoint = static_cast(endPoint); - UdpTransport * connection = (UdpTransport *) udpEndPoint->AppState; - if (connection->StateAllowsReceive()) - { - connection->OnReceiveError(connection, err, pktInfo); - } -} -/** - * Performs a non-blocking graceful close of the UDP based UdpTransport, delivering any - * remaining outgoing data before resetting the connection. - * - * This method provides no strong guarantee that any outgoing message not acknowledged at the application - * protocol level has been received by the remote peer. - * - * Once Close() has been called, the UdpTransport object can no longer be used for further communication. - * - * Calling Close() decrements the reference count associated with the UdpTransport object, whether or not - * the connection is open/active at the time the method is called. If this results in the reference count - * reaching zero, the resources associated with the connection object are freed. When this happens, the - * application must have no further interactions with the object. - * - * @sa Shutdown(), Abort(), Retain() and Release(). - * - * @return #CHIP_NO_ERROR unconditionally. - * - */ -CHIP_ERROR UdpTransport::Close() -{ - // Perform a graceful close. - DoClose(CHIP_NO_ERROR); - - // Decrement the ref count that was added when the UdpTransport object - // was allocated. - VerifyOrDie(mRefCount != 0); - mRefCount--; - - return CHIP_NO_ERROR; -} - -void UdpTransport::DoClose(CHIP_ERROR err) -{ - if (mState != kState_Closed) - { - if (mUDPEndPoint != NULL) - { - if (err == CHIP_NO_ERROR) - { - mUDPEndPoint->Close(); - } - mUDPEndPoint->Free(); - mUDPEndPoint = NULL; - } - } - uint8_t oldState = mState; - mState = kState_Closed; - ChipLogProgress(Inet, "Connection closed %ld", (long) err); - - // Decrement the ref count that was added when the connection started. - if (oldState != kState_ReadyToConnect && oldState != kState_Closed) - { - VerifyOrDie(mRefCount != 0); - mRefCount--; - } -} - -/** - * Reserve a reference to the UdpTransport object. - * - * The Retain() method increments the reference count associated with the UdpTransport object. For every - * call to Retain(), the application is responsible for making a corresponding call to either Release(), Close() - * or Abort(). - */ -void UdpTransport::Retain() -{ - VerifyOrDie(mRefCount < UINT8_MAX); - ++mRefCount; -} - -/** - * Decrement the reference count on the UdpTransport object. - * - * The Release() method decrements the reference count associated with the UdpTransport object. If - * this results in the reference count reaching zero, the connection is closed and the connection object - * is freed. When this happens, the application must have no further interactions with the object. - */ -void UdpTransport::Release() -{ - // If the only reference that will remain after this call is the one that was automatically added - // when the connection started, close the connection. - if (mRefCount == 2 && mState != kState_ReadyToConnect && mState != kState_Closed) - { - DoClose(CHIP_NO_ERROR); - } - - VerifyOrDie(mRefCount != 0); - mRefCount--; -} - -} // namespace chip diff --git a/src/transport/UdpTransport.h b/src/transport/UdpTransport.h deleted file mode 100644 index 03d239a4190684..00000000000000 --- a/src/transport/UdpTransport.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * - * Copyright (c) 2020 Project CHIP Authors - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 - * This file defines the CHIP Connection object that maintains a UDP connection. - * It binds to any avaiable local addr and port and begins listening. - * TODO This class should be extended to support TCP as well... - * - */ - -#ifndef __UDPTRANSPORT_H__ -#define __UDPTRANSPORT_H__ - -#include - -#include -#include -#include - -namespace chip { - -using namespace System; - -class DLL_EXPORT UdpTransport -{ -public: - /** - * @enum State - * - * @brief - * The State of the CHIP connection object. - * - */ - enum State - { - // TODO need more modes when TCP support is added - kState_NotReady = 0, /**< State before initialization. */ - kState_ReadyToConnect = 1, /**< State after initialization of the CHIP connection. */ - kState_Connected = 2, /**< State when the connection has been established. */ - kState_Closed = 3 /**< State when the connection is closed. */ - }; - - /** - * @brief - * Initialize a CHIP Connection - * - * @param inetLayer A pointer to the chip::Inet::InetLayer - */ - void Init(Inet::InetLayer * inetLayer); - - /** - * @brief - * Attempt to establish a connection to the given peer - * - * @param peerAddr The chip::Inet::IPAddress of the requested peer - * @param peerPort The port of the requested peer - * @return CHIP_ERROR The connection result - */ - CHIP_ERROR Connect(const IPAddress & peerAddr, uint16_t peerPort = 0); - - /** - * @brief - * Send a message to the currently connected peer - * - * @param msgBuf A PacketBuffer containing the message to be sent - * @return CHIP_ERROR The send result - * - * @details - * This method calls chip::System::PacketBuffer::Free on - * behalf of the caller regardless of the return status. - */ - CHIP_ERROR SendMessage(PacketBuffer * msgBuf); - - /** - * @brief - * Close an existing connection. Once close is called, the UdpTransport object can no longer be used - * - * @return CHIP_ERROR The close result - */ - CHIP_ERROR Close(void); - - void Retain(void); - void Release(void); - - /** - * This function is the application callback that is invoked when a message is received over a - * Chip connection. - * - * @param[in] con A pointer to the UdpTransport object. - * - * @param[in] msgBuf A pointer to the PacketBuffer object holding the message. - * - * @param[in] pktInfo A pointer to the IPPacketInfo object carrying sender details. - * - */ - typedef void (*MessageReceiveHandler)(UdpTransport * con, PacketBuffer * msgBuf, const IPPacketInfo * pktInfo); - MessageReceiveHandler OnMessageReceived; - - /** - * This function is the application callback invoked upon encountering an error when receiving - * a Chip message. - * - * @param[in] con A pointer to the UdpTransport object. - * - * @param[in] err The CHIP_ERROR encountered when receiving data over the connection. - * - * @param[in] pktInfo A pointer to the IPPacketInfo object carrying sender details. - * - */ - typedef void (*ReceiveErrorHandler)(UdpTransport * con, CHIP_ERROR err, const IPPacketInfo * pktInfo); - ReceiveErrorHandler OnReceiveError; - - UdpTransport(); - virtual ~UdpTransport() {} - -private: - Inet::InetLayer * mInetLayer; - UDPEndPoint * mUDPEndPoint; - IPAddress mPeerAddr; - uint16_t mPeerPort; - State mState; - uint8_t mRefCount; - - CHIP_ERROR DoConnect(); - void DoClose(CHIP_ERROR err); - bool StateAllowsSend(void) const { return mState == kState_Connected; } - bool StateAllowsReceive(void) const { return mState == kState_Connected; } - - static void HandleDataReceived(IPEndPointBasis * endPoint, chip::System::PacketBuffer * msg, const IPPacketInfo * pktInfo); - static void HandleReceiveError(IPEndPointBasis * endPoint, INET_ERROR err, const IPPacketInfo * pktInfo); -}; - -/// Associates a UDP transport with a state at creation time -template -class StatefulUdpTransport : public UdpTransport -{ -public: - StatefulUdpTransport(const StateType & state) : mState(state) {} - StatefulUdpTransport(StateType && state) : mState(std::move(state)) {} - - StateType & State(void) { return mState; } - const StateType & State(void) const { return mState; } - - /// Typesafe equivalent of UdpTransport::MessageReceivehandler - typedef void (*StatefulMessageReceiveHandler)(StatefulUdpTransport * con, PacketBuffer * msgBuf, const IPPacketInfo * pktInfo); - - /// Typesafe equivalent of UdpTransport::ReceiveErrorHandler - typedef void (*StatefulReceiveErrorHandler)(StatefulUdpTransport * con, CHIP_ERROR err, const IPPacketInfo * pktInfo); - - /// Sets the OnMessageReceived callback using a stateful callback - void SetMessageReceiveHandler(StatefulMessageReceiveHandler handler) - { - OnMessageReceived = reinterpret_cast(handler); - } - - /// Sets the OnMessageReceived callback using a stateful callback - void SetReceiveErrorHandler(StatefulReceiveErrorHandler handler) - { - OnReceiveError = reinterpret_cast(handler); - } - -private: - StateType mState; ///< State for this transport. Often a pointer. -}; - -} // namespace chip - -#endif // __UDPTRANSPORT_H__ diff --git a/src/transport/tests/Makefile.am b/src/transport/tests/Makefile.am index 5b8f650477a23b..044cd45bd5321b 100644 --- a/src/transport/tests/Makefile.am +++ b/src/transport/tests/Makefile.am @@ -42,7 +42,10 @@ lib_LIBRARIES = \ libTransportLayerTests_a_SOURCES = \ TestMessageHeader.cpp \ - TestUdpTransport.cpp \ + TestPeerConnections.cpp \ + TestSecureSession.cpp \ + TestSecureSessionMgr.cpp \ + TestUDP.cpp \ $(NULL) libTransportLayerTests_adir = $(includedir)/transport @@ -67,7 +70,6 @@ AM_CPPFLAGS = \ CHIP_LDADD = \ $(top_builddir)/src/lib/libCHIP.a \ - $(top_builddir)/src/crypto/libChipCrypto.a \ $(NULL) COMMON_LDADD = \ @@ -85,7 +87,10 @@ COMMON_LDADD = \ check_PROGRAMS = \ TestMessageHeader \ - TestUdpTransport \ + TestPeerConnections \ + TestSecureSessionMgr \ + TestSecureSession \ + TestUDP \ $(NULL) # Test applications and scripts that should be built and run when the @@ -106,8 +111,17 @@ TESTS_ENVIRONMENT = \ TestMessageHeader_SOURCES = TestMessageHeaderDriver.cpp TestMessageHeader_LDADD = $(COMMON_LDADD) -TestUdpTransport_SOURCES = TestUdpTransportDriver.cpp -TestUdpTransport_LDADD = $(COMMON_LDADD) +TestSecureSessionMgr_SOURCES = TestSecureSessionMgrDriver.cpp +TestSecureSessionMgr_LDADD = $(COMMON_LDADD) + +TestSecureSession_SOURCES = TestSecureSessionDriver.cpp +TestSecureSession_LDADD = $(COMMON_LDADD) + +TestUDP_SOURCES = TestUDPDriver.cpp +TestUDP_LDADD = $(COMMON_LDADD) + +TestPeerConnections_SOURCES = TestPeerConnectionsDriver.cpp +TestPeerConnections_LDADD = $(COMMON_LDADD) # # Foreign make dependencies @@ -119,6 +133,7 @@ NLFOREIGN_FILE_DEPENDENCIES = \ NLFOREIGN_SUBDIR_DEPENDENCIES = \ $(LWIP_FOREIGN_SUBDIR_DEPENDENCY) \ + $(NLFAULTINJECTION_FOREIGN_SUBDIR_DEPENDENCY) \ $(NLUNIT_TEST_FOREIGN_SUBDIR_DEPENDENCY) \ $(NULL) diff --git a/src/transport/tests/TestMessageHeader.cpp b/src/transport/tests/TestMessageHeader.cpp index 6ff85df59db2d2..bfffb0b20b326d 100644 --- a/src/transport/tests/TestMessageHeader.cpp +++ b/src/transport/tests/TestMessageHeader.cpp @@ -37,6 +37,10 @@ void TestHeaderInitialState(nlTestSuite * inSuite, void * inContext) { MessageHeader header; + NL_TEST_ASSERT(inSuite, header.GetSecureMsgType() == 0); + NL_TEST_ASSERT(inSuite, header.GetSecureSessionID() == 0); + NL_TEST_ASSERT(inSuite, header.GetIV() == 0); + NL_TEST_ASSERT(inSuite, header.GetTag() == 0); NL_TEST_ASSERT(inSuite, header.GetMessageId() == 0); NL_TEST_ASSERT(inSuite, !header.GetDestinationNodeId().HasValue()); NL_TEST_ASSERT(inSuite, !header.GetSourceNodeId().HasValue()); @@ -94,6 +98,23 @@ void TestHeaderEncodeDecode(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, header.GetMessageId() == 234); NL_TEST_ASSERT(inSuite, header.GetDestinationNodeId() == Optional::Value(88)); NL_TEST_ASSERT(inSuite, header.GetSourceNodeId() == Optional::Value(77)); + + header.SetMessageId(234).SetSourceNodeId(77).SetDestinationNodeId(88); + header.SetSecureMsgType(1122).SetSessionID(2233).SetIV(334455).SetTag(12345); + NL_TEST_ASSERT(inSuite, header.Encode(buffer, sizeof(buffer), &encodeLen) == CHIP_NO_ERROR); + + // change it to verify decoding + header.SetMessageId(222).SetSourceNodeId(1).SetDestinationNodeId(2); + header.SetSecureMsgType(2211).SetSessionID(3322).SetIV(554433).SetTag(54321); + NL_TEST_ASSERT(inSuite, header.Decode(buffer, encodeLen, &decodeLen) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, encodeLen == decodeLen); + NL_TEST_ASSERT(inSuite, header.GetMessageId() == 234); + NL_TEST_ASSERT(inSuite, header.GetDestinationNodeId() == Optional::Value(88)); + NL_TEST_ASSERT(inSuite, header.GetSourceNodeId() == Optional::Value(77)); + NL_TEST_ASSERT(inSuite, header.GetSecureMsgType() == 1122); + NL_TEST_ASSERT(inSuite, header.GetSecureSessionID() == 2233); + NL_TEST_ASSERT(inSuite, header.GetIV() == 334455); + NL_TEST_ASSERT(inSuite, header.GetTag() == 12345); } void TestHeaderEncodeDecodeBounds(nlTestSuite * inSuite, void * inContext) diff --git a/src/transport/tests/TestPeerConnections.cpp b/src/transport/tests/TestPeerConnections.cpp new file mode 100644 index 00000000000000..b71b7b417e83d5 --- /dev/null +++ b/src/transport/tests/TestPeerConnections.cpp @@ -0,0 +1,244 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements a process to effect a functional test for + * the PeerConnections class within the transport layer + * + */ +#include "TestTransportLayer.h" + +#include +#include + +#include + +namespace { + +using namespace chip; +using namespace chip::Transport; + +PeerAddress AddressFromString(const char * str) +{ + Inet::IPAddress addr; + + VerifyOrDie(Inet::IPAddress::FromString(str, addr)); + + return PeerAddress::UDP(addr); +} + +const PeerAddress kPeer1Addr = AddressFromString("10.1.2.3"); +const PeerAddress kPeer2Addr = AddressFromString("10.0.0.32"); +const PeerAddress kPeer3Addr = AddressFromString("100.200.0.1"); + +const NodeId kPeer1NodeId = 123; +const NodeId kPeer2NodeId = 6; +const NodeId kPeer3NodeId = 81; + +/// A Peer connections that supports exactly 2 connections and a test time source. +class TestPeerConnections : public PeerConnectionsBase +{ +public: + TestPeerConnections() : PeerConnectionsBase(mState, ArraySize(mState)) {} + Time::TimeSource & GetTimeSource() { return mTimeSource; } + +protected: + uint64_t GetCurrentMonotonicTimeMs() override { return mTimeSource.GetCurrentMonotonicTimeMs(); } + +private: + PeerConnectionState mState[2]; + Time::TimeSource mTimeSource; +}; + +void TestBasicFunctionality(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err; + PeerConnectionState * statePtr; + TestPeerConnections connections; + connections.GetTimeSource().SetCurrentMonotonicTimeMs(100); + + err = connections.CreateNewPeerConnectionState(kPeer1Addr, nullptr); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = connections.CreateNewPeerConnectionState(kPeer2Addr, &statePtr); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, statePtr != nullptr); + NL_TEST_ASSERT(inSuite, statePtr->GetPeerAddress() == kPeer2Addr); + NL_TEST_ASSERT(inSuite, statePtr->GetLastActivityTimeMs() == 100); + + // Insufficient space for new connections. Object is max size 2 + err = connections.CreateNewPeerConnectionState(kPeer3Addr, &statePtr); + NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); +} + +void TestFindByAddress(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err; + PeerConnectionState * statePtr; + TestPeerConnections connections; + + err = connections.CreateNewPeerConnectionState(kPeer1Addr, nullptr); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = connections.CreateNewPeerConnectionState(kPeer2Addr, nullptr); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + NL_TEST_ASSERT(inSuite, connections.FindPeerConnectionState(kPeer1Addr, &statePtr)); + NL_TEST_ASSERT(inSuite, statePtr->GetPeerAddress() == kPeer1Addr); + NL_TEST_ASSERT(inSuite, connections.FindPeerConnectionState(kPeer2Addr, &statePtr)); + NL_TEST_ASSERT(inSuite, statePtr->GetPeerAddress() == kPeer2Addr); + + NL_TEST_ASSERT(inSuite, !connections.FindPeerConnectionState(kPeer3Addr, &statePtr)); + NL_TEST_ASSERT(inSuite, statePtr == nullptr); +} + +void TestFindByNodeId(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err; + PeerConnectionState * statePtr; + TestPeerConnections connections; + + err = connections.CreateNewPeerConnectionState(kPeer1Addr, &statePtr); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + statePtr->SetPeerNodeId(kPeer1NodeId); + + err = connections.CreateNewPeerConnectionState(kPeer2Addr, &statePtr); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + statePtr->SetPeerNodeId(kPeer2NodeId); + + NL_TEST_ASSERT(inSuite, connections.FindPeerConnectionState(kPeer1NodeId, &statePtr)); + NL_TEST_ASSERT(inSuite, statePtr->GetPeerAddress() == kPeer1Addr); + NL_TEST_ASSERT(inSuite, statePtr->GetPeerNodeId() == kPeer1NodeId); + + NL_TEST_ASSERT(inSuite, connections.FindPeerConnectionState(kPeer2NodeId, &statePtr)); + NL_TEST_ASSERT(inSuite, statePtr->GetPeerAddress() == kPeer2Addr); + NL_TEST_ASSERT(inSuite, statePtr->GetPeerNodeId() == kPeer2NodeId); + + NL_TEST_ASSERT(inSuite, !connections.FindPeerConnectionState(kPeer3NodeId, &statePtr)); + NL_TEST_ASSERT(inSuite, statePtr == nullptr); +} + +struct ExpiredCallInfo +{ + int callCount = 0; + NodeId lastCallNodeId = 0; + PeerAddress lastCallPeerAddress = PeerAddress::Uninitialized(); +}; + +void OnConnectionExpired(const PeerConnectionState & state, ExpiredCallInfo * info) +{ + info->callCount++; + info->lastCallNodeId = state.GetPeerNodeId(); + info->lastCallPeerAddress = state.GetPeerAddress(); +} + +void TestExpireConnections(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err; + ExpiredCallInfo callInfo; + PeerConnectionState * statePtr; + TestPeerConnections connections; + + connections.SetConnectionExpiredHandler(OnConnectionExpired, &callInfo); + + connections.GetTimeSource().SetCurrentMonotonicTimeMs(100); + + err = connections.CreateNewPeerConnectionState(kPeer1Addr, nullptr); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + connections.GetTimeSource().SetCurrentMonotonicTimeMs(200); + err = connections.CreateNewPeerConnectionState(kPeer2Addr, &statePtr); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + statePtr->SetPeerNodeId(kPeer2NodeId); + + // cannot add before expiry + connections.GetTimeSource().SetCurrentMonotonicTimeMs(300); + err = connections.CreateNewPeerConnectionState(kPeer3Addr, &statePtr); + NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); + + // at time 300, this expires ip addr 1 + connections.ExpireInactiveConnections(150); + NL_TEST_ASSERT(inSuite, callInfo.callCount == 1); + NL_TEST_ASSERT(inSuite, callInfo.lastCallNodeId == kUndefinedNodeId); + NL_TEST_ASSERT(inSuite, callInfo.lastCallPeerAddress == kPeer1Addr); + NL_TEST_ASSERT(inSuite, !connections.FindPeerConnectionState(kPeer1NodeId, &statePtr)); + + // now that the connections were expired, we can add peer3 + connections.GetTimeSource().SetCurrentMonotonicTimeMs(300); + err = connections.CreateNewPeerConnectionState(kPeer3Addr, &statePtr); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + statePtr->SetPeerNodeId(kPeer3NodeId); + + connections.GetTimeSource().SetCurrentMonotonicTimeMs(400); + NL_TEST_ASSERT(inSuite, connections.FindPeerConnectionState(kPeer2NodeId, &statePtr)); + + connections.MarkConnectionActive(statePtr); + NL_TEST_ASSERT(inSuite, statePtr->GetLastActivityTimeMs() == connections.GetTimeSource().GetCurrentMonotonicTimeMs()); + + // At this time: + // Peer 3 active at time 300 + // Peer 2 active at time 400 + + connections.GetTimeSource().SetCurrentMonotonicTimeMs(500); + callInfo.callCount = 0; + connections.ExpireInactiveConnections(150); + + // peer 2 stays active + NL_TEST_ASSERT(inSuite, callInfo.callCount == 1); + NL_TEST_ASSERT(inSuite, callInfo.lastCallNodeId == kPeer3NodeId); + NL_TEST_ASSERT(inSuite, callInfo.lastCallPeerAddress == kPeer3Addr); + NL_TEST_ASSERT(inSuite, !connections.FindPeerConnectionState(kPeer1Addr, &statePtr)); + NL_TEST_ASSERT(inSuite, connections.FindPeerConnectionState(kPeer2Addr, &statePtr)); + NL_TEST_ASSERT(inSuite, !connections.FindPeerConnectionState(kPeer3Addr, &statePtr)); + + err = connections.CreateNewPeerConnectionState(kPeer1Addr, nullptr); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, connections.FindPeerConnectionState(kPeer1Addr, &statePtr)); + NL_TEST_ASSERT(inSuite, connections.FindPeerConnectionState(kPeer2Addr, &statePtr)); + NL_TEST_ASSERT(inSuite, !connections.FindPeerConnectionState(kPeer3Addr, &statePtr)); + + // peer 1 and 2 are active + connections.GetTimeSource().SetCurrentMonotonicTimeMs(1000); + callInfo.callCount = 0; + connections.ExpireInactiveConnections(100); + NL_TEST_ASSERT(inSuite, callInfo.callCount == 2); // everything expired + NL_TEST_ASSERT(inSuite, !connections.FindPeerConnectionState(kPeer1Addr, &statePtr)); + NL_TEST_ASSERT(inSuite, !connections.FindPeerConnectionState(kPeer2Addr, &statePtr)); + NL_TEST_ASSERT(inSuite, !connections.FindPeerConnectionState(kPeer3Addr, &statePtr)); +} + +} // namespace + +// clang-format off +static const nlTest sTests[] = +{ + NL_TEST_DEF("BasicFunctionality", TestBasicFunctionality), + NL_TEST_DEF("FindByPeerAddress", TestFindByAddress), + NL_TEST_DEF("FindByNodeId", TestFindByNodeId), + NL_TEST_DEF("ExpireConnections", TestExpireConnections), + NL_TEST_SENTINEL() +}; +// clang-format on + +int TestPeerConnections(void) +{ + nlTestSuite theSuite = { "Transport-PeerConnections", &sTests[0], NULL, NULL }; + nlTestRunner(&theSuite, NULL); + return nlTestRunnerStats(&theSuite); +} diff --git a/src/transport/tests/TestPeerConnectionsDriver.cpp b/src/transport/tests/TestPeerConnectionsDriver.cpp new file mode 100644 index 00000000000000..75ec048bc3a7bd --- /dev/null +++ b/src/transport/tests/TestPeerConnectionsDriver.cpp @@ -0,0 +1,34 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements a standalone/native program executable + * test driver for the CHIP Transport Layer PeerConnections class unit + * tests. + * + */ + +#include "TestTransportLayer.h" + +#include + +int main(void) +{ + nlTestSetOutputStyle(OUTPUT_CSV); + return TestPeerConnections(); +} diff --git a/src/lib/core/tests/TestCHIPSecureChannel.cpp b/src/transport/tests/TestSecureSession.cpp similarity index 76% rename from src/lib/core/tests/TestCHIPSecureChannel.cpp rename to src/transport/tests/TestSecureSession.cpp index 561cc80fd2e920..e772ac7f1c89bc 100644 --- a/src/lib/core/tests/TestCHIPSecureChannel.cpp +++ b/src/transport/tests/TestSecureSession.cpp @@ -18,17 +18,16 @@ /** * @file - * This file implements unit tests for the CHIPSecureChannel implementation. + * This file implements unit tests for the SecureSession implementation. */ -#include "TestCore.h" +#include "TestTransportLayer.h" #include -#include #include #include -#include +#include #include #include @@ -60,7 +59,7 @@ static const unsigned char secure_channel_test_public_key2[] = { 0x04, 0x30, 0x7 void SecureChannelInitTest(nlTestSuite * inSuite, void * inContext) { - ChipSecureChannel channel; + SecureSession channel; // Test all combinations of invalid parameters NL_TEST_ASSERT(inSuite, channel.Init(NULL, 0, NULL, 0, NULL, 0, NULL, 0) == CHIP_ERROR_INVALID_ARGUMENT); NL_TEST_ASSERT(inSuite, @@ -95,7 +94,7 @@ void SecureChannelInitTest(nlTestSuite * inSuite, void * inContext) // Test the channel can be initialized with valid salt const char * salt = "Test Salt"; - ChipSecureChannel channel2; + SecureSession channel2; NL_TEST_ASSERT(inSuite, channel2.Init(secure_channel_test_public_key1, sizeof(secure_channel_test_public_key1), secure_channel_test_private_key2, sizeof(secure_channel_test_private_key2), @@ -105,14 +104,15 @@ void SecureChannelInitTest(nlTestSuite * inSuite, void * inContext) void SecureChannelEncryptTest(nlTestSuite * inSuite, void * inContext) { - ChipSecureChannel channel; + SecureSession channel; const unsigned char plain_text[] = { 0x86, 0x74, 0x64, 0xe5, 0x0b, 0xd4, 0x0d, 0x90, 0xe1, 0x17, 0xa3, 0x2d, 0x4b, 0xd4, 0xe1, 0xe6 }; unsigned char output[128]; + MessageHeader header; // Test uninitialized channel - NL_TEST_ASSERT( - inSuite, channel.Encrypt(plain_text, sizeof(plain_text), output, sizeof(output)) == CHIP_ERROR_INVALID_USE_OF_SESSION_KEY); + NL_TEST_ASSERT(inSuite, + channel.Encrypt(plain_text, sizeof(plain_text), output, header) == CHIP_ERROR_INVALID_USE_OF_SESSION_KEY); const char * info = "Test Info"; const char * salt = "Test Salt"; @@ -123,32 +123,21 @@ void SecureChannelEncryptTest(nlTestSuite * inSuite, void * inContext) sizeof(info)) == CHIP_NO_ERROR); // Test initialized channel, but invalid arguments - NL_TEST_ASSERT(inSuite, channel.Encrypt(NULL, 0, NULL, 0) == CHIP_ERROR_INVALID_ARGUMENT); - NL_TEST_ASSERT(inSuite, channel.Encrypt(plain_text, 0, NULL, 0) == CHIP_ERROR_INVALID_ARGUMENT); - NL_TEST_ASSERT(inSuite, channel.Encrypt(plain_text, sizeof(plain_text), NULL, 0) == CHIP_ERROR_INVALID_ARGUMENT); - NL_TEST_ASSERT(inSuite, channel.Encrypt(plain_text, sizeof(plain_text), output, 0) == CHIP_ERROR_INVALID_ARGUMENT); - NL_TEST_ASSERT(inSuite, - channel.Encrypt(plain_text, sizeof(plain_text), output, sizeof(plain_text)) == CHIP_ERROR_INVALID_ARGUMENT); - NL_TEST_ASSERT(inSuite, channel.EncryptionOverhead() <= sizeof(output) - sizeof(plain_text)); - NL_TEST_ASSERT(inSuite, - channel.Encrypt(plain_text, sizeof(plain_text), output, sizeof(plain_text) + channel.EncryptionOverhead() - 1) == - CHIP_ERROR_INVALID_ARGUMENT); + NL_TEST_ASSERT(inSuite, channel.Encrypt(NULL, 0, NULL, header) == CHIP_ERROR_INVALID_ARGUMENT); + NL_TEST_ASSERT(inSuite, channel.Encrypt(plain_text, 0, NULL, header) == CHIP_ERROR_INVALID_ARGUMENT); + NL_TEST_ASSERT(inSuite, channel.Encrypt(plain_text, sizeof(plain_text), NULL, header) == CHIP_ERROR_INVALID_ARGUMENT); // Valid arguments - NL_TEST_ASSERT(inSuite, - channel.Encrypt(plain_text, sizeof(plain_text), output, sizeof(plain_text) + channel.EncryptionOverhead()) == - CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, - channel.Encrypt(plain_text, sizeof(plain_text), output, sizeof(plain_text) + channel.EncryptionOverhead() + 1) == - CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, channel.Encrypt(plain_text, sizeof(plain_text), output, header) == CHIP_NO_ERROR); } void SecureChannelDecryptTest(nlTestSuite * inSuite, void * inContext) { - ChipSecureChannel channel; + SecureSession channel; const unsigned char plain_text[] = { 0x86, 0x74, 0x64, 0xe5, 0x0b, 0xd4, 0x0d, 0x90, 0xe1, 0x17, 0xa3, 0x2d, 0x4b, 0xd4, 0xe1, 0xe6 }; unsigned char encrypted[128]; + MessageHeader header; const char * info = "Test Info"; const char * salt = "Test Salt"; @@ -158,17 +147,13 @@ void SecureChannelDecryptTest(nlTestSuite * inSuite, void * inContext) secure_channel_test_private_key2, sizeof(secure_channel_test_private_key2), (const unsigned char *) salt, sizeof(salt), (const unsigned char *) info, sizeof(info)) == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, - channel.Encrypt(plain_text, sizeof(plain_text), encrypted, sizeof(plain_text) + channel.EncryptionOverhead()) == - CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, channel.Encrypt(plain_text, sizeof(plain_text), encrypted, header) == CHIP_NO_ERROR); - ChipSecureChannel channel2; + SecureSession channel2; unsigned char output[128]; - size_t output_length = sizeof(output); // Uninitialized channel NL_TEST_ASSERT(inSuite, - channel2.Decrypt(encrypted, sizeof(plain_text) + channel.EncryptionOverhead(), output, output_length) == - CHIP_ERROR_INVALID_USE_OF_SESSION_KEY); + channel2.Decrypt(encrypted, sizeof(plain_text), output, header) == CHIP_ERROR_INVALID_USE_OF_SESSION_KEY); NL_TEST_ASSERT(inSuite, channel2.Init(secure_channel_test_public_key2, sizeof(secure_channel_test_public_key2), secure_channel_test_private_key1, sizeof(secure_channel_test_private_key1), @@ -176,24 +161,13 @@ void SecureChannelDecryptTest(nlTestSuite * inSuite, void * inContext) sizeof(info)) == CHIP_NO_ERROR); // Channel initialized, but invalid arguments to decrypt - output_length = 0; - NL_TEST_ASSERT(inSuite, channel2.Decrypt(NULL, 0, NULL, output_length) == CHIP_ERROR_INVALID_ARGUMENT); - NL_TEST_ASSERT(inSuite, channel2.Decrypt(encrypted, 0, NULL, output_length) == CHIP_ERROR_INVALID_ARGUMENT); - NL_TEST_ASSERT(inSuite, channel2.Decrypt(encrypted, sizeof(encrypted), NULL, output_length) == CHIP_ERROR_INVALID_ARGUMENT); - NL_TEST_ASSERT(inSuite, channel2.Decrypt(encrypted, sizeof(encrypted), output, output_length) == CHIP_ERROR_INVALID_ARGUMENT); + NL_TEST_ASSERT(inSuite, channel2.Decrypt(NULL, 0, NULL, header) == CHIP_ERROR_INVALID_ARGUMENT); + NL_TEST_ASSERT(inSuite, channel2.Decrypt(encrypted, 0, NULL, header) == CHIP_ERROR_INVALID_ARGUMENT); + NL_TEST_ASSERT(inSuite, channel2.Decrypt(encrypted, sizeof(encrypted), NULL, header) == CHIP_ERROR_INVALID_ARGUMENT); // Valid arguments - output_length = sizeof(output); - NL_TEST_ASSERT(inSuite, - channel2.Decrypt(encrypted, sizeof(plain_text) + channel.EncryptionOverhead(), output, output_length) == - CHIP_NO_ERROR); - - NL_TEST_ASSERT(inSuite, output_length == sizeof(plain_text)); - NL_TEST_ASSERT(inSuite, memcmp(plain_text, output, sizeof(plain_text)) == 0); - - NL_TEST_ASSERT(inSuite, channel2.Decrypt(encrypted, sizeof(encrypted), output, output_length) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, channel2.Decrypt(encrypted, sizeof(plain_text), output, header) == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, output_length == sizeof(plain_text)); NL_TEST_ASSERT(inSuite, memcmp(plain_text, output, sizeof(plain_text)) == 0); } @@ -226,7 +200,7 @@ static nlTestSuite sSuite = /** * Main */ -int TestCHIPSecureChannel() +int TestSecureSession() { // Run test suit against one context nlTestRunner(&sSuite, NULL); @@ -236,12 +210,10 @@ int TestCHIPSecureChannel() namespace chip { namespace Logging { -void Log(uint8_t module, uint8_t category, const char * format, ...) +void LogV(uint8_t module, uint8_t category, const char * format, va_list argptr) { - va_list argptr; - va_start(argptr, format); + (void) module, (void) category; vfprintf(stderr, format, argptr); - va_end(argptr); } } // namespace Logging } // namespace chip diff --git a/src/lib/core/tests/TestCHIPSecureChannelDriver.cpp b/src/transport/tests/TestSecureSessionDriver.cpp similarity index 93% rename from src/lib/core/tests/TestCHIPSecureChannelDriver.cpp rename to src/transport/tests/TestSecureSessionDriver.cpp index 0017bb006d56ca..436fc29ec1889c 100644 --- a/src/lib/core/tests/TestCHIPSecureChannelDriver.cpp +++ b/src/transport/tests/TestSecureSessionDriver.cpp @@ -22,7 +22,7 @@ * */ -#include "TestCore.h" +#include "TestTransportLayer.h" #include @@ -31,5 +31,5 @@ int main(void) // Generate machine-readable, comma-separated value (CSV) output. nlTestSetOutputStyle(OUTPUT_CSV); - return (TestCHIPSecureChannel()); + return (TestSecureSession()); } diff --git a/src/transport/tests/TestUdpTransport.cpp b/src/transport/tests/TestSecureSessionMgr.cpp similarity index 67% rename from src/transport/tests/TestUdpTransport.cpp rename to src/transport/tests/TestSecureSessionMgr.cpp index 97977d493e0c1d..88b449be7ed589 100644 --- a/src/transport/tests/TestUdpTransport.cpp +++ b/src/transport/tests/TestSecureSessionMgr.cpp @@ -18,14 +18,14 @@ /** * @file - * This file implements unit tests for the UdpTransport implementation. + * This file implements unit tests for the SecureSessionMgr implementation. */ #include "TestTransportLayer.h" #include #include -#include +#include #include #include @@ -34,8 +34,6 @@ using namespace chip; -using NlTestUdpTransport = StatefulUdpTransport; - static int Initialize(void * aContext); static int Finalize(void * aContext); @@ -48,19 +46,34 @@ struct TestContext struct TestContext sContext; -static const char PAYLOAD[] = "Hello!"; +static const unsigned char local_private_key[] = { 0x00, 0xd1, 0x90, 0xd9, 0xb3, 0x95, 0x1c, 0x5f, 0xa4, 0xe7, 0x47, + 0x92, 0x5b, 0x0a, 0xa9, 0xa7, 0xc1, 0x1c, 0xe7, 0x06, 0x10, 0xe2, + 0xdd, 0x16, 0x41, 0x52, 0x55, 0xb7, 0xb8, 0x80, 0x8d, 0x87, 0xa1 }; + +static const unsigned char remote_public_key[] = { 0x04, 0xe2, 0x07, 0x64, 0xff, 0x6f, 0x6a, 0x91, 0xd9, 0xc2, 0xc3, 0x0a, 0xc4, + 0x3c, 0x56, 0x4b, 0x42, 0x8a, 0xf3, 0xb4, 0x49, 0x29, 0x39, 0x95, 0xa2, 0xf7, + 0x02, 0x8c, 0xa5, 0xce, 0xf3, 0xc9, 0xca, 0x24, 0xc5, 0xd4, 0x5c, 0x60, 0x79, + 0x48, 0x30, 0x3c, 0x53, 0x86, 0xd9, 0x23, 0xe6, 0x61, 0x1f, 0x5a, 0x3d, 0xdf, + 0x9f, 0xdc, 0x35, 0xea, 0xd0, 0xde, 0x16, 0x7e, 0x64, 0xde, 0x7f, 0x3c, 0xa6 }; + +static const char PAYLOAD[] = "Hello!"; +constexpr NodeId kSourceNodeId = 123654; +constexpr NodeId kDestinationNodeId = 111222333; + +int ReceiveHandlerCallCount = 0; -static void MessageReceiveHandler(NlTestUdpTransport * con, PacketBuffer * msgBuf, const IPPacketInfo * pktInfo) +static void MessageReceiveHandler(const MessageHeader & header, const Inet::IPPacketInfo & source, System::PacketBuffer * msgBuf, + nlTestSuite * inSuite) { + NL_TEST_ASSERT(inSuite, header.GetSourceNodeId() == Optional::Value(kSourceNodeId)); + NL_TEST_ASSERT(inSuite, header.GetDestinationNodeId() == Optional::Value(kDestinationNodeId)); + size_t data_len = msgBuf->DataLength(); int compare = memcmp(msgBuf->Start(), PAYLOAD, data_len); - NL_TEST_ASSERT(con->State(), compare == 0); -}; + NL_TEST_ASSERT(inSuite, compare == 0); -static void ReceiveErrorHandler(NlTestUdpTransport * con, CHIP_ERROR err, const IPPacketInfo * pktInfo) -{ - NL_TEST_ASSERT(con->State(), false); + ReceiveHandlerCallCount++; }; static void DriveIO(TestContext & ctx) @@ -123,28 +136,10 @@ void CheckSimpleInitTest(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); - NlTestUdpTransport conn(inSuite); - conn.Init(&ctx.mInetLayer); - CHIP_ERROR err = conn.Close(); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); -} - -void CheckSimpleConnectTest(nlTestSuite * inSuite, void * inContext) -{ - TestContext & ctx = *reinterpret_cast(inContext); + SecureSessionMgr conn; + CHIP_ERROR err; - IPAddress addr; - IPAddress::FromString("127.0.0.1", addr); - CHIP_ERROR err = CHIP_NO_ERROR; - - NlTestUdpTransport conn(inSuite); - conn.Init(&ctx.mInetLayer); - err = conn.Connect(addr, 0); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - err = conn.Connect(addr, 0); - NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INCORRECT_STATE); - - err = conn.Close(); + err = conn.Init(kSourceNodeId, &ctx.mInetLayer, Transport::UdpListenParameters()); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); } @@ -162,24 +157,30 @@ void CheckMessageTest(nlTestSuite * inSuite, void * inContext) IPAddress::FromString("127.0.0.1", addr); CHIP_ERROR err = CHIP_NO_ERROR; - NlTestUdpTransport conn(inSuite); - conn.Init(&ctx.mInetLayer); - conn.SetMessageReceiveHandler(MessageReceiveHandler); - conn.SetReceiveErrorHandler(ReceiveErrorHandler); + SecureSessionMgr conn; + + err = conn.Init(kSourceNodeId, &ctx.mInetLayer, Transport::UdpListenParameters().SetAddressType(addr.Type())); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = conn.Connect(kDestinationNodeId, Transport::PeerAddress::UDP(addr)); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - err = conn.Connect(addr, 0); + err = conn.ManualKeyExchange(remote_public_key, sizeof(remote_public_key), local_private_key, sizeof(local_private_key)); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + conn.SetMessageReceiveHandler(MessageReceiveHandler, inSuite); + // Should be able to send a message to itself by just calling send. - conn.SendMessage(buffer); + ReceiveHandlerCallCount = 0; + err = conn.SendMessage(kDestinationNodeId, buffer); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); // allow the send and recv enough time DriveIO(ctx); sleep(1); DriveIO(ctx); - err = conn.Close(); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, ReceiveHandlerCallCount == 1); } // Test Suite @@ -191,7 +192,6 @@ void CheckMessageTest(nlTestSuite * inSuite, void * inContext) static const nlTest sTests[] = { NL_TEST_DEF("Simple Init Test", CheckSimpleInitTest), - NL_TEST_DEF("Simple Connect Test", CheckSimpleConnectTest), NL_TEST_DEF("Message Self Test", CheckMessageTest), NL_TEST_SENTINEL() @@ -252,7 +252,7 @@ static int Finalize(void * aContext) /** * Main */ -int TestUdpTransport() +int TestSecureSessionMgr() { // Run test suit against one context nlTestRunner(&sSuite, &sContext); diff --git a/src/inet/tests/TestInetBufferDriver.cpp b/src/transport/tests/TestSecureSessionMgrDriver.cpp similarity index 67% rename from src/inet/tests/TestInetBufferDriver.cpp rename to src/transport/tests/TestSecureSessionMgrDriver.cpp index fd8f992a6fcb2f..9e64f0a8e00610 100644 --- a/src/inet/tests/TestInetBufferDriver.cpp +++ b/src/transport/tests/TestSecureSessionMgrDriver.cpp @@ -18,34 +18,18 @@ /** * @file * This file implements a standalone/native program executable - * test driver for the CHIP Internet (inet) library buffer unit - * tests. + * test driver for the CHIP core library CHIP Connection tests. * */ -#include "TestInetLayer.h" - -#if CHIP_SYSTEM_CONFIG_USE_LWIP -#include -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP +#include "TestTransportLayer.h" #include -#include - int main(void) { -#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - - // Init lwip -#if INET_LWIP - tcpip_init(NULL, NULL); -#endif - // Generate machine-readable, comma-separated value (CSV) output. nlTestSetOutputStyle(OUTPUT_CSV); -#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES - - return (TestInetBuffer()); + return (TestSecureSessionMgr()); } diff --git a/src/transport/tests/TestTransportLayer.h b/src/transport/tests/TestTransportLayer.h index 60abdbd5f82d2d..293397ffd615b4 100644 --- a/src/transport/tests/TestTransportLayer.h +++ b/src/transport/tests/TestTransportLayer.h @@ -30,7 +30,10 @@ extern "C" { #endif int TestMessageHeader(void); -int TestUdpTransport(void); +int TestPeerConnections(void); +int TestSecureSession(void); +int TestSecureSessionMgr(void); +int TestUDP(void); #ifdef __cplusplus } diff --git a/src/transport/tests/TestUDP.cpp b/src/transport/tests/TestUDP.cpp new file mode 100644 index 00000000000000..ab03afd791ca93 --- /dev/null +++ b/src/transport/tests/TestUDP.cpp @@ -0,0 +1,290 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + * This file implements unit tests for the UdpTransport implementation. + */ + +#include "TestTransportLayer.h" + +#include +#include +#include + +#include +#include + +#include + +using namespace chip; + +static int Initialize(void * aContext); +static int Finalize(void * aContext); + +namespace { + +constexpr NodeId kSourceNodeId = 123654; +constexpr NodeId kDestinationNodeId = 111222333; +constexpr uint32_t kMessageId = 18; + +struct TestContext +{ + nlTestSuite * mSuite; + System::Layer mSystemLayer; + InetLayer mInetLayer; +}; + +struct TestContext sContext; + +const char PAYLOAD[] = "Hello!"; +int ReceiveHandlerCallCount = 0; + +void MessageReceiveHandler(const MessageHeader & header, const Inet::IPPacketInfo & source, System::PacketBuffer * msgBuf, + nlTestSuite * inSuite) +{ + NL_TEST_ASSERT(inSuite, header.GetSourceNodeId() == Optional::Value(kSourceNodeId)); + NL_TEST_ASSERT(inSuite, header.GetDestinationNodeId() == Optional::Value(kDestinationNodeId)); + NL_TEST_ASSERT(inSuite, header.GetMessageId() == kMessageId); + + size_t data_len = msgBuf->DataLength(); + int compare = memcmp(msgBuf->Start(), PAYLOAD, data_len); + NL_TEST_ASSERT(inSuite, compare == 0); + + ReceiveHandlerCallCount++; +} + +void DriveIO(TestContext & ctx) +{ +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS + // Set the select timeout to 100ms + struct timeval aSleepTime; + aSleepTime.tv_sec = 0; + aSleepTime.tv_usec = 100 * 1000; + + fd_set readFDs, writeFDs, exceptFDs; + int numFDs = 0; + + FD_ZERO(&readFDs); + FD_ZERO(&writeFDs); + FD_ZERO(&exceptFDs); + + if (ctx.mSystemLayer.State() == System::kLayerState_Initialized) + ctx.mSystemLayer.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime); + + if (ctx.mInetLayer.State == Inet::InetLayer::kState_Initialized) + ctx.mInetLayer.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime); + + int selectRes = select(numFDs, &readFDs, &writeFDs, &exceptFDs, &aSleepTime); + if (selectRes < 0) + { + printf("select failed: %s\n", ErrorStr(System::MapErrorPOSIX(errno))); + NL_TEST_ASSERT(ctx.mSuite, false); + return; + } + + if (ctx.mSystemLayer.State() == System::kLayerState_Initialized) + { + ctx.mSystemLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); + } + + if (ctx.mInetLayer.State == Inet::InetLayer::kState_Initialized) + { + ctx.mInetLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); + } +#endif +} + +} // namespace + +CHIP_ERROR InitLayers(System::Layer & systemLayer, InetLayer & inetLayer) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + // Initialize the CHIP System Layer. + err = systemLayer.Init(NULL); + SuccessOrExit(err); + + // Initialize the CHIP Inet layer. + err = inetLayer.Init(systemLayer, NULL); + SuccessOrExit(err); + +exit: + return err; +} + +/////////////////////////// Init test + +void CheckSimpleInitTest(nlTestSuite * inSuite, void * inContext, Inet::IPAddressType type) +{ + TestContext & ctx = *reinterpret_cast(inContext); + + Transport::UDP udp; + + CHIP_ERROR err = udp.Init(&ctx.mInetLayer, Transport::UdpListenParameters().SetAddressType(type)); + + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); +} + +void CheckSimpleInitTest4(nlTestSuite * inSuite, void * inContext) +{ + CheckSimpleInitTest(inSuite, inContext, kIPAddressType_IPv4); +} + +void CheckSimpleInitTest6(nlTestSuite * inSuite, void * inContext) +{ + CheckSimpleInitTest(inSuite, inContext, kIPAddressType_IPv6); +} + +/////////////////////////// Messaging test + +void CheckMessageTest(nlTestSuite * inSuite, void * inContext, const IPAddress & addr) +{ + TestContext & ctx = *reinterpret_cast(inContext); + + size_t payload_len = sizeof(PAYLOAD); + + chip::System::PacketBuffer * buffer = chip::System::PacketBuffer::NewWithAvailableSize(payload_len); + memmove(buffer->Start(), PAYLOAD, payload_len); + buffer->SetDataLength(payload_len); + + CHIP_ERROR err = CHIP_NO_ERROR; + + Transport::UDP udp; + + err = udp.Init(&ctx.mInetLayer, Transport::UdpListenParameters().SetAddressType(addr.Type())); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + udp.SetMessageReceiveHandler(MessageReceiveHandler, inSuite); + ReceiveHandlerCallCount = 0; + + MessageHeader header; + header.SetSourceNodeId(kSourceNodeId).SetDestinationNodeId(kDestinationNodeId).SetMessageId(kMessageId); + + // Should be able to send a message to itself by just calling send. + err = udp.SendMessage(header, Transport::PeerAddress::UDP(addr), buffer); + if (err == System::MapErrorPOSIX(EADDRNOTAVAIL)) + { + // TODO: the underlying system does not support IPV6. This early return should + // be removed and error should be made fatal. + printf("%s:%u: System does NOT support IPV6.\n", __FILE__, __LINE__); + return; + } + + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + // allow the send and recv enough time + DriveIO(ctx); + sleep(1); + DriveIO(ctx); + + NL_TEST_ASSERT(inSuite, ReceiveHandlerCallCount == 1); +} + +void CheckMessageTest4(nlTestSuite * inSuite, void * inContext) +{ + IPAddress addr; + IPAddress::FromString("127.0.0.1", addr); + CheckMessageTest(inSuite, inContext, addr); +} + +void CheckMessageTest6(nlTestSuite * inSuite, void * inContext) +{ + IPAddress addr; + IPAddress::FromString("::1", addr); + CheckMessageTest(inSuite, inContext, addr); +} + +// Test Suite + +/** + * Test Suite that lists all the test functions. + */ +// clang-format off +static const nlTest sTests[] = +{ + NL_TEST_DEF("Simple Init Test IPV4", CheckSimpleInitTest4), + NL_TEST_DEF("Simple Init Test IPV6", CheckSimpleInitTest6), + + NL_TEST_DEF("Message Self Test IPV4", CheckMessageTest4), + NL_TEST_DEF("Message Self Test IPV6", CheckMessageTest6), + + NL_TEST_SENTINEL() +}; +// clang-format on + +// clang-format off +static nlTestSuite sSuite = +{ + "Test-CHIP-Udp", + &sTests[0], + Initialize, + Finalize +}; +// clang-format on + +/** + * Initialize the test suite. + */ +static int Initialize(void * aContext) +{ + TestContext & lContext = *reinterpret_cast(aContext); + + CHIP_ERROR err = InitLayers(lContext.mSystemLayer, lContext.mInetLayer); + if (err != CHIP_NO_ERROR) + { + return FAILURE; + } + lContext.mSuite = &sSuite; + + return SUCCESS; +} + +/** + * Finalize the test suite. + */ +static int Finalize(void * aContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + TestContext & lContext = *reinterpret_cast(aContext); + + lContext.mSuite = NULL; + + err = lContext.mSystemLayer.Shutdown(); + if (err != CHIP_NO_ERROR) + { + return FAILURE; + } + err = lContext.mInetLayer.Shutdown(); + if (err != CHIP_NO_ERROR) + { + return FAILURE; + } + return SUCCESS; +} + +/** + * Main + */ +int TestUDP() +{ + // Run test suit against one context + nlTestRunner(&sSuite, &sContext); + + return (nlTestRunnerStats(&sSuite)); +} diff --git a/src/transport/tests/TestUdpTransportDriver.cpp b/src/transport/tests/TestUDPDriver.cpp similarity index 96% rename from src/transport/tests/TestUdpTransportDriver.cpp rename to src/transport/tests/TestUDPDriver.cpp index 08991a95fdd8ec..98868711ee7aa0 100644 --- a/src/transport/tests/TestUdpTransportDriver.cpp +++ b/src/transport/tests/TestUDPDriver.cpp @@ -31,5 +31,5 @@ int main(void) // Generate machine-readable, comma-separated value (CSV) output. nlTestSetOutputStyle(OUTPUT_CSV); - return (TestUdpTransport()); + return (TestUDP()); } diff --git a/third_party/inipp/repo/inipp.url b/third_party/inipp/repo/inipp.url new file mode 100644 index 00000000000000..3a2649e6ee7b2a --- /dev/null +++ b/third_party/inipp/repo/inipp.url @@ -0,0 +1 @@ +https://github.com/mcmtroffaes/inipp.git diff --git a/third_party/inipp/repo/inipp.version b/third_party/inipp/repo/inipp.version new file mode 100644 index 00000000000000..1004b8e60a79b7 --- /dev/null +++ b/third_party/inipp/repo/inipp.version @@ -0,0 +1 @@ +2b708f36e9802938f766b7ee1d065dab60787112 diff --git a/third_party/inipp/repo/inipp/.gitattributes b/third_party/inipp/repo/inipp/.gitattributes new file mode 100644 index 00000000000000..1ff0c423042b46 --- /dev/null +++ b/third_party/inipp/repo/inipp/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/third_party/inipp/repo/inipp/.gitignore b/third_party/inipp/repo/inipp/.gitignore new file mode 100644 index 00000000000000..1c9a181a44b4f9 --- /dev/null +++ b/third_party/inipp/repo/inipp/.gitignore @@ -0,0 +1,242 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +[Xx]64/ +[Xx]86/ +[Bb]uild/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml + +# TODO: Un-comment the next line if you do not want to checkin +# your web deploy settings because they may include unencrypted +# passwords +#*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# LightSwitch generated files +GeneratedArtifacts/ +ModelManifest.xml + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ diff --git a/third_party/inipp/repo/inipp/.travis.yml b/third_party/inipp/repo/inipp/.travis.yml new file mode 100644 index 00000000000000..93526b3167d823 --- /dev/null +++ b/third_party/inipp/repo/inipp/.travis.yml @@ -0,0 +1,10 @@ +dist: trusty +language: cpp +addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.9'] +install: + - export CXX="g++-4.9" + - export CXXFLAGS="-g -std=c++1y -Wall -pedantic" +script: cmake . && make && make test diff --git a/third_party/inipp/repo/inipp/CMakeLists.txt b/third_party/inipp/repo/inipp/CMakeLists.txt new file mode 100644 index 00000000000000..da9ac293b0bf64 --- /dev/null +++ b/third_party/inipp/repo/inipp/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 2.6) +project(inipp) +include_directories("inipp/") +install(FILES inipp/inipp.h DESTINATION include) +enable_testing() +add_subdirectory("unittest/") diff --git a/third_party/inipp/repo/inipp/LICENSE.txt b/third_party/inipp/repo/inipp/LICENSE.txt new file mode 100644 index 00000000000000..04b1506446f839 --- /dev/null +++ b/third_party/inipp/repo/inipp/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Matthias C. M. Troffaes + +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/third_party/inipp/repo/inipp/README.md b/third_party/inipp/repo/inipp/README.md new file mode 100644 index 00000000000000..45ed89864f6054 --- /dev/null +++ b/third_party/inipp/repo/inipp/README.md @@ -0,0 +1,70 @@ +# IniPP + +Simple header-only C++ ini parser and generator. + +[![Build Status](https://travis-ci.org/mcmtroffaes/inipp.svg?branch=develop)](https://travis-ci.org/mcmtroffaes/inipp) [![Build status](https://ci.appveyor.com/api/projects/status/74hf86c4yhtmb1j5/branch/develop?svg=true)](https://ci.appveyor.com/project/mcmtroffaes/inipp/branch/develop) + +## Features + +* Header-only. +* Both parsing and generating. +* Wide character support for native unicode on Windows. +* Default section support (similar to Python's ConfigParser). +* Interpolation support (i.e. variable substitution, similar to Python's ConfigParser). +* Simple design and implementation. +* Permissive MIT license. + +## Example + +```cpp +#include +#include "inipp.h" + +int main() { + inipp::Ini ini; + std::ifstream is("example.ini"); + ini.parse(is); + std::cout << "raw ini file:" << std::endl; + ini.generate(std::cout); + ini.default_section(ini.sections["DEFAULT"]); + ini.interpolate(); + std::cout << "ini file after default section and interpolation:" << std::endl; + ini.generate(std::cout); + int compression_level = -1; + inipp::extract(ini.sections["bitbucket.org"]["CompressionLevel"], compression_level); + std::cout << "bitbucket.org compression level: " << compression_level << std::endl; + return 0; +} +``` + +## Parsing algorithm + +* The *section* is set to the empty string. + +* Every *line* is read from the file and trimmed from whitespace. + + * If *line* is empty or starts with ``;`` then nothing happens. + + * Otherwise, if *line* starts with ``[`` then *section* is changed + to the string between ``[`` and ``]``. If *line* does not end + with ``]`` then an error is reported. + + * Otherwise, if *line* contains an ``=`` sign, then all characters + before ``=`` are treated as *variable* and all characters + following ``=`` are treated as *value*. Both are trimmed. If the + variable was already assigned earlier, an error is + reported. Otherwise, the corresponding assigment is added to the + *section*. + + * Otherwise, the *line* is reported as an error. + +## Default section algorithm + +Insert every variable from the default section into every other section, without overwriting existing variables. + +## Interpolation algorithm + +1. Locally within each section, every occurrence "${variable}" is replaced by "${section:variable}". +2. Every occurrence of "${section:variable}" is replaced by its value. +3. The previous step is repeated until no more replacements are possible, or until the recursion depth (by default, 10) is reached. + diff --git a/third_party/inipp/repo/inipp/appveyor.yml b/third_party/inipp/repo/inipp/appveyor.yml new file mode 100644 index 00000000000000..255b984bc6b280 --- /dev/null +++ b/third_party/inipp/repo/inipp/appveyor.yml @@ -0,0 +1,15 @@ +version: 1.0.{build} +build_script: + - msbuild inipp.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + - nuget pack nuget/IniPP.nuspec +test_script: + - vstest.console /logger:Appveyor /Platform:x64 x64\Debug\unittest.dll +artifacts: + - path: '*.nupkg' +deploy: + provider: NuGet + api_key: + secure: AcLNa0DwCGKnLLFXN23NHZHcl1Xten7fgacP//Cp+5CkwgDLUMN5res8TEx3kkbM + artifact: /.*\.nupkg/ + on: + appveyor_repo_tag: true diff --git a/third_party/inipp/repo/inipp/example/example.cpp b/third_party/inipp/repo/inipp/example/example.cpp new file mode 100644 index 00000000000000..360e988bcd22fb --- /dev/null +++ b/third_party/inipp/repo/inipp/example/example.cpp @@ -0,0 +1,18 @@ +#include +#include "inipp.h" + +int main() { + inipp::Ini ini; + std::ifstream is("example.ini"); + ini.parse(is); + std::cout << "raw ini file:" << std::endl; + ini.generate(std::cout); + ini.default_section(ini.sections["DEFAULT"]); + ini.interpolate(); + std::cout << "ini file after default section and interpolation:" << std::endl; + ini.generate(std::cout); + int compression_level = -1; + inipp::extract(ini.sections["bitbucket.org"]["CompressionLevel"], compression_level); + std::cout << "bitbucket.org compression level: " << compression_level << std::endl; + return 0; +} diff --git a/third_party/inipp/repo/inipp/example/example.ini b/third_party/inipp/repo/inipp/example/example.ini new file mode 100644 index 00000000000000..3dd29875fea547 --- /dev/null +++ b/third_party/inipp/repo/inipp/example/example.ini @@ -0,0 +1,12 @@ +[DEFAULT] +ServerAliveInterval = 45 +Compression = yes +CompressionLevel = 9 +ForwardX11 = yes + +[bitbucket.org] +User = hg + +[topsecret.server.com] +Port = 50022 +ForwardX11 = no diff --git a/third_party/inipp/repo/inipp/example/example.vcxproj b/third_party/inipp/repo/inipp/example/example.vcxproj new file mode 100644 index 00000000000000..035a5a6c42bbed --- /dev/null +++ b/third_party/inipp/repo/inipp/example/example.vcxproj @@ -0,0 +1,137 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {51631F50-58C3-4D8D-B378-16CE4F4ACB4B} + example + 8.1 + + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + ..\inipp;$(IncludePath) + + + ..\inipp;$(IncludePath) + + + ..\inipp;$(IncludePath) + + + ..\inipp;$(IncludePath) + + + + Level3 + Disabled + true + + + true + + + + + Level3 + Disabled + true + + + true + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/third_party/inipp/repo/inipp/example/example.vcxproj.filters b/third_party/inipp/repo/inipp/example/example.vcxproj.filters new file mode 100644 index 00000000000000..d1efcefe5db2fa --- /dev/null +++ b/third_party/inipp/repo/inipp/example/example.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/third_party/inipp/repo/inipp/inipp.sln b/third_party/inipp/repo/inipp/inipp.sln new file mode 100644 index 00000000000000..8d7bdfb5dfd614 --- /dev/null +++ b/third_party/inipp/repo/inipp/inipp.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inipp", "inipp\inipp.vcxproj", "{4139EAC6-0B81-4120-9130-07E132310564}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittest", "unittest\unittest.vcxproj", "{93B31110-0449-4AD7-8EA8-18F7D31857B4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example\example.vcxproj", "{51631F50-58C3-4D8D-B378-16CE4F4ACB4B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4139EAC6-0B81-4120-9130-07E132310564}.Debug|x64.ActiveCfg = Debug|x64 + {4139EAC6-0B81-4120-9130-07E132310564}.Debug|x64.Build.0 = Debug|x64 + {4139EAC6-0B81-4120-9130-07E132310564}.Debug|x86.ActiveCfg = Debug|Win32 + {4139EAC6-0B81-4120-9130-07E132310564}.Debug|x86.Build.0 = Debug|Win32 + {4139EAC6-0B81-4120-9130-07E132310564}.Release|x64.ActiveCfg = Release|x64 + {4139EAC6-0B81-4120-9130-07E132310564}.Release|x64.Build.0 = Release|x64 + {4139EAC6-0B81-4120-9130-07E132310564}.Release|x86.ActiveCfg = Release|Win32 + {4139EAC6-0B81-4120-9130-07E132310564}.Release|x86.Build.0 = Release|Win32 + {93B31110-0449-4AD7-8EA8-18F7D31857B4}.Debug|x64.ActiveCfg = Debug|x64 + {93B31110-0449-4AD7-8EA8-18F7D31857B4}.Debug|x64.Build.0 = Debug|x64 + {93B31110-0449-4AD7-8EA8-18F7D31857B4}.Debug|x86.ActiveCfg = Debug|Win32 + {93B31110-0449-4AD7-8EA8-18F7D31857B4}.Debug|x86.Build.0 = Debug|Win32 + {93B31110-0449-4AD7-8EA8-18F7D31857B4}.Release|x64.ActiveCfg = Release|x64 + {93B31110-0449-4AD7-8EA8-18F7D31857B4}.Release|x64.Build.0 = Release|x64 + {93B31110-0449-4AD7-8EA8-18F7D31857B4}.Release|x86.ActiveCfg = Release|Win32 + {93B31110-0449-4AD7-8EA8-18F7D31857B4}.Release|x86.Build.0 = Release|Win32 + {51631F50-58C3-4D8D-B378-16CE4F4ACB4B}.Debug|x64.ActiveCfg = Debug|x64 + {51631F50-58C3-4D8D-B378-16CE4F4ACB4B}.Debug|x64.Build.0 = Debug|x64 + {51631F50-58C3-4D8D-B378-16CE4F4ACB4B}.Debug|x86.ActiveCfg = Debug|Win32 + {51631F50-58C3-4D8D-B378-16CE4F4ACB4B}.Debug|x86.Build.0 = Debug|Win32 + {51631F50-58C3-4D8D-B378-16CE4F4ACB4B}.Release|x64.ActiveCfg = Release|x64 + {51631F50-58C3-4D8D-B378-16CE4F4ACB4B}.Release|x64.Build.0 = Release|x64 + {51631F50-58C3-4D8D-B378-16CE4F4ACB4B}.Release|x86.ActiveCfg = Release|Win32 + {51631F50-58C3-4D8D-B378-16CE4F4ACB4B}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/third_party/inipp/repo/inipp/inipp/inipp.h b/third_party/inipp/repo/inipp/inipp/inipp.h new file mode 100644 index 00000000000000..41391567378ff8 --- /dev/null +++ b/third_party/inipp/repo/inipp/inipp/inipp.h @@ -0,0 +1,224 @@ +/* +MIT License + +Copyright (c) 2017-2020 Matthias C. M. Troffaes + +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. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace inipp { + +namespace detail { + +// trim functions based on http://stackoverflow.com/a/217605 + +template +inline void ltrim(std::basic_string & s) { + s.erase(s.begin(), + std::find_if(s.begin(), s.end(), + [](int ch) { return !std::isspace(ch); })); +} + +template +inline void rtrim(std::basic_string & s) { + s.erase(std::find_if(s.rbegin(), s.rend(), + [](int ch) { return !std::isspace(ch); }).base(), + s.end()); +} + +// string replacement function based on http://stackoverflow.com/a/3418285 + +template +inline bool replace(std::basic_string & str, const std::basic_string & from, const std::basic_string & to) { + auto changed = false; + size_t start_pos = 0; + while ((start_pos = str.find(from, start_pos)) != std::basic_string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + changed = true; + } + return changed; +} + +} // namespace detail + +template +inline bool extract(const std::basic_string & value, T & dst) { + CharT c; + std::basic_istringstream is{ value }; + T result; + if ((is >> std::boolalpha >> result) && !(is >> c)) { + dst = result; + return true; + } + else { + return false; + } +} + +template +inline bool extract(const std::basic_string & value, std::basic_string & dst) { + dst = value; + return true; +} + +template +class Ini +{ +public: + typedef std::basic_string String; + typedef std::map Section; + typedef std::map Sections; + + Sections sections; + std::list errors; + + static const CharT char_section_start = (CharT)'['; + static const CharT char_section_end = (CharT)']'; + static const CharT char_assign = (CharT)'='; + static const CharT char_comment = (CharT)';'; + static const CharT char_interpol = (CharT)'$'; + static const CharT char_interpol_start = (CharT)'{'; + static const CharT char_interpol_sep = (CharT)':'; + static const CharT char_interpol_end = (CharT)'}'; + + static const int max_interpolation_depth = 10; + + void generate(std::basic_ostream & os) const { + for (auto const & sec : sections) { + os << char_section_start << sec.first << char_section_end << std::endl; + for (auto const & val : sec.second) { + os << val.first << char_assign << val.second << std::endl; + } + os << std::endl; + } + } + + void parse(std::basic_istream & is) { + String line; + String section; + while (std::getline(is, line)) { + detail::ltrim(line); + detail::rtrim(line); + const auto length = line.length(); + if (length > 0) { + const auto pos = line.find_first_of(char_assign); + const auto & front = line.front(); + if (front == char_comment) { + continue; + } + else if (front == char_section_start) { + if (line.back() == char_section_end) + section = line.substr(1, length - 2); + else + errors.push_back(line); + } + else if (pos != 0 && pos != String::npos) { + String variable(line.substr(0, pos)); + String value(line.substr(pos + 1, length)); + detail::rtrim(variable); + detail::ltrim(value); + auto & sec = sections[section]; + if (sec.find(variable) == sec.end()) + sec.insert(std::make_pair(variable, value)); + else + errors.push_back(line); + } + else { + errors.push_back(line); + } + } + } + } + + void interpolate() { + int global_iteration = 0; + auto changed = false; + // replace each "${variable}" by "${section:variable}" + for (auto & sec : sections) + replace_symbols(local_symbols(sec.first, sec.second), sec.second); + // replace each "${section:variable}" by its value + do { + changed = false; + const auto syms = global_symbols(); + for (auto & sec : sections) + changed |= replace_symbols(syms, sec.second); + } while (changed && (max_interpolation_depth > global_iteration++)); + } + + void default_section(const Section & sec) { + for (auto & sec2 : sections) + for (const auto & val : sec) + sec2.second.insert(val); + } + + void clear() { + sections.clear(); + errors.clear(); + } + +private: + typedef std::list > Symbols; + + String local_symbol(const String & name) const { + return char_interpol + (char_interpol_start + name + char_interpol_end); + } + + String global_symbol(const String & sec_name, const String & name) const { + return local_symbol(sec_name + char_interpol_sep + name); + } + + String local_symbols(const String & sec_name, const Section & sec) const { + Symbols result; + for (const auto & val : sec) + result.push_back(std::make_pair(local_symbol(val.first), global_symbol(sec_name, val.first))); + return result; + } + + String global_symbols() const { + Symbols result; + for (const auto & sec : sections) + for (const auto & val : sec.second) + result.push_back( + std::make_pair(global_symbol(sec.first, val.first), val.second)); + return result; + } + + bool replace_symbols(const Symbols & syms, Section & sec) const { + auto changed = false; + for (auto & sym : syms) + for (auto & val : sec) + changed |= detail::replace(val.second, sym.first, sym.second); + return changed; + } +}; + +} // namespace inipp diff --git a/third_party/inipp/repo/inipp/inipp/inipp.vcxproj b/third_party/inipp/repo/inipp/inipp/inipp.vcxproj new file mode 100644 index 00000000000000..0b665b59087716 --- /dev/null +++ b/third_party/inipp/repo/inipp/inipp/inipp.vcxproj @@ -0,0 +1,118 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {4139EAC6-0B81-4120-9130-07E132310564} + inipp + 8.1 + + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + + + + + Level3 + Disabled + true + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + + + + + + + + + \ No newline at end of file diff --git a/third_party/inipp/repo/inipp/inipp/inipp.vcxproj.filters b/third_party/inipp/repo/inipp/inipp/inipp.vcxproj.filters new file mode 100644 index 00000000000000..65366b2445aaf0 --- /dev/null +++ b/third_party/inipp/repo/inipp/inipp/inipp.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + \ No newline at end of file diff --git a/third_party/inipp/repo/inipp/nuget/IniPP.nuspec b/third_party/inipp/repo/inipp/nuget/IniPP.nuspec new file mode 100644 index 00000000000000..9271f5b0774441 --- /dev/null +++ b/third_party/inipp/repo/inipp/nuget/IniPP.nuspec @@ -0,0 +1,21 @@ + + + + IniPP + 1.0.12 + Matthias C. M. Troffaes + Matthias C. M. Troffaes + https://github.com/mcmtroffaes/inipp + MIT + false + Simple header-only C++ ini parser, with support for default section and interpolation similar to Python's configparser. + Simple C++ ini parser. + + Copyright 2017-2020 + ini parser native nativepackage + + + + + + diff --git a/third_party/inipp/repo/inipp/nuget/IniPP.targets b/third_party/inipp/repo/inipp/nuget/IniPP.targets new file mode 100644 index 00000000000000..5bd1a766e817dc --- /dev/null +++ b/third_party/inipp/repo/inipp/nuget/IniPP.targets @@ -0,0 +1,8 @@ + + + + + $(MSBuildThisFileDirectory)include;%(AdditionalIncludeDirectories) + + + diff --git a/third_party/inipp/repo/inipp/unittest/CMakeLists.txt b/third_party/inipp/repo/inipp/unittest/CMakeLists.txt new file mode 100644 index 00000000000000..8779a33e5ef4aa --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(headertest headertest.cpp) +add_executable(unittest unittest.cpp) +add_test(test0 headertest) +add_test(test1 unittest) diff --git a/third_party/inipp/repo/inipp/unittest/headertest.cpp b/third_party/inipp/repo/inipp/unittest/headertest.cpp new file mode 100644 index 00000000000000..4a3d71411a0ba1 --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/headertest.cpp @@ -0,0 +1,5 @@ +#include "test.h" + +int main() { + return 0; +} diff --git a/third_party/inipp/repo/inipp/unittest/test.h b/third_party/inipp/repo/inipp/unittest/test.h new file mode 100644 index 00000000000000..04efdbdc67ee9e --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/test.h @@ -0,0 +1,50 @@ +#include "inipp.h" + +#include +#include + +using namespace inipp; + +template +static inline auto read_all(const std::string & filename) { + std::basic_ifstream is(filename); + std::basic_stringstream sstr; + sstr << is.rdbuf(); + return sstr.str(); +} + +template +static inline auto parse(const std::string & filename, Ini & ini) { + std::basic_ifstream is(filename); + ini.parse(is); +} + +template +static inline void errors(std::basic_ostream & os, const Ini & ini) { + for (auto const & err : ini.errors) { + os << err << std::endl; + } +} + +template +static inline auto test(const std::string & inifile, Ini & ini) { + std::basic_ostringstream os; + parse(inifile, ini); + os << ">>> ERRORS <<<" << std::endl; + errors(os, ini); + os << ">>> GENERATE <<<" << std::endl; + ini.generate(os); + os << ">>> INTERPOLATE <<<" << std::endl; + ini.interpolate(); + ini.generate(os); + return os.str(); +} + +template +static inline auto runtest(const char *inifile, const char *expectedfile, std::basic_ostream & os) { + Ini ini; + auto actual = test(inifile, ini); + auto expected = read_all(expectedfile); + os << actual; + return (actual == expected); +} diff --git a/third_party/inipp/repo/inipp/unittest/test1.ini b/third_party/inipp/repo/inipp/unittest/test1.ini new file mode 100644 index 00000000000000..ab38ca5be50d25 --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/test1.ini @@ -0,0 +1,2 @@ +[section] +variable=value diff --git a/third_party/inipp/repo/inipp/unittest/test1.output b/third_party/inipp/repo/inipp/unittest/test1.output new file mode 100644 index 00000000000000..ef4e9220ec991e --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/test1.output @@ -0,0 +1,9 @@ +>>> ERRORS <<< +>>> GENERATE <<< +[section] +variable=value + +>>> INTERPOLATE <<< +[section] +variable=value + diff --git a/third_party/inipp/repo/inipp/unittest/test2.ini b/third_party/inipp/repo/inipp/unittest/test2.ini new file mode 100644 index 00000000000000..b55717220cdcb3 --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/test2.ini @@ -0,0 +1,26 @@ +; comment + ; comment + +[section1] +var1=val1 +var2 = val2 +var3= val3 +var4 =val4 + var5 = val5 + + [section2] +x = ${section0:test} + y = ${var1} +z = ${x} + ${y} + ${section0:a} + ${b} +b = monkey + + [section0] +test = ${a} ${b} ${c} +a = hello +b = world + +[section3] +a = wot +b = yz +c = tis +d = ${section0:test} \ No newline at end of file diff --git a/third_party/inipp/repo/inipp/unittest/test2.output b/third_party/inipp/repo/inipp/unittest/test2.output new file mode 100644 index 00000000000000..c2fd0d6d3605cf --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/test2.output @@ -0,0 +1,51 @@ +>>> ERRORS <<< +>>> GENERATE <<< +[section0] +a=hello +b=world +test=${a} ${b} ${c} + +[section1] +var1=val1 +var2=val2 +var3=val3 +var4=val4 +var5=val5 + +[section2] +b=monkey +x=${section0:test} +y=${var1} +z=${x} + ${y} + ${section0:a} + ${b} + +[section3] +a=wot +b=yz +c=tis +d=${section0:test} + +>>> INTERPOLATE <<< +[section0] +a=hello +b=world +test=hello world ${c} + +[section1] +var1=val1 +var2=val2 +var3=val3 +var4=val4 +var5=val5 + +[section2] +b=monkey +x=hello world ${c} +y=${var1} +z=hello world ${c} + ${var1} + hello + monkey + +[section3] +a=wot +b=yz +c=tis +d=hello world ${c} + diff --git a/third_party/inipp/repo/inipp/unittest/test3.ini b/third_party/inipp/repo/inipp/unittest/test3.ini new file mode 100644 index 00000000000000..c48e373b7a5aab --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/test3.ini @@ -0,0 +1,84 @@ +[builtin] +timestamp = 20171126 +audio_format = s16le +audio_rate = 44100 +audio_num_channels = 2 +video_format = nv12 +width = 1920 +height = 1080 +framerate_numerator = 30000 +framerate_denominator = 1001 + +[export] +folder = d: +base = ${folder}\export-${builtin:timestamp} +video = ${base}-raw-video.yuv +audio = ${base}-raw-audio.raw + +[batch] +batchfile = ${export:base}-run-encoder.bat +command = ${encode0} & ${encode1} & pause +preset0 = lossless +preset1 = low +preset2 = medium +preset3 = high + +ffmpeg = "${export:folder}\ffmpeg.exe" -y +audioinput = -f ${builtin:audio_format} -ar ${builtin:audio_rate} -ac ${builtin:audio_num_channels} -i "${export:audio}" +videoinput = -f rawvideo -pix_fmt ${builtin:video_format} -s:v ${builtin:width}x${builtin:height} -r ${builtin:framerate_numerator}/${builtin:framerate_denominator} -i "${export:video}" +inputs = ${audioinput} ${videoinput} + +codecs0 = ${presets:${preset0}_filter} ${presets:${preset0}_audiocodec} ${presets:${preset0}_videocodec} +codecs1 = ${presets:${preset1}_filter} ${presets:${preset1}_audiocodec} ${presets:${preset1}_videocodec} +codecs2 = ${presets:${preset2}_filter} ${presets:${preset2}_audiocodec} ${presets:${preset2}_videocodec} +codecs3 = ${presets:${preset3}_filter} ${presets:${preset3}_audiocodec} ${presets:${preset3}_videocodec} +codecs4 = ${presets:${preset4}_filter} ${presets:${preset4}_audiocodec} ${presets:${preset4}_videocodec} +codecs5 = ${presets:${preset5}_filter} ${presets:${preset5}_audiocodec} ${presets:${preset5}_videocodec} +codecs6 = ${presets:${preset6}_filter} ${presets:${preset6}_audiocodec} ${presets:${preset6}_videocodec} +codecs7 = ${presets:${preset7}_filter} ${presets:${preset7}_audiocodec} ${presets:${preset7}_videocodec} +codecs8 = ${presets:${preset8}_filter} ${presets:${preset8}_audiocodec} ${presets:${preset8}_videocodec} +codecs9 = ${presets:${preset9}_filter} ${presets:${preset9}_audiocodec} ${presets:${preset9}_videocodec} + +output0 = ${export:base}-${preset0}.${presets:${preset0}_container} +output1 = ${export:base}-${preset1}.${presets:${preset1}_container} +output2 = ${export:base}-${preset2}.${presets:${preset2}_container} +output3 = ${export:base}-${preset3}.${presets:${preset3}_container} +output4 = ${export:base}-${preset4}.${presets:${preset4}_container} +output5 = ${export:base}-${preset5}.${presets:${preset5}_container} +output6 = ${export:base}-${preset6}.${presets:${preset6}_container} +output7 = ${export:base}-${preset7}.${presets:${preset7}_container} +output8 = ${export:base}-${preset8}.${presets:${preset8}_container} +output9 = ${export:base}-${preset9}.${presets:${preset9}_container} + +encode0 = ${ffmpeg} ${inputs} ${codecs0} "${output0}" +encode1 = ${ffmpeg} ${inputs} ${codecs1} "${output1}" +encode2 = ${ffmpeg} ${inputs} ${codecs2} "${output2}" +encode3 = ${ffmpeg} ${inputs} ${codecs3} "${output3}" +encode4 = ${ffmpeg} ${inputs} ${codecs4} "${output4}" +encode5 = ${ffmpeg} ${inputs} ${codecs5} "${output5}" +encode6 = ${ffmpeg} ${inputs} ${codecs6} "${output6}" +encode7 = ${ffmpeg} ${inputs} ${codecs7} "${output7}" +encode8 = ${ffmpeg} ${inputs} ${codecs8} "${output8}" +encode9 = ${ffmpeg} ${inputs} ${codecs9} "${output9}" + +[presets] +lossless_container = mkv +lossless_audiocodec = -c:a flac +lossless_videocodec = -c:v ffv1 +lossless_filter = + +high_container = mkv +high_audiocodec = -c:a aac -b:a 384k +high_videocodec = -c:v libx264 -crf 18 +high_filter = + +medium_container = mkv +medium_audiocodec = -c:a aac -b:a 192k +medium_videocodec = -c:v libx264 -crf 23 +medium_filter = -vf scale=-1:720 + +low_container = mkv +low_audiocodec = -c:a aac -b:a 96k +low_videocodec = -c:v libx264 -crf 28 +; 480p +low_filter = -vf scale=854:-1 diff --git a/third_party/inipp/repo/inipp/unittest/test3.output b/third_party/inipp/repo/inipp/unittest/test3.output new file mode 100644 index 00000000000000..0c0f4fc77ffeff --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/test3.output @@ -0,0 +1,157 @@ +>>> ERRORS <<< +>>> GENERATE <<< +[batch] +audioinput=-f ${builtin:audio_format} -ar ${builtin:audio_rate} -ac ${builtin:audio_num_channels} -i "${export:audio}" +batchfile=${export:base}-run-encoder.bat +codecs0=${presets:${preset0}_filter} ${presets:${preset0}_audiocodec} ${presets:${preset0}_videocodec} +codecs1=${presets:${preset1}_filter} ${presets:${preset1}_audiocodec} ${presets:${preset1}_videocodec} +codecs2=${presets:${preset2}_filter} ${presets:${preset2}_audiocodec} ${presets:${preset2}_videocodec} +codecs3=${presets:${preset3}_filter} ${presets:${preset3}_audiocodec} ${presets:${preset3}_videocodec} +codecs4=${presets:${preset4}_filter} ${presets:${preset4}_audiocodec} ${presets:${preset4}_videocodec} +codecs5=${presets:${preset5}_filter} ${presets:${preset5}_audiocodec} ${presets:${preset5}_videocodec} +codecs6=${presets:${preset6}_filter} ${presets:${preset6}_audiocodec} ${presets:${preset6}_videocodec} +codecs7=${presets:${preset7}_filter} ${presets:${preset7}_audiocodec} ${presets:${preset7}_videocodec} +codecs8=${presets:${preset8}_filter} ${presets:${preset8}_audiocodec} ${presets:${preset8}_videocodec} +codecs9=${presets:${preset9}_filter} ${presets:${preset9}_audiocodec} ${presets:${preset9}_videocodec} +command=${encode0} & ${encode1} & pause +encode0=${ffmpeg} ${inputs} ${codecs0} "${output0}" +encode1=${ffmpeg} ${inputs} ${codecs1} "${output1}" +encode2=${ffmpeg} ${inputs} ${codecs2} "${output2}" +encode3=${ffmpeg} ${inputs} ${codecs3} "${output3}" +encode4=${ffmpeg} ${inputs} ${codecs4} "${output4}" +encode5=${ffmpeg} ${inputs} ${codecs5} "${output5}" +encode6=${ffmpeg} ${inputs} ${codecs6} "${output6}" +encode7=${ffmpeg} ${inputs} ${codecs7} "${output7}" +encode8=${ffmpeg} ${inputs} ${codecs8} "${output8}" +encode9=${ffmpeg} ${inputs} ${codecs9} "${output9}" +ffmpeg="${export:folder}\ffmpeg.exe" -y +inputs=${audioinput} ${videoinput} +output0=${export:base}-${preset0}.${presets:${preset0}_container} +output1=${export:base}-${preset1}.${presets:${preset1}_container} +output2=${export:base}-${preset2}.${presets:${preset2}_container} +output3=${export:base}-${preset3}.${presets:${preset3}_container} +output4=${export:base}-${preset4}.${presets:${preset4}_container} +output5=${export:base}-${preset5}.${presets:${preset5}_container} +output6=${export:base}-${preset6}.${presets:${preset6}_container} +output7=${export:base}-${preset7}.${presets:${preset7}_container} +output8=${export:base}-${preset8}.${presets:${preset8}_container} +output9=${export:base}-${preset9}.${presets:${preset9}_container} +preset0=lossless +preset1=low +preset2=medium +preset3=high +videoinput=-f rawvideo -pix_fmt ${builtin:video_format} -s:v ${builtin:width}x${builtin:height} -r ${builtin:framerate_numerator}/${builtin:framerate_denominator} -i "${export:video}" + +[builtin] +audio_format=s16le +audio_num_channels=2 +audio_rate=44100 +framerate_denominator=1001 +framerate_numerator=30000 +height=1080 +timestamp=20171126 +video_format=nv12 +width=1920 + +[export] +audio=${base}-raw-audio.raw +base=${folder}\export-${builtin:timestamp} +folder=d: +video=${base}-raw-video.yuv + +[presets] +high_audiocodec=-c:a aac -b:a 384k +high_container=mkv +high_filter= +high_videocodec=-c:v libx264 -crf 18 +lossless_audiocodec=-c:a flac +lossless_container=mkv +lossless_filter= +lossless_videocodec=-c:v ffv1 +low_audiocodec=-c:a aac -b:a 96k +low_container=mkv +low_filter=-vf scale=854:-1 +low_videocodec=-c:v libx264 -crf 28 +medium_audiocodec=-c:a aac -b:a 192k +medium_container=mkv +medium_filter=-vf scale=-1:720 +medium_videocodec=-c:v libx264 -crf 23 + +>>> INTERPOLATE <<< +[batch] +audioinput=-f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" +batchfile=d:\export-20171126-run-encoder.bat +codecs0= -c:a flac -c:v ffv1 +codecs1=-vf scale=854:-1 -c:a aac -b:a 96k -c:v libx264 -crf 28 +codecs2=-vf scale=-1:720 -c:a aac -b:a 192k -c:v libx264 -crf 23 +codecs3= -c:a aac -b:a 384k -c:v libx264 -crf 18 +codecs4=${presets:${preset4}_filter} ${presets:${preset4}_audiocodec} ${presets:${preset4}_videocodec} +codecs5=${presets:${preset5}_filter} ${presets:${preset5}_audiocodec} ${presets:${preset5}_videocodec} +codecs6=${presets:${preset6}_filter} ${presets:${preset6}_audiocodec} ${presets:${preset6}_videocodec} +codecs7=${presets:${preset7}_filter} ${presets:${preset7}_audiocodec} ${presets:${preset7}_videocodec} +codecs8=${presets:${preset8}_filter} ${presets:${preset8}_audiocodec} ${presets:${preset8}_videocodec} +codecs9=${presets:${preset9}_filter} ${presets:${preset9}_audiocodec} ${presets:${preset9}_videocodec} +command="d:\ffmpeg.exe" -y -f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" -c:a flac -c:v ffv1 "d:\export-20171126-lossless.mkv" & "d:\ffmpeg.exe" -y -f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" -vf scale=854:-1 -c:a aac -b:a 96k -c:v libx264 -crf 28 "d:\export-20171126-low.mkv" & pause +encode0="d:\ffmpeg.exe" -y -f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" -c:a flac -c:v ffv1 "d:\export-20171126-lossless.mkv" +encode1="d:\ffmpeg.exe" -y -f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" -vf scale=854:-1 -c:a aac -b:a 96k -c:v libx264 -crf 28 "d:\export-20171126-low.mkv" +encode2="d:\ffmpeg.exe" -y -f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" -vf scale=-1:720 -c:a aac -b:a 192k -c:v libx264 -crf 23 "d:\export-20171126-medium.mkv" +encode3="d:\ffmpeg.exe" -y -f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" -c:a aac -b:a 384k -c:v libx264 -crf 18 "d:\export-20171126-high.mkv" +encode4="d:\ffmpeg.exe" -y -f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" ${presets:${preset4}_filter} ${presets:${preset4}_audiocodec} ${presets:${preset4}_videocodec} "d:\export-20171126-${preset4}.${presets:${preset4}_container}" +encode5="d:\ffmpeg.exe" -y -f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" ${presets:${preset5}_filter} ${presets:${preset5}_audiocodec} ${presets:${preset5}_videocodec} "d:\export-20171126-${preset5}.${presets:${preset5}_container}" +encode6="d:\ffmpeg.exe" -y -f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" ${presets:${preset6}_filter} ${presets:${preset6}_audiocodec} ${presets:${preset6}_videocodec} "d:\export-20171126-${preset6}.${presets:${preset6}_container}" +encode7="d:\ffmpeg.exe" -y -f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" ${presets:${preset7}_filter} ${presets:${preset7}_audiocodec} ${presets:${preset7}_videocodec} "d:\export-20171126-${preset7}.${presets:${preset7}_container}" +encode8="d:\ffmpeg.exe" -y -f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" ${presets:${preset8}_filter} ${presets:${preset8}_audiocodec} ${presets:${preset8}_videocodec} "d:\export-20171126-${preset8}.${presets:${preset8}_container}" +encode9="d:\ffmpeg.exe" -y -f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" ${presets:${preset9}_filter} ${presets:${preset9}_audiocodec} ${presets:${preset9}_videocodec} "d:\export-20171126-${preset9}.${presets:${preset9}_container}" +ffmpeg="d:\ffmpeg.exe" -y +inputs=-f s16le -ar 44100 -ac 2 -i "d:\export-20171126-raw-audio.raw" -f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" +output0=d:\export-20171126-lossless.mkv +output1=d:\export-20171126-low.mkv +output2=d:\export-20171126-medium.mkv +output3=d:\export-20171126-high.mkv +output4=d:\export-20171126-${preset4}.${presets:${preset4}_container} +output5=d:\export-20171126-${preset5}.${presets:${preset5}_container} +output6=d:\export-20171126-${preset6}.${presets:${preset6}_container} +output7=d:\export-20171126-${preset7}.${presets:${preset7}_container} +output8=d:\export-20171126-${preset8}.${presets:${preset8}_container} +output9=d:\export-20171126-${preset9}.${presets:${preset9}_container} +preset0=lossless +preset1=low +preset2=medium +preset3=high +videoinput=-f rawvideo -pix_fmt nv12 -s:v 1920x1080 -r 30000/1001 -i "d:\export-20171126-raw-video.yuv" + +[builtin] +audio_format=s16le +audio_num_channels=2 +audio_rate=44100 +framerate_denominator=1001 +framerate_numerator=30000 +height=1080 +timestamp=20171126 +video_format=nv12 +width=1920 + +[export] +audio=d:\export-20171126-raw-audio.raw +base=d:\export-20171126 +folder=d: +video=d:\export-20171126-raw-video.yuv + +[presets] +high_audiocodec=-c:a aac -b:a 384k +high_container=mkv +high_filter= +high_videocodec=-c:v libx264 -crf 18 +lossless_audiocodec=-c:a flac +lossless_container=mkv +lossless_filter= +lossless_videocodec=-c:v ffv1 +low_audiocodec=-c:a aac -b:a 96k +low_container=mkv +low_filter=-vf scale=854:-1 +low_videocodec=-c:v libx264 -crf 28 +medium_audiocodec=-c:a aac -b:a 192k +medium_container=mkv +medium_filter=-vf scale=-1:720 +medium_videocodec=-c:v libx264 -crf 23 + diff --git a/third_party/inipp/repo/inipp/unittest/test4.ini b/third_party/inipp/repo/inipp/unittest/test4.ini new file mode 100644 index 00000000000000..5123d45b3a1e42 --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/test4.ini @@ -0,0 +1,11 @@ +a=1 +[badsec +badsec] +=badvar += +bla +[] +b=2 +c=${:a} +d=${:e} +b=3 diff --git a/third_party/inipp/repo/inipp/unittest/test4.output b/third_party/inipp/repo/inipp/unittest/test4.output new file mode 100644 index 00000000000000..1bd75605437e35 --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/test4.output @@ -0,0 +1,21 @@ +>>> ERRORS <<< +[badsec +badsec] +=badvar += +bla +b=3 +>>> GENERATE <<< +[] +a=1 +b=2 +c=${:a} +d=${:e} + +>>> INTERPOLATE <<< +[] +a=1 +b=2 +c=1 +d=${:e} + diff --git a/third_party/inipp/repo/inipp/unittest/unittest.cpp b/third_party/inipp/repo/inipp/unittest/unittest.cpp new file mode 100644 index 00000000000000..3f9530f762ab72 --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/unittest.cpp @@ -0,0 +1,199 @@ +#include "test.h" + +#ifdef _MSC_VER +#include "CppUnitTest.h" +using namespace Microsoft::VisualStudio::CppUnitTestFramework; +#else +#define TEST_CLASS(T) class T +#define TEST_METHOD(Func) void Func() + +namespace Assert { + + void IsTrue(bool result) { + if (!result) throw std::runtime_error("expected true"); + } + + void IsFalse(bool result) { + if (result) throw std::runtime_error("expected false"); + } + + template + void AreEqual(const T & a, const T & b) { + if (a != b) throw std::runtime_error("expected equality"); + } + +} // namespace Assert + +namespace Logger { + + void WriteMessage(const char *msg) { + std::wcout << msg; + } + + void WriteMessage(const wchar_t *msg) { + std::wcout << msg; + } + +} // namespace Logger +#endif + +template +void WriteMessage(const Ini & ini) { + std::basic_ostringstream os; + ini.generate(os); + Logger::WriteMessage(os.str().c_str()); +} + +namespace unittest +{ + TEST_CLASS(UnitTest) + { + public: + + TEST_METHOD(TestParseGenerate1) + { + std::basic_ostringstream os; + Assert::IsTrue(runtest("test1.ini", "test1.output", os)); + Logger::WriteMessage(os.str().c_str()); + } + + TEST_METHOD(TestParseGenerate1W) + { + std::basic_ostringstream os; + Assert::IsTrue(runtest("test1.ini", "test1.output", os)); + Logger::WriteMessage(os.str().c_str()); + } + + TEST_METHOD(TestParseGenerate2) + { + std::basic_ostringstream os; + Assert::IsTrue(runtest("test2.ini", "test2.output", os)); + Logger::WriteMessage(os.str().c_str()); + } + + TEST_METHOD(TestParseGenerate2W) + { + std::basic_ostringstream os; + Assert::IsTrue(runtest("test2.ini", "test2.output", os)); + Logger::WriteMessage(os.str().c_str()); + } + + TEST_METHOD(TestParseGenerate3) + { + std::basic_ostringstream os; + Assert::IsTrue(runtest("test3.ini", "test3.output", os)); + Logger::WriteMessage(os.str().c_str()); + } + + TEST_METHOD(TestParseGenerate4) + { + std::basic_ostringstream os; + Assert::IsTrue(runtest("test4.ini", "test4.output", os)); + Logger::WriteMessage(os.str().c_str()); + } + + TEST_METHOD(TestInterpolate1) + { + Ini ini; + ini.sections["sec1"]["x"] = "${sec2:z}"; + ini.sections["sec1"]["y"] = "2"; + ini.sections["sec2"]["z"] = "${y}"; + ini.interpolate(); + // we do not want x to be 2 + Assert::AreEqual(ini.sections.at("sec1").at("x"), std::string("${y}")); + ini.generate(std::cout); + } + + TEST_METHOD(TestInfiniteRecursion1) + { + Ini ini; + ini.sections["test"]["x"] = "0 ${y}"; + ini.sections["test"]["y"] = "1 ${x}"; + ini.interpolate(); + WriteMessage(ini); + } + + TEST_METHOD(TestInfiniteRecursion2) + { + Ini ini; + ini.sections["test1"]["x"] = "0 ${test2:y}"; + ini.sections["test2"]["y"] = "1 ${test1:x}"; + ini.interpolate(); + WriteMessage(ini); + } + + TEST_METHOD(TestInfiniteRecursion3) + { + Ini ini; + ini.sections["test1"]["x"] = "${test2:x}"; + ini.sections["test2"]["x"] = "${test3:x}"; + ini.sections["test3"]["x"] = "${test4:x}"; + ini.sections["test4"]["x"] = "x${test1:x}"; + ini.interpolate(); + WriteMessage(ini); + } + + TEST_METHOD(TestExtract) + { + std::string str{ "oops" }; + int16_t i16{ 0 }; + int32_t i32{ 0 }; + bool bool_{ true }; + Assert::IsTrue(extract(std::string(), str)); + Assert::AreEqual(std::string{ }, str); + Assert::IsTrue(extract(std::string{ "hello" }, str)); + Assert::AreEqual(std::string{ "hello" }, str); + Assert::IsTrue(extract(std::string{ "hello world" }, str)); + Assert::AreEqual(std::string{ "hello world" }, str); + Assert::IsTrue(extract(std::string{ "-10" }, i16)); + Assert::AreEqual(int16_t{ -10 }, i16); + Assert::IsTrue(extract(std::string{ "false" }, bool_)); + Assert::AreEqual(false, bool_); + Assert::IsTrue(extract(std::string{ "-10" }, i16)); + Assert::AreEqual(int16_t{ -10 }, i16); + Assert::IsFalse(extract(std::string{ "xxx" }, i16)); + Assert::AreEqual(int16_t{ -10 }, i16); + Assert::IsFalse(extract(std::string{ "1000000" }, i16)); + Assert::AreEqual(int16_t{ -10 }, i16); + Assert::IsFalse(extract(std::string{ "-20 xxx" }, i16)); + Assert::AreEqual(int16_t{ -10 }, i16); + Assert::IsTrue(extract(std::string{ "1000000" }, i32)); + Assert::AreEqual(int32_t{ 1000000 }, i32); + } + + TEST_METHOD(TestDefault) + { + Ini ini; + ini.sections["sec0"]["a"] = "0"; + ini.sections["sec0"]["b"] = "1"; + ini.sections["sec1"]["b"] = "2"; + ini.sections["sec1"]["c"] = "${a} ${b}"; + ini.sections["sec2"]["a"] = "3"; + ini.sections["sec2"]["c"] = "${a} ${b}"; + ini.default_section(ini.sections.at("sec0")); + ini.interpolate(); + WriteMessage(ini); + Assert::AreEqual(ini.sections.at("sec1").at("c"), std::string("0 2")); + Assert::AreEqual(ini.sections.at("sec2").at("c"), std::string("3 1")); + } + }; +} // namespace unittest + +#ifndef _MSC_VER +int main() { + unittest::UnitTest test; + test.TestParseGenerate1(); + test.TestParseGenerate1W(); + test.TestParseGenerate2(); + test.TestParseGenerate2W(); + test.TestParseGenerate3(); + test.TestParseGenerate4(); + test.TestInfiniteRecursion1(); + test.TestInfiniteRecursion2(); + test.TestInfiniteRecursion3(); + test.TestInterpolate1(); + test.TestExtract(); + test.TestDefault(); + return 0; +} +#endif diff --git a/third_party/inipp/repo/inipp/unittest/unittest.vcxproj b/third_party/inipp/repo/inipp/unittest/unittest.vcxproj new file mode 100644 index 00000000000000..2ed15ac7fb7bac --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/unittest.vcxproj @@ -0,0 +1,173 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {93B31110-0449-4AD7-8EA8-18F7D31857B4} + Win32Proj + unittest + 8.1 + + + + DynamicLibrary + true + v140 + MultiByte + false + + + DynamicLibrary + false + v140 + true + MultiByte + false + + + DynamicLibrary + true + v140 + MultiByte + false + + + DynamicLibrary + false + v140 + true + MultiByte + false + + + + + + + + + + + + + + + + + + + + + true + ..\inipp;$(IncludePath) + + + true + ..\inipp;$(IncludePath) + + + true + ..\inipp;$(IncludePath) + + + true + ..\inipp;$(IncludePath) + + + + Level3 + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + for %%f in (*.ini, *.output) do xcopy /y /d %%f $(OutDir) + + + + + Level3 + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + _DEBUG;%(PreprocessorDefinitions) + true + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + for %%f in (*.ini, *.output) do xcopy /y /d %%f $(OutDir) + + + + + Level3 + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + for %%f in (*.ini, *.output) do xcopy /y /d %%f $(OutDir) + + + + + Level3 + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + NDEBUG;%(PreprocessorDefinitions) + true + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + for %%f in (*.ini, *.output) do xcopy /y /d %%f $(OutDir) + + + + + + + + + + + + \ No newline at end of file diff --git a/third_party/inipp/repo/inipp/unittest/unittest.vcxproj.filters b/third_party/inipp/repo/inipp/unittest/unittest.vcxproj.filters new file mode 100644 index 00000000000000..02e48170635f69 --- /dev/null +++ b/third_party/inipp/repo/inipp/unittest/unittest.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/third_party/mynewt-core/NOTICE b/third_party/mynewt-core/NOTICE new file mode 100644 index 00000000000000..20a92e2f58450d --- /dev/null +++ b/third_party/mynewt-core/NOTICE @@ -0,0 +1,8 @@ +Apache Mynewt +Copyright 2015-2020 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +Portions of this software were developed at +Runtime Inc, copyright 2015.