diff --git a/.github/workflows/darwin.yaml b/.github/workflows/darwin.yaml index bc4441a84f1..e2ee439bbdb 100644 --- a/.github/workflows/darwin.yaml +++ b/.github/workflows/darwin.yaml @@ -117,9 +117,18 @@ jobs: export TEST_RUNNER_ASAN_OPTIONS=__CURRENT_VALUE__:detect_stack_use_after_return=1 # Disable BLE (CHIP_IS_BLE=NO) because the app does not have the permission to use it and that may crash the CI. - xcodebuild test -target "Matter" -scheme "Matter Framework Tests" -sdk macosx ${{ matrix.options.arguments }} \ + xcodebuild test -target "Matter" -scheme "Matter Framework Tests" \ + -resultBundlePath /tmp/darwin/framework-tests/TestResults.xcresult \ + -sdk macosx ${{ matrix.options.arguments }} \ CHIP_IS_BLE=NO GCC_PREPROCESSOR_DEFINITIONS='${inherited} ${{ matrix.options.defines }}' \ > >(tee /tmp/darwin/framework-tests/darwin-tests.log) 2> >(tee /tmp/darwin/framework-tests/darwin-tests-err.log >&2) + - name: Generate Summary + if: always() + working-directory: /tmp + run: | + wget https://github.com/a7ex/xcresultparser/releases/download/1.8.4/xcresultparser.zip + unzip -j xcresultparser.zip + ./xcresultparser --output-format md --failed-tests-only /tmp/darwin/framework-tests/TestResults.xcresult >>"$GITHUB_STEP_SUMMARY" - name: Collect crash logs if: failure() && !env.ACT run: | diff --git a/.github/workflows/examples-esp32.yaml b/.github/workflows/examples-esp32.yaml index b41db0b4609..cd626fd0266 100644 --- a/.github/workflows/examples-esp32.yaml +++ b/.github/workflows/examples-esp32.yaml @@ -93,7 +93,7 @@ jobs: mv scripts/codegen.py.renamed scripts/codegen.py mv scripts/tools/zap/generate.py.renamed scripts/tools/zap/generate.py - name: Build example All Clusters App(Target:ESP32C3) - run: scripts/examples/esp_example.sh all-clusters-app sdkconfig.defaults.esp32c3 esp32c3 + run: scripts/examples/esp_example.sh all-clusters-app sdkconfig.defaults esp32c3 - name: Build example All Clusters App(Target:ESP32) run: | ./scripts/run_in_build_env.sh \ @@ -114,10 +114,10 @@ jobs: /tmp/bloat_reports/ - name: Build example Lighting App (Target:ESP32H2) - run: scripts/examples/esp_example.sh lighting-app sdkconfig.defaults.esp32h2 esp32h2 + run: scripts/examples/esp_example.sh lighting-app sdkconfig.defaults esp32h2 - name: Build example Lighting App (Target:ESP32C6) - run: scripts/examples/esp_example.sh lighting-app sdkconfig.defaults.esp32c6 esp32c6 + run: scripts/examples/esp_example.sh lighting-app sdkconfig.defaults esp32c6 - name: Uploading Size Reports uses: ./.github/actions/upload-size-reports @@ -163,7 +163,7 @@ jobs: run: scripts/examples/esp_example.sh ota-provider-app sdkconfig.defaults - name: Build example Light Switch App (Target:ESP32C3) - run: scripts/examples/esp_example.sh light-switch-app sdkconfig.defaults.esp32c3 esp32c3 + run: scripts/examples/esp_example.sh light-switch-app sdkconfig.defaults esp32c3 - name: Build example Lighting App (external platform) run: scripts/examples/esp_example.sh lighting-app sdkconfig.ext_plat.defaults @@ -178,4 +178,4 @@ jobs: run: scripts/examples/esp_example.sh pigweed-app sdkconfig.defaults - name: Build example Lock App (Target:ESP32C6) - run: scripts/examples/esp_example.sh lock-app sdkconfig.defaults.esp32c6 esp32c6 + run: scripts/examples/esp_example.sh lock-app sdkconfig.defaults esp32c6 diff --git a/.github/workflows/restyled.yml b/.github/workflows/restyled.yml index 9174483ecb7..4bb39f389cf 100644 --- a/.github/workflows/restyled.yml +++ b/.github/workflows/restyled.yml @@ -15,6 +15,10 @@ jobs: uses: actions/checkout@v4 - uses: restyled-io/actions/setup@v4 + with: + # Pin the Restyler version to v0.6.0.2 + tag: 'v0.6.0.2' + - id: restyler uses: restyled-io/actions/run@v4 with: diff --git a/.github/workflows/third-party-check.yaml b/.github/workflows/third-party-check.yaml index beda0af41b0..ad072da7558 100644 --- a/.github/workflows/third-party-check.yaml +++ b/.github/workflows/third-party-check.yaml @@ -21,6 +21,7 @@ on: paths: - "third_party/**" - ".gitmodules" + types: [opened, synchronize, reopened, labeled, unlabeled] jobs: check-submodule-update-label: @@ -32,7 +33,7 @@ jobs: run: | echo This pull request attempts to update submodules without the changing-submodules-on-purpose label. Please apply that label if the changes are intentional, or remove those changes. exit 1 - - if: ${{ contains(github.event.pull_request.labels.*.name, 'changing-submodules-on-purpose') }} + - if: ${{ contains(github.event.pull_request.labels.*.name, 'changing-submodules-on-purpose') }} name: Success run: | echo PR looks good. diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0a2b379ccb0..ff584f221e6 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -157,6 +157,30 @@ ] } }, + { + "label": "Build EFR32 Unit Tests and Test Runner", + "type": "shell", + "command": "source scripts/activate.sh; scripts/build/build_examples.py --target efr32-${input:efr32Board}-unit-test build; scripts/build/build_examples.py --target linux-x64-efr32-test-runner build; pip3 install out/linux-x64-efr32-test-runner/chip_pw_test_runner_wheels/*.whl --force-reinstall", + "problemMatcher": [] + }, + { + "label": "Rebuild EFR32 Unit Tests and Test Runner", + "type": "shell", + "command": "source scripts/activate.sh; rm -rf out/efr32-brd*-unit-test out/linux-x64-efr32-test-runner; scripts/build/build_examples.py --target efr32-${input:efr32Board}-unit-test build; scripts/build/build_examples.py --target linux-x64-efr32-test-runner build; pip3 install out/linux-x64-efr32-test-runner/chip_pw_test_runner_wheels/*.whl --force-reinstall", + "problemMatcher": [] + }, + { + "label": "Clean EFR32 Unit Tests and Test Runner", + "type": "shell", + "command": "rm -rf out/efr32-brd*-unit-test out/linux-x64-efr32-test-runner", + "problemMatcher": [] + }, + { + "label": "Run EFR32 Unit Tests", + "type": "shell", + "command": "source scripts/activate.sh; python -m src.test_driver.efr32.py.pw_test_runner.pw_test_runner -d /dev/ttyACM1 -f out/efr32-brd2703a-unit-test/tests -o out.log", + "problemMatcher": [] + }, { "label": "Flash EFR32 board", "type": "shell", @@ -646,6 +670,27 @@ "tizen-arm-light" ] }, + { + "type": "pickString", + "id": "efr32Board", + "description": "Which board", + "options": [ + "brd2704b", + "brd4316a", + "brd4317a", + "brd4318a", + "brd4319a", + "brd4186a", + "brd4187a", + "brd2601b", + "brd4187c", + "brd4186c", + "brd2703a", + "brd4338a", + "brd2605a", + "brd4343a" + ] + }, { "type": "promptString", "id": "tizenTargetDeviceAddress", diff --git a/credentials/generate_revocation_set.py b/credentials/generate_revocation_set.py index 3765439e02e..afd69dcc1dd 100755 --- a/credentials/generate_revocation_set.py +++ b/credentials/generate_revocation_set.py @@ -240,7 +240,13 @@ def generate_revocation_set_from_crl(crl_file: x509.CertificateRevocationList, except Exception: pass - serialnumber_list.append(bytes(str('{:02X}'.format(revoked_cert.serial_number)), 'utf-8').decode('utf-8')) + # Ensure the serial number is always a 2-byte aligned hex string. + # TestDACRevocationDelegateImpl encodes the serial number as an even-length hex string + # using BytesToHex in src/lib/support/BytesToHex.cpp. + # As the primary consumer of this data, we should use the same here. + serialnumber = '{:02X}'.format(revoked_cert.serial_number) + serialnumber = serialnumber if len(serialnumber) % 2 == 0 else '0' + serialnumber + serialnumber_list.append(serialnumber) entry = { "type": "revocation_set", diff --git a/credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-dac-01.json b/credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-dac-01.json new file mode 100644 index 00000000000..05dc6b0cc80 --- /dev/null +++ b/credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-dac-01.json @@ -0,0 +1,9 @@ +{ + "description": "Revoked DAC 01: use this with revocation-sets/revocation-set-for-pai.json", + "basic_info_pid": 32769, + "certification_declaration": "3081e706092a864886f70d010702a081d93081d6020103310d300b0609608648016503040201304306092a864886f70d010701a0360434152400012501f1ff3602050180182403162c0413435341303030303053574330303030302d303124050024060024070124080018317d307b020103801462fa823359acfaa9963e1cfa140addf504f37160300b0609608648016503040201300a06082a8648ce3d0403020447304502204dc6be89beeb5a49adec51ee7f0e6d1263ffc9e6238f2044385a5e0c86751b83022100ed902842f7a5784368d63eba6a2fb90086dd65a0ce3c283d86b915a3536afdac", + "pai_cert": "308201cb30820171a003020102020856ad8222ad945b64300a06082a8648ce3d04030230303118301606035504030c0f4d617474657220546573742050414131143012060a2b0601040182a27c02010c04464646313020170d3232303230353030303030305a180f39393939313233313233353935395a303d3125302306035504030c1c4d6174746572204465762050414920307846464631206e6f2050494431143012060a2b0601040182a27c02010c04464646313059301306072a8648ce3d020106082a8648ce3d03010703420004419a9315c2173e0c8c876d03ccfc944852647f7fec5e5082f4059928eca894c594151309ac631e4cb03392af684b0bafb7e65b3b8162c2f52bf931b8e77aaa82a366306430120603551d130101ff040830060101ff020100300e0603551d0f0101ff040403020106301d0603551d0e0416041463540e47f64b1c38d13884a462d16c195d8ffb3c301f0603551d230418301680146afd22771f511fecbf1641976710dcdc31a1717e300a06082a8648ce3d0403020348003045022100b2ef27f49ae9b50fb91eeac94c4d0bdbb8d7929c6cb88face529368d12054c0c0220655dc92b86bd909882a6c62177b825d7d05edbe7c22f9fea71220e7ea703f891", + "dac_cert": "308201e53082018ba003020102020819367d978eac533a300a06082a8648ce3d040302303d3125302306035504030c1c4d6174746572204465762050414920307846464631206e6f2050494431143012060a2b0601040182a27c02010c04464646313020170d3234313231333030303030305a180f39393939313233313233353935395a30503122302006035504030c194d61747465722044657620444143205265766f6b656420303131143012060a2b0601040182a27c02010c044646463131143012060a2b0601040182a27c02020c04383030313059301306072a8648ce3d020106082a8648ce3d030107034200042a3d7370f2209bb139b42ca6d95dbf2f701888b4718e32c63ae71327145e05187868b528f1a99d246f1470b0ccd855ee94383526ee090de0bb2fde038fee3e0ba360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e04160414f58ae953a4e6cfb0cc3de4dc4c33afe097aa1182301f0603551d2304183016801463540e47f64b1c38d13884a462d16c195d8ffb3c300a06082a8648ce3d0403020348003045022018336444494d4d18c7814b6f3b0f3f7fa5dd21256515031da31fd7ff8d1b10fd022100e75cbf171a38907a932a1679e5d5b423e6a166729d823fec844f22ef398912a4", + "dac_private_key": "246eea662a44cb6de93effd614e96d3715de8cfb6d4e975644ee5e66ecb79c79", + "dac_public_key": "042a3d7370f2209bb139b42ca6d95dbf2f701888b4718e32c63ae71327145e05187868b528f1a99d246f1470b0ccd855ee94383526ee090de0bb2fde038fee3e0b" +} diff --git a/credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-dac-02.json b/credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-dac-02.json new file mode 100644 index 00000000000..ab3f927e8bd --- /dev/null +++ b/credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-dac-02.json @@ -0,0 +1,9 @@ +{ + "description": "Revoked DAC 02: use this with revocation-sets/revocation-set-for-pai.json", + "basic_info_pid": 32769, + "certification_declaration": "3081e706092a864886f70d010702a081d93081d6020103310d300b0609608648016503040201304306092a864886f70d010701a0360434152400012501f1ff3602050180182403162c0413435341303030303053574330303030302d303124050024060024070124080018317d307b020103801462fa823359acfaa9963e1cfa140addf504f37160300b0609608648016503040201300a06082a8648ce3d0403020447304502204dc6be89beeb5a49adec51ee7f0e6d1263ffc9e6238f2044385a5e0c86751b83022100ed902842f7a5784368d63eba6a2fb90086dd65a0ce3c283d86b915a3536afdac", + "pai_cert": "308201cb30820171a003020102020856ad8222ad945b64300a06082a8648ce3d04030230303118301606035504030c0f4d617474657220546573742050414131143012060a2b0601040182a27c02010c04464646313020170d3232303230353030303030305a180f39393939313233313233353935395a303d3125302306035504030c1c4d6174746572204465762050414920307846464631206e6f2050494431143012060a2b0601040182a27c02010c04464646313059301306072a8648ce3d020106082a8648ce3d03010703420004419a9315c2173e0c8c876d03ccfc944852647f7fec5e5082f4059928eca894c594151309ac631e4cb03392af684b0bafb7e65b3b8162c2f52bf931b8e77aaa82a366306430120603551d130101ff040830060101ff020100300e0603551d0f0101ff040403020106301d0603551d0e0416041463540e47f64b1c38d13884a462d16c195d8ffb3c301f0603551d230418301680146afd22771f511fecbf1641976710dcdc31a1717e300a06082a8648ce3d0403020348003045022100b2ef27f49ae9b50fb91eeac94c4d0bdbb8d7929c6cb88face529368d12054c0c0220655dc92b86bd909882a6c62177b825d7d05edbe7c22f9fea71220e7ea703f891", + "dac_cert": "308201e53082018ba00302010202082569383d24bb36ea300a06082a8648ce3d040302303d3125302306035504030c1c4d6174746572204465762050414920307846464631206e6f2050494431143012060a2b0601040182a27c02010c04464646313020170d3234313231333030303030305a180f39393939313233313233353935395a30503122302006035504030c194d61747465722044657620444143205265766f6b656420303231143012060a2b0601040182a27c02010c044646463131143012060a2b0601040182a27c02020c04383030313059301306072a8648ce3d020106082a8648ce3d03010703420004d9713c48a59527e93e7a3e549e8fb0e00829d4a02c8cedf28dfd74672b00198c7ee43f35e3ace1d1ab497b32dde4cdd6a8476162191c9514aef7bf115fb6c472a360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e04160414e3af9182b3340fdc85c7a58b4b3884a6554b314f301f0603551d2304183016801463540e47f64b1c38d13884a462d16c195d8ffb3c300a06082a8648ce3d0403020348003045022100afbe8ff0962e875a3054ee5a2df5c4c78c05465b40d3103f99f7e5628780f90302201a6157f0df7823223ae24aa3a8f7a6137b4914c6b6f5e40a297d7ab771e9cbc6", + "dac_private_key": "22f8d19a6b524130eccad5d187ce8eb7ea9d7ffd5868b4e0ca91bc2b6c84399c", + "dac_public_key": "04d9713c48a59527e93e7a3e549e8fb0e00829d4a02c8cedf28dfd74672b00198c7ee43f35e3ace1d1ab497b32dde4cdd6a8476162191c9514aef7bf115fb6c472" +} diff --git a/credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-dac-03.json b/credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-dac-03.json new file mode 100644 index 00000000000..a629f86bd57 --- /dev/null +++ b/credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-dac-03.json @@ -0,0 +1,9 @@ +{ + "description": "Revoked DAC 03: use this with revocation-sets/revocation-set-for-pai.json", + "basic_info_pid": 32769, + "certification_declaration": "3081e706092a864886f70d010702a081d93081d6020103310d300b0609608648016503040201304306092a864886f70d010701a0360434152400012501f1ff3602050180182403162c0413435341303030303053574330303030302d303124050024060024070124080018317d307b020103801462fa823359acfaa9963e1cfa140addf504f37160300b0609608648016503040201300a06082a8648ce3d0403020447304502204dc6be89beeb5a49adec51ee7f0e6d1263ffc9e6238f2044385a5e0c86751b83022100ed902842f7a5784368d63eba6a2fb90086dd65a0ce3c283d86b915a3536afdac", + "pai_cert": "308201cb30820171a003020102020856ad8222ad945b64300a06082a8648ce3d04030230303118301606035504030c0f4d617474657220546573742050414131143012060a2b0601040182a27c02010c04464646313020170d3232303230353030303030305a180f39393939313233313233353935395a303d3125302306035504030c1c4d6174746572204465762050414920307846464631206e6f2050494431143012060a2b0601040182a27c02010c04464646313059301306072a8648ce3d020106082a8648ce3d03010703420004419a9315c2173e0c8c876d03ccfc944852647f7fec5e5082f4059928eca894c594151309ac631e4cb03392af684b0bafb7e65b3b8162c2f52bf931b8e77aaa82a366306430120603551d130101ff040830060101ff020100300e0603551d0f0101ff040403020106301d0603551d0e0416041463540e47f64b1c38d13884a462d16c195d8ffb3c301f0603551d230418301680146afd22771f511fecbf1641976710dcdc31a1717e300a06082a8648ce3d0403020348003045022100b2ef27f49ae9b50fb91eeac94c4d0bdbb8d7929c6cb88face529368d12054c0c0220655dc92b86bd909882a6c62177b825d7d05edbe7c22f9fea71220e7ea703f891", + "dac_cert": "308201e63082018ba00302010202080ab042494323fe54300a06082a8648ce3d040302303d3125302306035504030c1c4d6174746572204465762050414920307846464631206e6f2050494431143012060a2b0601040182a27c02010c04464646313020170d3234313231333030303030305a180f39393939313233313233353935395a30503122302006035504030c194d61747465722044657620444143205265766f6b656420303331143012060a2b0601040182a27c02010c044646463131143012060a2b0601040182a27c02020c04383030313059301306072a8648ce3d020106082a8648ce3d0301070342000422e57365e98990ff7506e11ebec33ff834a39999f2d9d635ef5847ebe058d87d1bfbe1e5e2411bb6dc11f3f27e842cc0ccabe02a4f269d50f5ca072a2635db04a360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e041604142ad1e7d3f1480029366ee4c5f07107f24a9d9bad301f0603551d2304183016801463540e47f64b1c38d13884a462d16c195d8ffb3c300a06082a8648ce3d0403020349003046022100e66960cc3b6ea61f8830bf788830166b5c6a318e83284604a95bc2655bd874eb022100f503806797a512ab509312e8082fc828622696d3d7501f7f3e68b61abfe5bdb8", + "dac_private_key": "98c2d8b375f1d792e572c701b4740e7fb81a9e9cc936aa2a0f876ce8dae5578c", + "dac_public_key": "0422e57365e98990ff7506e11ebec33ff834a39999f2d9d635ef5847ebe058d87d1bfbe1e5e2411bb6dc11f3f27e842cc0ccabe02a4f269d50f5ca072a2635db04" +} diff --git a/credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-pai.json b/credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-pai.json new file mode 100644 index 00000000000..9f20a423330 --- /dev/null +++ b/credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-pai.json @@ -0,0 +1,9 @@ +{ + "description": "Revoked PAI: use this with revocation-sets/revocation-set-for-paa.json", + "basic_info_pid": 32769, + "certification_declaration": "3081e706092a864886f70d010702a081d93081d6020103310d300b0609608648016503040201304306092a864886f70d010701a0360434152400012501f1ff3602050180182403162c0413435341303030303053574330303030302d303124050024060024070124080018317d307b020103801462fa823359acfaa9963e1cfa140addf504f37160300b0609608648016503040201300a06082a8648ce3d0403020447304502204dc6be89beeb5a49adec51ee7f0e6d1263ffc9e6238f2044385a5e0c86751b83022100ed902842f7a5784368d63eba6a2fb90086dd65a0ce3c283d86b915a3536afdac", + "pai_cert": "308201d43082017aa0030201020208302664392b8a3f2a300a06082a8648ce3d04030230303118301606035504030c0f4d617474657220546573742050414131143012060a2b0601040182a27c02010c04464646313020170d3234313231333030303030305a180f39393939313233313233353935395a3046312e302c06035504030c254d617474657220546573742050414920307846464631206e6f20504944205265766f6b656431143012060a2b0601040182a27c02010c04464646313059301306072a8648ce3d020106082a8648ce3d03010703420004bd58a84f7862e0751c4e56280f149697df7c51aadc5a4fa393c96277a59079de40907ab7860d069de652ea8bc8f7053bfe7c3a8ef4700d76b9cc20db312caf91a366306430120603551d130101ff040830060101ff020100300e0603551d0f0101ff040403020106301d0603551d0e0416041491337c5cfe7bb29376fe887d3c94e7f59dd83d2f301f0603551d230418301680146afd22771f511fecbf1641976710dcdc31a1717e300a06082a8648ce3d04030203480030450221009c74f6a84b3acb5a369de90399114ebf0a55150babd3d52b2898708847bc9c6e0220651881c81b7a20c346b97c68313c4be1c0a88f4f3a4072ed3c7ea11dc60984fc", + "dac_cert": "308201e33082018aa00302010202087f1c17c6070c22d2300a06082a8648ce3d0403023046312e302c06035504030c254d617474657220546573742050414920307846464631206e6f20504944205265766f6b656431143012060a2b0601040182a27c02010c04464646313020170d3234313231333030303030305a180f39393939313233313233353935395a30463118301606035504030c0f4d617474657220546573742044414331143012060a2b0601040182a27c02010c044646463131143012060a2b0601040182a27c02020c04383030313059301306072a8648ce3d020106082a8648ce3d03010703420004b0d7f20c57c8240b975456ba886d2cb09cd4f9d2f5cf1406f463c97983f34ed5a36761f696d8aff4f3e282865bc37914deb512aae88e5a15a719a4b57a26640fa360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e0416041438248c0e17b222347e42aab9af3fdc1d3d614904301f0603551d2304183016801491337c5cfe7bb29376fe887d3c94e7f59dd83d2f300a06082a8648ce3d040302034700304402202bf8b3b554efe5b53f6612891a9a9e6cb7267a55257ef3414929b259d1b7a2e102206dcfd0d84ad5a32c9bd05eec4a1b0ff7a435feed4bc540f087b81f6ec5009ba2", + "dac_private_key": "91fa91640fee5dcc5ab56decb1cb4d2e0056c16b45283104c0c849ec13dcceef", + "dac_public_key": "04b0d7f20c57c8240b975456ba886d2cb09cd4f9d2f5cf1406f463c97983f34ed5a36761f696d8aff4f3e282865bc37914deb512aae88e5a15a719a4b57a26640f" +} diff --git a/credentials/test/revoked-attestation-certificates/revocation-sets/revocation-set-for-pai.json b/credentials/test/revoked-attestation-certificates/revocation-sets/revocation-set-for-pai.json index 6bf0d6f5ddd..46c3c1b66cd 100644 --- a/credentials/test/revoked-attestation-certificates/revocation-sets/revocation-set-for-pai.json +++ b/credentials/test/revoked-attestation-certificates/revocation-sets/revocation-set-for-pai.json @@ -4,7 +4,7 @@ "issuer_subject_key_id": "63540E47F64B1C38D13884A462D16C195D8FFB3C", "issuer_name": "MD0xJTAjBgNVBAMMHE1hdHRlciBEZXYgUEFJIDB4RkZGMSBubyBQSUQxFDASBgorBgEEAYKifAIBDARGRkYx", "revoked_serial_numbers": [ - "AB042494323FE54", + "0AB042494323FE54", "19367D978EAC533A", "2569383D24BB36EA" ], diff --git a/data_model/1.4/clusters/EcosystemInformationCluster.xml b/data_model/1.4/clusters/EcosystemInformationCluster.xml index 67c2bbbd108..ddebe956793 100644 --- a/data_model/1.4/clusters/EcosystemInformationCluster.xml +++ b/data_model/1.4/clusters/EcosystemInformationCluster.xml @@ -120,4 +120,18 @@ Davis, CA 95616, USA + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data_model/1.4/spec_sha b/data_model/1.4/spec_sha index b48b447d733..6f7d9bd245a 100644 --- a/data_model/1.4/spec_sha +++ b/data_model/1.4/spec_sha @@ -1 +1 @@ -3d8fc9ae05045a53fdaffd7e3593f92a48b10e30 +21812107312887c416632ed6dd2399af1f077548 diff --git a/docs/guides/device-attestation-revocation-guide.md b/docs/guides/device-attestation-revocation-guide.md new file mode 100644 index 00000000000..6ee4fee8461 --- /dev/null +++ b/docs/guides/device-attestation-revocation-guide.md @@ -0,0 +1,52 @@ +# Device Attestation Revocation Testing Guide + +## Overview + +The device attestation revocation tests help identify the devices with revoked +DACs (Device Attestation Certificates) and PAIs (Product Attestation +Intermediates) during commissioning. + +This guide demonstrates how to use a sample application and chip-tool to test +the device attestation revocation functionality. + +The sample application is injected with revoked DAC and/or PAI certificates. + +During commissioning, chip-tool is provided with a revocation set that is +pre-generated using the `generate_revocation_set.py` script. + +## Prerequisites + +- Matter application for Linux platform (e.g., examples/lighting-app/linux) +- DAC provider JSON file containing revoked DAC and/or PAI certificates +- chip-tool +- Device attestation revocation set for the respective DAC and/or PAI + +## Test Setup + +- Build the lighting-app/linux and chip-tool: + +``` +./scripts/examples/gn_build_example.sh examples/lighting-app/linux out/host +./scripts/examples/gn_build_example.sh examples/chip-tool out/host +``` + +- Run the lighting-app/linux: + +``` +./out/host/chip-lighting-app --dac_provider +``` + +- Run the chip-tool with the revocation set: + +``` +./out/host/chip-tool pairing onnetwork 11 20202021 --dac-revocation-set-path +``` + +### Test Vectors + +| Description | DAC Provider | Revocation Set | Expected Result | +| --------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | +| PAI revoked by PAA | [revoked-pai.json](../../credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-pai.json) | [revocation-set-for-paa.json](../../credentials/test/revoked-attestation-certificates/revocation-sets/revocation-set-for-paa.json) | Commissioning fails with `kPaiRevoked` (202) | +| DAC-01 revoked by PAI | [revoked-dac-01.json](../../credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-dac-01.json) | [revocation-set-for-pai.json](../../credentials/test/revoked-attestation-certificates/revocation-sets/revocation-set-for-pai.json) | Commissioning fails with `kDacRevoked` (302) | +| DAC-02 revoked by PAI | [revoked-dac-02.json](../../credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-dac-02.json) | [revocation-set-for-pai.json](../../credentials/test/revoked-attestation-certificates/revocation-sets/revocation-set-for-pai.json) | Commissioning fails with `kDacRevoked` (302) | +| DAC-03 revoked by PAI | [revoked-dac-03.json](../../credentials/test/revoked-attestation-certificates/dac-provider-test-vectors/revoked-dac-03.json) | [revocation-set-for-pai.json](../../credentials/test/revoked-attestation-certificates/revocation-sets/revocation-set-for-pai.json) | Commissioning fails with `kDacRevoked` (302) | diff --git a/examples/all-clusters-app/esp32/sdkconfig.defaults b/examples/all-clusters-app/esp32/sdkconfig.defaults index ed9adcc7d82..4dcff1583e4 100644 --- a/examples/all-clusters-app/esp32/sdkconfig.defaults +++ b/examples/all-clusters-app/esp32/sdkconfig.defaults @@ -19,14 +19,6 @@ # Some useful defaults for the demo app configuration. # - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - #enable BT CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y @@ -74,3 +66,6 @@ CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y CONFIG_LWIP_IPV6_NUM_ADDRESSES=6 CONFIG_MAX_GROUP_ENDPOINTS_PER_FABRIC=3 + +# Enable OTA Requestor +CONFIG_ENABLE_OTA_REQUESTOR=y diff --git a/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32c3 b/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32c3 index 8d7455d0d76..62c2cb2fd57 100644 --- a/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32c3 +++ b/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32c3 @@ -1,55 +1 @@ -# -# 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. -# -# Description: -# CI uses this to select the ESP32C3-DevKitM. -# CONFIG_IDF_TARGET="esp32c3" -CONFIG_IDF_TARGET_ESP32C3=y -CONFIG_DEVICE_TYPE_ESP32_C3_DEVKITM=y - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - -#enable BT -CONFIG_BT_ENABLED=y -CONFIG_BT_NIMBLE_ENABLED=y - -#enable lwip ipv6 autoconfig -CONFIG_LWIP_IPV6_AUTOCONFIG=y - -# Use a custom partition table -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" - -#enable lwIP route hooks -CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y -CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y - -# Serial Flasher config -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_ESPTOOLPY_FLASHSIZE="4MB" - -# Enable HKDF in mbedtls -CONFIG_MBEDTLS_HKDF_C=y - -# Move functions from IRAM to flash -CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y diff --git a/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32c6 b/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32c6 index 399eb5901c5..5844e52c9d3 100644 --- a/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32c6 +++ b/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32c6 @@ -1,63 +1 @@ CONFIG_IDF_TARGET="esp32c6" - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_FLASHMODE_QIO=y -CONFIG_ESPTOOLPY_FLASHFREQ_40M=y -CONFIG_ESPTOOLPY_FLASHFREQ="40m" -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - -# libsodium -CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y - - -# NIMBLE -CONFIG_BT_ENABLED=y -CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_BT_NIMBLE_EXT_ADV=n -CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 -CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=y - -# Disable OpenThread -CONFIG_OPENTHREAD_ENABLED=n - -# Disable lwip ipv6 autoconfig -CONFIG_LWIP_IPV6_AUTOCONFIG=y - -# Use a custom partition table -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" - -# LwIP config for OpenThread -CONFIG_LWIP_IPV6_NUM_ADDRESSES=8 -CONFIG_LWIP_MULTICAST_PING=y - -# mbedTLS -CONFIG_MBEDTLS_HARDWARE_AES=n -CONFIG_MBEDTLS_HARDWARE_MPI=n -CONFIG_MBEDTLS_HARDWARE_SHA=n -CONFIG_MBEDTLS_HARDWARE_ECC=y -CONFIG_MBEDTLS_CMAC_C=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y -CONFIG_MBEDTLS_ECJPAKE_C=y - -# MDNS platform -CONFIG_USE_MINIMAL_MDNS=y -CONFIG_ENABLE_EXTENDED_DISCOVERY=y - -# FreeRTOS should use legacy API -CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y - -# Wi-Fi Settings -CONFIG_ENABLE_WIFI_STATION=y -# Enable this to avoid implicit declaration of function 'esp_send_assoc_resp' -CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y - -# Enable OTA Requestor -CONFIG_ENABLE_OTA_REQUESTOR=y - -# Enable chip shell -CONFIG_ENABLE_CHIP_SHELL=y diff --git a/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32h2 b/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32h2 index c1f43ab77d1..078b7f72439 100644 --- a/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32h2 +++ b/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32h2 @@ -1,26 +1,4 @@ CONFIG_IDF_TARGET="esp32h2" -CONFIG_IDF_TARGET_ESP32H2_BETA_VERSION_2=y - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_FLASHMODE_QIO=y -CONFIG_ESPTOOLPY_FLASHFREQ_40M=y -CONFIG_ESPTOOLPY_FLASHFREQ="40m" -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - -# libsodium -CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y - - -# NIMBLE -CONFIG_BT_ENABLED=y -CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_BT_NIMBLE_EXT_ADV=n -CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 -CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=n # Enable OpenThread CONFIG_OPENTHREAD_ENABLED=y @@ -33,45 +11,13 @@ CONFIG_OPENTHREAD_DNS_CLIENT=y # Disable lwip ipv6 autoconfig CONFIG_LWIP_IPV6_AUTOCONFIG=n -# Use a custom partition table -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" - # LwIP config for OpenThread CONFIG_LWIP_IPV6_NUM_ADDRESSES=8 CONFIG_LWIP_MULTICAST_PING=y -# mbedTLS -CONFIG_MBEDTLS_HARDWARE_AES=n -CONFIG_MBEDTLS_HARDWARE_MPI=n -CONFIG_MBEDTLS_HARDWARE_SHA=n -CONFIG_MBEDTLS_HARDWARE_ECC=y -CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN=n -CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY=n -CONFIG_MBEDTLS_CMAC_C=y -CONFIG_MBEDTLS_SSL_PROTO_DTLS=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y - -# rtc clk for ble -# CONFIG_ESP32H2_RTC_CLK_SRC_EXT_CRYS=y - # MDNS platform CONFIG_USE_MINIMAL_MDNS=n CONFIG_ENABLE_EXTENDED_DISCOVERY=y -# FreeRTOS should use legacy API -CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y - -# Disable STA and AP for ESP32H2 +# Disable STA for ESP32H2 CONFIG_ENABLE_WIFI_STATION=n -# Enable this to avoid implicit declaration of function 'esp_send_assoc_resp' -CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y - -# Enable OTA Requestor -CONFIG_ENABLE_OTA_REQUESTOR=y - -# Enable chip shell -CONFIG_ENABLE_CHIP_SHELL=y - -# Enable HKDF in mbedtls -CONFIG_MBEDTLS_HKDF_C=y diff --git a/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32p4 b/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32p4 index 3211b306b2b..5a5709da53e 100644 --- a/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32p4 +++ b/examples/all-clusters-app/esp32/sdkconfig.defaults.esp32p4 @@ -1,35 +1,12 @@ CONFIG_IDF_TARGET="esp32p4" -# Flash size and partition -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" - # Enable BLE Host but use remote controller -CONFIG_BT_ENABLED=y -CONFIG_BT_NIMBLE_ENABLED=y CONFIG_BT_NIMBLE_TRANSPORT_UART=n CONFIG_ESP_ENABLE_BT=y # Increase main task stack size CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096 -# Disable Wi-Fi Soft AP -CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n - -# LwIP -CONFIG_LWIP_IPV6_AUTOCONFIG=y -CONFIG_LWIP_IPV6_NUM_ADDRESSES=6 - -# mbedtls -CONFIG_MBEDTLS_HKDF_C=y - -# Matter shell -CONFIG_ENABLE_CHIP_SHELL=y - -# OTA requestor -CONFIG_ENABLE_OTA_REQUESTOR=y - # Do not deinit BLE after commissioning CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=n diff --git a/examples/all-clusters-minimal-app/esp32/sdkconfig.defaults b/examples/all-clusters-minimal-app/esp32/sdkconfig.defaults index c551c966de3..ef68a68f15a 100644 --- a/examples/all-clusters-minimal-app/esp32/sdkconfig.defaults +++ b/examples/all-clusters-minimal-app/esp32/sdkconfig.defaults @@ -19,14 +19,6 @@ # Some useful defaults for the demo app configuration. # - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - #enable BT CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y diff --git a/examples/bridge-app/esp32/sdkconfig.defaults b/examples/bridge-app/esp32/sdkconfig.defaults index c92a558df84..79c1a45fc81 100644 --- a/examples/bridge-app/esp32/sdkconfig.defaults +++ b/examples/bridge-app/esp32/sdkconfig.defaults @@ -19,13 +19,6 @@ # Some useful defaults for the demo app configuration. # -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - #enable BT CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y diff --git a/examples/common/pigweed/protos/attributes_service.options b/examples/common/pigweed/protos/attributes_service.options index 247d0c5404c..aa6b7acb9cb 100644 --- a/examples/common/pigweed/protos/attributes_service.options +++ b/examples/common/pigweed/protos/attributes_service.options @@ -1,3 +1,4 @@ chip.rpc.AttributeData.data_bytes max_size:128 +chip.rpc.AttributeData.data_string max_size:128 chip.rpc.AttributeData.tlv_data max_size:256 diff --git a/examples/common/pigweed/protos/attributes_service.proto b/examples/common/pigweed/protos/attributes_service.proto index ef34448f93b..55276f0dadf 100644 --- a/examples/common/pigweed/protos/attributes_service.proto +++ b/examples/common/pigweed/protos/attributes_service.proto @@ -5,203 +5,203 @@ import 'pw_protobuf_protos/common.proto'; package chip.rpc; enum AttributeType { - ZCL_NO_DATA_ATTRIBUTE_TYPE = 0x00; // No data - ZCL_BOOLEAN_ATTRIBUTE_TYPE = 0x10; // Boolean - ZCL_BITMAP8_ATTRIBUTE_TYPE = 0x18; // 8-bit bitmap - ZCL_BITMAP16_ATTRIBUTE_TYPE = 0x19; // 16-bit bitmap - ZCL_BITMAP32_ATTRIBUTE_TYPE = 0x1B; // 32-bit bitmap - ZCL_BITMAP64_ATTRIBUTE_TYPE = 0x1F; // 64-bit bitmap - ZCL_INT8U_ATTRIBUTE_TYPE = 0x20; // Unsigned 8-bit integer - ZCL_INT16U_ATTRIBUTE_TYPE = 0x21; // Unsigned 16-bit integer - ZCL_INT24U_ATTRIBUTE_TYPE = 0x22; // Unsigned 24-bit integer - ZCL_INT32U_ATTRIBUTE_TYPE = 0x23; // Unsigned 32-bit integer - ZCL_INT40U_ATTRIBUTE_TYPE = 0x24; // Unsigned 40-bit integer - ZCL_INT48U_ATTRIBUTE_TYPE = 0x25; // Unsigned 48-bit integer - ZCL_INT56U_ATTRIBUTE_TYPE = 0x26; // Unsigned 56-bit integer - ZCL_INT64U_ATTRIBUTE_TYPE = 0x27; // Unsigned 64-bit integer - ZCL_INT8S_ATTRIBUTE_TYPE = 0x28; // Signed 8-bit integer - ZCL_INT16S_ATTRIBUTE_TYPE = 0x29; // Signed 16-bit integer - ZCL_INT24S_ATTRIBUTE_TYPE = 0x2A; // Signed 24-bit integer - ZCL_INT32S_ATTRIBUTE_TYPE = 0x2B; // Signed 32-bit integer - ZCL_INT40S_ATTRIBUTE_TYPE = 0x2C; // Signed 40-bit integer - ZCL_INT48S_ATTRIBUTE_TYPE = 0x2D; // Signed 48-bit integer - ZCL_INT56S_ATTRIBUTE_TYPE = 0x2E; // Signed 56-bit integer - ZCL_INT64S_ATTRIBUTE_TYPE = 0x2F; // Signed 64-bit integer - ZCL_ENUM8_ATTRIBUTE_TYPE = 0x30; // 8-bit enumeration - ZCL_ENUM16_ATTRIBUTE_TYPE = 0x31; // 16-bit enumeration - ZCL_SINGLE_ATTRIBUTE_TYPE = 0x39; // Single precision - ZCL_DOUBLE_ATTRIBUTE_TYPE = 0x3A; // Double precision - ZCL_OCTET_STRING_ATTRIBUTE_TYPE = 0x41; // Octet String - ZCL_CHAR_STRING_ATTRIBUTE_TYPE = 0x42; // Character String - ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE = 0x43; // Long Octet String - ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE = 0x44; // Long Character String - ZCL_ARRAY_ATTRIBUTE_TYPE = 0x48; // List - ZCL_STRUCT_ATTRIBUTE_TYPE = 0x4C; // Structure - ZCL_TOD_ATTRIBUTE_TYPE = 0xE0; // Time of day - ZCL_DATE_ATTRIBUTE_TYPE = 0xE1; // Date - ZCL_UTC_ATTRIBUTE_TYPE = 0xE2; // UTC Time - ZCL_EPOCH_US_ATTRIBUTE_TYPE = 0xE3; // Epoch Microseconds - ZCL_EPOCH_S_ATTRIBUTE_TYPE = 0xE4; // Epoch Seconds - ZCL_SYSTIME_US_ATTRIBUTE_TYPE = 0xE5; // System Time Microseconds - ZCL_PERCENT_ATTRIBUTE_TYPE = 0xE6; // Percentage units 1% - ZCL_PERCENT100THS_ATTRIBUTE_TYPE = 0xE7; // Percentage units 0.01% - ZCL_CLUSTER_ID_ATTRIBUTE_TYPE = 0xE8; // Cluster ID - ZCL_ATTRIB_ID_ATTRIBUTE_TYPE = 0xE9; // Attribute ID - ZCL_FIELD_ID_ATTRIBUTE_TYPE = 0xEA; // Field ID - ZCL_EVENT_ID_ATTRIBUTE_TYPE = 0xEB; // Event ID - ZCL_COMMAND_ID_ATTRIBUTE_TYPE = 0xEC; // Command ID - ZCL_ACTION_ID_ATTRIBUTE_TYPE = 0xED; // Action ID - ZCL_TRANS_ID_ATTRIBUTE_TYPE = 0xEF; // Transaction ID - ZCL_NODE_ID_ATTRIBUTE_TYPE = 0xF0; // Node ID - ZCL_VENDOR_ID_ATTRIBUTE_TYPE = 0xF1; // Vendor ID - ZCL_DEVTYPE_ID_ATTRIBUTE_TYPE = 0xF2; // Device Type ID - ZCL_FABRIC_ID_ATTRIBUTE_TYPE = 0xF3; // Fabric ID - ZCL_GROUP_ID_ATTRIBUTE_TYPE = 0xF4; // Group ID - ZCL_STATUS_ATTRIBUTE_TYPE = 0xF5; // Status Code - ZCL_DATA_VER_ATTRIBUTE_TYPE = 0xF6; // Data Version - ZCL_EVENT_NO_ATTRIBUTE_TYPE = 0xF7; // Event Number - ZCL_ENDPOINT_NO_ATTRIBUTE_TYPE = 0xF8; // Endpoint Number - ZCL_FABRIC_IDX_ATTRIBUTE_TYPE = 0xF9; // Fabric Index - ZCL_IPADR_ATTRIBUTE_TYPE = 0xFA; // IP Address - ZCL_IPV4ADR_ATTRIBUTE_TYPE = 0xFB; // IPv4 Address - ZCL_IPV6ADR_ATTRIBUTE_TYPE = 0xFC; // IPv6 Address - ZCL_IPV6PRE_ATTRIBUTE_TYPE = 0xFD; // IPv6 Prefix - ZCL_HWADR_ATTRIBUTE_TYPE = 0xFE; // Hardware Address - ZCL_UNKNOWN_ATTRIBUTE_TYPE = 0xFF; // Unknown + ZCL_NO_DATA_ATTRIBUTE_TYPE = 0x00; // No data + ZCL_BOOLEAN_ATTRIBUTE_TYPE = 0x10; // Boolean + ZCL_BITMAP8_ATTRIBUTE_TYPE = 0x18; // 8-bit bitmap + ZCL_BITMAP16_ATTRIBUTE_TYPE = 0x19; // 16-bit bitmap + ZCL_BITMAP32_ATTRIBUTE_TYPE = 0x1B; // 32-bit bitmap + ZCL_BITMAP64_ATTRIBUTE_TYPE = 0x1F; // 64-bit bitmap + ZCL_INT8U_ATTRIBUTE_TYPE = 0x20; // Unsigned 8-bit integer + ZCL_INT16U_ATTRIBUTE_TYPE = 0x21; // Unsigned 16-bit integer + ZCL_INT24U_ATTRIBUTE_TYPE = 0x22; // Unsigned 24-bit integer + ZCL_INT32U_ATTRIBUTE_TYPE = 0x23; // Unsigned 32-bit integer + ZCL_INT40U_ATTRIBUTE_TYPE = 0x24; // Unsigned 40-bit integer + ZCL_INT48U_ATTRIBUTE_TYPE = 0x25; // Unsigned 48-bit integer + ZCL_INT56U_ATTRIBUTE_TYPE = 0x26; // Unsigned 56-bit integer + ZCL_INT64U_ATTRIBUTE_TYPE = 0x27; // Unsigned 64-bit integer + ZCL_INT8S_ATTRIBUTE_TYPE = 0x28; // Signed 8-bit integer + ZCL_INT16S_ATTRIBUTE_TYPE = 0x29; // Signed 16-bit integer + ZCL_INT24S_ATTRIBUTE_TYPE = 0x2A; // Signed 24-bit integer + ZCL_INT32S_ATTRIBUTE_TYPE = 0x2B; // Signed 32-bit integer + ZCL_INT40S_ATTRIBUTE_TYPE = 0x2C; // Signed 40-bit integer + ZCL_INT48S_ATTRIBUTE_TYPE = 0x2D; // Signed 48-bit integer + ZCL_INT56S_ATTRIBUTE_TYPE = 0x2E; // Signed 56-bit integer + ZCL_INT64S_ATTRIBUTE_TYPE = 0x2F; // Signed 64-bit integer + ZCL_ENUM8_ATTRIBUTE_TYPE = 0x30; // 8-bit enumeration + ZCL_ENUM16_ATTRIBUTE_TYPE = 0x31; // 16-bit enumeration + ZCL_SINGLE_ATTRIBUTE_TYPE = 0x39; // Single precision + ZCL_DOUBLE_ATTRIBUTE_TYPE = 0x3A; // Double precision + ZCL_OCTET_STRING_ATTRIBUTE_TYPE = 0x41; // Octet String + ZCL_CHAR_STRING_ATTRIBUTE_TYPE = 0x42; // Character String + ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE = 0x43; // Long Octet String + ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE = 0x44; // Long Character String + ZCL_ARRAY_ATTRIBUTE_TYPE = 0x48; // List + ZCL_STRUCT_ATTRIBUTE_TYPE = 0x4C; // Structure + ZCL_TOD_ATTRIBUTE_TYPE = 0xE0; // Time of day + ZCL_DATE_ATTRIBUTE_TYPE = 0xE1; // Date + ZCL_UTC_ATTRIBUTE_TYPE = 0xE2; // UTC Time + ZCL_EPOCH_US_ATTRIBUTE_TYPE = 0xE3; // Epoch Microseconds + ZCL_EPOCH_S_ATTRIBUTE_TYPE = 0xE4; // Epoch Seconds + ZCL_SYSTIME_US_ATTRIBUTE_TYPE = 0xE5; // System Time Microseconds + ZCL_PERCENT_ATTRIBUTE_TYPE = 0xE6; // Percentage units 1% + ZCL_PERCENT100THS_ATTRIBUTE_TYPE = 0xE7; // Percentage units 0.01% + ZCL_CLUSTER_ID_ATTRIBUTE_TYPE = 0xE8; // Cluster ID + ZCL_ATTRIB_ID_ATTRIBUTE_TYPE = 0xE9; // Attribute ID + ZCL_FIELD_ID_ATTRIBUTE_TYPE = 0xEA; // Field ID + ZCL_EVENT_ID_ATTRIBUTE_TYPE = 0xEB; // Event ID + ZCL_COMMAND_ID_ATTRIBUTE_TYPE = 0xEC; // Command ID + ZCL_ACTION_ID_ATTRIBUTE_TYPE = 0xED; // Action ID + ZCL_TRANS_ID_ATTRIBUTE_TYPE = 0xEF; // Transaction ID + ZCL_NODE_ID_ATTRIBUTE_TYPE = 0xF0; // Node ID + ZCL_VENDOR_ID_ATTRIBUTE_TYPE = 0xF1; // Vendor ID + ZCL_DEVTYPE_ID_ATTRIBUTE_TYPE = 0xF2; // Device Type ID + ZCL_FABRIC_ID_ATTRIBUTE_TYPE = 0xF3; // Fabric ID + ZCL_GROUP_ID_ATTRIBUTE_TYPE = 0xF4; // Group ID + ZCL_STATUS_ATTRIBUTE_TYPE = 0xF5; // Status Code + ZCL_DATA_VER_ATTRIBUTE_TYPE = 0xF6; // Data Version + ZCL_EVENT_NO_ATTRIBUTE_TYPE = 0xF7; // Event Number + ZCL_ENDPOINT_NO_ATTRIBUTE_TYPE = 0xF8; // Endpoint Number + ZCL_FABRIC_IDX_ATTRIBUTE_TYPE = 0xF9; // Fabric Index + ZCL_IPADR_ATTRIBUTE_TYPE = 0xFA; // IP Address + ZCL_IPV4ADR_ATTRIBUTE_TYPE = 0xFB; // IPv4 Address + ZCL_IPV6ADR_ATTRIBUTE_TYPE = 0xFC; // IPv6 Address + ZCL_IPV6PRE_ATTRIBUTE_TYPE = 0xFD; // IPv6 Prefix + ZCL_HWADR_ATTRIBUTE_TYPE = 0xFE; // Hardware Address + ZCL_UNKNOWN_ATTRIBUTE_TYPE = 0xFF; // Unknown } enum ClusterType { - ZCL_INVALID_CLUSTER_ID = 0x0000; - ZCL_POWER_CONFIG_CLUSTER_ID = 0x0001; - ZCL_DEVICE_TEMP_CLUSTER_ID = 0x0002; - ZCL_IDENTIFY_CLUSTER_ID = 0x0003; - ZCL_GROUPS_CLUSTER_ID = 0x0004; - ZCL_ON_OFF_CLUSTER_ID = 0x0006; - ZCL_LEVEL_CONTROL_CLUSTER_ID = 0x0008; - ZCL_ALARM_CLUSTER_ID = 0x0009; - ZCL_TIME_CLUSTER_ID = 0x000A; - ZCL_POWER_PROFILE_CLUSTER_ID = 0x001A; - ZCL_APPLIANCE_CONTROL_CLUSTER_ID = 0x001B; - ZCL_DESCRIPTOR_CLUSTER_ID = 0x001D; - ZCL_POLL_CONTROL_CLUSTER_ID = 0x0020; - ZCL_ACTIONS_CLUSTER_ID = 0x0025; - ZCL_BASIC_CLUSTER_ID = 0x0028; - ZCL_OTA_SOFTWARE_UPDATE_PROVIDER_CLUSTER_ID = 0x0029; - ZCL_OTA_SOFTWARE_UPDATE_REQUESTOR_CLUSTER_ID = 0x002A; - ZCL_UNIT_LOCALIZATION_CLUSTER_ID = 0x002D; - ZCL_POWER_SOURCE_CLUSTER_ID = 0x002F; - ZCL_GENERAL_COMMISSIONING_CLUSTER_ID = 0x0030; - ZCL_NETWORK_COMMISSIONING_CLUSTER_ID = 0x0031; - ZCL_DIAGNOSTIC_LOGS_CLUSTER_ID = 0x0032; - ZCL_GENERAL_DIAGNOSTICS_CLUSTER_ID = 0x0033; - ZCL_SOFTWARE_DIAGNOSTICS_CLUSTER_ID = 0x0034; - ZCL_THREAD_NETWORK_DIAGNOSTICS_CLUSTER_ID = 0x0035; - ZCL_WIFI_NETWORK_DIAGNOSTICS_CLUSTER_ID = 0x0036; - ZCL_ETHERNET_NETWORK_DIAGNOSTICS_CLUSTER_ID = 0x0037; - ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_CLUSTER_ID = 0x0039; - ZCL_SWITCH_CLUSTER_ID = 0x003B; - ZCL_ADMINISTRATOR_COMMISSIONING_CLUSTER_ID = 0x003C; - ZCL_OPERATIONAL_CREDENTIALS_CLUSTER_ID = 0x003E; - ZCL_FIXED_LABEL_CLUSTER_ID = 0x0040; - ZCL_BOOLEAN_STATE_CLUSTER_ID = 0x0045; - ZCL_MODE_SELECT_CLUSTER_ID = 0x0050; - ZCL_LAUNDRY_WASHER_MODE_CLUSTER_ID = 0x0051; - ZCL_REFRIGERATOR_AND_TEMPERATURE_CONTROLLED_CABINET_MODE_CLUSTER_ID = 0x0052; - ZCL_LAUNDRY_WASHER_CONTROLS_CLUSTER_ID = 0x0053; - ZCL_RVC_RUN_MODE_CLUSTER_ID = 0x0054; - ZCL_RVC_CLEAN_MODE_CLUSTER_ID = 0x0055; - ZCL_TEMPERATURE_CONTROL_CLUSTER_ID = 0x0056; - ZCL_REFRIGERATOR_ALARM_CLUSTER_ID = 0x0057; - ZCL_DISHWASHER_MODE_CLUSTER_ID = 0x0059; - ZCL_AIR_QUALITY_CLUSTER_ID = 0x005B; - ZCL_DISHWASHER_ALARM_CLUSTER_ID = 0x005D; - ZCL_SMOKE_CO_ALARM_ID = 0x005C; - ZCL_OPERATIONAL_STATE_CLUSTER_ID = 0x0060; - ZCL_RVC_OPERATIONAL_STATE_CLUSTER_ID = 0x0061; - ZCL_HEPA_FILTER_MONITORING_CLUSTER_ID = 0x0071; - ZCL_ACTIVATED_CARBON_FILTER_MONITORING_CLUSTER_ID = 0x0072; - ZCL_SHADE_CONFIG_CLUSTER_ID = 0x0100; - ZCL_DOOR_LOCK_CLUSTER_ID = 0x0101; - ZCL_WINDOW_COVERING_CLUSTER_ID = 0x0102; - ZCL_PUMP_CONFIGURATION_AND_CONTROL_CLUSTER_ID = 0x0200; - ZCL_THERMOSTAT_CLUSTER_ID = 0x0201; - ZCL_FAN_CONTROL_CLUSTER_ID = 0x0202; - ZCL_DEHUMID_CONTROL_CLUSTER_ID = 0x0203; - ZCL_THERMOSTAT_USER_INTERFACE_CONFIGURATION_CLUSTER_ID = 0x0204; - ZCL_COLOR_CONTROL_CLUSTER_ID = 0x0300; - ZCL_BALLAST_CONFIGURATION_CLUSTER_ID = 0x0301; - ZCL_ILLUMINANCE_MEASUREMENT_CLUSTER_ID = 0x0400; - ZCL_TEMPERATURE_MEASUREMENT_CLUSTER_ID = 0x0402; - ZCL_PRESSURE_MEASUREMENT_CLUSTER_ID = 0x0403; - ZCL_FLOW_MEASUREMENT_CLUSTER_ID = 0x0404; - ZCL_RELATIVE_HUMIDITY_MEASUREMENT_CLUSTER_ID = 0x0405; - ZCL_OCCUPANCY_SENSING_CLUSTER_ID = 0x0406; - ZCL_CARBON_MONOXIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x040C; - ZCL_CARBON_DIOXIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x040D; - ZCL_ETHYLENE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x040E; - ZCL_ETHYLENE_OXIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x040F; - ZCL_HYDROGEN_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0410; - ZCL_HYDROGEN_SULPHIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0411; - ZCL_NITRIC_OXIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0412; - ZCL_NITROGEN_DIOXIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0413; - ZCL_OXYGEN_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0414; - ZCL_OZONE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0415; - ZCL_SULFUR_DIOXIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0416; - ZCL_DISSOLVED_OXYGEN_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0417; - ZCL_BROMATE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0418; - ZCL_CHLORAMINES_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0419; - ZCL_CHLORINE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x041A; - ZCL_FECAL_COLIFORM_AND_E_COLI_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x041B; - ZCL_FLUORIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x041C; - ZCL_HALOACETIC_ACIDS_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x041D; - ZCL_TOTAL_TRIHALOMETHANES_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x041E; - ZCL_TOTAL_COLIFORM_BACTERIA_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x041F; - ZCL_TURBIDITY_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0420; - ZCL_COPPER_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0421; - ZCL_LEAD_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0422; - ZCL_MANGANESE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0423; - ZCL_SULFATE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0424; - ZCL_BROMODICHLOROMETHANE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0425; - ZCL_BROMOFORM_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0426; - ZCL_CHLORODIBROMOMETHANE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0427; - ZCL_CHLOROFORM_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0428; - ZCL_SODIUM_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0429; - ZCL_PM2_5_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x042A; - ZCL_FORMALDEHYDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x042B; - ZCL_PM1_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x042C; - ZCL_PM10_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x042D; - ZCL_TOTAL_VOLATILE_ORGANIC_COMPOUNDS_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x042E; - ZCL_RADON_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x042F; - ZCL_WAKE_ON_LAN_CLUSTER_ID = 0x0503; - ZCL_CHANNEL_CLUSTER_ID = 0x0504; - ZCL_TARGET_NAVIGATOR_CLUSTER_ID = 0x0505; - ZCL_MEDIA_PLAYBACK_CLUSTER_ID = 0x0506; - ZCL_MEDIA_INPUT_CLUSTER_ID = 0x0507; - ZCL_LOW_POWER_CLUSTER_ID = 0x0508; - ZCL_KEYPAD_INPUT_CLUSTER_ID = 0x0509; - ZCL_CONTENT_LAUNCHER_CLUSTER_ID = 0x050A; - ZCL_AUDIO_OUTPUT_CLUSTER_ID = 0x050B; - ZCL_APPLICATION_LAUNCHER_CLUSTER_ID = 0x050C; - ZCL_APPLICATION_BASIC_CLUSTER_ID = 0x050D; - ZCL_ACCOUNT_LOGIN_CLUSTER_ID = 0x050E; - ZCL_CONTENT_CONTROL_CLUSTER_ID = 0x050F; - ZCL_MESSAGING_CLUSTER_ID = 0x0703; - ZCL_MESSAGES_CLUSTER_ID = 0x0097; - ZCL_APPLIANCE_IDENTIFICATION_CLUSTER_ID = 0x0B00; - ZCL_METER_IDENTIFICATION_CLUSTER_ID = 0x0B01; - ZCL_APPLIANCE_EVENTS_AND_ALERT_CLUSTER_ID = 0x0B02; - ZCL_APPLIANCE_STATISTICS_CLUSTER_ID = 0x0B03; - ZCL_SAMPLE_MFG_SPECIFIC_CLUSTER_ID = 0xFC00; + ZCL_INVALID_CLUSTER_ID = 0x0000; ZCL_POWER_CONFIG_CLUSTER_ID = 0x0001; + ZCL_DEVICE_TEMP_CLUSTER_ID = 0x0002; + ZCL_IDENTIFY_CLUSTER_ID = 0x0003; + ZCL_GROUPS_CLUSTER_ID = 0x0004; + ZCL_ON_OFF_CLUSTER_ID = 0x0006; + ZCL_LEVEL_CONTROL_CLUSTER_ID = 0x0008; + ZCL_ALARM_CLUSTER_ID = 0x0009; + ZCL_TIME_CLUSTER_ID = 0x000A; + ZCL_POWER_PROFILE_CLUSTER_ID = 0x001A; + ZCL_APPLIANCE_CONTROL_CLUSTER_ID = 0x001B; + ZCL_DESCRIPTOR_CLUSTER_ID = 0x001D; + ZCL_POLL_CONTROL_CLUSTER_ID = 0x0020; + ZCL_ACTIONS_CLUSTER_ID = 0x0025; + ZCL_BASIC_CLUSTER_ID = 0x0028; + ZCL_OTA_SOFTWARE_UPDATE_PROVIDER_CLUSTER_ID = 0x0029; + ZCL_OTA_SOFTWARE_UPDATE_REQUESTOR_CLUSTER_ID = 0x002A; + ZCL_UNIT_LOCALIZATION_CLUSTER_ID = 0x002D; + ZCL_POWER_SOURCE_CLUSTER_ID = 0x002F; + ZCL_GENERAL_COMMISSIONING_CLUSTER_ID = 0x0030; + ZCL_NETWORK_COMMISSIONING_CLUSTER_ID = 0x0031; + ZCL_DIAGNOSTIC_LOGS_CLUSTER_ID = 0x0032; + ZCL_GENERAL_DIAGNOSTICS_CLUSTER_ID = 0x0033; + ZCL_SOFTWARE_DIAGNOSTICS_CLUSTER_ID = 0x0034; + ZCL_THREAD_NETWORK_DIAGNOSTICS_CLUSTER_ID = 0x0035; + ZCL_WIFI_NETWORK_DIAGNOSTICS_CLUSTER_ID = 0x0036; + ZCL_ETHERNET_NETWORK_DIAGNOSTICS_CLUSTER_ID = 0x0037; + ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_CLUSTER_ID = 0x0039; + ZCL_SWITCH_CLUSTER_ID = 0x003B; + ZCL_ADMINISTRATOR_COMMISSIONING_CLUSTER_ID = 0x003C; + ZCL_OPERATIONAL_CREDENTIALS_CLUSTER_ID = 0x003E; + ZCL_FIXED_LABEL_CLUSTER_ID = 0x0040; + ZCL_BOOLEAN_STATE_CLUSTER_ID = 0x0045; + ZCL_MODE_SELECT_CLUSTER_ID = 0x0050; + ZCL_LAUNDRY_WASHER_MODE_CLUSTER_ID = 0x0051; + ZCL_REFRIGERATOR_AND_TEMPERATURE_CONTROLLED_CABINET_MODE_CLUSTER_ID = 0x0052; + ZCL_LAUNDRY_WASHER_CONTROLS_CLUSTER_ID = 0x0053; + ZCL_RVC_RUN_MODE_CLUSTER_ID = 0x0054; + ZCL_RVC_CLEAN_MODE_CLUSTER_ID = 0x0055; + ZCL_TEMPERATURE_CONTROL_CLUSTER_ID = 0x0056; + ZCL_REFRIGERATOR_ALARM_CLUSTER_ID = 0x0057; + ZCL_DISHWASHER_MODE_CLUSTER_ID = 0x0059; + ZCL_AIR_QUALITY_CLUSTER_ID = 0x005B; + ZCL_DISHWASHER_ALARM_CLUSTER_ID = 0x005D; + ZCL_SMOKE_CO_ALARM_ID = 0x005C; + ZCL_OPERATIONAL_STATE_CLUSTER_ID = 0x0060; + ZCL_RVC_OPERATIONAL_STATE_CLUSTER_ID = 0x0061; + ZCL_HEPA_FILTER_MONITORING_CLUSTER_ID = 0x0071; + ZCL_ACTIVATED_CARBON_FILTER_MONITORING_CLUSTER_ID = 0x0072; + ZCL_SHADE_CONFIG_CLUSTER_ID = 0x0100; + ZCL_DOOR_LOCK_CLUSTER_ID = 0x0101; + ZCL_WINDOW_COVERING_CLUSTER_ID = 0x0102; + ZCL_PUMP_CONFIGURATION_AND_CONTROL_CLUSTER_ID = 0x0200; + ZCL_THERMOSTAT_CLUSTER_ID = 0x0201; + ZCL_FAN_CONTROL_CLUSTER_ID = 0x0202; + ZCL_DEHUMID_CONTROL_CLUSTER_ID = 0x0203; + ZCL_THERMOSTAT_USER_INTERFACE_CONFIGURATION_CLUSTER_ID = 0x0204; + ZCL_COLOR_CONTROL_CLUSTER_ID = 0x0300; + ZCL_BALLAST_CONFIGURATION_CLUSTER_ID = 0x0301; + ZCL_ILLUMINANCE_MEASUREMENT_CLUSTER_ID = 0x0400; + ZCL_TEMPERATURE_MEASUREMENT_CLUSTER_ID = 0x0402; + ZCL_PRESSURE_MEASUREMENT_CLUSTER_ID = 0x0403; + ZCL_FLOW_MEASUREMENT_CLUSTER_ID = 0x0404; + ZCL_RELATIVE_HUMIDITY_MEASUREMENT_CLUSTER_ID = 0x0405; + ZCL_OCCUPANCY_SENSING_CLUSTER_ID = 0x0406; + ZCL_CARBON_MONOXIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x040C; + ZCL_CARBON_DIOXIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x040D; + ZCL_ETHYLENE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x040E; + ZCL_ETHYLENE_OXIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x040F; + ZCL_HYDROGEN_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0410; + ZCL_HYDROGEN_SULPHIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0411; + ZCL_NITRIC_OXIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0412; + ZCL_NITROGEN_DIOXIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0413; + ZCL_OXYGEN_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0414; + ZCL_OZONE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0415; + ZCL_SULFUR_DIOXIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0416; + ZCL_DISSOLVED_OXYGEN_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0417; + ZCL_BROMATE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0418; + ZCL_CHLORAMINES_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0419; + ZCL_CHLORINE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x041A; + ZCL_FECAL_COLIFORM_AND_E_COLI_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x041B; + ZCL_FLUORIDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x041C; + ZCL_HALOACETIC_ACIDS_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x041D; + ZCL_TOTAL_TRIHALOMETHANES_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x041E; + ZCL_TOTAL_COLIFORM_BACTERIA_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x041F; + ZCL_TURBIDITY_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0420; + ZCL_COPPER_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0421; + ZCL_LEAD_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0422; + ZCL_MANGANESE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0423; + ZCL_SULFATE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0424; + ZCL_BROMODICHLOROMETHANE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0425; + ZCL_BROMOFORM_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0426; + ZCL_CHLORODIBROMOMETHANE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0427; + ZCL_CHLOROFORM_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0428; + ZCL_SODIUM_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x0429; + ZCL_PM2_5_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x042A; + ZCL_FORMALDEHYDE_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x042B; + ZCL_PM1_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x042C; + ZCL_PM10_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x042D; + ZCL_TOTAL_VOLATILE_ORGANIC_COMPOUNDS_CONCENTRATION_MEASUREMENT_CLUSTER_ID = + 0x042E; + ZCL_RADON_CONCENTRATION_MEASUREMENT_CLUSTER_ID = 0x042F; + ZCL_WAKE_ON_LAN_CLUSTER_ID = 0x0503; + ZCL_CHANNEL_CLUSTER_ID = 0x0504; + ZCL_TARGET_NAVIGATOR_CLUSTER_ID = 0x0505; + ZCL_MEDIA_PLAYBACK_CLUSTER_ID = 0x0506; + ZCL_MEDIA_INPUT_CLUSTER_ID = 0x0507; + ZCL_LOW_POWER_CLUSTER_ID = 0x0508; + ZCL_KEYPAD_INPUT_CLUSTER_ID = 0x0509; + ZCL_CONTENT_LAUNCHER_CLUSTER_ID = 0x050A; + ZCL_AUDIO_OUTPUT_CLUSTER_ID = 0x050B; + ZCL_APPLICATION_LAUNCHER_CLUSTER_ID = 0x050C; + ZCL_APPLICATION_BASIC_CLUSTER_ID = 0x050D; + ZCL_ACCOUNT_LOGIN_CLUSTER_ID = 0x050E; + ZCL_CONTENT_CONTROL_CLUSTER_ID = 0x050F; + ZCL_MESSAGING_CLUSTER_ID = 0x0703; + ZCL_MESSAGES_CLUSTER_ID = 0x0097; + ZCL_APPLIANCE_IDENTIFICATION_CLUSTER_ID = 0x0B00; + ZCL_METER_IDENTIFICATION_CLUSTER_ID = 0x0B01; + ZCL_APPLIANCE_EVENTS_AND_ALERT_CLUSTER_ID = 0x0B02; + ZCL_APPLIANCE_STATISTICS_CLUSTER_ID = 0x0B03; + ZCL_SAMPLE_MFG_SPECIFIC_CLUSTER_ID = 0xFC00; - // NOTE: This is a large number that becomes negative as a 32-bit integer. - // - // Protobuf documentation states: - // Enumerator constants must be in the range of a 32-bit integer. Since enum values - // use varint encoding on the wire, negative values are inefficient and thus not recommended. - ZCL_TEST_CLUSTER_ID = -918523; // 0xFFF1FC05; + // NOTE: This is a large number that becomes negative as a 32-bit integer. + // + // Protobuf documentation states: + // Enumerator constants must be in the range of a 32-bit integer. Since enum + // values use varint encoding on the wire, negative values are inefficient + // and thus not recommended. + ZCL_TEST_CLUSTER_ID = -918523; // 0xFFF1FC05; } - message AttributeMetadata { uint32 endpoint = 1; ClusterType cluster = 2; @@ -220,6 +220,7 @@ message AttributeData { int32 data_int16 = 7; int32 data_int32 = 8; float data_single = 10; + string data_string = 11; }; optional bytes tlv_data = 9; } @@ -230,6 +231,6 @@ message AttributeWrite { } service Attributes { - rpc Write(AttributeWrite) returns (pw.protobuf.Empty){} - rpc Read(AttributeMetadata) returns (AttributeData){} + rpc Write(AttributeWrite) returns(pw.protobuf.Empty) {} + rpc Read(AttributeMetadata) returns(AttributeData) {} } diff --git a/examples/common/pigweed/rpc_services/Attributes.h b/examples/common/pigweed/rpc_services/Attributes.h index 261831ba9b6..cfbb400ae2f 100644 --- a/examples/common/pigweed/rpc_services/Attributes.h +++ b/examples/common/pigweed/rpc_services/Attributes.h @@ -18,6 +18,7 @@ #pragma once +#include "attributes_service/attributes_service.pb.h" #include "attributes_service/attributes_service.rpc.pb.h" #include "pigweed/rpc_services/internal/StatusUtils.h" @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -43,46 +45,177 @@ namespace rpc { class Attributes : public pw_rpc::nanopb::Attributes::Service { public: - ::pw::Status Write(const chip_rpc_AttributeWrite & request, pw_protobuf_Empty & response) + static constexpr TLV::Tag kAttributeDataTag = TLV::ContextTag(1); + static constexpr TLV::Tag kDataTag = TLV::ContextTag(to_underlying(chip::app::AttributeDataIB::Tag::kData)); + + CHIP_ERROR PositionOnDataElement(chip::TLV::TLVReader & reader) { - const void * data; - DeviceLayer::StackLock lock; + // Expect the TLV to be the full structure as received from a read (or subset) + // TLV is a full ReportDataMessage + // - Anonymous Structure (container of everything) + // - 1: Array (one element) + // - Anonymous (the element) + // - 1 (AttributeData/AttributeDataIB) - Structure + // - 0 - Data Version + // - 1 - Path (1: Node, 2: Endpoint, 3: Cluster, 4: Attribute, ...) + // - 2 - Data (variable - may be raw data or a Structure) + + TLV::TLVType unused_outer_type; + + // Enter anonymous wrapper + ReturnErrorOnFailure(reader.Next()); // got to anonymous + ReturnErrorOnFailure(reader.EnterContainer(unused_outer_type)); + + // Enter the array + ReturnErrorOnFailure(reader.Next()); + ReturnErrorOnFailure(reader.EnterContainer(unused_outer_type)); + + // enter the structure of data + ReturnErrorOnFailure(reader.Next()); + ReturnErrorOnFailure(reader.EnterContainer(unused_outer_type)); + + // Find AttributeData Container + { + chip::TLV::TLVReader tmp; + ReturnErrorOnFailure(reader.FindElementWithTag(kAttributeDataTag, tmp)); + reader = tmp; + } + + // Enter into AttributeData Container + ReturnErrorOnFailure(reader.EnterContainer(unused_outer_type)); + + // Find Data Container + { + chip::TLV::TLVReader tmp; + ReturnErrorOnFailure(reader.FindElementWithTag(kDataTag, tmp)); + reader = tmp; + } + + return CHIP_NO_ERROR; + } + + pw::Result ReadIntoTlv(const chip_rpc_AttributeData & data, chip::MutableByteSpan tempBuffer) + { + TLV::TLVReader result; + + if (data.has_tlv_data) + { + result.Init(data.tlv_data.bytes, data.tlv_data.size); + CHIP_ERROR err = PositionOnDataElement(result); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Support, "Failed to parse input TLV buffer: %" CHIP_ERROR_FORMAT, err.Format()); + return pw::Status::InvalidArgument(); + } + + return result; + } - switch (request.data.which_data) + TLV::TLVWriter writer; + + writer.Init(tempBuffer); + + CHIP_ERROR write_status; + + TLV::TLVType outer; + VerifyOrReturnError(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer) == CHIP_NO_ERROR, + pw::Status::Internal()); + + switch (data.which_data) { case chip_rpc_AttributeData_data_bool_tag: - data = &request.data.data.data_bool; + write_status = writer.Put(kDataTag, data.data.data_bool); break; case chip_rpc_AttributeData_data_uint8_tag: - data = &request.data.data.data_uint8; + write_status = writer.Put(kDataTag, data.data.data_uint8); break; case chip_rpc_AttributeData_data_uint16_tag: - data = &request.data.data.data_uint16; + write_status = writer.Put(kDataTag, data.data.data_uint16); break; case chip_rpc_AttributeData_data_uint32_tag: - data = &request.data.data.data_uint32; + write_status = writer.Put(kDataTag, data.data.data_uint32); break; case chip_rpc_AttributeData_data_int8_tag: - data = &request.data.data.data_int8; + write_status = writer.Put(kDataTag, data.data.data_int8); break; case chip_rpc_AttributeData_data_int16_tag: - data = &request.data.data.data_int16; + write_status = writer.Put(kDataTag, data.data.data_int16); break; case chip_rpc_AttributeData_data_int32_tag: - data = &request.data.data.data_int32; + write_status = writer.Put(kDataTag, data.data.data_int32); + break; + case chip_rpc_AttributeData_data_single_tag: + write_status = writer.Put(kDataTag, data.data.data_single); break; case chip_rpc_AttributeData_data_bytes_tag: - data = &request.data.data.data_bytes; + write_status = writer.PutBytes(kDataTag, data.data.data_bytes.bytes, data.data.data_bytes.size); break; - case chip_rpc_AttributeData_data_single_tag: - data = &request.data.data.data_single; + case chip_rpc_AttributeData_data_string_tag: + write_status = writer.PutString(kDataTag, data.data.data_string); break; default: return pw::Status::InvalidArgument(); } - RETURN_STATUS_IF_NOT_OK( - emberAfWriteAttribute(request.metadata.endpoint, request.metadata.cluster, request.metadata.attribute_id, - const_cast(static_cast(data)), request.metadata.type)); + + if (write_status != CHIP_NO_ERROR) + { + ChipLogError(Support, "Failed to encode TLV data: %" CHIP_ERROR_FORMAT, write_status.Format()); + return pw::Status::Internal(); + } + + VerifyOrReturnValue(writer.EndContainer(outer) == CHIP_NO_ERROR, pw::Status::Internal()); + VerifyOrReturnValue(writer.Finalize() == CHIP_NO_ERROR, pw::Status::Internal()); + result.Init(tempBuffer.data(), writer.GetLengthWritten()); + + VerifyOrReturnError(result.Next() == CHIP_NO_ERROR, pw::Status::Internal()); + VerifyOrReturnError(result.EnterContainer(outer) == CHIP_NO_ERROR, pw::Status::Internal()); + + // This positions on the data element + VerifyOrReturnError(result.Next() == CHIP_NO_ERROR, pw::Status::Internal()); + + return result; + } + + ::pw::Status Write(const chip_rpc_AttributeWrite & request, pw_protobuf_Empty & response) + { + app::ConcreteAttributePath path(request.metadata.endpoint, request.metadata.cluster, request.metadata.attribute_id); + + DeviceLayer::StackLock lock; + + // TODO: this assumes a singleton data model provider + app::DataModel::Provider * provider = app::InteractionModelEngine::GetInstance()->GetDataModelProvider(); + + app::DataModel::ServerClusterFinder serverClusterFinder(provider); + auto info = serverClusterFinder.Find(path); + if (!info.has_value()) + { + return ::pw::Status::NotFound(); + } + + Access::SubjectDescriptor subjectDescriptor{ .authMode = chip::Access::AuthMode::kPase }; + app::DataModel::WriteAttributeRequest write_request; + write_request.path = path; + write_request.operationFlags.Set(app::DataModel::OperationFlags::kInternal); + write_request.subjectDescriptor = &subjectDescriptor; + + uint8_t raw_value_buffer[64]; // enough to hold general types + pw::Result tlvReader = ReadIntoTlv(request.data, chip::MutableByteSpan(raw_value_buffer)); + + if (!tlvReader.status().ok()) + { + return tlvReader.status(); + } + + app::AttributeValueDecoder decoder(tlvReader.value(), subjectDescriptor); + app::DataModel::ActionReturnStatus result = provider->WriteAttribute(write_request, decoder); + + if (!result.IsSuccess()) + { + app::DataModel::ActionReturnStatus::StringStorage storage; + ChipLogError(Support, "Failed to write data: %s", result.c_str(storage)); + return ::pw::Status::Internal(); + } + return pw::OkStatus(); } @@ -91,6 +224,7 @@ class Attributes : public pw_rpc::nanopb::Attributes::Service app::ConcreteAttributePath path(request.endpoint, request.cluster, request.attribute_id); MutableByteSpan tlvBuffer(response.tlv_data.bytes); PW_TRY(ReadAttributeIntoTlvBuffer(path, tlvBuffer)); + // NOTE: TLV will be a full AttributeReportIB (so not purely the data) response.tlv_data.size = tlvBuffer.size(); response.has_tlv_data = true; @@ -136,6 +270,16 @@ class Attributes : public pw_rpc::nanopb::Attributes::Service PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_FloatingPointNumber, response.data.data_single)); response.which_data = chip_rpc_AttributeData_data_single_tag; break; + case chip_rpc_AttributeType_ZCL_OCTET_STRING_ATTRIBUTE_TYPE: + case chip_rpc_AttributeType_ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE: + PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_ByteString, response.data.data_bytes)); + response.which_data = chip_rpc_AttributeData_data_bytes_tag; + break; + case chip_rpc_AttributeType_ZCL_CHAR_STRING_ATTRIBUTE_TYPE: + case chip_rpc_AttributeType_ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE: + PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UTF8String, response.data.data_string)); + response.which_data = chip_rpc_AttributeData_data_string_tag; + break; case chip_rpc_AttributeType_ZCL_BITMAP8_ATTRIBUTE_TYPE: case chip_rpc_AttributeType_ZCL_BITMAP16_ATTRIBUTE_TYPE: case chip_rpc_AttributeType_ZCL_BITMAP32_ATTRIBUTE_TYPE: @@ -152,10 +296,6 @@ class Attributes : public pw_rpc::nanopb::Attributes::Service case chip_rpc_AttributeType_ZCL_INT56S_ATTRIBUTE_TYPE: case chip_rpc_AttributeType_ZCL_INT64S_ATTRIBUTE_TYPE: case chip_rpc_AttributeType_ZCL_DOUBLE_ATTRIBUTE_TYPE: - case chip_rpc_AttributeType_ZCL_OCTET_STRING_ATTRIBUTE_TYPE: - case chip_rpc_AttributeType_ZCL_CHAR_STRING_ATTRIBUTE_TYPE: - case chip_rpc_AttributeType_ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE: - case chip_rpc_AttributeType_ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE: case chip_rpc_AttributeType_ZCL_STRUCT_ATTRIBUTE_TYPE: case chip_rpc_AttributeType_ZCL_TOD_ATTRIBUTE_TYPE: case chip_rpc_AttributeType_ZCL_DATE_ATTRIBUTE_TYPE: @@ -221,7 +361,9 @@ class Attributes : public pw_rpc::nanopb::Attributes::Service request.operationFlags.Set(app::DataModel::OperationFlags::kInternal); request.subjectDescriptor = &subjectDescriptor; - std::optional info = provider->GetServerClusterInfo(path); + app::DataModel::ServerClusterFinder serverClusterFinder(provider); + auto info = serverClusterFinder.Find(path); + if (!info.has_value()) { return ::pw::Status::NotFound(); @@ -246,6 +388,24 @@ class Attributes : public pw_rpc::nanopb::Attributes::Service return ::pw::OkStatus(); } + template + CHIP_ERROR TlvGet(TLV::TLVReader & reader, T & value) + { + return reader.Get(value); + } + + CHIP_ERROR TlvGet(TLV::TLVReader & reader, chip_rpc_AttributeData_data_bytes_t & value) + { + value.size = reader.GetLength(); + return reader.GetBytes(value.bytes, sizeof(value.bytes)); + } + + template + CHIP_ERROR TlvGet(TLV::TLVReader & reader, char (&value)[N]) + { + return reader.GetString(value, N); + } + template ::pw::Status TlvBufferGetData(ByteSpan tlvBuffer, TLV::TLVType expectedDataType, T & responseData) { @@ -272,7 +432,7 @@ class Attributes : public pw_rpc::nanopb::Attributes::Service TLV::TLVReader dataReader; PW_TRY(ChipErrorToPwStatus(dataParser.GetData(&dataReader))); PW_TRY(CheckTlvTagAndType(&dataReader, TLV::ContextTag(0x2), expectedDataType)); - PW_TRY(ChipErrorToPwStatus(dataReader.Get(responseData))); + PW_TRY(ChipErrorToPwStatus(TlvGet(dataReader, responseData))); return ::pw::OkStatus(); } diff --git a/examples/common/tracing/BUILD.gn b/examples/common/tracing/BUILD.gn index 6e3bb1277ae..eec016e3e22 100644 --- a/examples/common/tracing/BUILD.gn +++ b/examples/common/tracing/BUILD.gn @@ -94,6 +94,7 @@ source_set("trace_handlers_decoder") { deps = [ ":trace_handlers", + "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/lib", "${chip_root}/src/lib/core:types", ] @@ -123,6 +124,7 @@ executable("chip-trace-decoder") { deps = [ "${chip_root}/src/platform/logging:stdio" ] public_deps = [ + "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/lib", "${chip_root}/src/lib/core:types", "${chip_root}/third_party/jsoncpp", diff --git a/examples/contact-sensor-app/nxp/k32w1/BUILD.gn b/examples/contact-sensor-app/nxp/k32w1/BUILD.gn index c96f9676b23..e3694a5c443 100644 --- a/examples/contact-sensor-app/nxp/k32w1/BUILD.gn +++ b/examples/contact-sensor-app/nxp/k32w1/BUILD.gn @@ -86,14 +86,6 @@ mcxw71_k32w1_sdk("sdk") { defines += [ "CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR=${setup_discriminator}", ] - - if (nxp_nvm_component == "littlefs") { - include_dirs += [ "${example_platform_dir}/board" ] - sources += [ - "${example_platform_dir}/board/peripherals.c", - "${example_platform_dir}/board/peripherals.h", - ] - } } mcxw71_k32w1_executable("contact_sensor_app") { diff --git a/examples/contact-sensor-app/nxp/k32w1/args.gni b/examples/contact-sensor-app/nxp/k32w1/args.gni index ee29002e00a..363718f1a8b 100644 --- a/examples/contact-sensor-app/nxp/k32w1/args.gni +++ b/examples/contact-sensor-app/nxp/k32w1/args.gni @@ -42,5 +42,3 @@ chip_openthread_ftd = false nxp_enable_ot_cli = false chip_with_diag_logs_demo = true - -nxp_nvm_component = "nvs" diff --git a/examples/contact-sensor-app/nxp/mcxw71/BUILD.gn b/examples/contact-sensor-app/nxp/mcxw71/BUILD.gn index 48719dbc9bd..d0c6109ce4b 100644 --- a/examples/contact-sensor-app/nxp/mcxw71/BUILD.gn +++ b/examples/contact-sensor-app/nxp/mcxw71/BUILD.gn @@ -85,14 +85,6 @@ mcxw71_k32w1_sdk("sdk") { defines += [ "CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR=${setup_discriminator}", ] - - if (nxp_nvm_component == "littlefs") { - include_dirs += [ "${example_platform_dir}/board" ] - sources += [ - "${example_platform_dir}/board/peripherals.c", - "${example_platform_dir}/board/peripherals.h", - ] - } } mcxw71_k32w1_executable("contact_sensor_app") { diff --git a/examples/contact-sensor-app/nxp/mcxw71/args.gni b/examples/contact-sensor-app/nxp/mcxw71/args.gni index 87a3b496327..bf06f4e4948 100644 --- a/examples/contact-sensor-app/nxp/mcxw71/args.gni +++ b/examples/contact-sensor-app/nxp/mcxw71/args.gni @@ -40,5 +40,3 @@ chip_openthread_ftd = false nxp_enable_ot_cli = false chip_with_diag_logs_demo = true - -nxp_nvm_component = "nvs" diff --git a/examples/energy-management-app/esp32/sdkconfig.defaults b/examples/energy-management-app/esp32/sdkconfig.defaults index 5bcdc334fa5..5499f3347f0 100644 --- a/examples/energy-management-app/esp32/sdkconfig.defaults +++ b/examples/energy-management-app/esp32/sdkconfig.defaults @@ -18,13 +18,6 @@ # Some useful defaults for the demo app configuration. # -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - #enable BT CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y @@ -85,3 +78,6 @@ CONFIG_CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER=y # Ensure kPowerForecastReporting (PFR) is set in the DeviceEnergyManagement::Feature attribute. Note cannot set PFR and SFR. CONFIG_DEM_SUPPORT_POWER_FORECAST_REPORTING=y + +# Enable OTA Requestor +CONFIG_ENABLE_OTA_REQUESTOR=y diff --git a/examples/energy-management-app/esp32/sdkconfig.defaults.esp32c6 b/examples/energy-management-app/esp32/sdkconfig.defaults.esp32c6 index 6127b910506..5844e52c9d3 100644 --- a/examples/energy-management-app/esp32/sdkconfig.defaults.esp32c6 +++ b/examples/energy-management-app/esp32/sdkconfig.defaults.esp32c6 @@ -1,66 +1 @@ CONFIG_IDF_TARGET="esp32c6" - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_FLASHMODE_QIO=y -CONFIG_ESPTOOLPY_FLASHFREQ_40M=y -CONFIG_ESPTOOLPY_FLASHFREQ="40m" -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - -# libsodium -CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y - - -# NIMBLE -CONFIG_BT_ENABLED=y -CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_BT_NIMBLE_EXT_ADV=n -CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 -CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=y - -# Disable OpenThread -CONFIG_OPENTHREAD_ENABLED=n - -# Disable lwip ipv6 autoconfig -CONFIG_LWIP_IPV6_AUTOCONFIG=y - -# Use a custom partition table -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" - -# LwIP config for OpenThread -CONFIG_LWIP_IPV6_NUM_ADDRESSES=8 -CONFIG_LWIP_MULTICAST_PING=y - -# mbedTLS -CONFIG_MBEDTLS_HARDWARE_AES=n -CONFIG_MBEDTLS_HARDWARE_MPI=n -CONFIG_MBEDTLS_HARDWARE_SHA=n -CONFIG_MBEDTLS_HARDWARE_ECC=y -CONFIG_MBEDTLS_CMAC_C=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y -CONFIG_MBEDTLS_ECJPAKE_C=y - -# MDNS platform -CONFIG_USE_MINIMAL_MDNS=y -CONFIG_ENABLE_EXTENDED_DISCOVERY=y - -# FreeRTOS should use legacy API -CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y - -# Wi-Fi Settings -CONFIG_ENABLE_WIFI_STATION=y -# Enable this to avoid implicit declaration of function 'esp_send_assoc_resp' -CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y - -# Enable OTA Requestor -CONFIG_ENABLE_OTA_REQUESTOR=y - -# Enable chip shell -CONFIG_ENABLE_CHIP_SHELL=y - -# Enable HKDF in mbedtls -CONFIG_MBEDTLS_HKDF_C=y diff --git a/examples/energy-management-app/esp32/sdkconfig.defaults.esp32h2 b/examples/energy-management-app/esp32/sdkconfig.defaults.esp32h2 index eb280c060b2..078b7f72439 100644 --- a/examples/energy-management-app/esp32/sdkconfig.defaults.esp32h2 +++ b/examples/energy-management-app/esp32/sdkconfig.defaults.esp32h2 @@ -1,29 +1,4 @@ CONFIG_IDF_TARGET="esp32h2" -CONFIG_IDF_TARGET_ESP32H2_BETA_VERSION_2=y - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_FLASHMODE_QIO=y -CONFIG_ESPTOOLPY_FLASHFREQ_40M=y -CONFIG_ESPTOOLPY_FLASHFREQ="40m" -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - -# libsodium -CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y - -# Serial Flasher config -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_ESPTOOLPY_FLASHSIZE="4MB" - -# NIMBLE -CONFIG_BT_ENABLED=y -CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_BT_NIMBLE_EXT_ADV=n -CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 -CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=n # Enable OpenThread CONFIG_OPENTHREAD_ENABLED=y @@ -36,45 +11,13 @@ CONFIG_OPENTHREAD_DNS_CLIENT=y # Disable lwip ipv6 autoconfig CONFIG_LWIP_IPV6_AUTOCONFIG=n -# Use a custom partition table -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" - # LwIP config for OpenThread CONFIG_LWIP_IPV6_NUM_ADDRESSES=8 CONFIG_LWIP_MULTICAST_PING=y -# mbedTLS -CONFIG_MBEDTLS_HARDWARE_AES=n -CONFIG_MBEDTLS_HARDWARE_MPI=n -CONFIG_MBEDTLS_HARDWARE_SHA=n -CONFIG_MBEDTLS_HARDWARE_ECC=y -CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN=n -CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY=n -CONFIG_MBEDTLS_CMAC_C=y -CONFIG_MBEDTLS_SSL_PROTO_DTLS=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y - -# rtc clk for ble -# CONFIG_ESP32H2_RTC_CLK_SRC_EXT_CRYS=y - # MDNS platform CONFIG_USE_MINIMAL_MDNS=n CONFIG_ENABLE_EXTENDED_DISCOVERY=y -# FreeRTOS should use legacy API -CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y - -# Disable STA and AP for ESP32H2 +# Disable STA for ESP32H2 CONFIG_ENABLE_WIFI_STATION=n -# Enable this to avoid implicit declaration of function 'esp_send_assoc_resp' -CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y - -# Enable OTA Requestor -CONFIG_ENABLE_OTA_REQUESTOR=y - -# Enable chip shell -CONFIG_ENABLE_CHIP_SHELL=y - -# Enable HKDF in mbedtls -CONFIG_MBEDTLS_HKDF_C=y diff --git a/examples/energy-management-app/linux/README.md b/examples/energy-management-app/linux/README.md index 9bb1cd3820f..37a4be4e7c0 100644 --- a/examples/energy-management-app/linux/README.md +++ b/examples/energy-management-app/linux/README.md @@ -156,7 +156,7 @@ CHIP-REPL is slightly easier to interact with when dealing with some of the complex structures. There are several test scripts provided for EVSE (in -[src/python_testing](src/python_testing)): +[src/python_testing](/src/python_testing)): - `TC_EEVSE_2_2`: This validates the primary functionality - `TC_EEVSE_2_3`: This validates Get/Set/Clear target commands diff --git a/examples/energy-management-app/silabs/src/AppTask.cpp b/examples/energy-management-app/silabs/src/AppTask.cpp index f90952ea456..b5155f1dd3b 100644 --- a/examples/energy-management-app/silabs/src/AppTask.cpp +++ b/examples/energy-management-app/silabs/src/AppTask.cpp @@ -133,7 +133,7 @@ AppTask AppTask::sAppTask; EndpointId GetEnergyDeviceEndpointId() { -#if defined(SL_CONFIG_ENABLE_EXAMPLE_WATER_HEATER_DEVICE) +#if SL_CONFIG_ENABLE_EXAMPLE_WATER_HEATER_DEVICE return kWaterHeaterEndpoint; #else return kEvseEndpoint; diff --git a/examples/light-switch-app/esp32/sdkconfig.defaults b/examples/light-switch-app/esp32/sdkconfig.defaults index d0e507742a2..a5f416889ac 100644 --- a/examples/light-switch-app/esp32/sdkconfig.defaults +++ b/examples/light-switch-app/esp32/sdkconfig.defaults @@ -18,13 +18,6 @@ # Some useful defaults for the demo app configuration. # -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - #enable BT CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y @@ -58,3 +51,9 @@ CONFIG_MBEDTLS_HKDF_C=y # Increase LwIP IPv6 address number CONFIG_LWIP_IPV6_NUM_ADDRESSES=6 + +# Move functions from IRAM to flash +CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y + +# Enable OTA Requestor +CONFIG_ENABLE_OTA_REQUESTOR=y diff --git a/examples/light-switch-app/esp32/sdkconfig.defaults.esp32c3 b/examples/light-switch-app/esp32/sdkconfig.defaults.esp32c3 index 4db6e947e50..62c2cb2fd57 100644 --- a/examples/light-switch-app/esp32/sdkconfig.defaults.esp32c3 +++ b/examples/light-switch-app/esp32/sdkconfig.defaults.esp32c3 @@ -1,35 +1 @@ CONFIG_IDF_TARGET="esp32c3" -CONFIG_IDF_TARGET_ESP32C3=y -CONFIG_DEVICE_TYPE_ESP32_C3_DEVKITM=y - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - -#enable BT -CONFIG_BT_ENABLED=y -CONFIG_BT_NIMBLE_ENABLED=y - -#enable lwip ipv6 autoconfig -CONFIG_LWIP_IPV6_AUTOCONFIG=y - -# Use a custom partition table -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" - -#enable lwIP route hooks -CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y -CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y - -# Serial Flasher config -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_ESPTOOLPY_FLASHSIZE="4MB" - -# Enable HKDF in mbedtls -CONFIG_MBEDTLS_HKDF_C=y - -# Move functions from IRAM to flash -CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y diff --git a/examples/lighting-app/esp32/sdkconfig.defaults b/examples/lighting-app/esp32/sdkconfig.defaults index a8833c13a1a..bd9f9235540 100644 --- a/examples/lighting-app/esp32/sdkconfig.defaults +++ b/examples/lighting-app/esp32/sdkconfig.defaults @@ -18,13 +18,6 @@ # Some useful defaults for the demo app configuration. # -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - #enable BT CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y @@ -64,3 +57,6 @@ CONFIG_DISABLE_READ_CLIENT=y # Increase LwIP IPv6 address number CONFIG_LWIP_IPV6_NUM_ADDRESSES=6 + +# Enable OTA Requestor +CONFIG_ENABLE_OTA_REQUESTOR=y diff --git a/examples/lighting-app/esp32/sdkconfig.defaults.esp32c6 b/examples/lighting-app/esp32/sdkconfig.defaults.esp32c6 index c8617158acb..5844e52c9d3 100644 --- a/examples/lighting-app/esp32/sdkconfig.defaults.esp32c6 +++ b/examples/lighting-app/esp32/sdkconfig.defaults.esp32c6 @@ -1,70 +1 @@ CONFIG_IDF_TARGET="esp32c6" - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_FLASHMODE_QIO=y -CONFIG_ESPTOOLPY_FLASHFREQ_40M=y -CONFIG_ESPTOOLPY_FLASHFREQ="40m" -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - -# libsodium -CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y - - -# NIMBLE -CONFIG_BT_ENABLED=y -CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_BT_NIMBLE_EXT_ADV=n -CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 -CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=y - -# Disable OpenThread -CONFIG_OPENTHREAD_ENABLED=n - -# Disable lwip ipv6 autoconfig -CONFIG_LWIP_IPV6_AUTOCONFIG=y - -# Use a custom partition table -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" - -# LwIP config for OpenThread -CONFIG_LWIP_IPV6_NUM_ADDRESSES=8 -CONFIG_LWIP_MULTICAST_PING=y - -# mbedTLS -CONFIG_MBEDTLS_HARDWARE_AES=n -CONFIG_MBEDTLS_HARDWARE_MPI=n -CONFIG_MBEDTLS_HARDWARE_SHA=n -CONFIG_MBEDTLS_HARDWARE_ECC=y -CONFIG_MBEDTLS_CMAC_C=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y -CONFIG_MBEDTLS_ECJPAKE_C=y - -# MDNS platform -CONFIG_USE_MINIMAL_MDNS=y -CONFIG_ENABLE_EXTENDED_DISCOVERY=y - -# FreeRTOS should use legacy API -CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y - -# Wi-Fi Settings -CONFIG_ENABLE_WIFI_STATION=y -# Enable this to avoid implicit declaration of function 'esp_send_assoc_resp' -CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y - -# Enable OTA Requestor -CONFIG_ENABLE_OTA_REQUESTOR=y - -# Enable chip shell -CONFIG_ENABLE_CHIP_SHELL=y - -# Enable HKDF in mbedtls -CONFIG_MBEDTLS_HKDF_C=y - -# Serial Flasher config -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_ESPTOOLPY_FLASHSIZE="4MB" diff --git a/examples/lighting-app/esp32/sdkconfig.defaults.esp32h2 b/examples/lighting-app/esp32/sdkconfig.defaults.esp32h2 index eb280c060b2..078b7f72439 100644 --- a/examples/lighting-app/esp32/sdkconfig.defaults.esp32h2 +++ b/examples/lighting-app/esp32/sdkconfig.defaults.esp32h2 @@ -1,29 +1,4 @@ CONFIG_IDF_TARGET="esp32h2" -CONFIG_IDF_TARGET_ESP32H2_BETA_VERSION_2=y - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_FLASHMODE_QIO=y -CONFIG_ESPTOOLPY_FLASHFREQ_40M=y -CONFIG_ESPTOOLPY_FLASHFREQ="40m" -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - -# libsodium -CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y - -# Serial Flasher config -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_ESPTOOLPY_FLASHSIZE="4MB" - -# NIMBLE -CONFIG_BT_ENABLED=y -CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_BT_NIMBLE_EXT_ADV=n -CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 -CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=n # Enable OpenThread CONFIG_OPENTHREAD_ENABLED=y @@ -36,45 +11,13 @@ CONFIG_OPENTHREAD_DNS_CLIENT=y # Disable lwip ipv6 autoconfig CONFIG_LWIP_IPV6_AUTOCONFIG=n -# Use a custom partition table -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" - # LwIP config for OpenThread CONFIG_LWIP_IPV6_NUM_ADDRESSES=8 CONFIG_LWIP_MULTICAST_PING=y -# mbedTLS -CONFIG_MBEDTLS_HARDWARE_AES=n -CONFIG_MBEDTLS_HARDWARE_MPI=n -CONFIG_MBEDTLS_HARDWARE_SHA=n -CONFIG_MBEDTLS_HARDWARE_ECC=y -CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN=n -CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY=n -CONFIG_MBEDTLS_CMAC_C=y -CONFIG_MBEDTLS_SSL_PROTO_DTLS=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y - -# rtc clk for ble -# CONFIG_ESP32H2_RTC_CLK_SRC_EXT_CRYS=y - # MDNS platform CONFIG_USE_MINIMAL_MDNS=n CONFIG_ENABLE_EXTENDED_DISCOVERY=y -# FreeRTOS should use legacy API -CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y - -# Disable STA and AP for ESP32H2 +# Disable STA for ESP32H2 CONFIG_ENABLE_WIFI_STATION=n -# Enable this to avoid implicit declaration of function 'esp_send_assoc_resp' -CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y - -# Enable OTA Requestor -CONFIG_ENABLE_OTA_REQUESTOR=y - -# Enable chip shell -CONFIG_ENABLE_CHIP_SHELL=y - -# Enable HKDF in mbedtls -CONFIG_MBEDTLS_HKDF_C=y diff --git a/examples/lighting-app/esp32/sdkconfig.defaults.esp32p4 b/examples/lighting-app/esp32/sdkconfig.defaults.esp32p4 index 3211b306b2b..5a5709da53e 100644 --- a/examples/lighting-app/esp32/sdkconfig.defaults.esp32p4 +++ b/examples/lighting-app/esp32/sdkconfig.defaults.esp32p4 @@ -1,35 +1,12 @@ CONFIG_IDF_TARGET="esp32p4" -# Flash size and partition -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" - # Enable BLE Host but use remote controller -CONFIG_BT_ENABLED=y -CONFIG_BT_NIMBLE_ENABLED=y CONFIG_BT_NIMBLE_TRANSPORT_UART=n CONFIG_ESP_ENABLE_BT=y # Increase main task stack size CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096 -# Disable Wi-Fi Soft AP -CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n - -# LwIP -CONFIG_LWIP_IPV6_AUTOCONFIG=y -CONFIG_LWIP_IPV6_NUM_ADDRESSES=6 - -# mbedtls -CONFIG_MBEDTLS_HKDF_C=y - -# Matter shell -CONFIG_ENABLE_CHIP_SHELL=y - -# OTA requestor -CONFIG_ENABLE_OTA_REQUESTOR=y - # Do not deinit BLE after commissioning CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=n diff --git a/examples/lighting-app/nxp/k32w1/BUILD.gn b/examples/lighting-app/nxp/k32w1/BUILD.gn index 91f06973228..e66b9c3bf2d 100644 --- a/examples/lighting-app/nxp/k32w1/BUILD.gn +++ b/examples/lighting-app/nxp/k32w1/BUILD.gn @@ -100,14 +100,6 @@ mcxw71_k32w1_sdk("sdk") { defines += [ "CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR=${setup_discriminator}", ] - - if (nxp_nvm_component == "littlefs") { - include_dirs += [ "${example_platform_dir}/board" ] - sources += [ - "${example_platform_dir}/board/peripherals.c", - "${example_platform_dir}/board/peripherals.h", - ] - } } mcxw71_k32w1_executable("light_app") { diff --git a/examples/lighting-app/nxp/k32w1/args.gni b/examples/lighting-app/nxp/k32w1/args.gni index 078847b9826..3d026f1fb48 100644 --- a/examples/lighting-app/nxp/k32w1/args.gni +++ b/examples/lighting-app/nxp/k32w1/args.gni @@ -35,7 +35,5 @@ chip_system_config_provide_statistics = false chip_system_config_use_open_thread_inet_endpoints = true chip_with_lwip = false -nxp_nvm_component = "nvs" - nxp_use_smu2_static = true nxp_use_smu2_dynamic = true diff --git a/examples/lighting-app/nxp/mcxw71/BUILD.gn b/examples/lighting-app/nxp/mcxw71/BUILD.gn index 8ee1b50b361..815f95debd9 100644 --- a/examples/lighting-app/nxp/mcxw71/BUILD.gn +++ b/examples/lighting-app/nxp/mcxw71/BUILD.gn @@ -100,14 +100,6 @@ mcxw71_k32w1_sdk("sdk") { defines += [ "CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR=${setup_discriminator}", ] - - if (nxp_nvm_component == "littlefs") { - include_dirs += [ "${example_platform_dir}/board" ] - sources += [ - "${example_platform_dir}/board/peripherals.c", - "${example_platform_dir}/board/peripherals.h", - ] - } } mcxw71_k32w1_executable("light_app") { diff --git a/examples/lighting-app/nxp/mcxw71/args.gni b/examples/lighting-app/nxp/mcxw71/args.gni index 5e8897a0d71..6be04211091 100644 --- a/examples/lighting-app/nxp/mcxw71/args.gni +++ b/examples/lighting-app/nxp/mcxw71/args.gni @@ -35,7 +35,5 @@ chip_system_config_provide_statistics = false chip_system_config_use_open_thread_inet_endpoints = true chip_with_lwip = false -nxp_nvm_component = "nvs" - nxp_use_smu2_static = true nxp_use_smu2_dynamic = true diff --git a/examples/lit-icd-app/esp32/sdkconfig.defaults b/examples/lit-icd-app/esp32/sdkconfig.defaults index 4ac165c475a..b71231e65dc 100644 --- a/examples/lit-icd-app/esp32/sdkconfig.defaults +++ b/examples/lit-icd-app/esp32/sdkconfig.defaults @@ -1,5 +1,3 @@ -# Monitoring baud and flash size -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y # Enable BT diff --git a/examples/lock-app/esp32/sdkconfig.defaults b/examples/lock-app/esp32/sdkconfig.defaults index 4755d2e2963..570699b7dad 100644 --- a/examples/lock-app/esp32/sdkconfig.defaults +++ b/examples/lock-app/esp32/sdkconfig.defaults @@ -19,13 +19,6 @@ # Some useful defaults for the demo app configuration. # -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - #enable BT CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y diff --git a/examples/lock-app/esp32/sdkconfig.defaults.esp32c6 b/examples/lock-app/esp32/sdkconfig.defaults.esp32c6 index a36808e3a4b..5844e52c9d3 100644 --- a/examples/lock-app/esp32/sdkconfig.defaults.esp32c6 +++ b/examples/lock-app/esp32/sdkconfig.defaults.esp32c6 @@ -1,59 +1 @@ CONFIG_IDF_TARGET="esp32c6" - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_FLASHMODE_QIO=y -CONFIG_ESPTOOLPY_FLASHFREQ_40M=y -CONFIG_ESPTOOLPY_FLASHFREQ="40m" -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - -# libsodium -CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y - -# NIMBLE -CONFIG_BT_ENABLED=y -CONFIG_BT_NIMBLE_ENABLED=y -CONFIG_BT_NIMBLE_EXT_ADV=n -CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 -CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=y - -# Disable OpenThread -CONFIG_OPENTHREAD_ENABLED=n - -# Disable lwip ipv6 autoconfig -CONFIG_LWIP_IPV6_AUTOCONFIG=y - -# Use a custom partition table -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" - -# LwIP config for OpenThread -CONFIG_LWIP_IPV6_NUM_ADDRESSES=8 -CONFIG_LWIP_MULTICAST_PING=y - -# mbedTLS -CONFIG_MBEDTLS_HARDWARE_AES=n -CONFIG_MBEDTLS_HARDWARE_MPI=n -CONFIG_MBEDTLS_HARDWARE_SHA=n -CONFIG_MBEDTLS_HARDWARE_ECC=y -CONFIG_MBEDTLS_CMAC_C=y -CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y -CONFIG_MBEDTLS_ECJPAKE_C=y - -# MDNS platform -CONFIG_USE_MINIMAL_MDNS=y -CONFIG_ENABLE_EXTENDED_DISCOVERY=y - -# FreeRTOS should use legacy API -CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y - -# Wi-Fi Settings -CONFIG_ENABLE_WIFI_STATION=y -# Enable this to avoid implicit declaration of function 'esp_send_assoc_resp' -CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y - -# Enable HKDF in mbedtls -CONFIG_MBEDTLS_HKDF_C=y diff --git a/examples/lock-app/nxp/k32w1/BUILD.gn b/examples/lock-app/nxp/k32w1/BUILD.gn index d54a69400bf..ba1d9053ce8 100644 --- a/examples/lock-app/nxp/k32w1/BUILD.gn +++ b/examples/lock-app/nxp/k32w1/BUILD.gn @@ -85,14 +85,6 @@ mcxw71_k32w1_sdk("sdk") { "CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR=${setup_discriminator}", ] - if (nxp_nvm_component == "littlefs") { - include_dirs += [ "${example_platform_dir}/board" ] - sources += [ - "${example_platform_dir}/board/peripherals.c", - "${example_platform_dir}/board/peripherals.h", - ] - } - if (nxp_multiple_ble_connections) { include_dirs += [ "${example_platform_dir}/app_ble/include" ] defines += [ diff --git a/examples/lock-app/nxp/mcxw71/BUILD.gn b/examples/lock-app/nxp/mcxw71/BUILD.gn index 57328e2da8f..f49e938aa7e 100644 --- a/examples/lock-app/nxp/mcxw71/BUILD.gn +++ b/examples/lock-app/nxp/mcxw71/BUILD.gn @@ -85,14 +85,6 @@ mcxw71_k32w1_sdk("sdk") { "CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR=${setup_discriminator}", ] - if (nxp_nvm_component == "littlefs") { - include_dirs += [ "${example_platform_dir}/board" ] - sources += [ - "${example_platform_dir}/board/peripherals.c", - "${example_platform_dir}/board/peripherals.h", - ] - } - if (nxp_multiple_ble_connections) { include_dirs += [ "${example_platform_dir}/app_ble/include" ] defines += [ diff --git a/examples/ota-provider-app/esp32/sdkconfig.defaults b/examples/ota-provider-app/esp32/sdkconfig.defaults index 6a06f08a0d5..b9bdeb01c33 100644 --- a/examples/ota-provider-app/esp32/sdkconfig.defaults +++ b/examples/ota-provider-app/esp32/sdkconfig.defaults @@ -19,14 +19,6 @@ # Some useful defaults for the demo app configuration. # - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - #enable BT CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y diff --git a/examples/ota-requestor-app/esp32/sdkconfig.defaults b/examples/ota-requestor-app/esp32/sdkconfig.defaults index b4629c26d79..2b56f64e4e4 100644 --- a/examples/ota-requestor-app/esp32/sdkconfig.defaults +++ b/examples/ota-requestor-app/esp32/sdkconfig.defaults @@ -19,14 +19,6 @@ # Some useful defaults for the demo app configuration. # - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - #enable BT CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y diff --git a/examples/persistent-storage/esp32/sdkconfig.defaults b/examples/persistent-storage/esp32/sdkconfig.defaults index 45488e615de..68318243ef2 100644 --- a/examples/persistent-storage/esp32/sdkconfig.defaults +++ b/examples/persistent-storage/esp32/sdkconfig.defaults @@ -15,13 +15,6 @@ # limitations under the License. # -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - # Use a custom partition table CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" diff --git a/examples/pigweed-app/esp32/sdkconfig.defaults b/examples/pigweed-app/esp32/sdkconfig.defaults index 6090ca73d6c..2991651fda8 100644 --- a/examples/pigweed-app/esp32/sdkconfig.defaults +++ b/examples/pigweed-app/esp32/sdkconfig.defaults @@ -19,14 +19,6 @@ # Some useful defaults for the demo app configuration. # - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - #enable lwip ipv6 autoconfig CONFIG_LWIP_IPV6_AUTOCONFIG=y diff --git a/examples/platform/esp32/external_platform/ESP32_custom/BUILD.gn b/examples/platform/esp32/external_platform/ESP32_custom/BUILD.gn index d8d2f92b542..c6725cb36cb 100644 --- a/examples/platform/esp32/external_platform/ESP32_custom/BUILD.gn +++ b/examples/platform/esp32/external_platform/ESP32_custom/BUILD.gn @@ -98,6 +98,7 @@ static_library("ESP32_custom") { ] deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/lib/dnssd:platform_header", "${chip_root}/src/platform/logging:headers", "${chip_root}/src/setup_payload", diff --git a/examples/platform/linux/system_rpc_server.cc b/examples/platform/linux/system_rpc_server.cc index b87a1c5d50b..345845cfc36 100644 --- a/examples/platform/linux/system_rpc_server.cc +++ b/examples/platform/linux/system_rpc_server.cc @@ -42,6 +42,7 @@ uint16_t socket_port = 33000; stream::ServerSocket server_socket; stream::SocketStream socket_stream; +bool socket_stream_ready = false; hdlc::RpcChannelOutput hdlc_channel_output(socket_stream, hdlc::kDefaultRpcAddress, "HDLC channel"); Channel channels[] = { rpc::Channel::Create<1>(&hdlc_channel_output) }; @@ -57,10 +58,20 @@ void set_socket_port(uint16_t new_socket_port) void Init() { PW_LOG_INFO("Starting pw_rpc server on port %d", socket_port); - PW_CHECK_OK(server_socket.Listen(socket_port)); + Status status = server_socket.Listen(socket_port); + if (!status.ok()) + { + PW_LOG_ERROR("Listen failed. Initialization failed."); + return; + } auto accept_result = server_socket.Accept(); - PW_CHECK_OK(accept_result.status()); - socket_stream = *std::move(accept_result); + if (!accept_result.status().ok()) + { + PW_LOG_ERROR("Accept failed. Initialization failed."); + return; + } + socket_stream = *std::move(accept_result); + socket_stream_ready = true; } rpc::Server & Server() @@ -70,6 +81,11 @@ rpc::Server & Server() Status Start() { + if (!socket_stream_ready) + { + PW_LOG_ERROR("Socket failed to initialize. PWRPC start failed."); + return Status::FailedPrecondition(); + } // Declare a buffer for decoding incoming HDLC frames. std::array input_buffer; hdlc::Decoder decoder(input_buffer); diff --git a/examples/platform/nxp/mcxw71_k32w1/app/support/BUILD.gn b/examples/platform/nxp/mcxw71_k32w1/app/support/BUILD.gn index 1034d91febf..1c1c3357a5a 100644 --- a/examples/platform/nxp/mcxw71_k32w1/app/support/BUILD.gn +++ b/examples/platform/nxp/mcxw71_k32w1/app/support/BUILD.gn @@ -55,8 +55,6 @@ source_set("freertos_memory_utils") { if (nxp_nvm_component == "fwk_nvm") { defines = [ "CHIP_PLAT_NVM_SUPPORT=1" ] - } else if (nxp_nvm_component == "littlefs") { - defines = [ "CHIP_PLAT_NVM_SUPPORT=3" ] } public_configs = [ diff --git a/examples/platform/silabs/BaseApplication.cpp b/examples/platform/silabs/BaseApplication.cpp index d7a1a80f9c0..9b0cf784fb4 100644 --- a/examples/platform/silabs/BaseApplication.cpp +++ b/examples/platform/silabs/BaseApplication.cpp @@ -184,7 +184,7 @@ void BaseApplicationDelegate::OnCommissioningSessionStopped() void BaseApplicationDelegate::OnCommissioningWindowClosed() { -#if CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI917 +#if CHIP_CONFIG_ENABLE_ICD_SERVER && (defined(SLI_SI91X_MCU_INTERFACE) && SLI_SI91X_MCU_INTERFACE == 1) if (!BaseApplication::GetProvisionStatus() && !isComissioningStarted) { int32_t status = wfx_power_save(RSI_SLEEP_MODE_8, DEEP_SLEEP_WITH_RAM_RETENTION); @@ -261,7 +261,7 @@ CHIP_ERROR BaseApplication::Init() * Wait for the WiFi to be initialized */ ChipLogProgress(AppServer, "APP: Wait WiFi Init"); - while (!wfx_hw_ready()) + while (!IsStationReady()) { osDelay(pdMS_TO_TICKS(10)); } diff --git a/examples/platform/silabs/MatterConfig.cpp b/examples/platform/silabs/MatterConfig.cpp index 0bf0af2a17c..da3ca0dfab2 100644 --- a/examples/platform/silabs/MatterConfig.cpp +++ b/examples/platform/silabs/MatterConfig.cpp @@ -26,7 +26,12 @@ #ifdef SL_WIFI #include -#endif /* SL_WIFI */ + +// TODO: We shouldn't need any platform specific includes in this file +#ifdef WF200_WIFI +#include +#endif // WF200_WIFI +#endif // SL_WIFI #if PW_RPC_ENABLED #include "Rpc.h" @@ -40,10 +45,14 @@ #include "MemMonitoring.h" #endif -#if defined(SLI_SI91X_MCU_INTERFACE) && SLI_SI91X_MCU_INTERFACE == 1 +// TODO: We shouldn't need any platform specific includes in this file +#if (defined(SLI_SI91X_MCU_INTERFACE) && SLI_SI91X_MCU_INTERFACE == 1) #include +#endif // (defined(SLI_SI91X_MCU_INTERFACE) && SLI_SI91X_MCU_INTERFACE == 1 ) + +#if ((defined(SLI_SI91X_MCU_INTERFACE) && SLI_SI91X_MCU_INTERFACE == 1) || defined(EXP_BOARD)) #include -#endif // SLI_SI91X_MCU_INTERFACE +#endif // ((defined(SLI_SI91X_MCU_INTERFACE) && SLI_SI91X_MCU_INTERFACE == 1) || defined(EXP_BOARD)) #include // If building with the EFR32-provided crypto backend, we can use the @@ -308,18 +317,16 @@ CHIP_ERROR SilabsMatterConfig::InitMatter(const char * appName) #ifdef SL_WIFI CHIP_ERROR SilabsMatterConfig::InitWiFi(void) { + // TODO: Platform specific init should not be required here #ifdef WF200_WIFI // Start wfx bus communication task. wfx_bus_start(); -#ifdef SL_WFX_USE_SECURE_LINK - // start securelink key renegotiation task - wfx_securelink_task_start(); -#endif // SL_WFX_USE_SECURE_LINK #endif // WF200_WIFI -#if defined(SLI_SI91X_MCU_INTERFACE) && SLI_SI91X_MCU_INTERFACE == 1 - VerifyOrReturnError(sl_matter_wifi_platform_init() == SL_STATUS_OK, CHIP_ERROR_INTERNAL); -#endif // SLI_SI91X_MCU_INTERFACE + // TODO: Platform specific init should not be required here +#if ((defined(SLI_SI91X_MCU_INTERFACE) && SLI_SI91X_MCU_INTERFACE == 1) || defined(EXP_BOARD)) + VerifyOrReturnError(InitSiWxWifi() == SL_STATUS_OK, CHIP_ERROR_INTERNAL); +#endif //((defined(SLI_SI91X_MCU_INTERFACE) && SLI_SI91X_MCU_INTERFACE == 1 ) || defined(EXP_BOARD)) return CHIP_NO_ERROR; } diff --git a/examples/platform/silabs/SiWx917/BUILD.gn b/examples/platform/silabs/SiWx917/BUILD.gn index 69379c11ff6..3087a5f64ba 100644 --- a/examples/platform/silabs/SiWx917/BUILD.gn +++ b/examples/platform/silabs/SiWx917/BUILD.gn @@ -18,6 +18,7 @@ import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") import("${chip_root}/examples/platform/silabs/args.gni") import("${chip_root}/src/lib/lib.gni") import("${chip_root}/src/platform/device.gni") +import("${chip_root}/src/platform/silabs/provision/args.gni") import("${chip_root}/src/platform/silabs/wifi/args.gni") import("${chip_root}/third_party/silabs/silabs_board.gni") import("${silabs_sdk_build_root}/SiWx917_sdk.gni") @@ -67,7 +68,7 @@ source_set("test-event-trigger") { "${silabs_common_plat_dir}/SilabsTestEventTriggerDelegate.h", ] - deps = [ "${chip_root}/src/platform/silabs/provision:provision-headers" ] + deps = [ "${sl_provision_root}:provision-headers" ] public_configs = [ ":test-event-trigger-config" ] public_deps = [ "${chip_root}/src/app:test-event-trigger", diff --git a/examples/platform/silabs/efr32/BUILD.gn b/examples/platform/silabs/efr32/BUILD.gn index 6d41d79c087..8b54549ad64 100644 --- a/examples/platform/silabs/efr32/BUILD.gn +++ b/examples/platform/silabs/efr32/BUILD.gn @@ -18,6 +18,7 @@ import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") import("${chip_root}/src/app/icd/icd.gni") import("${chip_root}/src/lib/lib.gni") import("${chip_root}/src/platform/device.gni") +import("${chip_root}/src/platform/silabs/provision/args.gni") import("${silabs_sdk_build_root}/efr32_sdk.gni") import("${silabs_sdk_build_root}/silabs_board.gni") @@ -71,7 +72,7 @@ source_set("test-event-trigger") { "${silabs_common_plat_dir}/SilabsTestEventTriggerDelegate.h", ] - deps = [ "${chip_root}/src/platform/silabs/provision:provision-headers" ] + deps = [ "${sl_provision_root}:provision-headers" ] public_configs = [ ":test-event-trigger-config" ] public_deps = [ "${chip_root}/src/app:test-event-trigger", diff --git a/examples/platform/silabs/provision/BUILD.gn b/examples/platform/silabs/provision/BUILD.gn index 517dd33c124..1ab4744a2e1 100644 --- a/examples/platform/silabs/provision/BUILD.gn +++ b/examples/platform/silabs/provision/BUILD.gn @@ -14,6 +14,7 @@ import("//build_overrides/chip.gni") import("//build_overrides/efr32_sdk.gni") +import("${chip_root}/src/platform/silabs/provision/args.gni") import("${silabs_sdk_build_root}/silabs_board.gni") if (wifi_soc) { @@ -48,8 +49,7 @@ source_set("storage") { deps = [ "${chip_root}/src/lib" ] - public_deps = - [ "${chip_root}/src/platform/silabs/provision:provision-headers" ] + public_deps = [ "${sl_provision_root}:provision-headers" ] if (sl_enable_test_event_trigger) { # Temporary workaround since we have duplicated configurations diff --git a/examples/shell/esp32/sdkconfig.defaults b/examples/shell/esp32/sdkconfig.defaults index efd5d235a1c..e6802e45906 100644 --- a/examples/shell/esp32/sdkconfig.defaults +++ b/examples/shell/esp32/sdkconfig.defaults @@ -15,14 +15,6 @@ # limitations under the License. # - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - #enable BT CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y diff --git a/examples/temperature-measurement-app/esp32/sdkconfig.defaults b/examples/temperature-measurement-app/esp32/sdkconfig.defaults index 3dcab272b29..9c6a93e1d0d 100644 --- a/examples/temperature-measurement-app/esp32/sdkconfig.defaults +++ b/examples/temperature-measurement-app/esp32/sdkconfig.defaults @@ -19,14 +19,6 @@ # Some useful defaults for the demo app configuration. # - -# Default to 921600 baud when flashing and monitoring device -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_BAUD=921600 -CONFIG_ESPTOOLPY_COMPRESSED=y -CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 - #enable BT CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y diff --git a/integrations/appengine/webapp_config.yaml b/integrations/appengine/webapp_config.yaml index f27ad7d68b8..84b204611ba 100644 --- a/integrations/appengine/webapp_config.yaml +++ b/integrations/appengine/webapp_config.yaml @@ -3,6 +3,9 @@ handlers: - url: / static_files: html/index.html upload: html/index.html + - url: /conformance/ + static_files: html/conformance_report.html + upload: html/conformance_report.html - url: /(.*) static_files: html/\1 upload: html/(.*) diff --git a/integrations/compute_engine/README.md b/integrations/compute_engine/README.md index 2260a79dd35..ffd821fb4b1 100644 --- a/integrations/compute_engine/README.md +++ b/integrations/compute_engine/README.md @@ -1,29 +1,33 @@ -## Startup Script of Compute Engine - -A startup script is a file that contains commands that run when a virtual -machine instance boots. Compute Engine provides support for running startup -scripts on Linux and Windows virtual machines. - -### Create a virtual machine instance using startup script - -The `startup-script.sh` could be used as the startup script of a virtual machine -instance which run Matter coverage report and publish the result via an App -Engine service. - -You can create a virtual machine instance by using the gcloud compute instances -create command with the `--metadata-from-file` flag. - -``` -gcloud compute instances create VM_NAME \ - --image-project=PROJECT_NAME \ - --image-family=ubuntu-22.04 \ - --metadata-from-file=startup-script=FILE_PATH -``` - -Replace the following: - -`PROJECT_NAME`: the name of the project host the virtual machine instance - -`VM_NAME`: the name of the virtual machine instance - -`FILE_PATH`: the relative path to the startup script file +## Google Cloud Compute Engine + +We have setup a Virtual Machine on +[Google Cloud](https://cloud.google.com/products/compute) to generate both the +[Matter SDK coverage report](https://matter-build-automation.ue.r.appspot.com) +and the +[Matter SDK Conformance report](https://matter-build-automation.ue.r.appspot.com/conformance_report.html). + +### The Matter SDK Virtual Machine and the "startup-script.sh" + +We created a VM named `matter-build-coverage`. The machine configuration is +located +[here](https://pantheon.corp.google.com/compute/instancesDetail/zones/us-central1-a/instances/matter-build-coverage?inv=1&invt=AbnAfg&project=matter-build-automation). +Reach out to Google team members if you need to make changes to this VM. + +This virtual machine is scheduled to run daily, starting at 11:45PM and stopping +at 2am. During boot, the machine runs the `startup-script.sh`. + +The `startup-script.sh` script contains commands to checkout the SDK repository +and create both the SDK coverage report and conformance report. The startup +script uses `scripts/build_coverage.sh` to generate the coverage report and +`scripts/examples/conformance_report.py` to generate the conformance report. The +resulting HTML files are published via an App Engine service and available here +([coverage report](https://matter-build-automation.ue.r.appspot.com/), +[conformance report](https://matter-build-automation.ue.r.appspot.com/conformance_report.html)). + +### Making Changes to "startup-script.sh" + +If you make changes to `startup-script.sh`, make sure you go to the +[VM configuration](https://pantheon.corp.google.com/compute/instancesDetail/zones/us-central1-a/instances/matter-build-coverage?inv=1&invt=AbnAfg&project=matter-build-automation), +click `edit` and update the startup script in the `Automation` text box, to +reflect your changes. The script in the Matter SDK repo is just a copy of the +configuration in the VM. diff --git a/integrations/compute_engine/startup-script.sh b/integrations/compute_engine/startup-script.sh index 113f45be260..94777c5a759 100755 --- a/integrations/compute_engine/startup-script.sh +++ b/integrations/compute_engine/startup-script.sh @@ -16,11 +16,23 @@ # limitations under the License. # +set -x + cd /tmp rm -rf connectedhomeip git clone --recurse-submodules https://github.com/project-chip/connectedhomeip.git cd connectedhomeip + +# Generate Coverage Report ./scripts/build_coverage.sh 2>&1 | tee /tmp/matter_build.log + +# Generate Conformance Report +source scripts/activate.sh +./scripts/build_python.sh -i out/python_env +python3 -u scripts/examples/conformance_report.py +cp /tmp/conformance_report/conformance_report.html out/coverage/coverage/html + +# Upload cd out/coverage/coverage gcloud app deploy webapp_config.yaml 2>&1 | tee /tmp/matter_publish.log versions=$(gcloud app versions list \ diff --git a/integrations/docker/images/base/chip-build/Dockerfile b/integrations/docker/images/base/chip-build/Dockerfile index db209a82f20..2a64ab7049d 100644 --- a/integrations/docker/images/base/chip-build/Dockerfile +++ b/integrations/docker/images/base/chip-build/Dockerfile @@ -69,6 +69,7 @@ RUN set -x \ libnl-route-3-dev \ libnspr4-dev \ libpango1.0-dev \ + libpcsclite-dev \ libpixman-1-dev \ libreadline-dev \ libsdl2-dev \ diff --git a/integrations/docker/images/base/chip-build/version b/integrations/docker/images/base/chip-build/version index 6c4ea451ea0..ad439b9fc61 100644 --- a/integrations/docker/images/base/chip-build/version +++ b/integrations/docker/images/base/chip-build/version @@ -1 +1 @@ -98 : [nrfconnect] Update nRF Connect SDK version. +100 : [linux] Added libpcsclite-dev package for NFC Commissioning on Linux platforms. diff --git a/integrations/docker/images/stage-2/chip-build-java/Dockerfile b/integrations/docker/images/stage-2/chip-build-java/Dockerfile index 8b1834cceda..bda9cb3e9f3 100644 --- a/integrations/docker/images/stage-2/chip-build-java/Dockerfile +++ b/integrations/docker/images/stage-2/chip-build-java/Dockerfile @@ -6,7 +6,7 @@ LABEL org.opencontainers.image.source https://github.com/project-chip/connectedh RUN set -x \ && apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -fy \ - openjdk-8-jdk \ + openjdk-11-jdk \ && rm -rf /var/lib/apt/lists/ \ && : # last line @@ -20,4 +20,4 @@ RUN set -x \ && : # last line ENV PATH $PATH:/usr/lib/kotlinc/bin -ENV JAVA_PATH=/usr/lib/jvm/java-8-openjdk-amd64 +ENV JAVA_PATH=/usr/lib/jvm/java-11-openjdk-amd64 diff --git a/integrations/docker/images/stage-3/chip-build-android/Dockerfile b/integrations/docker/images/stage-3/chip-build-android/Dockerfile index b2c33e7aaa5..219e6172902 100644 --- a/integrations/docker/images/stage-3/chip-build-android/Dockerfile +++ b/integrations/docker/images/stage-3/chip-build-android/Dockerfile @@ -8,7 +8,7 @@ LABEL org.opencontainers.image.source https://github.com/project-chip/connectedh RUN set -x \ && apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -fy \ - openjdk-8-jdk \ + openjdk-11-jdk \ rsync \ swig \ && rm -rf /var/lib/apt/lists/ \ diff --git a/scripts/build/builders/esp32.py b/scripts/build/builders/esp32.py index 02f7e25e55a..868bf0ad19d 100644 --- a/scripts/build/builders/esp32.py +++ b/scripts/build/builders/esp32.py @@ -124,7 +124,7 @@ def DefaultsFileName(board: Esp32Board, app: Esp32App, enable_rpcs: bool): return 'sdkconfig.defaults' rpc = "_rpc" if enable_rpcs else "" - if board == Esp32Board.DevKitC: + if board == Esp32Board.DevKitC or board == Esp32Board.C3DevKit: return 'sdkconfig{}.defaults'.format(rpc) elif board == Esp32Board.M5Stack: # a subset of apps have m5stack specific configurations. However others @@ -139,8 +139,6 @@ def DefaultsFileName(board: Esp32Board, app: Esp32App, enable_rpcs: bool): return 'sdkconfig_m5stack{}.defaults'.format(rpc) else: return 'sdkconfig{}.defaults'.format(rpc) - elif board == Esp32Board.C3DevKit: - return 'sdkconfig{}.defaults.esp32c3'.format(rpc) else: raise Exception('Unknown board type') @@ -173,6 +171,20 @@ def _IdfEnvExecute(self, cmd, title=None): ['bash', '-c', 'source $IDF_PATH/export.sh; source scripts/activate.sh; %s' % cmd], title=title) + @property + def TargetName(self): + if self.board == Esp32Board.C3DevKit: + return 'esp32c3' + else: + return 'esp32' + + @property + def TargetFileName(self) -> Optional[str]: + if self.board == Esp32Board.C3DevKit: + return 'sdkconfig.defaults.esp32c3' + else: + return None + @property def ExamplePath(self): return os.path.join(self.app.ExamplePath, 'esp32') @@ -195,6 +207,11 @@ def generate(self): self._Execute( ['rm', '-f', os.path.join(self.ExamplePath, 'sdkconfig')]) + if self.TargetFileName is not None: + target_defaults = os.path.join(self.ExamplePath, self.TargetFileName) + if os.path.exists(target_defaults): + self._Execute(['cp', target_defaults, os.path.join(self.output_dir, self.TargetFileName)]) + if not self.enable_ipv4: self._Execute( ['bash', '-c', 'echo -e "\\nCONFIG_DISABLE_IPV4=y\\n" >>%s' % shlex.quote(defaults_out)]) @@ -221,8 +238,9 @@ def generate(self): cmake_args = " ".join(cmake_args) defaults = shlex.quote(defaults_out) + target = shlex.quote(self.TargetName) - cmd = f"\nexport SDKCONFIG_DEFAULTS={defaults}\nidf.py {cmake_args} reconfigure" + cmd = f"\nexport SDKCONFIG_DEFAULTS={defaults}\nidf.py {cmake_args} -DIDF_TARGET={target} reconfigure" # This will do a 'cmake reconfigure' which will create ninja files without rebuilding self._IdfEnvExecute(cmd) diff --git a/scripts/build/testdata/dry_run_esp32-devkitc-light-rpc.txt b/scripts/build/testdata/dry_run_esp32-devkitc-light-rpc.txt index cb5e3df310e..844a6813257 100644 --- a/scripts/build/testdata/dry_run_esp32-devkitc-light-rpc.txt +++ b/scripts/build/testdata/dry_run_esp32-devkitc-light-rpc.txt @@ -12,7 +12,7 @@ bash -c 'echo -e "\nCONFIG_ESP_INSIGHTS_ENABLED=n\nCONFIG_ENABLE_ESP_INSIGHTS_TR bash -c 'source $IDF_PATH/export.sh; source scripts/activate.sh; export SDKCONFIG_DEFAULTS={out}/esp32-devkitc-light-rpc/sdkconfig.defaults -idf.py -C examples/lighting-app/esp32 -B {out}/esp32-devkitc-light-rpc reconfigure' +idf.py -C examples/lighting-app/esp32 -B {out}/esp32-devkitc-light-rpc -DIDF_TARGET=esp32 reconfigure' rm -f examples/lighting-app/esp32/sdkconfig diff --git a/scripts/dm_xml_ci_change_enforcement.py b/scripts/dm_xml_ci_change_enforcement.py index 0efe40f66fa..2c1ebdb5a00 100644 --- a/scripts/dm_xml_ci_change_enforcement.py +++ b/scripts/dm_xml_ci_change_enforcement.py @@ -36,7 +36,7 @@ def check_dm_directory(dir): def check_dir(dir): cmd = f'git diff HEAD^..HEAD --name-only -- {dir}' - output = subprocess.check_output(cmd, shell=True).decode().splitlines() + output = subprocess.check_output(cmd, shell=True).decode() if output and 'spec_sha' not in output and 'scraper_version' not in output: print(f'Data model directory {dir} had changes to the following files without a corresponding update to the spec SHA') print(output) @@ -44,7 +44,7 @@ def check_dir(dir): return 1 return 0 - ret = check_dir(clusters) + check_dir(device_types) + ret = check_dir(dir) sys.exit(ret) diff --git a/scripts/examples/conformance_report.py b/scripts/examples/conformance_report.py new file mode 100644 index 00000000000..fe38d542932 --- /dev/null +++ b/scripts/examples/conformance_report.py @@ -0,0 +1,400 @@ +import argparse +import csv +import glob +import os +import re +import subprocess +from datetime import datetime + +# Constants for default values +DEFAULT_TARGETS = [ + "linux-x64-air-purifier-no-ble", + "linux-x64-air-quality-sensor-no-ble", + "linux-x64-all-clusters-minimal-no-ble", + "linux-x64-all-clusters-no-ble-clang-boringssl", + "linux-x64-bridge-no-ble-clang-boringssl", + "linux-x64-contact-sensor-no-ble", + "linux-x64-dishwasher-no-ble", + "linux-x64-energy-management-no-ble-clang-boringssl", + "linux-x64-light-data-model-no-unique-id-no-ble", + "linux-x64-light-no-ble", + "linux-x64-lit-icd-no-ble", + "linux-x64-lock-no-ble-clang-boringssl", + "linux-x64-microwave-oven-no-ble-clang-boringssl", + "linux-x64-network-manager-ipv6only-no-ble-clang-boringssl", + "linux-x64-ota-provider-no-ble", + "linux-x64-ota-provider-no-ble-clang-boringssl", + "linux-x64-ota-requestor-no-ble", + "linux-x64-refrigerator-no-ble", + "linux-x64-rvc-no-ble", + "linux-x64-rvc-no-ble-clang-boringssl", + "linux-x64-thermostat-no-ble", + "linux-x64-tv-app-no-ble-clang-boringssl", + "linux-x64-tv-casting-app-no-ble", + "linux-x64-water-leak-detector-no-ble" +] +DEFAULT_TESTS = ["TC_DeviceBasicComposition", "TC_DeviceConformance"] +TMP_RESULTS_DIR = "/tmp/conformance_report" +OUT_DIR = "./out" +TEST_COMMAND = "scripts/run_in_python_env.sh out/python_env './scripts/tests/run_python_test.py --app {} --factory-reset --app-args \"--trace-to json:log\" --script src/python_testing/{}.py --script-args \"--qr-code MT:-24J0AFN00KA0648G00\"'" +BUILD_COMMAND = "python3 scripts/build/build_examples.py --ninja-jobs {} --target {} build" +NINJA_JOBS = max(os.cpu_count() - 2, 1) # Limit # of jobs to avoid using too much CPU and RAM + + +def find_executables(dirs): + """ + Look for the first executable file in a list of directories. + + dirs is a list of directories for each sample app built. We assume there's only + a single executable in each directory, so once we find one we put it on the list to return. + + This is just to avoid maintaining a list with the names of all executables for each target. + For example, for a list of directories: + + dirs = [out/linux-x64-lock-no-ble-clang-boringssl, + out/linux-x64-network-manager-ipv6only-no-ble-clang-boringssl] + + find_executables(dirs) will return: + + [out/linux-x64-lock-no-ble-clang-boringssl/chip-lock-app, + out/linux-x64-network-manager-ipv6only-no-ble-clang-boringssl/matter-network-manager-app] + + Args: + dirs: A list of directories to search. + + Returns: + A list of paths to the first executable found in each directory. + """ + executables = [] + for dir in dirs: + if not os.path.isdir(dir): + continue + for filename in os.listdir(dir): + filepath = os.path.join(dir, filename) + if os.path.isfile(filepath) and os.access(filepath, os.X_OK): + executables.append(filepath) + break # Move to the next directory + return executables + + +def parse_test_logs(test_log_paths): + """ + Analyze test output files and return a dictionary summarizing each test. + + Args: + test_log_paths: A list of paths to test output files. + + Returns: + A dictionary where keys are test names and values are lists of results. + Each result is a list: [app_target_name, test_pass_fail, error_count, test_cmd, error_summary]. + """ + + all_tests_results = {} + for test_log_path in test_log_paths: + print(f" Parsing {test_log_path}") + try: + with open(test_log_path, "r") as f: + output_lines = f.readlines() + except FileNotFoundError: + print(f"Result file not found {test_log_path}. Skipping...") + continue + + app_target_name = "" + test_pass_fail = "" + failures = [] + test_name_from_log = "" + test_cmd = "" + + # Use a for loop with enumerate for easier line processing + for i, line in enumerate(output_lines): + if not test_name_from_log and "INFO Executing " in line: + test_name_from_log = line.split("INFO Executing ")[1].split()[0].split(".")[0] + all_tests_results.setdefault(test_name_from_log, []) + + if not app_target_name and " --app " in line: + app_target_name = os.path.basename(line.split(" --app ")[1].split()[0]) + + if not test_pass_fail and "Final result: " in line: + test_pass_fail = line.split("Final result: ")[1].split()[0] + + if not test_cmd and "run_python_test.py" in line: + test_cmd = "run_python_test.py " + line.split("run_python_test.py")[1] + + if "Problem: ProblemSeverity.ERROR" in line: + try: + error_details = "\n".join( + [" " + re.sub(r"^\[.*?\]\[.*?\]\[.*?\]", "", error_line).strip() for error_line in output_lines[i:i+8]] + ) + failures.append(error_details + "\n") + except IndexError: + print("IndexError: End of file reached unexpectedly.") + break + + if not all([app_target_name, test_pass_fail, test_name_from_log]): + print("Invalid test output file, couldn't parse it. Skipping...") + continue + + if test_pass_fail == "FAIL" and not failures: + last_log_lines = output_lines[-100:] if len(output_lines) > 100 else output_lines + failures.append("Test didn't complete. Possible timeout or crash. Last log lines:") + failures.append("\n".join(last_log_lines)) + test_pass_fail = "INVALID" + + print(f"\t{app_target_name}\t{test_pass_fail}\t{len(failures)} errors.") + all_tests_results[test_name_from_log].append( + [app_target_name, test_pass_fail, len(failures), test_cmd, "\n".join(failures)] + ) + + return all_tests_results + + +def run_tests(tests, executable_paths, tmp_results_dir, skip_testing): + """ + Runs tests against executable files. + + Args: + tests: A list of test names to run. + executable_paths: A list of paths to executable files. + tmp_results_dir: Directory to store test output. + skip_testing: Flag to skip test execution. + """ + for test_name in tests: + for executable_path in executable_paths: + app_name = os.path.basename(executable_path) + if skip_testing: + print(f"Testing {app_name} ...skipped") + continue + + print(f"Testing {app_name} against {test_name}...") + try: + command = TEST_COMMAND.format(executable_path, test_name) + test_output_path = os.path.join(tmp_results_dir, f"{test_name}_{app_name}.txt") + with open(test_output_path, "wb") as f: + result = subprocess.run(command, shell=True, capture_output=False, + text=False, stdout=f, stderr=subprocess.STDOUT) + result.check_returncode() # Raise an exception if the command returned a non-zero exit code + print(f" - Test PASSED. Logs written to {test_output_path}") + except subprocess.CalledProcessError as e: + print(f" - Test FAILED. Logs written to {test_output_path}: {e}") + except Exception as e: + print(f"Error running test for {app_name}: {e}") + + +def build_targets(targets, skip_building): + """ + Builds targets using the specified build command. + + Args: + targets: A list of targets to build. + skip_building: Flag to skip building. + """ + for target in targets: + if skip_building: + print(f"Building: {target} ...skipped") + continue + + command = BUILD_COMMAND.format(NINJA_JOBS, target) + try: + print(f"Building: {target}") + print(command) + result = subprocess.run(command, shell=True, capture_output=False, text=True) + result.check_returncode() # Raise CalledProcessError if build fails + except subprocess.CalledProcessError as e: + print(f"Error building {target}:") + print(f" Return code: {e.returncode}") + print(f" stdout: {e.stdout}") + print(f" stderr: {e.stderr}") + except Exception as e: + print(f"Error building {target}: {e}") + + +def generate_csv_summaries(all_tests_results_dict, out_dir): + """ + Generates CSV summaries of test results. + + Args: + all_tests_results_dict: Dictionary of test results. + out_dir: Directory to save CSV files. + """ + for test_name, test_results in all_tests_results_dict.items(): + csv_filename = os.path.join(out_dir, f"{test_name}_summary.csv") + with open(csv_filename, 'w', newline='') as f: + writer = csv.writer(f) + test_passed_count = sum(1 for result in test_results if result[1] == "PASS") + writer.writerow([f"{test_name} ({test_passed_count} / {len(test_results)}) examples passed"]) + writer.writerow(["Application", "Result", "Errors", "Test Command", "Error Summary"]) + writer.writerows(test_results) + + print("CSV test summary saved to " + csv_filename) + + +def csv_to_html_report(csv_file_paths, html_page_title, html_out_dir, sha): + """ + Generates an HTML report from CSV files. + + Args: + csv_file_paths: List of paths to CSV files. + html_page_title: Title of the HTML report. + html_out_dir: Directory to save the HTML report. + sha: SHA commit hash for the report. + """ + now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") + html_report = f""" + + + + + {html_page_title} + +

{html_page_title}

+
+

Generated on {now}
SHA: {sha}

+
+ + """ + + for csv_file_path in csv_file_paths: + with open(csv_file_path, 'r') as csv_file: + reader = csv.reader(csv_file) + table_title = next(reader)[0] + headers = next(reader) + data = list(reader) + + html_table = f"

{table_title}

" + html_table += "" + "".join(f"" for header in headers) + "" + for row in data: + html_table += "" + for cell in row: + if len(cell) > 100: + html_table += "" + elif cell in ("PASS", "FAIL"): + html_table += f"" + else: + html_table += "" + html_table += "" + html_table += "
{header}
Show/Hide" + cell.replace('\n', '
') + "
{cell}" + cell.replace('\n', '
') + "
" + html_report += html_table + + html_report += """ + + + """ + + html_file = os.path.join(html_out_dir, "conformance_report.html") + print("Saving HTML report to " + html_file) + with open(html_file, "w") as f: + f.write(html_report) + + +def get_git_revision_hash() -> str: + return subprocess.check_output(['git', 'rev-parse', 'HEAD']).decode('ascii').strip() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Build examples, run conformance tests and generate a report with the results." + ) + parser.add_argument( + "--test-name", + help="Override the default tests with a specific test name." + ) + parser.add_argument( + "--target-name", + help="Override the default targets with a specific target name." + ) + parser.add_argument( + "--skip-building", + help="Skip building the target(s).", + action="store_true" + ) + parser.add_argument( + "--skip-testing", + help="Skip testing the target(s).", + action="store_true" + ) + parser.add_argument( + "--skip-html", + help="Skip generating the HTML report.", + action="store_true" + ) + parser.add_argument( + "--html-out-dir", + help="Specify the directory to save the HTML report.", + default=TMP_RESULTS_DIR + ) + + args = parser.parse_args() + + targets = [args.target_name] if args.target_name else DEFAULT_TARGETS + target_out_dirs = [os.path.join(OUT_DIR, target) for target in targets] + tests = [args.test_name] if args.test_name else DEFAULT_TESTS + + os.makedirs(TMP_RESULTS_DIR, exist_ok=True) + + build_targets(targets, args.skip_building) + executable_paths = find_executables(target_out_dirs) + run_tests(tests, executable_paths, TMP_RESULTS_DIR, args.skip_testing) + + print(f"Parsing all test output logs in {TMP_RESULTS_DIR}...") + test_logs = glob.glob(os.path.join(TMP_RESULTS_DIR, "*.txt")) + aggregated_results_dict = parse_test_logs(test_logs) + generate_csv_summaries(aggregated_results_dict, TMP_RESULTS_DIR) + csv_summaries_paths = glob.glob(os.path.join(TMP_RESULTS_DIR, "*.csv")) + + if not args.skip_html: + os.makedirs(args.html_out_dir, exist_ok=True) + csv_to_html_report( + csv_summaries_paths, + "Matter SDK Example Conformance Report", + args.html_out_dir, + get_git_revision_hash() + ) diff --git a/scripts/examples/gn_silabs_example.sh b/scripts/examples/gn_silabs_example.sh index 2a565662557..11f65022709 100755 --- a/scripts/examples/gn_silabs_example.sh +++ b/scripts/examples/gn_silabs_example.sh @@ -205,7 +205,7 @@ else NCP_DIR_SUFFIX="/"$2 USE_WIFI=true - optArgs+="chip_device_platform =\"efr32\" chip_crypto_keystore=\"psa\"" + optArgs+="chip_device_platform =\"efr32\" chip_crypto_keystore=\"psa\" " shift shift ;; diff --git a/scripts/spec_xml/generate_spec_xml.py b/scripts/spec_xml/generate_spec_xml.py index 43582fdb366..e6a77224683 100755 --- a/scripts/spec_xml/generate_spec_xml.py +++ b/scripts/spec_xml/generate_spec_xml.py @@ -19,21 +19,18 @@ import os import re import subprocess -import sys import xml.etree.ElementTree as ElementTree from pathlib import Path import click -from paths import Branch, get_chip_root, get_data_model_path, get_documentation_file_path, get_in_progress_defines +from chip.testing.spec_parsing import build_xml_clusters +from paths import get_chip_root, get_documentation_file_path, get_in_progress_defines # Use the get_in_progress_defines() function to fetch the in-progress defines CURRENT_IN_PROGRESS_DEFINES = get_in_progress_defines() # Replace hardcoded paths with dynamic paths using paths.py functions DEFAULT_CHIP_ROOT = get_chip_root() -DEFAULT_OUTPUT_DIR_1_3 = get_data_model_path(Branch.V1_3) -DEFAULT_OUTPUT_DIR_IN_PROGRESS = get_data_model_path(Branch.IN_PROGRESS) -DEFAULT_OUTPUT_DIR_TOT = get_data_model_path(Branch.MASTER) DEFAULT_DOCUMENTATION_FILE = get_documentation_file_path() @@ -71,6 +68,7 @@ def make_asciidoc(target: str, include_in_progress: str, spec_dir: str, dry_run: help='Path to the spec root') @click.option( '--output-dir', + required=True, help='Path to output xml files') @click.option( '--dry-run', @@ -81,9 +79,6 @@ def make_asciidoc(target: str, include_in_progress: str, spec_dir: str, dry_run: '--include-in-progress', type=click.Choice(['All', 'None', 'Current']), default='All') def main(scraper, spec_root, output_dir, dry_run, include_in_progress): - if not output_dir: - output_dir_map = {'All': DEFAULT_OUTPUT_DIR_TOT, 'None': DEFAULT_OUTPUT_DIR_1_3, 'Current': DEFAULT_OUTPUT_DIR_IN_PROGRESS} - output_dir = output_dir_map[include_in_progress] scrape_clusters(scraper, spec_root, output_dir, dry_run, include_in_progress) scrape_device_types(scraper, spec_root, output_dir, dry_run, include_in_progress) if not dry_run: @@ -195,18 +190,12 @@ def dump_versions(scraper, spec_root, output_dir): def dump_cluster_ids(output_dir): - python_testing_path = os.path.abspath( - os.path.join(DEFAULT_CHIP_ROOT, 'src', 'python_testing')) - sys.path.insert(0, python_testing_path) clusters_output_dir = os.path.abspath( os.path.join(output_dir, 'clusters')) - - from spec_parsing_support import build_xml_clusters - header = '# List of currently defined spec clusters\n' header += 'This file was **AUTOMATICALLY** generated by `python scripts/generate_spec_xml.py`. DO NOT EDIT BY HAND!\n\n' - clusters, problems = build_xml_clusters(clusters_output_dir) + clusters, problems = build_xml_clusters(Path(clusters_output_dir)) all_name_lens = [len(c.name) for c in clusters.values()] name_len = max(all_name_lens) title_id_decimal = ' ID (Decimal) ' diff --git a/scripts/spec_xml/spec_revision_diff_summary.py b/scripts/spec_xml/spec_revision_diff_summary.py new file mode 100644 index 00000000000..bf735e8d099 --- /dev/null +++ b/scripts/spec_xml/spec_revision_diff_summary.py @@ -0,0 +1,185 @@ +# +# Copyright (c) 2024 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. +# +# This script gives a print out of the differences between the two specified spec +# versions and a description of the provisional elements in the later version. +# Right now, this is just in print form. The intent is to use this for new +# data model XML drops to show the differences. This was also used to double-check +# spec expectations before the 1.4 release and we should continue to do so going forward. + +import click +from chip.testing.conformance import ConformanceDecision +from chip.testing.spec_parsing import PrebuiltDataModelDirectory, build_xml_clusters, build_xml_device_types + + +def get_changes(old, new): + added = [e.name for id, e in new.items() if id not in old.keys()] + removed = [e.name for id, e in old.items() if id not in new.keys()] + same_ids = set(new.keys()).intersection(set(old.keys())) + + return added, removed, same_ids + + +def str_changes(element, added, removed, change_ids, old, new): + if not added and not removed and not change_ids: + return [] + + ret = [] + if added: + ret.append(f'\t{element} added: {added}') + if removed: + ret.append(f'\t{element} removed: {removed}') + if change_ids: + ret.append(f'\t{element} changed:') + for id in change_ids: + name = old[id].name if old[id].name == new[id].name else f'{new[id].name} (previously {old[id].name})' + ret.append(f'\t\t{name}') + ret.append(f'\t\t\t{old[id]}') + ret.append(f'\t\t\t{new[id]}') + return ret + + +def str_element_changes(element, old, new): + added, removed, same_ids = get_changes(old, new) + change_ids = [id for id in same_ids if old[id] != new[id] or str(old[id].conformance) != str(new[id].conformance)] + return str_changes(element, added, removed, change_ids, old, new) + + +def diff_clusters(prior_revision: PrebuiltDataModelDirectory, new_revision: PrebuiltDataModelDirectory) -> None: + prior_clusters, _ = build_xml_clusters(PrebuiltDataModelDirectory.k1_3) + new_clusters, _ = build_xml_clusters(PrebuiltDataModelDirectory.k1_4) + + additional_clusters, removed_clusters, same_cluster_ids = get_changes(prior_clusters, new_clusters) + + print(f'\n\nClusters newly added in {new_revision.dirname}') + print(additional_clusters) + print(f'\n\nClusters removed since {prior_revision.dirname}') + print(removed_clusters) + + for cid in same_cluster_ids: + new = new_clusters[cid] + old = prior_clusters[cid] + + name = old.name if old.name == new.name else f'{new.name} (previously {old.name})' + + changes = [] + if old.revision != new.revision: + changes.append(f'\tRevision change - old: {old.revision} new: {new.revision}') + changes.extend(str_element_changes('Features', old.features, new.features)) + changes.extend(str_element_changes('Attributes', old.attributes, new.attributes)) + changes.extend(str_element_changes('Accepted Commands', old.accepted_commands, new.accepted_commands)) + changes.extend(str_element_changes('Generated Commands', old.generated_commands, new.generated_commands)) + changes.extend(str_element_changes('Events', old.events, new.events)) + + if changes: + print(f'\n\nCluster {name}') + print('\n'.join(changes)) + + +def diff_device_types(prior_revision: PrebuiltDataModelDirectory, new_revision: PrebuiltDataModelDirectory) -> None: + prior_device_types, _ = build_xml_device_types(prior_revision) + new_device_types, _ = build_xml_device_types(new_revision) + + additional_device_types, removed_device_types, same_device_type_ids = get_changes(prior_device_types, new_device_types) + + print(f'\n\nDevice Types newly added in {new_revision.dirname}') + print(additional_device_types) + print(f'\n\nDevice Types removed since {prior_revision.dirname}') + print(removed_device_types) + + for cid in same_device_type_ids: + new = new_device_types[cid] + old = prior_device_types[cid] + + name = old.name if old.name == new.name else f'{new.name} (previously {old.name})' + + changes = [] + if old.revision != new.revision: + changes.append(f'\tRevision change - old: {old.revision} new: {new.revision}') + changes.extend(str_element_changes('Server Clusters', old.server_clusters, new.server_clusters)) + changes.extend(str_element_changes('Client Clusters', old.client_clusters, new.client_clusters)) + + if changes: + print(f'\n\nDevice Type {name}') + print('\n'.join(changes)) + + +def _get_provisional(items): + return [e.name for e in items if e.conformance(0, [], []).decision == ConformanceDecision.PROVISIONAL] + + +def get_all_provisional_clusters(new_revision: PrebuiltDataModelDirectory): + clusters, _ = build_xml_clusters(new_revision) + + provisional_clusters = [c.name for c in clusters.values() if c.is_provisional] + print('\n\nProvisional Clusters') + print(f'\t{sorted(provisional_clusters)}') + + for c in clusters.values(): + features = _get_provisional(c.features.values()) + attributes = _get_provisional(c.attributes.values()) + accepted_commands = _get_provisional(c.accepted_commands.values()) + generated_commands = _get_provisional(c.generated_commands.values()) + events = _get_provisional(c.events.values()) + + if not features and not attributes and not accepted_commands and not generated_commands and not events: + continue + + print(f'\n{c.name}') + if features: + print(f'\tProvisional features: {features}') + if attributes: + print(f'\tProvisional attributes: {attributes}') + if accepted_commands: + print(f'\tProvisional accepted commands: {accepted_commands}') + if generated_commands: + print(f'\tProvisional generated commands: {generated_commands}') + if events: + print(f'\tProvisional events: {events}') + + +def get_all_provisional_device_types(new_revision: PrebuiltDataModelDirectory): + device_types, _ = build_xml_device_types(new_revision) + + for d in device_types.values(): + server_clusters = _get_provisional(d.server_clusters.values()) + client_clusters = _get_provisional(d.client_clusters.values()) + if not server_clusters and not client_clusters: + continue + + print(f'\n{d.name}') + if server_clusters: + print(f'\tProvisional server clusters: {server_clusters}') + if client_clusters: + print(f'\tProvisional client clusters: {client_clusters}') + + +REVISIONS = {'1.3': PrebuiltDataModelDirectory.k1_3, + '1.4': PrebuiltDataModelDirectory.k1_4, 'master': PrebuiltDataModelDirectory.kMaster} + + +@click.command() +@click.argument('prior_revision', type=click.Choice(list(REVISIONS.keys()))) +@click.argument('new_revision', type=click.Choice(list(REVISIONS.keys()))) +def main(prior_revision: str, new_revision: str): + diff_clusters(REVISIONS[prior_revision], REVISIONS[new_revision]) + diff_device_types(REVISIONS[prior_revision], REVISIONS[new_revision]) + get_all_provisional_clusters(REVISIONS[new_revision]) + get_all_provisional_device_types(REVISIONS[new_revision]) + + +if __name__ == "__main__": + main() diff --git a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/IMClusterCommandHandler.cpp b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/IMClusterCommandHandler.cpp index 09f8a2cbe1e..2027fc3f2a7 100644 --- a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/IMClusterCommandHandler.cpp +++ b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/IMClusterCommandHandler.cpp @@ -1748,47 +1748,6 @@ Protocols::InteractionModel::Status DispatchServerCommand(CommandHandler * apCom } // namespace ValveConfigurationAndControl -namespace WiFiNetworkDiagnostics { - -Protocols::InteractionModel::Status DispatchServerCommand(CommandHandler * apCommandObj, const ConcreteCommandPath & aCommandPath, - TLV::TLVReader & aDataTlv) -{ - CHIP_ERROR TLVError = CHIP_NO_ERROR; - bool wasHandled = false; - { - switch (aCommandPath.mCommandId) - { - case Commands::ResetCounts::Id: { - Commands::ResetCounts::DecodableType commandData; - TLVError = DataModel::Decode(aDataTlv, commandData); - if (TLVError == CHIP_NO_ERROR) - { - wasHandled = emberAfWiFiNetworkDiagnosticsClusterResetCountsCallback(apCommandObj, aCommandPath, commandData); - } - break; - } - default: { - // Unrecognized command ID, error status will apply. - ChipLogError(Zcl, "Unknown command " ChipLogFormatMEI " for cluster " ChipLogFormatMEI, - ChipLogValueMEI(aCommandPath.mCommandId), ChipLogValueMEI(aCommandPath.mClusterId)); - return Protocols::InteractionModel::Status::UnsupportedCommand; - } - } - } - - if (CHIP_NO_ERROR != TLVError || !wasHandled) - { - ChipLogProgress(Zcl, "Failed to dispatch command, TLVError=%" CHIP_ERROR_FORMAT, TLVError.Format()); - return Protocols::InteractionModel::Status::InvalidCommand; - } - - // We use success as a marker that no special handling is required - // This is to avoid having a std::optional which uses slightly more code. - return Protocols::InteractionModel::Status::Success; -} - -} // namespace WiFiNetworkDiagnostics - namespace WindowCovering { Protocols::InteractionModel::Status DispatchServerCommand(CommandHandler * apCommandObj, const ConcreteCommandPath & aCommandPath, @@ -1964,9 +1923,6 @@ void DispatchSingleClusterCommand(const ConcreteCommandPath & aCommandPath, TLV: case Clusters::ValveConfigurationAndControl::Id: errorStatus = Clusters::ValveConfigurationAndControl::DispatchServerCommand(apCommandObj, aCommandPath, aReader); break; - case Clusters::WiFiNetworkDiagnostics::Id: - errorStatus = Clusters::WiFiNetworkDiagnostics::DispatchServerCommand(apCommandObj, aCommandPath, aReader); - break; case Clusters::WindowCovering::Id: errorStatus = Clusters::WindowCovering::DispatchServerCommand(apCommandObj, aCommandPath, aReader); break; diff --git a/scripts/tools/zap/tests/outputs/lighting-app/app-templates/IMClusterCommandHandler.cpp b/scripts/tools/zap/tests/outputs/lighting-app/app-templates/IMClusterCommandHandler.cpp index 11b0dc14a23..61dae1f9daf 100644 --- a/scripts/tools/zap/tests/outputs/lighting-app/app-templates/IMClusterCommandHandler.cpp +++ b/scripts/tools/zap/tests/outputs/lighting-app/app-templates/IMClusterCommandHandler.cpp @@ -969,47 +969,6 @@ Protocols::InteractionModel::Status DispatchServerCommand(CommandHandler * apCom } // namespace ThreadNetworkDiagnostics -namespace WiFiNetworkDiagnostics { - -Protocols::InteractionModel::Status DispatchServerCommand(CommandHandler * apCommandObj, const ConcreteCommandPath & aCommandPath, - TLV::TLVReader & aDataTlv) -{ - CHIP_ERROR TLVError = CHIP_NO_ERROR; - bool wasHandled = false; - { - switch (aCommandPath.mCommandId) - { - case Commands::ResetCounts::Id: { - Commands::ResetCounts::DecodableType commandData; - TLVError = DataModel::Decode(aDataTlv, commandData); - if (TLVError == CHIP_NO_ERROR) - { - wasHandled = emberAfWiFiNetworkDiagnosticsClusterResetCountsCallback(apCommandObj, aCommandPath, commandData); - } - break; - } - default: { - // Unrecognized command ID, error status will apply. - ChipLogError(Zcl, "Unknown command " ChipLogFormatMEI " for cluster " ChipLogFormatMEI, - ChipLogValueMEI(aCommandPath.mCommandId), ChipLogValueMEI(aCommandPath.mClusterId)); - return Protocols::InteractionModel::Status::UnsupportedCommand; - } - } - } - - if (CHIP_NO_ERROR != TLVError || !wasHandled) - { - ChipLogProgress(Zcl, "Failed to dispatch command, TLVError=%" CHIP_ERROR_FORMAT, TLVError.Format()); - return Protocols::InteractionModel::Status::InvalidCommand; - } - - // We use success as a marker that no special handling is required - // This is to avoid having a std::optional which uses slightly more code. - return Protocols::InteractionModel::Status::Success; -} - -} // namespace WiFiNetworkDiagnostics - } // namespace Clusters void DispatchSingleClusterCommand(const ConcreteCommandPath & aCommandPath, TLV::TLVReader & aReader, CommandHandler * apCommandObj) @@ -1054,9 +1013,6 @@ void DispatchSingleClusterCommand(const ConcreteCommandPath & aCommandPath, TLV: case Clusters::ThreadNetworkDiagnostics::Id: errorStatus = Clusters::ThreadNetworkDiagnostics::DispatchServerCommand(apCommandObj, aCommandPath, aReader); break; - case Clusters::WiFiNetworkDiagnostics::Id: - errorStatus = Clusters::WiFiNetworkDiagnostics::DispatchServerCommand(apCommandObj, aCommandPath, aReader); - break; default: ChipLogError(Zcl, "Unknown cluster " ChipLogFormatMEI, ChipLogValueMEI(aCommandPath.mClusterId)); errorStatus = Protocols::InteractionModel::Status::UnsupportedCluster; diff --git a/src/access/BUILD.gn b/src/access/BUILD.gn index 66370090a4f..82eca63f428 100644 --- a/src/access/BUILD.gn +++ b/src/access/BUILD.gn @@ -43,6 +43,7 @@ source_set("types") { public_deps = [ ":access_config", + "${chip_root}/src/app:app_config", "${chip_root}/src/lib/core", "${chip_root}/src/lib/core:types", ] @@ -76,6 +77,7 @@ static_library("access") { "AccessRestrictionProvider.cpp", "AccessRestrictionProvider.h", ] + public_deps += [ "${chip_root}/src/app/common:cluster-objects" ] } } diff --git a/src/access/ProviderDeviceTypeResolver.h b/src/access/ProviderDeviceTypeResolver.h index 0a215d4a899..aff5b424a69 100644 --- a/src/access/ProviderDeviceTypeResolver.h +++ b/src/access/ProviderDeviceTypeResolver.h @@ -16,6 +16,8 @@ #pragma once #include +#include +#include #include namespace chip { @@ -31,10 +33,11 @@ class DynamicProviderDeviceTypeResolver : public chip::Access::AccessControl::De bool IsDeviceTypeOnEndpoint(chip::DeviceTypeId deviceType, chip::EndpointId endpoint) override { - app::DataModel::Provider * model = mModelGetter(); - for (auto type = model->FirstDeviceType(endpoint); type.has_value(); type = model->NextDeviceType(endpoint, *type)) + app::DataModel::ListBuilder builder; + (void) mModelGetter()->DeviceTypes(endpoint, builder); + for (auto & type : builder.TakeBuffer()) { - if (type->deviceTypeId == deviceType) + if (type.deviceTypeId == deviceType) { return true; } diff --git a/src/app/AttributePathExpandIterator.cpp b/src/app/AttributePathExpandIterator.cpp index 23273c7c58f..7f357164427 100644 --- a/src/app/AttributePathExpandIterator.cpp +++ b/src/app/AttributePathExpandIterator.cpp @@ -17,6 +17,10 @@ #include #include +#include +#include +#include +#include #include #include @@ -26,6 +30,10 @@ using namespace chip::app::DataModel; namespace chip { namespace app { +AttributePathExpandIterator::AttributePathExpandIterator(DataModel::Provider * dataModel, Position & position) : + mDataModelProvider(dataModel), mPosition(position) +{} + bool AttributePathExpandIterator::AdvanceOutputPath() { /// Output path invariants @@ -113,34 +121,61 @@ bool AttributePathExpandIterator::IsValidAttributeId(AttributeId attributeId) break; } + DataModel::AttributeFinder finder(mDataModelProvider); + const ConcreteAttributePath attributePath(mPosition.mOutputPath.mEndpointId, mPosition.mOutputPath.mClusterId, attributeId); - return mDataModelProvider->GetAttributeInfo(attributePath).has_value(); + return finder.Find(attributePath).has_value(); } std::optional AttributePathExpandIterator::NextAttributeId() { if (mPosition.mOutputPath.mAttributeId == kInvalidAttributeId) { - if (mPosition.mAttributePath->mValue.HasWildcardAttributeId()) + // Attribute ID is tied to attribute index. If no attribute id is available yet + // this means the index is invalid. Processing logic in output advance only resets + // attribute ID to invalid when resetting iteration. + mAttributeIndex = kInvalidIndex; + } + + if (mAttributeIndex == kInvalidIndex) + { + // start a new iteration of attributes on the current cluster path. + mAttributes = mDataModelProvider->AttributesIgnoreError(mPosition.mOutputPath); + + if (mPosition.mOutputPath.mAttributeId != kInvalidAttributeId) { - AttributeEntry entry = mDataModelProvider->FirstAttribute(mPosition.mOutputPath); - return entry.IsValid() // - ? entry.path.mAttributeId // - : Clusters::Globals::Attributes::GeneratedCommandList::Id; // + // Position on the correct attribute if we have a start point + mAttributeIndex = 0; + while ((mAttributeIndex < mAttributes.size()) && + (mAttributes[mAttributeIndex].attributeId != mPosition.mOutputPath.mAttributeId)) + { + mAttributeIndex++; + } } + } - // At this point, the attributeID is NOT a wildcard (i.e. it is fixed). - // - // For wildcard expansion, we validate that this is a valid attribute for the given - // cluster on the given endpoint. If not a wildcard expansion, return it as-is. - if (mPosition.mAttributePath->mValue.IsWildcardPath()) + if (mPosition.mOutputPath.mAttributeId == kInvalidAttributeId) + { + if (!mPosition.mAttributePath->mValue.HasWildcardAttributeId()) { - if (!IsValidAttributeId(mPosition.mAttributePath->mValue.mAttributeId)) + // The attributeID is NOT a wildcard (i.e. it is fixed). + // + // For wildcard expansion, we validate that this is a valid attribute for the given + // cluster on the given endpoint. If not a wildcard expansion, return it as-is. + if (mPosition.mAttributePath->mValue.IsWildcardPath()) { - return std::nullopt; + if (!IsValidAttributeId(mPosition.mAttributePath->mValue.mAttributeId)) + { + return std::nullopt; + } } + return mPosition.mAttributePath->mValue.mAttributeId; } - return mPosition.mAttributePath->mValue.mAttributeId; + mAttributeIndex = 0; + } + else + { + mAttributeIndex++; } // Advance the existing attribute id if it can be advanced. @@ -165,10 +200,9 @@ std::optional AttributePathExpandIterator::NextAttributeId() return std::nullopt; } - AttributeEntry entry = mDataModelProvider->NextAttribute(mPosition.mOutputPath); - if (entry.IsValid()) + if (mAttributeIndex < mAttributes.size()) { - return entry.path.mAttributeId; + return mAttributes[mAttributeIndex].attributeId; } // Finished the data model, start with global attributes @@ -178,55 +212,111 @@ std::optional AttributePathExpandIterator::NextAttributeId() std::optional AttributePathExpandIterator::NextClusterId() { - if (mPosition.mOutputPath.mClusterId == kInvalidClusterId) { - if (mPosition.mAttributePath->mValue.HasWildcardClusterId()) + // Cluster ID is tied to cluster index. If no cluster id available yet + // this means index is invalid. Processing logic in output advance only resets + // cluster ID to invalid when resetting iteration. + mClusterIndex = kInvalidIndex; + } + + if (mClusterIndex == kInvalidIndex) + { + // start a new iteration on the current endpoint + mClusters = mDataModelProvider->ServerClustersIgnoreError(mPosition.mOutputPath.mEndpointId); + + if (mPosition.mOutputPath.mClusterId != kInvalidClusterId) { - ClusterEntry entry = mDataModelProvider->FirstServerCluster(mPosition.mOutputPath.mEndpointId); - return entry.IsValid() ? std::make_optional(entry.path.mClusterId) : std::nullopt; + // Position on the correct cluster if we have a start point + mClusterIndex = 0; + while ((mClusterIndex < mClusters.size()) && (mClusters[mClusterIndex].clusterId != mPosition.mOutputPath.mClusterId)) + { + mClusterIndex++; + } } + } + + if (mPosition.mOutputPath.mClusterId == kInvalidClusterId) + { - // At this point, the clusterID is NOT a wildcard (i.e. is fixed). - // - // For wildcard expansion, we validate that this is a valid cluster for the endpoint. - // If non-wildcard expansion, we return as-is. - if (mPosition.mAttributePath->mValue.IsWildcardPath()) + if (!mPosition.mAttributePath->mValue.HasWildcardClusterId()) { - const ConcreteClusterPath clusterPath(mPosition.mOutputPath.mEndpointId, mPosition.mAttributePath->mValue.mClusterId); - if (!mDataModelProvider->GetServerClusterInfo(clusterPath).has_value()) + // The clusterID is NOT a wildcard (i.e. is fixed). + // + // For wildcard expansion, we validate that this is a valid cluster for the endpoint. + // If non-wildcard expansion, we return as-is. + if (mPosition.mAttributePath->mValue.IsWildcardPath()) { - return std::nullopt; + const ClusterId clusterId = mPosition.mAttributePath->mValue.mClusterId; + + bool found = false; + for (auto & entry : mClusters) + { + if (entry.clusterId == clusterId) + { + found = true; + break; + } + } + + if (!found) + { + return std::nullopt; + } } - } - return mPosition.mAttributePath->mValue.mClusterId; + return mPosition.mAttributePath->mValue.mClusterId; + } + mClusterIndex = 0; + } + else + { + mClusterIndex++; } VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardClusterId(), std::nullopt); + VerifyOrReturnValue(mClusterIndex < mClusters.size(), std::nullopt); - ClusterEntry entry = mDataModelProvider->NextServerCluster(mPosition.mOutputPath); - return entry.IsValid() ? std::make_optional(entry.path.mClusterId) : std::nullopt; + return mClusters[mClusterIndex].clusterId; } -std::optional AttributePathExpandIterator::NextEndpointId() +std::optional AttributePathExpandIterator::NextEndpointId() { + if (mEndpointIndex == kInvalidIndex) + { + // index is missing, have to start a new iteration + mEndpoints = mDataModelProvider->EndpointsIgnoreError(); + + if (mPosition.mOutputPath.mEndpointId != kInvalidEndpointId) + { + // Position on the correct endpoint if we have a start point + mEndpointIndex = 0; + while ((mEndpointIndex < mEndpoints.size()) && (mEndpoints[mEndpointIndex].id != mPosition.mOutputPath.mEndpointId)) + { + mEndpointIndex++; + } + } + } + if (mPosition.mOutputPath.mEndpointId == kInvalidEndpointId) { - if (mPosition.mAttributePath->mValue.HasWildcardEndpointId()) + if (!mPosition.mAttributePath->mValue.HasWildcardEndpointId()) { - EndpointEntry ep = mDataModelProvider->FirstEndpoint(); - return (ep.id != kInvalidEndpointId) ? std::make_optional(ep.id) : std::nullopt; + return mPosition.mAttributePath->mValue.mEndpointId; } - return mPosition.mAttributePath->mValue.mEndpointId; + // start from the beginning + mEndpointIndex = 0; + } + else + { + mEndpointIndex++; } - // Expand endpoints only if it is a wildcard on the endpoint specifically. VerifyOrReturnValue(mPosition.mAttributePath->mValue.HasWildcardEndpointId(), std::nullopt); + VerifyOrReturnValue(mEndpointIndex < mEndpoints.size(), std::nullopt); - EndpointEntry ep = mDataModelProvider->NextEndpoint(mPosition.mOutputPath.mEndpointId); - return (ep.id != kInvalidEndpointId) ? std::make_optional(ep.id) : std::nullopt; + return mEndpoints[mEndpointIndex].id; } } // namespace app diff --git a/src/app/AttributePathExpandIterator.h b/src/app/AttributePathExpandIterator.h index 2916a2e3bb7..e5d1f961434 100644 --- a/src/app/AttributePathExpandIterator.h +++ b/src/app/AttributePathExpandIterator.h @@ -19,9 +19,14 @@ #include #include +#include +#include #include #include #include +#include + +#include namespace chip { namespace app { @@ -96,9 +101,7 @@ class AttributePathExpandIterator ConcreteAttributePath mOutputPath; }; - AttributePathExpandIterator(DataModel::Provider * dataModel, Position & position) : - mDataModelProvider(dataModel), mPosition(position) - {} + AttributePathExpandIterator(DataModel::Provider * dataModel, Position & position); // This class may not be copied. A new one should be created when needed and they // should not overlap. @@ -113,9 +116,20 @@ class AttributePathExpandIterator bool Next(ConcreteAttributePath & path); private: + static constexpr size_t kInvalidIndex = std::numeric_limits::max(); + DataModel::Provider * mDataModelProvider; Position & mPosition; + DataModel::ReadOnlyBuffer mEndpoints; // all endpoints + size_t mEndpointIndex = kInvalidIndex; + + DataModel::ReadOnlyBuffer mClusters; // all clusters ON THE CURRENT endpoint + size_t mClusterIndex = kInvalidIndex; + + DataModel::ReadOnlyBuffer mAttributes; // all attributes ON THE CURRENT cluster + size_t mAttributeIndex = kInvalidIndex; + /// Move to the next endpoint/cluster/attribute triplet that is valid given /// the current mOutputPath and mpAttributePath. /// @@ -140,7 +154,7 @@ class AttributePathExpandIterator /// Will start from the beginning if current mOutputPath.mEndpointId is kInvalidEndpointId /// /// Respects path expansion/values in mpAttributePath - std::optional NextEndpointId(); + std::optional NextEndpointId(); /// Checks if the given attributeId is valid for the current mOutputPath(endpoint/cluster) /// diff --git a/src/app/AttributePathParams.h b/src/app/AttributePathParams.h index 52d5bb59721..f69340aa3a7 100644 --- a/src/app/AttributePathParams.h +++ b/src/app/AttributePathParams.h @@ -30,7 +30,7 @@ struct AttributePathParams { AttributePathParams() = default; - explicit AttributePathParams(EndpointId aEndpointId) : + constexpr explicit AttributePathParams(EndpointId aEndpointId) : AttributePathParams(aEndpointId, kInvalidClusterId, kInvalidAttributeId, kInvalidListIndex) {} @@ -38,19 +38,19 @@ struct AttributePathParams // TODO: (Issue #10596) Need to ensure that we do not encode the NodeId over the wire // if it is either not 'set', or is set to a value that matches accessing fabric // on which the interaction is undertaken. - AttributePathParams(EndpointId aEndpointId, ClusterId aClusterId) : + constexpr AttributePathParams(EndpointId aEndpointId, ClusterId aClusterId) : AttributePathParams(aEndpointId, aClusterId, kInvalidAttributeId, kInvalidListIndex) {} - AttributePathParams(EndpointId aEndpointId, ClusterId aClusterId, AttributeId aAttributeId) : + constexpr AttributePathParams(EndpointId aEndpointId, ClusterId aClusterId, AttributeId aAttributeId) : AttributePathParams(aEndpointId, aClusterId, aAttributeId, kInvalidListIndex) {} - AttributePathParams(ClusterId aClusterId, AttributeId aAttributeId) : + constexpr AttributePathParams(ClusterId aClusterId, AttributeId aAttributeId) : AttributePathParams(kInvalidEndpointId, aClusterId, aAttributeId, kInvalidListIndex) {} - AttributePathParams(EndpointId aEndpointId, ClusterId aClusterId, AttributeId aAttributeId, ListIndex aListIndex) : + constexpr AttributePathParams(EndpointId aEndpointId, ClusterId aClusterId, AttributeId aAttributeId, ListIndex aListIndex) : mClusterId(aClusterId), mAttributeId(aAttributeId), mEndpointId(aEndpointId), mListIndex(aListIndex) {} diff --git a/src/app/AttributeValueEncoder.h b/src/app/AttributeValueEncoder.h index 39bbe9cb035..a515164cda5 100644 --- a/src/app/AttributeValueEncoder.h +++ b/src/app/AttributeValueEncoder.h @@ -64,6 +64,34 @@ class AttributeValueEncoder return mAttributeValueEncoder.EncodeListItem(mCheckpoint, aArg); } + // overrides that save flash: no need to care about the extra const + // Without this, we have a usage of: + // chip::ChipError chip::app::AttributeValueEncoder::EncodeListItem + // Overall we tend to have very similar code expand (from nm): + // chip::ChipError chip::app::AttributeValueEncoder::Encode(unsigned char&&) + // chip::ChipError chip::app::AttributeValueEncoder::Encode(unsigned char&) + // chip::ChipError chip::app::AttributeValueEncoder::Encode(unsigned char const&) + // chip::ChipError chip::app::AttributeValueEncoder::Encode(unsigned short const&) + // chip::ChipError chip::app::AttributeValueEncoder::Encode(unsigned long&) + // chip::ChipError chip::app::AttributeValueEncoder::Encode(unsigned short&) + // chip::ChipError chip::app::AttributeValueEncoder::Encode(unsigned long long&) + // chip::ChipError chip::app::AttributeValueEncoder::Encode(unsigned short&&) + // chip::ChipError chip::app::AttributeValueEncoder::Encode(unsigned long long&&) + // that we try to reduce + // + // TODO: + // - we should figure where the extra const override is used + // - we should try to avoid having such footguns. This list template-explosion seems + // dangerous for flash. + // + // This relies on TLV numbers always being encoded as 64-bit value + inline CHIP_ERROR Encode(uint32_t const & aArg) const { return Encode(aArg); } + inline CHIP_ERROR Encode(uint32_t & aArg) const { return Encode(aArg); } + inline CHIP_ERROR Encode(uint16_t const & aArg) const { return Encode(aArg); } + inline CHIP_ERROR Encode(uint16_t & aArg) const { return Encode(aArg); } + inline CHIP_ERROR Encode(uint8_t const & aArg) const { return Encode(aArg); } + inline CHIP_ERROR Encode(uint8_t & aArg) const { return Encode(aArg); } + private: AttributeValueEncoder & mAttributeValueEncoder; // Avoid calling the TLVWriter constructor for every instantiation of diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 3fb0795abcd..642f6ee4b5f 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -219,6 +219,7 @@ static_library("interaction-model") { ":event-reporter", ":paths", ":subscription-info-provider", + "${chip_root}/src/access", "${chip_root}/src/app/MessageDef", "${chip_root}/src/app/data-model-provider", "${chip_root}/src/app/icd/server:icd-server-config", @@ -350,6 +351,7 @@ source_set("status-response") { ":constants", "${chip_root}/src/app/MessageDef", "${chip_root}/src/messaging", + "${chip_root}/src/protocols/interaction_model", ] } @@ -387,6 +389,7 @@ source_set("command-handler-impl") { ":status-response", "${chip_root}/src/access:types", "${chip_root}/src/app/MessageDef", + "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/app/data-model", "${chip_root}/src/app/data-model-provider", "${chip_root}/src/app/util:callbacks", diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index d4afe3347fe..ec999d3cc53 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -33,11 +33,15 @@ #include #include #include +#include #include #include #include +#include +#include #include #include +#include #include #include #include @@ -89,14 +93,13 @@ bool MayHaveAccessibleEventPathForEndpoint(DataModel::Provider * aProvider, Endp aSubjectDescriptor); } - DataModel::ClusterEntry clusterEntry = aProvider->FirstServerCluster(aEventPath.mEndpointId); - while (clusterEntry.IsValid()) + for (auto & cluster : aProvider->ServerClustersIgnoreError(aEventPath.mEndpointId)) { - if (MayHaveAccessibleEventPathForEndpointAndCluster(clusterEntry.path, aEventPath, aSubjectDescriptor)) + if (MayHaveAccessibleEventPathForEndpointAndCluster(ConcreteClusterPath(aEventPath.mEndpointId, cluster.clusterId), + aEventPath, aSubjectDescriptor)) { return true; } - clusterEntry = aProvider->NextServerCluster(clusterEntry.path); } return false; @@ -112,7 +115,7 @@ bool MayHaveAccessibleEventPath(DataModel::Provider * aProvider, const EventPath return MayHaveAccessibleEventPathForEndpoint(aProvider, aEventPath.mEndpointId, aEventPath, subjectDescriptor); } - for (DataModel::EndpointEntry ep = aProvider->FirstEndpoint(); ep.IsValid(); ep = aProvider->NextEndpoint(ep.id)) + for (const DataModel::EndpointEntry & ep : aProvider->EndpointsIgnoreError()) { if (MayHaveAccessibleEventPathForEndpoint(aProvider, ep.id, aEventPath, subjectDescriptor)) { @@ -1563,7 +1566,8 @@ CHIP_ERROR InteractionModelEngine::PushFrontAttributePathList(SingleLinkedListNo bool InteractionModelEngine::IsExistentAttributePath(const ConcreteAttributePath & path) { - return GetDataModelProvider()->GetAttributeInfo(path).has_value(); + DataModel::AttributeFinder finder(mDataModelProvider); + return finder.Find(path).has_value(); } void InteractionModelEngine::RemoveDuplicateConcreteAttributePath(SingleLinkedListNode *& aAttributePaths) @@ -1711,7 +1715,9 @@ void InteractionModelEngine::DispatchCommand(CommandHandlerImpl & apCommandObj, Protocols::InteractionModel::Status InteractionModelEngine::ValidateCommandCanBeDispatched(const DataModel::InvokeRequest & request) { - Status status = CheckCommandExistence(request.path); + DataModel::AcceptedCommandEntry acceptedCommandEntry; + + Status status = CheckCommandExistence(request.path, acceptedCommandEntry); if (status != Status::Success) { @@ -1720,13 +1726,14 @@ Protocols::InteractionModel::Status InteractionModelEngine::ValidateCommandCanBe return status; } - status = CheckCommandAccess(request); + status = CheckCommandAccess(request, acceptedCommandEntry); VerifyOrReturnValue(status == Status::Success, status); - return CheckCommandFlags(request); + return CheckCommandFlags(request, acceptedCommandEntry); } -Protocols::InteractionModel::Status InteractionModelEngine::CheckCommandAccess(const DataModel::InvokeRequest & aRequest) +Protocols::InteractionModel::Status InteractionModelEngine::CheckCommandAccess(const DataModel::InvokeRequest & aRequest, + const DataModel::AcceptedCommandEntry & entry) { if (aRequest.subjectDescriptor == nullptr) { @@ -1737,11 +1744,8 @@ Protocols::InteractionModel::Status InteractionModelEngine::CheckCommandAccess(c .endpoint = aRequest.path.mEndpointId, .requestType = Access::RequestType::kCommandInvokeRequest, .entityId = aRequest.path.mCommandId }; - std::optional commandInfo = mDataModelProvider->GetAcceptedCommandInfo(aRequest.path); - Access::Privilege minimumRequiredPrivilege = - commandInfo.has_value() ? commandInfo->invokePrivilege : Access::Privilege::kOperate; - CHIP_ERROR err = Access::GetAccessControl().Check(*aRequest.subjectDescriptor, requestPath, minimumRequiredPrivilege); + CHIP_ERROR err = Access::GetAccessControl().Check(*aRequest.subjectDescriptor, requestPath, entry.invokePrivilege); if (err != CHIP_NO_ERROR) { if ((err != CHIP_ERROR_ACCESS_DENIED) && (err != CHIP_ERROR_ACCESS_RESTRICTED_BY_ARL)) @@ -1754,14 +1758,11 @@ Protocols::InteractionModel::Status InteractionModelEngine::CheckCommandAccess(c return Status::Success; } -Protocols::InteractionModel::Status InteractionModelEngine::CheckCommandFlags(const DataModel::InvokeRequest & aRequest) +Protocols::InteractionModel::Status InteractionModelEngine::CheckCommandFlags(const DataModel::InvokeRequest & aRequest, + const DataModel::AcceptedCommandEntry & entry) { - std::optional commandInfo = mDataModelProvider->GetAcceptedCommandInfo(aRequest.path); - // This is checked by previous validations, so it should not happen - VerifyOrDie(commandInfo.has_value()); - - const bool commandNeedsTimedInvoke = commandInfo->flags.Has(DataModel::CommandQualityFlags::kTimed); - const bool commandIsFabricScoped = commandInfo->flags.Has(DataModel::CommandQualityFlags::kFabricScoped); + const bool commandNeedsTimedInvoke = entry.flags.Has(DataModel::CommandQualityFlags::kTimed); + const bool commandIsFabricScoped = entry.flags.Has(DataModel::CommandQualityFlags::kFabricScoped); if (commandNeedsTimedInvoke && !aRequest.invokeFlags.Has(DataModel::InvokeFlags::kTimed)) { @@ -1784,26 +1785,36 @@ Protocols::InteractionModel::Status InteractionModelEngine::CheckCommandFlags(co return Status::Success; } -Protocols::InteractionModel::Status InteractionModelEngine::CheckCommandExistence(const ConcreteCommandPath & aCommandPath) +Protocols::InteractionModel::Status InteractionModelEngine::CheckCommandExistence(const ConcreteCommandPath & aCommandPath, + DataModel::AcceptedCommandEntry & entry) { auto provider = GetDataModelProvider(); - if (provider->GetAcceptedCommandInfo(aCommandPath).has_value()) + + DataModel::ListBuilder acceptedCommands; + (void) provider->AcceptedCommands(aCommandPath, acceptedCommands); + for (auto & existing : acceptedCommands.TakeBuffer()) { - return Protocols::InteractionModel::Status::Success; + if (existing.commandId == aCommandPath.mCommandId) + { + entry = existing; + return Protocols::InteractionModel::Status::Success; + } } - // We failed, figure out why ... - // - if (provider->GetServerClusterInfo(aCommandPath).has_value()) { - return Protocols::InteractionModel::Status::UnsupportedCommand; // cluster exists, so command is invalid + DataModel::ServerClusterFinder finder(provider); + if (finder.Find(aCommandPath).has_value()) + { + // cluster exists, so command is invalid + return Protocols::InteractionModel::Status::UnsupportedCommand; + } } // At this point either cluster or endpoint does not exist. If we find the endpoint, then the cluster // is invalid - for (DataModel::EndpointEntry ep = provider->FirstEndpoint(); ep.IsValid(); ep = provider->NextEndpoint(ep.id)) { - if (ep.id == aCommandPath.mEndpointId) + DataModel::EndpointFinder finder(provider); + if (finder.Find(aCommandPath.mEndpointId)) { // endpoint exists, so cluster is invalid return Protocols::InteractionModel::Status::UnsupportedCluster; diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index c5a4a0811d2..362cdecab99 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -614,9 +615,12 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, void ShutdownMatchingSubscriptions(const Optional & aFabricIndex = NullOptional, const Optional & aPeerNodeId = NullOptional); - Status CheckCommandExistence(const ConcreteCommandPath & aCommandPath); - Status CheckCommandAccess(const DataModel::InvokeRequest & aRequest); - Status CheckCommandFlags(const DataModel::InvokeRequest & aRequest); + /** + * Validates that the command exists and on success returns the data for the command in `entry`. + */ + Status CheckCommandExistence(const ConcreteCommandPath & aCommandPath, DataModel::AcceptedCommandEntry & entry); + Status CheckCommandAccess(const DataModel::InvokeRequest & aRequest, const DataModel::AcceptedCommandEntry & entry); + Status CheckCommandFlags(const DataModel::InvokeRequest & aRequest, const DataModel::AcceptedCommandEntry & entry); /** * Check if the given attribute path is a valid path in the data model provider. diff --git a/src/app/MessageDef/BUILD.gn b/src/app/MessageDef/BUILD.gn index bf02f7a73d2..1c581961ebc 100644 --- a/src/app/MessageDef/BUILD.gn +++ b/src/app/MessageDef/BUILD.gn @@ -116,6 +116,7 @@ source_set("MessageDef") { "${chip_root}/src/app:constants", "${chip_root}/src/app:events", "${chip_root}/src/app:paths", + "${chip_root}/src/app/data-model", "${chip_root}/src/lib/core", "${chip_root}/src/lib/support", "${chip_root}/src/protocols/interaction_model", diff --git a/src/app/WriteHandler.cpp b/src/app/WriteHandler.cpp index 1e129ad0af9..9847641b61d 100644 --- a/src/app/WriteHandler.cpp +++ b/src/app/WriteHandler.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -110,7 +111,9 @@ std::optional WriteHandler::IsListAttributePath(const ConcreteAttributePat return std::nullopt; } - auto info = mDataModelProvider->GetAttributeInfo(path); + DataModel::AttributeFinder finder(mDataModelProvider); + std::optional info = finder.Find(path); + if (!info.has_value()) { return std::nullopt; diff --git a/src/app/clusters/descriptor/descriptor.cpp b/src/app/clusters/descriptor/descriptor.cpp index 7d8854bca1f..35ddcabfd42 100644 --- a/src/app/clusters/descriptor/descriptor.cpp +++ b/src/app/clusters/descriptor/descriptor.cpp @@ -15,10 +15,7 @@ * limitations under the License. */ -/**************************************************************************** - * @file - * @brief Implementation for the Descriptor Server Cluster - ***************************************************************************/ +#include "descriptor.h" #include #include @@ -26,14 +23,16 @@ #include #include #include +#include #include +#include #include #include +#include +#include #include #include -#include "descriptor.h" - using namespace chip; using namespace chip::app; using namespace chip::app::Clusters; @@ -42,6 +41,38 @@ using namespace chip::app::Clusters::Descriptor::Attributes; namespace { +/// Figures out if `childId` is a descendant of `parentId` given some specific endpoint entries +bool IsDescendantOf(const DataModel::EndpointEntry * __restrict__ childEndpoint, const EndpointId parentId, + Span allEndpoints) +{ + // NOTE: this is not very efficient as we loop through all endpoints for each parent search + // however endpoint depth should not be as large. + while (true) + { + + VerifyOrReturnValue(childEndpoint != nullptr, false); + VerifyOrReturnValue(childEndpoint->parentId != parentId, true); + + // Parent endpoint id 0 is never here: EndpointEntry::parentId uses + // kInvalidEndpointId to reference no explicit endpoint. See `EndpointEntry` + // comments. + VerifyOrReturnValue(childEndpoint->parentId != kInvalidEndpointId, false); + + const auto lookupId = childEndpoint->parentId; + childEndpoint = nullptr; // we will look it up again + + // find the requested value in the array to get its parent + for (const auto & ep : allEndpoints) + { + if (ep.id == lookupId) + { + childEndpoint = &ep; + break; + } + } + } +} + class DescriptorAttrAccess : public AttributeAccessInterface { public: @@ -54,7 +85,8 @@ class DescriptorAttrAccess : public AttributeAccessInterface CHIP_ERROR ReadTagListAttribute(EndpointId endpoint, AttributeValueEncoder & aEncoder); CHIP_ERROR ReadPartsAttribute(EndpointId endpoint, AttributeValueEncoder & aEncoder); CHIP_ERROR ReadDeviceAttribute(EndpointId endpoint, AttributeValueEncoder & aEncoder); - CHIP_ERROR ReadClientServerAttribute(EndpointId endpoint, AttributeValueEncoder & aEncoder, bool server); + CHIP_ERROR ReadClientClusters(EndpointId endpoint, AttributeValueEncoder & aEncoder); + CHIP_ERROR ReadServerClusters(EndpointId endpoint, AttributeValueEncoder & aEncoder); CHIP_ERROR ReadClusterRevision(EndpointId endpoint, AttributeValueEncoder & aEncoder); CHIP_ERROR ReadFeatureMap(EndpointId endpoint, AttributeValueEncoder & aEncoder); }; @@ -74,12 +106,13 @@ CHIP_ERROR DescriptorAttrAccess::ReadFeatureMap(EndpointId endpoint, AttributeVa CHIP_ERROR DescriptorAttrAccess::ReadTagListAttribute(EndpointId endpoint, AttributeValueEncoder & aEncoder) { - return aEncoder.EncodeList([&endpoint](const auto & encoder) -> CHIP_ERROR { - auto tag = InteractionModelEngine::GetInstance()->GetDataModelProvider()->GetFirstSemanticTag(endpoint); - while (tag.has_value()) + DataModel::ListBuilder semanticTagsList; + ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->GetDataModelProvider()->SemanticTags(endpoint, semanticTagsList)); + + return aEncoder.EncodeList([&semanticTagsList](const auto & encoder) -> CHIP_ERROR { + for (const auto & tag : semanticTagsList.TakeBuffer()) { - ReturnErrorOnFailure(encoder.Encode(tag.value())); - tag = InteractionModelEngine::GetInstance()->GetDataModelProvider()->GetNextSemanticTag(endpoint, tag.value()); + ReturnErrorOnFailure(encoder.Encode(tag)); } return CHIP_NO_ERROR; }); @@ -87,84 +120,89 @@ CHIP_ERROR DescriptorAttrAccess::ReadTagListAttribute(EndpointId endpoint, Attri CHIP_ERROR DescriptorAttrAccess::ReadPartsAttribute(EndpointId endpoint, AttributeValueEncoder & aEncoder) { - CHIP_ERROR err = CHIP_NO_ERROR; - - auto endpointInfo = InteractionModelEngine::GetInstance()->GetDataModelProvider()->GetEndpointInfo(endpoint); + DataModel::ListBuilder endpointsList; + ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->GetDataModelProvider()->Endpoints(endpointsList)); + auto endpoints = endpointsList.TakeBuffer(); if (endpoint == 0x00) { - err = aEncoder.EncodeList([](const auto & encoder) -> CHIP_ERROR { - auto endpointEntry = InteractionModelEngine::GetInstance()->GetDataModelProvider()->FirstEndpoint(); - while (endpointEntry.IsValid()) + return aEncoder.EncodeList([&endpoints](const auto & encoder) -> CHIP_ERROR { + for (const auto & ep : endpoints) { - if (endpointEntry.id != 0) + if (ep.id == 0) { - ReturnErrorOnFailure(encoder.Encode(endpointEntry.id)); + continue; } - endpointEntry = InteractionModelEngine::GetInstance()->GetDataModelProvider()->NextEndpoint(endpointEntry.id); + ReturnErrorOnFailure(encoder.Encode(ep.id)); } return CHIP_NO_ERROR; }); } - else if (endpointInfo.has_value() && endpointInfo->compositionPattern == DataModel::EndpointCompositionPattern::kFullFamily) + + // find the given endpoint + unsigned idx = 0; + while (idx < endpoints.size()) + { + if (endpoints[idx].id == endpoint) + { + break; + } + idx++; + } + if (idx >= endpoints.size()) { - err = aEncoder.EncodeList([endpoint](const auto & encoder) -> CHIP_ERROR { - auto endpointEntry = InteractionModelEngine::GetInstance()->GetDataModelProvider()->FirstEndpoint(); - while (endpointEntry.IsValid()) + // not found + return CHIP_ERROR_NOT_FOUND; + } + + auto & endpointInfo = endpoints[idx]; + + switch (endpointInfo.compositionPattern) + { + case DataModel::EndpointCompositionPattern::kFullFamily: + // encodes ALL endpoints that have the specified endpoint as a descendant + return aEncoder.EncodeList([&endpoints, endpoint](const auto & encoder) -> CHIP_ERROR { + for (const auto & ep : endpoints) { - EndpointId parentEndpointId = endpointEntry.info.parentId; - while (parentEndpointId != chip::kInvalidEndpointId) + if (IsDescendantOf(&ep, endpoint, endpoints)) { - if (parentEndpointId == endpoint) - { - ReturnErrorOnFailure(encoder.Encode(endpointEntry.id)); - break; - } - auto parentEndpointInfo = - InteractionModelEngine::GetInstance()->GetDataModelProvider()->GetEndpointInfo(parentEndpointId); - if (!parentEndpointInfo.has_value()) - { - break; - } - parentEndpointId = parentEndpointInfo->parentId; + ReturnErrorOnFailure(encoder.Encode(ep.id)); } - endpointEntry = InteractionModelEngine::GetInstance()->GetDataModelProvider()->NextEndpoint(endpointEntry.id); } - return CHIP_NO_ERROR; }); - } - else if (endpointInfo.has_value() && endpointInfo->compositionPattern == DataModel::EndpointCompositionPattern::kTree) - { - err = aEncoder.EncodeList([endpoint](const auto & encoder) -> CHIP_ERROR { - auto endpointEntry = InteractionModelEngine::GetInstance()->GetDataModelProvider()->FirstEndpoint(); - while (endpointEntry.IsValid()) + + case DataModel::EndpointCompositionPattern::kTree: + return aEncoder.EncodeList([&endpoints, endpoint](const auto & encoder) -> CHIP_ERROR { + for (const auto & ep : endpoints) { - if (endpointEntry.info.parentId == endpoint) + if (ep.parentId != endpoint) { - ReturnErrorOnFailure(encoder.Encode(endpointEntry.id)); + continue; } - endpointEntry = InteractionModelEngine::GetInstance()->GetDataModelProvider()->NextEndpoint(endpointEntry.id); + ReturnErrorOnFailure(encoder.Encode(ep.id)); } return CHIP_NO_ERROR; }); } - - return err; + // not actually reachable and compiler will validate we + // handle all switch cases above + return CHIP_NO_ERROR; } CHIP_ERROR DescriptorAttrAccess::ReadDeviceAttribute(EndpointId endpoint, AttributeValueEncoder & aEncoder) { - CHIP_ERROR err = aEncoder.EncodeList([&endpoint](const auto & encoder) -> CHIP_ERROR { - Descriptor::Structs::DeviceTypeStruct::Type deviceStruct; + DataModel::ListBuilder deviceTypesList; + ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->GetDataModelProvider()->DeviceTypes(endpoint, deviceTypesList)); - auto deviceType = InteractionModelEngine::GetInstance()->GetDataModelProvider()->FirstDeviceType(endpoint); + auto deviceTypes = deviceTypesList.TakeBuffer(); - while (deviceType.has_value()) + CHIP_ERROR err = aEncoder.EncodeList([&deviceTypes](const auto & encoder) -> CHIP_ERROR { + Descriptor::Structs::DeviceTypeStruct::Type deviceStruct; + for (const auto & type : deviceTypes) { - deviceStruct.deviceType = deviceType->deviceTypeId; - deviceStruct.revision = deviceType->deviceTypeRevision; + deviceStruct.deviceType = type.deviceTypeId; + deviceStruct.revision = type.deviceTypeRevision; ReturnErrorOnFailure(encoder.Encode(deviceStruct)); - deviceType = InteractionModelEngine::GetInstance()->GetDataModelProvider()->NextDeviceType(endpoint, *deviceType); } return CHIP_NO_ERROR; @@ -173,33 +211,30 @@ CHIP_ERROR DescriptorAttrAccess::ReadDeviceAttribute(EndpointId endpoint, Attrib return err; } -CHIP_ERROR DescriptorAttrAccess::ReadClientServerAttribute(EndpointId endpoint, AttributeValueEncoder & aEncoder, bool server) +CHIP_ERROR DescriptorAttrAccess::ReadServerClusters(EndpointId endpoint, AttributeValueEncoder & aEncoder) { - CHIP_ERROR err = aEncoder.EncodeList([&endpoint, server](const auto & encoder) -> CHIP_ERROR { - if (server) + DataModel::ListBuilder builder; + ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->GetDataModelProvider()->ServerClusters(endpoint, builder)); + return aEncoder.EncodeList([&builder](const auto & encoder) -> CHIP_ERROR { + for (const auto & cluster : builder.TakeBuffer()) { - auto clusterEntry = InteractionModelEngine::GetInstance()->GetDataModelProvider()->FirstServerCluster(endpoint); - while (clusterEntry.IsValid()) - { - ReturnErrorOnFailure(encoder.Encode(clusterEntry.path.mClusterId)); - clusterEntry = InteractionModelEngine::GetInstance()->GetDataModelProvider()->NextServerCluster(clusterEntry.path); - } + ReturnErrorOnFailure(encoder.Encode(cluster.clusterId)); } - else + return CHIP_NO_ERROR; + }); +} + +CHIP_ERROR DescriptorAttrAccess::ReadClientClusters(EndpointId endpoint, AttributeValueEncoder & aEncoder) +{ + DataModel::ListBuilder clusterIdList; + ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->GetDataModelProvider()->ClientClusters(endpoint, clusterIdList)); + return aEncoder.EncodeList([&clusterIdList](const auto & encoder) -> CHIP_ERROR { + for (const auto & id : clusterIdList.TakeBuffer()) { - ConcreteClusterPath clusterPath = - InteractionModelEngine::GetInstance()->GetDataModelProvider()->FirstClientCluster(endpoint); - while (clusterPath.HasValidIds()) - { - ReturnErrorOnFailure(encoder.Encode(clusterPath.mClusterId)); - clusterPath = InteractionModelEngine::GetInstance()->GetDataModelProvider()->NextClientCluster(clusterPath); - } + ReturnErrorOnFailure(encoder.Encode(id)); } - return CHIP_NO_ERROR; }); - - return err; } CHIP_ERROR DescriptorAttrAccess::ReadClusterRevision(EndpointId endpoint, AttributeValueEncoder & aEncoder) @@ -219,10 +254,10 @@ CHIP_ERROR DescriptorAttrAccess::Read(const ConcreteReadAttributePath & aPath, A return ReadDeviceAttribute(aPath.mEndpointId, aEncoder); } case ServerList::Id: { - return ReadClientServerAttribute(aPath.mEndpointId, aEncoder, true); + return ReadServerClusters(aPath.mEndpointId, aEncoder); } case ClientList::Id: { - return ReadClientServerAttribute(aPath.mEndpointId, aEncoder, false); + return ReadClientClusters(aPath.mEndpointId, aEncoder); } case PartsList::Id: { return ReadPartsAttribute(aPath.mEndpointId, aEncoder); @@ -242,6 +277,7 @@ CHIP_ERROR DescriptorAttrAccess::Read(const ConcreteReadAttributePath & aPath, A } return CHIP_NO_ERROR; } + } // anonymous namespace void MatterDescriptorPluginServerInitCallback() diff --git a/src/app/clusters/descriptor/descriptor.h b/src/app/clusters/descriptor/descriptor.h index 8dd4ea745fc..c116a406040 100644 --- a/src/app/clusters/descriptor/descriptor.h +++ b/src/app/clusters/descriptor/descriptor.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + namespace chip { namespace app { namespace Clusters { diff --git a/src/app/clusters/microwave-oven-control-server/microwave-oven-control-server.cpp b/src/app/clusters/microwave-oven-control-server/microwave-oven-control-server.cpp index 75b7e7df182..5d823d0cafe 100644 --- a/src/app/clusters/microwave-oven-control-server/microwave-oven-control-server.cpp +++ b/src/app/clusters/microwave-oven-control-server/microwave-oven-control-server.cpp @@ -16,12 +16,15 @@ * */ +#include "app/data-model-provider/MetadataList.h" #include #include #include +#include #include #include #include +#include #include #include @@ -247,10 +250,18 @@ void Instance::HandleSetCookingParameters(HandlerContext & ctx, const Commands:: if (startAfterSetting.HasValue()) { - ConcreteCommandPath commandPath(mEndpointId, OperationalState::Id, OperationalState::Commands::Start::Id); + + DataModel::ListBuilder acceptedCommandsList; + + InteractionModelEngine::GetInstance()->GetDataModelProvider()->AcceptedCommands( + ConcreteClusterPath(mEndpointId, OperationalState::Id), acceptedCommandsList); + auto acceptedCommands = acceptedCommandsList.TakeBuffer(); bool commandExists = - InteractionModelEngine::GetInstance()->GetDataModelProvider()->GetAcceptedCommandInfo(commandPath).has_value(); + std::find_if(acceptedCommands.begin(), acceptedCommands.end(), [](const DataModel::AcceptedCommandEntry & entry) { + return entry.commandId == OperationalState::Commands::Start::Id; + }) != acceptedCommands.end(); + VerifyOrExit( commandExists, status = Status::InvalidCommand; ChipLogError( Zcl, diff --git a/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.cpp b/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.cpp index 90fc1af4f5a..6c4b0a4cf61 100644 --- a/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.cpp +++ b/src/app/clusters/wifi-network-diagnostics-server/wifi-network-diagnostics-server.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -40,11 +42,15 @@ using chip::DeviceLayer::GetDiagnosticDataProvider; namespace { -class WiFiDiagosticsAttrAccess : public AttributeAccessInterface +class WiFiDiagosticsGlobalInstance : public AttributeAccessInterface, public CommandHandlerInterface { public: // Register for the WiFiNetworkDiagnostics cluster on all endpoints. - WiFiDiagosticsAttrAccess() : AttributeAccessInterface(Optional::Missing(), WiFiNetworkDiagnostics::Id) {} + WiFiDiagosticsGlobalInstance(DiagnosticDataProvider & diagnosticProvider) : + AttributeAccessInterface(Optional::Missing(), WiFiNetworkDiagnostics::Id), + CommandHandlerInterface(Optional::Missing(), WiFiNetworkDiagnostics::Id), + mDiagnosticProvider(diagnosticProvider) + {} CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; @@ -57,14 +63,20 @@ class WiFiDiagosticsAttrAccess : public AttributeAccessInterface CHIP_ERROR ReadWiFiVersion(AttributeValueEncoder & aEncoder); CHIP_ERROR ReadChannelNumber(AttributeValueEncoder & aEncoder); CHIP_ERROR ReadWiFiRssi(AttributeValueEncoder & aEncoder); + + void InvokeCommand(HandlerContext & ctx) override; + + void HandleResetCounts(HandlerContext & ctx, const Commands::ResetCounts::DecodableType & commandData); + + DiagnosticDataProvider & mDiagnosticProvider; }; template -CHIP_ERROR WiFiDiagosticsAttrAccess::ReadIfSupported(CHIP_ERROR (DiagnosticDataProvider::*getter)(T &), Type & data, - AttributeValueEncoder & aEncoder) +CHIP_ERROR WiFiDiagosticsGlobalInstance::ReadIfSupported(CHIP_ERROR (DiagnosticDataProvider::*getter)(T &), Type & data, + AttributeValueEncoder & aEncoder) { T value; - CHIP_ERROR err = (DeviceLayer::GetDiagnosticDataProvider().*getter)(value); + CHIP_ERROR err = (mDiagnosticProvider.*getter)(value); if (err == CHIP_NO_ERROR) { @@ -78,13 +90,13 @@ CHIP_ERROR WiFiDiagosticsAttrAccess::ReadIfSupported(CHIP_ERROR (DiagnosticDataP return aEncoder.Encode(data); } -CHIP_ERROR WiFiDiagosticsAttrAccess::ReadWiFiBssId(AttributeValueEncoder & aEncoder) +CHIP_ERROR WiFiDiagosticsGlobalInstance::ReadWiFiBssId(AttributeValueEncoder & aEncoder) { Attributes::Bssid::TypeInfo::Type bssid; uint8_t bssidBytes[chip::DeviceLayer::kMaxHardwareAddrSize]; MutableByteSpan bssidSpan(bssidBytes); - if (DeviceLayer::GetDiagnosticDataProvider().GetWiFiBssId(bssidSpan) == CHIP_NO_ERROR) + if (mDiagnosticProvider.GetWiFiBssId(bssidSpan) == CHIP_NO_ERROR) { if (!bssidSpan.empty()) { @@ -101,12 +113,12 @@ CHIP_ERROR WiFiDiagosticsAttrAccess::ReadWiFiBssId(AttributeValueEncoder & aEnco return aEncoder.Encode(bssid); } -CHIP_ERROR WiFiDiagosticsAttrAccess::ReadSecurityType(AttributeValueEncoder & aEncoder) +CHIP_ERROR WiFiDiagosticsGlobalInstance::ReadSecurityType(AttributeValueEncoder & aEncoder) { Attributes::SecurityType::TypeInfo::Type securityType; SecurityTypeEnum value = SecurityTypeEnum::kUnspecified; - if (DeviceLayer::GetDiagnosticDataProvider().GetWiFiSecurityType(value) == CHIP_NO_ERROR) + if (mDiagnosticProvider.GetWiFiSecurityType(value) == CHIP_NO_ERROR) { securityType.SetNonNull(value); ChipLogProgress(Zcl, "The current type of Wi-Fi security used: %d", to_underlying(value)); @@ -119,12 +131,12 @@ CHIP_ERROR WiFiDiagosticsAttrAccess::ReadSecurityType(AttributeValueEncoder & aE return aEncoder.Encode(securityType); } -CHIP_ERROR WiFiDiagosticsAttrAccess::ReadWiFiVersion(AttributeValueEncoder & aEncoder) +CHIP_ERROR WiFiDiagosticsGlobalInstance::ReadWiFiVersion(AttributeValueEncoder & aEncoder) { Attributes::WiFiVersion::TypeInfo::Type version; WiFiVersionEnum value = WiFiVersionEnum::kUnknownEnumValue; - if (DeviceLayer::GetDiagnosticDataProvider().GetWiFiVersion(value) == CHIP_NO_ERROR) + if (mDiagnosticProvider.GetWiFiVersion(value) == CHIP_NO_ERROR) { version.SetNonNull(value); ChipLogProgress(Zcl, "The current 802.11 standard version in use by the Node: %d", to_underlying(value)); @@ -137,12 +149,12 @@ CHIP_ERROR WiFiDiagosticsAttrAccess::ReadWiFiVersion(AttributeValueEncoder & aEn return aEncoder.Encode(version); } -CHIP_ERROR WiFiDiagosticsAttrAccess::ReadChannelNumber(AttributeValueEncoder & aEncoder) +CHIP_ERROR WiFiDiagosticsGlobalInstance::ReadChannelNumber(AttributeValueEncoder & aEncoder) { Attributes::ChannelNumber::TypeInfo::Type channelNumber; uint16_t value = 0; - if (DeviceLayer::GetDiagnosticDataProvider().GetWiFiChannelNumber(value) == CHIP_NO_ERROR) + if (mDiagnosticProvider.GetWiFiChannelNumber(value) == CHIP_NO_ERROR) { channelNumber.SetNonNull(value); ChipLogProgress(Zcl, "The channel that Wi-Fi communication is currently operating on is: %d", value); @@ -155,12 +167,12 @@ CHIP_ERROR WiFiDiagosticsAttrAccess::ReadChannelNumber(AttributeValueEncoder & a return aEncoder.Encode(channelNumber); } -CHIP_ERROR WiFiDiagosticsAttrAccess::ReadWiFiRssi(AttributeValueEncoder & aEncoder) +CHIP_ERROR WiFiDiagosticsGlobalInstance::ReadWiFiRssi(AttributeValueEncoder & aEncoder) { Attributes::Rssi::TypeInfo::Type rssi; int8_t value = 0; - if (DeviceLayer::GetDiagnosticDataProvider().GetWiFiRssi(value) == CHIP_NO_ERROR) + if (mDiagnosticProvider.GetWiFiRssi(value) == CHIP_NO_ERROR) { rssi.SetNonNull(value); ChipLogProgress(Zcl, "The current RSSI of the Node’s Wi-Fi radio in dB: %d", value); @@ -174,9 +186,7 @@ CHIP_ERROR WiFiDiagosticsAttrAccess::ReadWiFiRssi(AttributeValueEncoder & aEncod return aEncoder.Encode(rssi); } -WiFiDiagosticsAttrAccess gAttrAccess; - -CHIP_ERROR WiFiDiagosticsAttrAccess::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +CHIP_ERROR WiFiDiagosticsGlobalInstance::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) { if (aPath.mClusterId != WiFiNetworkDiagnostics::Id) { @@ -240,6 +250,25 @@ CHIP_ERROR WiFiDiagosticsAttrAccess::Read(const ConcreteReadAttributePath & aPat return CHIP_NO_ERROR; } +void WiFiDiagosticsGlobalInstance::InvokeCommand(HandlerContext & handlerContext) +{ + switch (handlerContext.mRequestPath.mCommandId) + { + case Commands::ResetCounts::Id: + CommandHandlerInterface::HandleCommand( + handlerContext, [this](HandlerContext & ctx, const auto & commandData) { HandleResetCounts(ctx, commandData); }); + break; + } +} + +void WiFiDiagosticsGlobalInstance::HandleResetCounts(HandlerContext & ctx, const Commands::ResetCounts::DecodableType & commandData) +{ + mDiagnosticProvider.ResetWiFiNetworkDiagnosticsCounts(); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::Success); +} + +WiFiDiagosticsGlobalInstance gWiFiDiagosticsInstance(DeviceLayer::GetDiagnosticDataProvider()); + } // anonymous namespace namespace chip { @@ -316,18 +345,10 @@ void WiFiDiagnosticsServer::OnConnectionStatusChanged(uint8_t connectionStatus) } // namespace app } // namespace chip -bool emberAfWiFiNetworkDiagnosticsClusterResetCountsCallback(app::CommandHandler * commandObj, - const app::ConcreteCommandPath & commandPath, - const Commands::ResetCounts::DecodableType & commandData) -{ - DeviceLayer::GetDiagnosticDataProvider().ResetWiFiNetworkDiagnosticsCounts(); - commandObj->AddStatus(commandPath, Protocols::InteractionModel::Status::Success); - - return true; -} - void MatterWiFiNetworkDiagnosticsPluginServerInitCallback() { - AttributeAccessInterfaceRegistry::Instance().Register(&gAttrAccess); + AttributeAccessInterfaceRegistry::Instance().Register(&gWiFiDiagosticsInstance); + CommandHandlerInterfaceRegistry::Instance().RegisterCommandHandler(&gWiFiDiagosticsInstance); + GetDiagnosticDataProvider().SetWiFiDiagnosticsDelegate(&WiFiDiagnosticsServer::Instance()); } diff --git a/src/app/common/templates/config-data.yaml b/src/app/common/templates/config-data.yaml index 72e548842d7..38f826c9ee6 100644 --- a/src/app/common/templates/config-data.yaml +++ b/src/app/common/templates/config-data.yaml @@ -52,6 +52,7 @@ CommandHandlerInterfaceOnlyClusters: - General Commissioning - General Diagnostics - Software Diagnostics + - Wi-Fi Network Diagnostics # We need a more configurable way of deciding which clusters have which init functions.... # See https://github.com/project-chip/connectedhomeip/issues/4369 diff --git a/src/app/data-model-provider/BUILD.gn b/src/app/data-model-provider/BUILD.gn index 40f34db127d..5804b0acf59 100644 --- a/src/app/data-model-provider/BUILD.gn +++ b/src/app/data-model-provider/BUILD.gn @@ -21,6 +21,10 @@ source_set("data-model-provider") { "ActionReturnStatus.h", "Context.h", "EventsGenerator.h", + "MetadataList.cpp", + "MetadataList.h", + "MetadataLookup.cpp", + "MetadataLookup.h", "MetadataTypes.cpp", "MetadataTypes.h", "OperationTypes.h", @@ -35,6 +39,7 @@ source_set("data-model-provider") { "${chip_root}/src/app:events", "${chip_root}/src/app:paths", "${chip_root}/src/app/MessageDef", + "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/app/data-model", "${chip_root}/src/lib/core", "${chip_root}/src/lib/core:error", diff --git a/src/app/data-model-provider/MetadataList.cpp b/src/app/data-model-provider/MetadataList.cpp new file mode 100644 index 00000000000..564ccdf53f9 --- /dev/null +++ b/src/app/data-model-provider/MetadataList.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2024 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 + +namespace chip { +namespace app { +namespace DataModel { +namespace detail { + +GenericAppendOnlyBuffer::~GenericAppendOnlyBuffer() +{ + if (mBufferIsAllocated && (mBuffer != nullptr)) + { + Platform::MemoryFree(mBuffer); + } +} + +GenericAppendOnlyBuffer::GenericAppendOnlyBuffer(GenericAppendOnlyBuffer && other) : mElementSize(other.mElementSize) +{ + // take over the data + mBuffer = other.mBuffer; + mElementCount = other.mElementCount; + mCapacity = other.mCapacity; + mBufferIsAllocated = other.mBufferIsAllocated; + + // clear other + other.mBuffer = nullptr; + other.mElementCount = 0; + other.mCapacity = 0; + other.mBufferIsAllocated = false; +} + +GenericAppendOnlyBuffer & GenericAppendOnlyBuffer::operator=(GenericAppendOnlyBuffer && other) +{ + VerifyOrDie(mElementSize == other.mElementSize); + + if (mBufferIsAllocated && (mBuffer != nullptr)) + { + Platform::Impl::PlatformMemoryManagement::MemoryFree(mBuffer); + } + + // take over the data + mBuffer = other.mBuffer; + mElementCount = other.mElementCount; + mCapacity = other.mCapacity; + mBufferIsAllocated = other.mBufferIsAllocated; + + // clear other + other.mBuffer = nullptr; + other.mElementCount = 0; + other.mCapacity = 0; + other.mBufferIsAllocated = false; + + return *this; +} + +CHIP_ERROR GenericAppendOnlyBuffer::EnsureAppendCapacity(size_t numElements) +{ + if (mCapacity >= mElementCount + numElements) + { + // Sufficient capacity already exists + return CHIP_NO_ERROR; + } + + if (mBuffer == nullptr) + { + mBuffer = static_cast(Platform::MemoryCalloc(numElements, mElementSize)); + mCapacity = numElements; + mBufferIsAllocated = true; + return mBuffer != nullptr ? CHIP_NO_ERROR : CHIP_ERROR_NO_MEMORY; + } + + // we already have the data in buffer. we have two choices: + // - allocated buffer needs to be extended + // - re-used const buffer needs to be copied over + if (mBufferIsAllocated) + { + auto new_buffer = static_cast(Platform::MemoryRealloc(mBuffer, (mElementCount + numElements) * mElementSize)); + VerifyOrReturnError(new_buffer != nullptr, CHIP_ERROR_NO_MEMORY); + mBuffer = new_buffer; + } + else + { + // this is NOT an allocated buffer, but it should become one + auto new_buffer = static_cast(Platform::MemoryCalloc(mElementCount + numElements, mElementSize)); + mBufferIsAllocated = true; + VerifyOrReturnError(new_buffer != nullptr, CHIP_ERROR_NO_MEMORY); + memcpy(new_buffer, mBuffer, mElementCount * mElementSize); + mBuffer = new_buffer; + } + mCapacity = mElementCount + numElements; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR GenericAppendOnlyBuffer::AppendSingleElementRaw(const void * buffer) +{ + VerifyOrReturnError(mElementCount < mCapacity, CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(mBuffer + mElementCount * mElementSize, buffer, mElementSize); + mElementCount++; + return CHIP_NO_ERROR; +} + +CHIP_ERROR GenericAppendOnlyBuffer::AppendElementArrayRaw(const void * buffer, size_t numElements) +{ + ReturnErrorOnFailure(EnsureAppendCapacity(numElements)); + + memcpy(mBuffer + mElementCount * mElementSize, buffer, numElements * mElementSize); + mElementCount += numElements; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR GenericAppendOnlyBuffer::ReferenceExistingElementArrayRaw(const void * buffer, size_t numElements) +{ + if (mBuffer == nullptr) + { + // we can NEVER append with 0 capacity, so const cast is safe + mBuffer = const_cast(static_cast(buffer)); + mElementCount = numElements; + // The assertions below are because we know the buffer is null/not allocated yet + VerifyOrDie(mCapacity == 0); + VerifyOrDie(!mBufferIsAllocated); + return CHIP_NO_ERROR; + } + + return AppendElementArrayRaw(buffer, numElements); +} + +void GenericAppendOnlyBuffer::ReleaseBuffer(void *& buffer, size_t & size, bool & allocated) +{ + buffer = mBuffer; + size = mElementCount; + allocated = mBufferIsAllocated; + + // we release the ownership + mBuffer = nullptr; + mCapacity = 0; + mElementCount = 0; + mBufferIsAllocated = false; +} + +ScopedBuffer::~ScopedBuffer() +{ + if (mBuffer != nullptr) + { + Platform::MemoryFree(mBuffer); + } +} + +ScopedBuffer & ScopedBuffer::operator=(ScopedBuffer && other) +{ + if (mBuffer != nullptr) + { + Platform::MemoryFree(mBuffer); + } + + mBuffer = other.mBuffer; + other.mBuffer = nullptr; + return *this; +} + +} // namespace detail +} // namespace DataModel +} // namespace app +} // namespace chip diff --git a/src/app/data-model-provider/MetadataList.h b/src/app/data-model-provider/MetadataList.h new file mode 100644 index 00000000000..5086a430749 --- /dev/null +++ b/src/app/data-model-provider/MetadataList.h @@ -0,0 +1,189 @@ +/* + * + * Copyright (c) 2024 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. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace chip { +namespace app { +namespace DataModel { + +namespace detail { + +class GenericAppendOnlyBuffer +{ +public: + GenericAppendOnlyBuffer(size_t elementSize) : mElementSize(elementSize) {} + ~GenericAppendOnlyBuffer(); + + GenericAppendOnlyBuffer(GenericAppendOnlyBuffer && other); + GenericAppendOnlyBuffer & operator=(GenericAppendOnlyBuffer &&); + + GenericAppendOnlyBuffer() = delete; + GenericAppendOnlyBuffer & operator=(const GenericAppendOnlyBuffer &) = delete; + + /// Ensure that at least the specified number of elements + /// can be appended to the internal buffer; + /// + /// This will cause the internal buffer to become and allocated buffer + CHIP_ERROR EnsureAppendCapacity(size_t numElements); + + bool IsEmpty() const { return mElementCount == 0; } + + /// Number of elements stored in the object. + size_t Size() const { return mElementCount; } + +protected: + /// Appends a single element of mElementSize size. + /// + /// ALWAYS COPIES the given element internally. + /// Sufficient capacity MUST exist to append. + CHIP_ERROR AppendSingleElementRaw(const void * buffer); + + /// Appends a list of elements from a raw array. + /// + /// This ALWAYS COPIES the elements internally. + /// Additional capacity is AUTOMATICALLY ADDED. + CHIP_ERROR AppendElementArrayRaw(const void * buffer, size_t numElements); + + /// Appends a list of elements from a raw array. + /// + /// If the buffer contains no elements, this will just REFERENCE the given + /// buffer, so its lifetime MUST be longer than the lifetime of this buffer and + /// its usage. + /// + /// If the buffer already contains some elements, this will AUTOMATICALLY + /// add additional capacity and COPY the elements at the end of the internal array. + CHIP_ERROR ReferenceExistingElementArrayRaw(const void * buffer, size_t numElements); + + /// release ownership of any used buffer. + /// + /// Returns the current buffer details and releases ownership of it (clears internal state) + void ReleaseBuffer(void *& buffer, size_t & size, bool & allocated); + +private: + const size_t mElementSize; // size of one element in the buffer + uint8_t * mBuffer = nullptr; + size_t mElementCount = 0; // how many elements are stored in the class + size_t mCapacity = 0; // how many elements can be stored in total in mBuffer + bool mBufferIsAllocated = false; // if mBuffer is an allocated buffer +}; + +/// Represents a RAII instance owning a buffer. +/// +/// It auto-frees the owned buffer on destruction +class ScopedBuffer +{ +public: + ScopedBuffer(void * buffer) : mBuffer(buffer) {} + ~ScopedBuffer(); + + ScopedBuffer(const ScopedBuffer &) = delete; + ScopedBuffer & operator=(const ScopedBuffer &) = delete; + + ScopedBuffer(ScopedBuffer && other) : mBuffer(other.mBuffer) { other.mBuffer = nullptr; } + ScopedBuffer & operator=(ScopedBuffer && other); + +private: + void * mBuffer; +}; + +} // namespace detail + +template +class ReadOnlyBuffer : public Span, detail::ScopedBuffer +{ +public: + ReadOnlyBuffer() : ScopedBuffer(nullptr) {} + ReadOnlyBuffer(const T * buffer, size_t size, bool allocated) : + Span(buffer, size), ScopedBuffer(allocated ? const_cast(static_cast(buffer)) : nullptr) + {} + ~ReadOnlyBuffer() = default; + + ReadOnlyBuffer & operator=(ReadOnlyBuffer && other) + { + *static_cast *>(this) = other; + *static_cast(this) = std::move(other); + return *this; + } +}; + +template +class ListBuilder : public detail::GenericAppendOnlyBuffer +{ +public: + using SpanType = Span; + + // we do not call destructors, just malloc things. + // Note that classes should also be trivially assignable (we do NOT call the assignment operator) + // This makes this class somewhat dangerous... + // + // Note: ideally we would want `is_trivially_copyable_v` as well however our chip::Optional + // implementation does not actually report that. + static_assert(std::is_trivially_destructible_v); + + ListBuilder() : GenericAppendOnlyBuffer(sizeof(T)) {} + + ListBuilder(const ListBuilder &) = delete; + ListBuilder & operator=(const ListBuilder & other) = delete; + + ListBuilder(ListBuilder && other) : GenericAppendOnlyBuffer(sizeof(T)) { *this = std::move(other); } + + ListBuilder & operator=(ListBuilder && other) + { + *static_cast(this) = std::move(other); + return *this; + } + + /// Reference methods attempt to reference the existing array IN PLACE + /// so its lifetime is assumed to be longer than the usage of this list. + CHIP_ERROR ReferenceExisting(SpanType span) { return ReferenceExistingElementArrayRaw(span.data(), span.size()); } + + /// Append always attempts to append/extend existing memory. + /// + /// Automatically attempts to allocate sufficient space to fulfill the element + /// requirements. + CHIP_ERROR AppendElements(SpanType span) { return AppendElementArrayRaw(span.data(), span.size()); } + + /// Append a single element. + /// Sufficent append capacity MUST exist. + CHIP_ERROR Append(const T & value) { return AppendSingleElementRaw(&value); } + + /// Once a list is built, the data is taken as a scoped SPAN that owns its data + /// and the original list is cleared + ReadOnlyBuffer TakeBuffer() + { + void * buffer; + size_t size; + bool allocated; + ReleaseBuffer(buffer, size, allocated); + + return ReadOnlyBuffer(static_cast(buffer), size, allocated); + } +}; + +} // namespace DataModel +} // namespace app +} // namespace chip diff --git a/src/app/data-model-provider/MetadataLookup.cpp b/src/app/data-model-provider/MetadataLookup.cpp new file mode 100644 index 00000000000..5ec6b451ed2 --- /dev/null +++ b/src/app/data-model-provider/MetadataLookup.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024 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 + +namespace chip { +namespace app { +namespace DataModel { + +std::optional ServerClusterFinder::Find(const ConcreteClusterPath & path) +{ + VerifyOrReturnValue(mProvider != nullptr, std::nullopt); + + if (mEndpointId != path.mEndpointId) + { + mEndpointId = path.mEndpointId; + mClusterEntries = mProvider->ServerClustersIgnoreError(path.mEndpointId); + } + + for (auto & clusterEntry : mClusterEntries) + { + if (clusterEntry.clusterId == path.mClusterId) + { + return clusterEntry; + } + } + + return std::nullopt; +} + +std::optional AttributeFinder::Find(const ConcreteAttributePath & path) +{ + VerifyOrReturnValue(mProvider != nullptr, std::nullopt); + + if (mClusterPath != path) + { + mClusterPath = path; + mAttributes = mProvider->AttributesIgnoreError(path); + } + + for (auto & attributeEntry : mAttributes) + { + if (attributeEntry.attributeId == path.mAttributeId) + { + return attributeEntry; + } + } + + return std::nullopt; +} + +EndpointFinder::EndpointFinder(ProviderMetadataTree * provider) : mProvider(provider) +{ + VerifyOrReturn(mProvider != nullptr); + mEndpoints = mProvider->EndpointsIgnoreError(); +} + +std::optional EndpointFinder::Find(EndpointId endpointId) +{ + for (auto & endpointEntry : mEndpoints) + { + if (endpointEntry.id == endpointId) + { + return endpointEntry; + } + } + + return std::nullopt; +} + +} // namespace DataModel +} // namespace app +} // namespace chip diff --git a/src/app/data-model-provider/MetadataLookup.h b/src/app/data-model-provider/MetadataLookup.h new file mode 100644 index 00000000000..9c2978d71a5 --- /dev/null +++ b/src/app/data-model-provider/MetadataLookup.h @@ -0,0 +1,84 @@ +/* + * + * Copyright (c) 2024 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace chip { +namespace app { +namespace DataModel { + +/// Helps search for a specific server cluster in the given +/// metadata provider. +/// +/// Facilitates the very common operation of "find a cluster on a given cluster path". +class ServerClusterFinder +{ +public: + ServerClusterFinder(ProviderMetadataTree * provider) : mProvider(provider) {} + + std::optional Find(const ConcreteClusterPath & path); + +private: + ProviderMetadataTree * mProvider; + EndpointId mEndpointId = kInvalidEndpointId; + ReadOnlyBuffer mClusterEntries; +}; + +/// Helps search for a specific server attribute in the given +/// metadata provider. +/// +/// Facilitates the very common operation of "find an attribute on a given attribute path". +class AttributeFinder +{ +public: + AttributeFinder(ProviderMetadataTree * provider) : mProvider(provider), mClusterPath(kInvalidEndpointId, kInvalidClusterId) {} + + std::optional Find(const ConcreteAttributePath & path); + +private: + ProviderMetadataTree * mProvider; + ConcreteClusterPath mClusterPath; + ReadOnlyBuffer mAttributes; +}; + +/// Helps search for a specific server endpoint in the given +/// metadata provider. +/// +/// Facilitates the operation of "find an endpoint with a given ID". +class EndpointFinder +{ +public: + EndpointFinder(ProviderMetadataTree * provider); + std::optional Find(EndpointId endpointId); + +private: + ProviderMetadataTree * mProvider; + ReadOnlyBuffer mEndpoints; +}; + +} // namespace DataModel +} // namespace app +} // namespace chip diff --git a/src/app/data-model-provider/MetadataTypes.cpp b/src/app/data-model-provider/MetadataTypes.cpp index 2729698c49b..6d87d9b5ea5 100644 --- a/src/app/data-model-provider/MetadataTypes.cpp +++ b/src/app/data-model-provider/MetadataTypes.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Project CHIP Authors + * Copyright (c) 2025 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,35 +14,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include +#include + namespace chip { namespace app { namespace DataModel { -const AttributeEntry AttributeEntry::kInvalid{ .path = ConcreteAttributePath(kInvalidEndpointId, kInvalidClusterId, - kInvalidAttributeId) }; +ReadOnlyBuffer ProviderMetadataTree::EndpointsIgnoreError() +{ -const CommandEntry CommandEntry::kInvalid{ .path = ConcreteCommandPath(kInvalidEndpointId, kInvalidClusterId, kInvalidCommandId) }; + ListBuilder builder; + (void) Endpoints(builder); + return builder.TakeBuffer(); +} -const ClusterEntry ClusterEntry::kInvalid{ - .path = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId), - .info = ClusterInfo(0 /* version */), // version of invalid cluster entry does not matter -}; +ReadOnlyBuffer ProviderMetadataTree::ServerClustersIgnoreError(EndpointId endpointId) +{ -const EndpointEntry EndpointEntry::kInvalid{ .id = kInvalidEndpointId, .info = EndpointInfo(kInvalidEndpointId) }; + ListBuilder builder; + (void) ServerClusters(endpointId, builder); + return builder.TakeBuffer(); +} -// A default implementation if just first/next exist -bool ProviderMetadataTree::EndpointExists(EndpointId endpoint) +ReadOnlyBuffer ProviderMetadataTree::AttributesIgnoreError(const ConcreteClusterPath & path) { - for (EndpointEntry ep = FirstEndpoint(); ep.IsValid(); ep = NextEndpoint(ep.id)) - { - if (ep.id == endpoint) - { - return true; - } - } - return false; + ListBuilder builder; + (void) Attributes(path, builder); + return builder.TakeBuffer(); } } // namespace DataModel diff --git a/src/app/data-model-provider/MetadataTypes.h b/src/app/data-model-provider/MetadataTypes.h index 9eeaa7d0504..5b6bccb863e 100644 --- a/src/app/data-model-provider/MetadataTypes.h +++ b/src/app/data-model-provider/MetadataTypes.h @@ -25,10 +25,12 @@ #include #include #include +#include #include #include #include #include +#include namespace chip { namespace app { @@ -50,29 +52,26 @@ enum class EndpointCompositionPattern : uint8_t kFullFamily = 0x2, }; -struct EndpointInfo +struct EndpointEntry { + EndpointId id; + // kInvalidEndpointId if there is no explicit parent endpoint (which means the parent is endpoint 0, // for endpoints other than endpoint 0). EndpointId parentId; EndpointCompositionPattern compositionPattern; - - explicit EndpointInfo(EndpointId parent) : parentId(parent), compositionPattern(EndpointCompositionPattern::kFullFamily) {} - EndpointInfo(EndpointId parent, EndpointCompositionPattern pattern) : parentId(parent), compositionPattern(pattern) {} }; -struct EndpointEntry +enum class ClusterQualityFlags : uint32_t { - EndpointId id; - EndpointInfo info; - - bool IsValid() const { return id != kInvalidEndpointId; } - static const EndpointEntry kInvalid; + kDiagnosticsData = 0x0001, // `K` quality, may be filtered out in subscriptions }; -enum class ClusterQualityFlags : uint32_t +struct ServerClusterEntry { - kDiagnosticsData = 0x0001, // `K` quality, may be filtered out in subscriptions + ClusterId clusterId; + DataVersion dataVersion; // current cluster data version, + BitFlags flags; }; struct ClusterInfo @@ -85,16 +84,6 @@ struct ClusterInfo ClusterInfo(DataVersion version) : dataVersion(version) {} }; -struct ClusterEntry -{ - ConcreteClusterPath path; - ClusterInfo info; - - bool IsValid() const { return path.HasValidIds(); } - - static const ClusterEntry kInvalid; -}; - enum class AttributeQualityFlags : uint32_t { kListAttribute = 0x0004, // This attribute is a list attribute @@ -104,25 +93,18 @@ enum class AttributeQualityFlags : uint32_t kTimed = 0x0040, // `T` quality on attributes (writes require timed interactions) }; -struct AttributeInfo +struct AttributeEntry { + AttributeId attributeId; BitFlags flags; // read/write access will be missing if read/write is NOT allowed + // + // NOTE: this should be compacted for size std::optional readPrivilege; // generally defaults to View if readable std::optional writePrivilege; // generally defaults to Operate if writable }; -struct AttributeEntry -{ - ConcreteAttributePath path; - AttributeInfo info; - - bool IsValid() const { return path.HasValidIds(); } - - static const AttributeEntry kInvalid; -}; - // Bitmask values for different Command qualities. enum class CommandQualityFlags : uint32_t { @@ -131,22 +113,16 @@ enum class CommandQualityFlags : uint32_t kLargeMessage = 0x0004, // `L` quality on commands }; -struct CommandInfo +struct AcceptedCommandEntry { + CommandId commandId; + + // TODO: this can be more compact (use some flags for privilege) + // to make this compact, add a compact enum and make flags/invokePrivilege getters (to still be type safe) BitFlags flags; Access::Privilege invokePrivilege = Access::Privilege::kOperate; }; -struct CommandEntry -{ - ConcreteCommandPath path; - CommandInfo info; - - bool IsValid() const { return path.HasValidIds(); } - - static const CommandEntry kInvalid; -}; - /// Represents a device type that resides on an endpoint struct DeviceTypeEntry { @@ -180,47 +156,18 @@ class ProviderMetadataTree public: virtual ~ProviderMetadataTree() = default; - // This iteration will list all the endpoints in the data model - virtual EndpointEntry FirstEndpoint() = 0; - virtual EndpointEntry NextEndpoint(EndpointId before) = 0; - virtual std::optional GetEndpointInfo(EndpointId id) = 0; - virtual bool EndpointExists(EndpointId id); + using SemanticTag = Clusters::Descriptor::Structs::SemanticTagStruct::Type; - // This iteration describes device types registered on an endpoint - virtual std::optional FirstDeviceType(EndpointId endpoint) = 0; - virtual std::optional NextDeviceType(EndpointId endpoint, const DeviceTypeEntry & previous) = 0; + virtual CHIP_ERROR Endpoints(ListBuilder & builder) = 0; - // This iteration describes semantic tags registered on an endpoint - using SemanticTag = Clusters::Descriptor::Structs::SemanticTagStruct::Type; - virtual std::optional GetFirstSemanticTag(EndpointId endpoint) = 0; - virtual std::optional GetNextSemanticTag(EndpointId endpoint, const SemanticTag & previous) = 0; - - // This iteration will list all server clusters on a given endpoint - virtual ClusterEntry FirstServerCluster(EndpointId endpoint) = 0; - virtual ClusterEntry NextServerCluster(const ConcreteClusterPath & before) = 0; - virtual std::optional GetServerClusterInfo(const ConcreteClusterPath & path) = 0; - - // This iteration will list all client clusters on a given endpoint - // As the client cluster is only a client without any attributes/commands, - // these functions only return the cluster path. - virtual ConcreteClusterPath FirstClientCluster(EndpointId endpoint) = 0; - virtual ConcreteClusterPath NextClientCluster(const ConcreteClusterPath & before) = 0; - - // Attribute iteration and accessors provide cluster-level access over - // attributes - virtual AttributeEntry FirstAttribute(const ConcreteClusterPath & cluster) = 0; - virtual AttributeEntry NextAttribute(const ConcreteAttributePath & before) = 0; - virtual std::optional GetAttributeInfo(const ConcreteAttributePath & path) = 0; - - // Command iteration and accessors provide cluster-level access over commands - virtual CommandEntry FirstAcceptedCommand(const ConcreteClusterPath & cluster) = 0; - virtual CommandEntry NextAcceptedCommand(const ConcreteCommandPath & before) = 0; - virtual std::optional GetAcceptedCommandInfo(const ConcreteCommandPath & path) = 0; - - // "generated" commands are purely for reporting what types of command ids can be - // returned as responses. - virtual ConcreteCommandPath FirstGeneratedCommand(const ConcreteClusterPath & cluster) = 0; - virtual ConcreteCommandPath NextGeneratedCommand(const ConcreteCommandPath & before) = 0; + virtual CHIP_ERROR SemanticTags(EndpointId endpointId, ListBuilder & builder) = 0; + virtual CHIP_ERROR DeviceTypes(EndpointId endpointId, ListBuilder & builder) = 0; + virtual CHIP_ERROR ClientClusters(EndpointId endpointId, ListBuilder & builder) = 0; + virtual CHIP_ERROR ServerClusters(EndpointId endpointId, ListBuilder & builder) = 0; + + virtual CHIP_ERROR Attributes(const ConcreteClusterPath & path, ListBuilder & builder) = 0; + virtual CHIP_ERROR GeneratedCommands(const ConcreteClusterPath & path, ListBuilder & builder) = 0; + virtual CHIP_ERROR AcceptedCommands(const ConcreteClusterPath & path, ListBuilder & builder) = 0; /// Workaround function to report attribute change. /// @@ -239,6 +186,12 @@ class ProviderMetadataTree /// TODO: We should remove this function when the AttributeAccessInterface/CommandHandlerInterface is able to report /// the attribute changes. virtual void Temporary_ReportAttributeChanged(const AttributePathParams & path) = 0; + + // "convenience" functions that just return the data and ignore the error + // This returns the builder as-is even after the error (e.g. not found would return empty data) + ReadOnlyBuffer EndpointsIgnoreError(); + ReadOnlyBuffer ServerClustersIgnoreError(EndpointId endpointId); + ReadOnlyBuffer AttributesIgnoreError(const ConcreteClusterPath & path); }; } // namespace DataModel diff --git a/src/app/data-model-provider/Provider.h b/src/app/data-model-provider/Provider.h index c547b75b819..1d3fff75324 100644 --- a/src/app/data-model-provider/Provider.h +++ b/src/app/data-model-provider/Provider.h @@ -16,15 +16,14 @@ */ #pragma once -#include "access/SubjectDescriptor.h" -#include "app/EventPathParams.h" -#include "lib/core/CHIPError.h" +#include #include #include #include #include #include +#include #include #include diff --git a/src/app/data-model-provider/tests/BUILD.gn b/src/app/data-model-provider/tests/BUILD.gn index 006976d0dce..193a622f889 100644 --- a/src/app/data-model-provider/tests/BUILD.gn +++ b/src/app/data-model-provider/tests/BUILD.gn @@ -20,6 +20,7 @@ chip_test_suite("tests") { test_sources = [ "TestActionReturnStatus.cpp", "TestEventEmitting.cpp", + "TestMetadataList.cpp", ] cflags = [ "-Wconversion" ] diff --git a/src/app/data-model-provider/tests/TestMetadataList.cpp b/src/app/data-model-provider/tests/TestMetadataList.cpp new file mode 100644 index 00000000000..f9fd7ab62be --- /dev/null +++ b/src/app/data-model-provider/tests/TestMetadataList.cpp @@ -0,0 +1,207 @@ +/* + * + * Copyright (c) 2024 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. + */ + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app::DataModel; + +namespace { + +class TestMetadataList : public ::testing::Test +{ +public: + static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); } + static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); } +}; + +template +struct IdAndValue +{ + uint32_t id; + T value; + + bool operator==(const IdAndValue & other) const + { + return (this == &other) || ((this->id == other.id) && (this->value == other.value)); + } +}; + +TEST_F(TestMetadataList, ListBuilderWorks) +{ + ListBuilder> list1; + EXPECT_EQ(list1.Size(), 0u); + EXPECT_TRUE(list1.IsEmpty()); + + ASSERT_EQ(list1.EnsureAppendCapacity(3), CHIP_NO_ERROR); + EXPECT_EQ(list1.Size(), 0u); + EXPECT_TRUE(list1.IsEmpty()); + + ASSERT_EQ(list1.EnsureAppendCapacity(2), CHIP_NO_ERROR); + + // Values can be appended until the capacity. + EXPECT_EQ(list1.Append({ 0xA1, 111 }), CHIP_NO_ERROR); + EXPECT_EQ(list1.Size(), 1u); + + EXPECT_EQ(list1.Append({ 0xA2, 222 }), CHIP_NO_ERROR); + EXPECT_EQ(list1.Size(), 2u); + + // capacity is 3 because of the largest ensure + EXPECT_EQ(list1.Append({ 0xA3, 333 }), CHIP_NO_ERROR); + EXPECT_EQ(list1.Size(), 3u); + + EXPECT_EQ(list1.Append({ 0xA4, 444 }), CHIP_ERROR_BUFFER_TOO_SMALL); + EXPECT_EQ(list1.Size(), 3u); + + ListBuilder> list2 = std::move(list1); + + // Moved-from list is "empty", un-Metadata and span is empty. + EXPECT_EQ(list1.Size(), 0u); // NOLINT(bugprone-use-after-move) + EXPECT_TRUE(list1.IsEmpty()); // NOLINT(bugprone-use-after-move) + EXPECT_TRUE(list1.TakeBuffer().empty()); // NOLINT(bugprone-use-after-move) + + // Moved-to list has storage. + EXPECT_EQ(list2.Size(), 3u); + EXPECT_FALSE(list2.IsEmpty()); + + // A span can be obtained over the list. + auto contents = list2.TakeBuffer(); + EXPECT_EQ(contents.size(), 3u); + + // contents takes ownersip of the list and clears it (and has no capacity) + EXPECT_TRUE(list2.IsEmpty()); + EXPECT_EQ(list2.Append({ 1, 2 }), CHIP_ERROR_BUFFER_TOO_SMALL); + + size_t idx = 0; + for (const auto & element : contents) + { + size_t oneBasedIndex = idx + 1; + EXPECT_EQ(element.id, 0xA0u + oneBasedIndex); + EXPECT_EQ(element.value, 111 * static_cast(oneBasedIndex)); + ++idx; + } + EXPECT_EQ(idx, 3u); +} + +TEST_F(TestMetadataList, ListBuilderConvertersWorks) +{ + { + ListBuilder list; + std::array kArray{ 1, 2, 3 }; + EXPECT_EQ(list.ReferenceExisting(Span(kArray)), CHIP_NO_ERROR); + + auto list2 = std::move(list); + EXPECT_EQ(list.Size(), 0u); // NOLINT(bugprone-use-after-move) + + auto list2Span = list2.TakeBuffer(); + EXPECT_TRUE(list2.IsEmpty()); // took over + EXPECT_EQ(list2Span.size(), 3u); + EXPECT_EQ(list2Span[0], 1); + EXPECT_EQ(list2Span[1], 2); + EXPECT_EQ(list2Span[2], 3); + } + + { + ListBuilder list; + std::array kArray{ 1, 2, 3 }; + std::array kArray2{ 4, 5, 6 }; + EXPECT_EQ(list.ReferenceExisting(Span(kArray)), CHIP_NO_ERROR); + EXPECT_EQ(list.ReferenceExisting(Span(kArray2)), CHIP_NO_ERROR); + + auto list2 = std::move(list); + EXPECT_EQ(list.Size(), 0u); // NOLINT(bugprone-use-after-move) + + auto list2Span = list2.TakeBuffer(); + EXPECT_TRUE(list2.IsEmpty()); // took over + EXPECT_EQ(list2Span.size(), 6u); + EXPECT_EQ(list2Span[0], 1); + EXPECT_EQ(list2Span[1], 2); + EXPECT_EQ(list2Span[2], 3); + EXPECT_EQ(list2Span[3], 4); + EXPECT_EQ(list2Span[4], 5); + EXPECT_EQ(list2Span[5], 6); + } + + { + ListBuilder list; + + EXPECT_EQ(list.Append(10), CHIP_ERROR_BUFFER_TOO_SMALL); + EXPECT_EQ(list.EnsureAppendCapacity(5), CHIP_NO_ERROR); + + EXPECT_EQ(list.Append(10), CHIP_NO_ERROR); + EXPECT_EQ(list.Append(11), CHIP_NO_ERROR); + + std::array kArray{ 1, 2, 3 }; + + EXPECT_EQ(list.ReferenceExisting(Span(kArray)), CHIP_NO_ERROR); + + auto list2 = std::move(list); + EXPECT_EQ(list.Size(), 0u); // NOLINT(bugprone-use-after-move) + + auto list2Span = list2.TakeBuffer(); + EXPECT_TRUE(list2.IsEmpty()); // took over + EXPECT_EQ(list2Span.size(), 5u); + EXPECT_EQ(list2Span[0], 10); + EXPECT_EQ(list2Span[1], 11); + EXPECT_EQ(list2Span[2], 1); + EXPECT_EQ(list2Span[3], 2); + EXPECT_EQ(list2Span[4], 3); + } + { + ListBuilder list; + + EXPECT_EQ(list.Append(10), CHIP_ERROR_BUFFER_TOO_SMALL); + EXPECT_EQ(list.EnsureAppendCapacity(1), CHIP_NO_ERROR); + + EXPECT_EQ(list.Append(10), CHIP_NO_ERROR); + EXPECT_EQ(list.Append(11), CHIP_ERROR_BUFFER_TOO_SMALL); + + std::array kArray{ 1, 2, 3 }; + + EXPECT_EQ(list.AppendElements(Span(kArray)), CHIP_NO_ERROR); + EXPECT_EQ(list.ReferenceExisting(Span(kArray)), CHIP_NO_ERROR); + + auto list2 = std::move(list); + EXPECT_EQ(list.Size(), 0u); // NOLINT(bugprone-use-after-move) + + auto list2Span = list2.TakeBuffer(); + EXPECT_TRUE(list2.IsEmpty()); // took over + EXPECT_EQ(list2Span.size(), 7u); + EXPECT_EQ(list2Span[0], 10); + EXPECT_EQ(list2Span[1], 1); + EXPECT_EQ(list2Span[2], 2); + EXPECT_EQ(list2Span[3], 3); + EXPECT_EQ(list2Span[4], 1); + EXPECT_EQ(list2Span[5], 2); + EXPECT_EQ(list2Span[6], 3); + } +} + +} // namespace diff --git a/src/app/dynamic_server/DynamicDispatcher.cpp b/src/app/dynamic_server/DynamicDispatcher.cpp index 498492085dd..3376128730c 100644 --- a/src/app/dynamic_server/DynamicDispatcher.cpp +++ b/src/app/dynamic_server/DynamicDispatcher.cpp @@ -195,6 +195,14 @@ uint8_t emberAfClusterCount(EndpointId endpoint, bool server) return 0; } +uint8_t emberAfClusterCountForEndpointType(const EmberAfEndpointType * type, bool server) +{ + const EmberAfClusterMask cluster_mask = server ? CLUSTER_MASK_SERVER : CLUSTER_MASK_CLIENT; + + return static_cast(std::count_if(type->cluster, type->cluster + type->clusterCount, + [=](const EmberAfCluster & cluster) { return (cluster.mask & cluster_mask) != 0; })); +} + Optional emberAfGetServerAttributeIdByIndex(EndpointId endpoint, ClusterId cluster, uint16_t attributeIndex) { return NullOptional; diff --git a/src/app/icd/client/CheckInDelegate.h b/src/app/icd/client/CheckInDelegate.h index 5c931d414aa..10fe573dc0b 100644 --- a/src/app/icd/client/CheckInDelegate.h +++ b/src/app/icd/client/CheckInDelegate.h @@ -70,8 +70,8 @@ class DLL_EXPORT CheckInDelegate * @brief Callback used to let the application know that the re-registration process is done. This callback will be called for * both success and failure cases. On failure, the callee should take appropriate corrective action based on the error. * - * @param[in] refreshKeySender - pointer to the RefreshKeySender object that was used for the key refresh process. The caller - * will NOT use this pointer any more. + * @param[in] refreshKeySender - pointer to the RefreshKeySender object that was used for the key refresh process. It will NOT + * be null regardless the key refresh status. The caller will NOT use this pointer any more. * @param[in] error - CHIP_NO_ERROR indicates successful re-registration using the new key * Other errors indicate the failure reason. */ diff --git a/src/app/icd/client/DefaultCheckInDelegate.cpp b/src/app/icd/client/DefaultCheckInDelegate.cpp index 081b4be7bb7..5e068db0ba9 100644 --- a/src/app/icd/client/DefaultCheckInDelegate.cpp +++ b/src/app/icd/client/DefaultCheckInDelegate.cpp @@ -66,20 +66,26 @@ RefreshKeySender * DefaultCheckInDelegate::OnKeyRefreshNeeded(ICDClientInfo & cl void DefaultCheckInDelegate::OnKeyRefreshDone(RefreshKeySender * refreshKeySender, CHIP_ERROR error) { + if (refreshKeySender == nullptr) + { + ChipLogError(ICD, "RefreshKeySender is null"); + return; + } + auto icdClientInfo = refreshKeySender->GetICDClientInfo(); + Platform::Delete(refreshKeySender); + refreshKeySender = nullptr; if (error == CHIP_NO_ERROR) { - ChipLogProgress(ICD, "Re-registration with new key completed successfully"); + ChipLogProgress(ICD, "Re-registration with new key completed successfully for peer node " ChipLogFormatScopedNodeId, + ChipLogValueScopedNodeId(icdClientInfo.peer_node)); } else { - ChipLogError(ICD, "Re-registration with new key failed with error : %" CHIP_ERROR_FORMAT, error.Format()); + ChipLogError( + ICD, "Re-registration with new key failed with error %" CHIP_ERROR_FORMAT " for peer node " ChipLogFormatScopedNodeId, + error.Format(), ChipLogValueScopedNodeId(icdClientInfo.peer_node)); // The callee can take corrective action based on the error received. } - if (refreshKeySender != nullptr) - { - Platform::Delete(refreshKeySender); - refreshKeySender = nullptr; - } } } // namespace app } // namespace chip diff --git a/src/app/icd/client/RefreshKeySender.cpp b/src/app/icd/client/RefreshKeySender.cpp index 9e4547538e6..de145b61821 100644 --- a/src/app/icd/client/RefreshKeySender.cpp +++ b/src/app/icd/client/RefreshKeySender.cpp @@ -36,6 +36,11 @@ RefreshKeySender::RefreshKeySender(CheckInDelegate * checkInDelegate, const ICDC mOnConnectedCallback(HandleDeviceConnected, this), mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this) {} +const ICDClientInfo & RefreshKeySender::GetICDClientInfo() +{ + return mICDClientInfo; +} + CHIP_ERROR RefreshKeySender::RegisterClientWithNewKey(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle) { auto onSuccess = [&](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) { diff --git a/src/app/icd/client/RefreshKeySender.h b/src/app/icd/client/RefreshKeySender.h index a92345fbe8a..1df23a7760e 100644 --- a/src/app/icd/client/RefreshKeySender.h +++ b/src/app/icd/client/RefreshKeySender.h @@ -52,6 +52,14 @@ class RefreshKeySender */ CHIP_ERROR EstablishSessionToPeer(); + /** + * @brief Used to retrieve ICDClientInfo from RefreshKeySender. + * + * @return ICDClientInfo - ICDClientInfo object representing the state associated with the + node that requested a key refresh. + */ + const ICDClientInfo & GetICDClientInfo(); + private: // CASE session callbacks /** diff --git a/src/app/icd/server/BUILD.gn b/src/app/icd/server/BUILD.gn index 1d77158e473..e0d51689105 100644 --- a/src/app/icd/server/BUILD.gn +++ b/src/app/icd/server/BUILD.gn @@ -102,6 +102,7 @@ source_set("manager") { ":observer", "${chip_root}/src/app:subscription-info-provider", "${chip_root}/src/app:test-event-trigger", + "${chip_root}/src/app/common:ids", "${chip_root}/src/credentials:credentials", "${chip_root}/src/lib/address_resolve:address_resolve", "${chip_root}/src/messaging", diff --git a/src/app/reporting/Engine.cpp b/src/app/reporting/Engine.cpp index ed23d44b055..c9d6392286a 100644 --- a/src/app/reporting/Engine.cpp +++ b/src/app/reporting/Engine.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -52,12 +53,16 @@ using Protocols::InteractionModel::Status; Status EventPathValid(DataModel::Provider * model, const ConcreteEventPath & eventPath) { - if (!model->GetServerClusterInfo(eventPath).has_value()) { - return model->EndpointExists(eventPath.mEndpointId) ? Status::UnsupportedCluster : Status::UnsupportedEndpoint; + DataModel::ServerClusterFinder serverClusterFinder(model); + if (serverClusterFinder.Find(eventPath).has_value()) + { + return Status::Success; + } } - return Status::Success; + DataModel::EndpointFinder endpointFinder(model); + return endpointFinder.Find(eventPath.mEndpointId).has_value() ? Status::UnsupportedCluster : Status::UnsupportedEndpoint; } /// Returns the status of ACL validation. @@ -77,7 +82,9 @@ std::optional ValidateReadAttributeACL(DataModel::Provider * dataMod .requestType = RequestType::kAttributeReadRequest, .entityId = path.mAttributeId }; - std::optional info = dataModel->GetAttributeInfo(path); + DataModel::AttributeFinder finder(dataModel); + + std::optional info = finder.Find(path); // If the attribute exists, we know whether it is readable (readPrivilege has value) // and what the required access privilege is. However for attributes missing from the metatada @@ -148,8 +155,10 @@ DataModel::ActionReturnStatus RetrieveClusterData(DataModel::Provider * dataMode readRequest.subjectDescriptor = &subjectDescriptor; readRequest.path = path; + DataModel::ServerClusterFinder serverClusterFinder(dataModel); + DataVersion version = 0; - if (std::optional clusterInfo = dataModel->GetServerClusterInfo(path); clusterInfo.has_value()) + if (auto clusterInfo = serverClusterFinder.Find(path); clusterInfo.has_value()) { version = clusterInfo->dataVersion; } @@ -209,13 +218,10 @@ DataModel::ActionReturnStatus RetrieveClusterData(DataModel::Provider * dataMode bool IsClusterDataVersionEqualTo(DataModel::Provider * dataModel, const ConcreteClusterPath & path, DataVersion dataVersion) { - std::optional info = dataModel->GetServerClusterInfo(path); - if (!info.has_value()) - { - return false; - } + DataModel::ServerClusterFinder serverClusterFinder(dataModel); + auto info = serverClusterFinder.Find(path); - return (info->dataVersion == dataVersion); + return info.has_value() && (info->dataVersion == dataVersion); } } // namespace diff --git a/src/app/reporting/reporting.cpp b/src/app/reporting/reporting.cpp index d0e07544149..8ae2a6e813f 100644 --- a/src/app/reporting/reporting.cpp +++ b/src/app/reporting/reporting.cpp @@ -18,7 +18,9 @@ #include #include +#include #include +#include #include using namespace chip; @@ -30,8 +32,10 @@ void MatterReportingAttributeChangeCallback(EndpointId endpoint, ClusterId clust // applications notifying about changes from their end. assertChipStackLockedByCurrentThread(); - InteractionModelEngine::GetInstance()->GetDataModelProvider()->Temporary_ReportAttributeChanged( - AttributePathParams(endpoint, clusterId, attributeId)); + DataModel::Provider * provider = InteractionModelEngine::GetInstance()->GetDataModelProvider(); + VerifyOrReturn(provider != nullptr); + + provider->Temporary_ReportAttributeChanged(AttributePathParams(endpoint, clusterId, attributeId)); } void MatterReportingAttributeChangeCallback(const ConcreteAttributePath & aPath) @@ -40,8 +44,10 @@ void MatterReportingAttributeChangeCallback(const ConcreteAttributePath & aPath) // applications notifying about changes from their end. assertChipStackLockedByCurrentThread(); - InteractionModelEngine::GetInstance()->GetDataModelProvider()->Temporary_ReportAttributeChanged( - AttributePathParams(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId)); + DataModel::Provider * provider = InteractionModelEngine::GetInstance()->GetDataModelProvider(); + VerifyOrReturn(provider != nullptr); + + provider->Temporary_ReportAttributeChanged(AttributePathParams(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId)); } void MatterReportingAttributeChangeCallback(EndpointId endpoint) @@ -50,5 +56,8 @@ void MatterReportingAttributeChangeCallback(EndpointId endpoint) // applications notifying about changes from their end. assertChipStackLockedByCurrentThread(); - InteractionModelEngine::GetInstance()->GetDataModelProvider()->Temporary_ReportAttributeChanged(AttributePathParams(endpoint)); + DataModel::Provider * provider = InteractionModelEngine::GetInstance()->GetDataModelProvider(); + VerifyOrReturn(provider != nullptr); + + provider->Temporary_ReportAttributeChanged(AttributePathParams(endpoint)); } diff --git a/src/app/tests/TestAttributePathExpandIterator.cpp b/src/app/tests/TestAttributePathExpandIterator.cpp index 4a6088a8196..2b8de0623f7 100644 --- a/src/app/tests/TestAttributePathExpandIterator.cpp +++ b/src/app/tests/TestAttributePathExpandIterator.cpp @@ -16,7 +16,8 @@ * limitations under the License. */ -#include "pw_unit_test/framework.h" +#include + #include #include #include @@ -24,15 +25,13 @@ #include #include #include +#include #include #include #include #include #include -#include -#include - using namespace chip; using namespace chip::Test; using namespace chip::app; @@ -41,7 +40,13 @@ namespace { using P = app::ConcreteAttributePath; -TEST(TestAttributePathExpandIterator, TestAllWildcard) +struct TestAttributePathExpandIterator : public ::testing::Test +{ + static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); } + static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); } +}; + +TEST_F(TestAttributePathExpandIterator, TestAllWildcard) { SingleLinkedListNode clusInfo; @@ -128,7 +133,7 @@ TEST(TestAttributePathExpandIterator, TestAllWildcard) EXPECT_EQ(index, ArraySize(paths)); } -TEST(TestAttributePathExpandIterator, TestWildcardEndpoint) +TEST_F(TestAttributePathExpandIterator, TestWildcardEndpoint) { SingleLinkedListNode clusInfo; clusInfo.mValue.mClusterId = chip::Test::MockClusterId(3); @@ -160,7 +165,7 @@ TEST(TestAttributePathExpandIterator, TestWildcardEndpoint) EXPECT_EQ(index, ArraySize(paths)); } -TEST(TestAttributePathExpandIterator, TestWildcardCluster) +TEST_F(TestAttributePathExpandIterator, TestWildcardCluster) { SingleLinkedListNode clusInfo; clusInfo.mValue.mEndpointId = chip::Test::kMockEndpoint3; @@ -195,7 +200,7 @@ TEST(TestAttributePathExpandIterator, TestWildcardCluster) EXPECT_EQ(index, ArraySize(paths)); } -TEST(TestAttributePathExpandIterator, TestWildcardClusterGlobalAttributeNotInMetadata) +TEST_F(TestAttributePathExpandIterator, TestWildcardClusterGlobalAttributeNotInMetadata) { SingleLinkedListNode clusInfo; clusInfo.mValue.mEndpointId = chip::Test::kMockEndpoint3; @@ -231,7 +236,7 @@ TEST(TestAttributePathExpandIterator, TestWildcardClusterGlobalAttributeNotInMet EXPECT_EQ(index, ArraySize(paths)); } -TEST(TestAttributePathExpandIterator, TestWildcardAttribute) +TEST_F(TestAttributePathExpandIterator, TestWildcardAttribute) { SingleLinkedListNode clusInfo; clusInfo.mValue.mEndpointId = chip::Test::kMockEndpoint2; @@ -271,7 +276,7 @@ TEST(TestAttributePathExpandIterator, TestWildcardAttribute) EXPECT_EQ(index, ArraySize(paths)); } -TEST(TestAttributePathExpandIterator, TestNoWildcard) +TEST_F(TestAttributePathExpandIterator, TestNoWildcard) { SingleLinkedListNode clusInfo; clusInfo.mValue.mEndpointId = chip::Test::kMockEndpoint2; @@ -304,7 +309,7 @@ TEST(TestAttributePathExpandIterator, TestNoWildcard) EXPECT_EQ(index, ArraySize(paths)); } -TEST(TestAttributePathExpandIterator, TestFixedPathExpansion) +TEST_F(TestAttributePathExpandIterator, TestFixedPathExpansion) { // expansion logic requires that: // - paths for wildcard expansion ARE VALIDATED @@ -367,7 +372,7 @@ TEST(TestAttributePathExpandIterator, TestFixedPathExpansion) } } -TEST(TestAttributePathExpandIterator, TestMultipleClusInfo) +TEST_F(TestAttributePathExpandIterator, TestMultipleClusInfo) { SingleLinkedListNode clusInfo1; diff --git a/src/app/tests/suites/certification/Test_TC_IDM_8_1.yaml b/src/app/tests/suites/certification/Test_TC_IDM_8_1.yaml index 7ac77d99517..596bbe950af 100644 --- a/src/app/tests/suites/certification/Test_TC_IDM_8_1.yaml +++ b/src/app/tests/suites/certification/Test_TC_IDM_8_1.yaml @@ -576,19 +576,3 @@ tests: [1660742276.568954][17561:17566] CHIP:DMG: MoveToState ReadClient[0x7f8624024eb0]: Moving to [AwaitingSu] [1660742276.568986][17561:17566] CHIP:EM: Piggybacking Ack for MessageCounter:39674556 on exchange: 26542i disabled: true - - - label: - "RC1 sends Subscribe Request Message to DUT with EventRequests set to - path where an event in the path is fabric-sensitive and the associated - fabric does not match the accessing fabric." - verification: | - Mark this as not testable /NA. Out of Scope for V1.1 - disabled: true - - - label: - "RC1 sends Read Request Message to DUT with EventRequests set to path - where an event in the path is fabric-sensitive and the associated - fabric does not match the accessing fabric." - verification: | - Mark this as not testable /NA. Out of Scope for V1.0 - disabled: true diff --git a/src/app/tests/suites/commands/interaction_model/InteractionModel.h b/src/app/tests/suites/commands/interaction_model/InteractionModel.h index 3acafb8de2a..d8f166f39ed 100644 --- a/src/app/tests/suites/commands/interaction_model/InteractionModel.h +++ b/src/app/tests/suites/commands/interaction_model/InteractionModel.h @@ -407,7 +407,7 @@ class InteractionModelWriter mTimedInteractionTimeoutMs, mSuppressResponse.ValueOr(false)); VerifyOrReturnError(mWriteClient != nullptr, CHIP_ERROR_NO_MEMORY); - for (uint8_t i = 0; i < pathsConfig.count; i++) + for (size_t i = 0; i < pathsConfig.count; i++) { auto & path = pathsConfig.attributePathParams[i]; auto & dataVersion = pathsConfig.dataVersionFilter[i].mDataVersion; diff --git a/src/app/tests/test-interaction-model-api.cpp b/src/app/tests/test-interaction-model-api.cpp index 1de18681b5a..d5659420dd6 100644 --- a/src/app/tests/test-interaction-model-api.cpp +++ b/src/app/tests/test-interaction-model-api.cpp @@ -149,107 +149,5 @@ std::optional TestImCustomDataModel::Invoke(const InvokeRequ return std::make_optional(CHIP_ERROR_NOT_IMPLEMENTED); } -DataModel::EndpointEntry TestImCustomDataModel::FirstEndpoint() -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->FirstEndpoint(); -} - -DataModel::EndpointEntry TestImCustomDataModel::NextEndpoint(EndpointId before) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->NextEndpoint(before); -} - -std::optional TestImCustomDataModel::GetEndpointInfo(EndpointId endpoint) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->GetEndpointInfo(endpoint); -} - -std::optional TestImCustomDataModel::FirstDeviceType(EndpointId endpoint) -{ - return std::nullopt; -} - -std::optional TestImCustomDataModel::NextDeviceType(EndpointId endpoint, - const DataModel::DeviceTypeEntry & previous) -{ - return std::nullopt; -} - -std::optional TestImCustomDataModel::GetFirstSemanticTag(EndpointId endpoint) -{ - return std::nullopt; -} - -std::optional TestImCustomDataModel::GetNextSemanticTag(EndpointId endpoint, - const SemanticTag & previous) -{ - return std::nullopt; -} - -ClusterEntry TestImCustomDataModel::FirstServerCluster(EndpointId endpoint) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->FirstServerCluster(endpoint); -} - -ClusterEntry TestImCustomDataModel::NextServerCluster(const ConcreteClusterPath & before) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->NextServerCluster(before); -} - -std::optional TestImCustomDataModel::GetServerClusterInfo(const ConcreteClusterPath & path) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->GetServerClusterInfo(path); -} - -ConcreteClusterPath TestImCustomDataModel::FirstClientCluster(EndpointId endpoint) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->FirstClientCluster(endpoint); -} - -ConcreteClusterPath TestImCustomDataModel::NextClientCluster(const ConcreteClusterPath & before) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->NextClientCluster(before); -} - -AttributeEntry TestImCustomDataModel::FirstAttribute(const ConcreteClusterPath & cluster) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->FirstAttribute(cluster); -} - -AttributeEntry TestImCustomDataModel::NextAttribute(const ConcreteAttributePath & before) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->NextAttribute(before); -} - -std::optional TestImCustomDataModel::GetAttributeInfo(const ConcreteAttributePath & path) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->GetAttributeInfo(path); -} - -CommandEntry TestImCustomDataModel::FirstAcceptedCommand(const ConcreteClusterPath & cluster) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->FirstAcceptedCommand(cluster); -} - -CommandEntry TestImCustomDataModel::NextAcceptedCommand(const ConcreteCommandPath & before) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->NextAcceptedCommand(before); -} - -std::optional TestImCustomDataModel::GetAcceptedCommandInfo(const ConcreteCommandPath & path) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->GetAcceptedCommandInfo(path); -} - -ConcreteCommandPath TestImCustomDataModel::FirstGeneratedCommand(const ConcreteClusterPath & cluster) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->FirstGeneratedCommand(cluster); -} - -ConcreteCommandPath TestImCustomDataModel::NextGeneratedCommand(const ConcreteCommandPath & before) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->NextGeneratedCommand(before); -} - } // namespace app } // namespace chip diff --git a/src/app/tests/test-interaction-model-api.h b/src/app/tests/test-interaction-model-api.h index 2120ec4304e..54efc316da9 100644 --- a/src/app/tests/test-interaction-model-api.h +++ b/src/app/tests/test-interaction-model-api.h @@ -26,6 +26,7 @@ #include #include #include +#include /** * Helper macro we can use to pretend we got a reply from the server in cases @@ -96,7 +97,7 @@ void DispatchSingleClusterCommand(const ConcreteCommandPath & aRequestCommandPat /// TODO items for above: /// - once IM only supports DataModel /// - break ember-overrides in this h/cpp file -class TestImCustomDataModel : public DataModel::Provider +class TestImCustomDataModel : public CodegenDataModelProvider { public: static TestImCustomDataModel & Instance(); @@ -109,29 +110,6 @@ class TestImCustomDataModel : public DataModel::Provider AttributeValueDecoder & decoder) override; std::optional Invoke(const DataModel::InvokeRequest & request, chip::TLV::TLVReader & input_arguments, CommandHandler * handler) override; - - DataModel::EndpointEntry FirstEndpoint() override; - DataModel::EndpointEntry NextEndpoint(EndpointId before) override; - std::optional GetEndpointInfo(EndpointId endpoint) override; - std::optional FirstDeviceType(EndpointId endpoint) override; - std::optional NextDeviceType(EndpointId endpoint, - const DataModel::DeviceTypeEntry & previous) override; - std::optional GetFirstSemanticTag(EndpointId endpoint) override; - std::optional GetNextSemanticTag(EndpointId endpoint, const SemanticTag & previous) override; - DataModel::ClusterEntry FirstServerCluster(EndpointId endpoint) override; - DataModel::ClusterEntry NextServerCluster(const ConcreteClusterPath & before) override; - std::optional GetServerClusterInfo(const ConcreteClusterPath & path) override; - ConcreteClusterPath FirstClientCluster(EndpointId endpoint) override; - ConcreteClusterPath NextClientCluster(const ConcreteClusterPath & before) override; - DataModel::AttributeEntry FirstAttribute(const ConcreteClusterPath & cluster) override; - DataModel::AttributeEntry NextAttribute(const ConcreteAttributePath & before) override; - std::optional GetAttributeInfo(const ConcreteAttributePath & path) override; - DataModel::CommandEntry FirstAcceptedCommand(const ConcreteClusterPath & cluster) override; - DataModel::CommandEntry NextAcceptedCommand(const ConcreteCommandPath & before) override; - std::optional GetAcceptedCommandInfo(const ConcreteCommandPath & path) override; - ConcreteCommandPath FirstGeneratedCommand(const ConcreteClusterPath & cluster) override; - ConcreteCommandPath NextGeneratedCommand(const ConcreteCommandPath & before) override; - void Temporary_ReportAttributeChanged(const AttributePathParams & path) override {} }; } // namespace app diff --git a/src/app/util/BUILD.gn b/src/app/util/BUILD.gn index 29b3bc9563c..2feae78fbfb 100644 --- a/src/app/util/BUILD.gn +++ b/src/app/util/BUILD.gn @@ -58,6 +58,7 @@ source_set("af-types") { deps = [ ":types", "${chip_root}/src/app:paths", + "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/app/data-model", "${chip_root}/src/messaging", "${chip_root}/src/protocols/interaction_model", diff --git a/src/app/util/attribute-storage.cpp b/src/app/util/attribute-storage.cpp index 3df2ba7c667..58fe5e20f7c 100644 --- a/src/app/util/attribute-storage.cpp +++ b/src/app/util/attribute-storage.cpp @@ -70,10 +70,6 @@ static bool emAfMatchCluster(const EmberAfCluster * cluster, const EmberAfAttrib static bool emAfMatchAttribute(const EmberAfCluster * cluster, const EmberAfAttributeMetadata * am, const EmberAfAttributeSearchRecord * attRecord); -// If server == true, returns the number of server clusters, -// otherwise number of client clusters on the endpoint at the given index. -static uint8_t emberAfClusterCountForEndpointType(const EmberAfEndpointType * endpointType, bool server); - // If server == true, returns the number of server clusters, // otherwise number of client clusters on the endpoint at the given index. static uint8_t emberAfClusterCountByIndex(uint16_t endpointIndex, bool server); diff --git a/src/app/util/attribute-storage.h b/src/app/util/attribute-storage.h index 6930db7a965..c833bf64872 100644 --- a/src/app/util/attribute-storage.h +++ b/src/app/util/attribute-storage.h @@ -273,6 +273,10 @@ void emberAfInitializeAttributes(chip::EndpointId endpoint); // otherwise number of client clusters on this endpoint uint8_t emberAfClusterCount(chip::EndpointId endpoint, bool server); +// If server == true, returns the number of server clusters, +// otherwise number of client clusters on the endpoint at the given index. +uint8_t emberAfClusterCountForEndpointType(const EmberAfEndpointType * endpointType, bool server); + // Returns the cluster of Nth server or client cluster, // depending on server toggle. const EmberAfCluster * emberAfGetNthCluster(chip::EndpointId endpoint, uint8_t n, bool server); diff --git a/src/app/util/mock/attribute-storage.cpp b/src/app/util/mock/attribute-storage.cpp index 3d9e910f457..c1647b41e6c 100644 --- a/src/app/util/mock/attribute-storage.cpp +++ b/src/app/util/mock/attribute-storage.cpp @@ -207,6 +207,14 @@ uint8_t emberAfClusterCount(chip::EndpointId endpoint, bool server) return (server) ? emberAfGetClusterCountForEndpoint(endpoint) : 0; } +uint8_t emberAfClusterCountForEndpointType(const EmberAfEndpointType * type, bool server) +{ + const EmberAfClusterMask cluster_mask = server ? CLUSTER_MASK_SERVER : CLUSTER_MASK_CLIENT; + + return static_cast(std::count_if(type->cluster, type->cluster + type->clusterCount, + [=](const EmberAfCluster & cluster) { return (cluster.mask & cluster_mask) != 0; })); +} + uint16_t emberAfGetServerAttributeCount(chip::EndpointId endpointId, chip::ClusterId clusterId) { auto cluster = GetMockNodeConfig().clusterByIds(endpointId, clusterId); diff --git a/src/app/zap-templates/zcl/data-model/chip/camera-av-stream-management-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/camera-av-stream-management-cluster.xml index 6182aba2448..0a5110ec402 100644 --- a/src/app/zap-templates/zcl/data-model/chip/camera-av-stream-management-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/camera-av-stream-management-cluster.xml @@ -1,6 +1,6 @@ @@ -508,7 +508,7 @@ Git: 1.4-446-g4a179b5f4 - This command SHALL be used to modify the resolution of a stream specified by the VideoStreamID. + This command SHALL be used to modify a stream specified by the VideoStreamID. diff --git a/src/controller/AutoCommissioner.cpp b/src/controller/AutoCommissioner.cpp index e9dac8df879..d156d305827 100644 --- a/src/controller/AutoCommissioner.cpp +++ b/src/controller/AutoCommissioner.cpp @@ -18,13 +18,14 @@ #include -#include - #include #include #include #include +#include +#include + namespace chip { namespace Controller { @@ -225,6 +226,32 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam mParams.SetICDClientType(params.GetICDClientType().Value()); } + auto extraReadPaths = params.GetExtraReadPaths(); + if (extraReadPaths.size() > 0) + { + using ReadPath = std::remove_pointer_t; + static_assert(std::is_trivially_copyable_v, "can't use memmove / memcpy, not trivially copyable"); + + if (mExtraReadPaths.AllocatedSize() == extraReadPaths.size()) + { + memmove(mExtraReadPaths.Get(), extraReadPaths.data(), extraReadPaths.size() * sizeof(ReadPath)); + } + else + { + // We can't reallocate mExtraReadPaths yet as this would free the old buffer, + // and the caller might be passing a sub-span of the old paths. + decltype(mExtraReadPaths) oldReadPaths(std::move(mExtraReadPaths)); + VerifyOrReturnError(mExtraReadPaths.Alloc(extraReadPaths.size()), CHIP_ERROR_NO_MEMORY); + memcpy(mExtraReadPaths.Get(), extraReadPaths.data(), extraReadPaths.size() * sizeof(ReadPath)); + } + + mParams.SetExtraReadPaths(mExtraReadPaths.Span()); + } + else + { + mExtraReadPaths.Free(); + } + return CHIP_NO_ERROR; } diff --git a/src/controller/AutoCommissioner.h b/src/controller/AutoCommissioner.h index deacda6e3cc..1589da42258 100644 --- a/src/controller/AutoCommissioner.h +++ b/src/controller/AutoCommissioner.h @@ -16,10 +16,13 @@ * limitations under the License. */ #pragma once + +#include #include #include #include #include +#include #include namespace chip { @@ -116,7 +119,9 @@ class AutoCommissioner : public CommissioningDelegate CommissioneeDeviceProxy * mCommissioneeDeviceProxy = nullptr; OperationalCredentialsDelegate * mOperationalCredentialsDelegate = nullptr; OperationalDeviceProxy mOperationalDeviceProxy; - // Memory space for the commisisoning parameters that come in as ByteSpans - the caller is not guaranteed to retain this memory + + // BEGIN memory space for commissioning parameters that are Spans or similar pointers: + // The caller is not guaranteed to retain the memory these parameters point to. uint8_t mSsid[CommissioningParameters::kMaxSsidLen]; uint8_t mCredentials[CommissioningParameters::kMaxCredentialsLen]; uint8_t mThreadOperationalDataset[CommissioningParameters::kMaxThreadDatasetLen]; @@ -136,6 +141,11 @@ class AutoCommissioner : public CommissioningDelegate static constexpr size_t kMaxDefaultNtpSize = 128; char mDefaultNtp[kMaxDefaultNtpSize]; + uint8_t mICDSymmetricKey[Crypto::kAES_CCM128_Key_Length]; + Platform::ScopedMemoryBufferWithSize mExtraReadPaths; + + // END memory space for commisisoning parameters + bool mNeedsNetworkSetup = false; ReadCommissioningInfo mDeviceCommissioningInfo; bool mNeedsDST = false; @@ -155,8 +165,6 @@ class AutoCommissioner : public CommissioningDelegate uint8_t mAttestationElements[Credentials::kMaxRspLen]; uint16_t mAttestationSignatureLen = 0; uint8_t mAttestationSignature[Crypto::kMax_ECDSA_Signature_Length]; - - uint8_t mICDSymmetricKey[Crypto::kAES_CCM128_Key_Length]; }; } // namespace Controller } // namespace chip diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 76e82d7f21a..e62bfb86916 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -2329,6 +2329,12 @@ void DeviceCommissioner::ContinueReadingCommissioningInfo(const CommissioningPar Clusters::IcdManagement::Attributes::ActiveModeDuration::Id)); VerifyOrReturn(builder.AddAttributePath(kRootEndpointId, Clusters::IcdManagement::Id, Clusters::IcdManagement::Attributes::ActiveModeThreshold::Id)); + + // Extra paths requested via CommissioningParameters + for (auto const & path : params.GetExtraReadPaths()) + { + VerifyOrReturn(builder.AddAttributePath(path)); + } }(); VerifyOrDie(builder.size() > 0); // our logic is broken if there is nothing to read @@ -2363,6 +2369,7 @@ void DeviceCommissioner::FinishReadingCommissioningInfo() // up returning an error (e.g. because some mandatory information was missing). CHIP_ERROR err = CHIP_NO_ERROR; ReadCommissioningInfo info; + info.attributes = mAttributeCache.get(); AccumulateErrors(err, ParseGeneralCommissioningInfo(info)); AccumulateErrors(err, ParseBasicInformation(info)); AccumulateErrors(err, ParseNetworkCommissioningInfo(info)); diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index 93a0a587529..c9f1652c969 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -816,6 +816,7 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, Optional GetCommissioningParameters() { + // TODO: Return a non-optional const & to avoid a copy, mDefaultCommissioner is never null return mDefaultCommissioner == nullptr ? NullOptional : MakeOptional(mDefaultCommissioner->GetCommissioningParameters()); } diff --git a/src/controller/CommissioningDelegate.h b/src/controller/CommissioningDelegate.h index 660a3d11d12..99763aa305c 100644 --- a/src/controller/CommissioningDelegate.h +++ b/src/controller/CommissioningDelegate.h @@ -18,11 +18,14 @@ #pragma once #include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -591,6 +594,18 @@ class CommissioningParameters } void ClearICDStayActiveDurationMsec() { mICDStayActiveDurationMsec.ClearValue(); } + Span GetExtraReadPaths() const { return mExtraReadPaths; } + + // Additional attribute paths to read as part of the kReadCommissioningInfo stage. + // These values read from the device will be available in ReadCommissioningInfo.attributes. + // Clients should avoid requesting paths that are already read internally by the commissioner + // as no consolidation of internally read and extra paths provided here will be performed. + CommissioningParameters & SetExtraReadPaths(Span paths) + { + mExtraReadPaths = paths; + return *this; + } + // Clear all members that depend on some sort of external buffer. Can be // used to make sure that we are not holding any dangling pointers. void ClearExternalBufferDependentValues() @@ -613,6 +628,7 @@ class CommissioningParameters mDSTOffsets.ClearValue(); mDefaultNTP.ClearValue(); mICDSymmetricKey.ClearValue(); + mExtraReadPaths = decltype(mExtraReadPaths)(); } private: @@ -662,6 +678,7 @@ class CommissioningParameters Optional mICDStayActiveDurationMsec; ICDRegistrationStrategy mICDRegistrationStrategy = ICDRegistrationStrategy::kIgnore; bool mCheckForMatchingFabric = false; + Span mExtraReadPaths; }; struct RequestedCertificate @@ -762,6 +779,9 @@ struct ICDManagementClusterInfo struct ReadCommissioningInfo { +#if CHIP_CONFIG_ENABLE_READ_CLIENT + app::ClusterStateCache const * attributes = nullptr; +#endif NetworkClusters network; BasicClusterInfo basic; GeneralCommissioningInfo general; diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index 69dd7b62df3..1ba302c3851 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -9823,7 +9823,7 @@ provisional cluster CameraAvStreamManagement = 1361 { command access(invoke: manage) AudioStreamDeallocate(AudioStreamDeallocateRequest): DefaultSuccess = 2; /** This command SHALL allocate a video stream on the camera and return an allocated video stream identifier. */ command access(invoke: manage) VideoStreamAllocate(VideoStreamAllocateRequest): VideoStreamAllocateResponse = 3; - /** This command SHALL be used to modify the resolution of a stream specified by the VideoStreamID. */ + /** This command SHALL be used to modify a stream specified by the VideoStreamID. */ command access(invoke: manage) VideoStreamModify(VideoStreamModifyRequest): DefaultSuccess = 5; /** This command SHALL deallocate a video stream on the camera, corresponding to the given video stream identifier. */ command access(invoke: manage) VideoStreamDeallocate(VideoStreamDeallocateRequest): DefaultSuccess = 6; diff --git a/src/controller/python/README.md b/src/controller/python/README.md index e94a95e1585..8fa2369a444 100644 --- a/src/controller/python/README.md +++ b/src/controller/python/README.md @@ -11,5 +11,5 @@ Python CHIP controller library from a shell. To learn more about the Python CHIP Controller and the REPL, how to build it and use its commands and advanced features, read the following guides: -- [Working with Python CHIP Controller](../../../docs/guides/python_chip_controller_building.md) -- [Using Python CHIP Controller advanced features](../../../docs/guides/python_chip_controller_advanced_usage.md) +- [Working with Python CHIP Controller](../../../docs/development_controllers/chip-repl/python_chip_controller_building.md) +- [Using Python CHIP Controller advanced features](../../../docs/development_controllers/chip-repl/python_chip_controller_advanced_usage.md) diff --git a/src/controller/python/chip/native/__init__.py b/src/controller/python/chip/native/__init__.py index 2528aeca32b..1256748dc56 100644 --- a/src/controller/python/chip/native/__init__.py +++ b/src/controller/python/chip/native/__init__.py @@ -16,6 +16,7 @@ import ctypes import enum +import functools import glob import os import platform @@ -146,21 +147,31 @@ def __ne__(self, other): return not self == other -PostAttributeChangeCallback = ctypes.CFUNCTYPE( +c_PostAttributeChangeCallback = ctypes.CFUNCTYPE( None, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint8, ctypes.c_uint16, - # TODO: This should be a pointer to uint8_t, but ctypes does not provide - # such a type. The best approximation is c_char_p, however, this - # requires the caller to pass NULL-terminate C-string which might - # not be the case here. - ctypes.c_char_p, + ctypes.POINTER(ctypes.c_char), ) +def PostAttributeChangeCallback(func): + @functools.wraps(func) + def wrapper( + endpoint: int, + clusterId: int, + attributeId: int, + xx_type: int, + size: int, + value: ctypes.POINTER(ctypes.c_char), + ): + return func(endpoint, clusterId, attributeId, xx_type, size, value[:size]) + return c_PostAttributeChangeCallback(wrapper) + + def FindNativeLibraryPath(library: Library) -> str: """Find the native CHIP dll/so path.""" @@ -234,7 +245,7 @@ def _GetLibraryHandle(lib: Library, expectAlreadyInitialized: bool) -> _Handle: [ctypes.POINTER(PyChipError), ctypes.c_char_p, ctypes.c_uint32]) elif lib == Library.SERVER: setter.Set("pychip_server_native_init", PyChipError, []) - setter.Set("pychip_server_set_callbacks", None, [PostAttributeChangeCallback]) + setter.Set("pychip_server_set_callbacks", None, [c_PostAttributeChangeCallback]) handle = _nativeLibraryHandles[lib] if expectAlreadyInitialized and not handle.initialized: diff --git a/src/controller/python/chip/server/__init__.py b/src/controller/python/chip/server/__init__.py index 8280ec09186..b42cc403099 100644 --- a/src/controller/python/chip/server/__init__.py +++ b/src/controller/python/chip/server/__init__.py @@ -17,11 +17,20 @@ import ctypes from chip import native -from chip.native import PostAttributeChangeCallback +from chip.native import PostAttributeChangeCallback, c_PostAttributeChangeCallback +__all__ = [ + "GetLibraryHandle", + "PostAttributeChangeCallback", +] -def GetLibraryHandle(cb: PostAttributeChangeCallback) -> ctypes.CDLL: - """Get a memoized handle to the chip native code dll.""" + +def GetLibraryHandle(cb: c_PostAttributeChangeCallback) -> ctypes.CDLL: + """Get a memoized handle to the chip native code dll. + + Args: + cb: A callback decorated by PostAttributeChangeCallback decorator. + """ handle = native._GetLibraryHandle(native.Library.SERVER, False) if not handle.initialized: diff --git a/src/controller/tests/TestEventCaching.cpp b/src/controller/tests/TestEventCaching.cpp index cbff4b743ea..5821b84b3fa 100644 --- a/src/controller/tests/TestEventCaching.cpp +++ b/src/controller/tests/TestEventCaching.cpp @@ -18,14 +18,13 @@ #include -#include "app-common/zap-generated/ids/Attributes.h" -#include "app-common/zap-generated/ids/Clusters.h" -#include "app/ClusterStateCache.h" -#include "app/ConcreteAttributePath.h" -#include "protocols/interaction_model/Constants.h" #include +#include +#include #include +#include #include +#include #include #include #include @@ -33,11 +32,13 @@ #include #include #include +#include #include #include #include #include #include +#include using namespace chip; using namespace chip::app; @@ -153,6 +154,7 @@ TEST_F(TestEventCaching, TestBasicCaching) app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); // Initialize the ember side server logic + engine->SetDataModelProvider(CodegenDataModelProviderInstance(nullptr /* delegate */)); InitDataModelHandler(); // Register our fake dynamic endpoint. diff --git a/src/controller/tests/TestEventNumberCaching.cpp b/src/controller/tests/TestEventNumberCaching.cpp index 9b580f759e0..666e6033f79 100644 --- a/src/controller/tests/TestEventNumberCaching.cpp +++ b/src/controller/tests/TestEventNumberCaching.cpp @@ -18,10 +18,10 @@ #include -#include "app-common/zap-generated/ids/Clusters.h" -#include "app/ClusterStateCache.h" #include +#include #include +#include #include #include #include @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ class TestEventNumberCaching : public chip::Test::AppContext // Performs setup for each test in the suite void SetUp() { + ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); const chip::app::LogStorageResources logStorageResources[] = { { &gDebugEventBuffer[0], sizeof(gDebugEventBuffer), chip::app::PriorityLevel::Debug }, { &gInfoEventBuffer[0], sizeof(gInfoEventBuffer), chip::app::PriorityLevel::Info }, @@ -80,6 +82,7 @@ class TestEventNumberCaching : public chip::Test::AppContext { chip::app::EventManagement::DestroyEventManagement(); AppContext::TearDown(); + chip::Platform::MemoryShutdown(); } private: @@ -144,6 +147,7 @@ TEST_F(TestEventNumberCaching, TestEventNumberCaching) app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); // Initialize the ember side server logic + engine->SetDataModelProvider(CodegenDataModelProviderInstance(nullptr /* delegate */)); InitDataModelHandler(); // Register our fake dynamic endpoint. diff --git a/src/controller/tests/data_model/DataModelFixtures.cpp b/src/controller/tests/data_model/DataModelFixtures.cpp index d5c02b5f0d1..930c7469039 100644 --- a/src/controller/tests/data_model/DataModelFixtures.cpp +++ b/src/controller/tests/data_model/DataModelFixtures.cpp @@ -477,107 +477,5 @@ std::optional CustomDataModel::Invoke(const InvokeRequest & return std::nullopt; // handler status is set by the dispatch } -DataModel::EndpointEntry CustomDataModel::FirstEndpoint() -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->FirstEndpoint(); -} - -DataModel::EndpointEntry CustomDataModel::NextEndpoint(EndpointId before) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->NextEndpoint(before); -} - -std::optional CustomDataModel::GetEndpointInfo(EndpointId endpoint) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->GetEndpointInfo(endpoint); -} - -std::optional CustomDataModel::FirstDeviceType(EndpointId endpoint) -{ - return std::nullopt; -} - -std::optional CustomDataModel::NextDeviceType(EndpointId endpoint, - const DataModel::DeviceTypeEntry & previous) -{ - return std::nullopt; -} - -std::optional CustomDataModel::GetFirstSemanticTag(EndpointId endpoint) -{ - return std::nullopt; -} - -std::optional CustomDataModel::GetNextSemanticTag(EndpointId endpoint, - const SemanticTag & previous) -{ - return std::nullopt; -} - -ClusterEntry CustomDataModel::FirstServerCluster(EndpointId endpoint) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->FirstServerCluster(endpoint); -} - -ClusterEntry CustomDataModel::NextServerCluster(const ConcreteClusterPath & before) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->NextServerCluster(before); -} - -std::optional CustomDataModel::GetServerClusterInfo(const ConcreteClusterPath & path) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->GetServerClusterInfo(path); -} - -ConcreteClusterPath CustomDataModel::FirstClientCluster(EndpointId endpoint) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->FirstClientCluster(endpoint); -} - -ConcreteClusterPath CustomDataModel::NextClientCluster(const ConcreteClusterPath & before) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->NextClientCluster(before); -} - -AttributeEntry CustomDataModel::FirstAttribute(const ConcreteClusterPath & cluster) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->FirstAttribute(cluster); -} - -AttributeEntry CustomDataModel::NextAttribute(const ConcreteAttributePath & before) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->NextAttribute(before); -} - -std::optional CustomDataModel::GetAttributeInfo(const ConcreteAttributePath & path) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->GetAttributeInfo(path); -} - -CommandEntry CustomDataModel::FirstAcceptedCommand(const ConcreteClusterPath & cluster) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->FirstAcceptedCommand(cluster); -} - -CommandEntry CustomDataModel::NextAcceptedCommand(const ConcreteCommandPath & before) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->NextAcceptedCommand(before); -} - -std::optional CustomDataModel::GetAcceptedCommandInfo(const ConcreteCommandPath & path) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->GetAcceptedCommandInfo(path); -} - -ConcreteCommandPath CustomDataModel::FirstGeneratedCommand(const ConcreteClusterPath & cluster) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->FirstGeneratedCommand(cluster); -} - -ConcreteCommandPath CustomDataModel::NextGeneratedCommand(const ConcreteCommandPath & before) -{ - return CodegenDataModelProviderInstance(nullptr /* delegate */)->NextGeneratedCommand(before); -} - } // namespace app } // namespace chip diff --git a/src/controller/tests/data_model/DataModelFixtures.h b/src/controller/tests/data_model/DataModelFixtures.h index 181bdf8fa3b..d847e4b5743 100644 --- a/src/controller/tests/data_model/DataModelFixtures.h +++ b/src/controller/tests/data_model/DataModelFixtures.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -108,7 +109,7 @@ extern CommandHandler::Handle gAsyncCommandHandle; /// TODO items for above: /// - once IM only supports DataModel /// - break ember-overrides in this h/cpp file -class CustomDataModel : public DataModel::Provider +class CustomDataModel : public CodegenDataModelProvider { public: static CustomDataModel & Instance(); @@ -121,29 +122,6 @@ class CustomDataModel : public DataModel::Provider AttributeValueDecoder & decoder) override; std::optional Invoke(const DataModel::InvokeRequest & request, chip::TLV::TLVReader & input_arguments, CommandHandler * handler) override; - - DataModel::EndpointEntry FirstEndpoint() override; - DataModel::EndpointEntry NextEndpoint(EndpointId before) override; - std::optional GetEndpointInfo(EndpointId endpoint) override; - std::optional FirstDeviceType(EndpointId endpoint) override; - std::optional NextDeviceType(EndpointId endpoint, - const DataModel::DeviceTypeEntry & previous) override; - std::optional GetFirstSemanticTag(EndpointId endpoint) override; - std::optional GetNextSemanticTag(EndpointId endpoint, const SemanticTag & previous) override; - DataModel::ClusterEntry FirstServerCluster(EndpointId endpoint) override; - DataModel::ClusterEntry NextServerCluster(const ConcreteClusterPath & before) override; - std::optional GetServerClusterInfo(const ConcreteClusterPath & path) override; - ConcreteClusterPath FirstClientCluster(EndpointId endpoint) override; - ConcreteClusterPath NextClientCluster(const ConcreteClusterPath & before) override; - DataModel::AttributeEntry FirstAttribute(const ConcreteClusterPath & cluster) override; - DataModel::AttributeEntry NextAttribute(const ConcreteAttributePath & before) override; - std::optional GetAttributeInfo(const ConcreteAttributePath & path) override; - DataModel::CommandEntry FirstAcceptedCommand(const ConcreteClusterPath & cluster) override; - DataModel::CommandEntry NextAcceptedCommand(const ConcreteCommandPath & before) override; - std::optional GetAcceptedCommandInfo(const ConcreteCommandPath & path) override; - ConcreteCommandPath FirstGeneratedCommand(const ConcreteClusterPath & cluster) override; - ConcreteCommandPath NextGeneratedCommand(const ConcreteCommandPath & before) override; - void Temporary_ReportAttributeChanged(const AttributePathParams & path) override {} }; } // namespace DataModelTests diff --git a/src/credentials/BUILD.gn b/src/credentials/BUILD.gn index 7551dfd519c..63b86083a52 100644 --- a/src/credentials/BUILD.gn +++ b/src/credentials/BUILD.gn @@ -127,6 +127,7 @@ static_library("credentials") { public_deps = [ ":build_time_header", + "${chip_root}/src/app/util:types", "${chip_root}/src/controller:delegates", "${chip_root}/src/crypto", "${chip_root}/src/lib/asn1", diff --git a/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.h b/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.h index a9adc87bb76..22d50cf5155 100644 --- a/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.h +++ b/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.h @@ -1,6 +1,5 @@ -/* - * - * Copyright (c) 2021 Project CHIP Authors +/** + * Copyright (c) 2021-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,17 +15,21 @@ * limitations under the License. */ -#pragma once - #import +#include #include #include #include NS_ASSUME_NONNULL_BEGIN +// Decodes an attribute value TLV into a typed ObjC value (see MTRStructsObjc.h) id _Nullable MTRDecodeAttributeValue(const chip::app::ConcreteAttributePath & aPath, chip::TLV::TLVReader & aReader, CHIP_ERROR * aError); +// Wrapper around the precending function that reads the attribute from a ClusterStateCache. +id _Nullable MTRDecodeAttributeValue(const chip::app::ConcreteAttributePath & aPath, const chip::app::ClusterStateCache & aCache, + CHIP_ERROR * aError); + NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.mm b/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.mm new file mode 100644 index 00000000000..d92da2f088a --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.mm @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2024 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. + */ + +#import "MTRAttributeTLVValueDecoder_Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +using namespace chip; + +id _Nullable MTRDecodeAttributeValue(const chip::app::ConcreteAttributePath & aPath, + const chip::app::ClusterStateCache & aCache, + CHIP_ERROR * aError) +{ + TLV::TLVReader reader; + *aError = aCache.Get(aPath, reader); + VerifyOrReturnValue(*aError == CHIP_NO_ERROR, nil); + return MTRDecodeAttributeValue(aPath, reader, aError); +} + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRCommissioneeInfo.h b/src/darwin/Framework/CHIP/MTRCommissioneeInfo.h new file mode 100644 index 00000000000..7c6ff4daea4 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRCommissioneeInfo.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2024 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 + +@class MTRProductIdentity; +@class MTREndpointInfo; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Information read from the commissionee device during commissioning. + */ +NS_SWIFT_SENDABLE +MTR_NEWLY_AVAILABLE +@interface MTRCommissioneeInfo : NSObject + +/** + * The product identity (VID / PID) of the commissionee. + */ +@property (nonatomic, copy, readonly) MTRProductIdentity * productIdentity; + +/** + * Endpoint information for all endpoints of the commissionee. + * Will be present only if readEndpointInformation is set to YES on MTRCommissioningParameters. + * + * Use `rootEndpoint` and `-[MTREndpointInfo children]` to traverse endpoints in composition order. + */ +@property (nonatomic, copy, readonly, nullable) NSDictionary * endpointsById; + +/** + * Endpoint information for the root endpoint of the commissionee. + * Will be present only if readEndpointInformation is set to YES on MTRCommissioningParameters. + */ +@property (nonatomic, copy, readonly, nullable) MTREndpointInfo * rootEndpoint; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRCommissioneeInfo.mm b/src/darwin/Framework/CHIP/MTRCommissioneeInfo.mm new file mode 100644 index 00000000000..045697649e0 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRCommissioneeInfo.mm @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2024 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 "MTRCommissioneeInfo_Internal.h" + +#import "MTRDefines_Internal.h" +#import "MTREndpointInfo_Internal.h" +#import "MTRProductIdentity.h" +#import "MTRUtilities.h" + +NS_ASSUME_NONNULL_BEGIN + +MTR_DIRECT_MEMBERS +@implementation MTRCommissioneeInfo + +- (instancetype)initWithCommissioningInfo:(const chip::Controller::ReadCommissioningInfo &)info +{ + self = [super init]; + _productIdentity = [[MTRProductIdentity alloc] initWithVendorID:@(info.basic.vendorId) productID:@(info.basic.productId)]; + + // TODO: We should probably hold onto our MTRCommissioningParameters so we can look at `readEndpointInformation` + // instead of just reading whatever Descriptor cluster information happens to be in the cache. + auto * endpoints = [MTREndpointInfo endpointsFromAttributeCache:info.attributes]; + if (endpoints.count > 0) { + _endpointsById = endpoints; + } + + return self; +} + +static NSString * const sProductIdentityCodingKey = @"pi"; +static NSString * const sEndpointsCodingKey = @"ep"; + +- (nullable instancetype)initWithCoder:(NSCoder *)coder +{ + self = [super init]; + _productIdentity = [coder decodeObjectOfClass:MTRProductIdentity.class forKey:sProductIdentityCodingKey]; + VerifyOrReturnValue(_productIdentity != nil, nil); + _endpointsById = [coder decodeDictionaryWithKeysOfClass:NSNumber.class + objectsOfClass:MTREndpointInfo.class + forKey:sEndpointsCodingKey]; + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder +{ + [coder encodeObject:_productIdentity forKey:sProductIdentityCodingKey]; + [coder encodeObject:_endpointsById forKey:sEndpointsCodingKey]; +} + ++ (BOOL)supportsSecureCoding +{ + return YES; +} + +- (NSUInteger)hash +{ + return _productIdentity.hash; +} + +- (BOOL)isEqual:(id)object +{ + VerifyOrReturnValue([object class] == [self class], NO); + MTRCommissioneeInfo * other = object; + VerifyOrReturnValue(MTREqualObjects(_productIdentity, other->_productIdentity), NO); + VerifyOrReturnValue(MTREqualObjects(_endpointsById, other->_endpointsById), NO); + return YES; +} + +- (id)copyWithZone:(nullable NSZone *)zone +{ + return self; // immutable +} + +- (nullable MTREndpointInfo *)rootEndpoint +{ + return self.endpointsById[@0]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRCommissioneeInfo_Internal.h b/src/darwin/Framework/CHIP/MTRCommissioneeInfo_Internal.h new file mode 100644 index 00000000000..f892c5f64d0 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRCommissioneeInfo_Internal.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2024 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 "MTRDefines_Internal.h" + +#include + +NS_ASSUME_NONNULL_BEGIN + +MTR_DIRECT_MEMBERS +@interface MTRCommissioneeInfo () + +- (instancetype)initWithCommissioningInfo:(const chip::Controller::ReadCommissioningInfo &)info; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRCommissioningParameters.h b/src/darwin/Framework/CHIP/MTRCommissioningParameters.h index 771dfe54fc8..3dcd1efc53d 100644 --- a/src/darwin/Framework/CHIP/MTRCommissioningParameters.h +++ b/src/darwin/Framework/CHIP/MTRCommissioningParameters.h @@ -1,6 +1,5 @@ /** - * - * Copyright (c) 2022-2023 Project CHIP Authors + * Copyright (c) 2022-2024 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. @@ -97,6 +96,12 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) */ @property (nonatomic, copy, nullable) NSString * countryCode MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)); +/** + * Read device type information from all endpoints during commissioning. + * Defaults to NO. + */ +@property (nonatomic, assign) BOOL readEndpointInformation MTR_NEWLY_AVAILABLE; + @end @interface MTRCommissioningParameters (Deprecated) diff --git a/src/darwin/Framework/CHIP/MTRDefines_Internal.h b/src/darwin/Framework/CHIP/MTRDefines_Internal.h index ba7d6be51d6..a93be2e6cf6 100644 --- a/src/darwin/Framework/CHIP/MTRDefines_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDefines_Internal.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 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. @@ -30,8 +30,12 @@ #ifdef DEBUG #define MTR_TESTABLE MTR_EXPORT +#define MTR_TESTABLE_DIRECT +#define MTR_TESTABLE_DIRECT_MEMBERS #else #define MTR_TESTABLE +#define MTR_TESTABLE_DIRECT MTR_DIRECT +#define MTR_TESTABLE_DIRECT_MEMBERS MTR_DIRECT_MEMBERS #endif // clang-format off diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index 3b222a1e3ec..2380a5af846 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -1,6 +1,5 @@ /** - * - * Copyright (c) 2020-2023 Project CHIP Authors + * Copyright (c) 2020-2024 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. @@ -656,11 +655,13 @@ - (void)controller:(MTRDeviceController *)controller } logString:__PRETTY_FUNCTION__]; } -- (void)controller:(MTRDeviceController *)controller readCommissioningInfo:(MTRProductIdentity *)info +- (void)controller:(MTRDeviceController *)controller readCommissioneeInfo:(MTRCommissioneeInfo *)info { [self _callDelegatesWithBlock:^(id delegate) { - if ([delegate respondsToSelector:@selector(controller:readCommissioningInfo:)]) { - [delegate controller:controller readCommissioningInfo:info]; + if ([delegate respondsToSelector:@selector(controller:readCommissioneeInfo:)]) { + [delegate controller:controller readCommissioneeInfo:info]; + } else if ([delegate respondsToSelector:@selector(controller:readCommissioningInfo:)]) { + [delegate controller:controller readCommissioningInfo:info.productIdentity]; } } logString:__PRETTY_FUNCTION__]; } diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerDelegate.h b/src/darwin/Framework/CHIP/MTRDeviceControllerDelegate.h index bd575e5a42d..5fac4483671 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerDelegate.h +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerDelegate.h @@ -1,6 +1,5 @@ /** - * - * Copyright (c) 2020-2023 Project CHIP Authors + * Copyright (c) 2020-2024 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. @@ -20,6 +19,13 @@ NS_ASSUME_NONNULL_BEGIN +@class MTRCommissioneeInfo; +@class MTRDeviceController; +@class MTRDeviceTypeRevision; +@class MTREndpointInfo; +@class MTRMetrics; +@class MTRProductIdentity; + typedef NS_ENUM(NSInteger, MTRCommissioningStatus) { MTRCommissioningStatusUnknown = 0, MTRCommissioningStatusSuccess = 1, @@ -29,22 +35,6 @@ typedef NS_ENUM(NSInteger, MTRCommissioningStatus) { = 3, } MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); -/** - * A representation of a (vendor, product) pair that identifies a specific product. - */ -MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)) -@interface MTRProductIdentity : NSObject - -@property (nonatomic, copy, readonly) NSNumber * vendorID; - -@property (nonatomic, copy, readonly) NSNumber * productID; - -- (instancetype)initWithVendorID:(NSNumber *)vendorID productID:(NSNumber *)productID; -@end - -@class MTRDeviceController; -@class MTRMetrics; - /** * The protocol definition for the MTRDeviceControllerDelegate. * @@ -98,14 +88,18 @@ MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) metrics:(MTRMetrics *)metrics MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)); /** - * Notify the delegate when commissioning infomation has been read from the Basic - * Information cluster of the commissionee. + * Notify the delegate when commissioning infomation has been read from the commissionee. * - * At the point when this notification happens, device attestation has not been performed yet, + * Note that this notification happens before device attestation is performed, * so the information delivered by this notification should not be trusted. */ - (void)controller:(MTRDeviceController *)controller - readCommissioningInfo:(MTRProductIdentity *)info MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)); + readCommissioneeInfo:(MTRCommissioneeInfo *)info MTR_NEWLY_AVAILABLE; + +- (void)controller:(MTRDeviceController *)controller + readCommissioningInfo:(MTRProductIdentity *)info + MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)) + MTR_NEWLY_DEPRECATED("Use controller:readCommissioneeInfo:"); /** * Notify the delegate when the suspended state changed of the controller, after this happens diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm index f19306eb7b6..33e67866549 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm @@ -1,6 +1,5 @@ /** - * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2024 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. @@ -16,12 +15,17 @@ */ #import "MTRDeviceControllerDelegateBridge.h" + +#import "MTRCommissioneeInfo_Internal.h" #import "MTRDeviceController.h" #import "MTRDeviceController_Internal.h" +#import "MTREndpointInfo_Internal.h" #import "MTRError_Internal.h" #import "MTRLogging_Internal.h" #import "MTRMetricKeys.h" #import "MTRMetricsCollector.h" +#import "MTRProductIdentity.h" +#import "MTRUtilities.h" using namespace chip::Tracing::DarwinFramework; @@ -124,20 +128,21 @@ void MTRDeviceControllerDelegateBridge::OnReadCommissioningInfo(const chip::Controller::ReadCommissioningInfo & info) { MTRDeviceController * strongController = mController; - - chip::VendorId vendorId = info.basic.vendorId; - uint16_t productId = info.basic.productId; - - MTR_LOG("%@ DeviceControllerDelegate Read Commissioning Info. VendorId %u ProductId %u", strongController, vendorId, productId); - id strongDelegate = mDelegate; - if (strongDelegate && mQueue && strongController) { - if ([strongDelegate respondsToSelector:@selector(controller:readCommissioningInfo:)]) { - dispatch_async(mQueue, ^{ - auto * info = [[MTRProductIdentity alloc] initWithVendorID:@(vendorId) productID:@(productId)]; - [strongDelegate controller:strongController readCommissioningInfo:info]; - }); - } + VerifyOrReturn(strongDelegate && mQueue && strongController); + + // TODO: These checks are pointless since currently mController == mDelegate + BOOL wantCommissioneeInfo = [strongDelegate respondsToSelector:@selector(controller:readCommissioneeInfo:)]; + BOOL wantProductIdentity = [strongDelegate respondsToSelector:@selector(controller:readCommissioningInfo:)]; + if (wantCommissioneeInfo || wantProductIdentity) { + auto * commissioneeInfo = [[MTRCommissioneeInfo alloc] initWithCommissioningInfo:info]; + dispatch_async(mQueue, ^{ + if (wantCommissioneeInfo) { // prefer the newer delegate method over the deprecated one + [strongDelegate controller:strongController readCommissioneeInfo:commissioneeInfo]; + } else if (wantProductIdentity) { + [strongDelegate controller:strongController readCommissioningInfo:commissioneeInfo.productIdentity]; + } + }); } } @@ -187,16 +192,3 @@ { mDeviceNodeId = deviceNodeId; } - -@implementation MTRProductIdentity - -- (instancetype)initWithVendorID:(NSNumber *)vendorID productID:(NSNumber *)productID -{ - if (self = [super init]) { - _vendorID = vendorID; - _productID = productID; - } - return self; -} - -@end diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_Concrete.mm b/src/darwin/Framework/CHIP/MTRDeviceController_Concrete.mm index b27844425ba..b67b07e8b81 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_Concrete.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController_Concrete.mm @@ -1,6 +1,5 @@ /** - * - * Copyright (c) 2020-2023 Project CHIP Authors + * Copyright (c) 2020-2024 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. @@ -34,6 +33,7 @@ #import "MTRDeviceController_Concrete.h" #import "MTRDevice_Concrete.h" #import "MTRDevice_Internal.h" +#import "MTREndpointInfo_Internal.h" #import "MTRError_Internal.h" #import "MTRKeypair.h" #import "MTRLogging_Internal.h" @@ -961,6 +961,9 @@ - (BOOL)commissionNodeWithID:(NSNumber *)nodeID auto block = ^BOOL { chip::Controller::CommissioningParameters params; + if (commissioningParams.readEndpointInformation) { + params.SetExtraReadPaths(MTREndpointInfo.requiredAttributePaths); + } if (commissioningParams.csrNonce) { params.SetCSRNonce(AsByteSpan(commissioningParams.csrNonce)); } diff --git a/src/darwin/Framework/CHIP/MTRDeviceType.h b/src/darwin/Framework/CHIP/MTRDeviceType.h index 0330b76d153..54802a19e12 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceType.h +++ b/src/darwin/Framework/CHIP/MTRDeviceType.h @@ -23,8 +23,11 @@ NS_ASSUME_NONNULL_BEGIN +/** + * Meta-data about a device type defined in the Matter specification. + */ MTR_AVAILABLE(ios(18.2), macos(15.2), watchos(11.2), tvos(18.2)) -@interface MTRDeviceType : NSObject +@interface MTRDeviceType : NSObject /* (see below) */ /** * Returns an MTRDeviceType for the given ID, if the ID is known. Returns nil @@ -52,4 +55,8 @@ MTR_AVAILABLE(ios(18.2), macos(15.2), watchos(11.2), tvos(18.2)) @end +MTR_NEWLY_AVAILABLE +@interface MTRDeviceType () +@end + NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRDeviceType.mm b/src/darwin/Framework/CHIP/MTRDeviceType.mm index 394640d2d72..3fb9b7aa033 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceType.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceType.mm @@ -17,27 +17,44 @@ #import +#import "MTRDefines_Internal.h" #import "MTRDeviceTypeMetadata.h" #import "MTRLogging_Internal.h" +#include #include +NS_ASSUME_NONNULL_BEGIN + using namespace chip; -@implementation MTRDeviceType +MTR_DIRECT_MEMBERS +@implementation MTRDeviceType { + const MTRDeviceTypeData * _meta; +} -- (nullable instancetype)initWithDeviceTypeID:(NSNumber *)id name:(NSString *)name isUtility:(BOOL)isUtility +- (instancetype)initWithDeviceTypeData:(const MTRDeviceTypeData *)metaData { - if (!(self = [super init])) { - return nil; - } - - _id = id; - _name = name; - _isUtility = isUtility; + self = [super init]; + _meta = metaData; return self; } +- (NSNumber *)id +{ + return @(_meta->id); +} + +- (NSString *)name +{ + return _meta->name; +} + +- (BOOL)isUtility +{ + return _meta->deviceClass != MTRDeviceTypeClass::Simple; +} + + (nullable MTRDeviceType *)deviceTypeForID:(NSNumber *)deviceTypeID { if (!CanCastTo(deviceTypeID.unsignedLongLongValue)) { @@ -50,10 +67,32 @@ + (nullable MTRDeviceType *)deviceTypeForID:(NSNumber *)deviceTypeID return nil; } - return [[MTRDeviceType alloc] - initWithDeviceTypeID:deviceTypeID - name:[NSString stringWithUTF8String:deviceTypeData->name] - isUtility:(deviceTypeData->deviceClass != MTRDeviceTypeClass::Simple)]; + return [[MTRDeviceType alloc] initWithDeviceTypeData:deviceTypeData]; +} + +- (id)copyWithZone:(nullable NSZone *)zone +{ + return self; // immutable +} + +- (NSUInteger)hash +{ + return _meta->id; +} + +- (BOOL)isEqual:(id)object +{ + VerifyOrReturnValue([object class] == [self class], NO); + MTRDeviceType * other = object; + return _meta->id == other->_meta->id; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ 0x%" PRIx32 " (%@)>", + self.class, _meta->id, _meta->name]; } @end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRDeviceTypeMetadata.h b/src/darwin/Framework/CHIP/MTRDeviceTypeMetadata.h index 2597f60ec47..b0f1a280532 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceTypeMetadata.h +++ b/src/darwin/Framework/CHIP/MTRDeviceTypeMetadata.h @@ -23,18 +23,16 @@ NS_ASSUME_NONNULL_BEGIN -enum class MTRDeviceTypeClass -{ +enum class MTRDeviceTypeClass { Utility, Simple, Node, // Might not be a real class, but we have it for Root Node for now. }; -struct MTRDeviceTypeData -{ +struct MTRDeviceTypeData { chip::DeviceTypeId id; MTRDeviceTypeClass deviceClass; - const char * name; + NSString * name; }; // Returns null for unknown device types. diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.h b/src/darwin/Framework/CHIP/MTRDeviceTypeRevision.h similarity index 69% rename from src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.h rename to src/darwin/Framework/CHIP/MTRDeviceTypeRevision.h index 51794881636..e5e55a56450 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.h +++ b/src/darwin/Framework/CHIP/MTRDeviceTypeRevision.h @@ -17,6 +17,9 @@ #import #import +@class MTRDescriptorClusterDeviceTypeStruct; +@class MTRDeviceType; + NS_ASSUME_NONNULL_BEGIN /** @@ -25,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN */ NS_SWIFT_SENDABLE MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) -@interface MTRDeviceTypeRevision : NSObject +@interface MTRDeviceTypeRevision : NSObject /* (see below) */ - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; @@ -38,9 +41,24 @@ MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) */ - (nullable instancetype)initWithDeviceTypeID:(NSNumber *)deviceTypeID revision:(NSNumber *)revision; +/** + * Initializes the receiver based on the values in the specified struct. + */ +- (nullable instancetype)initWithDeviceTypeStruct:(MTRDescriptorClusterDeviceTypeStruct *)deviceTypeStruct MTR_NEWLY_AVAILABLE; + @property (nonatomic, copy, readonly) NSNumber * deviceTypeID; @property (nonatomic, copy, readonly) NSNumber * deviceTypeRevision; +/** + * Returns the MTRDeviceType corresponding to deviceTypeID, + * or nil if deviceTypeID does not represent a known device type. + */ +@property (nonatomic, copy, readonly, nullable) MTRDeviceType * typeInformation MTR_NEWLY_AVAILABLE; + +@end + +MTR_NEWLY_AVAILABLE +@interface MTRDeviceTypeRevision () @end NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.mm b/src/darwin/Framework/CHIP/MTRDeviceTypeRevision.mm similarity index 60% rename from src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.mm rename to src/darwin/Framework/CHIP/MTRDeviceTypeRevision.mm index de5782c92e4..2e71dcddbe4 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceTypeRevision.mm @@ -14,14 +14,19 @@ * limitations under the License. */ +#import "MTRDeviceTypeRevision.h" #import "MTRDefines_Internal.h" +#import "MTRDeviceType.h" #import "MTRLogging_Internal.h" -#import +#import "MTRStructsObjc.h" #include #include +#include #include +NS_ASSUME_NONNULL_BEGIN + using namespace chip; MTR_DIRECT_MEMBERS @@ -29,6 +34,9 @@ @implementation MTRDeviceTypeRevision - (nullable instancetype)initWithDeviceTypeID:(NSNumber *)deviceTypeID revision:(NSNumber *)revision { + VerifyOrReturnValue(deviceTypeID != nil, nil); + VerifyOrReturnValue(revision != nil, nil); + auto deviceTypeIDValue = deviceTypeID.unsignedLongLongValue; if (!CanCastTo(deviceTypeIDValue)) { MTR_LOG_ERROR("MTRDeviceTypeRevision provided too-large device type ID: 0x%llx", deviceTypeIDValue); @@ -50,20 +58,49 @@ - (nullable instancetype)initWithDeviceTypeID:(NSNumber *)deviceTypeID revision: return [self initInternalWithDeviceTypeID:[deviceTypeID copy] revision:[revision copy]]; } +- (nullable instancetype)initWithDeviceTypeStruct:(MTRDescriptorClusterDeviceTypeStruct *)deviceTypeStruct +{ + return [self initWithDeviceTypeID:deviceTypeStruct.deviceType revision:deviceTypeStruct.revision]; +} + // initInternalWithDeviceTypeID:revision assumes that the device type ID and device // revision have already been validated and, if needed, copied from the input. - (instancetype)initInternalWithDeviceTypeID:(NSNumber *)deviceTypeID revision:(NSNumber *)revision { - if (!(self = [super init])) { - return nil; - } - + self = [super init]; _deviceTypeID = deviceTypeID; _deviceTypeRevision = revision; return self; } -- (id)copyWithZone:(NSZone *)zone +static NSString * const sTypeIdCodingKey = @"ty"; +static NSString * const sRevisionCodingKey = @"re"; + +- (nullable instancetype)initWithCoder:(NSCoder *)coder +{ + self = [super init]; + _deviceTypeID = @(static_cast([coder decodeInt64ForKey:sTypeIdCodingKey])); // int64_t encompasses uint32_t + _deviceTypeRevision = @(static_cast([coder decodeIntegerForKey:sRevisionCodingKey])); + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder +{ + [coder encodeInt64:static_cast(_deviceTypeID.unsignedLongLongValue) forKey:sTypeIdCodingKey]; + [coder encodeInteger:static_cast(_deviceTypeRevision.unsignedIntegerValue) forKey:sRevisionCodingKey]; +} + ++ (BOOL)supportsSecureCoding +{ + return YES; +} + +- (nullable MTRDeviceType *)typeInformation +{ + return [MTRDeviceType deviceTypeForID:_deviceTypeID]; +} + +- (id)copyWithZone:(nullable NSZone *)zone { // We have no mutable state. return self; @@ -85,4 +122,13 @@ - (NSUInteger)hash return _deviceTypeID.unsignedLongValue ^ _deviceTypeRevision.unsignedShortValue; } +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ 0x%" PRIx32 " (%@) rev %d>", + self.class, _deviceTypeID.unsignedIntValue, + self.typeInformation.name ?: @"???", _deviceTypeRevision.unsignedIntValue]; +} + @end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm index 264a268e253..cb92e52a1da 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm +++ b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm @@ -1487,6 +1487,7 @@ - (void)_reattemptSubscriptionNowIfNeededWithReason:(NSString *)reason os_unfair_lock_assert_owner(&self->_lock); if (!self.reattemptingSubscription) { + [self _clearSubscriptionPoolWork]; return; } @@ -3265,6 +3266,7 @@ - (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID if ([now compare:cutoffTime] == NSOrderedDescending) { // Our timed invoke timeout has expired already. Command // was queued for too long. Do not send it out. + MTR_LOG("Invoke work item [%llu] timed out its timed invoke timeout before being dispatched", workItemID); workDone(nil, [MTRError errorForIMStatusCode:Status::Timeout]); return; } diff --git a/src/darwin/Framework/CHIP/MTREndpointInfo.h b/src/darwin/Framework/CHIP/MTREndpointInfo.h new file mode 100644 index 00000000000..dc271a05483 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTREndpointInfo.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2024 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 + +@class MTRDeviceTypeRevision; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Meta-data about an endpoint of a Matter node. + */ +NS_SWIFT_SENDABLE +MTR_NEWLY_AVAILABLE +@interface MTREndpointInfo : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@property (nonatomic, copy, readonly) NSNumber * endpointID; + +@property (nonatomic, copy, readonly) NSArray * deviceTypes; +@property (nonatomic, copy, readonly) NSArray * partsList; + +/** + * The direct children of this endpoint. This excludes indirect descendants + * even if they are listed in the PartsList attribute of this endpoint due + * to the Full-Family Pattern being used. Refer to Endpoint Composition Patterns + * in the Matter specification for details. + */ +@property (nonatomic, copy, readonly) NSArray * children; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTREndpointInfo.mm b/src/darwin/Framework/CHIP/MTREndpointInfo.mm new file mode 100644 index 00000000000..b9a0fbf7fb4 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTREndpointInfo.mm @@ -0,0 +1,269 @@ +/** + * Copyright (c) 2024 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 "MTREndpointInfo_Internal.h" + +#import "MTRAttributeTLVValueDecoder_Internal.h" +#import "MTRDeviceTypeRevision.h" +#import "MTRLogging_Internal.h" +#import "MTRStructsObjc.h" + +#include +#include + +#include + +NS_ASSUME_NONNULL_BEGIN + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; + +enum class EndpointMark : uint8_t { + NotVisited = 0, + Visiting, + Visited, + ParentAssigned = NotVisited, // != Visited +}; + +MTR_DIRECT_MEMBERS +@implementation MTREndpointInfo { + EndpointId _endpointID; + EndpointMark _mark; // used by populateChildrenForEndpoints: +} + +- (instancetype)initWithEndpointID:(NSNumber *)endpointID + deviceTypes:(NSArray *)deviceTypes + partsList:(NSArray *)partsList +{ + self = [super init]; + _endpointID = endpointID.unsignedShortValue; + _deviceTypes = [deviceTypes copy]; + _partsList = [partsList copy]; + _children = @[]; + _mark = EndpointMark::NotVisited; + return self; +} + +static NSString * const sEndpointIDCodingKey = @"id"; +static NSString * const sDeviceTypesCodingKey = @"dt"; +static NSString * const sPartsListCodingKey = @"pl"; +static NSString * const sChildrenCodingKey = @"ch"; + +- (nullable instancetype)initWithCoder:(NSCoder *)coder +{ + self = [super init]; + _endpointID = static_cast([coder decodeIntegerForKey:sEndpointIDCodingKey]); + _deviceTypes = [coder decodeArrayOfObjectsOfClass:MTRDeviceTypeRevision.class forKey:sDeviceTypesCodingKey]; + VerifyOrReturnValue(_deviceTypes != nil, nil); + _partsList = [coder decodeArrayOfObjectsOfClass:NSNumber.class forKey:sPartsListCodingKey]; + VerifyOrReturnValue(_partsList != nil, nil); + _children = [coder decodeArrayOfObjectsOfClass:MTREndpointInfo.class forKey:sChildrenCodingKey]; + VerifyOrReturnValue(_children != nil, nil); + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder +{ + [coder encodeInteger:_endpointID forKey:sEndpointIDCodingKey]; + [coder encodeObject:_deviceTypes forKey:sDeviceTypesCodingKey]; + [coder encodeObject:_partsList forKey:sPartsListCodingKey]; + [coder encodeObject:_children forKey:sChildrenCodingKey]; +} + ++ (BOOL)supportsSecureCoding +{ + return YES; +} + +- (id)copyWithZone:(nullable NSZone *)zone +{ + return self; // no (externally) mutable state +} + +- (NSUInteger)hash +{ + return _endpointID; +} + +- (BOOL)isEqual:(id)object +{ + VerifyOrReturnValue([object class] == [self class], NO); + MTREndpointInfo * other = object; + VerifyOrReturnValue(_endpointID == other->_endpointID, NO); + VerifyOrReturnValue([_deviceTypes isEqual:other->_deviceTypes], NO); + VerifyOrReturnValue([_partsList isEqual:other->_partsList], NO); + // Children are derived from PartsLists, so we don't need to compare them. + // This avoids a lot recursive comparisons when comparing a dictionary of endpoints. + return YES; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ %u>", self.class, _endpointID]; +} + +- (NSNumber *)endpointID +{ + return @(_endpointID); +} + ++ (BOOL)populateChildrenForEndpoints:(NSDictionary *)endpoints +{ + // Populate the child list of each endpoint, ensuring no cycles (these are disallowed + // by the spec, but we need to ensure we don't create a retain cycle even under invalid + // input). Conservatively assume all endpoints use the Full-Family Pattern. + // Refer to "Endpoint Composition" in the Matter specification for details. + MTREndpointInfo * root = endpoints[@0]; + if (root == nil) { + MTR_LOG_ERROR("Missing root endpoint, not populating endpoint hierarchy"); + return NO; + } + + // Perform a depth-first search with an explicit stack and create a list of endpoint + // IDs in reverse topological order. Note that endpoints start with _mark == NotVisited. + BOOL valid = YES; + std::deque deque; // stack followed by sorted list + deque.emplace_front(root->_endpointID); + for (;;) { + EndpointId endpointID = deque.front(); + MTREndpointInfo * endpoint = endpoints[@(endpointID)]; + if (endpoint->_mark == EndpointMark::NotVisited) { + endpoint->_mark = EndpointMark::Visiting; + for (NSNumber * partNumber in endpoint->_partsList) { + MTREndpointInfo * descendant = endpoints[partNumber]; + if (!descendant) { + MTR_LOG_ERROR("Warning: PartsList of endpoint %u references non-existant endpoint %u", + endpointID, partNumber.unsignedShortValue); + valid = NO; + } else if (descendant->_mark == EndpointMark::NotVisited) { + deque.emplace_front(descendant->_endpointID); + } else if (descendant->_mark == EndpointMark::Visiting) { + MTR_LOG_ERROR("Warning: Cyclic endpoint composition involving endpoints %u and %u", + descendant->_endpointID, endpointID); + valid = NO; + } + } + } else if (endpoint->_mark == EndpointMark::Visiting) { + endpoint->_mark = EndpointMark::Visited; + deque.pop_front(); // remove from stack + deque.emplace_back(endpointID); // add to sorted list + if (endpointID == root->_endpointID) { + break; // visited the root, DFS traversal done + } + } else /* endpoint->_mark == EndpointMark::Visited */ { + // Endpoints can be visited multiple times due to Full-Family + // ancestors like the root node, or in scenarios where an + // endpoint is erroneously in the PartsList of two separate + // branches of the tree. There is no easy way to distinguish + // these cases here, so we are not setting valid = NO. + deque.pop_front(); // nothing else to do + } + } + if (deque.size() != endpoints.count) { + MTR_LOG_ERROR("Warning: Not all endpoints are descendants of the root endpoint"); + valid = NO; + } + + // Now iterate over the endpoints in reverse topological order, i.e. bottom up. This means + // that we will visit children before parents, so the first time we see an endpoint in a + // PartsList we can assign it as a child of the endpoint we're processing, and we can be sure + // that this is the closest parent, not some higher ancestor using the Full-Family Pattern. + NSMutableArray * children = [[NSMutableArray alloc] init]; + while (!deque.empty()) { + EndpointId endpointID = deque.front(); + MTREndpointInfo * endpoint = endpoints[@(endpointID)]; + deque.pop_front(); + + if (endpoint->_mark == EndpointMark::ParentAssigned) { + continue; // This endpoint is part of a cycle, don't populate its children. + } + + [children removeAllObjects]; + for (NSNumber * partNumber in endpoint->_partsList) { + MTREndpointInfo * descendant = endpoints[partNumber]; + if (descendant != nil && descendant->_mark != EndpointMark::ParentAssigned) { + descendant->_mark = EndpointMark::ParentAssigned; + [children addObject:descendant]; + } + } + endpoint->_children = [children copy]; + } + root->_mark = EndpointMark::ParentAssigned; + return valid; +} + ++ (NSDictionary *)endpointsFromAttributeCache:(const ClusterStateCache *)cache +{ + VerifyOrReturnValue(cache != nullptr, nil); + using namespace Descriptor::Attributes; + + NSMutableDictionary * endpoints = [[NSMutableDictionary alloc] init]; + cache->ForEachAttribute(Descriptor::Id, [&](const ConcreteAttributePath & path) -> CHIP_ERROR { + VerifyOrReturnError(path.mAttributeId == DeviceTypeList::Id, CHIP_NO_ERROR); + + CHIP_ERROR err = CHIP_NO_ERROR; + NSArray * deviceTypeList = MTRDecodeAttributeValue(path, *cache, &err); + if (!deviceTypeList) { + MTR_LOG_ERROR("Ignoring invalid DeviceTypeList for endpoint %u: %" CHIP_ERROR_FORMAT, path.mEndpointId, err.Format()); + // proceed with deviceTypeList == nil, equivalent to an empty list + } + + NSMutableArray * deviceTypes = [[NSMutableArray alloc] initWithCapacity:deviceTypeList.count]; + for (MTRDescriptorClusterDeviceTypeStruct * deviceTypeStruct in deviceTypeList) { + MTRDeviceTypeRevision * type = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeStruct:deviceTypeStruct]; + if (!type) { + MTR_LOG_ERROR("Ignoring invalid device type 0x%x rev %u for endpoint %u", + deviceTypeStruct.deviceType.unsignedIntValue, deviceTypeStruct.revision.unsignedShortValue, + path.mEndpointId); + continue; + } + [deviceTypes addObject:type]; + } + + ConcreteAttributePath partsListPath(path.mEndpointId, path.mClusterId, PartsList::Id); + NSArray * partsList = MTRDecodeAttributeValue(partsListPath, *cache, &err); + if (!partsList) { + MTR_LOG_ERROR("Ignoring invalid PartsList for endpoint %u: %" CHIP_ERROR_FORMAT, path.mEndpointId, err.Format()); + partsList = @[]; + } + + MTREndpointInfo * endpoint = [[MTREndpointInfo alloc] initWithEndpointID:@(path.mEndpointId) + deviceTypes:deviceTypes + partsList:partsList]; + endpoints[endpoint.endpointID] = endpoint; + return CHIP_NO_ERROR; + }); + + if (endpoints.count > 0) { + [self populateChildrenForEndpoints:endpoints]; + } + return [endpoints copy]; +} + ++ (Span)requiredAttributePaths +{ + using namespace Descriptor::Attributes; + static constexpr AttributePathParams kPaths[] = { + AttributePathParams(Descriptor::Id, DeviceTypeList::Id), + AttributePathParams(Descriptor::Id, PartsList::Id), + }; + return Span(kPaths); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTREndpointInfo_Internal.h b/src/darwin/Framework/CHIP/MTREndpointInfo_Internal.h new file mode 100644 index 00000000000..298ee8dab52 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTREndpointInfo_Internal.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2024 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 "MTREndpointInfo_Test.h" + +#include +#include +#include + +NS_ASSUME_NONNULL_BEGIN + +MTR_DIRECT_MEMBERS +@interface MTREndpointInfo () + +/** + * Returns a dictionary of endpoint metadata for a node. + * + * The provided cache must contain the result of reading the + * DeviceTypeList and PartsList attributes of all endpoints + * (as exposed by `requiredAttributePaths`). + * + * Any relevant information will be copied out of the cache; + * the caller is free to deallocate the cache once this method returns. + */ ++ (NSDictionary *)endpointsFromAttributeCache:(const chip::app::ClusterStateCache *)cache; + +/** + * Returns the set of AttributePathParams that must be read + * to populate endpoint information for a node. + */ ++ (chip::Span)requiredAttributePaths; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTREndpointInfo_Test.h b/src/darwin/Framework/CHIP/MTREndpointInfo_Test.h new file mode 100644 index 00000000000..8e36e8f74aa --- /dev/null +++ b/src/darwin/Framework/CHIP/MTREndpointInfo_Test.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2024 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 "MTRDefines_Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +MTR_TESTABLE_DIRECT_MEMBERS +@interface MTREndpointInfo () + +- (instancetype)initWithEndpointID:(NSNumber *)endpointID + deviceTypes:(NSArray *)deviceTypes + partsList:(NSArray *)partsList; + +// Populates the children array for each endpoint in the provided dictionary. +// Returns YES if the endpoint hierarchy was populated correctly. +// A return value of NO indicates that there were some issues, but +// an effort has been made to populate a valid subset of the hierarchy. ++ (BOOL)populateChildrenForEndpoints:(NSDictionary *)endpoints; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h b/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h index 928c80ba85a..86a054a550c 100644 --- a/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h +++ b/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h @@ -1,6 +1,5 @@ /** - * - * Copyright (c) 2021-2023 Project CHIP Authors + * Copyright (c) 2021-2024 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. @@ -15,8 +14,6 @@ * limitations under the License. */ -#include - #import #import @@ -32,6 +29,8 @@ #include #include +#include + NS_ASSUME_NONNULL_BEGIN class MTROperationalCredentialsDelegate : public chip::Controller::OperationalCredentialsDelegate { @@ -63,11 +62,6 @@ class MTROperationalCredentialsDelegate : public chip::Controller::OperationalCr mCppCommissioner = cppCommissioner; } - chip::Optional GetCommissioningParameters() - { - return mCppCommissioner == nullptr ? chip::NullOptional : mCppCommissioner->GetCommissioningParameters(); - } - void SetOperationalCertificateIssuer( id operationalCertificateIssuer, dispatch_queue_t operationalCertificateIssuerQueue) { diff --git a/src/darwin/Framework/CHIP/MTRProductIdentity.h b/src/darwin/Framework/CHIP/MTRProductIdentity.h new file mode 100644 index 00000000000..7277c644152 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRProductIdentity.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2024 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 + +NS_ASSUME_NONNULL_BEGIN + +/** + * A representation of a (vendor, product) pair that identifies a specific product. + */ +NS_SWIFT_SENDABLE +MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)) +@interface MTRProductIdentity : NSObject /* (see below) */ + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +- (instancetype)initWithVendorID:(NSNumber *)vendorID productID:(NSNumber *)productID; + +@property (nonatomic, copy, readonly) NSNumber * vendorID; +@property (nonatomic, copy, readonly) NSNumber * productID; + +@end + +MTR_NEWLY_AVAILABLE +@interface MTRProductIdentity () +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRProductIdentity.mm b/src/darwin/Framework/CHIP/MTRProductIdentity.mm new file mode 100644 index 00000000000..c236d8a4489 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRProductIdentity.mm @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2024 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 "MTRProductIdentity.h" + +#import "MTRDefines_Internal.h" +#import "MTRUtilities.h" + +#include + +MTR_DIRECT_MEMBERS +@implementation MTRProductIdentity { + uint16_t _vendorID; + uint16_t _productID; +} + +- (instancetype)initWithVendorID:(NSNumber *)vendorID productID:(NSNumber *)productID +{ + self = [super init]; + VerifyOrReturnValue(vendorID != nil && productID != nil, nil); + _vendorID = vendorID.unsignedShortValue; + _productID = productID.unsignedShortValue; + return self; +} + +static NSString * const sVendorIDKey = @"v"; +static NSString * const sProductIDKey = @"p"; + +- (nullable instancetype)initWithCoder:(NSCoder *)coder +{ + self = [super init]; + _vendorID = static_cast([coder decodeIntForKey:sVendorIDKey]); + _productID = static_cast([coder decodeIntForKey:sProductIDKey]); + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder +{ + [coder encodeInt:_vendorID forKey:sVendorIDKey]; + [coder encodeInt:_productID forKey:sProductIDKey]; +} + ++ (BOOL)supportsSecureCoding +{ + return YES; +} + +- (id)copyWithZone:(NSZone *)zone +{ + return self; // immutable +} + +- (NSUInteger)hash +{ + return (_vendorID << 16) | _productID; +} + +- (BOOL)isEqual:(id)object +{ + VerifyOrReturnValue([object class] == [self class], NO); + MTRProductIdentity * other = object; + VerifyOrReturnValue(_vendorID == other->_vendorID, NO); + VerifyOrReturnValue(_productID == other->_productID, NO); + return YES; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: vid 0x%x pid 0x%x>", self.class, _vendorID, _productID]; +} + +- (NSNumber *)vendorID +{ + return @(_vendorID); +} + +- (NSNumber *)productID +{ + return @(_productID); +} + +@end diff --git a/src/darwin/Framework/CHIP/Matter.h b/src/darwin/Framework/CHIP/Matter.h index 85a87c4ba12..bf643f654bf 100644 --- a/src/darwin/Framework/CHIP/Matter.h +++ b/src/darwin/Framework/CHIP/Matter.h @@ -36,6 +36,7 @@ #import #import #import +#import #import #import #import @@ -52,6 +53,7 @@ #import #import #import +#import #import #import #import @@ -62,6 +64,7 @@ #import #import #import +#import #import #import #import diff --git a/src/darwin/Framework/CHIP/Matter.modulemap b/src/darwin/Framework/CHIP/Matter.modulemap new file mode 100644 index 00000000000..d5159009463 --- /dev/null +++ b/src/darwin/Framework/CHIP/Matter.modulemap @@ -0,0 +1,5 @@ +framework module Matter [system] { + umbrella header "Matter.h" + export * + module * { export * } +} diff --git a/src/darwin/Framework/CHIP/templates/MTRDeviceTypeMetadata-src.zapt b/src/darwin/Framework/CHIP/templates/MTRDeviceTypeMetadata-src.zapt index 16322f398e9..df3e1ab99f0 100644 --- a/src/darwin/Framework/CHIP/templates/MTRDeviceTypeMetadata-src.zapt +++ b/src/darwin/Framework/CHIP/templates/MTRDeviceTypeMetadata-src.zapt @@ -6,10 +6,12 @@ using namespace chip; namespace { -constexpr MTRDeviceTypeData knownDeviceTypes[] = { +// Not constexpr in the strict sense because NSString * is not a literal +// type, but the array is in fact constant initialized by the compiler. +static /* constexpr */ const MTRDeviceTypeData knownDeviceTypes[] = { {{#zcl_device_types}} {{#if class}} - { {{asHex code 8}}, MTRDeviceTypeClass::{{class}}, "{{caption}}" }, + { {{asHex code 8}}, MTRDeviceTypeClass::{{class}}, @"{{caption}}" }, {{/if}} {{/zcl_device_types}} }; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h index 81934603647..54e4e59c1e5 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h @@ -14086,7 +14086,7 @@ MTR_PROVISIONALLY_AVAILABLE /** * Command VideoStreamModify * - * This command SHALL be used to modify the resolution of a stream specified by the VideoStreamID. + * This command SHALL be used to modify a stream specified by the VideoStreamID. */ - (void)videoStreamModifyWithParams:(MTRCameraAVStreamManagementClusterVideoStreamModifyParams *)params completion:(MTRStatusCompletion)completion MTR_PROVISIONALLY_AVAILABLE; /** diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRDeviceTypeMetadata.mm b/src/darwin/Framework/CHIP/zap-generated/MTRDeviceTypeMetadata.mm index cec53390010..9e4b98f3ffd 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRDeviceTypeMetadata.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRDeviceTypeMetadata.mm @@ -21,77 +21,79 @@ namespace { -constexpr MTRDeviceTypeData knownDeviceTypes[] = { - { 0x0000000A, MTRDeviceTypeClass::Simple, "Door Lock" }, - { 0x0000000B, MTRDeviceTypeClass::Simple, "Door Lock Controller" }, - { 0x0000000E, MTRDeviceTypeClass::Simple, "Aggregator" }, - { 0x0000000F, MTRDeviceTypeClass::Simple, "Generic Switch" }, - { 0x00000011, MTRDeviceTypeClass::Utility, "Power Source" }, - { 0x00000012, MTRDeviceTypeClass::Utility, "OTA Requestor" }, - { 0x00000013, MTRDeviceTypeClass::Utility, "Bridged Node" }, - { 0x00000014, MTRDeviceTypeClass::Utility, "OTA Provider" }, - { 0x00000015, MTRDeviceTypeClass::Simple, "Contact Sensor" }, - { 0x00000016, MTRDeviceTypeClass::Node, "Root Node" }, - { 0x00000017, MTRDeviceTypeClass::Simple, "Solar Power" }, - { 0x00000018, MTRDeviceTypeClass::Simple, "Battery Storage" }, - { 0x00000019, MTRDeviceTypeClass::Utility, "Secondary Network Interface" }, - { 0x00000022, MTRDeviceTypeClass::Simple, "Speaker" }, - { 0x00000023, MTRDeviceTypeClass::Simple, "Casting Video Player" }, - { 0x00000024, MTRDeviceTypeClass::Simple, "Content App" }, - { 0x00000027, MTRDeviceTypeClass::Simple, "Mode Select" }, - { 0x00000028, MTRDeviceTypeClass::Simple, "Basic Video Player" }, - { 0x00000029, MTRDeviceTypeClass::Simple, "Casting Video Client" }, - { 0x0000002A, MTRDeviceTypeClass::Simple, "Video Remote Control" }, - { 0x0000002B, MTRDeviceTypeClass::Simple, "Fan" }, - { 0x0000002C, MTRDeviceTypeClass::Simple, "Air Quality Sensor" }, - { 0x0000002D, MTRDeviceTypeClass::Simple, "Air Purifier" }, - { 0x00000041, MTRDeviceTypeClass::Simple, "Water Freeze Detector" }, - { 0x00000042, MTRDeviceTypeClass::Simple, "Water Valve" }, - { 0x00000043, MTRDeviceTypeClass::Simple, "Water Leak Detector" }, - { 0x00000044, MTRDeviceTypeClass::Simple, "Rain Sensor" }, - { 0x00000070, MTRDeviceTypeClass::Simple, "Refrigerator" }, - { 0x00000071, MTRDeviceTypeClass::Simple, "Temperature Controlled Cabinet" }, - { 0x00000072, MTRDeviceTypeClass::Simple, "Room Air Conditioner" }, - { 0x00000073, MTRDeviceTypeClass::Simple, "Laundry Washer" }, - { 0x00000074, MTRDeviceTypeClass::Simple, "Robotic Vacuum Cleaner" }, - { 0x00000075, MTRDeviceTypeClass::Simple, "Dishwasher" }, - { 0x00000076, MTRDeviceTypeClass::Simple, "Smoke CO Alarm" }, - { 0x00000077, MTRDeviceTypeClass::Simple, "Cook Surface" }, - { 0x00000078, MTRDeviceTypeClass::Simple, "Cooktop" }, - { 0x00000079, MTRDeviceTypeClass::Simple, "Microwave Oven" }, - { 0x0000007A, MTRDeviceTypeClass::Simple, "Extractor Hood" }, - { 0x0000007B, MTRDeviceTypeClass::Simple, "Oven" }, - { 0x0000007C, MTRDeviceTypeClass::Simple, "Laundry Dryer" }, - { 0x00000090, MTRDeviceTypeClass::Simple, "Network Infrastructure Manager" }, - { 0x00000091, MTRDeviceTypeClass::Simple, "Thread Border Router" }, - { 0x00000100, MTRDeviceTypeClass::Simple, "On/Off Light" }, - { 0x00000101, MTRDeviceTypeClass::Simple, "Dimmable Light" }, - { 0x00000103, MTRDeviceTypeClass::Simple, "On/Off Light Switch" }, - { 0x00000104, MTRDeviceTypeClass::Simple, "Dimmer Switch" }, - { 0x00000105, MTRDeviceTypeClass::Simple, "Color Dimmer Switch" }, - { 0x00000106, MTRDeviceTypeClass::Simple, "Light Sensor" }, - { 0x00000107, MTRDeviceTypeClass::Simple, "Occupancy Sensor" }, - { 0x0000010A, MTRDeviceTypeClass::Simple, "On/Off Plug-in Unit" }, - { 0x0000010B, MTRDeviceTypeClass::Simple, "Dimmable Plug-in Unit" }, - { 0x0000010C, MTRDeviceTypeClass::Simple, "Color Temperature Light" }, - { 0x0000010D, MTRDeviceTypeClass::Simple, "Extended Color Light" }, - { 0x00000202, MTRDeviceTypeClass::Simple, "Window Covering" }, - { 0x00000203, MTRDeviceTypeClass::Simple, "Window Covering Controller" }, - { 0x00000300, MTRDeviceTypeClass::Simple, "Heating/Cooling Unit" }, - { 0x00000301, MTRDeviceTypeClass::Simple, "Thermostat" }, - { 0x00000302, MTRDeviceTypeClass::Simple, "Temperature Sensor" }, - { 0x00000303, MTRDeviceTypeClass::Simple, "Pump" }, - { 0x00000304, MTRDeviceTypeClass::Simple, "Pump Controller" }, - { 0x00000305, MTRDeviceTypeClass::Simple, "Pressure Sensor" }, - { 0x00000306, MTRDeviceTypeClass::Simple, "Flow Sensor" }, - { 0x00000307, MTRDeviceTypeClass::Simple, "Humidity Sensor" }, - { 0x00000309, MTRDeviceTypeClass::Simple, "Heat Pump" }, - { 0x0000050C, MTRDeviceTypeClass::Simple, "EVSE" }, - { 0x0000050D, MTRDeviceTypeClass::Utility, "Device Energy Management" }, - { 0x0000050F, MTRDeviceTypeClass::Simple, "Water Heater" }, - { 0x00000510, MTRDeviceTypeClass::Utility, "Electrical Sensor" }, - { 0x00000840, MTRDeviceTypeClass::Simple, "Control Bridge" }, - { 0x00000850, MTRDeviceTypeClass::Simple, "On/Off Sensor" }, +// Not constexpr in the strict sense because NSString * is not a literal +// type, but the array is in fact constant initialized by the compiler. +static /* constexpr */ const MTRDeviceTypeData knownDeviceTypes[] = { + { 0x0000000A, MTRDeviceTypeClass::Simple, @"Door Lock" }, + { 0x0000000B, MTRDeviceTypeClass::Simple, @"Door Lock Controller" }, + { 0x0000000E, MTRDeviceTypeClass::Simple, @"Aggregator" }, + { 0x0000000F, MTRDeviceTypeClass::Simple, @"Generic Switch" }, + { 0x00000011, MTRDeviceTypeClass::Utility, @"Power Source" }, + { 0x00000012, MTRDeviceTypeClass::Utility, @"OTA Requestor" }, + { 0x00000013, MTRDeviceTypeClass::Utility, @"Bridged Node" }, + { 0x00000014, MTRDeviceTypeClass::Utility, @"OTA Provider" }, + { 0x00000015, MTRDeviceTypeClass::Simple, @"Contact Sensor" }, + { 0x00000016, MTRDeviceTypeClass::Node, @"Root Node" }, + { 0x00000017, MTRDeviceTypeClass::Simple, @"Solar Power" }, + { 0x00000018, MTRDeviceTypeClass::Simple, @"Battery Storage" }, + { 0x00000019, MTRDeviceTypeClass::Utility, @"Secondary Network Interface" }, + { 0x00000022, MTRDeviceTypeClass::Simple, @"Speaker" }, + { 0x00000023, MTRDeviceTypeClass::Simple, @"Casting Video Player" }, + { 0x00000024, MTRDeviceTypeClass::Simple, @"Content App" }, + { 0x00000027, MTRDeviceTypeClass::Simple, @"Mode Select" }, + { 0x00000028, MTRDeviceTypeClass::Simple, @"Basic Video Player" }, + { 0x00000029, MTRDeviceTypeClass::Simple, @"Casting Video Client" }, + { 0x0000002A, MTRDeviceTypeClass::Simple, @"Video Remote Control" }, + { 0x0000002B, MTRDeviceTypeClass::Simple, @"Fan" }, + { 0x0000002C, MTRDeviceTypeClass::Simple, @"Air Quality Sensor" }, + { 0x0000002D, MTRDeviceTypeClass::Simple, @"Air Purifier" }, + { 0x00000041, MTRDeviceTypeClass::Simple, @"Water Freeze Detector" }, + { 0x00000042, MTRDeviceTypeClass::Simple, @"Water Valve" }, + { 0x00000043, MTRDeviceTypeClass::Simple, @"Water Leak Detector" }, + { 0x00000044, MTRDeviceTypeClass::Simple, @"Rain Sensor" }, + { 0x00000070, MTRDeviceTypeClass::Simple, @"Refrigerator" }, + { 0x00000071, MTRDeviceTypeClass::Simple, @"Temperature Controlled Cabinet" }, + { 0x00000072, MTRDeviceTypeClass::Simple, @"Room Air Conditioner" }, + { 0x00000073, MTRDeviceTypeClass::Simple, @"Laundry Washer" }, + { 0x00000074, MTRDeviceTypeClass::Simple, @"Robotic Vacuum Cleaner" }, + { 0x00000075, MTRDeviceTypeClass::Simple, @"Dishwasher" }, + { 0x00000076, MTRDeviceTypeClass::Simple, @"Smoke CO Alarm" }, + { 0x00000077, MTRDeviceTypeClass::Simple, @"Cook Surface" }, + { 0x00000078, MTRDeviceTypeClass::Simple, @"Cooktop" }, + { 0x00000079, MTRDeviceTypeClass::Simple, @"Microwave Oven" }, + { 0x0000007A, MTRDeviceTypeClass::Simple, @"Extractor Hood" }, + { 0x0000007B, MTRDeviceTypeClass::Simple, @"Oven" }, + { 0x0000007C, MTRDeviceTypeClass::Simple, @"Laundry Dryer" }, + { 0x00000090, MTRDeviceTypeClass::Simple, @"Network Infrastructure Manager" }, + { 0x00000091, MTRDeviceTypeClass::Simple, @"Thread Border Router" }, + { 0x00000100, MTRDeviceTypeClass::Simple, @"On/Off Light" }, + { 0x00000101, MTRDeviceTypeClass::Simple, @"Dimmable Light" }, + { 0x00000103, MTRDeviceTypeClass::Simple, @"On/Off Light Switch" }, + { 0x00000104, MTRDeviceTypeClass::Simple, @"Dimmer Switch" }, + { 0x00000105, MTRDeviceTypeClass::Simple, @"Color Dimmer Switch" }, + { 0x00000106, MTRDeviceTypeClass::Simple, @"Light Sensor" }, + { 0x00000107, MTRDeviceTypeClass::Simple, @"Occupancy Sensor" }, + { 0x0000010A, MTRDeviceTypeClass::Simple, @"On/Off Plug-in Unit" }, + { 0x0000010B, MTRDeviceTypeClass::Simple, @"Dimmable Plug-in Unit" }, + { 0x0000010C, MTRDeviceTypeClass::Simple, @"Color Temperature Light" }, + { 0x0000010D, MTRDeviceTypeClass::Simple, @"Extended Color Light" }, + { 0x00000202, MTRDeviceTypeClass::Simple, @"Window Covering" }, + { 0x00000203, MTRDeviceTypeClass::Simple, @"Window Covering Controller" }, + { 0x00000300, MTRDeviceTypeClass::Simple, @"Heating/Cooling Unit" }, + { 0x00000301, MTRDeviceTypeClass::Simple, @"Thermostat" }, + { 0x00000302, MTRDeviceTypeClass::Simple, @"Temperature Sensor" }, + { 0x00000303, MTRDeviceTypeClass::Simple, @"Pump" }, + { 0x00000304, MTRDeviceTypeClass::Simple, @"Pump Controller" }, + { 0x00000305, MTRDeviceTypeClass::Simple, @"Pressure Sensor" }, + { 0x00000306, MTRDeviceTypeClass::Simple, @"Flow Sensor" }, + { 0x00000307, MTRDeviceTypeClass::Simple, @"Humidity Sensor" }, + { 0x00000309, MTRDeviceTypeClass::Simple, @"Heat Pump" }, + { 0x0000050C, MTRDeviceTypeClass::Simple, @"EVSE" }, + { 0x0000050D, MTRDeviceTypeClass::Utility, @"Device Energy Management" }, + { 0x0000050F, MTRDeviceTypeClass::Simple, @"Water Heater" }, + { 0x00000510, MTRDeviceTypeClass::Utility, @"Electrical Sensor" }, + { 0x00000840, MTRDeviceTypeClass::Simple, @"Control Bridge" }, + { 0x00000850, MTRDeviceTypeClass::Simple, @"On/Off Sensor" }, }; static_assert(ExtractVendorFromMEI(0xFFF10001) != 0, "Must have class defined for \"Orphan Clusters\" if it's a standard device type"); diff --git a/src/darwin/Framework/CHIPTests/MTRDeviceTypeRevisionTests.m b/src/darwin/Framework/CHIPTests/MTRDeviceTypeRevisionTests.m new file mode 100644 index 00000000000..f228e724df5 --- /dev/null +++ b/src/darwin/Framework/CHIPTests/MTRDeviceTypeRevisionTests.m @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2024 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 + +@interface MTRDeviceTypeRevisionTests : XCTestCase +@end + +@implementation MTRDeviceTypeRevisionTests + +- (void)testInvalidTypeID +{ + XCTAssertNil([[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:(id _Nonnull) nil revision:@1]); + XCTAssertNil([[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@0xC000 revision:@1]); // type > 0xBFFF + XCTAssertNil([[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@0x100000000 revision:@1]); +} + +- (void)testInvalidRevision +{ + XCTAssertNil([[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@1 revision:(id _Nonnull) nil]); + XCTAssertNil([[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@1 revision:@0]); // < 1 + XCTAssertNil([[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@1 revision:@0x10000]); // > 0xFFFF +} + +- (void)testInitWithStruct +{ + MTRDescriptorClusterDeviceTypeStruct * strukt = [[MTRDescriptorClusterDeviceTypeStruct alloc] init]; + strukt.deviceType = @42; + strukt.revision = @2; + MTRDeviceTypeRevision * typeRev = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeStruct:strukt]; + XCTAssertNotNil(typeRev); + XCTAssertEqualObjects(typeRev, [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@42 revision:@2]); +} + +- (void)testTypeInformation +{ + __auto_type * typeRev = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@(MTRDeviceTypeIDTypeRootNodeID) revision:@1]; + XCTAssertNotNil(typeRev); + XCTAssertNotNil(typeRev.typeInformation); + XCTAssertEqualObjects(typeRev.typeInformation.name, @"Root Node"); +} + +- (void)testEqualityAndCopying +{ + __auto_type * a1 = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@(MTRDeviceTypeIDTypeMicrowaveOvenID) revision:@1]; + XCTAssertNotNil(a1); + XCTAssertTrue([a1 isEqual:a1]); + XCTAssertTrue([a1 isEqual:[a1 copy]]); + XCTAssertFalse([a1 isEqual:nil]); + XCTAssertFalse([a1 isEqual:@(MTRDeviceTypeIDTypeMicrowaveOvenID)]); + + __auto_type * a2 = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@(MTRDeviceTypeIDTypeMicrowaveOvenID) revision:@1]; + XCTAssertNotNil(a2); + XCTAssertEqual(a1.hash, a2.hash); + XCTAssertTrue([a1 isEqual:a2]); + XCTAssertTrue([a2 isEqual:a1]); + + __auto_type * b = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@(MTRDeviceTypeIDTypePowerSourceID) revision:@1]; + XCTAssertNotNil(b); + XCTAssertFalse([a1 isEqual:b]); + XCTAssertFalse([b isEqual:a1]); + + __auto_type * c = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@(MTRDeviceTypeIDTypeMicrowaveOvenID) revision:@2]; + XCTAssertNotNil(c); + XCTAssertFalse([c isEqual:a1]); + XCTAssertFalse([a1 isEqual:c]); + XCTAssertFalse([c isEqual:b]); + XCTAssertFalse([b isEqual:c]); +} + +- (void)testSecureCoding +{ + MTRDeviceTypeRevision * a = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@(MTRDeviceTypeIDTypeMicrowaveOvenID) revision:@1]; + NSData * data = [NSKeyedArchiver archivedDataWithRootObject:a requiringSecureCoding:YES error:NULL]; + MTRDeviceTypeRevision * b = [NSKeyedUnarchiver unarchivedObjectOfClass:MTRDeviceTypeRevision.class fromData:data error:NULL]; + XCTAssertNotNil(b); + XCTAssertEqualObjects(b.deviceTypeID, a.deviceTypeID); + XCTAssertEqualObjects(b.deviceTypeRevision, a.deviceTypeRevision); + XCTAssertTrue([b isEqual:a]); +} + +@end diff --git a/src/darwin/Framework/CHIPTests/MTRDeviceTypeTests.m b/src/darwin/Framework/CHIPTests/MTRDeviceTypeTests.m index 7a41d42b40c..1a3f3fc740c 100644 --- a/src/darwin/Framework/CHIPTests/MTRDeviceTypeTests.m +++ b/src/darwin/Framework/CHIPTests/MTRDeviceTypeTests.m @@ -15,12 +15,9 @@ */ #import - -// system dependencies #import @interface MTRDeviceTypeTests : XCTestCase - @end @implementation MTRDeviceTypeTests @@ -55,7 +52,7 @@ - (void)testKnownUtilityID XCTAssertTrue(deviceType.isUtility); } -- (void)testRootNodeID +- (void)testPowerSource { __auto_type * deviceType = [MTRDeviceType deviceTypeForID:@(MTRDeviceTypeIDTypePowerSourceID)]; XCTAssertNotNil(deviceType); @@ -64,4 +61,24 @@ - (void)testRootNodeID XCTAssertTrue(deviceType.isUtility); } +- (void)testEqualityAndCopying +{ + __auto_type * a1 = [MTRDeviceType deviceTypeForID:@(MTRDeviceTypeIDTypeMicrowaveOvenID)]; + XCTAssertNotNil(a1); + XCTAssertTrue([a1 isEqual:a1]); + XCTAssertFalse([a1 isEqual:nil]); + XCTAssertFalse([a1 isEqual:@(MTRDeviceTypeIDTypeMicrowaveOvenID)]); + + __auto_type * a2 = [MTRDeviceType deviceTypeForID:@(MTRDeviceTypeIDTypeMicrowaveOvenID)]; + XCTAssertNotNil(a2); + XCTAssertEqual(a1.hash, a2.hash); + XCTAssertTrue([a1 isEqual:a2]); + XCTAssertTrue([a2 isEqual:a1]); + + __auto_type * b = [MTRDeviceType deviceTypeForID:@(MTRDeviceTypeIDTypePowerSourceID)]; + XCTAssertNotNil(b); + XCTAssertFalse([a1 isEqual:b]); + XCTAssertFalse([b isEqual:a1]); +} + @end diff --git a/src/darwin/Framework/CHIPTests/MTREndpointInfoTests.m b/src/darwin/Framework/CHIPTests/MTREndpointInfoTests.m new file mode 100644 index 00000000000..1999eb478b5 --- /dev/null +++ b/src/darwin/Framework/CHIPTests/MTREndpointInfoTests.m @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2024 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 "MTREndpointInfo_Test.h" +#import + +#import + +@interface MTREndpointInfoTests : XCTestCase +@end + +@implementation MTREndpointInfoTests + +static MTREndpointInfo * MakeEndpoint(NSNumber * endpointID, NSArray * parts) +{ + return [[MTREndpointInfo alloc] initWithEndpointID:endpointID deviceTypes:@[] partsList:parts]; +} + +static NSArray * ChildEndpointIDs(MTREndpointInfo * endpoint) +{ + return [[endpoint.children valueForKey:@"endpointID"] sortedArrayUsingSelector:@selector(compare:)]; +} + +static NSArray * Exclude(NSArray * numbers, NSNumber * numberToExclude) +{ + NSMutableArray * result = [numbers mutableCopy]; + [result removeObject:numberToExclude]; + return result; +} + +- (NSDictionary *)indexEndpoints:(NSArray *)endpoints +{ + NSMutableDictionary * indexed = [[NSMutableDictionary alloc] init]; + for (MTREndpointInfo * endpoint in endpoints) { + indexed[endpoint.endpointID] = endpoint; + } + XCTAssertEqual(indexed.count, endpoints.count, @"Duplicate endpoint IDs"); + return indexed; +} + +- (void)testPopulateChildren +{ + NSDictionary * endpoints = [self indexEndpoints:@[ + MakeEndpoint(@0, @[ @1, @2, @3, @4, @5, @6 ]), // full-family pattern + MakeEndpoint(@1, @[ @2, @3 ]), + MakeEndpoint(@2, @[]), + MakeEndpoint(@3, @[]), + MakeEndpoint(@4, @[ @5, @6 ]), // full-family pattern + MakeEndpoint(@5, @[ @6 ]), + MakeEndpoint(@6, @[]), + ]]; + XCTAssertTrue([MTREndpointInfo populateChildrenForEndpoints:endpoints]); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@0]), (@[ @1, @4 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@1]), (@[ @2, @3 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@2]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@3]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@4]), (@[ @5 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@5]), (@[ @6 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@6]), (@[])); +} + +- (void)testPopulateChildren2 +{ + // Same as testPopulateChildren, but with reversed PartsLists + NSDictionary * endpoints = [self indexEndpoints:@[ + MakeEndpoint(@0, @[ @6, @5, @4, @3, @2, @1 ]), // full-family pattern + MakeEndpoint(@1, @[ @3, @2 ]), + MakeEndpoint(@2, @[]), + MakeEndpoint(@3, @[]), + MakeEndpoint(@4, @[ @6, @5 ]), // full-family pattern + MakeEndpoint(@5, @[ @6 ]), + MakeEndpoint(@6, @[]), + ]]; + XCTAssertTrue([MTREndpointInfo populateChildrenForEndpoints:endpoints]); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@0]), (@[ @1, @4 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@1]), (@[ @2, @3 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@2]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@3]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@4]), (@[ @5 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@5]), (@[ @6 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@6]), (@[])); +} + +- (void)testPopulateChildrenRootOnly +{ + NSDictionary * endpoints = [self indexEndpoints:@[ + MakeEndpoint(@0, @[]), + ]]; + XCTAssertTrue([MTREndpointInfo populateChildrenForEndpoints:endpoints]); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@0]), (@[])); +} + +- (void)testPopulateChildrenInvalidCompositionCycle +{ + NSDictionary * endpoints = [self indexEndpoints:@[ + MakeEndpoint(@0, @[ @1, @2, @3, @4, @5, @6 ]), // full-family pattern + MakeEndpoint(@1, @[ @2, @3 ]), + MakeEndpoint(@2, @[]), + MakeEndpoint(@3, @[]), + MakeEndpoint(@4, @[ @5, @6 ]), // full-family pattern + MakeEndpoint(@5, @[ @6 ]), + MakeEndpoint(@6, @[ @4 ]), // not valid per spec: cycle 4 -> 5 -> 6 -> 4 + ]]; + XCTAssertFalse([MTREndpointInfo populateChildrenForEndpoints:endpoints]); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@0]), (@[ @1, @4 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@1]), (@[ @2, @3 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@2]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@3]), (@[])); + // We make no promises about child lists for endpoints involved in a cycle +} + +- (void)testPopulateChildrenInvalidNonTree +{ + NSDictionary * endpoints = [self indexEndpoints:@[ + MakeEndpoint(@0, @[ @1, @2, @3, @4, @5, @6 ]), // full-family pattern + MakeEndpoint(@1, @[ @2, @3, @6 ]), + MakeEndpoint(@2, @[]), + MakeEndpoint(@3, @[]), + MakeEndpoint(@4, @[ @5, @6 ]), // full-family pattern + MakeEndpoint(@5, @[ @6 ]), + MakeEndpoint(@6, @[]), // not valid per spec: 6 is a child of both 1 and 5 + ]]; + // Note: Not asserting a false return value here, this scenario is currently not detected. + [MTREndpointInfo populateChildrenForEndpoints:endpoints]; + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@0]), (@[ @1, @4 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@2]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@3]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@4]), (@[ @5 ])); + // Endpoint 6 has multiple parents, so we make no guarantees where (or if) it shows up + XCTAssertEqualObjects(Exclude(ChildEndpointIDs(endpoints[@1]), @6), (@[ @2, @3 ])); + XCTAssertEqualObjects(Exclude(ChildEndpointIDs(endpoints[@5]), @6), (@[])); +} + +- (void)testEqualityAndCopying +{ + MTRDeviceTypeRevision * doorLock = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@0x0A revision:@1]; + MTRDeviceTypeRevision * rootNode = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@0x16 revision:@1]; + MTREndpointInfo * a1 = [[MTREndpointInfo alloc] initWithEndpointID:@1 deviceTypes:@[ rootNode ] partsList:@[]]; + XCTAssertTrue([a1 isEqual:a1]); + XCTAssertTrue([a1 isEqual:[a1 copy]]); + XCTAssertFalse([a1 isEqual:nil]); + XCTAssertFalse([a1 isEqual:@"hello"]); + MTREndpointInfo * a2 = [[MTREndpointInfo alloc] initWithEndpointID:@1 deviceTypes:@[ rootNode ] partsList:@[]]; + XCTAssertTrue([a1 isEqual:a2]); + XCTAssertTrue([a2 isEqual:a1]); + XCTAssertEqual(a1.hash, a2.hash); + MTREndpointInfo * b = [[MTREndpointInfo alloc] initWithEndpointID:@1 deviceTypes:@[ rootNode ] partsList:@[ @2 ]]; + XCTAssertFalse([a1 isEqual:b]); + XCTAssertFalse([b isEqual:a1]); + MTREndpointInfo * c = [[MTREndpointInfo alloc] initWithEndpointID:@1 deviceTypes:@[ doorLock ] partsList:@[]]; + XCTAssertFalse([a1 isEqual:c]); + XCTAssertFalse([c isEqual:a1]); + MTREndpointInfo * d = [[MTREndpointInfo alloc] initWithEndpointID:@2 deviceTypes:@[ rootNode ] partsList:@[]]; + XCTAssertFalse([a1 isEqual:d]); + XCTAssertFalse([d isEqual:a1]); +} + +- (void)testSecureCoding +{ + NSDictionary * endpoints = [self indexEndpoints:@[ + MakeEndpoint(@0, @[ @1, @2, @3, @4, @5, @6 ]), // full-family pattern + MakeEndpoint(@1, @[ @2, @3 ]), + MakeEndpoint(@2, @[]), + MakeEndpoint(@3, @[]), + MakeEndpoint(@4, @[ @5, @6 ]), // full-family pattern + MakeEndpoint(@5, @[ @6 ]), + MakeEndpoint(@6, @[]), + ]]; + XCTAssertTrue([MTREndpointInfo populateChildrenForEndpoints:endpoints]); + + NSData * data = [NSKeyedArchiver archivedDataWithRootObject:endpoints.allValues requiringSecureCoding:YES error:NULL]; + NSArray * decodedEndpoints = [NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:MTREndpointInfo.class fromData:data error:NULL]; + + XCTAssertNotNil(decodedEndpoints); + XCTAssertEqualObjects(decodedEndpoints, endpoints.allValues); + + // Deeply compare by hand as well, `children` is not checked by isEqual: + [decodedEndpoints enumerateObjectsUsingBlock:^(MTREndpointInfo * decoded, NSUInteger idx, BOOL * stop) { + MTREndpointInfo * original = endpoints.allValues[idx]; + XCTAssertTrue([decoded isEqual:original]); + XCTAssertEqualObjects(decoded.endpointID, original.endpointID); + XCTAssertEqualObjects(decoded.deviceTypes, original.deviceTypes); + XCTAssertEqualObjects(decoded.partsList, original.partsList); + XCTAssertEqualObjects(decoded.children, original.children); + }]; +} + +@end diff --git a/src/darwin/Framework/CHIPTests/MTRPairingTests.m b/src/darwin/Framework/CHIPTests/MTRPairingTests.m index 675c04156e6..e0bac5f2a74 100644 --- a/src/darwin/Framework/CHIPTests/MTRPairingTests.m +++ b/src/darwin/Framework/CHIPTests/MTRPairingTests.m @@ -1,6 +1,5 @@ -/* - * - * Copyright (c) 2022 Project CHIP Authors +/** + * Copyright (c) 2022-2024 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. @@ -107,6 +106,7 @@ @interface MTRPairingTestControllerDelegate : NSObject attestationDelegate; @property (nonatomic, nullable) NSNumber * failSafeExtension; +@property (nonatomic) BOOL shouldReadEndpointInformation; @property (nullable) NSError * commissioningCompleteError; @end @@ -131,6 +131,7 @@ - (void)controller:(MTRDeviceController *)controller commissioningSessionEstabli __auto_type * params = [[MTRCommissioningParameters alloc] init]; params.deviceAttestationDelegate = self.attestationDelegate; params.failSafeTimeout = self.failSafeExtension; + params.readEndpointInformation = self.shouldReadEndpointInformation; NSError * commissionError = nil; XCTAssertTrue([controller commissionNodeWithID:@(sDeviceId) commissioningParams:params error:&commissionError], @@ -139,6 +140,40 @@ - (void)controller:(MTRDeviceController *)controller commissioningSessionEstabli // Keep waiting for onCommissioningComplete } +- (void)controller:(MTRDeviceController *)controller readCommissioneeInfo:(MTRCommissioneeInfo *)info +{ + XCTAssertNotNil(info.productIdentity); + XCTAssertEqualObjects(info.productIdentity.vendorID, /* Test Vendor 1 */ @0xFFF1); + + if (self.shouldReadEndpointInformation) { + XCTAssertNotNil(info.endpointsById); + XCTAssertNotNil(info.rootEndpoint); + XCTAssertGreaterThanOrEqual(info.rootEndpoint.children.count, 1); // at least one application endpoint + for (MTREndpointInfo * endpoint in info.endpointsById.allValues) { + XCTAssertGreaterThanOrEqual(endpoint.deviceTypes.count, 1); + XCTAssertNotNil(endpoint.children); + XCTAssertNotNil(endpoint.partsList); + XCTAssertGreaterThanOrEqual(endpoint.partsList.count, endpoint.children.count); + for (MTREndpointInfo * child in endpoint.children) { + XCTAssertTrue([endpoint.partsList containsObject:child.endpointID]); + } + } + + // There is currently no convenient way to initialize an MTRCommissioneeInfo + // object from basic ObjC data types, so we do some unit testing here. + NSData * data = [NSKeyedArchiver archivedDataWithRootObject:info requiringSecureCoding:YES error:NULL]; + MTRCommissioneeInfo * decoded = [NSKeyedUnarchiver unarchivedObjectOfClass:MTRCommissioneeInfo.class fromData:data error:NULL]; + XCTAssertNotNil(decoded); + XCTAssertTrue([decoded isEqual:info]); + XCTAssertEqualObjects(decoded.productIdentity, info.productIdentity); + XCTAssertEqualObjects(decoded.endpointsById, info.endpointsById); + XCTAssertEqualObjects(decoded.rootEndpoint.children, info.rootEndpoint.children); + } else { + XCTAssertNil(info.endpointsById); + XCTAssertNil(info.rootEndpoint); + } +} + - (void)controller:(MTRDeviceController *)controller commissioningComplete:(NSError * _Nullable)error { self.commissioningCompleteError = error; @@ -152,20 +187,20 @@ @interface MTRPairingTestMonitoringControllerDelegate : NSObject ", self, MTR_YES_NO(_statusUpdateCalled), MTR_YES_NO(_commissioningSessionEstablishmentDoneCalled), MTR_YES_NO(_commissioningCompleteCalled), MTR_YES_NO(_readCommissioningInfoCalled)]; + return [NSString stringWithFormat:@"", self, MTR_YES_NO(_statusUpdateCalled), MTR_YES_NO(_commissioningSessionEstablishmentDoneCalled), MTR_YES_NO(_commissioningCompleteCalled), MTR_YES_NO(_readCommissioneeInfoCalled)]; } - (void)_checkIfAllCallbacksCalled { if (self.allCallbacksCalledExpectation) { - if (self.statusUpdateCalled && self.commissioningSessionEstablishmentDoneCalled && self.commissioningCompleteCalled && self.readCommissioningInfoCalled) { + if (self.statusUpdateCalled && self.commissioningSessionEstablishmentDoneCalled && self.commissioningCompleteCalled && self.readCommissioneeInfoCalled) { [self.allCallbacksCalledExpectation fulfill]; self.allCallbacksCalledExpectation = nil; } @@ -193,11 +228,12 @@ - (void)controller:(MTRDeviceController *)controller [self _checkIfAllCallbacksCalled]; } -- (void)controller:(MTRDeviceController *)controller readCommissioningInfo:(MTRProductIdentity *)info +- (void)controller:(MTRDeviceController *)controller readCommissioneeInfo:(MTRCommissioneeInfo *)info { - self.readCommissioningInfoCalled = YES; + self.readCommissioneeInfoCalled = YES; [self _checkIfAllCallbacksCalled]; } + @end @interface MTRPairingTests : MTRTestCase @@ -318,7 +354,7 @@ - (void)doPairingTestWithAttestationDelegate:(id)a XCTAssertTrue(monitoringControllerDelegate.statusUpdateCalled); XCTAssertTrue(monitoringControllerDelegate.commissioningSessionEstablishmentDoneCalled); XCTAssertTrue(monitoringControllerDelegate.commissioningCompleteCalled); - XCTAssertTrue(monitoringControllerDelegate.readCommissioningInfoCalled); + XCTAssertTrue(monitoringControllerDelegate.readCommissioneeInfoCalled); [sController removeDeviceControllerDelegate:monitoringControllerDelegate]; } @@ -454,4 +490,29 @@ - (void)test008_pairingAfterCancellation_DeviceAttestationVerification XCTAssertTrue(delegateCalled); } +- (void)test009_PairWithReadingEndpointInformation +{ + [self startServerApp]; + + XCTestExpectation * expectation = [self expectationWithDescription:@"Commissioning Complete"]; + __auto_type * controllerDelegate = [[MTRPairingTestControllerDelegate alloc] initWithExpectation:expectation + attestationDelegate:nil + failSafeExtension:nil]; + + // Endpoint info is validated by MTRPairingTestControllerDelegate + controllerDelegate.shouldReadEndpointInformation = YES; + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.pairing", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); + [sController setDeviceControllerDelegate:controllerDelegate queue:callbackQueue]; + self.controllerDelegate = controllerDelegate; + + NSError * error; + __auto_type * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:kOnboardingPayload error:&error]; + XCTAssertTrue([sController setupCommissioningSessionWithPayload:payload newNodeID:@(++sDeviceId) error:&error]); + XCTAssertNil(error); + + [self waitForExpectations:@[ expectation ] timeout:kPairingTimeoutInSeconds]; + XCTAssertNil(controllerDelegate.commissioningCompleteError); +} + @end diff --git a/src/darwin/Framework/CHIPTests/MTRProductIdentityTests.m b/src/darwin/Framework/CHIPTests/MTRProductIdentityTests.m new file mode 100644 index 00000000000..e7b7795a873 --- /dev/null +++ b/src/darwin/Framework/CHIPTests/MTRProductIdentityTests.m @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2024 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 + +@interface MTRProductIdentityTests : XCTestCase + +@end + +@implementation MTRProductIdentityTests + +- (void)testEqualityAndCopying +{ + MTRProductIdentity * a1 = [[MTRProductIdentity alloc] initWithVendorID:@1 productID:@1]; + XCTAssertNotNil(a1); + XCTAssertTrue([a1 isEqual:a1]); + XCTAssertTrue([a1 isEqual:[a1 copy]]); + XCTAssertFalse([a1 isEqual:nil]); + XCTAssertFalse([a1 isEqual:@1]); + + MTRProductIdentity * a2 = [[MTRProductIdentity alloc] initWithVendorID:@1 productID:@1]; + XCTAssertNotNil(a2); + XCTAssertTrue([a1 isEqual:a2]); + XCTAssertTrue([a2 isEqual:a1]); + + MTRProductIdentity * b = [[MTRProductIdentity alloc] initWithVendorID:@1 productID:@555]; + XCTAssertNotNil(b); + XCTAssertFalse([b isEqual:a1]); + XCTAssertFalse([a1 isEqual:b]); + + MTRProductIdentity * c = [[MTRProductIdentity alloc] initWithVendorID:@555 productID:@1]; + XCTAssertNotNil(c); + XCTAssertFalse([c isEqual:a1]); + XCTAssertFalse([a1 isEqual:c]); + XCTAssertFalse([c isEqual:b]); + XCTAssertFalse([b isEqual:c]); +} + +- (void)testSecureCoding +{ + MTRProductIdentity * a = [[MTRProductIdentity alloc] initWithVendorID:@123 productID:@42]; + NSData * data = [NSKeyedArchiver archivedDataWithRootObject:a requiringSecureCoding:YES error:NULL]; + MTRProductIdentity * b = [NSKeyedUnarchiver unarchivedObjectOfClass:MTRProductIdentity.class fromData:data error:NULL]; + XCTAssertNotNil(b); + XCTAssertEqualObjects(b.vendorID, a.vendorID); + XCTAssertEqualObjects(b.productID, a.productID); + XCTAssertTrue([b isEqual:a]); +} + +@end diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj index 77cc1c03187..61f5bdf3d3c 100644 --- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj @@ -104,6 +104,9 @@ 3CF134AB289D8DF70017A19E /* MTRDeviceAttestationInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CF134AA289D8DF70017A19E /* MTRDeviceAttestationInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3CF134AD289D8E570017A19E /* MTRDeviceAttestationInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3CF134AC289D8E570017A19E /* MTRDeviceAttestationInfo.mm */; }; 3CF134AF289D90FF0017A19E /* MTROperationalCertificateIssuer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CF134AE289D90FF0017A19E /* MTROperationalCertificateIssuer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3D010DCF2D408FA300CFFA02 /* MTRCommissioneeInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D010DCE2D408FA300CFFA02 /* MTRCommissioneeInfo.mm */; }; + 3D010DD02D408FA300CFFA02 /* MTRCommissioneeInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D010DCD2D408FA300CFFA02 /* MTRCommissioneeInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3D010DD22D4091CC00CFFA02 /* MTRCommissioneeInfo_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D010DD12D4091C800CFFA02 /* MTRCommissioneeInfo_Internal.h */; }; 3D0C484B29DA4FA0006D811F /* MTRErrorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D0C484A29DA4FA0006D811F /* MTRErrorTests.m */; }; 3D3928D72BBCEA3D00CDEBB2 /* MTRAvailabilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D3928D62BBCEA3D00CDEBB2 /* MTRAvailabilityTests.m */; settings = {COMPILER_FLAGS = "-UMTR_NO_AVAILABILITY -Wno-unguarded-availability-new"; }; }; 3D4733AF2BDF1B80003DC19B /* MTRSetupPayloadTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D4733AE2BDF1B80003DC19B /* MTRSetupPayloadTests.m */; }; @@ -116,6 +119,16 @@ 3D843717294979230070D20A /* MTRClusters_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D843715294979230070D20A /* MTRClusters_Internal.h */; }; 3D843756294AD25A0070D20A /* MTRCertificateInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D843754294AD25A0070D20A /* MTRCertificateInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3D843757294AD25A0070D20A /* MTRCertificateInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D843755294AD25A0070D20A /* MTRCertificateInfo.mm */; }; + 3D9F2FBE2D10DB4F003CA2BB /* MTRProductIdentity.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D9F2FBC2D10DB4F003CA2BB /* MTRProductIdentity.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3D9F2FBF2D10DB4F003CA2BB /* MTRProductIdentity.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D9F2FBD2D10DB4F003CA2BB /* MTRProductIdentity.mm */; }; + 3D9F2FC12D10DCA2003CA2BB /* MTRProductIdentityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D9F2FC02D10DCA2003CA2BB /* MTRProductIdentityTests.m */; }; + 3D9F2FC32D10E207003CA2BB /* MTRDeviceTypeRevisionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D9F2FC22D10E207003CA2BB /* MTRDeviceTypeRevisionTests.m */; }; + 3D9F2FC62D10EA11003CA2BB /* MTREndpointInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D9F2FC52D10EA11003CA2BB /* MTREndpointInfo.mm */; }; + 3D9F2FC72D10EA11003CA2BB /* MTREndpointInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D9F2FC42D10EA11003CA2BB /* MTREndpointInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3D9F2FC92D1105BA003CA2BB /* MTREndpointInfo_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D9F2FC82D1105BA003CA2BB /* MTREndpointInfo_Internal.h */; }; + 3D9F2FCE2D112295003CA2BB /* MTRAttributeTLVValueDecoder_Internal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D9F2FCD2D112295003CA2BB /* MTRAttributeTLVValueDecoder_Internal.mm */; }; + 3D9F2FD02D11FE90003CA2BB /* MTREndpointInfoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D9F2FCF2D11FE90003CA2BB /* MTREndpointInfoTests.m */; }; + 3D9F2FD22D11FF27003CA2BB /* MTREndpointInfo_Test.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D9F2FD12D11FF27003CA2BB /* MTREndpointInfo_Test.h */; }; 3DA1A3552ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DA1A3522ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.h */; }; 3DA1A3562ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3DA1A3532ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.mm */; }; 3DA1A3582ABABF6A004F0BB9 /* MTRAsyncWorkQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DA1A3572ABABF69004F0BB9 /* MTRAsyncWorkQueueTests.m */; }; @@ -580,6 +593,9 @@ 3CF134AA289D8DF70017A19E /* MTRDeviceAttestationInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRDeviceAttestationInfo.h; sourceTree = ""; }; 3CF134AC289D8E570017A19E /* MTRDeviceAttestationInfo.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRDeviceAttestationInfo.mm; sourceTree = ""; }; 3CF134AE289D90FF0017A19E /* MTROperationalCertificateIssuer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTROperationalCertificateIssuer.h; sourceTree = ""; }; + 3D010DCD2D408FA300CFFA02 /* MTRCommissioneeInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRCommissioneeInfo.h; sourceTree = ""; }; + 3D010DCE2D408FA300CFFA02 /* MTRCommissioneeInfo.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRCommissioneeInfo.mm; sourceTree = ""; }; + 3D010DD12D4091C800CFFA02 /* MTRCommissioneeInfo_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRCommissioneeInfo_Internal.h; sourceTree = ""; }; 3D0C484A29DA4FA0006D811F /* MTRErrorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MTRErrorTests.m; sourceTree = ""; }; 3D3928D62BBCEA3D00CDEBB2 /* MTRAvailabilityTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MTRAvailabilityTests.m; sourceTree = ""; }; 3D4733AE2BDF1B80003DC19B /* MTRSetupPayloadTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRSetupPayloadTests.m; sourceTree = ""; }; @@ -611,6 +627,16 @@ 3D84372F294984AF0070D20A /* command_completion_type.zapt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = command_completion_type.zapt; sourceTree = ""; }; 3D843754294AD25A0070D20A /* MTRCertificateInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRCertificateInfo.h; sourceTree = ""; }; 3D843755294AD25A0070D20A /* MTRCertificateInfo.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRCertificateInfo.mm; sourceTree = ""; }; + 3D9F2FBC2D10DB4F003CA2BB /* MTRProductIdentity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRProductIdentity.h; sourceTree = ""; }; + 3D9F2FBD2D10DB4F003CA2BB /* MTRProductIdentity.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRProductIdentity.mm; sourceTree = ""; }; + 3D9F2FC02D10DCA2003CA2BB /* MTRProductIdentityTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MTRProductIdentityTests.m; sourceTree = ""; }; + 3D9F2FC22D10E207003CA2BB /* MTRDeviceTypeRevisionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MTRDeviceTypeRevisionTests.m; sourceTree = ""; }; + 3D9F2FC42D10EA11003CA2BB /* MTREndpointInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTREndpointInfo.h; sourceTree = ""; }; + 3D9F2FC52D10EA11003CA2BB /* MTREndpointInfo.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTREndpointInfo.mm; sourceTree = ""; }; + 3D9F2FC82D1105BA003CA2BB /* MTREndpointInfo_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTREndpointInfo_Internal.h; sourceTree = ""; }; + 3D9F2FCD2D112295003CA2BB /* MTRAttributeTLVValueDecoder_Internal.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRAttributeTLVValueDecoder_Internal.mm; sourceTree = ""; }; + 3D9F2FCF2D11FE90003CA2BB /* MTREndpointInfoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MTREndpointInfoTests.m; sourceTree = ""; }; + 3D9F2FD12D11FF27003CA2BB /* MTREndpointInfo_Test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTREndpointInfo_Test.h; sourceTree = ""; }; 3DA1A3522ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRAsyncWorkQueue.h; sourceTree = ""; }; 3DA1A3532ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRAsyncWorkQueue.mm; sourceTree = ""; }; 3DA1A3572ABABF69004F0BB9 /* MTRAsyncWorkQueueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRAsyncWorkQueueTests.m; sourceTree = ""; }; @@ -802,6 +828,7 @@ 75B765BF2A1D70F80014719B /* MTRAttributeSpecifiedCheck-src.zapt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "MTRAttributeSpecifiedCheck-src.zapt"; sourceTree = ""; }; 75B765C02A1D71BC0014719B /* MTRAttributeSpecifiedCheck.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRAttributeSpecifiedCheck.h; sourceTree = ""; }; 75B765C22A1D82D30014719B /* MTRAttributeSpecifiedCheck.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRAttributeSpecifiedCheck.mm; sourceTree = ""; }; + 7CD490112D378CF4007F9145 /* Matter.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = Matter.modulemap; sourceTree = ""; }; 8874C1312B69C7060084BEFD /* MTRMetricsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRMetricsTests.m; sourceTree = ""; }; 88E07D602B9A89A4005FD53E /* MTRMetricKeys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRMetricKeys.h; sourceTree = ""; }; 88E6C9432B6334ED001A1FE0 /* MTRMetrics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRMetrics.h; sourceTree = ""; }; @@ -1339,8 +1366,6 @@ 51D0B1362B618CC6006E3511 /* MTRServerAttribute.h */, 51D0B1372B618CC6006E3511 /* MTRServerAttribute.mm */, 514C79FF2B64223400DD6D7B /* MTRServerAttribute_Internal.h */, - 51D0B13A2B61B2F2006E3511 /* MTRDeviceTypeRevision.h */, - 51D0B13B2B61B2F2006E3511 /* MTRDeviceTypeRevision.mm */, 51D0B1262B617246006E3511 /* MTRServerEndpoint.h */, 51D0B1252B617246006E3511 /* MTRServerEndpoint.mm */, 514C7A002B64223400DD6D7B /* MTRServerEndpoint_Internal.h */, @@ -1415,6 +1440,7 @@ B202528F2459E34F00F97062 /* CHIP */ = { isa = PBXGroup; children = ( + 7CD490112D378CF4007F9145 /* Matter.modulemap */, CF3B63CB2CA31E71003C1C87 /* MTROTAImageTransferHandler.h */, CF3B63CD2CA31E71003C1C87 /* MTROTAImageTransferHandler.mm */, CF3B63CC2CA31E71003C1C87 /* MTROTAUnsolicitedBDXMessageHandler.h */, @@ -1451,6 +1477,7 @@ 27A53C1527FBC6920053F131 /* MTRAttestationTrustStoreBridge.h */, 27A53C1627FBC6920053F131 /* MTRAttestationTrustStoreBridge.mm */, 513DDB852761F69300DAA01A /* MTRAttributeTLVValueDecoder_Internal.h */, + 3D9F2FCD2D112295003CA2BB /* MTRAttributeTLVValueDecoder_Internal.mm */, 75B765C02A1D71BC0014719B /* MTRAttributeSpecifiedCheck.h */, 51EF279E2A2A3EB100E33F75 /* MTRBackwardsCompatShims.h */, 510470FA2A2F7DF60053EA7E /* MTRBackwardsCompatShims.mm */, @@ -1475,6 +1502,9 @@ 5178E67F2AE098510069DF72 /* MTRCommandTimedCheck.h */, 51FE723E2ACDEF3E00437032 /* MTRCommandPayloadExtensions_Internal.h */, 5178E6802AE098520069DF72 /* MTRCommissionableBrowserResult_Internal.h */, + 3D010DCD2D408FA300CFFA02 /* MTRCommissioneeInfo.h */, + 3D010DD12D4091C800CFFA02 /* MTRCommissioneeInfo_Internal.h */, + 3D010DCE2D408FA300CFFA02 /* MTRCommissioneeInfo.mm */, 99D466E02798936D0089A18F /* MTRCommissioningParameters.h */, 99AECC7F2798A57E00B6355B /* MTRCommissioningParameters.mm */, 3DFCB32B29678C9500332B35 /* MTRConversion.h */, @@ -1544,6 +1574,12 @@ 51F522692AE70761000C4050 /* MTRDeviceTypeMetadata.h */, 5109E9B22CB8B5DF0006884B /* MTRDeviceType.h */, 5109E9B32CB8B5DF0006884B /* MTRDeviceType.mm */, + 51D0B13A2B61B2F2006E3511 /* MTRDeviceTypeRevision.h */, + 51D0B13B2B61B2F2006E3511 /* MTRDeviceTypeRevision.mm */, + 3D9F2FC42D10EA11003CA2BB /* MTREndpointInfo.h */, + 3D9F2FD12D11FF27003CA2BB /* MTREndpointInfo_Test.h */, + 3D9F2FC82D1105BA003CA2BB /* MTREndpointInfo_Internal.h */, + 3D9F2FC52D10EA11003CA2BB /* MTREndpointInfo.mm */, 5129BCFC26A9EE3300122DDF /* MTRError.h */, B2E0D7AB245B0B5C003C5B48 /* MTRError_Internal.h */, 51578AEA2D0B9DC0001716FF /* MTRError_Testable.h */, @@ -1574,6 +1610,8 @@ 998F287026D56940001846C6 /* MTRP256KeypairBridge.mm */, 2C8C8FBD253E0C2100797F05 /* MTRPersistentStorageDelegateBridge.h */, 2C8C8FBF253E0C2100797F05 /* MTRPersistentStorageDelegateBridge.mm */, + 3D9F2FBC2D10DB4F003CA2BB /* MTRProductIdentity.h */, + 3D9F2FBD2D10DB4F003CA2BB /* MTRProductIdentity.mm */, 514654482A72F9DF00904E61 /* MTRDemuxingStorage.mm */, 5146544A2A72F9F500904E61 /* MTRDemuxingStorage.h */, 51E95DF92A78443C00A434F0 /* MTRSessionResumptionStorageBridge.h */, @@ -1623,7 +1661,9 @@ 5AE6D4E327A99041001F2493 /* MTRDeviceTests.m */, 75B326A12BCF12E900E17C4E /* MTRDeviceConnectivityMonitorTests.m */, 5109E9B62CB8B83D0006884B /* MTRDeviceTypeTests.m */, + 3D9F2FC22D10E207003CA2BB /* MTRDeviceTypeRevisionTests.m */, 51D9CB0A2BA37DCE0049D6DB /* MTRDSTOffsetTests.m */, + 3D9F2FCF2D11FE90003CA2BB /* MTREndpointInfoTests.m */, 3D0C484A29DA4FA0006D811F /* MTRErrorTests.m */, 51578AE82D0B9B1D001716FF /* MTRErrorMappingTests.m */, 5173A47829C0E82300F67F48 /* MTRFabricInfoTests.m */, @@ -1632,6 +1672,7 @@ 5142E39729D377F000A206F0 /* MTROTAProviderTests.m */, 51742B4D29CB6B88009974FE /* MTRPairingTests.m */, 51E95DF72A78110900A434F0 /* MTRPerControllerStorageTests.m */, + 3D9F2FC02D10DCA2003CA2BB /* MTRProductIdentityTests.m */, 51D0B1292B61766F006E3511 /* MTRServerEndpointTests.m */, 3D4733AE2BDF1B80003DC19B /* MTRSetupPayloadTests.m */, 519498312A25581C00B3BABE /* MTRSetupPayloadInitializationTests.m */, @@ -1829,6 +1870,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 3D9F2FBE2D10DB4F003CA2BB /* MTRProductIdentity.h in Headers */, 51D0B1282B617246006E3511 /* MTRServerEndpoint.h in Headers */, 51565CB62A7B0D6600469F18 /* MTRDeviceControllerParameters.h in Headers */, 51565CB42A7AD78D00469F18 /* MTRDeviceControllerStorageDelegate.h in Headers */, @@ -1905,6 +1947,7 @@ 3D843756294AD25A0070D20A /* MTRCertificateInfo.h in Headers */, 7596A83E28751220004DAE0E /* MTRBaseClusters_Internal.h in Headers */, 997DED182695344800975E97 /* MTRThreadOperationalDataset.h in Headers */, + 3D010DD02D408FA300CFFA02 /* MTRCommissioneeInfo.h in Headers */, 9956064426420367000C28DE /* MTRSetupPayload_Internal.h in Headers */, 27A53C1727FBC6920053F131 /* MTRAttestationTrustStoreBridge.h in Headers */, 5A830D6C27CFCF590053B85D /* MTRDeviceControllerOverXPC_Internal.h in Headers */, @@ -1913,6 +1956,7 @@ 514C7A012B64223400DD6D7B /* MTRServerAttribute_Internal.h in Headers */, 3DFCB32C29678C9500332B35 /* MTRConversion.h in Headers */, 5A60370827EA1FF60020DB79 /* MTRClusterStateCacheContainer+XPC.h in Headers */, + 3D9F2FC92D1105BA003CA2BB /* MTREndpointInfo_Internal.h in Headers */, 5ACDDD7E27CD3F3A00EFD68A /* MTRClusterStateCacheContainer_Internal.h in Headers */, 5136661328067D550025EDAE /* MTRDeviceController_Internal.h in Headers */, 998F286D26D55E10001846C6 /* MTRKeypair.h in Headers */, @@ -1941,6 +1985,7 @@ 9B231B042C62EF650030EB37 /* (null) in Headers */, 515BE4ED2B72C0C5000BC1FD /* MTRUnfairLock.h in Headers */, 998F286F26D55EC5001846C6 /* MTRP256KeypairBridge.h in Headers */, + 3D9F2FC72D10EA11003CA2BB /* MTREndpointInfo.h in Headers */, CF3B63CF2CA31E71003C1C87 /* MTROTAImageTransferHandler.h in Headers */, 2C222ADF255C811800E446B9 /* MTRBaseDevice_Internal.h in Headers */, 514C7A022B64223400DD6D7B /* MTRServerEndpoint_Internal.h in Headers */, @@ -1949,6 +1994,7 @@ 3D843713294977000070D20A /* NSDataSpanConversion.h in Headers */, 9B5CCB5D2C6EC890009DD99B /* MTRDevice_XPC.h in Headers */, 991DC08B247704DC00C13860 /* MTRLogging_Internal.h in Headers */, + 3D010DD22D4091CC00CFFA02 /* MTRCommissioneeInfo_Internal.h in Headers */, 51FE723F2ACDEF3E00437032 /* MTRCommandPayloadExtensions_Internal.h in Headers */, 51578AEB2D0B9DC0001716FF /* MTRError_Testable.h in Headers */, 51D0B12E2B6177FD006E3511 /* MTRAccessGrant.h in Headers */, @@ -1958,6 +2004,7 @@ 7592BCF42CBEE98C00EB74A0 /* EmberMetadata.h in Headers */, 7592BCF52CBEE98C00EB74A0 /* CodegenDataModelProvider.h in Headers */, 7596A84828762783004DAE0E /* MTRAsyncCallbackWorkQueue.h in Headers */, + 3D9F2FD22D11FF27003CA2BB /* MTREndpointInfo_Test.h in Headers */, 5A7947E527C0129F00434CF2 /* MTRDeviceController+XPC.h in Headers */, 51E95DFB2A78443C00A434F0 /* MTRSessionResumptionStorageBridge.h in Headers */, B2E0D7B4245B0B5C003C5B48 /* MTRError_Internal.h in Headers */, @@ -2253,6 +2300,7 @@ 7592BCF82CBEE98C00EB74A0 /* EmberMetadata.cpp in Sources */, 7592BCF92CBEE98C00EB74A0 /* CodegenDataModelProvider.cpp in Sources */, 7592BCFA2CBEE98C00EB74A0 /* CodegenDataModelProvider_Write.cpp in Sources */, + 3D010DCF2D408FA300CFFA02 /* MTRCommissioneeInfo.mm in Sources */, 93B2CF9A2B56E45C00E4D187 /* MTRClusterNames.mm in Sources */, 998F287126D56940001846C6 /* MTRP256KeypairBridge.mm in Sources */, 516416012B6B483C00D5CE11 /* MTRIMDispatch.mm in Sources */, @@ -2290,6 +2338,7 @@ CF3B63D12CA31E71003C1C87 /* MTROTAImageTransferHandler.mm in Sources */, 51D0B12F2B617800006E3511 /* MTRAccessGrant.mm in Sources */, 88E6C9482B6334ED001A1FE0 /* MTRMetrics.mm in Sources */, + 3D9F2FBF2D10DB4F003CA2BB /* MTRProductIdentity.mm in Sources */, 1ED276E226C5812A00547A89 /* MTRCluster.mm in Sources */, 9BFE5D512C6D3075007D4319 /* MTRDeviceController_XPC.mm in Sources */, 514A98B12CD98C5E000EF4FD /* MTRAttributeValueWaiter.mm in Sources */, @@ -2306,12 +2355,14 @@ 5ACDDD7D27CD16D200EFD68A /* MTRClusterStateCacheContainer.mm in Sources */, 75B3269E2BCDB9EA00E17C4E /* MTRDeviceConnectivityMonitor.mm in Sources */, 7534D1802CF8CE2000F64654 /* DefaultAttributePersistenceProvider.cpp in Sources */, + 3D9F2FC62D10EA11003CA2BB /* MTREndpointInfo.mm in Sources */, 9B5CCB5C2C6EC890009DD99B /* MTRDevice_XPC.mm in Sources */, 513DDB8A2761F6F900DAA01A /* MTRAttributeTLVValueDecoder.mm in Sources */, 5117DD3829A931AE00FFA1AA /* MTROperationalBrowser.mm in Sources */, 514C79F02B62ADDA00DD6D7B /* descriptor.cpp in Sources */, 5109E9B42CB8B5DF0006884B /* MTRDeviceType.mm in Sources */, 3D843757294AD25A0070D20A /* MTRCertificateInfo.mm in Sources */, + 3D9F2FCE2D112295003CA2BB /* MTRAttributeTLVValueDecoder_Internal.mm in Sources */, 5A7947E427C0129600434CF2 /* MTRDeviceController+XPC.mm in Sources */, 7592BD0B2CC6BCC300EB74A0 /* EmberAttributeDataBuffer.cpp in Sources */, 5A6FEC9027B563D900F25F42 /* MTRDeviceControllerOverXPC.mm in Sources */, @@ -2368,7 +2419,9 @@ 518D3F832AA132DC008E0007 /* MTRTestPerControllerStorage.m in Sources */, 51339B1F2A0DA64D00C798C1 /* MTRCertificateValidityTests.m in Sources */, 5173A47929C0E82300F67F48 /* MTRFabricInfoTests.m in Sources */, + 3D9F2FC12D10DCA2003CA2BB /* MTRProductIdentityTests.m in Sources */, 75B326A22BCF12E900E17C4E /* MTRDeviceConnectivityMonitorTests.m in Sources */, + 3D9F2FD02D11FE90003CA2BB /* MTREndpointInfoTests.m in Sources */, 5143851E2A65885500EDC8E6 /* MTRSwiftPairingTests.swift in Sources */, 75B0D01E2B71B47F002074DD /* MTRDeviceTestDelegate.m in Sources */, 3D0C484B29DA4FA0006D811F /* MTRErrorTests.m in Sources */, @@ -2381,6 +2434,7 @@ 51F9F9D52BF7A9EE00FEA0E2 /* MTRTestCase+ServerAppRunner.m in Sources */, 517BF3F3282B62CB00A8B7DB /* MTRCertificateTests.m in Sources */, 5142E39829D377F000A206F0 /* MTROTAProviderTests.m in Sources */, + 3D9F2FC32D10E207003CA2BB /* MTRDeviceTypeRevisionTests.m in Sources */, 51E0FC102ACBBF230001E197 /* MTRSwiftDeviceTests.swift in Sources */, 3D4733AF2BDF1B80003DC19B /* MTRSetupPayloadTests.m in Sources */, 5109E9B72CB8B83D0006884B /* MTRDeviceTypeTests.m in Sources */, @@ -2654,6 +2708,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LIBRARY_SEARCH_PATHS = "$(TEMP_DIR)/out/lib"; + MODULEMAP_FILE = CHIP/Matter.modulemap; OTHER_CFLAGS = "-fmacro-prefix-map=$(SRCROOT)/CHIP/="; OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", @@ -2823,6 +2878,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LIBRARY_SEARCH_PATHS = "$(TEMP_DIR)/out/lib"; + MODULEMAP_FILE = CHIP/Matter.modulemap; OTHER_CFLAGS = "-fmacro-prefix-map=$(SRCROOT)/CHIP/="; OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", diff --git a/src/data-model-providers/codegen/CodegenDataModelProvider.cpp b/src/data-model-providers/codegen/CodegenDataModelProvider.cpp index 1cf34fcb2c8..a7572fbb032 100644 --- a/src/data-model-providers/codegen/CodegenDataModelProvider.cpp +++ b/src/data-model-providers/codegen/CodegenDataModelProvider.cpp @@ -20,15 +20,18 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -38,208 +41,69 @@ #include #include +#include #include -#include namespace chip { namespace app { -namespace detail { +namespace { -Loop EnumeratorCommandFinder::HandlerCallback(CommandId id) +DataModel::AcceptedCommandEntry AcceptedCommandEntryFor(const ConcreteCommandPath & path) { - switch (mOperation) - { - case Operation::kFindFirst: - mFound = id; - return Loop::Break; - case Operation::kFindExact: - if (mTarget == id) - { - mFound = id; // found it - return Loop::Break; - } - break; - case Operation::kFindNext: - if (mTarget == id) - { - // Once we found the ID, get the first - mOperation = Operation::kFindFirst; - } - break; - } - return Loop::Continue; // keep searching -} + const CommandId commandId = path.mCommandId; -std::optional EnumeratorCommandFinder::FindCommandId(Operation operation, const ConcreteCommandPath & path) -{ - mOperation = operation; - mTarget = path.mCommandId; + DataModel::AcceptedCommandEntry entry; - CommandHandlerInterface * interface = - CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId); + entry.commandId = path.mCommandId; + entry.invokePrivilege = RequiredPrivilege::ForInvokeCommand(path); + entry.flags.Set(DataModel::CommandQualityFlags::kTimed, CommandNeedsTimedInvoke(path.mClusterId, commandId)); + entry.flags.Set(DataModel::CommandQualityFlags::kFabricScoped, CommandIsFabricScoped(path.mClusterId, commandId)); + entry.flags.Set(DataModel::CommandQualityFlags::kLargeMessage, CommandHasLargePayload(path.mClusterId, commandId)); - if (interface == nullptr) - { - return std::nullopt; // no data: no interface - } - - CHIP_ERROR err = (interface->*mCallback)(path, HandlerCallbackFn, this); - if (err == CHIP_ERROR_NOT_IMPLEMENTED) - { - return std::nullopt; // no data provided by the interface - } - - if (err != CHIP_NO_ERROR) - { -#if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING - // Report the error here since we lose actual error. This generally should NOT be possible as CommandHandlerInterface - // usually returns unimplemented or should just work for our use case (our callback never fails) - ChipLogError(DataManagement, "Enumerate error: %" CHIP_ERROR_FORMAT, err.Format()); -#endif - return kInvalidCommandId; - } - - return mFound.value_or(kInvalidCommandId); + return entry; } -} // namespace detail - -using detail::EnumeratorCommandFinder; - -namespace { - -/// Search by device type within a span of EmberAfDeviceType (finds the device type that matches the given -/// DataModel::DeviceTypeEntry) -struct ByDeviceType -{ - using Key = DataModel::DeviceTypeEntry; - using Type = const EmberAfDeviceType; - static Span GetSpan(Span & data) { return data; } - static bool HasKey(const Key & id, const Type & instance) - { - return (instance.deviceId == id.deviceTypeId) && (instance.deviceVersion == id.deviceTypeRevision); - } -}; - -const CommandId * AcceptedCommands(const EmberAfCluster & cluster) +DataModel::ServerClusterEntry ServerClusterEntryFrom(EndpointId endpointId, const EmberAfCluster & cluster) { - return cluster.acceptedCommandList; -} + DataModel::ServerClusterEntry entry; -const CommandId * GeneratedCommands(const EmberAfCluster & cluster) -{ - return cluster.generatedCommandList; -} + entry.clusterId = cluster.clusterId; -/// Load the cluster information into the specified destination -std::variant LoadClusterInfo(const ConcreteClusterPath & path, const EmberAfCluster & cluster) -{ - DataVersion * versionPtr = emberAfDataVersionStorage(path); + DataVersion * versionPtr = emberAfDataVersionStorage(ConcreteClusterPath(endpointId, cluster.clusterId)); if (versionPtr == nullptr) { #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING - ChipLogError(AppServer, "Failed to get data version for %d/" ChipLogFormatMEI, static_cast(path.mEndpointId), + ChipLogError(AppServer, "Failed to get data version for %d/" ChipLogFormatMEI, endpointId, ChipLogValueMEI(cluster.clusterId)); #endif - return CHIP_ERROR_NOT_FOUND; + entry.dataVersion = 0; } - - DataModel::ClusterInfo info(*versionPtr); - // TODO: set entry flags: - // info->flags.Set(ClusterQualityFlags::kDiagnosticsData) - return info; -} - -/// Converts a EmberAfCluster into a ClusterEntry -std::variant ClusterEntryFrom(EndpointId endpointId, const EmberAfCluster & cluster) -{ - ConcreteClusterPath clusterPath(endpointId, cluster.clusterId); - auto info = LoadClusterInfo(clusterPath, cluster); - - if (CHIP_ERROR * err = std::get_if(&info)) + else { - return *err; + entry.dataVersion = *versionPtr; } - if (DataModel::ClusterInfo * infoValue = std::get_if(&info)) - { - return DataModel::ClusterEntry{ - .path = clusterPath, - .info = *infoValue, - }; - } - return CHIP_ERROR_INCORRECT_STATE; -} - -/// Finds the first server cluster entry for the given endpoint data starting at [start_index] -/// -/// Returns an invalid entry if no more server clusters are found -DataModel::ClusterEntry FirstServerClusterEntry(EndpointId endpointId, const EmberAfEndpointType * endpoint, unsigned start_index, - unsigned & found_index) -{ - for (unsigned cluster_idx = start_index; cluster_idx < endpoint->clusterCount; cluster_idx++) - { - const EmberAfCluster & cluster = endpoint->cluster[cluster_idx]; - if (!cluster.IsServer()) - { - continue; - } - - found_index = cluster_idx; - auto entry = ClusterEntryFrom(endpointId, cluster); - - if (DataModel::ClusterEntry * entryValue = std::get_if(&entry)) - { - return *entryValue; - } - -#if CHIP_ERROR_LOGGING && CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING - if (CHIP_ERROR * errValue = std::get_if(&entry)) - { - ChipLogError(AppServer, "Failed to load cluster entry: %" CHIP_ERROR_FORMAT, errValue->Format()); - } - else - { - // Should NOT be possible: entryFrom has only 2 variants - ChipLogError(AppServer, "Failed to load cluster entry, UNKNOWN entry return type"); - } -#endif - } + // TODO: set entry flags: + // entry.flags.Set(ClusterQualityFlags::kDiagnosticsData) - return DataModel::ClusterEntry::kInvalid; + return entry; } -ClusterId FirstClientClusterId(const EmberAfEndpointType * endpoint, unsigned start_index, unsigned & found_index) +DataModel::AttributeEntry AttributeEntryFrom(const ConcreteClusterPath & clusterPath, const EmberAfAttributeMetadata & attribute) { - for (unsigned cluster_idx = start_index; cluster_idx < endpoint->clusterCount; cluster_idx++) - { - const EmberAfCluster & cluster = endpoint->cluster[cluster_idx]; - if (!cluster.IsClient()) - { - continue; - } - - found_index = cluster_idx; - return cluster.clusterId; - } + DataModel::AttributeEntry entry; - return kInvalidClusterId; -} + const ConcreteAttributePath attributePath(clusterPath.mEndpointId, clusterPath.mClusterId, attribute.attributeId); -/// Load the attribute information into the specified destination -/// -/// `info` is assumed to be default-constructed/clear (i.e. this sets flags, but does not reset them). -void LoadAttributeInfo(const ConcreteAttributePath & path, const EmberAfAttributeMetadata & attribute, - DataModel::AttributeInfo * info) -{ - info->readPrivilege = RequiredPrivilege::ForReadAttribute(path); + entry.attributeId = attribute.attributeId; + entry.readPrivilege = RequiredPrivilege::ForReadAttribute(attributePath); if (!attribute.IsReadOnly()) { - info->writePrivilege = RequiredPrivilege::ForWriteAttribute(path); + entry.writePrivilege = RequiredPrivilege::ForWriteAttribute(attributePath); } - info->flags.Set(DataModel::AttributeQualityFlags::kListAttribute, (attribute.attributeType == ZCL_ARRAY_ATTRIBUTE_TYPE)); - info->flags.Set(DataModel::AttributeQualityFlags::kTimed, attribute.MustUseTimedWrite()); + entry.flags.Set(DataModel::AttributeQualityFlags::kListAttribute, (attribute.attributeType == ZCL_ARRAY_ATTRIBUTE_TYPE)); + entry.flags.Set(DataModel::AttributeQualityFlags::kTimed, attribute.MustUseTimedWrite()); // NOTE: we do NOT provide additional info for: // - IsExternal/IsSingleton/IsAutomaticallyPersisted is not used by IM handling @@ -248,34 +112,9 @@ void LoadAttributeInfo(const ConcreteAttributePath & path, const EmberAfAttribut // fixed, source attribution) // TODO: Set additional flags: - // info->flags.Set(DataModel::AttributeQualityFlags::kFabricScoped) - // info->flags.Set(DataModel::AttributeQualityFlags::kFabricSensitive) - // info->flags.Set(DataModel::AttributeQualityFlags::kChangesOmitted) -} - -DataModel::AttributeEntry AttributeEntryFrom(const ConcreteClusterPath & clusterPath, const EmberAfAttributeMetadata & attribute) -{ - DataModel::AttributeEntry entry; - - entry.path = ConcreteAttributePath(clusterPath.mEndpointId, clusterPath.mClusterId, attribute.attributeId); - LoadAttributeInfo(entry.path, attribute, &entry.info); - - return entry; -} - -DataModel::CommandEntry CommandEntryFrom(const ConcreteClusterPath & clusterPath, CommandId clusterCommandId) -{ - DataModel::CommandEntry entry; - entry.path = ConcreteCommandPath(clusterPath.mEndpointId, clusterPath.mClusterId, clusterCommandId); - entry.info.invokePrivilege = RequiredPrivilege::ForInvokeCommand(entry.path); - - entry.info.flags.Set(DataModel::CommandQualityFlags::kTimed, CommandNeedsTimedInvoke(clusterPath.mClusterId, clusterCommandId)); - - entry.info.flags.Set(DataModel::CommandQualityFlags::kFabricScoped, - CommandIsFabricScoped(clusterPath.mClusterId, clusterCommandId)); - - entry.info.flags.Set(DataModel::CommandQualityFlags::kLargeMessage, - CommandHasLargePayload(clusterPath.mClusterId, clusterCommandId)); + // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricScoped) + // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricSensitive) + // entry.flags.Set(DataModel::AttributeQualityFlags::kChangesOmitted) return entry; } @@ -283,111 +122,16 @@ DataModel::CommandEntry CommandEntryFrom(const ConcreteClusterPath & clusterPath // to a common type is probably better. Need to figure out dependencies since // this would make ember return datamodel-provider types. // See: https://github.com/project-chip/connectedhomeip/issues/35889 -std::optional DeviceTypeEntryFromEmber(const EmberAfDeviceType * other) +DataModel::DeviceTypeEntry DeviceTypeEntryFromEmber(const EmberAfDeviceType & other) { - if (other == nullptr) - { - return std::nullopt; - } - return DataModel::DeviceTypeEntry{ - .deviceTypeId = other->deviceId, - .deviceTypeRevision = other->deviceVersion, + .deviceTypeId = other.deviceId, + .deviceTypeRevision = other.deviceVersion, }; } const ConcreteCommandPath kInvalidCommandPath(kInvalidEndpointId, kInvalidClusterId, kInvalidCommandId); -std::optional GetEndpointInfoAtIndex(uint16_t endpointIndex) -{ - VerifyOrReturnValue(emberAfEndpointIndexIsEnabled(endpointIndex), std::nullopt); - EndpointId parent = emberAfParentEndpointFromIndex(endpointIndex); - if (GetCompositionForEndpointIndex(endpointIndex) == EndpointComposition::kFullFamily) - { - return DataModel::EndpointInfo(parent, DataModel::EndpointCompositionPattern::kFullFamily); - } - if (GetCompositionForEndpointIndex(endpointIndex) == EndpointComposition::kTree) - { - return DataModel::EndpointInfo(parent, DataModel::EndpointCompositionPattern::kTree); - } - return std::nullopt; -} - -DataModel::EndpointEntry FirstEndpointEntry(unsigned start_index, uint16_t & found_index) -{ - // find the first enabled index after the start index - const uint16_t lastEndpointIndex = emberAfEndpointCount(); - for (uint16_t endpoint_idx = static_cast(start_index); endpoint_idx < lastEndpointIndex; endpoint_idx++) - { - if (emberAfEndpointIndexIsEnabled(endpoint_idx)) - { - found_index = endpoint_idx; - DataModel::EndpointEntry endpointEntry = DataModel::EndpointEntry::kInvalid; - endpointEntry.id = emberAfEndpointFromIndex(endpoint_idx); - auto endpointInfo = GetEndpointInfoAtIndex(endpoint_idx); - // The endpoint info should have value as this endpoint should be valid at this time - VerifyOrDie(endpointInfo.has_value()); - endpointEntry.info = endpointInfo.value(); - return endpointEntry; - } - } - - // No enabled endpoint found. Give up - return DataModel::EndpointEntry::kInvalid; -} - -bool operator==(const DataModel::Provider::SemanticTag & tagA, const DataModel::Provider::SemanticTag & tagB) -{ - // Label is an optional and nullable value of CharSpan. Optional and Nullable have overload for ==, - // But `==` is deleted for CharSpan. Here we only check whether the string is the same. - if (tagA.label.HasValue() != tagB.label.HasValue()) - { - return false; - } - if (tagA.label.HasValue()) - { - if (tagA.label.Value().IsNull() != tagB.label.Value().IsNull()) - { - return false; - } - if (!tagA.label.Value().IsNull()) - { - if (!tagA.label.Value().Value().data_equal(tagB.label.Value().Value())) - { - return false; - } - } - } - return (tagA.tag == tagB.tag) && (tagA.mfgCode == tagB.mfgCode) && (tagA.namespaceID == tagB.namespaceID); -} - -std::optional FindNextSemanticTagIndex(EndpointId endpoint, const DataModel::Provider::SemanticTag & previous, - unsigned hintWherePreviousMayBe) -{ - DataModel::Provider::SemanticTag hintTag; - // Check whether the hint is the previous tag - if (GetSemanticTagForEndpointAtIndex(endpoint, hintWherePreviousMayBe, hintTag) == CHIP_NO_ERROR) - { - if (previous == hintTag) - { - return std::make_optional(hintWherePreviousMayBe + 1); - } - } - // If the hint is not the previous tag, iterate over all the tags to find the index for the previous tag - unsigned index = 0; - // Ensure that the next index is in the range - while (GetSemanticTagForEndpointAtIndex(endpoint, index + 1, hintTag) == CHIP_NO_ERROR && - GetSemanticTagForEndpointAtIndex(endpoint, index, hintTag) == CHIP_NO_ERROR) - { - if (previous == hintTag) - { - return std::make_optional(index + 1); - } - index++; - } - return std::nullopt; -} - DefaultAttributePersistenceProvider gDefaultAttributePersistence; } // namespace @@ -423,72 +167,6 @@ CHIP_ERROR CodegenDataModelProvider::Startup(DataModel::InteractionModelContext return CHIP_NO_ERROR; } -std::optional CodegenDataModelProvider::EmberCommandListIterator::First(const CommandId * list) -{ - VerifyOrReturnValue(list != nullptr, std::nullopt); - mCurrentList = mCurrentHint = list; - - VerifyOrReturnValue(*mCurrentList != kInvalidCommandId, std::nullopt); - return *mCurrentList; -} - -std::optional CodegenDataModelProvider::EmberCommandListIterator::Next(const CommandId * list, CommandId previousId) -{ - VerifyOrReturnValue(list != nullptr, std::nullopt); - VerifyOrReturnValue(previousId != kInvalidCommandId, std::nullopt); - - if (mCurrentList != list) - { - // invalidate the hint if switching lists... - mCurrentHint = nullptr; - mCurrentList = list; - } - - if ((mCurrentHint == nullptr) || (*mCurrentHint != previousId)) - { - // we did not find a usable hint. Search from the to set the hint - mCurrentHint = mCurrentList; - while ((*mCurrentHint != kInvalidCommandId) && (*mCurrentHint != previousId)) - { - mCurrentHint++; - } - } - - VerifyOrReturnValue(*mCurrentHint == previousId, std::nullopt); - - // hint is valid and can be used immediately - mCurrentHint++; // this is the next value - return (*mCurrentHint == kInvalidCommandId) ? std::nullopt : std::make_optional(*mCurrentHint); -} - -bool CodegenDataModelProvider::EmberCommandListIterator::Exists(const CommandId * list, CommandId toCheck) -{ - VerifyOrReturnValue(list != nullptr, false); - VerifyOrReturnValue(toCheck != kInvalidCommandId, false); - - if (mCurrentList != list) - { - // invalidate the hint if switching lists... - mCurrentHint = nullptr; - mCurrentList = list; - } - - // maybe already positioned correctly - if ((mCurrentHint != nullptr) && (*mCurrentHint == toCheck)) - { - return true; - } - - // move and try to find it - mCurrentHint = mCurrentList; - while ((*mCurrentHint != kInvalidCommandId) && (*mCurrentHint != toCheck)) - { - mCurrentHint++; - } - - return (*mCurrentHint == toCheck); -} - std::optional CodegenDataModelProvider::Invoke(const DataModel::InvokeRequest & request, TLV::TLVReader & input_arguments, CommandHandler * handler) @@ -513,24 +191,37 @@ std::optional CodegenDataModelProvider::Invoke(co return std::nullopt; } -bool CodegenDataModelProvider::EndpointExists(EndpointId endpoint) +CHIP_ERROR CodegenDataModelProvider::Endpoints(DataModel::ListBuilder & builder) { - return (emberAfIndexFromEndpoint(endpoint) != kEmberInvalidEndpointIndex); -} + const uint16_t endpointCount = emberAfEndpointCount(); -std::optional CodegenDataModelProvider::GetEndpointInfo(EndpointId endpoint) -{ - std::optional endpoint_idx = TryFindEndpointIndex(endpoint); - if (endpoint_idx.has_value()) + ReturnErrorOnFailure(builder.EnsureAppendCapacity(endpointCount)); + + for (uint16_t endpointIndex = 0; endpointIndex < endpointCount; endpointIndex++) { - return GetEndpointInfoAtIndex(static_cast(*endpoint_idx)); + if (!emberAfEndpointIndexIsEnabled(endpointIndex)) + { + continue; + } + + DataModel::EndpointEntry entry; + entry.id = emberAfEndpointFromIndex(endpointIndex); + entry.parentId = emberAfParentEndpointFromIndex(endpointIndex); + + switch (GetCompositionForEndpointIndex(endpointIndex)) + { + case EndpointComposition::kFullFamily: + entry.compositionPattern = DataModel::EndpointCompositionPattern::kFullFamily; + break; + case EndpointComposition::kTree: + case EndpointComposition::kInvalid: // should NOT happen, but force compiler to check we validate all versions + entry.compositionPattern = DataModel::EndpointCompositionPattern::kTree; + break; + } + ReturnErrorOnFailure(builder.Append(entry)); } - return std::nullopt; -} -DataModel::EndpointEntry CodegenDataModelProvider::FirstEndpoint() -{ - return FirstEndpointEntry(0, mEndpointIterationHint); + return CHIP_NO_ERROR; } std::optional CodegenDataModelProvider::TryFindEndpointIndex(EndpointId id) const @@ -553,165 +244,75 @@ std::optional CodegenDataModelProvider::TryFindEndpointIndex(EndpointI return std::make_optional(idx); } -DataModel::EndpointEntry CodegenDataModelProvider::NextEndpoint(EndpointId before) -{ - std::optional before_idx = TryFindEndpointIndex(before); - if (!before_idx.has_value()) - { - return DataModel::EndpointEntry::kInvalid; - } - return FirstEndpointEntry(*before_idx + 1, mEndpointIterationHint); -} - -DataModel::ClusterEntry CodegenDataModelProvider::FirstServerCluster(EndpointId endpointId) +CHIP_ERROR CodegenDataModelProvider::ServerClusters(EndpointId endpointId, + DataModel::ListBuilder & builder) { const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId); - VerifyOrReturnValue(endpoint != nullptr, DataModel::ClusterEntry::kInvalid); - VerifyOrReturnValue(endpoint->clusterCount > 0, DataModel::ClusterEntry::kInvalid); - VerifyOrReturnValue(endpoint->cluster != nullptr, DataModel::ClusterEntry::kInvalid); - return FirstServerClusterEntry(endpointId, endpoint, 0, mServerClusterIterationHint); -} + VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND); + VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR); + VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR); -std::optional CodegenDataModelProvider::TryFindClusterIndex(const EmberAfEndpointType * endpoint, ClusterId id, - ClusterSide side) const -{ - const unsigned clusterCount = endpoint->clusterCount; - unsigned hint = side == ClusterSide::kServer ? mServerClusterIterationHint : mClientClusterIterationHint; + ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ true))); - if (hint < clusterCount) + const EmberAfCluster * begin = endpoint->cluster; + const EmberAfCluster * end = endpoint->cluster + endpoint->clusterCount; + for (const EmberAfCluster * cluster = begin; cluster != end; cluster++) { - const EmberAfCluster & cluster = endpoint->cluster[hint]; - if (((side == ClusterSide::kServer) && cluster.IsServer()) || ((side == ClusterSide::kClient) && cluster.IsClient())) - { - if (cluster.clusterId == id) - { - return std::make_optional(hint); - } - } - } - - // linear search, this may be slow - // does NOT use emberAfClusterIndex to not iterate over endpoints as we have - // already found the correct endpoint - for (unsigned cluster_idx = 0; cluster_idx < clusterCount; cluster_idx++) - { - const EmberAfCluster & cluster = endpoint->cluster[cluster_idx]; - if (((side == ClusterSide::kServer) && !cluster.IsServer()) || ((side == ClusterSide::kClient) && !cluster.IsClient())) + if (!cluster->IsServer()) { continue; } - if (cluster.clusterId == id) - { - return std::make_optional(cluster_idx); - } - } - - return std::nullopt; -} - -DataModel::ClusterEntry CodegenDataModelProvider::NextServerCluster(const ConcreteClusterPath & before) -{ - // TODO: This search still seems slow (ember will loop). Should use index hints as long - // as ember API supports it - const EmberAfEndpointType * endpoint = emberAfFindEndpointType(before.mEndpointId); - - VerifyOrReturnValue(endpoint != nullptr, DataModel::ClusterEntry::kInvalid); - VerifyOrReturnValue(endpoint->clusterCount > 0, DataModel::ClusterEntry::kInvalid); - VerifyOrReturnValue(endpoint->cluster != nullptr, DataModel::ClusterEntry::kInvalid); - - std::optional cluster_idx = TryFindClusterIndex(endpoint, before.mClusterId, ClusterSide::kServer); - if (!cluster_idx.has_value()) - { - return DataModel::ClusterEntry::kInvalid; + ReturnErrorOnFailure(builder.Append(ServerClusterEntryFrom(endpointId, *cluster))); } - return FirstServerClusterEntry(before.mEndpointId, endpoint, *cluster_idx + 1, mServerClusterIterationHint); + return CHIP_NO_ERROR; } -std::optional CodegenDataModelProvider::GetServerClusterInfo(const ConcreteClusterPath & path) +CHIP_ERROR CodegenDataModelProvider::Attributes(const ConcreteClusterPath & path, + DataModel::ListBuilder & builder) { const EmberAfCluster * cluster = FindServerCluster(path); - VerifyOrReturnValue(cluster != nullptr, std::nullopt); - - auto info = LoadClusterInfo(path, *cluster); - - if (CHIP_ERROR * err = std::get_if(&info)) - { -#if CHIP_ERROR_LOGGING && CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING - ChipLogError(AppServer, "Failed to load cluster info: %" CHIP_ERROR_FORMAT, err->Format()); -#else - (void) err->Format(); -#endif - return std::nullopt; - } - - return std::make_optional(std::get(info)); -} - -ConcreteClusterPath CodegenDataModelProvider::FirstClientCluster(EndpointId endpointId) -{ - const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId); - VerifyOrReturnValue(endpoint != nullptr, ConcreteClusterPath(endpointId, kInvalidClusterId)); - VerifyOrReturnValue(endpoint->clusterCount > 0, ConcreteClusterPath(endpointId, kInvalidClusterId)); - VerifyOrReturnValue(endpoint->cluster != nullptr, ConcreteClusterPath(endpointId, kInvalidClusterId)); + VerifyOrReturnValue(cluster != nullptr, CHIP_ERROR_NOT_FOUND); + VerifyOrReturnValue(cluster->attributeCount > 0, CHIP_NO_ERROR); + VerifyOrReturnValue(cluster->attributes != nullptr, CHIP_NO_ERROR); - return ConcreteClusterPath(endpointId, FirstClientClusterId(endpoint, 0, mClientClusterIterationHint)); -} + // TODO: if ember would encode data in AttributeEntry form, we could reference things directly + ReturnErrorOnFailure(builder.EnsureAppendCapacity(cluster->attributeCount)); -ConcreteClusterPath CodegenDataModelProvider::NextClientCluster(const ConcreteClusterPath & before) -{ - // TODO: This search still seems slow (ember will loop). Should use index hints as long - // as ember API supports it - const EmberAfEndpointType * endpoint = emberAfFindEndpointType(before.mEndpointId); + Span attributeSpan(cluster->attributes, cluster->attributeCount); - VerifyOrReturnValue(endpoint != nullptr, ConcreteClusterPath(before.mEndpointId, kInvalidClusterId)); - VerifyOrReturnValue(endpoint->clusterCount > 0, ConcreteClusterPath(before.mEndpointId, kInvalidClusterId)); - VerifyOrReturnValue(endpoint->cluster != nullptr, ConcreteClusterPath(before.mEndpointId, kInvalidClusterId)); - - std::optional cluster_idx = TryFindClusterIndex(endpoint, before.mClusterId, ClusterSide::kClient); - if (!cluster_idx.has_value()) + for (auto & attribute : attributeSpan) { - return ConcreteClusterPath(before.mEndpointId, kInvalidClusterId); + ReturnErrorOnFailure(builder.Append(AttributeEntryFrom(path, attribute))); } - return ConcreteClusterPath(before.mEndpointId, FirstClientClusterId(endpoint, *cluster_idx + 1, mClientClusterIterationHint)); + return CHIP_NO_ERROR; } -DataModel::AttributeEntry CodegenDataModelProvider::FirstAttribute(const ConcreteClusterPath & path) +CHIP_ERROR CodegenDataModelProvider::ClientClusters(EndpointId endpointId, DataModel::ListBuilder & builder) { - const EmberAfCluster * cluster = FindServerCluster(path); - - VerifyOrReturnValue(cluster != nullptr, DataModel::AttributeEntry::kInvalid); - VerifyOrReturnValue(cluster->attributeCount > 0, DataModel::AttributeEntry::kInvalid); - VerifyOrReturnValue(cluster->attributes != nullptr, DataModel::AttributeEntry::kInvalid); + const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId); - mAttributeIterationHint = 0; - return AttributeEntryFrom(path, cluster->attributes[0]); -} + VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND); + VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR); + VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR); -std::optional CodegenDataModelProvider::TryFindAttributeIndex(const EmberAfCluster * cluster, AttributeId id) const -{ - const unsigned attributeCount = cluster->attributeCount; - - // attempt to find this based on the embedded hint - if ((mAttributeIterationHint < attributeCount) && (cluster->attributes[mAttributeIterationHint].attributeId == id)) - { - return std::make_optional(mAttributeIterationHint); - } + ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ false))); - // linear search is required. This may be slow - for (unsigned attribute_idx = 0; attribute_idx < attributeCount; attribute_idx++) + const EmberAfCluster * begin = endpoint->cluster; + const EmberAfCluster * end = endpoint->cluster + endpoint->clusterCount; + for (const EmberAfCluster * cluster = begin; cluster != end; cluster++) { - - if (cluster->attributes[attribute_idx].attributeId == id) + if (!cluster->IsClient()) { - return std::make_optional(attribute_idx); + continue; } + ReturnErrorOnFailure(builder.Append(cluster->clusterId)); } - return std::nullopt; + return CHIP_NO_ERROR; } const EmberAfCluster * CodegenDataModelProvider::FindServerCluster(const ConcreteClusterPath & path) @@ -732,134 +333,150 @@ const EmberAfCluster * CodegenDataModelProvider::FindServerCluster(const Concret return cluster; } -CommandId CodegenDataModelProvider::FindCommand(const ConcreteCommandPath & path, detail::EnumeratorCommandFinder & handlerFinder, - detail::EnumeratorCommandFinder::Operation operation, - CodegenDataModelProvider::EmberCommandListIterator & emberIterator, - CommandListGetter commandListGetter) +CHIP_ERROR CodegenDataModelProvider::AcceptedCommands(const ConcreteClusterPath & path, + DataModel::ListBuilder & builder) { - - std::optional handlerCommandId = handlerFinder.FindCommandId(operation, path); - if (handlerCommandId.has_value()) + CommandHandlerInterface * interface = + CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId); + if (interface != nullptr) { - return *handlerCommandId; - } + size_t commandCount = 0; - const EmberAfCluster * cluster = FindServerCluster(path); - VerifyOrReturnValue(cluster != nullptr, kInvalidCommandId); + CHIP_ERROR err = interface->EnumerateAcceptedCommands( + path, + [](CommandId id, void * context) -> Loop { + *reinterpret_cast(context) += 1; + return Loop::Continue; + }, + reinterpret_cast(&commandCount)); - const CommandId * commandList = commandListGetter(*cluster); - - switch (operation) - { - case EnumeratorCommandFinder::Operation::kFindFirst: - return emberIterator.First(commandList).value_or(kInvalidCommandId); - case EnumeratorCommandFinder::Operation::kFindNext: - return emberIterator.Next(commandList, path.mCommandId).value_or(kInvalidCommandId); - case EnumeratorCommandFinder::Operation::kFindExact: - default: - return emberIterator.Exists(commandList, path.mCommandId) ? path.mCommandId : kInvalidCommandId; + if (err == CHIP_NO_ERROR) + { + using EnumerationData = struct + { + ConcreteCommandPath commandPath; + DataModel::ListBuilder * acceptedCommandList; + CHIP_ERROR processingError; + }; + + EnumerationData enumerationData; + enumerationData.commandPath = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId); + enumerationData.processingError = CHIP_NO_ERROR; + enumerationData.acceptedCommandList = &builder; + + ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount)); + + ReturnErrorOnFailure(interface->EnumerateAcceptedCommands( + path, + [](CommandId commandId, void * context) -> Loop { + auto input = reinterpret_cast(context); + input->commandPath.mCommandId = commandId; + CHIP_ERROR appendError = input->acceptedCommandList->Append(AcceptedCommandEntryFor(input->commandPath)); + if (appendError != CHIP_NO_ERROR) + { + input->processingError = appendError; + return Loop::Break; + } + return Loop::Continue; + }, + reinterpret_cast(&enumerationData))); + ReturnErrorOnFailure(enumerationData.processingError); + + // the two invocations MUST return the same sizes. + VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL); + return CHIP_NO_ERROR; + } + VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err); } -} -DataModel::AttributeEntry CodegenDataModelProvider::NextAttribute(const ConcreteAttributePath & before) -{ - const EmberAfCluster * cluster = FindServerCluster(before); - VerifyOrReturnValue(cluster != nullptr, DataModel::AttributeEntry::kInvalid); - VerifyOrReturnValue(cluster->attributeCount > 0, DataModel::AttributeEntry::kInvalid); - VerifyOrReturnValue(cluster->attributes != nullptr, DataModel::AttributeEntry::kInvalid); - - // find the given attribute in the list and then return the next one - std::optional attribute_idx = TryFindAttributeIndex(cluster, before.mAttributeId); - if (!attribute_idx.has_value()) - { - return DataModel::AttributeEntry::kInvalid; - } + const EmberAfCluster * serverCluster = FindServerCluster(path); + VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND); + VerifyOrReturnError(serverCluster->acceptedCommandList != nullptr, CHIP_NO_ERROR); - unsigned next_idx = *attribute_idx + 1; - if (next_idx < cluster->attributeCount) + const chip::CommandId * endOfList = serverCluster->acceptedCommandList; + while (*endOfList != kInvalidCommandId) { - mAttributeIterationHint = next_idx; - return AttributeEntryFrom(before, cluster->attributes[next_idx]); + endOfList++; } + const auto commandCount = static_cast(endOfList - serverCluster->acceptedCommandList); - // iteration complete - return DataModel::AttributeEntry::kInvalid; -} - -std::optional CodegenDataModelProvider::GetAttributeInfo(const ConcreteAttributePath & path) -{ - const EmberAfCluster * cluster = FindServerCluster(path); - - VerifyOrReturnValue(cluster != nullptr, std::nullopt); - VerifyOrReturnValue(cluster->attributeCount > 0, std::nullopt); - VerifyOrReturnValue(cluster->attributes != nullptr, std::nullopt); - - std::optional attribute_idx = TryFindAttributeIndex(cluster, path.mAttributeId); + // TODO: if ember would store command entries, we could simplify this code to use static data + ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount)); - if (!attribute_idx.has_value()) + ConcreteCommandPath commandPath = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId); + for (const chip::CommandId * p = serverCluster->acceptedCommandList; p != endOfList; p++) { - return std::nullopt; + commandPath.mCommandId = *p; + ReturnErrorOnFailure(builder.Append(AcceptedCommandEntryFor(commandPath))); } - DataModel::AttributeInfo info; - LoadAttributeInfo(path, cluster->attributes[*attribute_idx], &info); - return std::make_optional(info); -} - -DataModel::CommandEntry CodegenDataModelProvider::FirstAcceptedCommand(const ConcreteClusterPath & path) -{ - EnumeratorCommandFinder handlerFinder(&CommandHandlerInterface::EnumerateAcceptedCommands); - - CommandId commandId = - FindCommand(ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId), handlerFinder, - detail::EnumeratorCommandFinder::Operation::kFindFirst, mAcceptedCommandsIterator, AcceptedCommands); - - VerifyOrReturnValue(commandId != kInvalidCommandId, DataModel::CommandEntry::kInvalid); - return CommandEntryFrom(path, commandId); -} - -DataModel::CommandEntry CodegenDataModelProvider::NextAcceptedCommand(const ConcreteCommandPath & before) -{ - - EnumeratorCommandFinder handlerFinder(&CommandHandlerInterface::EnumerateAcceptedCommands); - CommandId commandId = FindCommand(before, handlerFinder, detail::EnumeratorCommandFinder::Operation::kFindNext, - mAcceptedCommandsIterator, AcceptedCommands); - - VerifyOrReturnValue(commandId != kInvalidCommandId, DataModel::CommandEntry::kInvalid); - return CommandEntryFrom(before, commandId); + return CHIP_NO_ERROR; } -std::optional CodegenDataModelProvider::GetAcceptedCommandInfo(const ConcreteCommandPath & path) +CHIP_ERROR CodegenDataModelProvider::GeneratedCommands(const ConcreteClusterPath & path, + DataModel::ListBuilder & builder) { + CommandHandlerInterface * interface = + CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId); + if (interface != nullptr) + { + size_t commandCount = 0; - EnumeratorCommandFinder handlerFinder(&CommandHandlerInterface::EnumerateAcceptedCommands); - CommandId commandId = FindCommand(path, handlerFinder, detail::EnumeratorCommandFinder::Operation::kFindExact, - mAcceptedCommandsIterator, AcceptedCommands); - - VerifyOrReturnValue(commandId != kInvalidCommandId, std::nullopt); - return CommandEntryFrom(path, commandId).info; -} - -ConcreteCommandPath CodegenDataModelProvider::FirstGeneratedCommand(const ConcreteClusterPath & path) -{ - EnumeratorCommandFinder handlerFinder(&CommandHandlerInterface::EnumerateGeneratedCommands); - CommandId commandId = - FindCommand(ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId), handlerFinder, - detail::EnumeratorCommandFinder::Operation::kFindFirst, mGeneratedCommandsIterator, GeneratedCommands); + CHIP_ERROR err = interface->EnumerateGeneratedCommands( + path, + [](CommandId id, void * context) -> Loop { + *reinterpret_cast(context) += 1; + return Loop::Continue; + }, + reinterpret_cast(&commandCount)); - VerifyOrReturnValue(commandId != kInvalidCommandId, kInvalidCommandPath); - return ConcreteCommandPath(path.mEndpointId, path.mClusterId, commandId); -} + if (err == CHIP_NO_ERROR) + { + ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount)); -ConcreteCommandPath CodegenDataModelProvider::NextGeneratedCommand(const ConcreteCommandPath & before) -{ - EnumeratorCommandFinder handlerFinder(&CommandHandlerInterface::EnumerateGeneratedCommands); + using EnumerationData = struct + { + DataModel::ListBuilder * generatedCommandList; + CHIP_ERROR processingError; + }; + EnumerationData enumerationData; + enumerationData.processingError = CHIP_NO_ERROR; + enumerationData.generatedCommandList = &builder; + + ReturnErrorOnFailure(interface->EnumerateGeneratedCommands( + path, + [](CommandId id, void * context) -> Loop { + auto input = reinterpret_cast(context); + + CHIP_ERROR appendError = input->generatedCommandList->Append(id); + if (appendError != CHIP_NO_ERROR) + { + input->processingError = appendError; + return Loop::Break; + } + return Loop::Continue; + }, + reinterpret_cast(&enumerationData))); + ReturnErrorOnFailure(enumerationData.processingError); + + // the two invocations MUST return the same sizes. + VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL); + return CHIP_NO_ERROR; + } + VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err); + } - CommandId commandId = FindCommand(before, handlerFinder, detail::EnumeratorCommandFinder::Operation::kFindNext, - mGeneratedCommandsIterator, GeneratedCommands); + const EmberAfCluster * serverCluster = FindServerCluster(path); + VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND); + VerifyOrReturnError(serverCluster->generatedCommandList != nullptr, CHIP_NO_ERROR); - VerifyOrReturnValue(commandId != kInvalidCommandId, kInvalidCommandPath); - return ConcreteCommandPath(before.mEndpointId, before.mClusterId, commandId); + const chip::CommandId * endOfList = serverCluster->generatedCommandList; + while (*endOfList != kInvalidCommandId) + { + endOfList++; + } + const auto commandCount = static_cast(endOfList - serverCluster->generatedCommandList); + return builder.ReferenceExisting({ serverCluster->generatedCommandList, commandCount }); } void CodegenDataModelProvider::InitDataModelForTesting() @@ -868,67 +485,47 @@ void CodegenDataModelProvider::InitDataModelForTesting() InitDataModelHandler(); } -std::optional CodegenDataModelProvider::FirstDeviceType(EndpointId endpoint) +CHIP_ERROR CodegenDataModelProvider::DeviceTypes(EndpointId endpointId, + DataModel::ListBuilder & builder) { - // Use the `Index` version even though `emberAfDeviceTypeListFromEndpoint` would work because - // index finding is cached in TryFindEndpointIndex and this avoids an extra `emberAfIndexFromEndpoint` - // during `Next` loops. This avoids O(n^2) on number of indexes when iterating over all device types. - // - // Not actually needed for `First`, however this makes First and Next consistent. - std::optional endpoint_index = TryFindEndpointIndex(endpoint); + std::optional endpoint_index = TryFindEndpointIndex(endpointId); if (!endpoint_index.has_value()) { - return std::nullopt; + return {}; } CHIP_ERROR err = CHIP_NO_ERROR; Span deviceTypes = emberAfDeviceTypeListFromEndpointIndex(*endpoint_index, err); - SpanSearchValue> searchable(&deviceTypes); - return DeviceTypeEntryFromEmber(searchable.First(mDeviceTypeIterationHint).Value()); -} + ReturnErrorOnFailure(builder.EnsureAppendCapacity(deviceTypes.size())); -std::optional CodegenDataModelProvider::NextDeviceType(EndpointId endpoint, - const DataModel::DeviceTypeEntry & previous) -{ - // Use the `Index` version even though `emberAfDeviceTypeListFromEndpoint` would work because - // index finding is cached in TryFindEndpointIndex and this avoids an extra `emberAfIndexFromEndpoint` - // during `Next` loops. This avoids O(n^2) on number of indexes when iterating over all device types. - std::optional endpoint_index = TryFindEndpointIndex(endpoint); - if (!endpoint_index.has_value()) + for (auto & entry : deviceTypes) { - return std::nullopt; + ReturnErrorOnFailure(builder.Append(DeviceTypeEntryFromEmber(entry))); } - CHIP_ERROR err = CHIP_NO_ERROR; - chip::Span deviceTypes = emberAfDeviceTypeListFromEndpointIndex(*endpoint_index, err); - SpanSearchValue> searchable(&deviceTypes); - - return DeviceTypeEntryFromEmber(searchable.Next(previous, mDeviceTypeIterationHint).Value()); + return CHIP_NO_ERROR; } -std::optional CodegenDataModelProvider::GetFirstSemanticTag(EndpointId endpoint) +CHIP_ERROR CodegenDataModelProvider::SemanticTags(EndpointId endpointId, DataModel::ListBuilder & builder) { - Clusters::Descriptor::Structs::SemanticTagStruct::Type tag; - // we start at the beginning - mSemanticTagIterationHint = 0; - if (GetSemanticTagForEndpointAtIndex(endpoint, 0, tag) == CHIP_NO_ERROR) + DataModel::Provider::SemanticTag semanticTag; + size_t count = 0; + + while (GetSemanticTagForEndpointAtIndex(endpointId, count, semanticTag) == CHIP_NO_ERROR) { - return std::make_optional(tag); + count++; } - return std::nullopt; -} -std::optional CodegenDataModelProvider::GetNextSemanticTag(EndpointId endpoint, - const SemanticTag & previous) -{ - Clusters::Descriptor::Structs::SemanticTagStruct::Type tag; - std::optional idx = FindNextSemanticTagIndex(endpoint, previous, mSemanticTagIterationHint); - if (idx.has_value() && GetSemanticTagForEndpointAtIndex(endpoint, *idx, tag) == CHIP_NO_ERROR) + ReturnErrorOnFailure(builder.EnsureAppendCapacity(count)); + + for (size_t idx = 0; idx < count; idx++) { - return std::make_optional(tag); + ReturnErrorOnFailure(GetSemanticTagForEndpointAtIndex(endpointId, idx, semanticTag)); + ReturnErrorOnFailure(builder.Append(semanticTag)); } - return std::nullopt; + + return CHIP_NO_ERROR; } } // namespace app diff --git a/src/data-model-providers/codegen/CodegenDataModelProvider.h b/src/data-model-providers/codegen/CodegenDataModelProvider.h index e3455d5ce03..764f5f74874 100644 --- a/src/data-model-providers/codegen/CodegenDataModelProvider.h +++ b/src/data-model-providers/codegen/CodegenDataModelProvider.h @@ -21,67 +21,13 @@ #include #include #include +#include #include #include namespace chip { namespace app { -namespace detail { - -/// Handles going through callback-based enumeration of generated/accepted commands -/// for CommandHandlerInterface based items. -/// -/// Offers the ability to focus on some operation for finding a given -/// command id: -/// - FindFirst will return the first found element -/// - FindExact finds the element with the given id -/// - FindNext finds the element following the given id -class EnumeratorCommandFinder -{ -public: - using HandlerCallbackFunction = CHIP_ERROR (CommandHandlerInterface::*)(const ConcreteClusterPath &, - CommandHandlerInterface::CommandIdCallback, void *); - - enum class Operation - { - kFindFirst, // Find the first value in the list - kFindExact, // Find the given value - kFindNext // Find the value AFTER this value - }; - - EnumeratorCommandFinder(HandlerCallbackFunction callback) : - mCallback(callback), mOperation(Operation::kFindFirst), mTarget(kInvalidCommandId) - {} - - /// Find the given command ID that matches the given operation/path. - /// - /// If operation is kFindFirst, then path commandID is ignored. Otherwise it is used as a key to - /// kFindExact or kFindNext. - /// - /// Returns: - /// - std::nullopt if no command found using the command handler interface - /// - kInvalidCommandId if the find failed (but command handler interface does provide a list) - /// - valid id if command handler interface usage succeeds - std::optional FindCommandId(Operation operation, const ConcreteCommandPath & path); - -private: - HandlerCallbackFunction mCallback; - Operation mOperation; - CommandId mTarget; - std::optional mFound = std::nullopt; - - Loop HandlerCallback(CommandId id); - - static Loop HandlerCallbackFn(CommandId id, void * context) - { - auto self = static_cast(context); - return self->HandlerCallback(id); - } -}; - -} // namespace detail - /// An implementation of `InteractionModel::Model` that relies on code-generation /// via zap/ember. /// @@ -96,44 +42,10 @@ class EnumeratorCommandFinder /// however they would share the exact same underlying data and storage). class CodegenDataModelProvider : public DataModel::Provider { -private: - /// Ember commands are stored as a `CommandId *` pointer that is either null (i.e. no commands) - /// or is terminated with 0xFFFF_FFFF aka kInvalidCommandId - /// - /// Since iterator implementations in the data model use Next(before_path) calls, iterating - /// such lists from the beginning would be very inefficient as O(n^2). - /// - /// This class maintains a cached position inside such iteration, such that `Next` calls - /// can be faster. - class EmberCommandListIterator - { - private: - const CommandId * mCurrentList = nullptr; - const CommandId * mCurrentHint = nullptr; // Invariant: mCurrentHint is INSIDE mCurrentList - public: - EmberCommandListIterator() = default; - - /// Returns the first command in the given list (or nullopt if list is null or starts with 0xFFFFFFF) - std::optional First(const CommandId * list); - - /// Returns the command after `previousId` in the given list - std::optional Next(const CommandId * list, CommandId previousId); - - /// Checks if the given command id exists in the given list - bool Exists(const CommandId * list, CommandId toCheck); - - void Reset() { mCurrentList = mCurrentHint = nullptr; } - }; - public: /// clears out internal caching. Especially useful in unit tests, /// where path caching does not really apply (the same path may result in different outcomes) - void Reset() - { - mAcceptedCommandsIterator.Reset(); - mGeneratedCommandsIterator.Reset(); - mPreviouslyFoundCluster = std::nullopt; - } + void Reset() { mPreviouslyFoundCluster = std::nullopt; } void SetPersistentStorageDelegate(PersistentStorageDelegate * delegate) { mPersistentStorageDelegate = delegate; } PersistentStorageDelegate * GetPersistentStorageDelegate() { return mPersistentStorageDelegate; } @@ -155,35 +67,15 @@ class CodegenDataModelProvider : public DataModel::Provider CommandHandler * handler) override; /// attribute tree iteration - DataModel::EndpointEntry FirstEndpoint() override; - DataModel::EndpointEntry NextEndpoint(EndpointId before) override; - std::optional GetEndpointInfo(EndpointId endpoint) override; - bool EndpointExists(EndpointId endpoint) override; - - std::optional FirstDeviceType(EndpointId endpoint) override; - std::optional NextDeviceType(EndpointId endpoint, - const DataModel::DeviceTypeEntry & previous) override; - - std::optional GetFirstSemanticTag(EndpointId endpoint) override; - std::optional GetNextSemanticTag(EndpointId endpoint, const SemanticTag & previous) override; - - DataModel::ClusterEntry FirstServerCluster(EndpointId endpoint) override; - DataModel::ClusterEntry NextServerCluster(const ConcreteClusterPath & before) override; - std::optional GetServerClusterInfo(const ConcreteClusterPath & path) override; - - ConcreteClusterPath FirstClientCluster(EndpointId endpoint) override; - ConcreteClusterPath NextClientCluster(const ConcreteClusterPath & before) override; - - DataModel::AttributeEntry FirstAttribute(const ConcreteClusterPath & cluster) override; - DataModel::AttributeEntry NextAttribute(const ConcreteAttributePath & before) override; - std::optional GetAttributeInfo(const ConcreteAttributePath & path) override; - - DataModel::CommandEntry FirstAcceptedCommand(const ConcreteClusterPath & cluster) override; - DataModel::CommandEntry NextAcceptedCommand(const ConcreteCommandPath & before) override; - std::optional GetAcceptedCommandInfo(const ConcreteCommandPath & path) override; - - ConcreteCommandPath FirstGeneratedCommand(const ConcreteClusterPath & cluster) override; - ConcreteCommandPath NextGeneratedCommand(const ConcreteCommandPath & before) override; + CHIP_ERROR Endpoints(DataModel::ListBuilder & out) override; + CHIP_ERROR SemanticTags(EndpointId endpointId, DataModel::ListBuilder & builder) override; + CHIP_ERROR DeviceTypes(EndpointId endpointId, DataModel::ListBuilder & builder) override; + CHIP_ERROR ClientClusters(EndpointId endpointId, DataModel::ListBuilder & builder) override; + CHIP_ERROR ServerClusters(EndpointId endpointId, DataModel::ListBuilder & builder) override; + CHIP_ERROR GeneratedCommands(const ConcreteClusterPath & path, DataModel::ListBuilder & builder) override; + CHIP_ERROR AcceptedCommands(const ConcreteClusterPath & path, + DataModel::ListBuilder & builder) override; + CHIP_ERROR Attributes(const ConcreteClusterPath & path, DataModel::ListBuilder & builder) override; void Temporary_ReportAttributeChanged(const AttributePathParams & path) override; @@ -196,14 +88,7 @@ class CodegenDataModelProvider : public DataModel::Provider private: // Iteration is often done in a tight loop going through all values. // To avoid N^2 iterations, cache a hint of where something is positioned - uint16_t mEndpointIterationHint = 0; - unsigned mServerClusterIterationHint = 0; - unsigned mClientClusterIterationHint = 0; - unsigned mAttributeIterationHint = 0; - unsigned mDeviceTypeIterationHint = 0; - unsigned mSemanticTagIterationHint = 0; - EmberCommandListIterator mAcceptedCommandsIterator; - EmberCommandListIterator mGeneratedCommandsIterator; + uint16_t mEndpointIterationHint = 0; // represents a remembered cluster reference that has been found as // looking for clusters is very common (for every attribute iteration) @@ -232,20 +117,8 @@ class CodegenDataModelProvider : public DataModel::Provider /// Effectively the same as `emberAfFindServerCluster` except with some caching capabilities const EmberAfCluster * FindServerCluster(const ConcreteClusterPath & path); - /// Find the index of the given attribute id - std::optional TryFindAttributeIndex(const EmberAfCluster * cluster, AttributeId id) const; - - /// Find the index of the given cluster id - std::optional TryFindClusterIndex(const EmberAfEndpointType * endpoint, ClusterId id, ClusterSide clusterSide) const; - /// Find the index of the given endpoint id std::optional TryFindEndpointIndex(EndpointId id) const; - - using CommandListGetter = const CommandId *(const EmberAfCluster &); - - CommandId FindCommand(const ConcreteCommandPath & path, detail::EnumeratorCommandFinder & handlerFinder, - detail::EnumeratorCommandFinder::Operation operation, - CodegenDataModelProvider::EmberCommandListIterator & emberIterator, CommandListGetter commandListGetter); }; } // namespace app diff --git a/src/data-model-providers/codegen/CodegenDataModelProvider_Write.cpp b/src/data-model-providers/codegen/CodegenDataModelProvider_Write.cpp index 1eb25f122d3..5cd6602fe08 100644 --- a/src/data-model-providers/codegen/CodegenDataModelProvider_Write.cpp +++ b/src/data-model-providers/codegen/CodegenDataModelProvider_Write.cpp @@ -190,15 +190,16 @@ DataModel::ActionReturnStatus CodegenDataModelProvider::WriteAttribute(const Dat if (request.path.mDataVersion.HasValue()) { - std::optional clusterInfo = GetServerClusterInfo(request.path); - if (!clusterInfo.has_value()) + DataVersion * versionPtr = emberAfDataVersionStorage(request.path); + + if (versionPtr == nullptr) { ChipLogError(DataManagement, "Unable to get cluster info for Endpoint 0x%x, Cluster " ChipLogFormatMEI, request.path.mEndpointId, ChipLogValueMEI(request.path.mClusterId)); return Status::DataVersionMismatch; } - if (request.path.mDataVersion.Value() != clusterInfo->dataVersion) + if (request.path.mDataVersion.Value() != *versionPtr) { ChipLogError(DataManagement, "Write Version mismatch for Endpoint 0x%x, Cluster " ChipLogFormatMEI, request.path.mEndpointId, ChipLogValueMEI(request.path.mClusterId)); diff --git a/src/data-model-providers/codegen/tests/TestCodegenModelViaMocks.cpp b/src/data-model-providers/codegen/tests/TestCodegenModelViaMocks.cpp index fd216598389..1045114b7f9 100644 --- a/src/data-model-providers/codegen/tests/TestCodegenModelViaMocks.cpp +++ b/src/data-model-providers/codegen/tests/TestCodegenModelViaMocks.cpp @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include #include @@ -34,6 +33,8 @@ #include #include #include +#include +#include #include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -139,6 +141,12 @@ bool operator==(const Access::SubjectDescriptor & a, const Access::SubjectDescri return true; } +struct TestCodegenModelViaMocks : public ::testing::Test +{ + static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); } + static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); } +}; + class TestProviderChangeListener : public ProviderChangeListener { public: @@ -892,331 +900,160 @@ void WriteLe16(void * buffer, uint16_t value) } // namespace -TEST(TestCodegenModelViaMocks, IterateOverEndpoints) +TEST_F(TestCodegenModelViaMocks, IterateOverEndpoints) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; // This iteration relies on the hard-coding that occurs when mock_ember is used - EndpointEntry ep = model.FirstEndpoint(); - EXPECT_EQ(ep.id, kMockEndpoint1); - EXPECT_EQ(ep.info.parentId, kInvalidEndpointId); - EXPECT_EQ(ep.info.compositionPattern, EndpointCompositionPattern::kFullFamily); - ep = model.NextEndpoint(kMockEndpoint1); - EXPECT_EQ(ep.id, kMockEndpoint2); - EXPECT_EQ(ep.info.parentId, kInvalidEndpointId); - EXPECT_EQ(ep.info.compositionPattern, EndpointCompositionPattern::kTree); - ep = model.NextEndpoint(kMockEndpoint2); - EXPECT_EQ(ep.id, kMockEndpoint3); - EXPECT_EQ(ep.info.parentId, kInvalidEndpointId); - EXPECT_EQ(ep.info.compositionPattern, EndpointCompositionPattern::kFullFamily); - ep = model.NextEndpoint(kMockEndpoint3); - EXPECT_EQ(ep.id, kInvalidEndpointId); - - /// Some out of order requests should work as well - ep = model.NextEndpoint(kMockEndpoint2); - EXPECT_EQ(ep.id, kMockEndpoint3); - EXPECT_EQ(ep.info.parentId, kInvalidEndpointId); - EXPECT_EQ(ep.info.compositionPattern, EndpointCompositionPattern::kFullFamily); - ep = model.NextEndpoint(kMockEndpoint2); - EXPECT_EQ(ep.id, kMockEndpoint3); - EXPECT_EQ(ep.info.parentId, kInvalidEndpointId); - EXPECT_EQ(ep.info.compositionPattern, EndpointCompositionPattern::kFullFamily); - ep = model.NextEndpoint(kMockEndpoint1); - EXPECT_EQ(ep.id, kMockEndpoint2); - EXPECT_EQ(ep.info.parentId, kInvalidEndpointId); - EXPECT_EQ(ep.info.compositionPattern, EndpointCompositionPattern::kTree); - ep = model.NextEndpoint(kMockEndpoint1); - EXPECT_EQ(ep.id, kMockEndpoint2); - EXPECT_EQ(ep.info.parentId, kInvalidEndpointId); - EXPECT_EQ(ep.info.compositionPattern, EndpointCompositionPattern::kTree); - ep = model.NextEndpoint(kMockEndpoint2); - EXPECT_EQ(ep.id, kMockEndpoint3); - EXPECT_EQ(ep.info.parentId, kInvalidEndpointId); - EXPECT_EQ(ep.info.compositionPattern, EndpointCompositionPattern::kFullFamily); - ep = model.NextEndpoint(kMockEndpoint1); - EXPECT_EQ(ep.id, kMockEndpoint2); - EXPECT_EQ(ep.info.parentId, kInvalidEndpointId); - EXPECT_EQ(ep.info.compositionPattern, EndpointCompositionPattern::kTree); - ep = model.NextEndpoint(kMockEndpoint3); - EXPECT_EQ(ep.id, kInvalidEndpointId); - ep = model.NextEndpoint(kMockEndpoint3); - EXPECT_EQ(ep.id, kInvalidEndpointId); - ep = model.FirstEndpoint(); - EXPECT_EQ(ep.id, kMockEndpoint1); - EXPECT_EQ(ep.info.parentId, kInvalidEndpointId); - EXPECT_EQ(ep.info.compositionPattern, EndpointCompositionPattern::kFullFamily); - ep = model.FirstEndpoint(); - EXPECT_EQ(ep.id, kMockEndpoint1); - EXPECT_EQ(ep.info.parentId, kInvalidEndpointId); - EXPECT_EQ(ep.info.compositionPattern, EndpointCompositionPattern::kFullFamily); - - // invalid endpoiunts - ep = model.NextEndpoint(kInvalidEndpointId); - EXPECT_EQ(ep.id, kInvalidEndpointId); - ep = model.NextEndpoint(987u); - EXPECT_EQ(ep.id, kInvalidEndpointId); -} - -TEST(TestCodegenModelViaMocks, GetEndpointInfo) -{ - UseMockNodeConfig config(gTestNodeConfig); - CodegenDataModelProviderWithContext model; + DataModel::ListBuilder endpointsBuilder; - std::optional info = model.GetEndpointInfo(kMockEndpoint1); - ASSERT_TRUE(info.has_value()); - EXPECT_EQ(info->parentId, kInvalidEndpointId); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(info->compositionPattern, // NOLINT(bugprone-unchecked-optional-access) - EndpointCompositionPattern::kFullFamily); - info = model.GetEndpointInfo(kMockEndpoint2); - ASSERT_TRUE(info.has_value()); - EXPECT_EQ(info->parentId, kInvalidEndpointId); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(info->compositionPattern, // NOLINT(bugprone-unchecked-optional-access) - EndpointCompositionPattern::kTree); - info = model.GetEndpointInfo(kMockEndpoint3); - ASSERT_TRUE(info.has_value()); - EXPECT_EQ(info->parentId, kInvalidEndpointId); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(info->compositionPattern, // NOLINT(bugprone-unchecked-optional-access) - EndpointCompositionPattern::kFullFamily); + ASSERT_EQ(model.Endpoints(endpointsBuilder), CHIP_NO_ERROR); + + auto endpoints = endpointsBuilder.TakeBuffer(); + + ASSERT_EQ(endpoints.size(), 3u); + + EXPECT_EQ(endpoints[0].id, kMockEndpoint1); + EXPECT_EQ(endpoints[0].parentId, kInvalidEndpointId); + EXPECT_EQ(endpoints[0].compositionPattern, EndpointCompositionPattern::kFullFamily); - // invalid endpoiunts - info = model.GetEndpointInfo(kInvalidEndpointId); - EXPECT_FALSE(info.has_value()); - info = model.GetEndpointInfo(987u); - EXPECT_FALSE(info.has_value()); + EXPECT_EQ(endpoints[1].id, kMockEndpoint2); + EXPECT_EQ(endpoints[1].parentId, kInvalidEndpointId); + EXPECT_EQ(endpoints[1].compositionPattern, EndpointCompositionPattern::kTree); + + EXPECT_EQ(endpoints[2].id, kMockEndpoint3); + EXPECT_EQ(endpoints[2].parentId, kInvalidEndpointId); + EXPECT_EQ(endpoints[2].compositionPattern, EndpointCompositionPattern::kFullFamily); } -TEST(TestCodegenModelViaMocks, IterateOverServerClusters) +TEST_F(TestCodegenModelViaMocks, IterateOverServerClusters) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; chip::Test::ResetVersion(); - EXPECT_FALSE(model.FirstServerCluster(kEndpointIdThatIsMissing).path.HasValidIds()); - EXPECT_FALSE(model.FirstServerCluster(kInvalidEndpointId).path.HasValidIds()); - EXPECT_FALSE(model.NextServerCluster(ConcreteClusterPath(kInvalidEndpointId, 123)).path.HasValidIds()); - EXPECT_FALSE(model.NextServerCluster(ConcreteClusterPath(kMockEndpoint1, kInvalidClusterId)).path.HasValidIds()); - EXPECT_FALSE(model.NextServerCluster(ConcreteClusterPath(kMockEndpoint1, 981u)).path.HasValidIds()); + DataModel::ListBuilder builder; - // mock endpoint 1 has 2 mock clusters: 1 and 2 - ClusterEntry entry = model.FirstServerCluster(kMockEndpoint1); - ASSERT_TRUE(entry.path.HasValidIds()); - EXPECT_EQ(entry.path.mEndpointId, kMockEndpoint1); - EXPECT_EQ(entry.path.mClusterId, MockClusterId(1)); - EXPECT_EQ(entry.info.dataVersion, 0u); - EXPECT_EQ(entry.info.flags.Raw(), 0u); + EXPECT_NE(model.ServerClusters(kEndpointIdThatIsMissing, builder), CHIP_NO_ERROR); + EXPECT_TRUE(builder.IsEmpty()); + EXPECT_NE(model.ServerClusters(kInvalidEndpointId, builder), CHIP_NO_ERROR); + EXPECT_TRUE(builder.IsEmpty()); - chip::Test::BumpVersion(); + // mock endpoint 1 has 2 mock clusters: 1 and 2 + EXPECT_EQ(model.ServerClusters(kMockEndpoint1, builder), CHIP_NO_ERROR); + auto serverClusters = builder.TakeBuffer(); + ASSERT_EQ(serverClusters.size(), 2u); - entry = model.NextServerCluster(entry.path); - ASSERT_TRUE(entry.path.HasValidIds()); - EXPECT_EQ(entry.path.mEndpointId, kMockEndpoint1); - EXPECT_EQ(entry.path.mClusterId, MockClusterId(2)); - EXPECT_EQ(entry.info.dataVersion, 1u); - EXPECT_EQ(entry.info.flags.Raw(), 0u); + EXPECT_EQ(serverClusters[0].clusterId, MockClusterId(1)); + EXPECT_EQ(serverClusters[0].dataVersion, 0u); + EXPECT_EQ(serverClusters[0].flags.Raw(), 0u); - entry = model.NextServerCluster(entry.path); - EXPECT_FALSE(entry.path.HasValidIds()); + EXPECT_EQ(serverClusters[1].clusterId, MockClusterId(2)); + EXPECT_EQ(serverClusters[1].dataVersion, 0u); + EXPECT_EQ(serverClusters[1].flags.Raw(), 0u); - // mock endpoint 3 has 4 mock clusters: 1 through 4 - entry = model.FirstServerCluster(kMockEndpoint3); - for (uint16_t clusterId = 1; clusterId <= 4; clusterId++) - { - ASSERT_TRUE(entry.path.HasValidIds()); - EXPECT_EQ(entry.path.mEndpointId, kMockEndpoint3); - EXPECT_EQ(entry.path.mClusterId, MockClusterId(clusterId)); - entry = model.NextServerCluster(entry.path); - } - EXPECT_FALSE(entry.path.HasValidIds()); + chip::Test::BumpVersion(); - // repeat calls should work - for (int i = 0; i < 10; i++) - { - entry = model.FirstServerCluster(kMockEndpoint1); - ASSERT_TRUE(entry.path.HasValidIds()); - EXPECT_EQ(entry.path.mEndpointId, kMockEndpoint1); - EXPECT_EQ(entry.path.mClusterId, MockClusterId(1)); - } + EXPECT_EQ(model.ServerClusters(kMockEndpoint1, builder), CHIP_NO_ERROR); + serverClusters = builder.TakeBuffer(); + ASSERT_EQ(serverClusters.size(), 2u); + EXPECT_EQ(serverClusters[0].dataVersion, 1u); + EXPECT_EQ(serverClusters[1].dataVersion, 1u); - for (int i = 0; i < 10; i++) - { - ClusterEntry nextEntry = model.NextServerCluster(entry.path); - ASSERT_TRUE(nextEntry.path.HasValidIds()); - EXPECT_EQ(nextEntry.path.mEndpointId, kMockEndpoint1); - EXPECT_EQ(nextEntry.path.mClusterId, MockClusterId(2)); - } + // mock endpoint 3 has 4 mock clusters: 1 through 4 + EXPECT_EQ(model.ServerClusters(kMockEndpoint3, builder), CHIP_NO_ERROR); + serverClusters = builder.TakeBuffer(); + ASSERT_EQ(serverClusters.size(), 4u); + EXPECT_EQ(serverClusters[0].clusterId, MockClusterId(1)); + EXPECT_EQ(serverClusters[1].clusterId, MockClusterId(2)); + EXPECT_EQ(serverClusters[2].clusterId, MockClusterId(3)); + EXPECT_EQ(serverClusters[3].clusterId, MockClusterId(4)); } -TEST(TestCodegenModelViaMocks, GetServerClusterInfo) +TEST_F(TestCodegenModelViaMocks, IterateOverClientClusters) { - UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; - chip::Test::ResetVersion(); - - ASSERT_FALSE(model.GetServerClusterInfo(ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId)).has_value()); - ASSERT_FALSE(model.GetServerClusterInfo(ConcreteClusterPath(kInvalidEndpointId, MockClusterId(1))).has_value()); - ASSERT_FALSE(model.GetServerClusterInfo(ConcreteClusterPath(kMockEndpoint1, kInvalidClusterId)).has_value()); - ASSERT_FALSE(model.GetServerClusterInfo(ConcreteClusterPath(kMockEndpoint1, MockClusterId(10))).has_value()); + DataModel::ListBuilder builder; - // now get the value - std::optional info = model.GetServerClusterInfo(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1))); - ASSERT_TRUE(info.has_value()); - EXPECT_EQ(info->dataVersion, 0u); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(info->flags.Raw(), 0u); // NOLINT(bugprone-unchecked-optional-access) - - chip::Test::BumpVersion(); - info = model.GetServerClusterInfo(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1))); - ASSERT_TRUE(info.has_value()); - EXPECT_EQ(info->dataVersion, 1u); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(info->flags.Raw(), 0u); // NOLINT(bugprone-unchecked-optional-access) -} - -TEST(TestCodegenModelViaMocks, IterateOverClientClusters) -{ - UseMockNodeConfig config(gTestNodeConfig); - CodegenDataModelProviderWithContext model; - - EXPECT_FALSE(model.FirstClientCluster(kEndpointIdThatIsMissing).HasValidIds()); - EXPECT_FALSE(model.FirstClientCluster(kInvalidEndpointId).HasValidIds()); - EXPECT_FALSE(model.NextClientCluster(ConcreteClusterPath(kInvalidEndpointId, 123)).HasValidIds()); - EXPECT_FALSE(model.NextClientCluster(ConcreteClusterPath(kMockEndpoint1, kInvalidClusterId)).HasValidIds()); - EXPECT_FALSE(model.NextClientCluster(ConcreteClusterPath(kMockEndpoint1, 981u)).HasValidIds()); + EXPECT_EQ(model.ClientClusters(kEndpointIdThatIsMissing, builder), CHIP_ERROR_NOT_FOUND); + EXPECT_TRUE(builder.IsEmpty()); + EXPECT_EQ(model.ClientClusters(kInvalidEndpointId, builder), CHIP_ERROR_NOT_FOUND); + EXPECT_TRUE(builder.IsEmpty()); // mock endpoint 1 has 2 mock client clusters: 3 and 4 - ConcreteClusterPath path = model.FirstClientCluster(kMockEndpoint1); - ASSERT_TRUE(path.HasValidIds()); - EXPECT_EQ(path.mEndpointId, kMockEndpoint1); - EXPECT_EQ(path.mClusterId, MockClusterId(3)); - - path = model.NextClientCluster(path); - ASSERT_TRUE(path.HasValidIds()); - EXPECT_EQ(path.mEndpointId, kMockEndpoint1); - EXPECT_EQ(path.mClusterId, MockClusterId(4)); + EXPECT_EQ(model.ClientClusters(kMockEndpoint1, builder), CHIP_NO_ERROR); + auto clientClusters = builder.TakeBuffer(); - path = model.NextClientCluster(path); - EXPECT_FALSE(path.HasValidIds()); + const ClusterId kExpectedClusters1[] = { MockClusterId(3), MockClusterId(4) }; + ASSERT_TRUE(clientClusters.data_equal(Span(kExpectedClusters1))); // mock endpoint 2 has 1 mock client clusters: 3(has server side at the same time) and 4 - path = model.FirstClientCluster(kMockEndpoint2); - for (uint16_t clusterId = 3; clusterId <= 4; clusterId++) - { - ASSERT_TRUE(path.HasValidIds()); - EXPECT_EQ(path.mEndpointId, kMockEndpoint2); - EXPECT_EQ(path.mClusterId, MockClusterId(clusterId)); - path = model.NextClientCluster(path); - } - EXPECT_FALSE(path.HasValidIds()); - - // repeat calls should work - for (int i = 0; i < 10; i++) - { - path = model.FirstClientCluster(kMockEndpoint1); - ASSERT_TRUE(path.HasValidIds()); - EXPECT_EQ(path.mEndpointId, kMockEndpoint1); - EXPECT_EQ(path.mClusterId, MockClusterId(3)); - } + EXPECT_EQ(model.ClientClusters(kMockEndpoint2, builder), CHIP_NO_ERROR); + clientClusters = builder.TakeBuffer(); - for (int i = 0; i < 10; i++) - { - ConcreteClusterPath nextPath = model.NextClientCluster(path); - ASSERT_TRUE(nextPath.HasValidIds()); - EXPECT_EQ(nextPath.mEndpointId, kMockEndpoint1); - EXPECT_EQ(nextPath.mClusterId, MockClusterId(4)); - } + const ClusterId kExpectedClusters2[] = { MockClusterId(3), MockClusterId(4) }; + ASSERT_TRUE(clientClusters.data_equal(Span(kExpectedClusters2))); } -TEST(TestCodegenModelViaMocks, IterateOverAttributes) +TEST_F(TestCodegenModelViaMocks, IterateOverAttributes) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; // invalid paths should return in "no more data" - ASSERT_FALSE(model.FirstAttribute(ConcreteClusterPath(kEndpointIdThatIsMissing, MockClusterId(1))).path.HasValidIds()); - ASSERT_FALSE(model.FirstAttribute(ConcreteClusterPath(kInvalidEndpointId, MockClusterId(1))).path.HasValidIds()); - ASSERT_FALSE(model.FirstAttribute(ConcreteClusterPath(kMockEndpoint1, MockClusterId(10))).path.HasValidIds()); - ASSERT_FALSE(model.FirstAttribute(ConcreteClusterPath(kMockEndpoint1, kInvalidClusterId)).path.HasValidIds()); - - ASSERT_FALSE(model.NextAttribute(ConcreteAttributePath(kEndpointIdThatIsMissing, MockClusterId(1), 1u)).path.HasValidIds()); - ASSERT_FALSE(model.NextAttribute(ConcreteAttributePath(kInvalidEndpointId, MockClusterId(1), 1u)).path.HasValidIds()); - ASSERT_FALSE(model.NextAttribute(ConcreteAttributePath(kMockEndpoint1, MockClusterId(10), 1u)).path.HasValidIds()); - ASSERT_FALSE(model.NextAttribute(ConcreteAttributePath(kMockEndpoint1, kInvalidClusterId, 1u)).path.HasValidIds()); - ASSERT_FALSE(model.NextAttribute(ConcreteAttributePath(kMockEndpoint1, MockClusterId(1), 987u)).path.HasValidIds()); + ASSERT_TRUE(model.AttributesIgnoreError(ConcreteClusterPath(kEndpointIdThatIsMissing, MockClusterId(1))).empty()); + ASSERT_TRUE(model.AttributesIgnoreError(ConcreteClusterPath(kInvalidEndpointId, MockClusterId(1))).empty()); + ASSERT_TRUE(model.AttributesIgnoreError(ConcreteClusterPath(kMockEndpoint1, MockClusterId(10))).empty()); + ASSERT_TRUE(model.AttributesIgnoreError(ConcreteClusterPath(kMockEndpoint1, kInvalidClusterId)).empty()); // should be able to iterate over valid paths - AttributeEntry entry = model.FirstAttribute(ConcreteClusterPath(kMockEndpoint2, MockClusterId(2))); - ASSERT_TRUE(entry.path.HasValidIds()); - ASSERT_EQ(entry.path.mEndpointId, kMockEndpoint2); - ASSERT_EQ(entry.path.mClusterId, MockClusterId(2)); - ASSERT_EQ(entry.path.mAttributeId, ClusterRevision::Id); - ASSERT_FALSE(entry.info.flags.Has(AttributeQualityFlags::kListAttribute)); - - entry = model.NextAttribute(entry.path); - ASSERT_TRUE(entry.path.HasValidIds()); - ASSERT_EQ(entry.path.mEndpointId, kMockEndpoint2); - ASSERT_EQ(entry.path.mClusterId, MockClusterId(2)); - ASSERT_EQ(entry.path.mAttributeId, FeatureMap::Id); - ASSERT_FALSE(entry.info.flags.Has(AttributeQualityFlags::kListAttribute)); - - entry = model.NextAttribute(entry.path); - ASSERT_TRUE(entry.path.HasValidIds()); - ASSERT_EQ(entry.path.mEndpointId, kMockEndpoint2); - ASSERT_EQ(entry.path.mClusterId, MockClusterId(2)); - ASSERT_EQ(entry.path.mAttributeId, MockAttributeId(1)); - ASSERT_FALSE(entry.info.flags.Has(AttributeQualityFlags::kListAttribute)); - - entry = model.NextAttribute(entry.path); - ASSERT_TRUE(entry.path.HasValidIds()); - ASSERT_EQ(entry.path.mEndpointId, kMockEndpoint2); - ASSERT_EQ(entry.path.mClusterId, MockClusterId(2)); - ASSERT_EQ(entry.path.mAttributeId, MockAttributeId(2)); - ASSERT_TRUE(entry.info.flags.Has(AttributeQualityFlags::kListAttribute)); - - entry = model.NextAttribute(entry.path); - ASSERT_FALSE(entry.path.HasValidIds()); - - // repeated calls should work - for (int i = 0; i < 10; i++) - { - entry = model.FirstAttribute(ConcreteClusterPath(kMockEndpoint2, MockClusterId(2))); - ASSERT_TRUE(entry.path.HasValidIds()); - ASSERT_EQ(entry.path.mEndpointId, kMockEndpoint2); - ASSERT_EQ(entry.path.mClusterId, MockClusterId(2)); - ASSERT_EQ(entry.path.mAttributeId, ClusterRevision::Id); - ASSERT_FALSE(entry.info.flags.Has(AttributeQualityFlags::kListAttribute)); - } + DataModel::ListBuilder builder; - for (int i = 0; i < 10; i++) - { - entry = model.NextAttribute(ConcreteAttributePath(kMockEndpoint2, MockClusterId(2), MockAttributeId(1))); - ASSERT_TRUE(entry.path.HasValidIds()); - ASSERT_EQ(entry.path.mEndpointId, kMockEndpoint2); - ASSERT_EQ(entry.path.mClusterId, MockClusterId(2)); - ASSERT_EQ(entry.path.mAttributeId, MockAttributeId(2)); - ASSERT_TRUE(entry.info.flags.Has(AttributeQualityFlags::kListAttribute)); - } + // invalid paths return errors + ASSERT_EQ(model.Attributes(ConcreteClusterPath(kEndpointIdThatIsMissing, MockClusterId(1)), builder), CHIP_ERROR_NOT_FOUND); + ASSERT_EQ(model.Attributes(ConcreteClusterPath(kInvalidEndpointId, MockClusterId(1)), builder), CHIP_ERROR_NOT_FOUND); + ASSERT_EQ(model.Attributes(ConcreteClusterPath(kMockEndpoint1, MockClusterId(10)), builder), CHIP_ERROR_NOT_FOUND); + ASSERT_EQ(model.Attributes(ConcreteClusterPath(kMockEndpoint1, kInvalidClusterId), builder), CHIP_ERROR_NOT_FOUND); + + EXPECT_EQ(model.Attributes(ConcreteClusterPath(kMockEndpoint2, MockClusterId(2)), builder), CHIP_NO_ERROR); + auto attributes = builder.TakeBuffer(); + ASSERT_EQ(attributes.size(), 4u); + + ASSERT_EQ(attributes[0].attributeId, ClusterRevision::Id); + ASSERT_FALSE(attributes[0].flags.Has(AttributeQualityFlags::kListAttribute)); + + ASSERT_EQ(attributes[1].attributeId, FeatureMap::Id); + ASSERT_FALSE(attributes[1].flags.Has(AttributeQualityFlags::kListAttribute)); + + ASSERT_EQ(attributes[2].attributeId, MockAttributeId(1)); + ASSERT_FALSE(attributes[2].flags.Has(AttributeQualityFlags::kListAttribute)); + + ASSERT_EQ(attributes[3].attributeId, MockAttributeId(2)); + ASSERT_TRUE(attributes[3].flags.Has(AttributeQualityFlags::kListAttribute)); } -TEST(TestCodegenModelViaMocks, GetAttributeInfo) +TEST_F(TestCodegenModelViaMocks, FindAttribute) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; + AttributeFinder finder(&model); + // various non-existent or invalid paths should return no info data - ASSERT_FALSE( - model.GetAttributeInfo(ConcreteAttributePath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId)).has_value()); - ASSERT_FALSE(model.GetAttributeInfo(ConcreteAttributePath(kInvalidEndpointId, kInvalidClusterId, FeatureMap::Id)).has_value()); - ASSERT_FALSE(model.GetAttributeInfo(ConcreteAttributePath(kInvalidEndpointId, MockClusterId(1), FeatureMap::Id)).has_value()); - ASSERT_FALSE(model.GetAttributeInfo(ConcreteAttributePath(kMockEndpoint1, kInvalidClusterId, FeatureMap::Id)).has_value()); - ASSERT_FALSE(model.GetAttributeInfo(ConcreteAttributePath(kMockEndpoint1, MockClusterId(10), FeatureMap::Id)).has_value()); - ASSERT_FALSE(model.GetAttributeInfo(ConcreteAttributePath(kMockEndpoint1, MockClusterId(10), kInvalidAttributeId)).has_value()); - ASSERT_FALSE(model.GetAttributeInfo(ConcreteAttributePath(kMockEndpoint1, MockClusterId(1), MockAttributeId(10))).has_value()); + ASSERT_FALSE(finder.Find(ConcreteAttributePath(kInvalidEndpointId, kInvalidClusterId, kInvalidAttributeId)).has_value()); + ASSERT_FALSE(finder.Find(ConcreteAttributePath(kInvalidEndpointId, kInvalidClusterId, FeatureMap::Id)).has_value()); + ASSERT_FALSE(finder.Find(ConcreteAttributePath(kInvalidEndpointId, MockClusterId(1), FeatureMap::Id)).has_value()); + ASSERT_FALSE(finder.Find(ConcreteAttributePath(kMockEndpoint1, kInvalidClusterId, FeatureMap::Id)).has_value()); + ASSERT_FALSE(finder.Find(ConcreteAttributePath(kMockEndpoint1, MockClusterId(10), FeatureMap::Id)).has_value()); + ASSERT_FALSE(finder.Find(ConcreteAttributePath(kMockEndpoint1, MockClusterId(10), kInvalidAttributeId)).has_value()); + ASSERT_FALSE(finder.Find(ConcreteAttributePath(kMockEndpoint1, MockClusterId(1), MockAttributeId(10))).has_value()); // valid info - std::optional info = - model.GetAttributeInfo(ConcreteAttributePath(kMockEndpoint1, MockClusterId(1), FeatureMap::Id)); + std::optional info = finder.Find(ConcreteAttributePath(kMockEndpoint1, MockClusterId(1), FeatureMap::Id)); ASSERT_TRUE(info.has_value()); EXPECT_FALSE(info->flags.Has(AttributeQualityFlags::kListAttribute)); // NOLINT(bugprone-unchecked-optional-access) @@ -1224,14 +1061,14 @@ TEST(TestCodegenModelViaMocks, GetAttributeInfo) EXPECT_EQ(info->readPrivilege, chip::Access::Privilege::kAdminister); // NOLINT(bugprone-unchecked-optional-access) EXPECT_EQ(info->writePrivilege, chip::Access::Privilege::kAdminister); // NOLINT(bugprone-unchecked-optional-access) - info = model.GetAttributeInfo(ConcreteAttributePath(kMockEndpoint2, MockClusterId(2), MockAttributeId(2))); + info = finder.Find(ConcreteAttributePath(kMockEndpoint2, MockClusterId(2), MockAttributeId(2))); ASSERT_TRUE(info.has_value()); EXPECT_TRUE(info->flags.Has(AttributeQualityFlags::kListAttribute)); // NOLINT(bugprone-unchecked-optional-access) EXPECT_EQ(info->readPrivilege, chip::Access::Privilege::kAdminister); // NOLINT(bugprone-unchecked-optional-access) EXPECT_EQ(info->writePrivilege, chip::Access::Privilege::kAdminister); // NOLINT(bugprone-unchecked-optional-access) // test a read-only attribute, which will not have a write privilege - info = model.GetAttributeInfo(ConcreteAttributePath(kMockEndpoint3, MockClusterId(3), kReadOnlyAttributeId)); + info = finder.Find(ConcreteAttributePath(kMockEndpoint3, MockClusterId(3), kReadOnlyAttributeId)); ASSERT_TRUE(info.has_value()); EXPECT_FALSE(info->flags.Has(AttributeQualityFlags::kListAttribute)); // NOLINT(bugprone-unchecked-optional-access) EXPECT_EQ(info->readPrivilege, chip::Access::Privilege::kAdminister); // NOLINT(bugprone-unchecked-optional-access) @@ -1239,178 +1076,87 @@ TEST(TestCodegenModelViaMocks, GetAttributeInfo) } // global attributes are EXPLICITLY not supported -TEST(TestCodegenModelViaMocks, GlobalAttributeInfo) +TEST_F(TestCodegenModelViaMocks, GlobalAttributeInfo) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; - std::optional info = model.GetAttributeInfo( + AttributeFinder finder(&model); + + std::optional info = finder.Find( ConcreteAttributePath(kMockEndpoint1, MockClusterId(1), Clusters::Globals::Attributes::GeneratedCommandList::Id)); ASSERT_FALSE(info.has_value()); - info = model.GetAttributeInfo( - ConcreteAttributePath(kMockEndpoint1, MockClusterId(1), Clusters::Globals::Attributes::AttributeList::Id)); + info = finder.Find(ConcreteAttributePath(kMockEndpoint1, MockClusterId(1), Clusters::Globals::Attributes::AttributeList::Id)); ASSERT_FALSE(info.has_value()); } -TEST(TestCodegenModelViaMocks, IterateOverAcceptedCommands) +TEST_F(TestCodegenModelViaMocks, IterateOverAcceptedCommands) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; - // invalid paths should return in "no more data" - ASSERT_FALSE(model.FirstAcceptedCommand(ConcreteClusterPath(kEndpointIdThatIsMissing, MockClusterId(1))).path.HasValidIds()); - ASSERT_FALSE(model.FirstAcceptedCommand(ConcreteClusterPath(kInvalidEndpointId, MockClusterId(1))).path.HasValidIds()); - ASSERT_FALSE(model.FirstAcceptedCommand(ConcreteClusterPath(kMockEndpoint1, MockClusterId(10))).path.HasValidIds()); - ASSERT_FALSE(model.FirstAcceptedCommand(ConcreteClusterPath(kMockEndpoint1, kInvalidClusterId)).path.HasValidIds()); - - // should be able to iterate over valid paths - CommandEntry entry = model.FirstAcceptedCommand(ConcreteClusterPath(kMockEndpoint2, MockClusterId(2))); - ASSERT_TRUE(entry.path.HasValidIds()); - EXPECT_EQ(entry.path.mEndpointId, kMockEndpoint2); - EXPECT_EQ(entry.path.mClusterId, MockClusterId(2)); - EXPECT_EQ(entry.path.mCommandId, 1u); - - entry = model.NextAcceptedCommand(entry.path); - ASSERT_TRUE(entry.path.HasValidIds()); - EXPECT_EQ(entry.path.mEndpointId, kMockEndpoint2); - EXPECT_EQ(entry.path.mClusterId, MockClusterId(2)); - EXPECT_EQ(entry.path.mCommandId, 2u); - - entry = model.NextAcceptedCommand(entry.path); - ASSERT_TRUE(entry.path.HasValidIds()); - EXPECT_EQ(entry.path.mEndpointId, kMockEndpoint2); - EXPECT_EQ(entry.path.mClusterId, MockClusterId(2)); - EXPECT_EQ(entry.path.mCommandId, 23u); - - entry = model.NextAcceptedCommand(entry.path); - ASSERT_FALSE(entry.path.HasValidIds()); - - // attempt some out-of-order requests as well - entry = model.FirstAcceptedCommand(ConcreteClusterPath(kMockEndpoint2, MockClusterId(3))); - ASSERT_TRUE(entry.path.HasValidIds()); - EXPECT_EQ(entry.path.mEndpointId, kMockEndpoint2); - EXPECT_EQ(entry.path.mClusterId, MockClusterId(3)); - EXPECT_EQ(entry.path.mCommandId, 11u); - - for (int i = 0; i < 10; i++) - { - entry = model.NextAcceptedCommand(ConcreteCommandPath(kMockEndpoint2, MockClusterId(2), 2)); - ASSERT_TRUE(entry.path.HasValidIds()); - EXPECT_EQ(entry.path.mEndpointId, kMockEndpoint2); - EXPECT_EQ(entry.path.mClusterId, MockClusterId(2)); - EXPECT_EQ(entry.path.mCommandId, 23u); - } - - for (int i = 0; i < 10; i++) - { - entry = model.NextAcceptedCommand(ConcreteCommandPath(kMockEndpoint2, MockClusterId(2), 1)); - ASSERT_TRUE(entry.path.HasValidIds()); - EXPECT_EQ(entry.path.mEndpointId, kMockEndpoint2); - EXPECT_EQ(entry.path.mClusterId, MockClusterId(2)); - EXPECT_EQ(entry.path.mCommandId, 2u); - } - - for (int i = 0; i < 10; i++) - { - entry = model.NextAcceptedCommand(ConcreteCommandPath(kMockEndpoint2, MockClusterId(3), 10)); - EXPECT_FALSE(entry.path.HasValidIds()); - } -} - -TEST(TestCodegenModelViaMocks, AcceptedCommandInfo) -{ - UseMockNodeConfig config(gTestNodeConfig); - CodegenDataModelProviderWithContext model; + DataModel::ListBuilder builder; // invalid paths should return in "no more data" - ASSERT_FALSE(model.GetAcceptedCommandInfo(ConcreteCommandPath(kEndpointIdThatIsMissing, MockClusterId(1), 1)).has_value()); - ASSERT_FALSE(model.GetAcceptedCommandInfo(ConcreteCommandPath(kInvalidEndpointId, MockClusterId(1), 1)).has_value()); - ASSERT_FALSE(model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint1, MockClusterId(10), 1)).has_value()); - ASSERT_FALSE(model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint1, kInvalidClusterId, 1)).has_value()); - ASSERT_FALSE( - model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint1, MockClusterId(1), kInvalidCommandId)).has_value()); - - std::optional info = model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint2, MockClusterId(2), 1u)); - ASSERT_TRUE(info.has_value()); + ASSERT_EQ(model.AcceptedCommands(ConcreteClusterPath(kEndpointIdThatIsMissing, MockClusterId(1)), builder), + CHIP_ERROR_NOT_FOUND); + ASSERT_TRUE(builder.IsEmpty()); + ASSERT_EQ(model.AcceptedCommands(ConcreteClusterPath(kInvalidEndpointId, MockClusterId(1)), builder), CHIP_ERROR_NOT_FOUND); + ASSERT_TRUE(builder.IsEmpty()); + ASSERT_EQ(model.AcceptedCommands(ConcreteClusterPath(kMockEndpoint1, MockClusterId(10)), builder), CHIP_ERROR_NOT_FOUND); + ASSERT_TRUE(builder.IsEmpty()); + ASSERT_EQ(model.AcceptedCommands(ConcreteClusterPath(kMockEndpoint1, kInvalidClusterId), builder), CHIP_ERROR_NOT_FOUND); + ASSERT_TRUE(builder.IsEmpty()); - info = model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint2, MockClusterId(2), 2u)); - ASSERT_TRUE(info.has_value()); + ASSERT_EQ(model.AcceptedCommands(ConcreteClusterPath(kMockEndpoint2, MockClusterId(2)), builder), CHIP_NO_ERROR); + ASSERT_EQ(builder.Size(), 3u); - info = model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint2, MockClusterId(2), 1u)); - ASSERT_TRUE(info.has_value()); + auto cmds = builder.TakeBuffer(); - info = model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint2, MockClusterId(2), 1u)); - ASSERT_TRUE(info.has_value()); + // took ownership + ASSERT_EQ(builder.Size(), 0u); + ASSERT_TRUE(builder.IsEmpty()); - info = model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint2, MockClusterId(2), 23u)); - ASSERT_TRUE(info.has_value()); - - info = model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint2, MockClusterId(2), 1234u)); - ASSERT_FALSE(info.has_value()); + ASSERT_EQ(cmds.size(), 3u); + ASSERT_EQ(cmds[0].commandId, 1u); + ASSERT_EQ(cmds[1].commandId, 2u); + ASSERT_EQ(cmds[2].commandId, 23u); } -TEST(TestCodegenModelViaMocks, IterateOverGeneratedCommands) +TEST_F(TestCodegenModelViaMocks, IterateOverGeneratedCommands) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; + DataModel::ListBuilder builder; + // invalid paths should return in "no more data" - ASSERT_FALSE(model.FirstGeneratedCommand(ConcreteClusterPath(kEndpointIdThatIsMissing, MockClusterId(1))).HasValidIds()); - ASSERT_FALSE(model.FirstGeneratedCommand(ConcreteClusterPath(kInvalidEndpointId, MockClusterId(1))).HasValidIds()); - ASSERT_FALSE(model.FirstGeneratedCommand(ConcreteClusterPath(kMockEndpoint1, MockClusterId(10))).HasValidIds()); - ASSERT_FALSE(model.FirstGeneratedCommand(ConcreteClusterPath(kMockEndpoint1, kInvalidClusterId)).HasValidIds()); + ASSERT_EQ(model.GeneratedCommands(ConcreteClusterPath(kEndpointIdThatIsMissing, MockClusterId(1)), builder), + CHIP_ERROR_NOT_FOUND); + ASSERT_TRUE(builder.IsEmpty()); + ASSERT_EQ(model.GeneratedCommands(ConcreteClusterPath(kInvalidEndpointId, MockClusterId(1)), builder), CHIP_ERROR_NOT_FOUND); + ASSERT_TRUE(builder.IsEmpty()); + ASSERT_EQ(model.GeneratedCommands(ConcreteClusterPath(kMockEndpoint1, MockClusterId(10)), builder), CHIP_ERROR_NOT_FOUND); + ASSERT_TRUE(builder.IsEmpty()); + ASSERT_EQ(model.GeneratedCommands(ConcreteClusterPath(kMockEndpoint1, kInvalidClusterId), builder), CHIP_ERROR_NOT_FOUND); + ASSERT_TRUE(builder.IsEmpty()); // should be able to iterate over valid paths - ConcreteCommandPath path = model.FirstGeneratedCommand(ConcreteClusterPath(kMockEndpoint2, MockClusterId(2))); - ASSERT_TRUE(path.HasValidIds()); - EXPECT_EQ(path.mEndpointId, kMockEndpoint2); - EXPECT_EQ(path.mClusterId, MockClusterId(2)); - EXPECT_EQ(path.mCommandId, 2u); - - path = model.NextGeneratedCommand(path); - ASSERT_TRUE(path.HasValidIds()); - EXPECT_EQ(path.mEndpointId, kMockEndpoint2); - EXPECT_EQ(path.mClusterId, MockClusterId(2)); - EXPECT_EQ(path.mCommandId, 10u); - - path = model.NextGeneratedCommand(path); - ASSERT_FALSE(path.HasValidIds()); - - // attempt some out-of-order requests as well - path = model.FirstGeneratedCommand(ConcreteClusterPath(kMockEndpoint2, MockClusterId(3))); - ASSERT_TRUE(path.HasValidIds()); - EXPECT_EQ(path.mEndpointId, kMockEndpoint2); - EXPECT_EQ(path.mClusterId, MockClusterId(3)); - EXPECT_EQ(path.mCommandId, 4u); - - for (int i = 0; i < 10; i++) - { - path = model.NextGeneratedCommand(ConcreteCommandPath(kMockEndpoint2, MockClusterId(2), 2)); - ASSERT_TRUE(path.HasValidIds()); - EXPECT_EQ(path.mEndpointId, kMockEndpoint2); - EXPECT_EQ(path.mClusterId, MockClusterId(2)); - EXPECT_EQ(path.mCommandId, 10u); - } + ASSERT_EQ(model.GeneratedCommands(ConcreteClusterPath(kMockEndpoint2, MockClusterId(2)), builder), CHIP_NO_ERROR); + auto cmds = builder.TakeBuffer(); - for (int i = 0; i < 10; i++) - { - path = model.NextGeneratedCommand(ConcreteCommandPath(kMockEndpoint2, MockClusterId(3), 4)); - ASSERT_TRUE(path.HasValidIds()); - EXPECT_EQ(path.mEndpointId, kMockEndpoint2); - EXPECT_EQ(path.mClusterId, MockClusterId(3)); - EXPECT_EQ(path.mCommandId, 6u); - } + const CommandId expectedCommands2[] = { 2, 10 }; + ASSERT_TRUE(cmds.data_equal(Span(expectedCommands2))); - for (int i = 0; i < 10; i++) - { - path = model.NextGeneratedCommand(ConcreteCommandPath(kMockEndpoint2, MockClusterId(3), 6)); - EXPECT_FALSE(path.HasValidIds()); - } + ASSERT_EQ(model.GeneratedCommands(ConcreteClusterPath(kMockEndpoint2, MockClusterId(3)), builder), CHIP_NO_ERROR); + cmds = builder.TakeBuffer(); + const CommandId expectedCommands3[] = { 4, 6 }; + ASSERT_TRUE(cmds.data_equal(Span(expectedCommands3))); } -TEST(TestCodegenModelViaMocks, CommandHandlerInterfaceAcceptedCommands) +TEST_F(TestCodegenModelViaMocks, CommandHandlerInterfaceCommandHandling) { UseMockNodeConfig config(gTestNodeConfig); @@ -1420,18 +1166,23 @@ TEST(TestCodegenModelViaMocks, CommandHandlerInterfaceAcceptedCommands) // Validate that these work CustomListCommandHandler handler(MakeOptional(kMockEndpoint1), MockClusterId(1)); + DataModel::ListBuilder generatedBuilder; + DataModel::ListBuilder acceptedBuilder; + // At this point, without overrides, there should be no accepted/generated commands - EXPECT_FALSE(model.FirstAcceptedCommand(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1))).IsValid()); - EXPECT_FALSE(model.FirstGeneratedCommand(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1))).HasValidIds()); - EXPECT_FALSE(model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint1, MockClusterId(1), 1234)).has_value()); + ASSERT_EQ(model.GeneratedCommands(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1)), generatedBuilder), CHIP_NO_ERROR); + ASSERT_TRUE(generatedBuilder.IsEmpty()); + ASSERT_EQ(model.AcceptedCommands(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1)), acceptedBuilder), CHIP_NO_ERROR); + ASSERT_TRUE(acceptedBuilder.IsEmpty()); handler.SetOverrideAccepted(true); handler.SetOverrideGenerated(true); // with overrides, the list is still empty ... - EXPECT_FALSE(model.FirstAcceptedCommand(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1))).IsValid()); - EXPECT_FALSE(model.FirstGeneratedCommand(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1))).HasValidIds()); - EXPECT_FALSE(model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint1, MockClusterId(1), 1234)).has_value()); + ASSERT_EQ(model.GeneratedCommands(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1)), generatedBuilder), CHIP_NO_ERROR); + ASSERT_TRUE(generatedBuilder.IsEmpty()); + ASSERT_EQ(model.AcceptedCommands(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1)), acceptedBuilder), CHIP_NO_ERROR); + ASSERT_TRUE(acceptedBuilder.IsEmpty()); // set some overrides handler.AcceptedVec().push_back(1234); @@ -1439,32 +1190,20 @@ TEST(TestCodegenModelViaMocks, CommandHandlerInterfaceAcceptedCommands) handler.GeneratedVec().push_back(33); - DataModel::CommandEntry entry; - - entry = model.FirstAcceptedCommand(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1))); - EXPECT_TRUE(entry.IsValid()); - EXPECT_EQ(entry.path, ConcreteCommandPath(kMockEndpoint1, MockClusterId(1), 1234)); + ASSERT_EQ(model.AcceptedCommands(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1)), acceptedBuilder), CHIP_NO_ERROR); + auto acceptedCommands = acceptedBuilder.TakeBuffer(); - entry = model.NextAcceptedCommand(entry.path); - EXPECT_TRUE(entry.IsValid()); - EXPECT_EQ(entry.path, ConcreteCommandPath(kMockEndpoint1, MockClusterId(1), 999)); + ASSERT_EQ(acceptedCommands.size(), 2u); + ASSERT_EQ(acceptedCommands[0].commandId, 1234u); + ASSERT_EQ(acceptedCommands[1].commandId, 999u); - entry = model.NextAcceptedCommand(entry.path); - EXPECT_FALSE(entry.IsValid()); - - ConcreteCommandPath path = model.FirstGeneratedCommand(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1))); - EXPECT_TRUE(path.HasValidIds()); - EXPECT_EQ(path, ConcreteCommandPath(kMockEndpoint1, MockClusterId(1), 33)); - path = model.NextGeneratedCommand(path); - EXPECT_FALSE(path.HasValidIds()); - - // Command finding should work - EXPECT_TRUE(model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint1, MockClusterId(1), 1234)).has_value()); - EXPECT_FALSE(model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint1, MockClusterId(1), 88)).has_value()); - EXPECT_FALSE(model.GetAcceptedCommandInfo(ConcreteCommandPath(kMockEndpoint1, MockClusterId(1), 33)).has_value()); + ASSERT_EQ(model.GeneratedCommands(ConcreteClusterPath(kMockEndpoint1, MockClusterId(1)), generatedBuilder), CHIP_NO_ERROR); + auto generatedCommands = generatedBuilder.TakeBuffer(); + const CommandId expectedGeneratedCommands[] = { 33 }; + ASSERT_TRUE(generatedCommands.data_equal(Span(expectedGeneratedCommands))); } -TEST(TestCodegenModelViaMocks, ReadForInvalidGlobalAttributePath) +TEST_F(TestCodegenModelViaMocks, ReadForInvalidGlobalAttributePath) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -1487,7 +1226,7 @@ TEST(TestCodegenModelViaMocks, ReadForInvalidGlobalAttributePath) } } -TEST(TestCodegenModelViaMocks, EmberAttributeInvalidRead) +TEST_F(TestCodegenModelViaMocks, EmberAttributeInvalidRead) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -1521,7 +1260,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeInvalidRead) } } -TEST(TestCodegenModelViaMocks, AccessInterfaceUnsupportedRead) +TEST_F(TestCodegenModelViaMocks, AccessInterfaceUnsupportedRead) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -1544,63 +1283,63 @@ TEST(TestCodegenModelViaMocks, AccessInterfaceUnsupportedRead) ASSERT_FALSE(encoder->TriedEncode()); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadInt32S) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadInt32S) { TestEmberScalarTypeRead(-1234); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadEnum16) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadEnum16) { TestEmberScalarTypeRead(0x1234); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadFloat) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadFloat) { TestEmberScalarTypeRead(0.625); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadDouble) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadDouble) { TestEmberScalarTypeRead(0.625); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadInt24U) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadInt24U) { TestEmberScalarTypeRead, ZCL_INT24U_ATTRIBUTE_TYPE>(0x1234AB); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadInt32U) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadInt32U) { TestEmberScalarTypeRead(0x1234ABCD); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadInt40U) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadInt40U) { TestEmberScalarTypeRead, ZCL_INT40U_ATTRIBUTE_TYPE>(0x1122334455); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadInt48U) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadInt48U) { TestEmberScalarTypeRead, ZCL_INT48U_ATTRIBUTE_TYPE>(0xAABB11223344); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadInt56U) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadInt56U) { TestEmberScalarTypeRead, ZCL_INT56U_ATTRIBUTE_TYPE>(0xAABB11223344); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadBool) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadBool) { TestEmberScalarTypeRead(true); TestEmberScalarTypeRead(false); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadInt8U) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadInt8U) { TestEmberScalarTypeRead(0x12); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadNulls) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadNulls) { TestEmberScalarNullRead(); TestEmberScalarNullRead(); @@ -1626,7 +1365,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeReadNulls) TestEmberScalarNullRead(); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadErrorReading) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadErrorReading) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -1660,7 +1399,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeReadErrorReading) chip::Test::SetEmberReadOutput(ByteSpan()); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadNullOctetString) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadNullOctetString) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -1695,7 +1434,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeReadNullOctetString) ASSERT_TRUE(actual.IsNull()); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadOctetString) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadOctetString) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -1733,7 +1472,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeReadOctetString) ASSERT_TRUE(actual.data_equal(expected)); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadLongOctetString) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadLongOctetString) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -1770,7 +1509,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeReadLongOctetString) ASSERT_TRUE(actual.data_equal(expected)); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadShortString) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadShortString) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -1806,7 +1545,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeReadShortString) ASSERT_TRUE(actual.data_equal("abcde"_span)); } -TEST(TestCodegenModelViaMocks, EmberAttributeReadLongString) +TEST_F(TestCodegenModelViaMocks, EmberAttributeReadLongString) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -1842,7 +1581,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeReadLongString) ASSERT_TRUE(actual.data_equal("abcde"_span)); } -TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceStructRead) +TEST_F(TestCodegenModelViaMocks, AttributeAccessInterfaceStructRead) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -1887,7 +1626,7 @@ TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceStructRead) ASSERT_TRUE(actual.e.data_equal("foo"_span)); } -TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceReadError) +TEST_F(TestCodegenModelViaMocks, AttributeAccessInterfaceReadError) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -1904,7 +1643,7 @@ TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceReadError) ASSERT_EQ(model.ReadAttribute(testRequest.GetRequest(), *encoder), CHIP_ERROR_KEY_NOT_FOUND); } -TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceListRead) +TEST_F(TestCodegenModelViaMocks, AttributeAccessInterfaceListRead) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -1958,7 +1697,7 @@ TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceListRead) } } -TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceListOverflowRead) +TEST_F(TestCodegenModelViaMocks, AttributeAccessInterfaceListOverflowRead) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2018,7 +1757,7 @@ TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceListOverflowRead) } } -TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceListIncrementalRead) +TEST_F(TestCodegenModelViaMocks, AttributeAccessInterfaceListIncrementalRead) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2083,7 +1822,7 @@ TEST(TestCodegenModelViaMocks, AttributeAccessInterfaceListIncrementalRead) } } -TEST(TestCodegenModelViaMocks, ReadGlobalAttributeAttributeList) +TEST_F(TestCodegenModelViaMocks, ReadGlobalAttributeAttributeList) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2139,7 +1878,7 @@ TEST(TestCodegenModelViaMocks, ReadGlobalAttributeAttributeList) } } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteAclDeny) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteAclDeny) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2162,7 +1901,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteAclDeny) ASSERT_TRUE(model.ChangeListener().DirtyList().empty()); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteBasicTypes) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteBasicTypes) { TestEmberScalarTypeWrite(0x12); TestEmberScalarTypeWrite(0x1234); @@ -2188,7 +1927,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteBasicTypes) TestEmberScalarTypeWrite(0.625); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteInvalidValueToNullable) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteInvalidValueToNullable) { TestEmberScalarTypeWriteNullValueToNullable(); TestEmberScalarTypeWriteNullValueToNullable(); @@ -2214,7 +1953,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteInvalidValueToNullable) TestEmberScalarTypeWriteNullValueToNullable(); } -TEST(TestCodegenModelViaMocks, EmberTestWriteReservedNullPlaceholderToNullable) +TEST_F(TestCodegenModelViaMocks, EmberTestWriteReservedNullPlaceholderToNullable) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2231,7 +1970,7 @@ TEST(TestCodegenModelViaMocks, EmberTestWriteReservedNullPlaceholderToNullable) ASSERT_EQ(model.WriteAttribute(test.GetRequest(), decoder), Status::ConstraintError); } -TEST(TestCodegenModelViaMocks, EmberTestWriteOutOfRepresentableRangeOddIntegerNonNullable) +TEST_F(TestCodegenModelViaMocks, EmberTestWriteOutOfRepresentableRangeOddIntegerNonNullable) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2247,7 +1986,7 @@ TEST(TestCodegenModelViaMocks, EmberTestWriteOutOfRepresentableRangeOddIntegerNo ASSERT_EQ(model.WriteAttribute(test.GetRequest(), decoder), CHIP_IM_GLOBAL_STATUS(ConstraintError)); } -TEST(TestCodegenModelViaMocks, EmberTestWriteOutOfRepresentableRangeOddIntegerNullable) +TEST_F(TestCodegenModelViaMocks, EmberTestWriteOutOfRepresentableRangeOddIntegerNullable) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2263,7 +2002,7 @@ TEST(TestCodegenModelViaMocks, EmberTestWriteOutOfRepresentableRangeOddIntegerNu ASSERT_EQ(model.WriteAttribute(test.GetRequest(), decoder), CHIP_IM_GLOBAL_STATUS(ConstraintError)); } -TEST(TestCodegenModelViaMocksNullValueToNullables, EmberAttributeWriteBasicTypesLowestValue) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteBasicTypesLowestValue) { TestEmberScalarTypeWrite(-127); TestEmberScalarTypeWrite(-32767); @@ -2275,7 +2014,7 @@ TEST(TestCodegenModelViaMocksNullValueToNullables, EmberAttributeWriteBasicTypes TestEmberScalarTypeWrite(-9223372036854775807); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteNulls) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteNulls) { TestEmberScalarNullWrite(); TestEmberScalarNullWrite(); @@ -2299,7 +2038,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteNulls) TestEmberScalarNullWrite(); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteShortString) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteShortString) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2316,7 +2055,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteShortString) ASSERT_TRUE(asCharSpan.data_equal("\x0Bhello world"_span)); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteLongStringOutOfBounds) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteLongStringOutOfBounds) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2333,7 +2072,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteLongStringOutOfBounds) ASSERT_EQ(model.WriteAttribute(test.GetRequest(), decoder), Status::InvalidValue); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteLongString) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteLongString) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2355,7 +2094,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteLongString) ASSERT_TRUE(asCharSpan.data_equal("text"_span)); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteNullableLongStringValue) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteNullableLongStringValue) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2377,7 +2116,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteNullableLongStringValue) ASSERT_TRUE(asCharSpan.data_equal("text"_span)); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteLongNullableStringNull) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteLongNullableStringNull) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2395,7 +2134,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteLongNullableStringNull) ASSERT_EQ(writtenData[1], 0xFF); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteShortBytes) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteShortBytes) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2417,7 +2156,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteShortBytes) EXPECT_EQ(writtenData[3], 13u); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteLongBytes) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteLongBytes) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2442,7 +2181,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteLongBytes) EXPECT_EQ(writtenData[4], 13u); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteTimedWrite) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteTimedWrite) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2460,7 +2199,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteTimedWrite) ASSERT_EQ(model.WriteAttribute(test.GetRequest(), decoder), CHIP_NO_ERROR); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteReadOnlyAttribute) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteReadOnlyAttribute) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2478,7 +2217,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteReadOnlyAttribute) ASSERT_EQ(model.WriteAttribute(test.GetRequest(), decoder), CHIP_NO_ERROR); } -TEST(TestCodegenModelViaMocks, EmberAttributeWriteDataVersion) +TEST_F(TestCodegenModelViaMocks, EmberAttributeWriteDataVersion) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2504,7 +2243,7 @@ TEST(TestCodegenModelViaMocks, EmberAttributeWriteDataVersion) ASSERT_EQ(model.WriteAttribute(test.GetRequest(), decoder), CHIP_NO_ERROR); } -TEST(TestCodegenModelViaMocks, WriteToInvalidPath) +TEST_F(TestCodegenModelViaMocks, WriteToInvalidPath) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2534,7 +2273,7 @@ TEST(TestCodegenModelViaMocks, WriteToInvalidPath) } } -TEST(TestCodegenModelViaMocks, WriteToGlobalAttribute) +TEST_F(TestCodegenModelViaMocks, WriteToGlobalAttribute) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2547,7 +2286,7 @@ TEST(TestCodegenModelViaMocks, WriteToGlobalAttribute) ASSERT_EQ(model.WriteAttribute(test.GetRequest(), decoder), Status::UnsupportedWrite); } -TEST(TestCodegenModelViaMocks, EmberWriteFailure) +TEST_F(TestCodegenModelViaMocks, EmberWriteFailure) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2570,7 +2309,7 @@ TEST(TestCodegenModelViaMocks, EmberWriteFailure) chip::Test::SetEmberReadOutput(ByteSpan()); } -TEST(TestCodegenModelViaMocks, EmberWriteAttributeAccessInterfaceTest) +TEST_F(TestCodegenModelViaMocks, EmberWriteAttributeAccessInterfaceTest) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2609,7 +2348,7 @@ TEST(TestCodegenModelViaMocks, EmberWriteAttributeAccessInterfaceTest) TestEmberScalarNullWrite(); } -TEST(TestCodegenModelViaMocks, EmberInvokeTest) +TEST_F(TestCodegenModelViaMocks, EmberInvokeTest) { // Ember invoke is fully code-generated - there is a single function for Dispatch // that will do a `switch` on the path elements and invoke a corresponding `emberAf*` @@ -2650,7 +2389,7 @@ TEST(TestCodegenModelViaMocks, EmberInvokeTest) } } -TEST(TestCodegenModelViaMocks, EmberWriteAttributeAccessInterfaceReturningError) +TEST_F(TestCodegenModelViaMocks, EmberWriteAttributeAccessInterfaceReturningError) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2676,7 +2415,7 @@ TEST(TestCodegenModelViaMocks, EmberWriteAttributeAccessInterfaceReturningError) ASSERT_TRUE(model.ChangeListener().DirtyList().empty()); } -TEST(TestCodegenModelViaMocks, EmberWriteInvalidDataType) +TEST_F(TestCodegenModelViaMocks, EmberWriteInvalidDataType) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; @@ -2704,104 +2443,77 @@ TEST(TestCodegenModelViaMocks, EmberWriteInvalidDataType) ASSERT_TRUE(model.ChangeListener().DirtyList().empty()); } -TEST(TestCodegenModelViaMocks, DeviceTypeIteration) +TEST_F(TestCodegenModelViaMocks, DeviceTypeIteration) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; // Mock endpoint 1 has 3 device types - std::optional entry = model.FirstDeviceType(kMockEndpoint1); - ASSERT_EQ(entry, - std::make_optional(DeviceTypeEntry{ .deviceTypeId = kDeviceTypeId1, .deviceTypeRevision = kDeviceTypeId1Version })); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access): Assert above that this is not none - entry = model.NextDeviceType(kMockEndpoint1, *entry); - ASSERT_EQ(entry, - std::make_optional(DeviceTypeEntry{ .deviceTypeId = kDeviceTypeId2, .deviceTypeRevision = kDeviceTypeId2Version })); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access): Assert above that this is not none - entry = model.NextDeviceType(kMockEndpoint1, *entry); - ASSERT_EQ(entry, - std::make_optional(DeviceTypeEntry{ .deviceTypeId = kDeviceTypeId3, .deviceTypeRevision = kDeviceTypeId3Version })); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access): Assert above that this is not none - entry = model.NextDeviceType(kMockEndpoint1, *entry); - ASSERT_FALSE(entry.has_value()); + DataModel::ListBuilder builder; + ASSERT_EQ(model.DeviceTypes(kMockEndpoint1, builder), CHIP_NO_ERROR); + auto deviceTypes = builder.TakeBuffer(); + ASSERT_EQ(deviceTypes.size(), 3u); + + const DeviceTypeEntry expected1[] = { + { .deviceTypeId = kDeviceTypeId1, .deviceTypeRevision = kDeviceTypeId1Version }, + { .deviceTypeId = kDeviceTypeId2, .deviceTypeRevision = kDeviceTypeId2Version }, + { .deviceTypeId = kDeviceTypeId3, .deviceTypeRevision = kDeviceTypeId3Version }, + }; + for (unsigned i = 0; i < 3; i++) + { + ASSERT_EQ(deviceTypes[i], expected1[i]); + } // Mock endpoint 2 has 1 device types - entry = model.FirstDeviceType(kMockEndpoint2); - ASSERT_EQ(entry, - std::make_optional(DeviceTypeEntry{ .deviceTypeId = kDeviceTypeId2, .deviceTypeRevision = kDeviceTypeId2Version })); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access): Assert above that this is not none - entry = model.NextDeviceType(kMockEndpoint2, *entry); - ASSERT_FALSE(entry.has_value()); - - // out of order query works - entry = std::make_optional(DeviceTypeEntry{ .deviceTypeId = kDeviceTypeId2, .deviceTypeRevision = kDeviceTypeId2Version }); - entry = model.NextDeviceType(kMockEndpoint1, *entry); - ASSERT_EQ(entry, - std::make_optional(DeviceTypeEntry{ .deviceTypeId = kDeviceTypeId3, .deviceTypeRevision = kDeviceTypeId3Version })); - - // invalid query fails - entry = std::make_optional(DeviceTypeEntry{ .deviceTypeId = kDeviceTypeId1, .deviceTypeRevision = kDeviceTypeId1Version }); - entry = model.NextDeviceType(kMockEndpoint2, *entry); - ASSERT_FALSE(entry.has_value()); + ASSERT_TRUE(builder.IsEmpty()); // ownership taken above, we start fresh + ASSERT_EQ(model.DeviceTypes(kMockEndpoint2, builder), CHIP_NO_ERROR); + deviceTypes = builder.TakeBuffer(); + ASSERT_EQ(deviceTypes.size(), 1u); + const DeviceTypeEntry expected2 = { .deviceTypeId = kDeviceTypeId2, .deviceTypeRevision = kDeviceTypeId2Version }; + ASSERT_EQ(deviceTypes[0], expected2); // empty endpoint works - entry = model.FirstDeviceType(kMockEndpoint3); - ASSERT_FALSE(entry.has_value()); + ASSERT_TRUE(builder.IsEmpty()); // ownership taken above, we start fresh + ASSERT_EQ(model.DeviceTypes(kMockEndpoint3, builder), CHIP_NO_ERROR); + ASSERT_TRUE(builder.IsEmpty()); + ASSERT_TRUE(builder.TakeBuffer().empty()); } -TEST(TestCodegenModelViaMocks, SemanticTagIteration) +TEST_F(TestCodegenModelViaMocks, SemanticTagIteration) { UseMockNodeConfig config(gTestNodeConfig); CodegenDataModelProviderWithContext model; - // Mock endpoint 1 has 3 semantic tags - std::optional tag = model.GetFirstSemanticTag(kMockEndpoint1); - ASSERT_TRUE(tag.has_value()); - EXPECT_EQ(tag->mfgCode, MakeNullable(VendorId::TestVendor1)); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(tag->namespaceID, kNamespaceID1); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(tag->tag, kTag1); // NOLINT(bugprone-unchecked-optional-access) - ASSERT_TRUE(tag->label.HasValue() && (!tag->label.Value().IsNull())); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_TRUE( - tag->label.Value().Value().data_equal(CharSpan::fromCharString(kLabel1))); // NOLINT(bugprone-unchecked-optional-access) - tag = model.GetNextSemanticTag(kMockEndpoint1, *tag); // NOLINT(bugprone-unchecked-optional-access) - ASSERT_TRUE(tag.has_value()); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_TRUE(tag->mfgCode.IsNull()); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(tag->namespaceID, kNamespaceID2); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(tag->tag, kTag2); // NOLINT(bugprone-unchecked-optional-access) - ASSERT_TRUE(tag->label.HasValue() && (!tag->label.Value().IsNull())); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_TRUE( - tag->label.Value().Value().data_equal(CharSpan::fromCharString(kLabel2))); // NOLINT(bugprone-unchecked-optional-access) - tag = model.GetNextSemanticTag(kMockEndpoint1, *tag); // NOLINT(bugprone-unchecked-optional-access) - ASSERT_TRUE(tag.has_value()); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(tag->mfgCode, MakeNullable(VendorId::TestVendor3)); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(tag->namespaceID, kNamespaceID3); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(tag->tag, kTag3); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_FALSE(tag->label.HasValue()); // NOLINT(bugprone-unchecked-optional-access) - tag = model.GetNextSemanticTag(kMockEndpoint1, *tag); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_FALSE(tag.has_value()); - - // out of order query works - DataModel::Provider::SemanticTag existTag = { - .mfgCode = MakeNullable(VendorId::TestVendor1), - .namespaceID = kNamespaceID1, - .tag = kTag1, - .label = MakeOptional(MakeNullable(CharSpan::fromCharString(kLabel1))), - }; - tag = model.GetNextSemanticTag(kMockEndpoint1, existTag); - ASSERT_TRUE(tag.has_value()); - EXPECT_TRUE(tag->mfgCode.IsNull()); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(tag->namespaceID, kNamespaceID2); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_EQ(tag->tag, kTag2); // NOLINT(bugprone-unchecked-optional-access) - ASSERT_TRUE(tag->label.HasValue() && (!tag->label.Value().IsNull())); // NOLINT(bugprone-unchecked-optional-access) - EXPECT_TRUE( - tag->label.Value().Value().data_equal(CharSpan::fromCharString(kLabel2))); // NOLINT(bugprone-unchecked-optional-access) - - // invalid query fails - existTag.tag = kTag2; - tag = model.GetNextSemanticTag(kMockEndpoint1, existTag); - ASSERT_FALSE(tag.has_value()); + DataModel::ListBuilder builder; + ASSERT_EQ(model.SemanticTags(kMockEndpoint2, builder), CHIP_NO_ERROR); + ASSERT_TRUE(builder.IsEmpty()); + auto tags = builder.TakeBuffer(); + ASSERT_TRUE(tags.empty()); - // empty endpoint works - tag = model.GetFirstSemanticTag(kMockEndpoint2); - ASSERT_FALSE(tag.has_value()); + // Mock endpoint 1 has 3 semantic tags + ASSERT_EQ(model.SemanticTags(kMockEndpoint1, builder), CHIP_NO_ERROR); + ASSERT_EQ(builder.Size(), 3u); + tags = builder.TakeBuffer(); + ASSERT_EQ(tags.size(), 3u); + ASSERT_TRUE(builder.IsEmpty()); // ownership taken + + auto tag = tags[0]; + EXPECT_EQ(tag.mfgCode, MakeNullable(VendorId::TestVendor1)); + EXPECT_EQ(tag.namespaceID, kNamespaceID1); + EXPECT_EQ(tag.tag, kTag1); + ASSERT_TRUE(tag.label.HasValue() && (!tag.label.Value().IsNull())); + EXPECT_TRUE(tag.label.Value().Value().data_equal(CharSpan::fromCharString(kLabel1))); + + tag = tags[1]; + EXPECT_TRUE(tag.mfgCode.IsNull()); + EXPECT_EQ(tag.namespaceID, kNamespaceID2); + EXPECT_EQ(tag.tag, kTag2); + ASSERT_TRUE(tag.label.HasValue() && (!tag.label.Value().IsNull())); + EXPECT_TRUE(tag.label.Value().Value().data_equal(CharSpan::fromCharString(kLabel2))); + + tag = tags[2]; + EXPECT_EQ(tag.mfgCode, MakeNullable(VendorId::TestVendor3)); + EXPECT_EQ(tag.namespaceID, kNamespaceID3); + EXPECT_EQ(tag.tag, kTag3); + EXPECT_FALSE(tag.label.HasValue()); } diff --git a/src/lib/support/ScopedBuffer.h b/src/lib/support/ScopedBuffer.h index 7a3c0aaca59..923c532e15b 100644 --- a/src/lib/support/ScopedBuffer.h +++ b/src/lib/support/ScopedBuffer.h @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -218,11 +219,12 @@ class ScopedMemoryBufferWithSize : public ScopedMemoryBuffer return *this; } - ~ScopedMemoryBufferWithSize() { mCount = 0; } - // return the size as count of elements inline size_t AllocatedSize() const { return mCount; } + chip::Span Span() { return chip::Span(this->Get(), AllocatedSize()); } + chip::Span Span() const { return chip::Span(this->Get(), AllocatedSize()); } + void Free() { mCount = 0; diff --git a/src/lib/support/tests/BUILD.gn b/src/lib/support/tests/BUILD.gn index d814f70c477..e8b0e163931 100644 --- a/src/lib/support/tests/BUILD.gn +++ b/src/lib/support/tests/BUILD.gn @@ -87,6 +87,7 @@ chip_test_suite("tests") { ] public_deps = [ + "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/credentials", "${chip_root}/src/lib/core", "${chip_root}/src/lib/core:string-builder-adapters", diff --git a/src/platform/ASR/BUILD.gn b/src/platform/ASR/BUILD.gn index 24a819f35f6..783b40e2c97 100755 --- a/src/platform/ASR/BUILD.gn +++ b/src/platform/ASR/BUILD.gn @@ -21,8 +21,7 @@ import("${chip_root}/src/platform/device.gni") assert(chip_device_platform == "asr") static_library("ASR") { - sources = [] - sources += [ + sources = [ "../SingletonConfigurationManager.cpp", "ASRConfig.cpp", "ASRConfig.h", @@ -72,6 +71,7 @@ static_library("ASR") { ] deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/lib/dnssd:platform_header", "${chip_root}/src/platform/logging:headers", "${chip_root}/src/setup_payload", diff --git a/src/platform/Ameba/BUILD.gn b/src/platform/Ameba/BUILD.gn index 69bf149fee4..969424470d1 100755 --- a/src/platform/Ameba/BUILD.gn +++ b/src/platform/Ameba/BUILD.gn @@ -54,6 +54,7 @@ static_library("Ameba") { ] deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/lib/dnssd:platform_header", "${chip_root}/src/platform/logging:headers", "${chip_root}/src/setup_payload", diff --git a/src/platform/BUILD.gn b/src/platform/BUILD.gn index 80b210398ec..cbde538bf22 100644 --- a/src/platform/BUILD.gn +++ b/src/platform/BUILD.gn @@ -539,7 +539,8 @@ if (chip_device_platform != "none") { public_deps = [ ":platform_base", - "${chip_root}/src/app/common:cluster-objects", + "${chip_root}/src/app/common:ids", + "${chip_root}/src/app/util:types", "${chip_root}/src/crypto", "${chip_root}/src/lib/support", ] diff --git a/src/platform/Beken/BUILD.gn b/src/platform/Beken/BUILD.gn index 903379b9360..8c9b872ab5f 100755 --- a/src/platform/Beken/BUILD.gn +++ b/src/platform/Beken/BUILD.gn @@ -32,9 +32,6 @@ static_library("Beken") { "ConfigurationManagerImpl.h", "ConnectivityManagerImpl.cpp", "ConnectivityManagerImpl.h", - - #"DeviceInfoProviderImpl.cpp", - #"DeviceInfoProviderImpl.h", "DiagnosticDataProviderImpl.cpp", "DiagnosticDataProviderImpl.h", "KeyValueStoreManagerImpl.cpp", @@ -50,6 +47,7 @@ static_library("Beken") { ] deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/lib/dnssd:platform_header", "${chip_root}/src/platform/logging:headers", "${chip_root}/src/setup_payload", diff --git a/src/platform/Darwin/BUILD.gn b/src/platform/Darwin/BUILD.gn index 659c7a03a7a..e263c8f398c 100644 --- a/src/platform/Darwin/BUILD.gn +++ b/src/platform/Darwin/BUILD.gn @@ -100,6 +100,7 @@ static_library("Darwin") { deps = [ ":logging", ":tracing", + "${chip_root}/src/app:app_config", "${chip_root}/src/lib/dnssd:platform_header", "${chip_root}/src/platform/logging:headers", "${chip_root}/src/setup_payload", diff --git a/src/platform/ESP32/BUILD.gn b/src/platform/ESP32/BUILD.gn index 1ffae2593e8..77cbc94bb43 100644 --- a/src/platform/ESP32/BUILD.gn +++ b/src/platform/ESP32/BUILD.gn @@ -65,6 +65,7 @@ static_library("ESP32") { ] deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/lib/dnssd:platform_header", "${chip_root}/src/platform/logging:headers", "${chip_root}/src/setup_payload", @@ -166,6 +167,7 @@ static_library("ESP32") { "ThreadStackManagerImpl.cpp", "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] if (chip_mdns == "platform") { sources += [ "../OpenThread/OpenThreadDnssdImpl.cpp", diff --git a/src/platform/ESP32/NetworkCommissioningDriver.cpp b/src/platform/ESP32/NetworkCommissioningDriver.cpp index 77394d17f4e..1ded189218c 100644 --- a/src/platform/ESP32/NetworkCommissioningDriver.cpp +++ b/src/platform/ESP32/NetworkCommissioningDriver.cpp @@ -15,7 +15,6 @@ * limitations under the License. */ -#include // nogncheck #include #include #include @@ -459,9 +458,6 @@ void ESPWiFiDriver::OnScanWiFiNetworkDone() void ESPWiFiDriver::OnNetworkStatusChange() { - // This function reports the status to the data model provider, so skip it if the provider is not ready. - VerifyOrReturn(app::InteractionModelEngine::GetInstance() && - app::InteractionModelEngine::GetInstance()->GetDataModelProvider()); Network configuredNetwork; bool staEnabled = false, staConnected = false; VerifyOrReturn(ESP32Utils::IsStationEnabled(staEnabled) == CHIP_NO_ERROR); diff --git a/src/platform/Infineon/CYW30739/BUILD.gn b/src/platform/Infineon/CYW30739/BUILD.gn index ec9aeeda994..cccacc30fb5 100644 --- a/src/platform/Infineon/CYW30739/BUILD.gn +++ b/src/platform/Infineon/CYW30739/BUILD.gn @@ -45,6 +45,7 @@ static_library("CYW30739") { ] deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/crypto", "${matter_wpan_sdk_build_root}:wpan_sdk", ] diff --git a/src/platform/Infineon/PSOC6/BUILD.gn b/src/platform/Infineon/PSOC6/BUILD.gn index 3fd150eeeaa..a0413064dc7 100644 --- a/src/platform/Infineon/PSOC6/BUILD.gn +++ b/src/platform/Infineon/PSOC6/BUILD.gn @@ -67,6 +67,7 @@ static_library("PSOC6") { ] deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/lib/dnssd:platform_header", "${chip_root}/src/platform/logging:headers", "${chip_root}/src/setup_payload", diff --git a/src/platform/Linux/BUILD.gn b/src/platform/Linux/BUILD.gn index 7961b3d648d..3fc48f3d36a 100644 --- a/src/platform/Linux/BUILD.gn +++ b/src/platform/Linux/BUILD.gn @@ -70,6 +70,7 @@ static_library("Linux") { ] deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/app/icd/server:icd-server-config", "${chip_root}/src/credentials:credentials_header", "${chip_root}/src/setup_payload", @@ -134,6 +135,7 @@ static_library("Linux") { "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] public_deps += [ "dbus/openthread" ] } diff --git a/src/platform/NuttX/BUILD.gn b/src/platform/NuttX/BUILD.gn index 65113454ee0..46cde89f10e 100644 --- a/src/platform/NuttX/BUILD.gn +++ b/src/platform/NuttX/BUILD.gn @@ -70,6 +70,7 @@ static_library("NuttX") { ] deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/app/icd/server:icd-server-config", "${chip_root}/src/credentials:credentials_header", "${chip_root}/src/setup_payload", @@ -132,6 +133,7 @@ static_library("NuttX") { "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] public_deps += [ "dbus/openthread" ] } diff --git a/src/platform/Tizen/BUILD.gn b/src/platform/Tizen/BUILD.gn index 1bd7af3bc0b..e64d4c4ba5e 100644 --- a/src/platform/Tizen/BUILD.gn +++ b/src/platform/Tizen/BUILD.gn @@ -68,6 +68,7 @@ static_library("Tizen") { deps = [ "${chip_root}/src/setup_payload" ] public_deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/platform:platform_base", "${chip_root}/src/platform/logging:headers", "${tizen_root}:tizen", @@ -100,6 +101,7 @@ static_library("Tizen") { "ThreadStackManagerImpl.cpp", "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] } if (chip_enable_wifi) { diff --git a/src/platform/Zephyr/BUILD.gn b/src/platform/Zephyr/BUILD.gn index 2e13df9f3eb..9701efbe342 100644 --- a/src/platform/Zephyr/BUILD.gn +++ b/src/platform/Zephyr/BUILD.gn @@ -53,7 +53,10 @@ static_library("Zephyr") { ] public_deps = [ "${chip_root}/src/platform:platform_base" ] - deps = [ "${chip_root}/src/platform/logging:headers" ] + deps = [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/platform/logging:headers", + ] if (chip_enable_openthread) { sources += [ @@ -61,6 +64,7 @@ static_library("Zephyr") { "ThreadStackManagerImpl.cpp", "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] if (chip_mdns == "platform") { sources += [ diff --git a/src/platform/android/BUILD.gn b/src/platform/android/BUILD.gn index 9893907f858..e5e915a850f 100644 --- a/src/platform/android/BUILD.gn +++ b/src/platform/android/BUILD.gn @@ -84,6 +84,7 @@ static_library("android") { deps = [ ":tracing", + "${chip_root}/src/app:app_config", "${chip_root}/src/lib/dnssd:platform_header", "${chip_root}/src/setup_payload", ] diff --git a/src/platform/bouffalolab/BL602/BUILD.gn b/src/platform/bouffalolab/BL602/BUILD.gn index 7e4877490de..e60deb43773 100644 --- a/src/platform/bouffalolab/BL602/BUILD.gn +++ b/src/platform/bouffalolab/BL602/BUILD.gn @@ -70,6 +70,7 @@ static_library("BL602") { } deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/credentials:credentials_header", "${chip_root}/src/lib/dnssd:platform_header", "${chip_root}/src/platform/logging:headers", diff --git a/src/platform/bouffalolab/BL616/BUILD.gn b/src/platform/bouffalolab/BL616/BUILD.gn index f77216b48b6..a45709e73b2 100644 --- a/src/platform/bouffalolab/BL616/BUILD.gn +++ b/src/platform/bouffalolab/BL616/BUILD.gn @@ -66,6 +66,7 @@ static_library("BL616") { } deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/credentials:credentials_header", "${chip_root}/src/platform/logging:headers", ] @@ -86,7 +87,10 @@ static_library("BL616") { import("//build_overrides/bouffalolab_iot_sdk.gni") import("${bouffalolab_iot_sdk_build_root}/bl616/bouffalo_sdk.gni") - deps += [ "${bouffalolab_iot_sdk_build_root}/bl616:bouffalo_sdk" ] + deps += [ + "${bouffalolab_iot_sdk_build_root}/bl616:bouffalo_sdk", + "${chip_root}/src/app:app_config", + ] sources += [ "../../OpenThread/OpenThreadUtils.cpp", diff --git a/src/platform/bouffalolab/BL702/BUILD.gn b/src/platform/bouffalolab/BL702/BUILD.gn index c9112449a65..544f78fdebd 100644 --- a/src/platform/bouffalolab/BL702/BUILD.gn +++ b/src/platform/bouffalolab/BL702/BUILD.gn @@ -92,7 +92,10 @@ static_library("BL702") { import("//build_overrides/bouffalolab_iot_sdk.gni") import("${bouffalolab_iot_sdk_build_root}/bl702/bl_iot_sdk.gni") - deps += [ "${bouffalolab_iot_sdk_build_root}/bl702:bl_iot_sdk" ] + deps += [ + "${bouffalolab_iot_sdk_build_root}/bl702:bl_iot_sdk", + "${chip_root}/src/app:app_config", + ] sources += [ "../../OpenThread/OpenThreadUtils.cpp", @@ -116,7 +119,10 @@ static_library("BL702") { "EthernetInterface.c", ] - deps += [ "${chip_root}/src/lib/dnssd:platform_header" ] + deps += [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/lib/dnssd:platform_header", + ] } public_deps = [ "${chip_root}/src/platform:platform_base" ] diff --git a/src/platform/bouffalolab/BL702L/BUILD.gn b/src/platform/bouffalolab/BL702L/BUILD.gn index c32dc33255e..1eec252a6f8 100644 --- a/src/platform/bouffalolab/BL702L/BUILD.gn +++ b/src/platform/bouffalolab/BL702L/BUILD.gn @@ -70,6 +70,7 @@ static_library("BL702L") { } deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/credentials:credentials_header", "${chip_root}/src/platform/logging:headers", ] @@ -79,7 +80,10 @@ static_library("BL702L") { import("//build_overrides/bouffalolab_iot_sdk.gni") import("${bouffalolab_iot_sdk_build_root}/bl702l/bl_iot_sdk.gni") - deps += [ "${bouffalolab_iot_sdk_build_root}/bl702l:bl_iot_sdk" ] + deps += [ + "${bouffalolab_iot_sdk_build_root}/bl702l:bl_iot_sdk", + "${chip_root}/src/app:app_config", + ] sources += [ "../../OpenThread/OpenThreadUtils.cpp", diff --git a/src/platform/cc13xx_26xx/cc13x4_26x4/BUILD.gn b/src/platform/cc13xx_26xx/cc13x4_26x4/BUILD.gn index 8fc4e76daaf..d4bceeb7ee8 100644 --- a/src/platform/cc13xx_26xx/cc13x4_26x4/BUILD.gn +++ b/src/platform/cc13xx_26xx/cc13x4_26x4/BUILD.gn @@ -49,7 +49,10 @@ static_library("cc13x4_26x4") { "SystemPlatformConfig.h", ] - deps = [ "${chip_root}/src/platform/logging:headers" ] + deps = [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/platform/logging:headers", + ] public = [ "${chip_root}/src/credentials/CHIPCert.h", @@ -95,6 +98,7 @@ static_library("cc13x4_26x4") { "../ThreadStackManagerImpl.cpp", "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] if (chip_mdns == "platform") { deps += [ "${chip_root}/src/lib/dnssd:platform_header" ] diff --git a/src/platform/cc32xx/BUILD.gn b/src/platform/cc32xx/BUILD.gn index 1c29df46389..3004d999635 100644 --- a/src/platform/cc32xx/BUILD.gn +++ b/src/platform/cc32xx/BUILD.gn @@ -43,6 +43,7 @@ static_library("cc32xx") { ] public_deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/crypto", "${chip_root}/src/platform:platform_base", ] diff --git a/src/platform/fake/BUILD.gn b/src/platform/fake/BUILD.gn index 858517e8bf7..936e949bff6 100644 --- a/src/platform/fake/BUILD.gn +++ b/src/platform/fake/BUILD.gn @@ -35,6 +35,7 @@ static_library("fake") { "PlatformManagerImpl.cpp", "PlatformManagerImpl.h", ] + deps = [ "${chip_root}/src/app:app_config" ] public_deps = [ "${chip_root}/src/lib/dnssd:platform_header", "${chip_root}/src/platform:platform_base", diff --git a/src/platform/mbed/BUILD.gn b/src/platform/mbed/BUILD.gn index f73a39f8531..90aa98f50fc 100644 --- a/src/platform/mbed/BUILD.gn +++ b/src/platform/mbed/BUILD.gn @@ -39,7 +39,10 @@ static_library("mbed") { ] public_deps = [ "${chip_root}/src/platform:platform_base" ] - deps = [ "${chip_root}/src/platform/logging:headers" ] + deps = [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/platform/logging:headers", + ] if (chip_enable_wifi) { sources += [ diff --git a/src/platform/mt793x/BUILD.gn b/src/platform/mt793x/BUILD.gn index 3071988a280..7d5bd9a5ae9 100644 --- a/src/platform/mt793x/BUILD.gn +++ b/src/platform/mt793x/BUILD.gn @@ -59,12 +59,14 @@ static_library("mt793x") { ] } - # "${chip_root}/src/app:app_config", public_deps = [ "${chip_root}/src/platform:platform_base" ] public_deps += [ "${chip_root}/third_party/mt793x_sdk/mDNSResponder" ] - deps = [ "${chip_root}/src/platform/logging:headers" ] + deps = [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/platform/logging:headers", + ] # mDNS if (chip_mdns == "platform") { diff --git a/src/platform/nrfconnect/BUILD.gn b/src/platform/nrfconnect/BUILD.gn index 82b5be883c7..8b02a628924 100644 --- a/src/platform/nrfconnect/BUILD.gn +++ b/src/platform/nrfconnect/BUILD.gn @@ -60,7 +60,10 @@ static_library("nrfconnect") { ] public_deps = [ "${chip_root}/src/platform:platform_base" ] - deps = [ "${chip_root}/src/platform/logging:headers" ] + deps = [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/platform/logging:headers", + ] if (chip_enable_factory_data) { sources += [ @@ -89,6 +92,7 @@ static_library("nrfconnect") { "../Zephyr/ThreadStackManagerImpl.cpp", "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] if (chip_mdns_platform) { sources += [ diff --git a/src/platform/nxp/common/ota/ota.gni b/src/platform/nxp/common/ota/ota.gni new file mode 100644 index 00000000000..9bb621f5628 --- /dev/null +++ b/src/platform/nxp/common/ota/ota.gni @@ -0,0 +1,35 @@ +# Copyright (c) 2025 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("//build_overrides/nxp_sdk.gni") +import("${nxp_sdk_build_root}/nxp_sdk.gni") + +ota_root = get_path_info(".", "abspath") + +ota_sources = [ + "${ota_root}/OTAFirmwareProcessor.cpp", + "${ota_root}/OTAFirmwareProcessor.h", + "${ota_root}/OTAHooks.cpp", + "${ota_root}/OTAImageProcessorImpl.cpp", + "${ota_root}/OTAImageProcessorImpl.h", + "${ota_root}/OTATlvProcessor.cpp", + "${ota_root}/OTATlvProcessor.h", +] + +if (nxp_use_factory_data && nxp_enable_ota_factory_data_processor) { + ota_sources += [ + "${ota_root}/OTAFactoryDataProcessor.cpp", + "${ota_root}/OTAFactoryDataProcessor.h", + ] +} diff --git a/src/platform/nxp/k32w0/BUILD.gn b/src/platform/nxp/k32w0/BUILD.gn index 77d53f62de7..752e04ffabb 100644 --- a/src/platform/nxp/k32w0/BUILD.gn +++ b/src/platform/nxp/k32w0/BUILD.gn @@ -108,7 +108,10 @@ static_library("nxp_platform") { } } - deps = [ "${chip_root}/src/platform/logging:headers" ] + deps = [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/platform/logging:headers", + ] public_deps = [ "${chip_root}/src/platform:platform_base" ] @@ -135,6 +138,7 @@ static_library("nxp_platform") { "ThreadStackManagerImpl.cpp", "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] if (chip_mdns == "platform") { sources += [ diff --git a/src/platform/nxp/mcxw71_k32w1/BUILD.gn b/src/platform/nxp/mcxw71_k32w1/BUILD.gn index 865475d1e43..df4a711f691 100644 --- a/src/platform/nxp/mcxw71_k32w1/BUILD.gn +++ b/src/platform/nxp/mcxw71_k32w1/BUILD.gn @@ -18,6 +18,7 @@ import("//build_overrides/openthread.gni") import("${chip_root}/src/crypto/crypto.gni") import("${chip_root}/src/platform/device.gni") +import("${chip_root}/src/platform/nxp/common/ota/ota.gni") import("${nxp_sdk_build_root}/nxp_sdk.gni") @@ -53,22 +54,7 @@ source_set("nxp_factory_data") { source_set("nxp_ota") { public = [ "../common/ota/OTAImageProcessorImpl.h" ] - sources = [ - "../common/ota/OTAFirmwareProcessor.cpp", - "../common/ota/OTAFirmwareProcessor.h", - "../common/ota/OTAHooks.cpp", - "../common/ota/OTAImageProcessorImpl.cpp", - "../common/ota/OTAImageProcessorImpl.h", - "../common/ota/OTATlvProcessor.cpp", - "../common/ota/OTATlvProcessor.h", - ] - - if (nxp_use_factory_data && nxp_enable_ota_factory_data_processor) { - sources += [ - "../common/ota/OTAFactoryDataProcessor.cpp", - "../common/ota/OTAFactoryDataProcessor.h", - ] - } + sources = ota_sources deps = [ ":nxp_platform", @@ -87,7 +73,6 @@ config("nxp_platform_config") { } static_library("nxp_platform") { - deps = [] defines = [ "NXP_DEVICE_K32W1_MCXW7X=1", "NXP_USE_MML=1", @@ -111,6 +96,7 @@ static_library("nxp_platform") { "PlatformManagerImpl.h", "SystemTimeSupport.cpp", ] + deps = [ "${chip_root}/src/app:app_config" ] if (nxp_nvm_component == "fwk_nvm") { defines += [ "CHIP_PLAT_NVM_SUPPORT=1" ] @@ -123,18 +109,6 @@ static_library("nxp_platform") { "ram_storage.c", "ram_storage.h", ] - } else if (nxp_nvm_component == "littlefs") { - defines += [ - "CHIP_PLAT_NVM_SUPPORT=3", - "EXTERNAL_KEYVALUESTOREMANAGERIMPL_HEADER=\"platform/nxp/common/KeyValueStoreManagerImpl.h\"", - ] - - sources += [ - "../common/KeyValueStoreManagerImpl.cpp", - "../common/KeyValueStoreManagerImpl.h", - "../common/NXPConfig.h", - "../common/NXPConfigKS.cpp", - ] } else if (nxp_nvm_component == "nvs") { defines += [ "gAppNvsExternalFlash_c=0", @@ -150,6 +124,12 @@ static_library("nxp_platform") { ] } + # When the app is built using cmake, we must ensure the OTA platform files are also compiled. + # When the app is built using gn, the app selects the nxp_ota target through its build file. + if (nxp_external_sdk && chip_enable_ota_requestor) { + sources += ota_sources + } + if (nxp_use_plain_dac_key) { defines += [ "CHIP_USE_PLAIN_DAC_KEY=1" ] } else { @@ -190,6 +170,7 @@ static_library("nxp_platform") { "ThreadStackManagerImpl.cpp", "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] deps += [ "${chip_root}/third_party/openthread:openthread" ] public_deps += [ "${chip_root}/third_party/openthread:openthread-platform" ] diff --git a/src/platform/nxp/mcxw71_k32w1/args.gni b/src/platform/nxp/mcxw71_k32w1/args.gni index 50cfbaba96e..7ec8fdf3d56 100644 --- a/src/platform/nxp/mcxw71_k32w1/args.gni +++ b/src/platform/nxp/mcxw71_k32w1/args.gni @@ -21,6 +21,7 @@ openthread_root = nxp_platform = "mcxw71_k32w1" nxp_sdk_name = "mcxw71_k32w1_sdk" nxp_device_layer = "nxp/${nxp_platform}" +nxp_nvm_component = "nvs" nxp_use_lwip = false # ARM architecture flags will be set based on NXP board. diff --git a/src/platform/nxp/mcxw71_k32w1/k32w1-chip-mbedtls-config.h b/src/platform/nxp/mcxw71_k32w1/k32w1-chip-mbedtls-config.h index f3d9949f730..ef244a7f4da 100644 --- a/src/platform/nxp/mcxw71_k32w1/k32w1-chip-mbedtls-config.h +++ b/src/platform/nxp/mcxw71_k32w1/k32w1-chip-mbedtls-config.h @@ -19,15 +19,9 @@ #ifndef MBEDTLS_CONFIG_H #define MBEDTLS_CONFIG_H -#include "openthread-core-config.h" - #include #include -#include -#include -#include - #undef MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES #define MBEDTLS_PLATFORM_SNPRINTF_MACRO snprintf @@ -67,16 +61,6 @@ #define MBEDTLS_SSL_PROTO_DTLS #define MBEDTLS_SSL_TLS_C -#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE || OPENTHREAD_CONFIG_COMMISSIONER_ENABLE || OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE -#define MBEDTLS_SSL_COOKIE_C -#define MBEDTLS_SSL_SRV_C -#endif - -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE -#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED -#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED -#endif - #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED #define MBEDTLS_BASE64_C #define MBEDTLS_ECDH_C @@ -87,16 +71,6 @@ #define MBEDTLS_X509_CRT_PARSE_C #endif -#if OPENTHREAD_CONFIG_ECDSA_ENABLE -#define MBEDTLS_BASE64_C -#define MBEDTLS_ECDH_C -#define MBEDTLS_ECDSA_C -#define MBEDTLS_ECDSA_DETERMINISTIC -#define MBEDTLS_OID_C -#define MBEDTLS_PEM_PARSE_C -#define MBEDTLS_PK_WRITE_C -#endif - #define MBEDTLS_MPI_WINDOW_SIZE 1 /**< Maximum windows size used. */ #define MBEDTLS_MPI_MAX_SIZE 32 /**< Maximum number of bytes for usable MPIs. */ #define MBEDTLS_ECP_MAX_BITS 256 /**< Maximum bit size of groups */ @@ -104,19 +78,9 @@ #define MBEDTLS_ECP_FIXED_POINT_OPTIM 0 /**< Enable fixed-point speed-up */ #define MBEDTLS_ENTROPY_MAX_SOURCES 2 /**< Maximum number of sources supported */ -#if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE -#define MBEDTLS_PLATFORM_STD_CALLOC otPlatCAlloc /**< Default allocator to use, can be undefined */ -#define MBEDTLS_PLATFORM_STD_FREE otPlatFree /**< Default free to use, can be undefined */ -#else #define MBEDTLS_MEMORY_BUFFER_ALLOC_C -#endif -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE -#define MBEDTLS_SSL_MAX_CONTENT_LEN 900 /**< Maxium fragment length in bytes */ -#else #define MBEDTLS_SSL_MAX_CONTENT_LEN 768 /**< Maxium fragment length in bytes */ -#endif - #define MBEDTLS_SSL_IN_CONTENT_LEN MBEDTLS_SSL_MAX_CONTENT_LEN #define MBEDTLS_SSL_OUT_CONTENT_LEN MBEDTLS_SSL_MAX_CONTENT_LEN #define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 diff --git a/src/platform/nxp/mw320/BUILD.gn b/src/platform/nxp/mw320/BUILD.gn index 6b2729b8474..0220edebc93 100644 --- a/src/platform/nxp/mw320/BUILD.gn +++ b/src/platform/nxp/mw320/BUILD.gn @@ -73,7 +73,10 @@ static_library("mw320") { # Use ethernet/wifi interface for network commissioning. Default: WiFi defines += [ "USE_ETHERNET_COMMISSION=0" ] - deps = [ "${chip_root}/src/platform/logging:headers" ] + deps = [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/platform/logging:headers", + ] public_deps = [ "${chip_root}/src/platform:platform_base" ] @@ -83,6 +86,7 @@ static_library("mw320") { "ThreadStackManagerImpl.cpp", "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] if (chip_mdns == "platform") { sources += [ "../../OpenThread/MdnsImpl.cpp" ] diff --git a/src/platform/nxp/rt/rt1060/BUILD.gn b/src/platform/nxp/rt/rt1060/BUILD.gn index 36db6591032..e326ec12fd8 100644 --- a/src/platform/nxp/rt/rt1060/BUILD.gn +++ b/src/platform/nxp/rt/rt1060/BUILD.gn @@ -60,7 +60,6 @@ static_library("nxp_platform") { "-Wno-conversion", "-Wno-sign-compare", ] - deps = [] defines = [] sources = [ "../../../FreeRTOS/SystemTimeSupport.cpp", @@ -85,6 +84,7 @@ static_library("nxp_platform") { "CHIPDevicePlatformConfig.h", "PlatformManagerImpl.cpp", ] + deps = [ "${chip_root}/src/app:app_config" ] if (chip_enable_ble) { sources += [ @@ -134,7 +134,10 @@ static_library("nxp_platform") { "../../common/ThreadStackManagerImpl.h", ] - deps += [ "${chip_root}/src/app/common:ids" ] + deps += [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/app/common:ids", + ] if (!nxp_build_matter_standalone_lib) { deps += [ "${chip_root}/third_party/openthread:openthread" ] diff --git a/src/platform/nxp/rt/rt1170/BUILD.gn b/src/platform/nxp/rt/rt1170/BUILD.gn index 0017fc44eb3..2b0dca93799 100644 --- a/src/platform/nxp/rt/rt1170/BUILD.gn +++ b/src/platform/nxp/rt/rt1170/BUILD.gn @@ -63,7 +63,6 @@ static_library("nxp_platform") { "-Wno-conversion", "-Wno-sign-compare", ] - deps = [] defines = [] sources = [ "../../../FreeRTOS/SystemTimeSupport.cpp", @@ -88,6 +87,7 @@ static_library("nxp_platform") { "CHIPDevicePlatformConfig.h", "PlatformManagerImpl.cpp", ] + deps = [ "${chip_root}/src/app:app_config" ] if (chip_enable_ble) { sources += [ @@ -137,7 +137,10 @@ static_library("nxp_platform") { "../../common/ThreadStackManagerImpl.h", ] - deps += [ "${chip_root}/src/app/common:ids" ] + deps += [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/app/common:ids", + ] if (!nxp_build_matter_standalone_lib) { deps += [ "${chip_root}/third_party/openthread:openthread" ] diff --git a/src/platform/nxp/rt/rw61x/BUILD.gn b/src/platform/nxp/rt/rw61x/BUILD.gn index 684faf3a7f5..c14bb911264 100644 --- a/src/platform/nxp/rt/rw61x/BUILD.gn +++ b/src/platform/nxp/rt/rw61x/BUILD.gn @@ -80,7 +80,6 @@ static_library("nxp_platform") { "-Wno-conversion", "-Wno-sign-compare", ] - deps = [] defines = [] sources = [ "../../../FreeRTOS/SystemTimeSupport.cpp", @@ -101,6 +100,7 @@ static_library("nxp_platform") { "CHIPDevicePlatformConfig.h", "PlatformManagerImpl.cpp", ] + deps = [ "${chip_root}/src/app:app_config" ] if (nxp_nvm_component == "nvm_fwk" || nxp_nvm_component == "littlefs") { sources += [ @@ -168,7 +168,10 @@ static_library("nxp_platform") { "../../common/ThreadStackManagerImpl.h", ] - deps += [ "${chip_root}/src/app/common:ids" ] + deps += [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/app/common:ids", + ] if (!nxp_build_matter_standalone_lib) { deps += [ "${chip_root}/third_party/openthread:openthread" ] diff --git a/src/platform/nxp/zephyr/BUILD.gn b/src/platform/nxp/zephyr/BUILD.gn index b00af4edb59..aa0cefbd3d2 100644 --- a/src/platform/nxp/zephyr/BUILD.gn +++ b/src/platform/nxp/zephyr/BUILD.gn @@ -60,7 +60,10 @@ static_library("nxp_zephyr") { ] public_deps = [ "${chip_root}/src/platform:platform_base" ] - deps = [ "${chip_root}/src/platform/logging:headers" ] + deps = [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/platform/logging:headers", + ] if (chip_enable_factory_data) { sources += [ diff --git a/src/platform/openiotsdk/BUILD.gn b/src/platform/openiotsdk/BUILD.gn index 51ffe33bbe8..8b2387afc2a 100644 --- a/src/platform/openiotsdk/BUILD.gn +++ b/src/platform/openiotsdk/BUILD.gn @@ -74,7 +74,10 @@ static_library("openiotsdk") { "${chip_root}/src/platform:platform_base", ] - deps = [ "${chip_root}/src/platform/logging:headers" ] + deps = [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/platform/logging:headers", + ] if (chip_enable_ota_requestor) { sources += [ diff --git a/src/platform/qpg/BUILD.gn b/src/platform/qpg/BUILD.gn index d9983ae6bce..a1e045c92f7 100644 --- a/src/platform/qpg/BUILD.gn +++ b/src/platform/qpg/BUILD.gn @@ -49,7 +49,10 @@ static_library("qpg") { "qpgConfig.h", ] - deps = [ "${chip_root}/src/platform/logging:headers" ] + deps = [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/platform/logging:headers", + ] public = [ "${chip_root}/src/credentials/DeviceAttestationCredsProvider.h" ] @@ -78,6 +81,7 @@ static_library("qpg") { "ThreadStackManagerImpl.cpp", "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] if (chip_mdns == "platform") { sources += [ diff --git a/src/platform/silabs/ConfigurationManagerImpl.cpp b/src/platform/silabs/ConfigurationManagerImpl.cpp index ce920fedd69..8df78e4342a 100644 --- a/src/platform/silabs/ConfigurationManagerImpl.cpp +++ b/src/platform/silabs/ConfigurationManagerImpl.cpp @@ -275,14 +275,14 @@ void ConfigurationManagerImpl::ClearThreadStack() void ConfigurationManagerImpl::DoFactoryReset(intptr_t arg) { - CHIP_ERROR err; + CHIP_ERROR error = CHIP_NO_ERROR; ChipLogProgress(DeviceLayer, "Performing factory reset"); - err = SilabsConfig::FactoryResetConfig(); - if (err != CHIP_NO_ERROR) + error = SilabsConfig::FactoryResetConfig(); + if (error != CHIP_NO_ERROR) { - ChipLogError(DeviceLayer, "FactoryResetConfig() failed: %s", chip::ErrorStr(err)); + ChipLogError(DeviceLayer, "FactoryResetConfig() failed: %s", chip::ErrorStr(error)); } GetDefaultInstance().ClearThreadStack(); @@ -290,11 +290,12 @@ void ConfigurationManagerImpl::DoFactoryReset(intptr_t arg) PersistedStorage::KeyValueStoreMgrImpl().ErasePartition(); #if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION - sl_status_t status = sl_matter_wifi_disconnect(); - if (status != SL_STATUS_OK) + error = TriggerDisconnection(); + if (error != CHIP_NO_ERROR) { - ChipLogError(DeviceLayer, "sl_matter_wifi_disconnect() failed: %lx", status); + ChipLogError(DeviceLayer, "TriggerDisconnection() failed: %s", chip::ErrorStr(error)); } + ChipLogProgress(DeviceLayer, "Clearing WiFi provision"); wfx_clear_wifi_provision(); #endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION diff --git a/src/platform/silabs/ConnectivityManagerImpl_WIFI.cpp b/src/platform/silabs/ConnectivityManagerImpl_WIFI.cpp index 11b0dc924bc..a5394f4d122 100644 --- a/src/platform/silabs/ConnectivityManagerImpl_WIFI.cpp +++ b/src/platform/silabs/ConnectivityManagerImpl_WIFI.cpp @@ -128,8 +128,7 @@ ConnectivityManager::WiFiStationMode ConnectivityManagerImpl::_GetWiFiStationMod { if (mWiFiStationMode != kWiFiStationMode_ApplicationControlled) { - wifi_mode_t curWiFiMode = wfx_get_wifi_mode(); - if ((curWiFiMode == WIFI_MODE_STA) || (curWiFiMode == WIFI_MODE_APSTA)) + if (IsStationModeEnabled()) { mWiFiStationMode = kWiFiStationMode_Enabled; } @@ -138,6 +137,7 @@ ConnectivityManager::WiFiStationMode ConnectivityManagerImpl::_GetWiFiStationMod mWiFiStationMode = kWiFiStationMode_Disabled; } } + return mWiFiStationMode; } @@ -259,14 +259,9 @@ void ConnectivityManagerImpl::DriveStationState() (mWiFiStationMode != kWiFiStationMode_Enabled && !IsWiFiStationProvisioned())) { ChipLogProgress(DeviceLayer, "Disconnecting WiFi station interface"); - sl_status_t status = sl_matter_wifi_disconnect(); - if (status != SL_STATUS_OK) - { - ChipLogError(DeviceLayer, "wfx_wifi_disconnect() failed: %lx", status); - // TODO: Clean the function up to remove the usage of goto - goto exit; - } + CHIP_ERROR error = TriggerDisconnection(); + SuccessOrExitAction(error, ChipLogError(DeviceLayer, "TriggerDisconnection() failed: %s", ErrorStr(error))); ChangeWiFiStationState(kWiFiStationState_Disconnecting); } @@ -344,7 +339,6 @@ void ConnectivityManagerImpl::DriveStationState() void ConnectivityManagerImpl::OnStationConnected() { - wfx_setup_ip6_link_local(SL_WFX_STA_INTERFACE); NetworkCommissioning::SlWiFiDriver::GetInstance().OnConnectWiFiNetwork(); UpdateInternetConnectivityState(); diff --git a/src/platform/silabs/DiagnosticDataProviderImpl.cpp b/src/platform/silabs/DiagnosticDataProviderImpl.cpp index 0cb4c85d3dd..58128528fef 100644 --- a/src/platform/silabs/DiagnosticDataProviderImpl.cpp +++ b/src/platform/silabs/DiagnosticDataProviderImpl.cpp @@ -133,30 +133,30 @@ void DiagnosticDataProviderImpl::ReleaseThreadMetrics(ThreadMetrics * threadMetr CHIP_ERROR DiagnosticDataProviderImpl::GetRebootCount(uint16_t & rebootCount) { - uint32_t count = 0; - CHIP_ERROR err = ConfigurationMgr().GetRebootCount(count); + uint32_t count = 0; + CHIP_ERROR error = ConfigurationMgr().GetRebootCount(count); - if (err == CHIP_NO_ERROR) + if (error == CHIP_NO_ERROR) { VerifyOrReturnError(count <= UINT16_MAX, CHIP_ERROR_INVALID_INTEGER_VALUE); rebootCount = static_cast(count); } - return err; + return error; } CHIP_ERROR DiagnosticDataProviderImpl::GetBootReason(BootReasonType & bootReason) { - uint32_t reason = 0; - CHIP_ERROR err = ConfigurationMgr().GetBootReason(reason); + uint32_t reason = 0; + CHIP_ERROR error = ConfigurationMgr().GetBootReason(reason); - if (err == CHIP_NO_ERROR) + if (error == CHIP_NO_ERROR) { VerifyOrReturnError(reason <= UINT8_MAX, CHIP_ERROR_INVALID_INTEGER_VALUE); bootReason = static_cast(reason); } - return err; + return error; } CHIP_ERROR DiagnosticDataProviderImpl::GetUpTime(uint64_t & upTime) @@ -252,8 +252,8 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetNetworkInterfaces(NetworkInterface ** ifp->name = CharSpan::fromCharString(ifp->Name); ifp->isOperational = true; Inet::InterfaceType interfaceType; - CHIP_ERROR err = interfaceIterator.GetInterfaceType(interfaceType); - if (err == CHIP_NO_ERROR || err == CHIP_ERROR_NOT_IMPLEMENTED) + CHIP_ERROR error = interfaceIterator.GetInterfaceType(interfaceType); + if (error == CHIP_NO_ERROR || error == CHIP_ERROR_NOT_IMPLEMENTED) { switch (interfaceType) { @@ -336,16 +336,17 @@ void DiagnosticDataProviderImpl::ReleaseNetworkInterfaces(NetworkInterface * net CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiBssId(MutableByteSpan & BssId) { constexpr size_t bssIdSize = 6; + wfx_wifi_scan_result_t ap = { 0 }; + VerifyOrReturnError(BssId.size() >= bssIdSize, CHIP_ERROR_BUFFER_TOO_SMALL); - wfx_wifi_scan_result_t ap; - int32_t err = wfx_get_ap_info(&ap); - if (err == 0) + if (GetAccessPointInfo(ap) == CHIP_NO_ERROR) { memcpy(BssId.data(), ap.bssid, bssIdSize); BssId.reduce_size(bssIdSize); return CHIP_NO_ERROR; } + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; } @@ -353,9 +354,9 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiSecurityType(app::Clusters::WiFiNe { using app::Clusters::WiFiNetworkDiagnostics::SecurityTypeEnum; - wfx_wifi_scan_result_t ap; - int32_t err = wfx_get_ap_info(&ap); - if (err == 0) + wfx_wifi_scan_result_t ap = { 0 }; + CHIP_ERROR error = GetAccessPointInfo(ap); + if (error == CHIP_NO_ERROR) { // TODO: Is this actually right? Do the wfx_wifi_scan_result_t values // match the Matter spec ones? @@ -373,9 +374,9 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiVersion(app::Clusters::WiFiNetwork CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiChannelNumber(uint16_t & channelNumber) { - wfx_wifi_scan_result_t ap; - int32_t err = wfx_get_ap_info(&ap); - if (err == 0) + wfx_wifi_scan_result_t ap = { 0 }; + CHIP_ERROR error = GetAccessPointInfo(ap); + if (error == CHIP_NO_ERROR) { channelNumber = ap.chan; return CHIP_NO_ERROR; @@ -385,9 +386,9 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiChannelNumber(uint16_t & channelNu CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiRssi(int8_t & rssi) { - wfx_wifi_scan_result_t ap; - int32_t err = wfx_get_ap_info(&ap); - if (err == 0) + wfx_wifi_scan_result_t ap = { 0 }; + CHIP_ERROR error = GetAccessPointInfo(ap); + if (error == CHIP_NO_ERROR) { rssi = ap.rssi; return CHIP_NO_ERROR; @@ -397,9 +398,9 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiRssi(int8_t & rssi) CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiBeaconLostCount(uint32_t & beaconLostCount) { - wfx_wifi_scan_ext_t extra_info; - int32_t err = wfx_get_ap_ext(&extra_info); - if (err == 0) + wfx_wifi_scan_ext_t extra_info = { 0 }; + CHIP_ERROR error = GetAccessPointExtendedInfo(extra_info); + if (error == CHIP_NO_ERROR) { beaconLostCount = extra_info.beacon_lost_count; return CHIP_NO_ERROR; @@ -414,9 +415,9 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiCurrentMaxRate(uint64_t & currentM CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiPacketMulticastRxCount(uint32_t & packetMulticastRxCount) { - wfx_wifi_scan_ext_t extra_info; - int32_t err = wfx_get_ap_ext(&extra_info); - if (err == 0) + wfx_wifi_scan_ext_t extra_info = { 0 }; + CHIP_ERROR error = GetAccessPointExtendedInfo(extra_info); + if (error == CHIP_NO_ERROR) { packetMulticastRxCount = extra_info.mcast_rx_count; return CHIP_NO_ERROR; @@ -426,9 +427,9 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiPacketMulticastRxCount(uint32_t & CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiPacketMulticastTxCount(uint32_t & packetMulticastTxCount) { - wfx_wifi_scan_ext_t extra_info; - int32_t err = wfx_get_ap_ext(&extra_info); - if (err == 0) + wfx_wifi_scan_ext_t extra_info = { 0 }; + CHIP_ERROR error = GetAccessPointExtendedInfo(extra_info); + if (error == CHIP_NO_ERROR) { packetMulticastTxCount = extra_info.mcast_tx_count; return CHIP_NO_ERROR; @@ -438,9 +439,9 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiPacketMulticastTxCount(uint32_t & CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiPacketUnicastRxCount(uint32_t & packetUnicastRxCount) { - wfx_wifi_scan_ext_t extra_info; - int32_t err = wfx_get_ap_ext(&extra_info); - if (err == 0) + wfx_wifi_scan_ext_t extra_info = { 0 }; + CHIP_ERROR error = GetAccessPointExtendedInfo(extra_info); + if (error == CHIP_NO_ERROR) { packetUnicastRxCount = extra_info.ucast_rx_count; return CHIP_NO_ERROR; @@ -450,9 +451,9 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiPacketUnicastRxCount(uint32_t & pa CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiPacketUnicastTxCount(uint32_t & packetUnicastTxCount) { - wfx_wifi_scan_ext_t extra_info; - int32_t err = wfx_get_ap_ext(&extra_info); - if (err == 0) + wfx_wifi_scan_ext_t extra_info = { 0 }; + CHIP_ERROR error = GetAccessPointExtendedInfo(extra_info); + if (error == CHIP_NO_ERROR) { packetUnicastTxCount = extra_info.ucast_tx_count; return CHIP_NO_ERROR; @@ -462,9 +463,9 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiPacketUnicastTxCount(uint32_t & pa CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiOverrunCount(uint64_t & overrunCount) { - wfx_wifi_scan_ext_t extra_info; - int32_t err = wfx_get_ap_ext(&extra_info); - if (err == 0) + wfx_wifi_scan_ext_t extra_info = { 0 }; + CHIP_ERROR error = GetAccessPointExtendedInfo(extra_info); + if (error == CHIP_NO_ERROR) { overrunCount = extra_info.overrun_count; return CHIP_NO_ERROR; @@ -474,9 +475,9 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiOverrunCount(uint64_t & overrunCou CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiBeaconRxCount(uint32_t & beaconRxCount) { - wfx_wifi_scan_ext_t extra_info; - int32_t err = wfx_get_ap_ext(&extra_info); - if (err == 0) + wfx_wifi_scan_ext_t extra_info = { 0 }; + CHIP_ERROR error = GetAccessPointExtendedInfo(extra_info); + if (error == CHIP_NO_ERROR) { beaconRxCount = extra_info.beacon_rx_count; return CHIP_NO_ERROR; @@ -486,12 +487,7 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetWiFiBeaconRxCount(uint32_t & beaconRxC CHIP_ERROR DiagnosticDataProviderImpl::ResetWiFiNetworkDiagnosticsCounts() { - int32_t err = wfx_reset_counts(); - if (err == 0) - { - return CHIP_NO_ERROR; - } - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + return ResetCounters(); } #endif // SL_WIFI diff --git a/src/platform/silabs/NetworkCommissioningWiFiDriver.cpp b/src/platform/silabs/NetworkCommissioningWiFiDriver.cpp index bdabd686b33..1390668b842 100644 --- a/src/platform/silabs/NetworkCommissioningWiFiDriver.cpp +++ b/src/platform/silabs/NetworkCommissioningWiFiDriver.cpp @@ -136,15 +136,10 @@ Status SlWiFiDriver::ReorderNetwork(ByteSpan networkId, uint8_t index, MutableCh CHIP_ERROR SlWiFiDriver::ConnectWiFiNetwork(const char * ssid, uint8_t ssidLen, const char * key, uint8_t keyLen) { - int32_t status = SL_STATUS_OK; if (ConnectivityMgr().IsWiFiStationProvisioned()) { ChipLogProgress(DeviceLayer, "Disconecting for current wifi"); - status = sl_matter_wifi_disconnect(); - if (status != SL_STATUS_OK) - { - return CHIP_ERROR_INTERNAL; - } + ReturnErrorOnFailure(TriggerDisconnection()); } ReturnErrorOnFailure(ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Disabled)); diff --git a/src/platform/silabs/PlatformManagerImpl.cpp b/src/platform/silabs/PlatformManagerImpl.cpp index 7b33b934b36..14026796eb0 100644 --- a/src/platform/silabs/PlatformManagerImpl.cpp +++ b/src/platform/silabs/PlatformManagerImpl.cpp @@ -84,10 +84,10 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) err = chip::DeviceLayer::PersistedStorage::KeyValueStoreMgrImpl().Init(); SuccessOrExit(err); -#if CHIP_SYSTEM_CONFIG_USE_LWIP && !defined(SLI_SI91X_MCU_INTERFACE) +#if CHIP_SYSTEM_CONFIG_USE_LWIP && !defined(SLI_SI91X_MCU_INTERFACE) && !defined(EXP_BOARD) // Initialize LwIP. tcpip_init(NULL, NULL); -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP && !defined(SLI_SI91X_MCU_INTERFACE) +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP && !defined(SLI_SI91X_MCU_INTERFACE) && !defined(EXP_BOARD) ReturnErrorOnFailure(System::Clock::InitClock_RealTime()); diff --git a/src/platform/silabs/SiWx917/BUILD.gn b/src/platform/silabs/SiWx917/BUILD.gn index 3f7adf8269c..bca3755f1de 100644 --- a/src/platform/silabs/SiWx917/BUILD.gn +++ b/src/platform/silabs/SiWx917/BUILD.gn @@ -18,6 +18,7 @@ import("${chip_root}/src/platform/device.gni") import("${chip_root}/build/chip/buildconfig_header.gni") import("${chip_root}/src/crypto/crypto.gni") +import("${chip_root}/src/platform/silabs/provision/args.gni") import("${chip_root}/src/platform/silabs/wifi/args.gni") import("${chip_root}/third_party/silabs/SiWx917_sdk.gni") import("${chip_root}/third_party/silabs/silabs_board.gni") @@ -87,7 +88,10 @@ static_library("SiWx917") { "${chip_root}/src/app/icd/server:icd-server-config", "${chip_root}/src/platform:platform_base", ] - deps = [ "${chip_root}/src/platform/logging:headers" ] + deps = [ + "${chip_root}/src/platform/logging:headers", + "${sl_provision_root}:provision-headers", + ] # Add platform crypto implementation if (chip_crypto == "platform") { diff --git a/src/platform/silabs/SiWx917/SiWxPlatformInterface.h b/src/platform/silabs/SiWx917/SiWxPlatformInterface.h index 287ed50e942..36242c65f5c 100644 --- a/src/platform/silabs/SiWx917/SiWxPlatformInterface.h +++ b/src/platform/silabs/SiWx917/SiWxPlatformInterface.h @@ -59,6 +59,16 @@ void gpio_uulp_pin_interrupt_callback(uint32_t pin_intr) } } +/** + * @brief Processing function when a button is triggered + * + * TODO: Move this to SPAM + * + * @param btn which button was pressed + * @param btnAction the action that triggered the buttone vent + */ +void sl_button_on_change(uint8_t btn, uint8_t btnAction); + #endif // SLI_SI91X_MCU_INTERFACE #endif // CHIP_CONFIG_ENABLE_ICD_SERVER #ifdef __cplusplus diff --git a/src/platform/silabs/efr32/BUILD.gn b/src/platform/silabs/efr32/BUILD.gn index f39628d4845..3102a4d924f 100644 --- a/src/platform/silabs/efr32/BUILD.gn +++ b/src/platform/silabs/efr32/BUILD.gn @@ -17,6 +17,7 @@ import("//build_overrides/chip.gni") import("${chip_root}/build/chip/buildconfig_header.gni") import("${chip_root}/src/crypto/crypto.gni") import("${chip_root}/src/platform/device.gni") +import("${chip_root}/src/platform/silabs/provision/args.gni") import("${chip_root}/third_party/silabs/efr32_sdk.gni") import("${chip_root}/third_party/silabs/silabs_board.gni") @@ -114,7 +115,7 @@ static_library("efr32") { "${chip_root}/src/platform:platform_base", "${chip_root}/src/platform/logging:headers", ] - deps = [ "${silabs_platform_dir}/provision:provision-headers" ] + deps = [ "${sl_provision_root}:provision-headers" ] public_configs = [] # Add platform crypto implementation @@ -136,7 +137,11 @@ static_library("efr32") { if (chip_enable_openthread) { public_deps += [ "${chip_root}/third_party/openthread:openthread" ] - deps += [ "${chip_root}/third_party/openthread:openthread_cli" ] + deps += [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/app:app_config", + "${chip_root}/third_party/openthread:openthread_cli", + ] sources += [ "${silabs_platform_dir}/ThreadStackManagerImpl.h", diff --git a/src/platform/silabs/provision/args.gni b/src/platform/silabs/provision/args.gni new file mode 100644 index 00000000000..a92793192be --- /dev/null +++ b/src/platform/silabs/provision/args.gni @@ -0,0 +1,19 @@ +# 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("//build_overrides/chip.gni") + +declare_args() { + sl_provision_root = "${chip_root}/src/platform/silabs/provision" +} diff --git a/src/platform/silabs/wifi/SiWx/WifiInterfaceImpl.cpp b/src/platform/silabs/wifi/SiWx/WifiInterfaceImpl.cpp index 6976c98f706..00094dbb9a9 100644 --- a/src/platform/silabs/wifi/SiWx/WifiInterfaceImpl.cpp +++ b/src/platform/silabs/wifi/SiWx/WifiInterfaceImpl.cpp @@ -95,17 +95,11 @@ namespace { bool btn0_pressed = false; #endif // CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI91X_MCU_INTERFACE -bool hasNotifiedWifiConnectivity = false; -bool hasNotifiedIPV6 = false; -#if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) -bool hasNotifiedIPV4 = false; -#endif /* CHIP_DEVICE_CONFIG_ENABLE_IPV4 */ - wfx_wifi_scan_ext_t temp_reset; osSemaphoreId_t sScanCompleteSemaphore; osSemaphoreId_t sScanInProgressSemaphore; -osTimerId_t sDHCPTimer; + osMessageQueueId_t sWifiEventQueue = nullptr; sl_net_wifi_lwip_context_t wifi_client_context; @@ -261,27 +255,6 @@ sl_status_t BackgroundScanCallback(sl_wifi_event_t event, sl_wifi_scan_result_t return SL_STATUS_OK; } -void DHCPTimerEventHandler(void * arg) -{ - WifiPlatformEvent event = WifiPlatformEvent::kStationDhcpPoll; - sl_matter_wifi_post_event(event); -} - -void CancelDHCPTimer(void) -{ - VerifyOrReturn(osTimerIsRunning(sDHCPTimer), ChipLogDetail(DeviceLayer, "CancelDHCPTimer: timer not running")); - VerifyOrReturn(osTimerStop(sDHCPTimer) == osOK, ChipLogError(DeviceLayer, "CancelDHCPTimer: failed to stop timer")); -} - -void StartDHCPTimer(uint32_t timeout) -{ - // Cancel timer if already started - CancelDHCPTimer(); - - VerifyOrReturn(osTimerStart(sDHCPTimer, pdMS_TO_TICKS(timeout)) == osOK, - ChipLogError(DeviceLayer, "StartDHCPTimer: failed to start timer")); -} - sl_status_t sl_wifi_siwx917_init(void) { sl_status_t status = SL_STATUS_OK; @@ -308,12 +281,6 @@ sl_status_t sl_wifi_siwx917_init(void) RSI_NPSSGPIO_InputBufferEn(RTE_UULP_GPIO_1_PIN, 1); #endif // ENABLE_CHIP_SHELL #endif // CHIP_CONFIG_ENABLE_ICD_SERVER - -#else - // NCP Configurations - status = sl_matter_wifi_platform_init(); - VerifyOrReturnError(status == SL_STATUS_OK, status, - ChipLogError(DeviceLayer, "sl_matter_wifi_platform_init failed: 0x%lx", static_cast(status))); #endif // SLI_SI91X_MCU_INTERFACE sl_wifi_firmware_version_t version = { 0 }; @@ -347,8 +314,6 @@ sl_status_t sl_wifi_siwx917_init(void) return status; } -// TODO: this changes will be reverted back after the Silabs WiFi SDK team fix the scan API -#ifndef EXP_BOARD sl_status_t ScanCallback(sl_wifi_event_t event, sl_wifi_scan_result_t * scan_result, uint32_t result_length, void * arg) { sl_status_t status = SL_STATUS_OK; @@ -373,17 +338,13 @@ sl_status_t ScanCallback(sl_wifi_event_t event, sl_wifi_scan_result_t * scan_res osSemaphoreRelease(sScanCompleteSemaphore); return status; } -#endif sl_status_t InitiateScan() { sl_status_t status = SL_STATUS_OK; -// TODO: this changes will be reverted back after the Silabs WiFi SDK team fix the scan API -#ifndef EXP_BOARD sl_wifi_ssid_t ssid = { 0 }; - // TODO: this changes will be reverted back after the Silabs WiFi SDK team fix the scan API sl_wifi_scan_configuration_t wifi_scan_configuration = default_wifi_scan_configuration; ssid.length = wfx_rsi.sec.ssid_length; @@ -403,7 +364,6 @@ sl_status_t InitiateScan() } osSemaphoreRelease(sScanInProgressSemaphore); -#endif return status; } @@ -478,7 +438,7 @@ sl_status_t JoinWifiNetwork(void) if (status == SL_STATUS_OK || status == SL_STATUS_IN_PROGRESS) { WifiPlatformEvent event = WifiPlatformEvent::kStationConnect; - sl_matter_wifi_post_event(event); + PostWifiPlatformEvent(event); return status; } @@ -492,115 +452,67 @@ sl_status_t JoinWifiNetwork(void) wfx_retry_connection(++wfx_rsi.join_retries); WifiPlatformEvent event = WifiPlatformEvent::kStationStartJoin; - sl_matter_wifi_post_event(event); + PostWifiPlatformEvent(event); return status; } } // namespace -/** - * @brief Wifi initialization called from app main - * - * @return sl_status_t Returns underlying Wi-Fi initialization error - */ -sl_status_t sl_matter_wifi_platform_init(void) +sl_status_t TriggerPlatformWifiDisconnection() { - sl_status_t status = SL_STATUS_OK; - - status = sl_net_init((sl_net_interface_t) SL_NET_WIFI_CLIENT_INTERFACE, &config, &wifi_client_context, nullptr); - VerifyOrReturnError(status == SL_STATUS_OK, status, ChipLogError(DeviceLayer, "sl_net_init failed: %lx", status)); - - // Create Sempaphore for scan completion - sScanCompleteSemaphore = osSemaphoreNew(1, 0, nullptr); - VerifyOrReturnError(sScanCompleteSemaphore != nullptr, SL_STATUS_ALLOCATION_FAILED); - - // Create Semaphore for scan in-progress protection - sScanInProgressSemaphore = osSemaphoreNew(1, 1, nullptr); - VerifyOrReturnError(sScanCompleteSemaphore != nullptr, SL_STATUS_ALLOCATION_FAILED); - - // Create the message queue - sWifiEventQueue = osMessageQueueNew(kWfxQueueSize, sizeof(WifiPlatformEvent), nullptr); - VerifyOrReturnError(sWifiEventQueue != nullptr, SL_STATUS_ALLOCATION_FAILED); - - // Create timer for DHCP polling - // TODO: Use LWIP timer instead of creating a new one here - sDHCPTimer = osTimerNew(DHCPTimerEventHandler, osTimerPeriodic, nullptr, nullptr); - VerifyOrReturnError(sDHCPTimer != nullptr, SL_STATUS_ALLOCATION_FAILED); - - return status; + return sl_net_down((sl_net_interface_t) SL_NET_WIFI_CLIENT_INTERFACE); } -/****************************************************************** - * @fn int32_t wfx_rsi_get_ap_info(wfx_wifi_scan_result_t *ap) - * @brief - * Getting the AP details - * @param[in] ap: access point - * @return - * status - *********************************************************************/ -int32_t wfx_rsi_get_ap_info(wfx_wifi_scan_result_t * ap) +CHIP_ERROR GetAccessPointInfo(wfx_wifi_scan_result_t & info) { // TODO: Convert this to a int8 - int32_t rssi = 0; - ap->security = wfx_rsi.sec.security; - ap->chan = wfx_rsi.ap_chan; + int32_t rssi = 0; + info.security = wfx_rsi.sec.security; + info.chan = wfx_rsi.ap_chan; - chip::MutableByteSpan output(ap->ssid, WFX_MAX_SSID_LENGTH); + chip::MutableByteSpan output(info.ssid, WFX_MAX_SSID_LENGTH); // Cast is a workaround until the wfx_rsi structure is refactored chip::ByteSpan ssid(reinterpret_cast(wfx_rsi.sec.ssid), wfx_rsi.sec.ssid_length); chip::CopySpanToMutableSpan(ssid, output); - ap->ssid_length = output.size(); + info.ssid_length = output.size(); chip::ByteSpan apMacSpan(wfx_rsi.ap_mac.data(), wfx_rsi.ap_mac.size()); - chip::MutableByteSpan bssidSpan(ap->bssid, kWifiMacAddressLength); + chip::MutableByteSpan bssidSpan(info.bssid, kWifiMacAddressLength); chip::CopySpanToMutableSpan(apMacSpan, bssidSpan); // TODO: add error processing sl_wifi_get_signal_strength(SL_WIFI_CLIENT_INTERFACE, &(rssi)); - ap->rssi = rssi; + info.rssi = rssi; - return SL_STATUS_OK; + return CHIP_NO_ERROR; } -/****************************************************************** - * @fn int32_t wfx_rsi_get_ap_ext(wfx_wifi_scan_ext_t *extra_info) - * @brief - * Getting the AP extra details - * @param[in] extra info: access point extra information - * @return - * status - *********************************************************************/ -int32_t wfx_rsi_get_ap_ext(wfx_wifi_scan_ext_t * extra_info) +CHIP_ERROR GetAccessPointExtendedInfo(wfx_wifi_scan_ext_t & info) { - sl_status_t status = SL_STATUS_OK; sl_wifi_statistics_t test = { 0 }; - status = sl_wifi_get_statistics(SL_WIFI_CLIENT_INTERFACE, &test); - VERIFY_STATUS_AND_RETURN(status); - extra_info->beacon_lost_count = test.beacon_lost_count - temp_reset.beacon_lost_count; - extra_info->beacon_rx_count = test.beacon_rx_count - temp_reset.beacon_rx_count; - extra_info->mcast_rx_count = test.mcast_rx_count - temp_reset.mcast_rx_count; - extra_info->mcast_tx_count = test.mcast_tx_count - temp_reset.mcast_tx_count; - extra_info->ucast_rx_count = test.ucast_rx_count - temp_reset.ucast_rx_count; - extra_info->ucast_tx_count = test.ucast_tx_count - temp_reset.ucast_tx_count; - extra_info->overrun_count = test.overrun_count - temp_reset.overrun_count; - return status; + + sl_status_t status = sl_wifi_get_statistics(SL_WIFI_CLIENT_INTERFACE, &test); + VerifyOrReturnError(status == SL_STATUS_OK, CHIP_ERROR_INTERNAL); + + info.beacon_lost_count = test.beacon_lost_count - temp_reset.beacon_lost_count; + info.beacon_rx_count = test.beacon_rx_count - temp_reset.beacon_rx_count; + info.mcast_rx_count = test.mcast_rx_count - temp_reset.mcast_rx_count; + info.mcast_tx_count = test.mcast_tx_count - temp_reset.mcast_tx_count; + info.ucast_rx_count = test.ucast_rx_count - temp_reset.ucast_rx_count; + info.ucast_tx_count = test.ucast_tx_count - temp_reset.ucast_tx_count; + info.overrun_count = test.overrun_count - temp_reset.overrun_count; + + return CHIP_NO_ERROR; } -/****************************************************************** - * @fn int32_t wfx_rsi_reset_count(void) - * @brief - * Getting the driver reset count - * @param[in] None - * @return - * status - *********************************************************************/ -int32_t wfx_rsi_reset_count(void) +CHIP_ERROR ResetCounters() { sl_wifi_statistics_t test = { 0 }; - sl_status_t status = SL_STATUS_OK; - status = sl_wifi_get_statistics(SL_WIFI_CLIENT_INTERFACE, &test); - VERIFY_STATUS_AND_RETURN(status); + + sl_status_t status = sl_wifi_get_statistics(SL_WIFI_CLIENT_INTERFACE, &test); + VerifyOrReturnError(status == SL_STATUS_OK, CHIP_ERROR_INTERNAL); + temp_reset.beacon_lost_count = test.beacon_lost_count; temp_reset.beacon_rx_count = test.beacon_rx_count; temp_reset.mcast_rx_count = test.mcast_rx_count; @@ -608,30 +520,33 @@ int32_t wfx_rsi_reset_count(void) temp_reset.ucast_rx_count = test.ucast_rx_count; temp_reset.ucast_tx_count = test.ucast_tx_count; temp_reset.overrun_count = test.overrun_count; - return status; -} -/****************************************************************** - * @fn sl_wifi_platform_disconnect(void) - * @brief - * Getting the driver disconnect status - * @param[in] None - * @return - * status - *********************************************************************/ -int32_t sl_wifi_platform_disconnect(void) -{ - return sl_net_down((sl_net_interface_t) SL_NET_WIFI_CLIENT_INTERFACE); + return CHIP_NO_ERROR; } -/// NotifyConnectivity -/// @brief Notify the application about the connectivity status if it has not been notified yet. -/// Helper function for HandleDHCPPolling. -void NotifyConnectivity(void) +sl_status_t InitSiWxWifi(void) { - VerifyOrReturn(!hasNotifiedWifiConnectivity); - NotifyConnection(wfx_rsi.ap_mac); - hasNotifiedWifiConnectivity = true; + sl_status_t status = SL_STATUS_OK; + + status = sl_net_init((sl_net_interface_t) SL_NET_WIFI_CLIENT_INTERFACE, &config, &wifi_client_context, nullptr); + VerifyOrReturnError(status == SL_STATUS_OK, status, ChipLogError(DeviceLayer, "sl_net_init failed: %lx", status)); + + // Create Sempaphore for scan completion + sScanCompleteSemaphore = osSemaphoreNew(1, 0, nullptr); + VerifyOrReturnError(sScanCompleteSemaphore != nullptr, SL_STATUS_ALLOCATION_FAILED); + + // Create Semaphore for scan in-progress protection + sScanInProgressSemaphore = osSemaphoreNew(1, 1, nullptr); + VerifyOrReturnError(sScanCompleteSemaphore != nullptr, SL_STATUS_ALLOCATION_FAILED); + + // Create the message queue + sWifiEventQueue = osMessageQueueNew(kWfxQueueSize, sizeof(WifiPlatformEvent), nullptr); + VerifyOrReturnError(sWifiEventQueue != nullptr, SL_STATUS_ALLOCATION_FAILED); + + status = CreateDHCPTimer(); + VerifyOrReturnError(status == SL_STATUS_OK, status); + + return status; } void HandleDHCPPolling(void) @@ -644,61 +559,45 @@ void HandleDHCPPolling(void) #if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) uint8_t dhcp_state = dhcpclient_poll(sta_netif); - if (dhcp_state == DHCP_ADDRESS_ASSIGNED && !hasNotifiedIPV4) + if (dhcp_state == DHCP_ADDRESS_ASSIGNED && !HasNotifiedIPv4Change()) { wfx_dhcp_got_ipv4((uint32_t) sta_netif->ip_addr.u_addr.ip4.addr); - hasNotifiedIPV4 = true; - event = WifiPlatformEvent::kStationDhcpDone; - sl_matter_wifi_post_event(event); + NotifyIPv4Change(true); + event = WifiPlatformEvent::kStationDhcpDone; + PostWifiPlatformEvent(event); NotifyConnectivity(); } else if (dhcp_state == DHCP_OFF) { NotifyIPv4Change(false); - hasNotifiedIPV4 = false; } #endif /* CHIP_DEVICE_CONFIG_ENABLE_IPV4 */ /* Checks if the assigned IPv6 address is preferred by evaluating * the first block of IPv6 address ( block 0) */ - if ((ip6_addr_ispreferred(netif_ip6_addr_state(sta_netif, 0))) && !hasNotifiedIPV6) + if ((ip6_addr_ispreferred(netif_ip6_addr_state(sta_netif, 0))) && !HasNotifiedIPv6Change()) { char addrStr[chip::Inet::IPAddress::kMaxStringLength] = { 0 }; VerifyOrReturn(ip6addr_ntoa_r(netif_ip6_addr(sta_netif, 0), addrStr, sizeof(addrStr)) != nullptr); ChipLogProgress(DeviceLayer, "SLAAC OK: linklocal addr: %s", addrStr); NotifyIPv6Change(true); - hasNotifiedIPV6 = true; - event = WifiPlatformEvent::kStationDhcpDone; - sl_matter_wifi_post_event(event); + event = WifiPlatformEvent::kStationDhcpDone; + PostWifiPlatformEvent(event); NotifyConnectivity(); } } -void sl_matter_wifi_post_event(WifiPlatformEvent event) + +void PostWifiPlatformEvent(WifiPlatformEvent event) { sl_status_t status = osMessageQueuePut(sWifiEventQueue, &event, 0, 0); if (status != osOK) { - ChipLogError(DeviceLayer, "sl_matter_wifi_post_event: failed to post event with status: %ld", status); + ChipLogError(DeviceLayer, "PostWifiPlatformEvent: failed to post event with status: %ld", status); // TODO: Handle error, requeue event depending on queue size or notify relevant task, // Chipdie, etc. } } -/// ResetDHCPNotificationFlags -/// @brief Reset the flags that are used to notify the application about DHCP connectivity -/// and emits a WifiPlatformEvent::kStationDoDhcp event to trigger DHCP polling checks. Helper function for ProcessEvent. -void ResetDHCPNotificationFlags(void) -{ - -#if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) - hasNotifiedIPV4 = false; -#endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 - hasNotifiedIPV6 = false; - hasNotifiedWifiConnectivity = false; - - WifiPlatformEvent event = WifiPlatformEvent::kStationDoDhcp; - sl_matter_wifi_post_event(event); -} void ProcessEvent(WifiPlatformEvent event) { @@ -820,7 +719,7 @@ void ProcessEvent(WifiPlatformEvent event) } /********************************************************************************* - * @fn void sl_matter_wifi_task(void *arg) + * @fn void MatterWifiTask(void *arg) * @brief * The main WLAN task - started by StartWifiTask() that interfaces with RSI. * The rest of RSI stuff come in call-backs. @@ -830,20 +729,19 @@ void ProcessEvent(WifiPlatformEvent event) * None **********************************************************************************/ /* ARGSUSED */ -void sl_matter_wifi_task(void * arg) +void MatterWifiTask(void * arg) { (void) arg; WifiPlatformEvent event; sl_status_t status = SL_STATUS_OK; status = sl_wifi_siwx917_init(); - VerifyOrReturn( - status == SL_STATUS_OK, - ChipLogError(DeviceLayer, "sl_matter_wifi_task: sl_wifi_siwx917_init failed: 0x%lx", static_cast(status))); + VerifyOrReturn(status == SL_STATUS_OK, + ChipLogError(DeviceLayer, "MatterWifiTask: sl_wifi_siwx917_init failed: 0x%lx", static_cast(status))); sl_matter_wifi_task_started(); - ChipLogDetail(DeviceLayer, "sl_matter_wifi_task: starting event loop"); + ChipLogDetail(DeviceLayer, "MatterWifiTask: starting event loop"); for (;;) { if (osMessageQueueGet(sWifiEventQueue, &event, nullptr, osWaitForever) == osOK) @@ -852,7 +750,7 @@ void sl_matter_wifi_task(void * arg) } else { - ChipLogError(DeviceLayer, "sl_matter_wifi_task: get event failed: 0x%lx", static_cast(status)); + ChipLogError(DeviceLayer, "MatterWifiTask: get event failed: 0x%lx", static_cast(status)); } } } diff --git a/src/platform/silabs/wifi/SiWx/ncp/efx32_ncp_host.c b/src/platform/silabs/wifi/SiWx/ncp/efx32_ncp_host.c index e5d0fb4a3a1..a684c4ca063 100644 --- a/src/platform/silabs/wifi/SiWx/ncp/efx32_ncp_host.c +++ b/src/platform/silabs/wifi/SiWx/ncp/efx32_ncp_host.c @@ -27,8 +27,11 @@ #include "sl_si91x_host_interface.h" #include "sl_si91x_ncp_utility.h" #include "sl_si91x_status.h" +#include "sl_spidrv_exp_config.h" +#include "sl_spidrv_instances.h" #include "sl_status.h" #include "sl_wifi_constants.h" +#include "spidrv.h" #include #include #include @@ -42,35 +45,22 @@ #include "sl_board_control.h" #endif // SL_BOARD_NAME -static bool dma_callback(unsigned int channel, unsigned int sequenceNo, void * userParam); +#define LDMA_MAX_TRANSFER_LENGTH 4096 +#define LDMA_DESCRIPTOR_ARRAY_LENGTH (LDMA_MAX_TRANSFER_LENGTH / 2048) +#define SPI_HANDLE sl_spidrv_exp_handle +#define MAX_DATA_PACKET_SIZE 1800 + +// use SPI handle for EXP header (configured in project settings) +extern SPIDRV_Handle_t sl_spidrv_exp_handle; +static uint8_t dummy_buffer[MAX_DATA_PACKET_SIZE] = { 0 }; +static sl_si91x_host_init_configuration init_config = { 0 }; uint32_t rx_ldma_channel; uint32_t tx_ldma_channel; osMutexId_t ncp_transfer_mutex = 0; -static uint32_t dummy_buffer; -static sl_si91x_host_init_configuration init_config = { 0 }; - -// LDMA descriptor and transfer configuration structures for USART TX channel -LDMA_Descriptor_t ldmaTXDescriptor; -LDMA_TransferCfg_t ldmaTXConfig; - -// LDMA descriptor and transfer configuration structures for USART RX channel -LDMA_Descriptor_t ldmaRXDescriptor; -LDMA_TransferCfg_t ldmaRXConfig; - static osSemaphoreId_t transfer_done_semaphore = NULL; -static bool dma_callback([[maybe_unused]] unsigned int channel, [[maybe_unused]] unsigned int sequenceNo, - [[maybe_unused]] void * userParam) -{ -#if defined(SL_CATLOG_POWER_MANAGER_PRESENT) - sl_power_manager_remove_em_requirement(SL_POWER_MANAGER_EM1); -#endif - osSemaphoreRelease(transfer_done_semaphore); - return false; -} - static void gpio_interrupt([[maybe_unused]] uint8_t interrupt_number) { if (NULL != init_config.rx_irq) @@ -79,54 +69,27 @@ static void gpio_interrupt([[maybe_unused]] uint8_t interrupt_number) } } -static void efx32_spi_init(void) +static void spi_dma_callback(struct SPIDRV_HandleData * handle, Ecode_t transferStatus, int itemsTransferred) { - // Default asynchronous initializer (master mode, 1 Mbps, 8-bit data) - USART_InitSync_TypeDef init = USART_INITSYNC_DEFAULT; + UNUSED_PARAMETER(handle); + UNUSED_PARAMETER(transferStatus); + UNUSED_PARAMETER(itemsTransferred); +#if defined(SL_CATLOG_POWER_MANAGER_PRESENT) + sl_power_manager_remove_em_requirement(SL_POWER_MANAGER_EM1); +#endif + osSemaphoreRelease(transfer_done_semaphore); + return; +} - init.msbf = true; // MSB first transmission for SPI compatibility - init.autoCsEnable = false; - init.baudrate = USART_INITSYNC_BAUDRATE; +static void efx32_spi_init(void) +{ + SPIDRV_SetBitrate(SPI_HANDLE, USART_INITSYNC_BAUDRATE); // Configure SPI bus pins GPIO_PinModeSet(SPI_MISO_PIN.port, SPI_MISO_PIN.pin, gpioModeInput, 0); GPIO_PinModeSet(SPI_MOSI_PIN.port, SPI_MOSI_PIN.pin, gpioModePushPull, 0); GPIO_PinModeSet(SPI_CLOCK_PIN.port, SPI_CLOCK_PIN.pin, gpioModePushPullAlternate, 0); GPIO_PinModeSet(SPI_CS_PIN.port, SPI_CS_PIN.pin, gpioModePushPull, 1); - // Enable clock (not needed on xG21) - CMU_ClockEnable(SPI_USART_CMU_CLOCK, true); - - /* - * Route USART RX, TX, and CLK to the specified pins. Note that CS is - * not controlled by USART so there is no write to the corresponding - * USARTROUTE register to do this. - */ - GPIO->USARTROUTE[SPI_USART_ROUTE_INDEX].RXROUTE = - (SPI_MISO_PIN.port << _GPIO_USART_RXROUTE_PORT_SHIFT) | (SPI_MISO_PIN.pin << _GPIO_USART_RXROUTE_PIN_SHIFT); - GPIO->USARTROUTE[SPI_USART_ROUTE_INDEX].TXROUTE = - (SPI_MOSI_PIN.port << _GPIO_USART_TXROUTE_PORT_SHIFT) | (SPI_MOSI_PIN.pin << _GPIO_USART_TXROUTE_PIN_SHIFT); - GPIO->USARTROUTE[SPI_USART_ROUTE_INDEX].CLKROUTE = - (SPI_CLOCK_PIN.port << _GPIO_USART_CLKROUTE_PORT_SHIFT) | (SPI_CLOCK_PIN.pin << _GPIO_USART_CLKROUTE_PIN_SHIFT); - GPIO->USARTROUTE[SPI_USART_ROUTE_INDEX].CSROUTE = - (SPI_CS_PIN.port << _GPIO_USART_CSROUTE_PORT_SHIFT) | (SPI_CS_PIN.pin << _GPIO_USART_CSROUTE_PIN_SHIFT); - - // Enable USART interface pins - GPIO->USARTROUTE[SPI_USART_ROUTE_INDEX].ROUTEEN = GPIO_USART_ROUTEEN_RXPEN | // MISO - GPIO_USART_ROUTEEN_TXPEN | // MOSI -#if !SL_SPICTRL_MUX - GPIO_USART_ROUTEEN_CSPEN | -#endif - GPIO_USART_ROUTEEN_CLKPEN; - - // Set slew rate for alternate usage pins - GPIO_SlewrateSet(SPI_CLOCK_PIN.port, 7, 7); - - // Configure and enable USART - USART_InitSync(SPI_USART, &init); - - SPI_USART->TIMING |= USART_TIMING_TXDELAY_ONE | USART_TIMING_CSSETUP_ONE | USART_TIMING_CSHOLD_ONE; - - // SPI_USART->CTRL_SET |= USART_CTRL_SMSDELAY; // configure packet pending interrupt priority NVIC_SetPriority(GPIO_ODD_IRQn, PACKET_PENDING_INT_PRI); @@ -135,6 +98,46 @@ static void efx32_spi_init(void) GPIO_ExtIntConfig(INTERRUPT_PIN.port, INTERRUPT_PIN.pin, INTERRUPT_PIN.pin, true, false, true); } +Ecode_t si91x_SPIDRV_MTransfer(SPIDRV_Handle_t handle, const void * txBuffer, void * rxBuffer, int count, + SPIDRV_Callback_t callback) +{ + USART_TypeDef * usart = handle->initData.port; + uint8_t * tx = (txBuffer != NULL) ? (uint8_t *) txBuffer : dummy_buffer; + uint8_t * rx = (rxBuffer != NULL) ? (uint8_t *) rxBuffer : dummy_buffer; + + // For transfers less than 16 bytes, directly interacting with USART buffers is faster than using DMA + if (count < 16) + { + while (count > 0) + { + while (!(usart->STATUS & USART_STATUS_TXBL)) + { + } + usart->TXDATA = (uint32_t) *tx; + while (!(usart->STATUS & USART_STATUS_TXC)) + { + } + *rx = (uint8_t) usart->RXDATA; + if (txBuffer != NULL) + { + tx++; + } + if (rxBuffer != NULL) + { + rx++; + } + count--; + } + // callback(handle, ECODE_EMDRV_SPIDRV_OK, 0); + return ECODE_EMDRV_SPIDRV_OK; + } + else + { + SPIDRV_MTransfer(handle, tx, rx, count, callback); + } + return ECODE_EMDRV_SPIDRV_BUSY; +} + void sl_si91x_host_set_sleep_indicator(void) { GPIO_PinOutSet(SLEEP_CONFIRM_PIN.port, SLEEP_CONFIRM_PIN.pin); @@ -160,15 +163,16 @@ sl_status_t sl_si91x_host_init(const sl_si91x_host_init_configuration * config) return status; } #endif // SL_SPICTRL_MUX - init_config.rx_irq = config->rx_irq; - init_config.rx_done = config->rx_done; + init_config.rx_irq = config->rx_irq; + init_config.rx_done = config->rx_done; + init_config.boot_option = config->boot_option; // Enable clock (not needed on xG21) CMU_ClockEnable(cmuClock_GPIO, true); #if SL_SPICTRL_MUX spi_board_init(); -#endif +#endif // SL_SPICTRL_MUX if (transfer_done_semaphore == NULL) { @@ -189,10 +193,6 @@ sl_status_t sl_si91x_host_init(const sl_si91x_host_init_configuration * config) GPIO_PinModeSet(SLEEP_CONFIRM_PIN.port, SLEEP_CONFIRM_PIN.pin, gpioModeWiredOrPullDown, 1); GPIO_PinModeSet(WAKE_INDICATOR_PIN.port, WAKE_INDICATOR_PIN.pin, gpioModeWiredOrPullDown, 0); - DMADRV_Init(); - DMADRV_AllocateChannel((unsigned int *) &rx_ldma_channel, NULL); - DMADRV_AllocateChannel((unsigned int *) &tx_ldma_channel, NULL); - return SL_STATUS_OK; } @@ -223,68 +223,8 @@ sl_status_t sl_si91x_host_spi_transfer(const void * tx_buffer, void * rx_buffer, sl_wfx_host_spi_cs_assert(); #endif // SL_SPICTRL_MUX - if (buffer_length < 16) + if (ECODE_EMDRV_SPIDRV_BUSY == si91x_SPIDRV_MTransfer(SPI_HANDLE, tx_buffer, rx_buffer, buffer_length, spi_dma_callback)) { - uint8_t * tx = (tx_buffer != NULL) ? (uint8_t *) tx_buffer : (uint8_t *) &dummy_buffer; - uint8_t * rx = (rx_buffer != NULL) ? (uint8_t *) rx_buffer : (uint8_t *) &dummy_buffer; - while (buffer_length > 0) - { - while (!(SPI_USART->STATUS & USART_STATUS_TXBL)) - { - } - SPI_USART->TXDATA = (uint32_t) *tx; - while (!(SPI_USART->STATUS & USART_STATUS_TXC)) - { - } - *rx = (uint8_t) SPI_USART->RXDATA; - if (tx_buffer != NULL) - { - tx++; - } - if (rx_buffer != NULL) - { - rx++; - } - buffer_length--; - } - } - else - { - if (tx_buffer == NULL) - { - dummy_buffer = 0; - ldmaTXDescriptor = - (LDMA_Descriptor_t) LDMA_DESCRIPTOR_SINGLE_P2P_BYTE(&dummy_buffer, &(SPI_USART->TXDATA), buffer_length); - } - else - { - ldmaTXDescriptor = (LDMA_Descriptor_t) LDMA_DESCRIPTOR_SINGLE_M2P_BYTE(tx_buffer, &(SPI_USART->TXDATA), buffer_length); - } - - if (rx_buffer == NULL) - { - ldmaRXDescriptor = - (LDMA_Descriptor_t) LDMA_DESCRIPTOR_SINGLE_P2P_BYTE(&(SPI_USART->RXDATA), &dummy_buffer, buffer_length); - } - else - { - ldmaRXDescriptor = (LDMA_Descriptor_t) LDMA_DESCRIPTOR_SINGLE_P2M_BYTE(&(SPI_USART->RXDATA), rx_buffer, buffer_length); - } - - // Transfer a byte on free space in the USART buffer - ldmaTXConfig = (LDMA_TransferCfg_t) LDMA_TRANSFER_CFG_PERIPHERAL(SPI_USART_LDMA_TX); - - // Transfer a byte on receive data valid - ldmaRXConfig = (LDMA_TransferCfg_t) LDMA_TRANSFER_CFG_PERIPHERAL(SPI_USART_LDMA_RX); - -#if defined(SL_CATLOG_POWER_MANAGER_PRESENT) - sl_power_manager_remove_em_requirement(SL_POWER_MANAGER_EM1); -#endif - - // Start both channels - DMADRV_LdmaStartTransfer(rx_ldma_channel, &ldmaRXConfig, &ldmaRXDescriptor, dma_callback, NULL); - DMADRV_LdmaStartTransfer(tx_ldma_channel, &ldmaTXConfig, &ldmaTXDescriptor, NULL, NULL); - if (osSemaphoreAcquire(transfer_done_semaphore, 1000) != osOK) { BREAKPOINT(); @@ -300,7 +240,6 @@ sl_status_t sl_si91x_host_spi_transfer(const void * tx_buffer, void * rx_buffer, void sl_si91x_host_hold_in_reset(void) { - GPIO_PinModeSet(RESET_PIN.port, RESET_PIN.pin, gpioModePushPull, 1); GPIO_PinOutClear(RESET_PIN.port, RESET_PIN.pin); } diff --git a/src/platform/silabs/wifi/WifiInterface.cpp b/src/platform/silabs/wifi/WifiInterface.cpp index ddc4de8f6d6..8174c58d8a5 100644 --- a/src/platform/silabs/wifi/WifiInterface.cpp +++ b/src/platform/silabs/wifi/WifiInterface.cpp @@ -45,6 +45,11 @@ constexpr uint8_t kWlanMaxRetryIntervalsInSec = 60; uint8_t retryInterval = kWlanMinRetryIntervalsInSec; osTimerId_t sRetryTimer; +bool hasNotifiedIPV6 = false; +#if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) +bool hasNotifiedIPV4 = false; +#endif /* CHIP_DEVICE_CONFIG_ENABLE_IPV4 */ + /* * Notifications to the upper-layer * All done in the context of the RSI/WiFi task (rsi_if.c) @@ -66,15 +71,19 @@ void RetryConnectionTimerHandler(void * arg) void NotifyIPv6Change(bool gotIPv6Addr) { + hasNotifiedIPV6 = gotIPv6Addr; + sl_wfx_generic_message_t eventData = {}; eventData.header.id = gotIPv6Addr ? to_underlying(WifiEvent::kGotIPv6) : to_underlying(WifiEvent::kLostIP); eventData.header.length = sizeof(eventData.header); HandleWFXSystemEvent(&eventData); } - +#if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) void NotifyIPv4Change(bool gotIPv4Addr) { + hasNotifiedIPV4 = gotIPv4Addr; + sl_wfx_generic_message_t eventData; memset(&eventData, 0, sizeof(eventData)); @@ -82,6 +91,7 @@ void NotifyIPv4Change(bool gotIPv4Addr) eventData.header.length = sizeof(eventData.header); HandleWFXSystemEvent(&eventData); } +#endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 void NotifyDisconnection(WifiDisconnectionReasons reason) { @@ -106,6 +116,25 @@ void NotifyConnection(const MacAddress & ap) HandleWFXSystemEvent((sl_wfx_generic_message_t *) &evt); } +bool HasNotifiedIPv6Change() +{ + return hasNotifiedIPV6; +} + +#if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) +bool HasNotifiedIPv4Change() +{ + return hasNotifiedIPV4; +} +#endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 + +void ResetIPNotificationStates() +{ + hasNotifiedIPV6 = false; +#if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) + hasNotifiedIPV4 = false; +#endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 +} /* Function to update */ /*********************************************************************************** diff --git a/src/platform/silabs/wifi/WifiInterface.h b/src/platform/silabs/wifi/WifiInterface.h index 4f62850429a..bc64846e0cf 100644 --- a/src/platform/silabs/wifi/WifiInterface.h +++ b/src/platform/silabs/wifi/WifiInterface.h @@ -120,15 +120,6 @@ typedef struct wfx_sec_t security; } wfx_wifi_provision_t; -typedef enum -{ - WIFI_MODE_NULL = 0, - WIFI_MODE_STA, - WIFI_MODE_AP, - WIFI_MODE_APSTA, - WIFI_MODE_MAX, -} wifi_mode_t; - typedef struct wfx_wifi_scan_result { uint8_t ssid[WFX_MAX_SSID_LENGTH]; // excludes null-character @@ -218,6 +209,36 @@ void NotifyDisconnection(WifiDisconnectionReasons reason); */ void NotifyConnection(const MacAddress & ap); +/** + * @brief Returns the IPv6 notification state + * + * @note This function is necessary because the class inheritance structure has not been done as of yet. + * Once the inheritance is done, sub-classes will have access to the member directly wihtout needing to use an extra guetter. + * + * @return true, IPv6 change has been notified + false, otherwise + */ +bool HasNotifiedIPv6Change(); + +/** + * @brief Returns the IPv4 notification state + * + * @note This function is necessary because the class inheritance structure has not been done as of yet. + * Once the inheritance is done, sub-classes will have access to the member directly wihtout needing to use an extra guetter. + * + * @return true, IPv4 change has been notified + false, otherwise + */ +bool HasNotifiedIPv4Change(); + +/** + * @brief Function resets the IP notification states + * + * * @note This function is necessary because the class inheritance structure has not been done as of yet. + * Once the inheritance is done, sub-classes will have access to the member directly wihtout needing to use an extra guetter. + */ +void ResetIPNotificationStates(); + /** * @brief Returns the provide interfaces MAC address * Valid buffer large enough for the MAC address must be provided to the function @@ -277,26 +298,70 @@ bool IsStationConnected(); * @return true, if the Wi-Fi Station mode is enabled * false, otherwise */ -bool IsStationModeEnabled(void); +bool IsStationModeEnabled(); + +/** + * @brief Returns the state of the Wi-Fi station initialization + * + * @return true, if the initialization was successful + * false, otherwise + */ +bool IsStationReady(); + +/** + * @brief Triggers the device to disconnect from the connected Wi-Fi network + * + * @note The disconnection is not immediate. It can take a certain amount of time for the device to be in a disconnected state once + * the function is called. When the function returns, the device might not have yet disconnected from the Wi-Fi network. + * + * @return CHIP_ERROR CHIP_NO_ERROR, disconnection request was succesfully triggered + * otherwise, CHIP_ERROR_INTERNAL + */ +CHIP_ERROR TriggerDisconnection(); + +/** + * @brief Gets the connected access point information. + * See @wfx_wifi_scan_result_t for the information that is returned by the function. + * + * @param[out] info AP information + * + * @return CHIP_ERROR CHIP_NO_ERROR, device has succesfully pulled all the AP information + * CHIP_ERROR_INTERNAL, otherwise. If the function returns an error, the data in ap cannot be used. + */ +CHIP_ERROR GetAccessPointInfo(wfx_wifi_scan_result_t & info); + +/** + * @brief Gets the connected access point extended information. + * See @wfx_wifi_scan_ext_t for the information that is returned by the information + * + * @param[out] info AP extended information + * + * @return CHIP_ERROR CHIP_NO_ERROR, device has succesfully pulled all the AP information + * CHIP_ERROR_INTERNAL, otherwise. If the function returns an error, the data in ap cannot be used. + */ +CHIP_ERROR GetAccessPointExtendedInfo(wfx_wifi_scan_ext_t & info); + +/** + * @brief Function resets the BeaconLostCount, BeaconRxCount, PacketMulticastRxCount, PacketMulticastTxCount, PacketUnicastRxCount, + * PacketUnicastTxCount back to 0 + * + * @return CHIP_ERROR CHIP_NO_ERROR, the counters were succesfully reset to 0. + * CHIP_ERROR_INTERNAL, if there was an error when resetting the counter values + */ +CHIP_ERROR ResetCounters(); /* Function to update */ void wfx_set_wifi_provision(wfx_wifi_provision_t * wifiConfig); bool wfx_get_wifi_provision(wfx_wifi_provision_t * wifiConfig); -int32_t wfx_get_ap_info(wfx_wifi_scan_result_t * ap); -int32_t wfx_get_ap_ext(wfx_wifi_scan_ext_t * extra_info); -int32_t wfx_reset_counts(); void wfx_clear_wifi_provision(void); sl_status_t wfx_connect_to_ap(void); -void wfx_setup_ip6_link_local(sl_wfx_interface_t); -sl_status_t sl_matter_wifi_disconnect(void); #if CHIP_DEVICE_CONFIG_ENABLE_IPV4 bool wfx_have_ipv4_addr(sl_wfx_interface_t); #endif /* CHIP_DEVICE_CONFIG_ENABLE_IPV4 */ bool wfx_have_ipv6_addr(sl_wfx_interface_t); -wifi_mode_t wfx_get_wifi_mode(void); void wfx_cancel_scan(void); /* @@ -324,8 +389,6 @@ int32_t wfx_rsi_send_data(void * p, uint16_t len); #endif //!(EXP_BOARD) #endif // RS911X_WIFI -bool wfx_hw_ready(void); - #ifdef RS911X_WIFI // for RS9116, 917 NCP and 917 SoC /* RSI Power Save */ #if SL_ICD_ENABLED @@ -337,27 +400,12 @@ sl_status_t wfx_power_save(); #endif /* SL_ICD_ENABLED */ #endif /* RS911X_WIFI */ -void sl_matter_wifi_task(void * arg); - -int32_t wfx_rsi_get_ap_info(wfx_wifi_scan_result_t * ap); -int32_t wfx_rsi_get_ap_ext(wfx_wifi_scan_ext_t * extra_info); -int32_t wfx_rsi_reset_count(); -int32_t sl_wifi_platform_disconnect(); - #ifdef __cplusplus extern "C" { #endif -#if (SLI_SI91X_MCU_INTERFACE) -#if SL_ICD_ENABLED -// TODO : This should be moved outside of the Wifi interface functions -void sl_button_on_change(uint8_t btn, uint8_t btnAction); -#endif /* SL_ICD_ENABLED */ -#endif /* SLI_SI91X_MCU_INTERFACE */ - #ifdef WF200_WIFI void sl_wfx_host_gpio_init(void); -void wfx_bus_start(void); #endif /* WF200_WIFI */ #ifdef __cplusplus diff --git a/src/platform/silabs/wifi/lwip-support/ethernetif.cpp b/src/platform/silabs/wifi/lwip-support/ethernetif.cpp index 0f97ac948ad..84485f71a92 100644 --- a/src/platform/silabs/wifi/lwip-support/ethernetif.cpp +++ b/src/platform/silabs/wifi/lwip-support/ethernetif.cpp @@ -334,8 +334,7 @@ static err_t low_level_output(struct netif * netif, struct pbuf * p) { #if (SLI_SI91X_MCU_INTERFACE | EXP_BOARD) UNUSED_PARAMETER(netif); - sl_status_t status; - status = sl_wifi_send_raw_data_frame(SL_WIFI_CLIENT_INTERFACE, (uint8_t *) p->payload, p->len); + sl_status_t status = sl_wifi_send_raw_data_frame(SL_WIFI_CLIENT_INTERFACE, (uint8_t *) p->payload, p->len); if (status != SL_STATUS_OK) { return ERR_IF; @@ -367,14 +366,14 @@ static err_t low_level_output(struct netif * netif, struct pbuf * p) #endif if ((netif->flags & (NETIF_FLAG_LINK_UP | NETIF_FLAG_UP)) != (NETIF_FLAG_LINK_UP | NETIF_FLAG_UP)) { - ChipLogProgress(DeviceLayer, "EN-RSI:NOT UP"); + ChipLogError(DeviceLayer, "EN-RSI:NOT UP"); xSemaphoreGive(ethout_sem); return ERR_IF; } packet = wfx_rsi_alloc_pkt(); if (!packet) { - ChipLogProgress(DeviceLayer, "EN-RSI:No buf"); + ChipLogError(DeviceLayer, "EN-RSI:No buf"); xSemaphoreGive(ethout_sem); return ERR_IF; } @@ -402,9 +401,10 @@ static err_t low_level_output(struct netif * netif, struct pbuf * p) /* forward the generated packet to RSI to * send the data over wifi network */ - if (wfx_rsi_send_data(packet, datalength)) + int32_t status = wfx_rsi_send_data(packet, datalength); + if (status != 0) { - ChipLogProgress(DeviceLayer, "*ERR*EN-RSI:Send fail"); + ChipLogError(DeviceLayer, "*ERR*EN-RSI:Send fail: %ld", status); xSemaphoreGive(ethout_sem); return ERR_IF; } diff --git a/src/platform/silabs/wifi/rs911x/WifiInterfaceImpl.cpp b/src/platform/silabs/wifi/rs911x/WifiInterfaceImpl.cpp index a4dbf002ef1..d55cddf7e5a 100644 --- a/src/platform/silabs/wifi/rs911x/WifiInterfaceImpl.cpp +++ b/src/platform/silabs/wifi/rs911x/WifiInterfaceImpl.cpp @@ -19,6 +19,7 @@ #include "silabs_utils.h" #include "sl_status.h" #include +#include #include #include #include @@ -70,18 +71,10 @@ osThreadAttr_t kDrvTaskAttr = { .name = "drv_rsi", .stack_size = kDrvTaskSize, .priority = osPriorityHigh }; -bool hasNotifiedIPV6 = false; -#if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) -bool hasNotifiedIPV4 = false; -#endif /* CHIP_DEVICE_CONFIG_ENABLE_IPV4 */ -bool hasNotifiedWifiConnectivity = false; - #if (RSI_BLE_ENABLE) extern rsi_semaphore_handle_t sl_rs_ble_init_sem; #endif -// DHCP Poll timer -static osTimerId_t sDHCPTimer; static osMessageQueueId_t sWifiEventQueue = NULL; /* * This file implements the interface to the RSI SAPIs @@ -89,149 +82,78 @@ static osMessageQueueId_t sWifiEventQueue = NULL; static uint8_t wfx_rsi_drv_buf[WFX_RSI_BUF_SZ]; static wfx_wifi_scan_ext_t temp_reset; -/****************************************************************** - * @fn void rsi_wireless_driver_task_wrapper(void * argument) - * @brief - * wrapper thread for the driver task - * @param[in] argument: argument - * @return - * None - *********************************************************************/ -static void rsi_wireless_driver_task_wrapper(void * argument) +sl_status_t TriggerPlatformWifiDisconnection() { - rsi_wireless_driver_task(); + VerifyOrReturnError(rsi_wlan_disconnect() == RSI_SUCCESS, SL_STATUS_FAIL); + return SL_STATUS_OK; } -static void DHCPTimerEventHandler(void * arg) +CHIP_ERROR GetAccessPointInfo(wfx_wifi_scan_result_t & info) { - WifiPlatformEvent event = WifiPlatformEvent::kStationDhcpPoll; - sl_matter_wifi_post_event(event); -} + int32_t status = RSI_SUCCESS; + uint8_t rssi = 0; -static void CancelDHCPTimer(void) -{ - osStatus_t status; + info.security = wfx_rsi.sec.security; + info.chan = wfx_rsi.ap_chan; + memcpy(&(info.bssid[0]), wfx_rsi.ap_mac.data(), kWifiMacAddressLength); - // Check if timer started - if (!osTimerIsRunning(sDHCPTimer)) - { - ChipLogError(DeviceLayer, "CancelDHCPTimer: timer not running"); - return; - } + status = rsi_wlan_get(RSI_RSSI, &rssi, sizeof(rssi)); + VerifyOrReturnError(status = RSI_SUCCESS, CHIP_ERROR_INTERNAL); - status = osTimerStop(sDHCPTimer); - if (status != osOK) - { - ChipLogError(DeviceLayer, "CancelDHCPTimer: failed to stop timer with status: %d", status); - } + info.rssi = (-1) * rssi; + + return CHIP_NO_ERROR; } -static void StartDHCPTimer(uint32_t timeout) +CHIP_ERROR GetAccessPointExtendedInfo(wfx_wifi_scan_ext_t & info) { - osStatus_t status; + rsi_wlan_ext_stats_t stats = { 0 }; - // Cancel timer if already started - CancelDHCPTimer(); + int32_t status = rsi_wlan_get(RSI_WLAN_EXT_STATS, reinterpret_cast(&stats), sizeof(stats)); + VerifyOrReturnError(status == RSI_SUCCESS, CHIP_ERROR_INTERNAL, + ChipLogError(DeviceLayer, "Failed, Error Code : 0x%lX", status)); - status = osTimerStart(sDHCPTimer, pdMS_TO_TICKS(timeout)); - if (status != osOK) - { - ChipLogError(DeviceLayer, "StartDHCPTimer: failed to start timer with status: %d", status); - } -} + info.beacon_lost_count = stats.beacon_lost_count - temp_reset.beacon_lost_count; + info.beacon_rx_count = stats.beacon_rx_count - temp_reset.beacon_rx_count; + info.mcast_rx_count = stats.mcast_rx_count - temp_reset.mcast_rx_count; + info.mcast_tx_count = stats.mcast_tx_count - temp_reset.mcast_tx_count; + info.ucast_rx_count = stats.ucast_rx_count - temp_reset.ucast_rx_count; + info.ucast_tx_count = stats.ucast_tx_count - temp_reset.ucast_tx_count; + info.overrun_count = stats.overrun_count - temp_reset.overrun_count; -/****************************************************************** - * @fn int32_t wfx_rsi_get_ap_info(wfx_wifi_scan_result_t *ap) - * @brief - * Getting the AP details - * @param[in] ap: access point - * @return - * status - *********************************************************************/ -int32_t wfx_rsi_get_ap_info(wfx_wifi_scan_result_t * ap) -{ - int32_t status = RSI_SUCCESS; - uint8_t rssi; - ap->security = wfx_rsi.sec.security; - ap->chan = wfx_rsi.ap_chan; - memcpy(&ap->bssid[0], wfx_rsi.ap_mac.data(), kWifiMacAddressLength); - status = rsi_wlan_get(RSI_RSSI, &rssi, sizeof(rssi)); - if (status == RSI_SUCCESS) - { - ap->rssi = (-1) * rssi; - } - return status; + return CHIP_NO_ERROR; } -/****************************************************************** - * @fn int32_t wfx_rsi_get_ap_ext(wfx_wifi_scan_ext_t *extra_info) - * @brief - * Getting the AP extra details - * @param[in] extra info: access point extra information - * @return - * status - *********************************************************************/ -int32_t wfx_rsi_get_ap_ext(wfx_wifi_scan_ext_t * extra_info) +CHIP_ERROR ResetCounters() { - int32_t status; - uint8_t buff[RSI_RESPONSE_MAX_SIZE] = { 0 }; - status = rsi_wlan_get(RSI_WLAN_EXT_STATS, buff, sizeof(buff)); - if (status != RSI_SUCCESS) - { - ChipLogError(DeviceLayer, "Failed, Error Code : 0x%lX", status); - return status; - } - rsi_wlan_ext_stats_t * test = (rsi_wlan_ext_stats_t *) buff; - extra_info->beacon_lost_count = test->beacon_lost_count - temp_reset.beacon_lost_count; - extra_info->beacon_rx_count = test->beacon_rx_count - temp_reset.beacon_rx_count; - extra_info->mcast_rx_count = test->mcast_rx_count - temp_reset.mcast_rx_count; - extra_info->mcast_tx_count = test->mcast_tx_count - temp_reset.mcast_tx_count; - extra_info->ucast_rx_count = test->ucast_rx_count - temp_reset.ucast_rx_count; - extra_info->ucast_tx_count = test->ucast_tx_count - temp_reset.ucast_tx_count; - extra_info->overrun_count = test->overrun_count - temp_reset.overrun_count; - return status; -} + rsi_wlan_ext_stats_t stats = { 0 }; -/****************************************************************** - * @fn int32_t wfx_rsi_reset_count(void) - * @brief - * Getting the driver reset count - * @param[in] None - * @return - * status - *********************************************************************/ -int32_t wfx_rsi_reset_count(void) -{ - int32_t status; - uint8_t buff[RSI_RESPONSE_MAX_SIZE] = { 0 }; - status = rsi_wlan_get(RSI_WLAN_EXT_STATS, buff, sizeof(buff)); - if (status != RSI_SUCCESS) - { - ChipLogError(DeviceLayer, "Failed, Error Code : 0x%lX", status); - return status; - } - rsi_wlan_ext_stats_t * test = (rsi_wlan_ext_stats_t *) buff; - temp_reset.beacon_lost_count = test->beacon_lost_count; - temp_reset.beacon_rx_count = test->beacon_rx_count; - temp_reset.mcast_rx_count = test->mcast_rx_count; - temp_reset.mcast_tx_count = test->mcast_tx_count; - temp_reset.ucast_rx_count = test->ucast_rx_count; - temp_reset.ucast_tx_count = test->ucast_tx_count; - temp_reset.overrun_count = test->overrun_count; - return status; + int32_t status = rsi_wlan_get(RSI_WLAN_EXT_STATS, reinterpret_cast(&stats), sizeof(stats)); + VerifyOrReturnError(status == RSI_SUCCESS, CHIP_ERROR_INTERNAL, + ChipLogError(DeviceLayer, "Failed, Error Code : 0x%lX", status)); + + temp_reset.beacon_lost_count = stats.beacon_lost_count; + temp_reset.beacon_rx_count = stats.beacon_rx_count; + temp_reset.mcast_rx_count = stats.mcast_rx_count; + temp_reset.mcast_tx_count = stats.mcast_tx_count; + temp_reset.ucast_rx_count = stats.ucast_rx_count; + temp_reset.ucast_tx_count = stats.ucast_tx_count; + temp_reset.overrun_count = stats.overrun_count; + + return CHIP_NO_ERROR; } /****************************************************************** - * @fn sl_wifi_platform_disconnect(void) + * @fn void rsi_wireless_driver_task_wrapper(void * argument) * @brief - * Getting the driver disconnect status - * @param[in] None + * wrapper thread for the driver task + * @param[in] argument: argument * @return - * status + * None *********************************************************************/ -int32_t sl_wifi_platform_disconnect(void) +static void rsi_wireless_driver_task_wrapper(void * argument) { - return rsi_wlan_disconnect(); + rsi_wireless_driver_task(); } /****************************************************************** @@ -263,7 +185,7 @@ static void wfx_rsi_join_cb(uint16_t status, const uint8_t * buf, const uint16_t ChipLogProgress(DeviceLayer, "wfx_rsi_join_cb: success"); memset(&temp_reset, 0, sizeof(wfx_wifi_scan_ext_t)); WifiPlatformEvent event = WifiPlatformEvent::kStationConnect; - sl_matter_wifi_post_event(event); + PostWifiPlatformEvent(event); wfx_rsi.join_retries = 0; } @@ -285,7 +207,7 @@ static void wfx_rsi_join_fail_cb(uint16_t status, uint8_t * buf, uint32_t len) wfx_rsi.dev_state.Clear(WifiState::kStationConnecting).Clear(WifiState::kStationConnected); WifiPlatformEvent event = WifiPlatformEvent::kStationStartJoin; - sl_matter_wifi_post_event(event); + PostWifiPlatformEvent(event); } /************************************************************************************* * @fn wfx_rsi_wlan_pkt_cb(uint16_t status, uint8_t *buf, uint32_t len) @@ -391,13 +313,9 @@ static int32_t sl_matter_wifi_init(void) return SL_STATUS_ALLOCATION_FAILED; } - // Create timer for DHCP polling // TODO: Use LWIP timer instead of creating a new one here - sDHCPTimer = osTimerNew(DHCPTimerEventHandler, osTimerPeriodic, NULL, NULL); - if (sDHCPTimer == NULL) - { - return SL_STATUS_ALLOCATION_FAILED; - } + status = CreateDHCPTimer(); + VerifyOrReturnError(status == SL_STATUS_OK, status); /* * Register callbacks - We are only interested in the connectivity CBs @@ -555,19 +473,6 @@ static void sl_wifi_platform_join_network(void) } } -/** NotifyConnectivity - * @brief Notify the application about the connectivity status if it has not been notified yet. - * Helper function for HandleDHCPPolling. - */ -void NotifyConnectivity(void) -{ - if (!hasNotifiedWifiConnectivity) - { - NotifyConnection(wfx_rsi.ap_mac); - hasNotifiedWifiConnectivity = true; - } -} - void HandleDHCPPolling(void) { struct netif * sta_netif; @@ -582,56 +487,39 @@ void HandleDHCPPolling(void) } #if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) uint8_t dhcp_state = dhcpclient_poll(sta_netif); - if (dhcp_state == DHCP_ADDRESS_ASSIGNED && !hasNotifiedIPV4) + if (dhcp_state == DHCP_ADDRESS_ASSIGNED && !HasNotifiedIPv4Change()) { wfx_dhcp_got_ipv4((uint32_t) sta_netif->ip_addr.u_addr.ip4.addr); - hasNotifiedIPV4 = true; + NotifyIPv4Change(true); NotifyConnectivity(); } else if (dhcp_state == DHCP_OFF) { NotifyIPv4Change(false); - hasNotifiedIPV4 = false; } #endif /* CHIP_DEVICE_CONFIG_ENABLE_IPV4 */ /* Checks if the assigned IPv6 address is preferred by evaluating * the first block of IPv6 address ( block 0) */ - if ((ip6_addr_ispreferred(netif_ip6_addr_state(sta_netif, 0))) && !hasNotifiedIPV6) + if ((ip6_addr_ispreferred(netif_ip6_addr_state(sta_netif, 0))) && !HasNotifiedIPv6Change()) { + char addrStr[chip::Inet::IPAddress::kMaxStringLength] = { 0 }; + VerifyOrReturn(ip6addr_ntoa_r(netif_ip6_addr(sta_netif, 0), addrStr, sizeof(addrStr)) != nullptr); + ChipLogProgress(DeviceLayer, "SLAAC OK: linklocal addr: %s", addrStr); NotifyIPv6Change(true); - hasNotifiedIPV6 = true; WifiPlatformEvent event = WifiPlatformEvent::kStationDhcpDone; - sl_matter_wifi_post_event(event); + PostWifiPlatformEvent(event); NotifyConnectivity(); } } -/** ResetDHCPNotificationFlags - * @brief Reset the flags that are used to notify the application about DHCP connectivity - * and emits a WifiPlatformEvent::kStationDoDhcp event to trigger DHCP polling checks. Helper function for ProcessEvent. - */ -void ResetDHCPNotificationFlags(void) -{ - WifiPlatformEvent outEvent; - -#if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) - hasNotifiedIPV4 = false; -#endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 - hasNotifiedIPV6 = false; - hasNotifiedWifiConnectivity = false; - - outEvent = WifiPlatformEvent::kStationDoDhcp; - sl_matter_wifi_post_event(outEvent); -} - -void sl_matter_wifi_post_event(WifiPlatformEvent event) +void PostWifiPlatformEvent(WifiPlatformEvent event) { sl_status_t status = osMessageQueuePut(sWifiEventQueue, &event, 0, 0); if (status != osOK) { - ChipLogError(DeviceLayer, "sl_matter_wifi_post_event: failed to post event with status: %ld", status); + ChipLogError(DeviceLayer, "PostWifiPlatformEvent: failed to post event with status: %ld", status); // TODO: Handle error, requeue event depending on queue size or notify relevant task, // Chipdie, etc. } @@ -758,31 +646,20 @@ void ProcessEvent(WifiPlatformEvent event) } } -/********************************************************************************* - * @fn void sl_matter_wifi_task(void *arg) - * @brief - * The main WLAN task - started by StartWifiTask () that interfaces with RSI. - * The rest of RSI stuff come in call-backs. - * The initialization has been already done. - * @param[in] arg: - * @return - * None - **********************************************************************************/ -/* ARGSUSED */ -void sl_matter_wifi_task(void * arg) +void MatterWifiTask(void * arg) { (void) arg; uint32_t rsi_status = sl_matter_wifi_init(); if (rsi_status != RSI_SUCCESS) { - ChipLogError(DeviceLayer, "sl_matter_wifi_task: sl_matter_wifi_init failed: %ld", rsi_status); + ChipLogError(DeviceLayer, "MatterWifiTask: sl_matter_wifi_init failed: %ld", rsi_status); return; } WifiPlatformEvent event; sl_matter_lwip_start(); sl_matter_wifi_task_started(); - ChipLogProgress(DeviceLayer, "sl_matter_wifi_task: starting event loop"); + ChipLogProgress(DeviceLayer, "MatterWifiTask: starting event loop"); for (;;) { osStatus_t status = osMessageQueueGet(sWifiEventQueue, &event, NULL, osWaitForever); @@ -792,7 +669,7 @@ void sl_matter_wifi_task(void * arg) } else { - ChipLogProgress(DeviceLayer, "sl_matter_wifi_task: get event failed: %x", status); + ChipLogProgress(DeviceLayer, "MatterWifiTask: get event failed: %x", status); } } } diff --git a/src/platform/silabs/wifi/wf200/WifiInterfaceImpl.cpp b/src/platform/silabs/wifi/wf200/WifiInterfaceImpl.cpp index c28b55cd560..798f6a8d8fd 100644 --- a/src/platform/silabs/wifi/wf200/WifiInterfaceImpl.cpp +++ b/src/platform/silabs/wifi/wf200/WifiInterfaceImpl.cpp @@ -105,8 +105,6 @@ struct netif * sta_netif; wfx_wifi_provision_t wifi_provision; #define PUT_COUNTER(name) ChipLogDetail(DeviceLayer, "%-24s %lu", #name, (unsigned long) counters->body.count_##name); -bool hasNotifiedIPV6 = false; -bool hasNotifiedIPV4 = false; bool hasNotifiedWifiConnectivity = false; static uint8_t retryJoin = 0; bool retryInProgress = false; @@ -367,6 +365,73 @@ bool IsStationConnected() return wifi_extra.Has(WifiState::kStationConnected); } +bool IsStationReady() +{ + return wifi_extra.Has(WifiState::kStationInit); +} + +CHIP_ERROR TriggerDisconnection(void) +{ + ChipLogProgress(DeviceLayer, "STA-Disconnecting"); + + sl_status_t status = sl_wfx_send_disconnect_command(); + VerifyOrReturnError(status == SL_STATUS_OK, CHIP_ERROR_INTERNAL); + + wifi_extra.Clear(WifiState::kStationConnected); + + xEventGroupSetBits(sl_wfx_event_group, SL_WFX_RETRY_CONNECT); + return CHIP_NO_ERROR; +} + +CHIP_ERROR GetAccessPointInfo(wfx_wifi_scan_result_t & info) +{ + uint32_t signal_strength = 0; + + // TODO: The ap_info.ssid isn't populated anywhere. The returned value is always 0. + chip::ByteSpan apSsidSpan(ap_info.ssid, ap_info.ssid_length); + chip::MutableByteSpan apSsidMutableSpan(info.ssid, WFX_MAX_SSID_LENGTH); + chip::CopySpanToMutableSpan(apSsidSpan, apSsidMutableSpan); + info.ssid_length = apSsidMutableSpan.size(); + + // TODO: The ap_info.bssid isn't populated anywhere. The returned value is always 0. + chip::ByteSpan apBssidSpan(ap_info.bssid, kWifiMacAddressLength); + chip::MutableByteSpan apBssidMutableSpan(info.bssid, kWifiMacAddressLength); + chip::CopySpanToMutableSpan(apBssidSpan, apBssidMutableSpan); + + info.security = ap_info.security; + info.chan = ap_info.chan; + + sl_status_t status = sl_wfx_get_signal_strength(&signal_strength); + VerifyOrReturnError(status == SL_STATUS_OK, CHIP_ERROR_INTERNAL); + + info.rssi = (signal_strength - 220) / 2; + + ChipLogDetail(DeviceLayer, "WIFI:SSID : %s", ap_info.ssid); + ChipLogDetail(DeviceLayer, "WIFI:BSSID : %02x:%02x:%02x:%02x:%02x:%02x", ap_info.bssid[0], ap_info.bssid[1], + ap_info.bssid[2], ap_info.bssid[3], ap_info.bssid[4], ap_info.bssid[5]); + ChipLogDetail(DeviceLayer, "WIFI:security : %d", info.security); + ChipLogDetail(DeviceLayer, "WIFI:channel : %d", info.chan); + ChipLogDetail(DeviceLayer, "signal_strength: %ld", signal_strength); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR GetAccessPointExtendedInfo(wfx_wifi_scan_ext_t & info) +{ + sl_status_t status = get_all_counters(); + VerifyOrReturnError(status == SL_STATUS_OK, CHIP_ERROR_INTERNAL, ChipLogError(DeviceLayer, "Failed to get the couters")); + + info.beacon_lost_count = counters->body.count_miss_beacon; + info.beacon_rx_count = counters->body.count_rx_beacon; + info.mcast_rx_count = counters->body.count_rx_multicast_frames; + info.mcast_tx_count = counters->body.count_tx_multicast_frames; + info.ucast_rx_count = counters->body.count_rx_packets; + info.ucast_tx_count = counters->body.count_tx_packets; + info.overrun_count = gOverrunCount; + + return CHIP_NO_ERROR; +} + /*************************************************************************** * @brief * Creates WFX events processing task. @@ -588,8 +653,8 @@ static void sl_wfx_connect_callback(sl_wfx_connect_ind_body_t connect_indication case WFM_STATUS_SUCCESS: { ChipLogProgress(DeviceLayer, "STA-Connected"); memcpy(ap_mac.data(), mac, kWifiMacAddressLength); - sl_wfx_context->state = - static_cast(static_cast(sl_wfx_context->state) | static_cast(SL_WFX_STA_INTERFACE_CONNECTED)); + + wifi_extra.Set(WifiState::kStationConnected); xEventGroupSetBits(sl_wfx_event_group, SL_WFX_CONNECT); break; } @@ -623,8 +688,7 @@ static void sl_wfx_connect_callback(sl_wfx_connect_ind_body_t connect_indication retryJoin += 1; retryInProgress = false; ChipLogProgress(DeviceLayer, "Retry to connect to network count: %d", retryJoin); - sl_wfx_context->state = - static_cast(static_cast(sl_wfx_context->state) & ~static_cast(SL_WFX_STARTED)); + wfx_retry_connection(retryJoin); } } @@ -639,8 +703,8 @@ static void sl_wfx_disconnect_callback(uint8_t * mac, uint16_t reason) { (void) (mac); ChipLogProgress(DeviceLayer, "Disconnected %d", reason); - sl_wfx_context->state = - static_cast(static_cast(sl_wfx_context->state) & ~static_cast(SL_WFX_STA_INTERFACE_CONNECTED)); + wifi_extra.Clear(WifiState::kStationConnected); + retryInProgress = false; wfx_retry_connection(retryJoin); } @@ -654,8 +718,8 @@ static void sl_wfx_disconnect_callback(uint8_t * mac, uint16_t reason) static void sl_wfx_start_ap_callback(uint32_t status) { VerifyOrReturnLogError(status == AP_START_SUCCESS, CHIP_ERROR_INTERNAL); - sl_wfx_context->state = - static_cast(static_cast(sl_wfx_context->state) | static_cast(SL_WFX_AP_INTERFACE_UP)); + wifi_extra.Set(WifiState::kAPReady); + xEventGroupSetBits(sl_wfx_event_group, SL_WFX_START_AP); } @@ -668,8 +732,8 @@ static void sl_wfx_stop_ap_callback(void) // TODO // dhcpserver_clear_stored_mac(); ChipLogProgress(DeviceLayer, "SoftAP stopped"); - sl_wfx_context->state = - static_cast(static_cast(sl_wfx_context->state) & ~static_cast(SL_WFX_AP_INTERFACE_UP)); + wifi_extra.Clear(WifiState::kAPReady); + xEventGroupSetBits(sl_wfx_event_group, SL_WFX_STOP_AP); } @@ -754,10 +818,10 @@ static void wfx_events_task(void * p_arg) #if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) uint8_t dhcp_state = dhcpclient_poll(sta_netif); - if ((dhcp_state == DHCP_ADDRESS_ASSIGNED) && !hasNotifiedIPV4) + if ((dhcp_state == DHCP_ADDRESS_ASSIGNED) && !HasNotifiedIPv4Change()) { wfx_dhcp_got_ipv4((uint32_t) sta_netif->ip_addr.u_addr.ip4.addr); - hasNotifiedIPV4 = true; + NotifyIPv4Change(true); if (!hasNotifiedWifiConnectivity) { ChipLogProgress(DeviceLayer, "will notify WiFi connectivity"); @@ -768,13 +832,11 @@ static void wfx_events_task(void * p_arg) else if (dhcp_state == DHCP_OFF) { NotifyIPv4Change(false); - hasNotifiedIPV4 = false; } #endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 - if ((ip6_addr_ispreferred(netif_ip6_addr_state(sta_netif, 0))) && !hasNotifiedIPV6) + if ((ip6_addr_ispreferred(netif_ip6_addr_state(sta_netif, 0))) && !HasNotifiedIPv6Change()) { NotifyIPv6Change(true); - hasNotifiedIPV6 = true; if (!hasNotifiedWifiConnectivity) { NotifyConnection(ap_mac); @@ -789,17 +851,15 @@ static void wfx_events_task(void * p_arg) { #if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) NotifyIPv4Change(false); - hasNotifiedIPV4 = false; #endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 NotifyIPv6Change(false); - hasNotifiedIPV6 = false; hasNotifiedWifiConnectivity = false; ChipLogProgress(DeviceLayer, "connected to AP"); wifi_extra.Set(WifiState::kStationConnected); retryJoin = 0; wfx_lwip_set_sta_link_up(); #if CHIP_CONFIG_ENABLE_ICD_SERVER - if (!(wifiContext.state & SL_WFX_AP_INTERFACE_UP)) + if (!(wifi_extra.Has(WifiState::kAPReady))) { // Enable the power save ChipLogProgress(DeviceLayer, "WF200 going to DTIM based sleep"); @@ -814,10 +874,8 @@ static void wfx_events_task(void * p_arg) #if (CHIP_DEVICE_CONFIG_ENABLE_IPV4) NotifyIPv4Change(false); - hasNotifiedIPV4 = false; #endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 NotifyIPv6Change(false); - hasNotifiedIPV6 = false; hasNotifiedWifiConnectivity = false; wifi_extra.Clear(WifiState::kStationConnected); wfx_lwip_set_sta_link_down(); @@ -905,7 +963,7 @@ static sl_status_t wfx_init(void) wifiContext.mac_addr_0.octet[4], wifiContext.mac_addr_0.octet[5]); ChipLogProgress(DeviceLayer, "WF200 Init OK"); - if (wifiContext.state == SL_WFX_STA_INTERFACE_CONNECTED) + if (wifi_extra.Has(WifiState::kStationConnected)) { sl_wfx_send_disconnect_command(); } @@ -956,7 +1014,6 @@ static sl_status_t wfx_wifi_hw_start(void) ChipLogDetail(DeviceLayer, "WF200:Start LWIP"); sl_matter_lwip_start(); sl_matter_wifi_task_started(); - wifiContext.state = SL_WFX_STARTED; /* Really this is a bit mask */ ChipLogDetail(DeviceLayer, "WF200:ready."); wifi_extra.Set(WifiState::kStationStarted); @@ -964,100 +1021,10 @@ static sl_status_t wfx_wifi_hw_start(void) return SL_STATUS_OK; } -/*********************************************************************** - * @brief - * Get AP info - * @param[in] ap: access point information - * @return returns -1 - **************************************************************************/ -int32_t wfx_get_ap_info(wfx_wifi_scan_result_t * ap) -{ - uint32_t signal_strength = 0; - - chip::ByteSpan apSsidSpan(ap_info.ssid, ap_info.ssid_length); - chip::MutableByteSpan apSsidMutableSpan(ap->ssid, WFX_MAX_SSID_LENGTH); - chip::CopySpanToMutableSpan(apSsidSpan, apSsidMutableSpan); - ap->ssid_length = apSsidMutableSpan.size(); - - chip::ByteSpan apBssidSpan(ap_info.bssid, kWifiMacAddressLength); - chip::MutableByteSpan apBssidMutableSpan(ap->bssid, kWifiMacAddressLength); - chip::CopySpanToMutableSpan(apBssidSpan, apBssidMutableSpan); - - ap->security = ap_info.security; - ap->chan = ap_info.chan; - - sl_status_t status = sl_wfx_get_signal_strength(&signal_strength); - VerifyOrReturnError(status == SL_STATUS_OK, status); - ap->rssi = (signal_strength - 220) / 2; - - ChipLogDetail(DeviceLayer, "WIFI:SSID : %s", ap_info.ssid); - ChipLogDetail(DeviceLayer, "WIFI:BSSID : %02x:%02x:%02x:%02x:%02x:%02x", ap_info.bssid[0], ap_info.bssid[1], - ap_info.bssid[2], ap_info.bssid[3], ap_info.bssid[4], ap_info.bssid[5]); - ChipLogDetail(DeviceLayer, "WIFI:security : %d", ap->security); - ChipLogDetail(DeviceLayer, "WIFI:channel : %d", ap->chan); - ChipLogDetail(DeviceLayer, "signal_strength: %ld", signal_strength); - - return status; -} - -/************************************************************************ - * @brief - * Get AP extra info - * @param[in] extra_info: access point extra information - * @return returns -1 - **************************************************************************/ -int32_t wfx_get_ap_ext(wfx_wifi_scan_ext_t * extra_info) +CHIP_ERROR ResetCounters() { - int32_t status; - status = get_all_counters(); - if (status != SL_STATUS_OK) - { - ChipLogError(DeviceLayer, "Failed to get the couters"); - } - else - { - extra_info->beacon_lost_count = counters->body.count_miss_beacon; - extra_info->beacon_rx_count = counters->body.count_rx_beacon; - extra_info->mcast_rx_count = counters->body.count_rx_multicast_frames; - extra_info->mcast_tx_count = counters->body.count_tx_multicast_frames; - extra_info->ucast_rx_count = counters->body.count_rx_packets; - extra_info->ucast_tx_count = counters->body.count_tx_packets; - extra_info->overrun_count = gOverrunCount; - } - return status; -} - -/************************************************************************ - * @brief - * reset the count - * @return returns -1 - **************************************************************************/ -int32_t wfx_reset_counts(void) -{ - /* TODO */ - return -1; -} - -/**************************************************************************** - * @brief - * getnetif using interface - * @param[in] interface: - * @return returns selectedNetif - *****************************************************************************/ -struct netif * wfx_GetNetif(sl_wfx_interface_t interface) -{ - struct netif * SelectedNetif = nullptr; - if (interface == SL_WFX_STA_INTERFACE) - { - SelectedNetif = sta_netif; - } -#ifdef SL_WFX_CONFIG_SOFTAP - else if (interface == SL_WFX_SOFTAP_INTERFACE) - { - // no ap currently - } -#endif - return SelectedNetif; + // TODO: Implement the function + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; } /**************************************************************************** @@ -1169,56 +1136,6 @@ bool wfx_have_ipv6_addr(sl_wfx_interface_t which_if) return IsStationConnected(); } -/**************************************************************************** - * @brief - * Disconnect station mode from connected AP - * @returns Returns SL_STATUS_OK if successful, - * SL_STATUS_FAIL otherwise - *****************************************************************************/ -sl_status_t sl_matter_wifi_disconnect(void) -{ - ChipLogProgress(DeviceLayer, "STA-Disconnecting"); - - int32_t status = sl_wfx_send_disconnect_command(); - wifi_extra.Clear(WifiState::kStationConnected); - - xEventGroupSetBits(sl_wfx_event_group, SL_WFX_RETRY_CONNECT); - return status; -} - -/**************************************************************************** - * @brief - * It is automatically done when lwip link up - * @return returns true if sucessful, - * false otherwise - *****************************************************************************/ -void wfx_setup_ip6_link_local(sl_wfx_interface_t whichif) {} - -/**************************************************************************** - * @brief - * get the wifi mode - * @return returns WIFI_MODE_NULL if sucessful, - * WIFI_MODE_STA otherwise - *****************************************************************************/ -wifi_mode_t wfx_get_wifi_mode(void) -{ - if (wifiContext.state & SL_WFX_STARTED) - return WIFI_MODE_STA; - return WIFI_MODE_NULL; -} - -/***************************************************************************** - * @brief - * This is called from the context of AppTask - * For WF200 - Start WIFI here - * @return returns true if sucessful, - * false otherwise - ******************************************************************************/ -bool wfx_hw_ready(void) -{ - return (wifiContext.state & SL_WFX_STARTED) ? true : false; -} - #if CHIP_DEVICE_CONFIG_ENABLE_IPV4 /***************************************************************************** * @brief diff --git a/src/platform/silabs/wifi/wiseconnect-interface/WiseconnectWifiInterface.cpp b/src/platform/silabs/wifi/wiseconnect-interface/WiseconnectWifiInterface.cpp index 111e98100f1..0b188740882 100644 --- a/src/platform/silabs/wifi/wiseconnect-interface/WiseconnectWifiInterface.cpp +++ b/src/platform/silabs/wifi/wiseconnect-interface/WiseconnectWifiInterface.cpp @@ -14,9 +14,11 @@ * limitations under the License. */ +#include #include #include #include +#include extern WfxRsi_t wfx_rsi; @@ -35,6 +37,9 @@ constexpr osThreadAttr_t kWlanTaskAttr = { .name = "wlan_rsi", .stack_size = kWlanTaskSize, .priority = osPriorityAboveNormal7 }; +osTimerId_t sDHCPTimer; +bool hasNotifiedWifiConnectivity = false; + } // namespace CHIP_ERROR GetMacAddress(sl_wfx_interface_t interface, chip::MutableByteSpan & address) @@ -76,7 +81,7 @@ CHIP_ERROR StartNetworkScan(chip::ByteSpan ssid, ScanCallback callback) // TODO: We should be calling the start function directly instead of doing it asynchronously WifiPlatformEvent event = WifiPlatformEvent::kScan; - sl_matter_wifi_post_event(event); + PostWifiPlatformEvent(event); return CHIP_NO_ERROR; } @@ -88,7 +93,7 @@ CHIP_ERROR StartWifiTask() wfx_rsi.dev_state.Set(WifiState::kStationStarted); // Creating a Wi-Fi task thread - sWlanThread = osThreadNew(sl_matter_wifi_task, NULL, &kWlanTaskAttr); + sWlanThread = osThreadNew(MatterWifiTask, NULL, &kWlanTaskAttr); VerifyOrReturnError(sWlanThread != NULL, CHIP_ERROR_NO_MEMORY, ChipLogError(DeviceLayer, "Unable to create the WifiTask.");); return CHIP_NO_ERROR; @@ -109,6 +114,73 @@ bool IsStationConnected() return wfx_rsi.dev_state.Has(WifiState::kStationConnected); } +bool IsStationReady() +{ + return wfx_rsi.dev_state.Has(WifiState::kStationInit); +} + +CHIP_ERROR TriggerDisconnection() +{ + VerifyOrReturnError(TriggerPlatformWifiDisconnection() == SL_STATUS_OK, CHIP_ERROR_INTERNAL); + wfx_rsi.dev_state.Clear(WifiState::kStationConnected); + + return CHIP_NO_ERROR; +} + +void DHCPTimerEventHandler(void * arg) +{ + WifiPlatformEvent event = WifiPlatformEvent::kStationDhcpPoll; + PostWifiPlatformEvent(event); +} + +void CancelDHCPTimer(void) +{ + VerifyOrReturn(osTimerIsRunning(sDHCPTimer), ChipLogDetail(DeviceLayer, "CancelDHCPTimer: timer not running")); + VerifyOrReturn(osTimerStop(sDHCPTimer) == osOK, ChipLogError(DeviceLayer, "CancelDHCPTimer: failed to stop timer")); +} + +void StartDHCPTimer(uint32_t timeout) +{ + // Cancel timer if already started + CancelDHCPTimer(); + + VerifyOrReturn(osTimerStart(sDHCPTimer, pdMS_TO_TICKS(timeout)) == osOK, + ChipLogError(DeviceLayer, "StartDHCPTimer: failed to start timer")); +} + +void NotifyConnectivity(void) +{ + VerifyOrReturn(!hasNotifiedWifiConnectivity); + + NotifyConnection(wfx_rsi.ap_mac); + hasNotifiedWifiConnectivity = true; +} + +sl_status_t CreateDHCPTimer() +{ + // TODO: Use LWIP timer instead of creating a new one here + sDHCPTimer = osTimerNew(DHCPTimerEventHandler, osTimerPeriodic, nullptr, nullptr); + VerifyOrReturnError(sDHCPTimer != nullptr, SL_STATUS_ALLOCATION_FAILED); + + return SL_STATUS_OK; +} + +/** + * @brief Reset the flags that are used to notify the application about DHCP connectivity + * and emits a WifiPlatformEvent::kStationDoDhcp event to trigger DHCP polling checks. + * + * TODO: This function should be moved to the protected section once the class structure is done. + */ +void ResetDHCPNotificationFlags(void) +{ + + ResetIPNotificationStates(); + hasNotifiedWifiConnectivity = false; + + WifiPlatformEvent event = WifiPlatformEvent::kStationDoDhcp; + PostWifiPlatformEvent(event); +} + /********************************************************************* * @fn void wfx_set_wifi_provision(wfx_wifi_provision_t *cfg) * @brief @@ -169,55 +241,10 @@ sl_status_t wfx_connect_to_ap(void) ChipLogProgress(DeviceLayer, "connect to access point: %s", wfx_rsi.sec.ssid); WifiPlatformEvent event = WifiPlatformEvent::kStationStartJoin; - sl_matter_wifi_post_event(event); + PostWifiPlatformEvent(event); return SL_STATUS_OK; } -/********************************************************************* - * @fn void wfx_setup_ip6_link_local(sl_wfx_interface_t whichif) - * @brief - * Implement the ipv6 setup - * @param[in] whichif: - * @return None - ***********************************************************************/ -void wfx_setup_ip6_link_local(sl_wfx_interface_t whichif) -{ - /* - * TODO: Implement IPV6 setup, currently in sl_matter_wifi_task() - * This is hooked with MATTER code. - */ -} - -/********************************************************************* - * @fn wifi_mode_t wfx_get_wifi_mode(void) - * @brief - * get the wifi mode - * @param[in] None - * @return return WIFI_MODE_NULL if successful, - * WIFI_MODE_STA otherwise - ***********************************************************************/ -wifi_mode_t wfx_get_wifi_mode(void) -{ - if (wfx_rsi.dev_state.Has(WifiState::kStationInit)) - return WIFI_MODE_STA; - return WIFI_MODE_NULL; -} - -/********************************************************************* - * @fn sl_status_t sl_matter_wifi_disconnect(void) - * @brief - * called fuction when STA disconnected - * @param[in] None - * @return return SL_STATUS_OK if successful, - * SL_STATUS_FAIL otherwise - ***********************************************************************/ -sl_status_t sl_matter_wifi_disconnect(void) -{ - sl_status_t status; - status = sl_wifi_platform_disconnect(); - wfx_rsi.dev_state.Clear(WifiState::kStationConnected); - return status; -} #if CHIP_DEVICE_CONFIG_ENABLE_IPV4 /********************************************************************* * @fn bool wfx_have_ipv4_addr(sl_wfx_interface_t which_if) @@ -249,58 +276,6 @@ bool wfx_have_ipv6_addr(sl_wfx_interface_t which_if) return wfx_rsi.dev_state.Has(WifiState::kStationConnected); } -/********************************************************************* - * @fn bool wfx_hw_ready(void) - * @brief - * called fuction when driver ready - * @param[in] None - * @return returns ture if successful, - * false otherwise - ***********************************************************************/ -bool wfx_hw_ready(void) -{ - return wfx_rsi.dev_state.Has(WifiState::kStationInit); -} - -/********************************************************************* - * @fn int32_t wfx_get_ap_info(wfx_wifi_scan_result_t *ap) - * @brief - * get the access point information - * @param[in] ap: access point - * @return - * access point information - ***********************************************************************/ -int32_t wfx_get_ap_info(wfx_wifi_scan_result_t * ap) -{ - return wfx_rsi_get_ap_info(ap); -} - -/********************************************************************* - * @fn int32_t wfx_get_ap_ext(wfx_wifi_scan_ext_t *extra_info) - * @brief - * get the access point extra information - * @param[in] extra_info:access point extra information - * @return - * access point extra information - ***********************************************************************/ -int32_t wfx_get_ap_ext(wfx_wifi_scan_ext_t * extra_info) -{ - return wfx_rsi_get_ap_ext(extra_info); -} - -/*************************************************************************** - * @fn int32_t wfx_reset_counts(void) - * @brief - * get the driver reset count - * @param[in] None - * @return - * reset count - *****************************************************************************/ -int32_t wfx_reset_counts(void) -{ - return wfx_rsi_reset_count(); -} - /*************************************************************************** * @fn void wfx_cancel_scan(void) * @brief diff --git a/src/platform/silabs/wifi/wiseconnect-interface/WiseconnectWifiInterface.h b/src/platform/silabs/wifi/wiseconnect-interface/WiseconnectWifiInterface.h index 8acede58149..1db5d145fef 100644 --- a/src/platform/silabs/wifi/wiseconnect-interface/WiseconnectWifiInterface.h +++ b/src/platform/silabs/wifi/wiseconnect-interface/WiseconnectWifiInterface.h @@ -15,11 +15,8 @@ */ #pragma once -#include -#include -#include #include -#include +#include #define WFX_RSI_DHCP_POLL_INTERVAL (250) /* Poll interval in ms for DHCP */ @@ -36,18 +33,94 @@ enum class WifiPlatformEvent : uint8_t kStationDhcpPoll = 8, }; -void sl_matter_wifi_task(void * arg); +/** + * @brief Function calls the underlying platforms disconnection API. + * + * @note This abstraction layer here is used to reduce the duplication for wiseconnect platforms. + * Since the only difference is the disconnection API, the common implementation is in the WiseconnectWifiInterface + * which calls this abstraction function that is implemented by the different platforms. + * + * @return sl_status_t SL_STATUS_OK, the Wi-Fi disconnection was succesfully triggered + * SL_STATUS_FAILURE, otherwise + */ +sl_status_t TriggerPlatformWifiDisconnection(); + +/** + * @brief Callback function for the DHCP timer event. + * + * TODO: Once the class structure is done, move this to the protected section. Should not be public. + */ +void DHCPTimerEventHandler(void * arg); + +/** + * @brief Function cancels the DHCP timer if it is running. + * If the timer isn't running, function doesn't do anything. + * + * TODO: Once the class structure is done, move this to the protected section. Should not be public. + */ +void CancelDHCPTimer(void); + +/** + * @brief Function starts the DHCP timer with the given timeout. + * + * TODO: Once the class structure is done, move this to the protected section. Should not be public. + * + * @param timeout timer duration in milliseconds + */ +void StartDHCPTimer(uint32_t timeout); + +/** + * @brief Reset the flags that are used to notify the application about DHCP connectivity + * and emits a WifiPlatformEvent::kStationDoDhcp event to trigger DHCP polling checks. + * + * TODO: This function should be moved to the protected section once the class structure is done. + */ +void ResetDHCPNotificationFlags(); -int32_t wfx_rsi_get_ap_info(wfx_wifi_scan_result_t * ap); -int32_t wfx_rsi_get_ap_ext(wfx_wifi_scan_ext_t * extra_info); -int32_t wfx_rsi_reset_count(); -int32_t sl_wifi_platform_disconnect(); +/** + * @brief Function creates the DHCP timer + * + * @note This function is necessary for the time being since the WifiInterface don't leverage inheritance for the time being and as + * such don't have access to all data structures. Once the class structure is done, this function will not be necessary + * anymore. + * + * @return sl_status_t SL_STATUS_OK, the timer was successfully created + */ +sl_status_t CreateDHCPTimer(); -sl_status_t sl_matter_wifi_platform_init(void); +/** + * @brief Notify the application about the connectivity status if it has not been notified yet. + * + * TODO: This function should be moved to the protected section once the class structure is done. + */ +void NotifyConnectivity(void); /** * @brief Posts an event to the Wi-Fi task * + * TODO: Once the class structure is in place, the function implementation can be in the protected section of this class instead of + * implemented twice. + * * @param[in] event Event to process. */ -void sl_matter_wifi_post_event(WifiPlatformEvent event); +void PostWifiPlatformEvent(WifiPlatformEvent event); + +/** + * @brief Main worker function for the Matter Wi-Fi task responsible of processing Wi-Fi platform events. + * Function is used in the StartWifiTask. + * + * @param[in] arg context pointer + */ +void MatterWifiTask(void * arg); + +/** + * @brief Function initializes SiWx Wi-Fi interface + * + * TODO: This function is specific to the SiWx platform and should not be present in the WiseconnectWifiInterface. + * Once the SoC and NCP init sequence are harmonised, remove this API. + * + * @return sl_status_t SL_STATUS_OK, if the initialization succeeded + * SL_STATUS_ALLOCATION_FAILED, if there are a memory allocation failure, + * SL_STATUS_FAILURE, otherwise + */ +sl_status_t InitSiWxWifi(); diff --git a/src/platform/stm32/BUILD.gn b/src/platform/stm32/BUILD.gn index 061a2c4c268..a77193e7792 100644 --- a/src/platform/stm32/BUILD.gn +++ b/src/platform/stm32/BUILD.gn @@ -95,7 +95,10 @@ static_library("stm32") { import("${stm32_sdk_build_root}/stm32_sdk.gni") public_deps += [ "${stm32_sdk_build_root}:stm32_sdk" ] - deps += [ "${chip_root}/src/lib/dnssd:platform_header" ] + deps += [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/lib/dnssd:platform_header", + ] } # Set the compiler flags diff --git a/src/platform/telink/BUILD.gn b/src/platform/telink/BUILD.gn index a41c5e4e279..f5fb715a50d 100644 --- a/src/platform/telink/BUILD.gn +++ b/src/platform/telink/BUILD.gn @@ -56,7 +56,10 @@ static_library("telink") { ] public_deps = [ "${chip_root}/src/platform:platform_base" ] - deps = [ "${chip_root}/src/platform/logging:headers" ] + deps = [ + "${chip_root}/src/app:app_config", + "${chip_root}/src/platform/logging:headers", + ] if (chip_enable_factory_data) { sources += [ @@ -80,6 +83,7 @@ static_library("telink") { "ThreadStackManagerImpl.cpp", "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] if (chip_mdns_platform) { sources += [ diff --git a/src/platform/webos/BUILD.gn b/src/platform/webos/BUILD.gn index 503bd216c4e..3d50c7c6e5a 100644 --- a/src/platform/webos/BUILD.gn +++ b/src/platform/webos/BUILD.gn @@ -100,6 +100,7 @@ static_library("webos") { ] deps = [ + "${chip_root}/src/app:app_config", "${chip_root}/src/platform/logging:headers", "${chip_root}/src/setup_payload", ] @@ -133,6 +134,7 @@ static_library("webos") { "ThreadStackManagerImpl.h", ] + deps += [ "${chip_root}/src/app:app_config" ] public_deps += [ "dbus/openthread" ] } diff --git a/src/python_testing/TC_CADMIN_1_19.py b/src/python_testing/TC_CADMIN_1_19.py index a7a97aa395b..0faea18ef36 100644 --- a/src/python_testing/TC_CADMIN_1_19.py +++ b/src/python_testing/TC_CADMIN_1_19.py @@ -137,8 +137,8 @@ async def test_TC_CADMIN_1_19(self): fids_fa_dir[next_fabric] = fids_ca_dir[current_fabrics + 1].NewFabricAdmin(vendorId=0xFFF1, fabricId=next_fabric) try: - fids[next_fabric] = fids_fa_dir[next_fabric].NewController(nodeId=next_fabric) - await fids[next_fabric].CommissionOnNetwork( + next_fabric_controller = fids_fa_dir[next_fabric].NewController(nodeId=next_fabric) + await next_fabric_controller.CommissionOnNetwork( nodeId=self.dut_node_id, setupPinCode=params.commissioningParameters.setupPinCode, filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=params.randomDiscriminator) diff --git a/src/python_testing/TC_DGSW_2_1.py b/src/python_testing/TC_DGSW_2_1.py index 595a1115a24..86022bef0fd 100644 --- a/src/python_testing/TC_DGSW_2_1.py +++ b/src/python_testing/TC_DGSW_2_1.py @@ -36,36 +36,12 @@ # import chip.clusters as Clusters +from chip.testing import matter_asserts from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main -from mobly import asserts class TC_DGSW_2_1(MatterBaseTest): - @staticmethod - def is_valid_uint64_value(value): - return isinstance(value, int) and 0 <= value <= 0xFFFFFFFFFFFFFFFF - - @staticmethod - def is_valid_uint32_value(value): - return isinstance(value, int) and 0 <= value <= 0xFFFFFFFF - - @staticmethod - def is_valid_str_value(value): - return isinstance(value, str) and len(value) > 0 - - def assert_valid_uint64(self, value, field_name): - """Asserts that the value is a valid uint64.""" - asserts.assert_true(self.is_valid_uint64_value(value), f"{field_name} field should be a uint64 type") - - def assert_valid_uint32(self, value, field_name): - """Asserts that the value is a valid uint32.""" - asserts.assert_true(self.is_valid_uint32_value(value), f"{field_name} field should be a uint32 type") - - def assert_valid_str(self, value, field_name): - """Asserts that the value is a non-empty string.""" - asserts.assert_true(self.is_valid_str_value(value), f"{field_name} field should be a non-empty string") - async def read_dgsw_attribute_expect_success(self, endpoint, attribute): cluster = Clusters.Objects.SoftwareDiagnostics return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) @@ -106,41 +82,41 @@ async def test_TC_DGSW_2_1(self): # Validate each element in the thread_metrics_list for metric in thread_metrics_list: # The Id field is mandatory - self.assert_valid_uint64(metric.id, "Id") + matter_asserts.assert_valid_uint64(metric.id, "Id") # Validate the optional Name field if metric.name is not None: - self.assert_valid_str(metric.name, "Name") + matter_asserts.assert_is_string(metric.name, "Name") # Validate the optional StackFreeCurrent field if metric.stackFreeCurrent is not None: - self.assert_valid_uint32(metric.stackFreeCurrent, "StackFreeCurrent") + matter_asserts.assert_valid_uint32(metric.stackFreeCurrent, "StackFreeCurrent") # Validate the optional StackFreeMinimum field if metric.stackFreeMinimum is not None: - self.assert_valid_uint32(metric.stackFreeMinimum, "StackFreeMinimum") + matter_asserts.assert_valid_uint32(metric.stackFreeMinimum, "StackFreeMinimum") # Validate the optional StackSize field if metric.stackSize is not None: - self.assert_valid_uint32(metric.stackSize, "StackSize") + matter_asserts.assert_valid_uint32(metric.stackSize, "StackSize") # STEP 3: TH reads from the DUT the CurrentHeapFree attribute self.step(3) if self.pics_guard(attributes.CurrentHeapFree.attribute_id in attribute_list): current_heap_free_attr = await self.read_dgsw_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentHeapFree) - self.assert_valid_uint64(current_heap_free_attr, "CurrentHeapFree") + matter_asserts.assert_valid_uint64(current_heap_free_attr, "CurrentHeapFree") # STEP 4: TH reads from the DUT the CurrentHeapUsed attribute self.step(4) if self.pics_guard(attributes.CurrentHeapUsed.attribute_id in attribute_list): current_heap_used_attr = await self.read_dgsw_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentHeapUsed) - self.assert_valid_uint64(current_heap_used_attr, "CurrentHeapUsed") + matter_asserts.assert_valid_uint64(current_heap_used_attr, "CurrentHeapUsed") # STEP 5: TH reads from the DUT the CurrentHeapHighWatermark attribute self.step(5) if self.pics_guard(attributes.CurrentHeapHighWatermark.attribute_id in attribute_list): current_heap_high_watermark_attr = await self.read_dgsw_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentHeapHighWatermark) - self.assert_valid_uint64(current_heap_high_watermark_attr, "CurrentHeapHighWatermark") + matter_asserts.assert_valid_uint64(current_heap_high_watermark_attr, "CurrentHeapHighWatermark") if __name__ == "__main__": diff --git a/src/python_testing/TC_DGSW_2_2.py b/src/python_testing/TC_DGSW_2_2.py index 21d451abfa3..1d93c9da8ab 100644 --- a/src/python_testing/TC_DGSW_2_2.py +++ b/src/python_testing/TC_DGSW_2_2.py @@ -42,16 +42,12 @@ # import chip.clusters as Clusters +from chip.testing import matter_asserts from chip.testing.matter_testing import EventChangeCallback, MatterBaseTest, TestStep, async_test_body, default_matter_test_main -from mobly import asserts class TC_DGSW_2_2(MatterBaseTest): - @staticmethod - def is_valid_uint64_value(value): - return isinstance(value, int) and 0 <= value <= 0xFFFFFFFFFFFFFFFF - @staticmethod def is_valid_octet_string(value): return isinstance(value, (bytes, bytearray)) @@ -70,22 +66,13 @@ def validate_soft_fault_event_data(self, event_data): """ # Validate 'Id' field: Ensure it is a uint64 type - asserts.assert_true( - self.is_valid_uint64_value(event_data.id), - "The 'Id' field must be a uint64 type" - ) + matter_asserts.assert_valid_uint64(event_data.id, "Id") # Validate 'Name' field: Ensure it is a string - asserts.assert_true( - isinstance(event_data.name, str), - "The 'Name' field must be a string type" - ) + matter_asserts.assert_is_string(event_data.name, "Name") # Validate 'FaultRecording' field: Ensure it is an octet string (bytes or bytearray) - asserts.assert_true( - self.is_valid_octet_string(event_data.faultRecording), - "The 'FaultRecording' field must be an octet string (bytes or bytearray)" - ) + matter_asserts.assert_is_octstr(event_data.faultRecording, "FaultRecording") def desc_TC_DGSW_2_2(self) -> str: """Returns a description of this test""" diff --git a/src/python_testing/TC_DGSW_2_3.py b/src/python_testing/TC_DGSW_2_3.py index 0cba7d7dcf7..1daec5e2db6 100644 --- a/src/python_testing/TC_DGSW_2_3.py +++ b/src/python_testing/TC_DGSW_2_3.py @@ -38,36 +38,13 @@ import logging import chip.clusters as Clusters +from chip.testing import matter_asserts from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main from mobly import asserts class TC_DGSW_2_3(MatterBaseTest): - @staticmethod - def is_valid_uint64_value(value): - return isinstance(value, int) and 0 <= value <= 0xFFFFFFFFFFFFFFFF - - @staticmethod - def is_valid_uint32_value(value): - return isinstance(value, int) and 0 <= value <= 0xFFFFFFFF - - @staticmethod - def is_valid_str_value(value): - return isinstance(value, str) and len(value) > 0 - - def assert_valid_uint64(self, value, field_name): - """Asserts that the value is a valid uint64.""" - asserts.assert_true(self.is_valid_uint64_value(value), f"{field_name} field should be a uint64 type") - - def assert_valid_uint32(self, value, field_name): - """Asserts that the value is a valid uint32.""" - asserts.assert_true(self.is_valid_uint32_value(value), f"{field_name} field should be a uint32 type") - - def assert_valid_str(self, value, field_name): - """Asserts that the value is a non-empty string.""" - asserts.assert_true(self.is_valid_str_value(value), f"{field_name} field should be a non-empty string") - async def read_dgsw_attribute_expect_success(self, endpoint, attribute): cluster = Clusters.Objects.SoftwareDiagnostics return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) @@ -118,31 +95,31 @@ async def test_TC_DGSW_2_3(self): # Iterate over all items in the list and validate each one for metric in thread_metrics_original: # The Id field is mandatory - self.assert_valid_uint64(metric.id, "Id") + matter_asserts.assert_valid_uint64(metric.id, "Id") if metric.name is not None: - self.assert_valid_str(metric.name, "Name") + matter_asserts.assert_is_string(metric.name, "Name") if metric.stackFreeCurrent is not None: - self.assert_valid_uint32(metric.stackFreeCurrent, "StackFreeCurrent") + matter_asserts.assert_valid_uint32(metric.stackFreeCurrent, "StackFreeCurrent") if metric.stackFreeMinimum is not None: - self.assert_valid_uint32(metric.stackFreeMinimum, "StackFreeMinimum") + matter_asserts.assert_valid_uint32(metric.stackFreeMinimum, "StackFreeMinimum") if metric.stackSize is not None: - self.assert_valid_uint32(metric.stackSize, "StackSize") + matter_asserts.assert_valid_uint32(metric.stackSize, "StackSize") # STEP 4: TH reads from the DUT the CurrentHeapHighWatermark attribute self.step(4) if self.pics_guard(attributes.CurrentHeapHighWatermark.attribute_id in attribute_list): high_watermark_original = await self.read_dgsw_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentHeapHighWatermark) - self.assert_valid_uint64(high_watermark_original, "CurrentHeapHighWatermark") + matter_asserts.assert_valid_uint64(high_watermark_original, "CurrentHeapHighWatermark") # STEP 5: TH reads from the DUT the CurrentHeapUsed attribute self.step(5) if self.pics_guard(attributes.CurrentHeapUsed.attribute_id in attribute_list): current_heap_used_original = await self.read_dgsw_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentHeapUsed) - self.assert_valid_uint64(current_heap_used_original, "CurrentHeapUsed") + matter_asserts.assert_valid_uint64(current_heap_used_original, "CurrentHeapUsed") if high_watermark_original is not None: asserts.assert_true(current_heap_used_original <= high_watermark_original, @@ -157,7 +134,7 @@ async def test_TC_DGSW_2_3(self): self.step(7) if self.pics_guard(attributes.CurrentHeapHighWatermark.attribute_id in attribute_list): current_heap_high_watermark = await self.read_dgsw_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentHeapHighWatermark) - self.assert_valid_uint64(current_heap_high_watermark, "CurrentHeapHighWatermark") + matter_asserts.assert_valid_uint64(current_heap_high_watermark, "CurrentHeapHighWatermark") # Verify that the returned value is <= high_watermark_original asserts.assert_true(current_heap_high_watermark <= high_watermark_original, @@ -176,19 +153,19 @@ async def test_TC_DGSW_2_3(self): # Validate all elements in the list for metric in thread_metrics_reset: - self.assert_valid_uint64(metric.id, "Id") + matter_asserts.assert_valid_uint64(metric.id, "Id") if metric.name is not None: - self.assert_valid_str(metric.name, "Name") + matter_asserts.assert_is_string(metric.name, "Name") if metric.stackFreeCurrent is not None: - self.assert_valid_uint32(metric.stackFreeCurrent, "StackFreeCurrent") + matter_asserts.assert_valid_uint32(metric.stackFreeCurrent, "StackFreeCurrent") if metric.stackFreeMinimum is not None: - self.assert_valid_uint32(metric.stackFreeMinimum, "StackFreeMinimum") + matter_asserts.assert_valid_uint32(metric.stackFreeMinimum, "StackFreeMinimum") if metric.stackSize is not None: - self.assert_valid_uint32(metric.stackSize, "StackSize") + matter_asserts.assert_valid_uint32(metric.stackSize, "StackSize") # Ensure the list length matches thread_metrics_original to simplify matching asserts.assert_equal(len(thread_metrics_reset), len(thread_metrics_original), diff --git a/src/python_testing/TC_DGWIFI_2_1.py b/src/python_testing/TC_DGWIFI_2_1.py index 4290ec9f385..e32f1487291 100644 --- a/src/python_testing/TC_DGWIFI_2_1.py +++ b/src/python_testing/TC_DGWIFI_2_1.py @@ -34,6 +34,7 @@ import chip.clusters as Clusters from chip.clusters.Types import Nullable, NullValue +from chip.testing import matter_asserts from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main from mobly import asserts @@ -63,48 +64,11 @@ def is_valid_bssid(bssid) -> bool: return False - @staticmethod - def is_valid_uint_value(value, bit_count=64): - """ - Checks if 'value' is a non-negative integer fitting into 'bit_count' bits. - For example, bit_count=32 => must fit within 0 <= value <= 0xFFFFFFFF - """ - if not isinstance(value, int): - return False - if value < 0: - return False - return value < 2**bit_count - def assert_valid_bssid(self, value, field_name): - """Asserts that the value is a valid BSSID (MAC address), None, or NullValue.""" - if isinstance(value, Nullable): - if value == NullValue: - return - value = value.Value - - if value is not None: + """Asserts that the value is a valid BSSID (MAC address), or NullValue.""" + if value is not NullValue: asserts.assert_true(self.is_valid_bssid(value), - f"{field_name} should be a valid BSSID string (e.g., '00:11:22:33:44:55') or None/NullValue.") - - def assert_valid_uint64(self, value, field_name): - """Asserts that the value is a valid uint64 or None (if attribute can return NULL).""" - asserts.assert_true(value is None or self.is_valid_uint_value(value, bit_count=64), - f"{field_name} should be a uint64 or NULL.") - - def assert_valid_uint32(self, value, field_name): - """Asserts that the value is a valid uint32 or None (if attribute can return NULL).""" - asserts.assert_true(value is None or self.is_valid_uint_value(value, bit_count=32), - f"{field_name} should be a uint32 or NULL.") - - def assert_valid_uint16(self, value, field_name): - """Asserts that the value is a valid uint16 or None (if attribute can return NULL).""" - asserts.assert_true(value is None or self.is_valid_uint_value(value, bit_count=16), - f"{field_name} should be a uint16 or NULL.") - - def assert_valid_uint8(self, value, field_name): - """Asserts that the value is a valid uint16 or None (if attribute can return NULL).""" - asserts.assert_true(value is None or self.is_valid_uint_value(value, bit_count=8), - f"{field_name} should be a uint8 or NULL.") + f"{field_name} should be a valid BSSID string (e.g., '00:11:22:33:44:55') or NullValue.") async def read_dgwifi_attribute_expect_success(self, endpoint, attribute): cluster = Clusters.Objects.WiFiNetworkDiagnostics @@ -166,26 +130,19 @@ async def test_TC_DGWIFI_2_1(self): # SecurityType is an enum. If the interface is not operational, it could be NULL. # If not NULL, we expect an integer in the SecurityType enum range. # Just do a minimal check here; you can refine or extend based on the spec. - if security_type is not None: - asserts.assert_true(isinstance(security_type, Nullable), - "SecurityType must be of type 'Nullable' when not None.") - - if security_type is not NullValue: - security_type_value = security_type.Value - self.assert_valid_uint8(security_type_value, "SecurityType") - - # Check if the security_type is a valid SecurityTypeEnum member - self.assert_true( - security_type_value in [item.value for item in Clusters.Objects.WiFiNetworkDiagnostics.Enums.SecurityTypeEnum], - f"SecurityType {security_type_value} is not a valid SecurityTypeEnum value" - ) - - # Additional check that it's not kUnknownEnumValue: - self.assert_true( - security_type_value != Clusters.Objects.WiFiNetworkDiagnostics.Enums.SecurityTypeEnum.kUnknownEnumValue.value, - f"SecurityType should not be kUnknownEnumValue " - f"({Clusters.Objects.WiFiNetworkDiagnostics.Enums.SecurityTypeEnum.kUnknownEnumValue.value})" - ) + if security_type is not NullValue: + matter_asserts.assert_valid_uint8(security_type, "SecurityType") + + # Check if the security_type is a valid SecurityTypeEnum member + matter_asserts.assert_valid_enum(security_type, "SecurityType", + Clusters.Objects.WiFiNetworkDiagnostics.Enums.SecurityTypeEnum) + + # Additional check that it's not kUnknownEnumValue: + self.assert_true( + security_type != Clusters.Objects.WiFiNetworkDiagnostics.Enums.SecurityTypeEnum.kUnknownEnumValue, + f"SecurityType should not be kUnknownEnumValue " + f"({Clusters.Objects.WiFiNetworkDiagnostics.Enums.SecurityTypeEnum.kUnknownEnumValue})" + ) # # STEP 4: TH reads WiFiVersion attribute @@ -193,21 +150,16 @@ async def test_TC_DGWIFI_2_1(self): self.step(4) wifi_version = await self.read_dgwifi_attribute_expect_success(endpoint=endpoint, attribute=attributes.WiFiVersion) # WiFiVersion is an enum. If not configured or operational, might be NULL. - if wifi_version is not None: - asserts.assert_true(isinstance(wifi_version, Nullable), - "WiFiVersion must be of type 'Nullable' when not None.") + if wifi_version is not NullValue: + matter_asserts.assert_valid_uint8(wifi_version, "WiFiVersion") - if wifi_version is not NullValue: - wifi_version_value = wifi_version.Value - self.assert_valid_uint8(wifi_version_value, "WiFiVersion") + # Check if the wifi_version is a valid WiFiVersionEnum member + matter_asserts.assert_valid_enum(wifi_version, "WiFiVersion", + Clusters.Objects.WiFiNetworkDiagnostics.Enums.WiFiVersionEnum) - # Check if the wifi_version is a valid WiFiVersionEnum member - self.assert_true(wifi_version_value in [item.value for item in Clusters.Objects.WiFiNetworkDiagnostics.Enums.WiFiVersionEnum], - f"WiFiVersion {wifi_version_value} is not a valid WiFiVersionEnum value") - - # Additional check that it's not kUnknownEnumValue: - self.assert_true(wifi_version_value != Clusters.Objects.WiFiNetworkDiagnostics.Enums.WiFiVersionEnum.kUnknownEnumValue.value, - f"WiFiVersion should not be kUnknownEnumValue ({Clusters.Objects.WiFiNetworkDiagnostics.Enums.WiFiVersionEnum.kUnknownEnumValue.value})") + # Additional check that it's not kUnknownEnumValue: + self.assert_true(wifi_version != Clusters.Objects.WiFiNetworkDiagnostics.Enums.WiFiVersionEnum.kUnknownEnumValue, + f"WiFiVersion should not be kUnknownEnumValue ({Clusters.Objects.WiFiNetworkDiagnostics.Enums.WiFiVersionEnum.kUnknownEnumValue})") # # STEP 5: TH reads ChannelNumber attribute @@ -215,12 +167,8 @@ async def test_TC_DGWIFI_2_1(self): self.step(5) channel_number = await self.read_dgwifi_attribute_expect_success(endpoint=endpoint, attribute=attributes.ChannelNumber) # If not operational, might be NULL. Else we expect an unsigned integer channel. - if channel_number is not None: - asserts.assert_true(isinstance(channel_number, Nullable), - "ChannelNumber must be of type 'Nullable' when not None.") - - if channel_number is not NullValue: - self.assert_valid_uint16(channel_number.Value, "ChannelNumber") + if channel_number is not NullValue: + matter_asserts.assert_valid_uint16(channel_number, "ChannelNumber") # # STEP 6: TH reads RSSI attribute @@ -228,14 +176,9 @@ async def test_TC_DGWIFI_2_1(self): self.step(6) rssi = await self.read_dgwifi_attribute_expect_success(endpoint=endpoint, attribute=attributes.Rssi) # RSSI is typically a signed integer (dB). If not operational, might be NULL. - if rssi is not None: - asserts.assert_true(isinstance(rssi, Nullable), - "RSSI must be of type 'Nullable' when not None.") - - if rssi is not NullValue: - rssi_value = rssi.Value - asserts.assert_true(isinstance(rssi_value, int) and -120 <= rssi_value <= 0, - "rssi_value is not within valid range.") + if rssi is not NullValue: + asserts.assert_true(isinstance(rssi, int) and -120 <= rssi <= 0, + "rssi_value is not within valid range.") # # STEP 7: TH reads BeaconLostCount attribute @@ -248,7 +191,7 @@ async def test_TC_DGWIFI_2_1(self): "BeaconLostCount must be of type 'Nullable' when not None.") if beacon_lost_count is not NullValue: - self.assert_valid_uint32(beacon_lost_count.Value, "BeaconLostCount") + matter_asserts.assert_valid_uint32(beacon_lost_count, "BeaconLostCount") # # STEP 8: TH reads BeaconRxCount attribute @@ -256,11 +199,8 @@ async def test_TC_DGWIFI_2_1(self): self.step(8) beacon_rx_count = await self.read_dgwifi_attribute_expect_success(endpoint=endpoint, attribute=attributes.BeaconRxCount) if beacon_rx_count is not None: - asserts.assert_true(isinstance(beacon_rx_count, Nullable), - "BeaconRxCount must be of type 'Nullable' when not None.") - if beacon_rx_count is not NullValue: - self.assert_valid_uint32(beacon_rx_count.Value, "BeaconRxCount") + matter_asserts.assert_valid_uint32(beacon_rx_count, "BeaconRxCount") # # STEP 9: TH reads PacketMulticastRxCount attribute @@ -268,11 +208,8 @@ async def test_TC_DGWIFI_2_1(self): self.step(9) pkt_multi_rx = await self.read_dgwifi_attribute_expect_success(endpoint=endpoint, attribute=attributes.PacketMulticastRxCount) if pkt_multi_rx is not None: - asserts.assert_true(isinstance(pkt_multi_rx, Nullable), - "PacketMulticastRxCount must be of type 'Nullable' when not None.") - if pkt_multi_rx is not NullValue: - self.assert_valid_uint32(pkt_multi_rx.Value, "PacketMulticastRxCount") + matter_asserts.assert_valid_uint32(pkt_multi_rx, "PacketMulticastRxCount") # # STEP 10: TH reads PacketMulticastTxCount attribute @@ -280,11 +217,8 @@ async def test_TC_DGWIFI_2_1(self): self.step(10) pkt_multi_tx = await self.read_dgwifi_attribute_expect_success(endpoint=endpoint, attribute=attributes.PacketMulticastTxCount) if pkt_multi_tx is not None: - asserts.assert_true(isinstance(pkt_multi_tx, Nullable), - "PacketMulticastTxCount must be of type 'Nullable' when not None.") - if pkt_multi_tx is not NullValue: - self.assert_valid_uint32(pkt_multi_tx.Value, "PacketMulticastTxCount") + matter_asserts.assert_valid_uint32(pkt_multi_tx, "PacketMulticastTxCount") # # STEP 11: TH reads PacketUnicastRxCount attribute @@ -292,11 +226,8 @@ async def test_TC_DGWIFI_2_1(self): self.step(11) pkt_uni_rx = await self.read_dgwifi_attribute_expect_success(endpoint=endpoint, attribute=attributes.PacketUnicastRxCount) if pkt_uni_rx is not None: - asserts.assert_true(isinstance(pkt_uni_rx, Nullable), - "PacketUnicastRxCount must be of type 'Nullable' when not None.") - if pkt_uni_rx is not NullValue: - self.assert_valid_uint32(pkt_uni_rx.Value, "PacketUnicastRxCount") + matter_asserts.assert_valid_uint32(pkt_uni_rx, "PacketUnicastRxCount") # # STEP 12: TH reads PacketUnicastTxCount attribute @@ -304,11 +235,8 @@ async def test_TC_DGWIFI_2_1(self): self.step(12) pkt_uni_tx = await self.read_dgwifi_attribute_expect_success(endpoint=endpoint, attribute=attributes.PacketUnicastTxCount) if pkt_uni_tx is not None: - asserts.assert_true(isinstance(pkt_uni_tx, Nullable), - "PacketUnicastTxCount must be of type 'Nullable' when not None.") - if pkt_uni_tx is not NullValue: - self.assert_valid_uint32(pkt_uni_tx.Value, "PacketUnicastTxCount") + matter_asserts.assert_valid_uint32(pkt_uni_tx, "PacketUnicastTxCount") # # STEP 13: TH reads CurrentMaxRate attribute @@ -317,11 +245,8 @@ async def test_TC_DGWIFI_2_1(self): current_max_rate = await self.read_dgwifi_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentMaxRate) # According to the spec, this is bytes per second (uint). if current_max_rate is not None: - asserts.assert_true(isinstance(current_max_rate, Nullable), - "CurrentMaxRate must be of type 'Nullable' when not None.") - if current_max_rate is not NullValue: - self.assert_valid_uint64(current_max_rate.Value, "CurrentMaxRate") + matter_asserts.assert_valid_uint64(current_max_rate, "CurrentMaxRate") # # STEP 14: TH reads OverrunCount attribute @@ -330,11 +255,8 @@ async def test_TC_DGWIFI_2_1(self): overrun_count = await self.read_dgwifi_attribute_expect_success(endpoint=endpoint, attribute=attributes.OverrunCount) # This is a uint and may reset to 0 after node reboot. if overrun_count is not None: - asserts.assert_true(isinstance(overrun_count, Nullable), - "OverrunCount must be of type 'Nullable' when not None.") - if overrun_count is not NullValue: - self.assert_valid_uint64(overrun_count.Value, "OverrunCount") + matter_asserts.assert_valid_uint64(overrun_count, "OverrunCount") if __name__ == "__main__": diff --git a/src/python_testing/TC_DGWIFI_2_3.py b/src/python_testing/TC_DGWIFI_2_3.py index c9fe7131f16..06502e158ca 100644 --- a/src/python_testing/TC_DGWIFI_2_3.py +++ b/src/python_testing/TC_DGWIFI_2_3.py @@ -33,7 +33,7 @@ # import chip.clusters as Clusters -from chip.clusters.Types import Nullable, NullValue +from chip.clusters.Types import NullValue from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main from mobly import asserts @@ -60,10 +60,9 @@ async def read_attribute_and_validate(self, endpoint, attribute, validation_func value = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=Clusters.Objects.WiFiNetworkDiagnostics, attribute=attribute) if value is None: return - asserts.assert_true(isinstance(value, Nullable), f"{field_name} must be of type 'Nullable' when not None.") if value == NullValue: return - asserts.assert_true(validation_func(value.Value), f"{field_name} has an invalid value: {value.Value}") + asserts.assert_true(validation_func(value), f"{field_name} has an invalid value: {value}") def desc_TC_DGWIFI_2_3(self) -> str: """Returns a description of this test.""" diff --git a/src/python_testing/matter_testing_infrastructure/BUILD.gn b/src/python_testing/matter_testing_infrastructure/BUILD.gn index 64e27b47bb0..28269a3df1f 100644 --- a/src/python_testing/matter_testing_infrastructure/BUILD.gn +++ b/src/python_testing/matter_testing_infrastructure/BUILD.gn @@ -39,9 +39,11 @@ pw_python_package("chip-testing-module") { "chip/testing/choice_conformance.py", "chip/testing/conformance.py", "chip/testing/global_attribute_ids.py", + "chip/testing/matter_asserts.py", "chip/testing/matter_testing.py", "chip/testing/metadata.py", "chip/testing/pics.py", + "chip/testing/runner.py", "chip/testing/spec_parsing.py", "chip/testing/taglist_and_topology_test.py", "chip/testing/tasks.py", @@ -49,6 +51,7 @@ pw_python_package("chip-testing-module") { tests = [ "chip/testing/test_metadata.py", "chip/testing/test_tasks.py", + "chip/testing/test_matter_asserts.py", ] } diff --git a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_asserts.py b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_asserts.py new file mode 100644 index 00000000000..4b17612d7e2 --- /dev/null +++ b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_asserts.py @@ -0,0 +1,328 @@ +""" +Matter-specific assertions building on top of Mobly asserts. +""" + +from typing import Any, List, Optional, Type, TypeVar + +from mobly import asserts + +T = TypeVar('T') + + +# Internal helper functions + +def is_valid_uint_value(value: Any, bit_count: int = 64) -> bool: + """ + Checks if 'value' is a non-negative integer that fits into 'bit_count' bits. + For example, bit_count=32 => 0 <= value <= 0xFFFFFFFF + """ + if not isinstance(value, int): + return False + if value < 0: + return False + return value < (1 << bit_count) + + +def is_valid_int_value(value: Any, bit_count: int = 8) -> bool: + """ + Checks if 'value' is a signed integer that fits into 'bit_count' bits. + For example, for int8: -128 <= value <= 127. + """ + min_val = -(1 << (bit_count - 1)) + max_val = (1 << (bit_count - 1)) - 1 + return isinstance(value, int) and (min_val <= value <= max_val) + + +def is_valid_bool_value(value: Any) -> bool: + """ + Checks if 'value' is a boolean. + """ + return isinstance(value, bool) + + +# Integer assertions + +def assert_valid_uint64(value: Any, description: str) -> None: + """ + Asserts that the value is a valid uint64 (0 <= value < 2^64). + """ + asserts.assert_true(is_valid_uint_value(value, bit_count=64), + f"{description} must be a valid uint64 integer") + + +def assert_valid_uint32(value: Any, description: str) -> None: + """ + Asserts that the value is a valid uint32 (0 <= value < 2^32). + """ + asserts.assert_true(is_valid_uint_value(value, bit_count=32), + f"{description} must be a valid uint32 integer") + + +def assert_valid_uint16(value: Any, description: str) -> None: + """ + Asserts that the value is a valid uint16 (0 <= value < 2^16). + """ + asserts.assert_true(is_valid_uint_value(value, bit_count=16), + f"{description} must be a valid uint16 integer") + + +def assert_valid_uint8(value: Any, description: str) -> None: + """ + Asserts that the value is a valid uint8 (0 <= value < 2^8). + """ + asserts.assert_true(is_valid_uint_value(value, bit_count=8), + f"{description} must be a valid uint8 integer") + + +def assert_valid_int64(value: Any, description: str) -> None: + """ + Asserts that the value is a valid int64 (-2^63 <= value <= 2^63-1). + """ + asserts.assert_true(is_valid_int_value(value, bit_count=64), + f"{description} must be a valid int64 integer") + + +def assert_valid_int32(value: Any, description: str) -> None: + """ + Asserts that the value is a valid int32 (-2^31 <= value <= 2^31-1). + """ + asserts.assert_true(is_valid_int_value(value, bit_count=32), + f"{description} must be a valid int32 integer") + + +def assert_valid_int16(value: Any, description: str) -> None: + """ + Asserts that the value is a valid int16 (-2^15 <= value <= 2^15-1). + """ + asserts.assert_true(is_valid_int_value(value, bit_count=16), + f"{description} must be a valid int16 integer") + + +def assert_valid_int8(value: Any, description: str) -> None: + """ + Asserts that the value is a valid int8 (-128 <= value <= 127). + """ + asserts.assert_true(is_valid_int_value(value, bit_count=8), + f"{description} must be a valid int8 integer") + + +def assert_int_in_range(value: Any, min_value: int, max_value: int, description: str) -> None: + """ + Asserts that the value is an integer within the specified range (inclusive). + + Args: + value: The value to check + min_value: Minimum allowed value (inclusive) + max_value: Maximum allowed value (inclusive) + description: User-defined description for error messages + + Raises: + AssertionError: If value is not an integer or outside the specified range + """ + asserts.assert_true(isinstance(value, int), f"{description} must be an integer") + asserts.assert_greater_equal(value, min_value, f"{description} must be greater than or equal to {min_value}") + asserts.assert_less_equal(value, max_value, f"{description} must be less than or equal to {max_value}") + + +# List assertions + +def assert_list(value: Any, description: str, min_length: Optional[int] = None, max_length: Optional[int] = None) -> None: + """ + Asserts that the value is a list with optional length constraints. + + Args: + value: The value to check + description: User-defined description for error messages + min_length: Optional minimum length (inclusive) + max_length: Optional maximum length (inclusive) + + Raises: + AssertionError: If value is not a list or fails length constraints + """ + asserts.assert_true(isinstance(value, list), f"{description} must be a list") + + if min_length is not None: + asserts.assert_greater_equal(len(value), min_length, + f"{description} must have at least {min_length} elements") + + if max_length is not None: + asserts.assert_less_equal(len(value), max_length, + f"{description} must not exceed {max_length} elements") + + +def assert_list_element_type(value: List[Any], description: str, expected_type: Type[T]) -> None: + """ + Asserts that all elements in the list are of the expected type. + + Args: + value: The list to validate + description: User-defined description for error messages + expected_type: The type that all elements should match + + Raises: + AssertionError: If value is not a list or contains elements of wrong type + """ + assert_list(value, description) + for i, item in enumerate(value): + asserts.assert_true(isinstance(item, expected_type), + f"{description}[{i}] must be of type {expected_type.__name__}") + + +# String assertions + +def assert_is_string(value: Any, description: str) -> None: + """ + Asserts that the value is a string. + + Args: + value: The value to check + description: User-defined description for error messages + + Raises: + AssertionError: If value is not a string + """ + asserts.assert_true(isinstance(value, str), f"{description} must be a string") + + +def assert_string_length(value: Any, description: str, min_length: Optional[int] = None, max_length: Optional[int] = None) -> None: + """ + Asserts that the string length is within the specified bounds. + + Args: + value: The value to check + description: User-defined description for error messages + min_length: Optional minimum length (inclusive) + max_length: Optional maximum length (inclusive) + + Raises: + AssertionError: If value is not a string or fails length constraints + + Note: + - Use min_length=1 instead of assert_non_empty_string when you want to ensure non-emptiness + - Use min_length=None, max_length=None to only validate string type (same as assert_is_string) + """ + assert_is_string(value, description) + if min_length is not None: + asserts.assert_greater_equal(len(value), min_length, + f"{description} length must be at least {min_length} characters") + if max_length is not None: + asserts.assert_less_equal(len(value), max_length, + f"{description} length must not exceed {max_length} characters") + + +def assert_non_empty_string(value: Any, description: str) -> None: + """ + Asserts that the value is a non-empty string. + + Args: + value: The value to check + description: User-defined description for error messages + + Raises: + AssertionError: If value is not a string or is empty + """ + assert_string_length(value, description, min_length=1) + + +def assert_is_octstr(value: Any, description: str) -> None: + """ + Asserts that the value is a octet string. + + Args: + value: The value to check + description: User-defined description for error messages + + Raises: + AssertionError: If value is not a octet string (bytes) + """ + asserts.assert_true(isinstance(value, bytes), f"{description} must be a octet string (bytes)") + + +# Matter-specific assertions + +def assert_string_matches_pattern(value: str, description: str, pattern: str) -> None: + """ + Asserts that the string matches the given regex pattern. + + Args: + value: The string to check + description: User-defined description for error messages + pattern: Regular expression pattern to match against + + Raises: + AssertionError: If value is not a string or doesn't match the pattern + """ + import re + assert_is_string(value, description) + asserts.assert_true(bool(re.match(pattern, value)), + f"{description} must match pattern: {pattern}") + + +def assert_valid_attribute_id(id: int, allow_test: bool = False) -> None: + """ + Asserts that the given ID is a valid attribute ID. + + Args: + id: The attribute ID to validate + allow_test: Whether to allow test attribute IDs + + Raises: + AssertionError: If the ID is not a valid attribute ID + """ + from chip.testing.global_attribute_ids import is_valid_attribute_id + asserts.assert_true(is_valid_attribute_id(id, allow_test), + f"Invalid attribute ID: {hex(id)}") + + +def assert_standard_attribute_id(id: int) -> None: + """ + Asserts that the given ID is a standard attribute ID. + + Args: + id: The attribute ID to validate + + Raises: + AssertionError: If the ID is not a standard attribute ID + """ + from chip.testing.global_attribute_ids import is_standard_attribute_id + asserts.assert_true(is_standard_attribute_id(id), + f"Not a standard attribute ID: {hex(id)}") + + +def assert_valid_command_id(id: int, allow_test: bool = False) -> None: + """ + Asserts that the given ID is a valid command ID. + + Args: + id: The command ID to validate + allow_test: Whether to allow test command IDs + + Raises: + AssertionError: If the ID is not a valid command ID + """ + from chip.testing.global_attribute_ids import is_valid_command_id + asserts.assert_true(is_valid_command_id(id, allow_test), + f"Invalid command ID: {hex(id)}") + + +def assert_standard_command_id(id: int) -> None: + """ + Asserts that the given ID is a standard command ID. + + Args: + id: The command ID to validate + + Raises: + AssertionError: If the ID is not a standard command ID + """ + from chip.testing.global_attribute_ids import is_standard_command_id + asserts.assert_true(is_standard_command_id(id), + f"Not a standard command ID: {hex(id)}") + + +def assert_valid_enum(value: Any, description: str, enum_type: type) -> None: + """ + Asserts that 'value' is a valid instance of the specified enum type. + """ + asserts.assert_true(isinstance(value, enum_type), + f"{description} must be of type {enum_type.__name__}") diff --git a/src/python_testing/test_testing/MockTestRunner.py b/src/python_testing/matter_testing_infrastructure/chip/testing/runner.py similarity index 80% rename from src/python_testing/test_testing/MockTestRunner.py rename to src/python_testing/matter_testing_infrastructure/chip/testing/runner.py index 85d3e613d50..ea060074662 100644 --- a/src/python_testing/test_testing/MockTestRunner.py +++ b/src/python_testing/matter_testing_infrastructure/chip/testing/runner.py @@ -17,7 +17,6 @@ import asyncio import importlib -import os import sys from pathlib import Path from unittest.mock import MagicMock @@ -33,11 +32,12 @@ async def __call__(self, *args, **kwargs): class MockTestRunner(): - def __init__(self, filename: str, classname: str, test: str, endpoint: int = None, pics: dict[str, bool] = None, paa_trust_store_path=None): + def __init__(self, abs_filename: str, classname: str, test: str, endpoint: int = None, + pics: dict[str, bool] = None, paa_trust_store_path=None): self.kvs_storage = 'kvs_admin.json' self.config = MatterTestConfig(endpoint=endpoint, paa_trust_store_path=paa_trust_store_path, pics=pics, storage_path=self.kvs_storage) - self.set_test(filename, classname, test) + self.set_test(abs_filename, classname, test) self.set_test_config(self.config) @@ -48,17 +48,16 @@ def __init__(self, filename: str, classname: str, test: str, endpoint: int = Non catTags=self.config.controller_cat_tags ) - def set_test(self, filename: str, classname: str, test: str): + def set_test(self, abs_filename: str, classname: str, test: str): self.test = test self.config.tests = [self.test] - module_name = Path(os.path.basename(filename)).stem - try: - module = importlib.import_module(module_name) + filename_path = Path(abs_filename) + module = importlib.import_module(filename_path.stem) except ModuleNotFoundError: - sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) - module = importlib.import_module(module_name) + sys.path.append(str(filename_path.parent.resolve())) + module = importlib.import_module(filename_path.stem) self.test_class = getattr(module, classname) @@ -72,7 +71,7 @@ def set_test_config(self, test_config: MatterTestConfig = MatterTestConfig()): def Shutdown(self): self.stack.Shutdown() - def run_test_with_mock_read(self, read_cache: Attribute.AsyncReadTransaction.ReadResponse, hooks=None): + def run_test_with_mock_read(self, read_cache: Attribute.AsyncReadTransaction.ReadResponse, hooks=None): self.default_controller.Read = AsyncMock(return_value=read_cache) # This doesn't need to do anything since we are overriding the read anyway self.default_controller.FindOrEstablishPASESession = AsyncMock(return_value=None) diff --git a/src/python_testing/matter_testing_infrastructure/chip/testing/spec_parsing.py b/src/python_testing/matter_testing_infrastructure/chip/testing/spec_parsing.py index 8160dc7ba99..7bd247746a0 100644 --- a/src/python_testing/matter_testing_infrastructure/chip/testing/spec_parsing.py +++ b/src/python_testing/matter_testing_infrastructure/chip/testing/spec_parsing.py @@ -33,7 +33,7 @@ parse_callable_from_xml, parse_device_type_callable_from_xml) from chip.testing.global_attribute_ids import GlobalAttributeIds from chip.testing.matter_testing import (AttributePathLocation, ClusterPathLocation, CommandPathLocation, DeviceTypePathLocation, - EventPathLocation, FeaturePathLocation, ProblemNotice, ProblemSeverity) + EventPathLocation, FeaturePathLocation, ProblemLocation, ProblemNotice, ProblemSeverity) from chip.tlv import uint _PRIVILEGE_STR = { @@ -53,18 +53,22 @@ class SpecParsingException(Exception): pass +# passing in feature map, attribute list, command list +ConformanceCallable = Callable[[uint, list[uint], list[uint]], ConformanceDecision] + + @dataclass class XmlFeature: code: str name: str - conformance: Callable[[uint], ConformanceDecision] + conformance: ConformanceCallable @dataclass class XmlAttribute: name: str datatype: str - conformance: Callable[[uint], ConformanceDecision] + conformance: ConformanceCallable read_access: Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum write_access: Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum write_optional: bool @@ -84,20 +88,20 @@ def __str__(self): class XmlCommand: id: int name: str - conformance: Callable[[uint], ConformanceDecision] + conformance: ConformanceCallable @dataclass class XmlEvent: name: str - conformance: Callable[[uint], ConformanceDecision] + conformance: ConformanceCallable @dataclass class XmlCluster: name: str revision: int - derived: str + derived: Optional[str] feature_map: dict[str, uint] attribute_map: dict[str, uint] command_map: dict[str, uint] @@ -122,7 +126,7 @@ class ClusterSide(Enum): class XmlDeviceTypeClusterRequirements: name: str side: ClusterSide - conformance: Callable[[uint, list[uint], list[uint]], ConformanceDecision] + conformance: ConformanceCallable # TODO: add element requirements def __str__(self): @@ -177,21 +181,22 @@ class CommandType(Enum): DEVICE_TYPE_NAME_FIXES = {0x010b: 'Dimmable Plug-In Unit', 0x010a: 'On/Off Plug-in Unit'} -def get_location_from_element(element: ElementTree.Element, cluster_id: int): +def get_location_from_element(element: ElementTree.Element, cluster_id: Optional[int]) -> ProblemLocation: + if cluster_id is None: + cluster_id = 0 if element.tag == 'feature': - location = FeaturePathLocation(endpoint_id=0, cluster_id=cluster_id, feature_code=element.attrib['code']) + return FeaturePathLocation(endpoint_id=0, cluster_id=cluster_id, feature_code=element.attrib['code']) elif element.tag == 'command': - location = CommandPathLocation(endpoint_id=0, cluster_id=cluster_id, command_id=int(element.attrib['id'], 0)) + return CommandPathLocation(endpoint_id=0, cluster_id=cluster_id, command_id=int(element.attrib['id'], 0)) elif element.tag == 'attribute': - location = AttributePathLocation(endpoint_id=0, cluster_id=cluster_id, attribute_id=int(element.attrib['id'], 0)) + return AttributePathLocation(endpoint_id=0, cluster_id=cluster_id, attribute_id=int(element.attrib['id'], 0)) elif element.tag == 'event': - location = EventPathLocation(endpoint_id=0, cluster_id=cluster_id, event_id=int(element.attrib['id'], 0)) + return EventPathLocation(endpoint_id=0, cluster_id=cluster_id, event_id=int(element.attrib['id'], 0)) else: - location = ClusterPathLocation(endpoint_id=0, cluster_id=cluster_id) - return location + return ClusterPathLocation(endpoint_id=0, cluster_id=cluster_id) -def get_conformance(element: ElementTree.Element, cluster_id: int) -> tuple[ElementTree.Element, typing.Optional[ProblemNotice]]: +def get_conformance(element: ElementTree.Element, cluster_id: Optional[uint]) -> tuple[ElementTree.Element, typing.Optional[ProblemNotice]]: for sub in element: if sub.tag in TOP_LEVEL_CONFORMANCE_TAGS: return sub, None @@ -201,8 +206,13 @@ def get_conformance(element: ElementTree.Element, cluster_id: int) -> tuple[Elem return ElementTree.Element(OPTIONAL_CONFORM), problem +# Tuple of the root element, the conformance xml element within the root and the optional access element within the root +XmlElementDescriptor = tuple[ElementTree.Element, ElementTree.Element, Optional[ElementTree.Element]] + + class ClusterParser: - def __init__(self, cluster, cluster_id, name): + # Cluster ID is optional to support base clusters that have no ID of their own. + def __init__(self, cluster: ElementTree.Element, cluster_id: Optional[uint], name: str): self._problems: list[ProblemNotice] = [] self._cluster = cluster self._cluster_id = cluster_id @@ -222,6 +232,7 @@ def __init__(self, cluster, cluster_id, name): if list(id.iter('provisionalConform')): self._is_provisional = True + self._pics: Optional[str] = None try: classification = next(cluster.iter('classification')) self._pics = classification.attrib['picsCode'] @@ -250,7 +261,7 @@ def get_access(self, element: ElementTree.Element) -> Optional[ElementTree.Eleme return sub return None - def get_all_type(self, type_container: str, type_name: str, key_name: str) -> list[tuple[ElementTree.Element, ElementTree.Element, ElementTree.Element]]: + def get_all_type(self, type_container: str, type_name: str, key_name: str) -> list[XmlElementDescriptor]: ret = [] container_tags = self._cluster.iter(type_container) for container in container_tags: @@ -266,54 +277,56 @@ def get_all_type(self, type_container: str, type_name: str, key_name: str) -> li ret.append((element, conformance, access)) return ret - def get_all_feature_elements(self) -> list[tuple[ElementTree.Element, ElementTree.Element]]: + def get_all_feature_elements(self) -> list[XmlElementDescriptor]: ''' Returns a list of features and their conformances''' return self.get_all_type('features', 'feature', 'code') - def get_all_attribute_elements(self) -> list[tuple[ElementTree.Element, ElementTree.Element]]: + def get_all_attribute_elements(self) -> list[XmlElementDescriptor]: ''' Returns a list of attributes and their conformances''' return self.get_all_type('attributes', 'attribute', 'id') - def get_all_command_elements(self) -> list[tuple[ElementTree.Element, ElementTree.Element]]: + def get_all_command_elements(self) -> list[XmlElementDescriptor]: ''' Returns a list of commands and their conformances ''' return self.get_all_type('commands', 'command', 'id') - def get_all_event_elements(self) -> list[tuple[ElementTree.Element, ElementTree.Element]]: + def get_all_event_elements(self) -> list[XmlElementDescriptor]: ''' Returns a list of events and their conformances''' return self.get_all_type('events', 'event', 'id') def create_feature_map(self) -> dict[str, uint]: features = {} for element, _, _ in self.feature_elements: - features[element.attrib['code']] = 1 << int(element.attrib['bit'], 0) + features[element.attrib['code']] = uint(1 << int(element.attrib['bit'], 0)) return features def create_attribute_map(self) -> dict[str, uint]: attributes = {} for element, conformance, _ in self.attribute_elements: - attributes[element.attrib['name']] = int(element.attrib['id'], 0) + attributes[element.attrib['name']] = uint(int(element.attrib['id'], 0)) return attributes def create_command_map(self) -> dict[str, uint]: commands = {} for element, _, _ in self.command_elements: - commands[element.attrib['name']] = int(element.attrib['id'], 0) + commands[element.attrib['name']] = uint(int(element.attrib['id'], 0)) return commands - def parse_conformance(self, conformance_xml: ElementTree.Element) -> Callable: + def parse_conformance(self, conformance_xml: ElementTree.Element) -> Optional[ConformanceCallable]: try: return parse_callable_from_xml(conformance_xml, self.params) except ConformanceException as ex: # Just point to the general cluster, because something is mismatched, but it's not clear what - location = ClusterPathLocation(endpoint_id=0, cluster_id=self._cluster_id) + location = ClusterPathLocation(endpoint_id=0, cluster_id=self._cluster_id if self._cluster_id is not None else 0) self._problems.append(ProblemNotice(test_name='Spec XML parsing', location=location, severity=ProblemSeverity.WARNING, problem=str(ex))) return None - def parse_write_optional(self, element_xml: ElementTree.Element, access_xml: ElementTree.Element) -> bool: + def parse_write_optional(self, element_xml: ElementTree.Element, access_xml: Optional[ElementTree.Element]) -> bool: + if access_xml is None: + return False return access_xml.attrib['write'] == 'optional' - def parse_access(self, element_xml: ElementTree.Element, access_xml: ElementTree.Element, conformance: Callable) -> tuple[Optional[Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum], Optional[Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum], Optional[Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum]]: + def parse_access(self, element_xml: ElementTree.Element, access_xml: Optional[ElementTree.Element], conformance: Callable) -> tuple[Optional[Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum], Optional[Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum], Optional[Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum]]: ''' Returns a tuple of access types for read / write / invoke''' def str_to_access_type(privilege_str: str) -> Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum: if privilege_str == 'view': @@ -361,7 +374,7 @@ def str_to_access_type(privilege_str: str) -> Clusters.AccessControl.Enums.Acces def parse_features(self) -> dict[uint, XmlFeature]: features = {} for element, conformance_xml, _ in self.feature_elements: - mask = 1 << int(element.attrib['bit'], 0) + mask = uint(1 << int(element.attrib['bit'], 0)) conformance = self.parse_conformance(conformance_xml) if conformance is None: continue @@ -370,9 +383,9 @@ def parse_features(self) -> dict[uint, XmlFeature]: return features def parse_attributes(self) -> dict[uint, XmlAttribute]: - attributes = {} + attributes: dict[uint, XmlAttribute] = {} for element, conformance_xml, access_xml in self.attribute_elements: - code = int(element.attrib['id'], 0) + code = uint(int(element.attrib['id'], 0)) # Some deprecated attributes don't have their types included, for now, lets just fallback to UNKNOWN try: datatype = element.attrib['type'] @@ -394,7 +407,7 @@ def parse_attributes(self) -> dict[uint, XmlAttribute]: # Add in the global attributes for the base class for id in GlobalAttributeIds: # TODO: Add data type here. Right now it's unused. We should parse this from the spec. - attributes[id] = XmlAttribute(name=id.to_name(), datatype="", conformance=mandatory( + attributes[uint(id)] = XmlAttribute(name=id.to_name(), datatype="", conformance=mandatory( ), read_access=Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kView, write_access=Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kUnknownEnumValue, write_optional=False) return attributes @@ -419,27 +432,28 @@ def parse_unknown_commands(self) -> list[XmlCommand]: continue code = int(element.attrib['id'], 0) conformance = self.parse_conformance(conformance_xml) - commands.append(XmlCommand(id=code, name=element.attrib['name'], conformance=conformance)) + if conformance is not None: + commands.append(XmlCommand(id=code, name=element.attrib['name'], conformance=conformance)) return commands def parse_commands(self, command_type: CommandType) -> dict[uint, XmlCommand]: - commands = {} + commands: dict[uint, XmlCommand] = {} for element, conformance_xml, access_xml in self.command_elements: if self.get_command_type(element) != command_type: continue - code = int(element.attrib['id'], 0) + code = uint(int(element.attrib['id'], 0)) conformance = self.parse_conformance(conformance_xml) if conformance is None: continue if code in commands: conformance = or_operation([conformance, commands[code].conformance]) - commands[code] = XmlCommand(id=code, name=element.attrib['name'], conformance=conformance) + commands[uint(code)] = XmlCommand(id=code, name=element.attrib['name'], conformance=conformance) return commands - def parse_events(self) -> dict[uint, XmlAttribute]: - events = {} + def parse_events(self) -> dict[uint, XmlEvent]: + events: dict[uint, XmlEvent] = {} for element, conformance_xml, access_xml in self.event_elements: - code = int(element.attrib['id'], 0) + code = uint(int(element.attrib['id'], 0)) conformance = self.parse_conformance(conformance_xml) if conformance is None: continue @@ -467,7 +481,7 @@ def get_problems(self) -> list[ProblemNotice]: return self._problems -def add_cluster_data_from_xml(xml: ElementTree.Element, clusters: dict[int, XmlCluster], pure_base_clusters: dict[str, XmlCluster], ids_by_name: dict[str, int], problems: list[ProblemNotice]) -> None: +def add_cluster_data_from_xml(xml: ElementTree.Element, clusters: dict[uint, XmlCluster], pure_base_clusters: dict[str, XmlCluster], ids_by_name: dict[str, uint], problems: list[ProblemNotice]) -> None: ''' Adds cluster data to the supplied dicts as appropriate xml: XML element read from from the XML cluster file @@ -482,10 +496,16 @@ def add_cluster_data_from_xml(xml: ElementTree.Element, clusters: dict[int, XmlC ids = c.iter('clusterId') for id in ids: name = id.get('name') - cluster_id = id.get('id') - if cluster_id: - cluster_id = int(id.get('id'), 0) - ids_by_name[name] = cluster_id + cluster_id_str = id.get('id') + cluster_id: Optional[uint] = None + if cluster_id_str: + cluster_id = uint(int(cluster_id_str, 0)) + + if name is None: + location = ClusterPathLocation(endpoint_id=0, cluster_id=0 if cluster_id is None else cluster_id) + problems.append(ProblemNotice(test_name="Spec XML parsing", location=location, + severity=ProblemSeverity.WARNING, problem=f"Cluster with no name {cluster}")) + continue parser = ClusterParser(c, cluster_id, name) new = parser.create_cluster() @@ -493,6 +513,7 @@ def add_cluster_data_from_xml(xml: ElementTree.Element, clusters: dict[int, XmlC if cluster_id: clusters[cluster_id] = new + ids_by_name[name] = cluster_id else: # Fully derived clusters have no id, but also shouldn't appear on a device. # We do need to keep them, though, because we need to update the derived @@ -501,7 +522,7 @@ def add_cluster_data_from_xml(xml: ElementTree.Element, clusters: dict[int, XmlC pure_base_clusters[name] = new -def check_clusters_for_unknown_commands(clusters: dict[int, XmlCluster], problems: list[ProblemNotice]): +def check_clusters_for_unknown_commands(clusters: dict[uint, XmlCluster], problems: list[ProblemNotice]): for id, cluster in clusters.items(): for cmd in cluster.unknown_commands: problems.append(ProblemNotice(test_name="Spec XML parsing", location=CommandPathLocation( @@ -553,7 +574,7 @@ def get_data_model_directory(data_model_directory: Union[PrebuiltDataModelDirect 'data_model').joinpath(data_model_directory.dirname).joinpath(data_model_level.dirname) -def build_xml_clusters(data_model_directory: Union[PrebuiltDataModelDirectory, Traversable] = PrebuiltDataModelDirectory.k1_4) -> typing.Tuple[dict[int, dict], list]: +def build_xml_clusters(data_model_directory: Union[PrebuiltDataModelDirectory, Traversable] = PrebuiltDataModelDirectory.k1_4) -> typing.Tuple[dict[uint, XmlCluster], list[ProblemNotice]]: """ Build XML clusters from the specified data model directory. This function supports both pre-built locations and full paths. @@ -563,9 +584,9 @@ def build_xml_clusters(data_model_directory: Union[PrebuiltDataModelDirectory, T with all XML files in it) """ - clusters: dict[int, XmlCluster] = {} + clusters: dict[uint, XmlCluster] = {} pure_base_clusters: dict[str, XmlCluster] = {} - ids_by_name: dict[str, int] = {} + ids_by_name: dict[str, uint] = {} problems: list[ProblemNotice] = [] top = get_data_model_directory(data_model_directory, DataModelLevel.kCluster) @@ -601,13 +622,13 @@ def remove_problem(location: typing.Union[CommandPathLocation, FeaturePathLocati nonlocal problems problems = [p for p in problems if p.location != location] - descriptor_id = Clusters.Descriptor.id + descriptor_id = uint(Clusters.Descriptor.id) code = 'TAGLIST' mask = clusters[descriptor_id].feature_map[code] clusters[descriptor_id].features[mask].conformance = optional() remove_problem(FeaturePathLocation(endpoint_id=0, cluster_id=descriptor_id, feature_code=code)) - action_id = Clusters.Actions.id + action_id = uint(Clusters.Actions.id) for c in Clusters.ClusterObjects.ALL_ACCEPTED_COMMANDS[action_id]: clusters[action_id].accepted_commands[c].conformance = optional() remove_problem(CommandPathLocation(endpoint_id=0, cluster_id=action_id, command_id=c)) @@ -619,7 +640,7 @@ def remove_problem(location: typing.Union[CommandPathLocation, FeaturePathLocati # to implement either arithmetic conformance handling (once spec changes land here) or specific test # https://github.com/CHIP-Specifications/connectedhomeip-spec/pull/7808 for spec changes. # see 3.2.8. Defined Primaries Information Attribute Set, affects Primary<#>X/Y/Intensity attributes. - cc_id = Clusters.ColorControl.id + cc_id = uint(Clusters.ColorControl.id) cc_attr = Clusters.ColorControl.Attributes affected_attributes = [cc_attr.Primary1X, cc_attr.Primary1Y, @@ -645,44 +666,45 @@ def remove_problem(location: typing.Union[CommandPathLocation, FeaturePathLocati # Workaround for temp control cluster - this is parsed incorrectly in the DM XML and is missing all its attributes # Remove this workaround when https://github.com/csa-data-model/projects/issues/330 is fixed - temp_control_id = Clusters.TemperatureControl.id + temp_control_id = uint(Clusters.TemperatureControl.id) if temp_control_id in clusters and not clusters[temp_control_id].attributes: view = Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kView none = Clusters.AccessControl.Enums.AccessControlEntryPrivilegeEnum.kUnknownEnumValue clusters[temp_control_id].attributes = { - 0x00: XmlAttribute(name='TemperatureSetpoint', datatype='temperature', conformance=feature(0x01, 'TN'), read_access=view, write_access=none, write_optional=False), - 0x01: XmlAttribute(name='MinTemperature', datatype='temperature', conformance=feature(0x01, 'TN'), read_access=view, write_access=none, write_optional=False), - 0x02: XmlAttribute(name='MaxTemperature', datatype='temperature', conformance=feature(0x01, 'TN'), read_access=view, write_access=none, write_optional=False), - 0x03: XmlAttribute(name='Step', datatype='temperature', conformance=feature(0x04, 'STEP'), read_access=view, write_access=none, write_optional=False), - 0x04: XmlAttribute(name='SelectedTemperatureLevel', datatype='uint8', conformance=feature(0x02, 'TL'), read_access=view, write_access=none, write_optional=False), - 0x05: XmlAttribute(name='SupportedTemperatureLevels', datatype='list', conformance=feature(0x02, 'TL'), read_access=view, write_access=none, write_optional=False), + uint(0x00): XmlAttribute(name='TemperatureSetpoint', datatype='temperature', conformance=feature(uint(0x01), 'TN'), read_access=view, write_access=none, write_optional=False), + uint(0x01): XmlAttribute(name='MinTemperature', datatype='temperature', conformance=feature(uint(0x01), 'TN'), read_access=view, write_access=none, write_optional=False), + uint(0x02): XmlAttribute(name='MaxTemperature', datatype='temperature', conformance=feature(uint(0x01), 'TN'), read_access=view, write_access=none, write_optional=False), + uint(0x03): XmlAttribute(name='Step', datatype='temperature', conformance=feature(uint(0x04), 'STEP'), read_access=view, write_access=none, write_optional=False), + uint(0x04): XmlAttribute(name='SelectedTemperatureLevel', datatype='uint8', conformance=feature(uint(0x02), 'TL'), read_access=view, write_access=none, write_optional=False), + uint(0x05): XmlAttribute(name='SupportedTemperatureLevels', datatype='list', conformance=feature(uint(0x02), 'TL'), read_access=view, write_access=none, write_optional=False), } # TODO: Need automated parsing for atomic attributes. - atomic_request_cmd_id = 0xFE - atomic_response_cmd_id = 0xFD + atomic_request_cmd_id = uint(0xFE) + atomic_response_cmd_id = uint(0xFD) atomic_request_name = "Atomic Request" atomic_response_name = "Atomic Response" presets_name = "Presets" schedules_name = "Schedules" - if clusters[Clusters.Thermostat.id].revision >= 8: - presents_id = clusters[Clusters.Thermostat.id].attribute_map[presets_name] - schedules_id = clusters[Clusters.Thermostat.id].attribute_map[schedules_name] + thermostat_id = uint(Clusters.Thermostat.id) + if clusters[thermostat_id].revision >= 8: + presents_id = clusters[thermostat_id].attribute_map[presets_name] + schedules_id = clusters[thermostat_id].attribute_map[schedules_name] conformance = or_operation([conformance_support.attribute(presents_id, presets_name), conformance_support.attribute(schedules_id, schedules_name)]) - clusters[Clusters.Thermostat.id].accepted_commands[atomic_request_cmd_id] = XmlCommand( + clusters[thermostat_id].accepted_commands[atomic_request_cmd_id] = XmlCommand( id=atomic_request_cmd_id, name=atomic_request_name, conformance=conformance) - clusters[Clusters.Thermostat.id].generated_commands[atomic_response_cmd_id] = XmlCommand( + clusters[thermostat_id].generated_commands[atomic_response_cmd_id] = XmlCommand( id=atomic_response_cmd_id, name=atomic_response_name, conformance=conformance) - clusters[Clusters.Thermostat.id].command_map[atomic_request_name] = atomic_request_cmd_id - clusters[Clusters.Thermostat.id].command_map[atomic_response_name] = atomic_response_cmd_id + clusters[thermostat_id].command_map[atomic_request_name] = atomic_request_cmd_id + clusters[thermostat_id].command_map[atomic_response_name] = atomic_response_cmd_id check_clusters_for_unknown_commands(clusters, problems) return clusters, problems -def combine_derived_clusters_with_base(xml_clusters: dict[int, XmlCluster], pure_base_clusters: dict[str, XmlCluster], ids_by_name: dict[str, int], problems: list[ProblemNotice]) -> None: +def combine_derived_clusters_with_base(xml_clusters: dict[uint, XmlCluster], pure_base_clusters: dict[str, XmlCluster], ids_by_name: dict[str, uint], problems: list[ProblemNotice]) -> None: ''' Overrides base elements with the derived cluster values for derived clusters. ''' def combine_attributes(base: dict[uint, XmlAttribute], derived: dict[uint, XmlAttribute], cluster_id: uint, problems: list[ProblemNotice]) -> dict[uint, XmlAttribute]: @@ -734,10 +756,10 @@ def combine_attributes(base: dict[uint, XmlAttribute], derived: dict[uint, XmlAt events.update(c.events) unknown_commands = deepcopy(base.unknown_commands) for cmd in c.unknown_commands: - if cmd.id in accepted_commands.keys() and cmd.name == accepted_commands[cmd.id].name: - accepted_commands[cmd.id].conformance = cmd.conformance - elif cmd.id in generated_commands.keys() and cmd.name == generated_commands[cmd.id].name: - generated_commands[cmd.id].conformance = cmd.conformance + if cmd.id in accepted_commands.keys() and cmd.name == accepted_commands[uint(cmd.id)].name: + accepted_commands[uint(cmd.id)].conformance = cmd.conformance + elif cmd.id in generated_commands.keys() and cmd.name == generated_commands[uint(cmd.id)].name: + generated_commands[uint(cmd.id)].conformance = cmd.conformance else: unknown_commands.append(cmd) provisional = c.is_provisional or base.is_provisional @@ -784,7 +806,6 @@ def parse_single_device_type(root: ElementTree.Element) -> tuple[dict[int, XmlDe except (KeyError, StopIteration): # this is fine for base device type if id == -1: - classification = 'BASE' scope = 'BASE' device_class = 'BASE' else: @@ -797,7 +818,7 @@ def parse_single_device_type(root: ElementTree.Element) -> tuple[dict[int, XmlDe clusters = d.iter('cluster') for c in clusters: try: - cid = int(c.attrib['id'], 0) + cid = uint(int(c.attrib['id'], 0)) conformance_xml, tmp_problem = get_conformance(c, cid) if tmp_problem: problems.append(tmp_problem) @@ -825,7 +846,7 @@ def parse_single_device_type(root: ElementTree.Element) -> tuple[dict[int, XmlDe def build_xml_device_types(data_model_directory: typing.Union[PrebuiltDataModelDirectory, Traversable] = PrebuiltDataModelDirectory.k1_4) -> tuple[dict[int, XmlDeviceType], list[ProblemNotice]]: top = get_data_model_directory(data_model_directory, DataModelLevel.kDeviceType) device_types: dict[int, XmlDeviceType] = {} - problems = [] + problems: list[ProblemNotice] = [] found_xmls = 0 diff --git a/src/python_testing/matter_testing_infrastructure/chip/testing/test_matter_asserts.py b/src/python_testing/matter_testing_infrastructure/chip/testing/test_matter_asserts.py new file mode 100644 index 00000000000..88df62ffdad --- /dev/null +++ b/src/python_testing/matter_testing_infrastructure/chip/testing/test_matter_asserts.py @@ -0,0 +1,293 @@ +"""Unit tests for matter_asserts module.""" + +import enum +import unittest + +from chip.testing import matter_asserts +from mobly import signals + + +class MyTestEnum(enum.Enum): + VALID_MEMBER = 1 + ANOTHER_MEMBER = 2 + + +class TestMatterAsserts(unittest.TestCase): + """Unit tests for matter_asserts module.""" + + # Integer assertion tests + def test_assert_valid_uint32(self): + """Test assert_valid_uint32 with valid and invalid values.""" + # Valid cases + matter_asserts.assert_valid_uint32(0, "test_min") + matter_asserts.assert_valid_uint32(0xFFFFFFFF, "test_max") + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint32(-1, "test_negative") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint32(0x100000000, "test_too_large") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint32("42", "test_string") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint32(42.0, "test_float") + + def test_assert_valid_uint64(self): + """Test assert_valid_uint64 with valid and invalid values.""" + # Valid cases + matter_asserts.assert_valid_uint64(0, "test_min") + matter_asserts.assert_valid_uint64(0xFFFFFFFFFFFFFFFF, "test_max") + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint64(-1, "test_negative") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint64(0x10000000000000000, "test_too_large") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint64("42", "test_string") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint64(42.0, "test_float") + + def test_assert_valid_uint16(self): + """Test assert_valid_uint16 with valid and invalid values.""" + # Valid cases + matter_asserts.assert_valid_uint16(0, "test_min") + matter_asserts.assert_valid_uint16(0xFFFF, "test_max") + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint16(-1, "test_negative") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint16(0x10000, "test_too_large") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint16("42", "test_string") + + def test_assert_valid_uint8(self): + """Test assert_valid_uint8 with valid and invalid values.""" + # Valid cases + matter_asserts.assert_valid_uint8(0, "test_min") + matter_asserts.assert_valid_uint8(0xFF, "test_max") + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint8(-1, "test_negative") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint8(0x100, "test_too_large") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_uint8("42", "test_string") + + def test_assert_valid_int64(self): + """Test assert_valid_int64 with valid and invalid values.""" + # Valid cases + matter_asserts.assert_valid_int64(-2**63, "test_min") + matter_asserts.assert_valid_int64(2**63 - 1, "test_max") + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_int64(-2**63 - 1, "test_too_small") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_int64(2**63, "test_too_large") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_int64("42", "test_string") + + def test_assert_valid_int32(self): + """Test assert_valid_int32 with valid and invalid values.""" + # Valid cases + matter_asserts.assert_valid_int32(-2**31, "test_min") + matter_asserts.assert_valid_int32(2**31 - 1, "test_max") + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_int32(-2**31 - 1, "test_too_small") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_int32(2**31, "test_too_large") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_int32("42", "test_string") + + def test_assert_valid_int16(self): + """Test assert_valid_int16 with valid and invalid values.""" + # Valid cases + matter_asserts.assert_valid_int16(-2**15, "test_min") + matter_asserts.assert_valid_int16(2**15 - 1, "test_max") + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_int16(-2**15 - 1, "test_too_small") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_int16(2**15, "test_too_large") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_int16("42", "test_string") + + def test_assert_valid_int8(self): + """Test assert_valid_int8 with valid and invalid values.""" + # Valid cases + matter_asserts.assert_valid_int8(-128, "test_min") + matter_asserts.assert_valid_int8(127, "test_max") + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_int8(-129, "test_too_small") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_int8(128, "test_too_large") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_int8("42", "test_string") + + def test_assert_int_in_range(self): + """Test assert_int_in_range with valid and invalid values.""" + # Valid cases + matter_asserts.assert_int_in_range(0, 0, 10, "test_min") + matter_asserts.assert_int_in_range(10, 0, 10, "test_max") + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_int_in_range(-1, 0, 10, "test_below_min") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_int_in_range(11, 0, 10, "test_above_max") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_int_in_range("5", 0, 10, "test_string") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_int_in_range(5.0, 0, 10, "test_float") + + # List assertion tests + def test_assert_list(self): + """Test assert_list with valid and invalid values.""" + # Valid cases + matter_asserts.assert_list([], "test_empty") + matter_asserts.assert_list([1, 2, 3], "test_nonempty") + matter_asserts.assert_list([1, 2], "test_length", min_length=1, max_length=3) + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_list("not_a_list", "test_string") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_list([], "test_min_length", min_length=1) + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_list([1, 2, 3], "test_max_length", max_length=2) + + def test_assert_list_element_type(self): + """Test assert_list_element_type with valid and invalid values.""" + # Valid cases + matter_asserts.assert_list_element_type([], "test_empty", str) + matter_asserts.assert_list_element_type(["a", "b"], "test_strings", str) + matter_asserts.assert_list_element_type([1, 2, 3], "test_ints", int) + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_list_element_type("not_a_list", "test_not_list", str) + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_list_element_type([1, "2", 3], "test_mixed", int) + + # String assertion tests + def test_assert_is_string(self): + """Test assert_is_string with valid and invalid values.""" + # Valid cases + matter_asserts.assert_is_string("", "test_empty") + matter_asserts.assert_is_string("hello", "test_nonempty") + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_is_string(42, "test_int") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_is_string(["str"], "test_list") + + def test_assert_string_length(self): + """Test assert_string_length with valid and invalid values.""" + # Valid cases + matter_asserts.assert_string_length("", "test_empty") + matter_asserts.assert_string_length("abc", "test_length", min_length=1, max_length=5) + matter_asserts.assert_string_length("abc", "test_exact", min_length=3, max_length=3) + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_string_length(42, "test_not_string") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_string_length("", "test_min", min_length=1) + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_string_length("toolong", "test_max", max_length=5) + + def test_assert_non_empty_string(self): + """Test assert_non_empty_string with valid and invalid values.""" + # Valid cases + matter_asserts.assert_non_empty_string("a", "test_single") + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_non_empty_string("", "test_empty") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_non_empty_string(42, "test_not_string") + + def test_assert_is_octstr(self): + """Test assert_is_octstr with valid and invalid values.""" + # Valid case + matter_asserts.assert_is_octstr(b"", "test_empty_bytes") + matter_asserts.assert_is_octstr(b"\x01\x02", "test_some_bytes") + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_is_octstr("not_bytes", "test_string") + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_is_octstr(123, "test_int") + + def test_assert_string_matches_pattern(self): + """Test assert_string_matches_pattern with valid and invalid values.""" + # Valid cases + matter_asserts.assert_string_matches_pattern("abc123", "test_alphanumeric", r'^[a-z0-9]+$') + matter_asserts.assert_string_matches_pattern("hello", "test_hello", r'^hello$') + + # Invalid cases + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_string_matches_pattern(123, "test_not_string", r'^.*$') + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_string_matches_pattern("abc!", "test_pattern_mismatch", r'^[a-z0-9]+$') + + def test_assert_valid_enum(self): + """Test assert_valid_enum with valid and invalid values.""" + # Valid case + matter_asserts.assert_valid_enum(MyTestEnum.VALID_MEMBER, "test_enum_member", MyTestEnum) + + # Invalid cases: not an enum member or wrong type + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_enum(1, "test_int_instead_of_enum", MyTestEnum) + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_enum("INVALID", "test_string", MyTestEnum) + + # Matter-specific assertion tests + def test_assert_valid_attribute_id(self): + """Test assert_valid_attribute_id with valid and invalid values.""" + # Valid case - standard global attribute + matter_asserts.assert_valid_attribute_id(0x0000_F000) + + # Invalid case - standard global bad attribute + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_attribute_id(0x0000_FFFF) + + def test_assert_standard_attribute_id(self): + """Test assert_standard_attribute_id with valid and invalid values.""" + # Valid case - standard global attribute + matter_asserts.assert_standard_attribute_id(0x0000_F000) + + # Invalid case - manufacturer bad attribute + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_standard_attribute_id(0x0001_0000) + + def test_assert_valid_command_id(self): + """Test assert_valid_command_id with valid and invalid values.""" + # Valid case - standard global command + matter_asserts.assert_valid_command_id(0x0000_00E0) + + # Invalid case - standard global bad command + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_valid_command_id(0x0000_FFFF) + + def test_assert_standard_command_id(self): + """Test assert_standard_command_id with valid and invalid values.""" + # Valid case - standard global command + matter_asserts.assert_standard_command_id(0x0000_00E0) + + # Invalid case - manufacturer command + with self.assertRaises(signals.TestFailure): + matter_asserts.assert_standard_command_id(0x0001_0000) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python_testing/test_testing/TestDecorators.py b/src/python_testing/test_testing/TestDecorators.py index 4d4b268ee91..c2d754580c1 100644 --- a/src/python_testing/test_testing/TestDecorators.py +++ b/src/python_testing/test_testing/TestDecorators.py @@ -26,14 +26,15 @@ # You will get step_* calls as appropriate in between the test_start and test_stop calls if the test is not skipped. import sys +from pathlib import Path from typing import Optional import chip.clusters as Clusters from chip.clusters import Attribute from chip.testing.matter_testing import (MatterBaseTest, MatterTestConfig, async_test_body, has_attribute, has_cluster, has_feature, run_if_endpoint_matches, run_on_singleton_matching_endpoint, should_run_test_on_endpoint) +from chip.testing.runner import MockTestRunner from mobly import asserts -from MockTestRunner import MockTestRunner def get_clusters(endpoints: list[int]) -> Attribute.AsyncReadTransaction.ReadResponse: @@ -216,7 +217,8 @@ async def test_no_run_on_singleton_matching_endpoint(self): def main(): failures = [] hooks = DecoratorTestRunnerHooks() - test_runner = MockTestRunner('TestDecorators.py', 'TestDecorators', 'test_checkers') + test_runner = MockTestRunner(Path(__file__).parent / 'TestDecorators.py', + 'TestDecorators', 'test_checkers') read_resp = get_clusters([0, 1]) ok = test_runner.run_test_with_mock_read(read_resp, hooks) if not ok: diff --git a/src/python_testing/test_testing/common_icdm_data.py b/src/python_testing/test_testing/common_icdm_data.py index aa2fb235e12..802d1240da4 100644 --- a/src/python_testing/test_testing/common_icdm_data.py +++ b/src/python_testing/test_testing/common_icdm_data.py @@ -18,10 +18,11 @@ import string from dataclasses import dataclass +from pathlib import Path import chip.clusters as Clusters from chip.clusters import Attribute -from MockTestRunner import MockTestRunner +from chip.testing.runner import MockTestRunner c = Clusters.IcdManagement attr = c.Attributes @@ -54,8 +55,8 @@ def test_spec_to_attribute_cache(test_icdm: ICDMData) -> Attribute.AsyncReadTran def run_tests(pics, label, test_cases, test_name): - test_runner = MockTestRunner( - label, label, test_name, 0, pics) + test_runner = MockTestRunner(Path(__file__).parent / f"../{label}.py", + label, test_name, 0, pics) failures = [] for idx, t in enumerate(test_cases): ok = test_runner.run_test_with_mock_read( diff --git a/src/python_testing/test_testing/test_IDM_10_4.py b/src/python_testing/test_testing/test_IDM_10_4.py index 8a30609e94e..f7e93e9f47e 100644 --- a/src/python_testing/test_testing/test_IDM_10_4.py +++ b/src/python_testing/test_testing/test_IDM_10_4.py @@ -16,13 +16,13 @@ # limitations under the License. # -import os import sys +from pathlib import Path import chip.clusters as Clusters from chip.clusters import Attribute from chip.testing.pics import parse_pics_xml -from MockTestRunner import MockTestRunner +from chip.testing.runner import MockTestRunner # Reachable attribute is off in the pics file # MaxPathsPerInvoke is not include in the pics file @@ -80,10 +80,12 @@ def create_read(include_reachable: bool = False, include_max_paths: bool = False def main(): # TODO: add the same test for commands and features - script_dir = os.path.dirname(os.path.realpath(__file__)) - with open(f'{script_dir}/example_pics_xml_basic_info.xml') as f: + + script_dir = Path(__file__).resolve().parent + with open(script_dir / 'example_pics_xml_basic_info.xml') as f: pics = parse_pics_xml(f.read()) - test_runner = MockTestRunner('TC_pics_checker.py', 'TC_PICS_Checker', 'test_TC_IDM_10_4', 0, pics) + test_runner = MockTestRunner(script_dir / '../TC_pics_checker.py', + 'TC_PICS_Checker', 'test_TC_IDM_10_4', 0, pics) failures = [] # Success, include vendor ID, which IS in the pics file, and neither of the incorrect ones diff --git a/src/python_testing/test_testing/test_TC_CCNTL_2_2.py b/src/python_testing/test_testing/test_TC_CCNTL_2_2.py index 7fd7390ab6f..7162bca33f0 100644 --- a/src/python_testing/test_testing/test_TC_CCNTL_2_2.py +++ b/src/python_testing/test_testing/test_TC_CCNTL_2_2.py @@ -19,16 +19,16 @@ import asyncio import base64 import os -import pathlib import sys import typing +from pathlib import Path import chip.clusters as Clusters import click from chip import ChipDeviceCtrl from chip.clusters import Attribute from chip.interaction_model import InteractionModelError, Status -from MockTestRunner import AsyncMock, MockTestRunner +from chip.testing.runner import AsyncMock, MockTestRunner try: from chip.testing.matter_testing import MatterTestConfig, get_default_paa_trust_store, run_tests_no_exit @@ -175,13 +175,14 @@ def run_test_with_mock(self, dynamic_invoke_return: typing.Callable, dynamic_eve @click.command() @click.argument('th_server_app', type=click.Path(exists=True)) def main(th_server_app: str): - root = os.path.abspath(os.path.join(pathlib.Path(__file__).resolve().parent, '..', '..', '..')) + root = os.path.abspath(os.path.join(Path(__file__).resolve().parent, '..', '..', '..')) print(f'root = {root}') paa_path = get_default_paa_trust_store(root) print(f'paa = {paa_path}') pics = {"PICS_SDK_CI_ONLY": True} - test_runner = MyMock('TC_CCTRL_2_2', 'TC_CCTRL_2_2', 'test_TC_CCTRL_2_2', paa_trust_store_path=paa_path, pics=pics) + test_runner = MyMock(Path(__file__).parent / '../TC_CCTRL_2_2.py', + 'TC_CCTRL_2_2', 'test_TC_CCTRL_2_2', paa_trust_store_path=paa_path, pics=pics) config = MatterTestConfig() config.global_test_params = {'th_server_app_path': th_server_app} test_runner.set_test_config(config) diff --git a/src/python_testing/test_testing/test_TC_DGGEN_3_2.py b/src/python_testing/test_testing/test_TC_DGGEN_3_2.py index 34b80514035..b35480cf943 100644 --- a/src/python_testing/test_testing/test_TC_DGGEN_3_2.py +++ b/src/python_testing/test_testing/test_TC_DGGEN_3_2.py @@ -18,10 +18,11 @@ import sys from dataclasses import dataclass +from pathlib import Path import chip.clusters as Clusters from chip.clusters import Attribute -from MockTestRunner import MockTestRunner +from chip.testing.runner import MockTestRunner @dataclass @@ -51,7 +52,8 @@ def test_spec_to_attribute_cache(test_spec: TestSpec) -> Attribute.AsyncReadTran def main(): - test_runner = MockTestRunner('TC_DGGEN_3_2', 'TC_DGGEN_3_2', 'test_TC_DGGEN_3_2', 0) + test_runner = MockTestRunner(Path(__file__).parent / '../TC_DGGEN_3_2.py', + 'TC_DGGEN_3_2', 'test_TC_DGGEN_3_2', 0) failures = [] for idx, t in enumerate(TEST_CASES): ok = test_runner.run_test_with_mock_read(test_spec_to_attribute_cache(t)) == t.expect_pass diff --git a/src/python_testing/test_testing/test_TC_MCORE_FS_1_1.py b/src/python_testing/test_testing/test_TC_MCORE_FS_1_1.py index 0cf6b033b2f..225617cca73 100644 --- a/src/python_testing/test_testing/test_TC_MCORE_FS_1_1.py +++ b/src/python_testing/test_testing/test_TC_MCORE_FS_1_1.py @@ -19,16 +19,16 @@ import asyncio import base64 import os -import pathlib import sys import typing +from pathlib import Path import chip.clusters as Clusters import click from chip import ChipDeviceCtrl from chip.clusters import Attribute from chip.interaction_model import InteractionModelError, Status -from MockTestRunner import AsyncMock, MockTestRunner +from chip.testing.runner import AsyncMock, MockTestRunner try: from chip.testing.matter_testing import MatterTestConfig, get_default_paa_trust_store, run_tests_no_exit @@ -146,13 +146,14 @@ def run_test_with_mock(self, dynamic_invoke_return: typing.Callable, dynamic_eve @click.command() @click.argument('th_server_app', type=click.Path(exists=True)) def main(th_server_app: str): - root = os.path.abspath(os.path.join(pathlib.Path(__file__).resolve().parent, '..', '..', '..')) + root = os.path.abspath(os.path.join(Path(__file__).resolve().parent, '..', '..', '..')) print(f'root = {root}') paa_path = get_default_paa_trust_store(root) print(f'paa = {paa_path}') pics = {"PICS_SDK_CI_ONLY": True} - test_runner = MyMock('TC_MCORE_FS_1_1', 'TC_MCORE_FS_1_1', 'test_TC_MCORE_FS_1_1', paa_trust_store_path=paa_path, pics=pics) + test_runner = MyMock(Path(__file__).parent / '../TC_MCORE_FS_1_1.py', + 'TC_MCORE_FS_1_1', 'test_TC_MCORE_FS_1_1', paa_trust_store_path=paa_path, pics=pics) config = MatterTestConfig() config.user_params = {'th_server_app_path': th_server_app} test_runner.set_test_config(config) diff --git a/src/python_testing/test_testing/test_TC_SC_7_1.py b/src/python_testing/test_testing/test_TC_SC_7_1.py index 96dced72f95..5f82a675987 100644 --- a/src/python_testing/test_testing/test_TC_SC_7_1.py +++ b/src/python_testing/test_testing/test_TC_SC_7_1.py @@ -17,12 +17,13 @@ # import sys +from pathlib import Path from random import randbytes import chip.clusters as Clusters from chip.clusters import Attribute from chip.testing.matter_testing import MatterTestConfig -from MockTestRunner import MockTestRunner +from chip.testing.runner import MockTestRunner def read_trusted_root(filled: bool) -> Attribute.AsyncReadTransaction.ReadResponse: @@ -43,7 +44,8 @@ def main(): manual_3333_20202021 = '31693312339' manual_2222_20202024 = '20055212333' - test_runner = MockTestRunner('TC_SC_7_1', 'TC_SC_7_1', 'test_TC_SC_7_1', 0) + test_runner = MockTestRunner(Path(__file__).parent / '../TC_SC_7_1.py', + 'TC_SC_7_1', 'test_TC_SC_7_1', 0) failures = [] # Tests with no code specified should fail diff --git a/src/python_testing/test_testing/test_TC_TMP_2_1.py b/src/python_testing/test_testing/test_TC_TMP_2_1.py index fe888a4896c..135cf7b9186 100644 --- a/src/python_testing/test_testing/test_TC_TMP_2_1.py +++ b/src/python_testing/test_testing/test_TC_TMP_2_1.py @@ -19,11 +19,12 @@ import sys import typing from dataclasses import dataclass +from pathlib import Path import chip.clusters as Clusters from chip.clusters import Attribute from chip.clusters.Types import NullValue -from MockTestRunner import MockTestRunner +from chip.testing.runner import MockTestRunner @dataclass @@ -160,7 +161,8 @@ def test_spec_to_attribute_cache(test_spec: TestSpec) -> Attribute.AsyncReadTran def main(): - test_runner = MockTestRunner('TC_TMP_2_1', 'TC_TMP_2_1', 'test_TC_TMP_2_1', 1) + test_runner = MockTestRunner(Path(__file__).parent / '../TC_TMP_2_1.py', + 'TC_TMP_2_1', 'test_TC_TMP_2_1', 1) failures = [] for idx, t in enumerate(TEST_CASES): ok = test_runner.run_test_with_mock_read(test_spec_to_attribute_cache(t)) == t.expect_pass diff --git a/src/test_driver/efr32/BUILD.gn b/src/test_driver/efr32/BUILD.gn index dd1969a5acd..1ff7a3a505c 100644 --- a/src/test_driver/efr32/BUILD.gn +++ b/src/test_driver/efr32/BUILD.gn @@ -18,6 +18,7 @@ import("//build_overrides/efr32_sdk.gni") import("//build_overrides/pigweed.gni") import("${build_root}/config/defaults.gni") +import("${chip_root}/src/platform/silabs/provision/args.gni") import("${silabs_sdk_build_root}/efr32_sdk.gni") import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") @@ -94,8 +95,8 @@ source_set("efr32_test_main") { "${chip_root}/examples/common/pigweed:system_rpc_server", "${chip_root}/src/lib", "${chip_root}/src/lib/support:pw_tests_wrapper", - "${chip_root}/src/platform/silabs/provision:provision-headers", "${examples_common_plat_dir}/pw_sys_io:pw_sys_io_silabs", + "${sl_provision_root}:provision-headers", ] # OpenThread Settings diff --git a/src/test_driver/efr32/README.md b/src/test_driver/efr32/README.md index c846426890a..877b7df6dcd 100644 --- a/src/test_driver/efr32/README.md +++ b/src/test_driver/efr32/README.md @@ -1,30 +1,17 @@ -#CHIP EFR32 Test Driver +This is a test driver for the Matter unit tests to be run on an EFR32 device. It +builds a set of test binaries, each of which contains one directory of unit +tests that can be flashed onto a device and run. The device is controlled using +the included RPCs through the python test runner. -This builds and runs the unit tests on the efr32 device. +- [Prerequisites](#prerequisites) +- [Building and Running the Unit Tests](#building-and-running-the-unit-tests) +- [Building and Running the Unit Tests with the build_examples Script](#building-and-running-the-unit-tests-with-the-build_examples-script) -
- -- [Introduction](#introduction) -- [Building](#building) -- [Running The Tests](#running-the-tests) - -
- - - -## Introduction - -This builds a set of test binaries which contain the unit tests and can be -flashed onto a device. The device is controlled using the included RPCs, through -the python test runner. - - - -## Building +## Prerequisites - Download the [Simplicity Commander](https://www.silabs.com/mcu/programming-options) - command line tool, and ensure that `commander` is your shell search path. + command line tool, and ensure that `commander` is in your shell search path. (For Mac OS X, `commander` is located inside `Commander.app/Contents/MacOS/`.) @@ -32,11 +19,11 @@ the python test runner. bootstrap already installs the toolchain): [GNU Arm Embedded Toolchain 12.2 Rel1](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) -- Install some additional tools(likely already present for CHIP developers): +- Install some additional tools: -#Linux `sudo apt-get install git libwebkitgtk-1.0-0 ninja-build` + For Linux: `sudo apt-get install git libwebkitgtk-1.0-0 ninja-build` -#Mac OS X `brew install ninja` + For Mac OS: `brew install ninja` - Supported hardware: @@ -49,60 +36,49 @@ the python test runner. - BRD4187A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm - BRD4187C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm -OR use GN/Ninja directly +## Building and Running the Unit Tests - ``` - cd ~/connectedhomeip/src/test_driver/efr32/ - git submodule update --init - source third_party/connectedhomeip/scripts/activate.sh - export SILABS_BOARD=BRD4187C - gn gen out/debug - ninja -C out/debug - ``` +### Building the Unit Tests -- To delete generated executable, libraries and object files use: +The unit tests can be built using gn and ninja. Set the appropriate board name. - ``` - cd ~/connectedhomeip/src/test_driver/efr32/ - rm -rf out/ - ``` - - + source scripts/activate.sh + cd src/test_driver/efr32 + export SILABS_BOARD=BRD2703A + gn gen out/debug + ninja -C out/debug -## Running The Tests +### Building and Installing the Runner -Build the runner using gn: +The python wheels for the runner can be built and installed like this: - ``` - cd /src/test_driver/efr32 - gn gen out/debug ninja -C out/debug runner - ``` + pip3 install out/debug/chip_pw_test_runner_wheels/*.whl --force-reinstall + +### Running the Unit Tests + +The unit tests can be run using the test runner python script: + + python -m py.pw_test_runner.pw_test_runner -d /dev/ttyACM1 -f out/debug/tests -o out.log + +## Building and Running the Unit Tests with the build_examples Script -Or build using build script from the root +### Building the Unit Tests - ``` - cd - ./scripts/build/build_examples.py --target linux-x64-pw-test-runner build - ``` +The unit tests can be built using the build_examples script. Set the appropriate +board name. -The runner will be installed into the venv and python wheels will be packaged in -the output folder for deploying. + scripts/build/build_examples.py --target efr32-brd2703a-unit-test build -Then the python wheels need to installed using pip3. +### Building and Installing the Runner - ``` - pip3 install out/debug/chip_pw_test_runner_wheels/*.whl - ``` +The python wheels for the runner can be built and installed like this: -Other python libraries may need to be installed such as + scripts/build/build_examples.py --target linux-x64-efr32-test-runner build + pip3 install out/linux-x64-efr32-test-runner/chip_pw_test_runner_wheels/*.whl --force-reinstall - ``` - pip3 install pyserial - ``` +### Running the Unit Tests -- To run all tests: +The unit tests can be run using the test runner python script: - ``` - python -m pw_test_runner.pw_test_runner -d /dev/ttyACM1 -f out/debug/matter-silabs-device_tests.s37 -o out.log - ``` + python -m src.test_driver.efr32.py.pw_test_runner.pw_test_runner -d /dev/ttyACM1 -f out/efr32-brd2703a-unit-test/tests -o out.log diff --git a/src/test_driver/efr32/py/pw_test_runner/pw_test_runner.py b/src/test_driver/efr32/py/pw_test_runner/pw_test_runner.py index 8e10903920c..ed8c43f0190 100644 --- a/src/test_driver/efr32/py/pw_test_runner/pw_test_runner.py +++ b/src/test_driver/efr32/py/pw_test_runner/pw_test_runner.py @@ -105,8 +105,7 @@ def run(args) -> int: def list_images(flash_directory: str) -> list[str]: - filenames: list[str] = glob.glob(os.path.join(flash_directory, "*.s37")) - return list(map(lambda x: os.path.join(flash_directory, x), filenames)) + return glob.glob(os.path.join(flash_directory, "*.s37")) def main() -> int: diff --git a/src/transport/raw/tests/TestTCP.cpp b/src/transport/raw/tests/TestTCP.cpp index 130c99d9114..213eb6c757e 100644 --- a/src/transport/raw/tests/TestTCP.cpp +++ b/src/transport/raw/tests/TestTCP.cpp @@ -53,7 +53,6 @@ namespace { constexpr size_t kMaxTcpActiveConnectionCount = 4; constexpr size_t kMaxTcpPendingPackets = 4; constexpr size_t kPacketSizeBytes = sizeof(uint32_t); -uint16_t gChipTCPPort = static_cast(CHIP_PORT + chip::Crypto::GetRandU16() % 100); chip::Transport::AppTCPConnectionCallbackCtxt gAppTCPConnCbCtxt; chip::Transport::ActiveTCPConnectionState * gActiveTCPConnState = nullptr; @@ -67,6 +66,11 @@ constexpr uint32_t kMessageCounter = 18; const char PAYLOAD[] = "Hello!"; const char messageSize_TEST[] = "\x00\x00\x00\x00"; +uint16_t GetRandomPort() +{ + return static_cast(CHIP_PORT + chip::Crypto::GetRandU16() % 100); +} + class MockTransportMgrDelegate : public chip::TransportMgrDelegate { public: @@ -135,11 +139,10 @@ class MockTransportMgrDelegate : public chip::TransportMgrDelegate } } - void InitializeMessageTest(TCPImpl & tcp, const IPAddress & addr) + void InitializeMessageTest(TCPImpl & tcp, const IPAddress & addr, uint16_t port) { - CHIP_ERROR err = tcp.Init(Transport::TcpListenParameters(mIOContext->GetTCPEndPointManager()) - .SetAddressType(addr.Type()) - .SetListenPort(gChipTCPPort)); + CHIP_ERROR err = tcp.Init( + Transport::TcpListenParameters(mIOContext->GetTCPEndPointManager()).SetAddressType(addr.Type()).SetListenPort(port)); // retry a few times in case the port is somehow in use. // this is a WORKAROUND for flaky testing if we run tests very fast after each other. @@ -160,7 +163,7 @@ class MockTransportMgrDelegate : public chip::TransportMgrDelegate chip::test_utils::SleepMillis(100); err = tcp.Init(Transport::TcpListenParameters(mIOContext->GetTCPEndPointManager()) .SetAddressType(addr.Type()) - .SetListenPort(gChipTCPPort)); + .SetListenPort(port)); } EXPECT_EQ(err, CHIP_NO_ERROR); @@ -178,7 +181,7 @@ class MockTransportMgrDelegate : public chip::TransportMgrDelegate gAppTCPConnCbCtxt.connClosedCb = nullptr; } - void SingleMessageTest(TCPImpl & tcp, const IPAddress & addr) + void SingleMessageTest(TCPImpl & tcp, const IPAddress & addr, uint16_t port) { chip::System::PacketBufferHandle buffer = chip::System::PacketBufferHandle::NewWithData(PAYLOAD, sizeof(PAYLOAD)); ASSERT_FALSE(buffer.IsNull()); @@ -193,7 +196,7 @@ class MockTransportMgrDelegate : public chip::TransportMgrDelegate EXPECT_EQ(err, CHIP_NO_ERROR); // Should be able to send a message to itself by just calling send. - err = tcp.SendMessage(Transport::PeerAddress::TCP(addr, gChipTCPPort), std::move(buffer)); + err = tcp.SendMessage(Transport::PeerAddress::TCP(addr, port), std::move(buffer)); EXPECT_EQ(err, CHIP_NO_ERROR); mIOContext->DriveIOUntil(chip::System::Clock::Seconds16(5), [this]() { return mReceiveHandlerCallCount != 0; }); @@ -202,38 +205,38 @@ class MockTransportMgrDelegate : public chip::TransportMgrDelegate SetCallback(nullptr); } - void ConnectTest(TCPImpl & tcp, const IPAddress & addr) + void ConnectTest(TCPImpl & tcp, const IPAddress & addr, uint16_t port) { // Connect and wait for seeing active connection - CHIP_ERROR err = tcp.TCPConnect(Transport::PeerAddress::TCP(addr, gChipTCPPort), &gAppTCPConnCbCtxt, &gActiveTCPConnState); + CHIP_ERROR err = tcp.TCPConnect(Transport::PeerAddress::TCP(addr, port), &gAppTCPConnCbCtxt, &gActiveTCPConnState); EXPECT_EQ(err, CHIP_NO_ERROR); mIOContext->DriveIOUntil(chip::System::Clock::Seconds16(5), [&tcp]() { return tcp.HasActiveConnections(); }); EXPECT_EQ(tcp.HasActiveConnections(), true); } - void HandleConnectCompleteCbCalledTest(TCPImpl & tcp, const IPAddress & addr) + void HandleConnectCompleteCbCalledTest(TCPImpl & tcp, const IPAddress & addr, uint16_t port) { // Connect and wait for seeing active connection and connection complete // handler being called. - CHIP_ERROR err = tcp.TCPConnect(Transport::PeerAddress::TCP(addr, gChipTCPPort), &gAppTCPConnCbCtxt, &gActiveTCPConnState); + CHIP_ERROR err = tcp.TCPConnect(Transport::PeerAddress::TCP(addr, port), &gAppTCPConnCbCtxt, &gActiveTCPConnState); EXPECT_EQ(err, CHIP_NO_ERROR); mIOContext->DriveIOUntil(chip::System::Clock::Seconds16(5), [this]() { return mHandleConnectionCompleteCalled; }); EXPECT_EQ(mHandleConnectionCompleteCalled, true); } - void HandleConnectCloseCbCalledTest(TCPImpl & tcp, const IPAddress & addr) + void HandleConnectCloseCbCalledTest(TCPImpl & tcp, const IPAddress & addr, uint16_t port) { // Connect and wait for seeing active connection and connection complete // handler being called. - CHIP_ERROR err = tcp.TCPConnect(Transport::PeerAddress::TCP(addr, gChipTCPPort), &gAppTCPConnCbCtxt, &gActiveTCPConnState); + CHIP_ERROR err = tcp.TCPConnect(Transport::PeerAddress::TCP(addr, port), &gAppTCPConnCbCtxt, &gActiveTCPConnState); EXPECT_EQ(err, CHIP_NO_ERROR); mIOContext->DriveIOUntil(chip::System::Clock::Seconds16(5), [this]() { return mHandleConnectionCompleteCalled; }); EXPECT_EQ(mHandleConnectionCompleteCalled, true); - tcp.TCPDisconnect(Transport::PeerAddress::TCP(addr, gChipTCPPort)); + tcp.TCPDisconnect(Transport::PeerAddress::TCP(addr, port)); mIOContext->DriveIOUntil(chip::System::Clock::Seconds16(5), [&tcp]() { return !tcp.HasActiveConnections(); }); EXPECT_EQ(mHandleConnectionCloseCalled, true); } @@ -246,10 +249,10 @@ class MockTransportMgrDelegate : public chip::TransportMgrDelegate EXPECT_EQ(tcp.HasActiveConnections(), false); } - void DisconnectTest(TCPImpl & tcp, const IPAddress & addr) + void DisconnectTest(TCPImpl & tcp, const IPAddress & addr, uint16_t port) { // Disconnect and wait for seeing peer close - tcp.TCPDisconnect(Transport::PeerAddress::TCP(addr, gChipTCPPort)); + tcp.TCPDisconnect(Transport::PeerAddress::TCP(addr, port)); mIOContext->DriveIOUntil(chip::System::Clock::Seconds16(5), [&tcp]() { return !tcp.HasActiveConnections(); }); EXPECT_EQ(tcp.HasActiveConnections(), false); } @@ -449,8 +452,9 @@ class TestTCP : public ::testing::Test { TCPImpl tcp; - CHIP_ERROR err = tcp.Init( - Transport::TcpListenParameters(mIOContext->GetTCPEndPointManager()).SetAddressType(type).SetListenPort(gChipTCPPort)); + uint16_t port = GetRandomPort(); + CHIP_ERROR err = + tcp.Init(Transport::TcpListenParameters(mIOContext->GetTCPEndPointManager()).SetAddressType(type).SetListenPort(port)); EXPECT_EQ(err, CHIP_NO_ERROR); } @@ -460,51 +464,56 @@ class TestTCP : public ::testing::Test { TCPImpl tcp; + uint16_t port = GetRandomPort(); MockTransportMgrDelegate gMockTransportMgrDelegate(mIOContext); - gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr); - gMockTransportMgrDelegate.SingleMessageTest(tcp, addr); - gMockTransportMgrDelegate.DisconnectTest(tcp, addr); + gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr, port); + gMockTransportMgrDelegate.SingleMessageTest(tcp, addr, port); + gMockTransportMgrDelegate.DisconnectTest(tcp, addr, port); } void ConnectToSelfTest(const IPAddress & addr) { TCPImpl tcp; + uint16_t port = GetRandomPort(); MockTransportMgrDelegate gMockTransportMgrDelegate(mIOContext); - gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr); - gMockTransportMgrDelegate.ConnectTest(tcp, addr); - gMockTransportMgrDelegate.DisconnectTest(tcp, addr); + gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr, port); + gMockTransportMgrDelegate.ConnectTest(tcp, addr, port); + gMockTransportMgrDelegate.DisconnectTest(tcp, addr, port); } void ConnectSendMessageThenCloseTest(const IPAddress & addr) { TCPImpl tcp; + uint16_t port = GetRandomPort(); MockTransportMgrDelegate gMockTransportMgrDelegate(mIOContext); - gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr); - gMockTransportMgrDelegate.ConnectTest(tcp, addr); - gMockTransportMgrDelegate.SingleMessageTest(tcp, addr); - gMockTransportMgrDelegate.DisconnectTest(tcp, addr); + gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr, port); + gMockTransportMgrDelegate.ConnectTest(tcp, addr, port); + gMockTransportMgrDelegate.SingleMessageTest(tcp, addr, port); + gMockTransportMgrDelegate.DisconnectTest(tcp, addr, port); } void HandleConnCompleteTest(const IPAddress & addr) { TCPImpl tcp; + uint16_t port = GetRandomPort(); MockTransportMgrDelegate gMockTransportMgrDelegate(mIOContext); - gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr); - gMockTransportMgrDelegate.HandleConnectCompleteCbCalledTest(tcp, addr); - gMockTransportMgrDelegate.DisconnectTest(tcp, addr); + gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr, port); + gMockTransportMgrDelegate.HandleConnectCompleteCbCalledTest(tcp, addr, port); + gMockTransportMgrDelegate.DisconnectTest(tcp, addr, port); } void HandleConnCloseTest(const IPAddress & addr) { TCPImpl tcp; + uint16_t port = GetRandomPort(); MockTransportMgrDelegate gMockTransportMgrDelegate(mIOContext); - gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr); - gMockTransportMgrDelegate.HandleConnectCloseCbCalledTest(tcp, addr); - gMockTransportMgrDelegate.DisconnectTest(tcp, addr); + gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr, port); + gMockTransportMgrDelegate.HandleConnectCloseCbCalledTest(tcp, addr, port); + gMockTransportMgrDelegate.DisconnectTest(tcp, addr, port); } // Callback used by CheckProcessReceivedBuffer. @@ -556,8 +565,9 @@ TEST_F(TestTCP, InitializeAsTCPClient) { TCPImpl tcp; auto tcpListenParams = Transport::TcpListenParameters(mIOContext->GetTCPEndPointManager()); + uint16_t port = GetRandomPort(); CHIP_ERROR err = - tcp.Init(tcpListenParams.SetAddressType(IPAddressType::kIPv6).SetListenPort(gChipTCPPort).SetServerListenEnabled(false)); + tcp.Init(tcpListenParams.SetAddressType(IPAddressType::kIPv6).SetListenPort(port).SetServerListenEnabled(false)); EXPECT_EQ(err, CHIP_NO_ERROR); @@ -570,7 +580,8 @@ TEST_F(TestTCP, InitializeAsTCPClientServer) TCPImpl tcp; auto tcpListenParams = Transport::TcpListenParameters(mIOContext->GetTCPEndPointManager()); - CHIP_ERROR err = tcp.Init(tcpListenParams.SetAddressType(IPAddressType::kIPv6).SetListenPort(gChipTCPPort)); + uint16_t port = GetRandomPort(); + CHIP_ERROR err = tcp.Init(tcpListenParams.SetAddressType(IPAddressType::kIPv6).SetListenPort(port)); EXPECT_EQ(err, CHIP_NO_ERROR); @@ -643,11 +654,12 @@ TEST_F(TestTCP, CheckTCPEndpointAfterCloseTest) IPAddress addr; IPAddress::FromString("::1", addr); + uint16_t port = GetRandomPort(); MockTransportMgrDelegate gMockTransportMgrDelegate(mIOContext); - gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr); - gMockTransportMgrDelegate.ConnectTest(tcp, addr); + gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr, port); + gMockTransportMgrDelegate.ConnectTest(tcp, addr, port); - Transport::PeerAddress lPeerAddress = Transport::PeerAddress::TCP(addr, gChipTCPPort); + Transport::PeerAddress lPeerAddress = Transport::PeerAddress::TCP(addr, port); void * state = TestAccess::FindActiveConnection(tcp, lPeerAddress); ASSERT_NE(state, nullptr); TCPEndPoint * lEndPoint = TestAccess::GetEndpoint(state); @@ -666,14 +678,15 @@ TEST_F(TestTCP, CheckProcessReceivedBuffer) IPAddress addr; IPAddress::FromString("::1", addr); + uint16_t port = GetRandomPort(); MockTransportMgrDelegate gMockTransportMgrDelegate(mIOContext); - gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr); + gMockTransportMgrDelegate.InitializeMessageTest(tcp, addr, port); // Send a packet to get TCP going, so that we can find a TCPEndPoint to pass to ProcessReceivedBuffer. // (The current TCPEndPoint implementation is not effectively mockable.) - gMockTransportMgrDelegate.SingleMessageTest(tcp, addr); + gMockTransportMgrDelegate.SingleMessageTest(tcp, addr, port); - Transport::PeerAddress lPeerAddress = Transport::PeerAddress::TCP(addr, gChipTCPPort); + Transport::PeerAddress lPeerAddress = Transport::PeerAddress::TCP(addr, port); void * state = TestAccess::FindActiveConnection(tcp, lPeerAddress); ASSERT_NE(state, nullptr); TCPEndPoint * lEndPoint = TestAccess::GetEndpoint(state); diff --git a/third_party/openthread/platforms/nxp/mcxw71_k32w1/BUILD.gn b/third_party/openthread/platforms/nxp/mcxw71_k32w1/BUILD.gn index 2050591b8f4..2bbfc4fae4b 100644 --- a/third_party/openthread/platforms/nxp/mcxw71_k32w1/BUILD.gn +++ b/third_party/openthread/platforms/nxp/mcxw71_k32w1/BUILD.gn @@ -24,7 +24,10 @@ import("${nxp_sdk_build_root}/nxp_sdk.gni") openthread_nxp_root = "${chip_root}/third_party/openthread/ot-nxp" config("openthread_k32w1_config") { - include_dirs = [ "${openthread_nxp_root}/src/k32w1" ] + include_dirs = [ + "${openthread_nxp_root}/src/k32w1", + "${openthread_nxp_root}/src/mcxw71", + ] defines = [ "OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE=1", @@ -111,10 +114,9 @@ source_set("libopenthread-k32w1") { "../..:libopenthread-platform-utils", ] + public_deps += [ "${nxp_sdk_build_root}:nxp_mbedtls" ] + if (!nxp_external_sdk) { - public_deps += [ - "${nxp_sdk_build_root}:nxp_mbedtls", - nxp_sdk_target, - ] + public_deps += [ nxp_sdk_target ] } } diff --git a/third_party/silabs/matter_support b/third_party/silabs/matter_support index 8c594d45be3..7ad2bdc6c2c 160000 --- a/third_party/silabs/matter_support +++ b/third_party/silabs/matter_support @@ -1 +1 @@ -Subproject commit 8c594d45be3a3bc1bad4f3de0e3bfc16bd3cab56 +Subproject commit 7ad2bdc6c2c100ee6775b76b6cd055c40d29e117 diff --git a/third_party/silabs/silabs_board.gni b/third_party/silabs/silabs_board.gni index d013e967b61..6385a880a47 100644 --- a/third_party/silabs/silabs_board.gni +++ b/third_party/silabs/silabs_board.gni @@ -69,6 +69,13 @@ declare_args() { chip_enable_ble_rs911x = use_rs9116 || use_SiWx917 } +# TODO - This needs to be removed once multiplexing issues resolved +if (use_SiWx917) { + disable_lcd = true + show_qr_code = false + use_external_flash = false +} + if (silabs_board == "") { silabs_board = getenv("SILABS_BOARD") } diff --git a/third_party/tizen/tizen_qemu.py b/third_party/tizen/tizen_qemu.py index 7b7eb412a23..78c840b404a 100755 --- a/third_party/tizen/tizen_qemu.py +++ b/third_party/tizen/tizen_qemu.py @@ -69,7 +69,8 @@ help=("host directory to share with the guest")) parser.add_argument( '--runner', type=str, - help=("path to the runner script which will run automatically after boot. path should be relative to shared directory")) + help=("path to the runner script which will be executed after boot; " + "it should be relative to the shared directory")) parser.add_argument( '--output', metavar='FILE', default="/dev/null", help="store the QEMU output in a FILE") diff --git a/zzz_generated/app-common/app-common/zap-generated/callback.h b/zzz_generated/app-common/app-common/zap-generated/callback.h index 871d52be3f0..480a2c3434c 100644 --- a/zzz_generated/app-common/app-common/zap-generated/callback.h +++ b/zzz_generated/app-common/app-common/zap-generated/callback.h @@ -5964,12 +5964,6 @@ bool emberAfDiagnosticLogsClusterRetrieveLogsRequestCallback( bool emberAfThreadNetworkDiagnosticsClusterResetCountsCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::ThreadNetworkDiagnostics::Commands::ResetCounts::DecodableType & commandData); -/** - * @brief Wi-Fi Network Diagnostics Cluster ResetCounts Command callback (from client) - */ -bool emberAfWiFiNetworkDiagnosticsClusterResetCountsCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WiFiNetworkDiagnostics::Commands::ResetCounts::DecodableType & commandData); /** * @brief Ethernet Network Diagnostics Cluster ResetCounts Command callback (from client) */