diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 03e3c76c625b7d..88588363d03ad4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -333,7 +333,7 @@ jobs: scripts/run_in_build_env.sh 'virtualenv pyenv' source pyenv/bin/activate pip3 install -r src/setup_payload/python/requirements.txt - python3 src/setup_payload/tests/run_python_setup_payload_gen_test.py out/chip-tool + python3 src/setup_payload/tests/run_python_setup_payload_test.py out/chip-tool build_linux_python_lighting_device: name: Build on Linux (python lighting-app) diff --git a/.github/workflows/darwin.yaml b/.github/workflows/darwin.yaml index ad2a513209cc65..ba9c0f1d9f02c3 100644 --- a/.github/workflows/darwin.yaml +++ b/.github/workflows/darwin.yaml @@ -97,10 +97,10 @@ jobs: bootstrap-logs-framework-${{ matrix.options.flavor }} - name: Build example All Clusters Server run: | - scripts/examples/gn_build_example.sh examples/all-clusters-app/linux out/debug chip_config_network_layer_ble=false + scripts/examples/gn_build_example.sh examples/all-clusters-app/linux out/debug/all-clusters-app chip_config_network_layer_ble=false - name: Build example OTA Provider run: | - scripts/examples/gn_build_example.sh examples/ota-provider-app/linux out/debug chip_config_network_layer_ble=false + scripts/examples/gn_build_example.sh examples/ota-provider-app/linux out/debug/ota-provider-app chip_config_network_layer_ble=false - name: Build example OTA Requestor run: | scripts/examples/gn_build_example.sh examples/ota-requestor-app/linux out/debug/ota-requestor-app chip_config_network_layer_ble=false non_spec_compliant_ota_action_delay_floor=0 @@ -113,13 +113,8 @@ jobs: run: | mkdir -p /tmp/darwin/framework-tests echo "This is a simple log" > /tmp/darwin/framework-tests/end_user_support_log.txt - ../../../out/debug/chip-all-clusters-app --interface-id -1 --end_user_support_log /tmp/darwin/framework-tests/end_user_support_log.txt > >(tee /tmp/darwin/framework-tests/all-cluster-app.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-err.log >&2) & - ../../../out/debug/chip-all-clusters-app --interface-id -1 --dac_provider ../../../credentials/development/commissioner_dut/struct_cd_origin_pid_vid_correct/test_case_vector.json --product-id 32768 --discriminator 3839 --secured-device-port 5539 --KVS /tmp/chip-all-clusters-app-kvs2 > >(tee /tmp/darwin/framework-tests/all-cluster-app-origin-vid.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-origin-vid-err.log >&2) & - ../../../out/debug/chip-all-clusters-app --interface-id -1 --discriminator 101 --passcode 1001 --KVS /tmp/chip-all-clusters-app-kvs101 --secured-device-port 5531 & - ../../../out/debug/chip-all-clusters-app --interface-id -1 --discriminator 102 --passcode 1002 --KVS /tmp/chip-all-clusters-app-kvs102 --secured-device-port 5532 & - ../../../out/debug/chip-all-clusters-app --interface-id -1 --discriminator 103 --passcode 1003 --KVS /tmp/chip-all-clusters-app-kvs103 --secured-device-port 5533 & - ../../../out/debug/chip-all-clusters-app --interface-id -1 --discriminator 104 --passcode 1004 --KVS /tmp/chip-all-clusters-app-kvs104 --secured-device-port 5534 & - ../../../out/debug/chip-all-clusters-app --interface-id -1 --discriminator 105 --passcode 1005 --KVS /tmp/chip-all-clusters-app-kvs105 --secured-device-port 5535 & + ../../../out/debug/all-clusters-app/chip-all-clusters-app --interface-id -1 --end_user_support_log /tmp/darwin/framework-tests/end_user_support_log.txt > >(tee /tmp/darwin/framework-tests/all-cluster-app.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-err.log >&2) & + ../../../out/debug/all-clusters-app/chip-all-clusters-app --interface-id -1 --dac_provider ../../../credentials/development/commissioner_dut/struct_cd_origin_pid_vid_correct/test_case_vector.json --product-id 32768 --discriminator 3839 --secured-device-port 5539 --KVS /tmp/chip-all-clusters-app-kvs2 > >(tee /tmp/darwin/framework-tests/all-cluster-app-origin-vid.log) 2> >(tee /tmp/darwin/framework-tests/all-cluster-app-origin-vid-err.log >&2) & export TEST_RUNNER_ASAN_OPTIONS=__CURRENT_VALUE__:detect_stack_use_after_return=1 diff --git a/.github/workflows/examples-linux-tv-casting-app.yaml b/.github/workflows/examples-linux-tv-casting-app.yaml index 679f4be57a35db..dfae4ee568840c 100644 --- a/.github/workflows/examples-linux-tv-casting-app.yaml +++ b/.github/workflows/examples-linux-tv-casting-app.yaml @@ -67,7 +67,7 @@ jobs: run: | ./scripts/run_in_build_env.sh \ "python3 ./scripts/tests/run_tv_casting_test.py" - timeout-minutes: 1 + timeout-minutes: 2 # Comment this out to debug if GitHub Action times out. - name: Uploading Size Reports uses: ./.github/actions/upload-size-reports diff --git a/.github/workflows/examples-qpg.yaml b/.github/workflows/examples-qpg.yaml index 2448a0f8cda8fc..bb800eb1016624 100644 --- a/.github/workflows/examples-qpg.yaml +++ b/.github/workflows/examples-qpg.yaml @@ -63,8 +63,6 @@ jobs: --enable-flashbundle \ --target qpg-qpg6105-lock \ --target qpg-qpg6105-light \ - --target qpg-qpg6105-shell \ - --target qpg-qpg6105-persistent-storage \ --target qpg-qpg6105-light-switch \ --target qpg-qpg6105-thermostat \ build \ diff --git a/.github/workflows/examples-stm32.yaml b/.github/workflows/examples-stm32.yaml index f5cc530badffe6..ffc20a7aafbe10 100644 --- a/.github/workflows/examples-stm32.yaml +++ b/.github/workflows/examples-stm32.yaml @@ -50,7 +50,7 @@ jobs: uses: ./.github/actions/checkout-submodules-and-bootstrap with: platform: stm32 - + extra-submodule-parameters: --recursive - name: Set up environment for size reports uses: ./.github/actions/setup-size-reports if: ${{ !env.ACT }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4685948fd7bd12..bd5385580b9c22 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -98,7 +98,6 @@ jobs: --known-failure controller/ExamplePersistentStorage.cpp \ --known-failure controller/ExamplePersistentStorage.h \ --known-failure app/AttributeAccessToken.h \ - --known-failure app/CommandHandler.h \ --known-failure app/CommandHandlerInterface.h \ --known-failure app/CommandResponseSender.h \ --known-failure app/CommandSenderLegacyCallback.h \ diff --git a/.gitmodules b/.gitmodules index 26fff510eebfb5..55d44f9dba9e9f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -313,7 +313,7 @@ [submodule "third_party/st/STM32CubeWB"] path = third_party/st/STM32CubeWB url = https://github.com/STMicroelectronics/STM32CubeWB.git - branch = v1.17.0 + branch = v1.18.0 platforms = stm32 [submodule "p6/lwip-network-interface-integration"] path = third_party/infineon/psoc6/psoc6_sdk/libs/lwip-network-interface-integration diff --git a/.gn b/.gn index b9586c7fce69ea..f844fd20e9b87d 100644 --- a/.gn +++ b/.gn @@ -25,6 +25,7 @@ script_executable = "python3" default_args = { pw_unit_test_AUTOMATIC_RUNNER = "$dir_pigweed/targets/host/run_test" + pw_unit_test_CONFIG = "//config/pw_unit_test:define_overrides" pw_build_PIP_CONSTRAINTS = [ "//scripts/setup/constraints.txt" ] pw_build_PIP_REQUIREMENTS = [ "//scripts/setup/requirements.build.txt" ] diff --git a/.vscode/settings.json b/.vscode/settings.json index 415d4980d00d24..d38758524ac8a5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,25 +7,29 @@ "${workspaceFolder}/build/default/src/lib/**", "${workspaceFolder}/config/standalone/", "${workspaceFolder}/config/standalone/**", + "${workspaceFolder}/darwin/Framework/CHIP/**", "${workspaceFolder}/examples/**", "${workspaceFolder}/src/**", + "${workspaceFolder}/src/app/**", + "${workspaceFolder}/src/ble/**", + "${workspaceFolder}/src/controller/**", + "${workspaceFolder}/src/credentials/**", + "${workspaceFolder}/src/crypto/**", + "${workspaceFolder}/src/darwin/**", "${workspaceFolder}/src/include/", "${workspaceFolder}/src/include/**", + "${workspaceFolder}/src/inet/**", "${workspaceFolder}/src/lib/**", - "${workspaceFolder}/src/system/**", - "${workspaceFolder}/third_party/nlassert/repo/include/**", - "${workspaceFolder}/third_party/nlio/repo/include/**", - "${workspaceFolder}/darwin/Framework/CHIP/**", + "${workspaceFolder}/src/lwip/**", "${workspaceFolder}/src/messaging/**", + "${workspaceFolder}/src/platform/**", "${workspaceFolder}/src/protocols/**", + "${workspaceFolder}/src/setup_payload/**", + "${workspaceFolder}/src/system/**", "${workspaceFolder}/src/tracing/**", "${workspaceFolder}/src/transport/**", - "${workspaceFolder}/src/inet/**", - "${workspaceFolder}/src/credentials/**", - "${workspaceFolder}/src/data_model/**", - "${workspaceFolder}/src/app/**", - "${workspaceFolder}/src/crytpo/**", - "${workspaceFolder}/src/platform/**" + "${workspaceFolder}/third_party/nlassert/repo/include/**", + "${workspaceFolder}/third_party/nlio/repo/include/**" ], "[cpp]": { "editor.defaultFormatter": "xaver.clang-format" diff --git a/config/pw_unit_test/BUILD.gn b/config/pw_unit_test/BUILD.gn new file mode 100644 index 00000000000000..20889115f74538 --- /dev/null +++ b/config/pw_unit_test/BUILD.gn @@ -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("//build_overrides/build.gni") +import("//build_overrides/chip.gni") +import("//build_overrides/pigweed.gni") + +import("${chip_root}/build/chip/tests.gni") + +import("$dir_pw_build/target_types.gni") +pw_source_set("define_overrides") { + public_configs = [ ":define_options" ] +} + +config("define_options") { + if (chip_fake_platform && chip_link_tests) { + defines = [ "PW_UNIT_TEST_CONFIG_MEMORY_POOL_SIZE=65536" ] + } else { + defines = [ "PW_UNIT_TEST_CONFIG_MEMORY_POOL_SIZE=16384" ] + } +} diff --git a/config/qpg/chip-gn/build.sh b/config/qpg/chip-gn/build.sh index ce0306c2ab9809..541da2625253eb 100755 --- a/config/qpg/chip-gn/build.sh +++ b/config/qpg/chip-gn/build.sh @@ -23,6 +23,7 @@ env GN_ROOT_TARGET=$(dirname "$0") CHIP_ROOT=$GN_ROOT_TARGET/../../../ OUTDIR=$CHIP_ROOT/out +GN_ARGS="qpg_target_ic=\"qpg6105\" qpg_flavour=\"_ext_flash\"" mkdir -p "$OUTDIR" gn \ @@ -33,6 +34,7 @@ gn \ --export-compile-commands \ gen \ --check \ + --args="$GN_ARGS" \ --fail-on-unused-args \ "$OUTDIR" ninja -C "$OUTDIR" diff --git a/credentials/generate-revocation-set.py b/credentials/generate-revocation-set.py index 534edc15b0f203..bfc5ce560c1f80 100644 --- a/credentials/generate-revocation-set.py +++ b/credentials/generate-revocation-set.py @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # -# 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. @@ -22,6 +22,7 @@ import base64 import json +import logging import subprocess import sys from enum import Enum @@ -30,6 +31,16 @@ import requests from click_option_group import RequiredMutuallyExclusiveOptionGroup, optgroup from cryptography import x509 +from cryptography.hazmat.primitives.asymmetric import ec + +# Supported log levels, mapping string values required for argument +# parsing into logging constants +__LOG_LEVELS__ = { + 'debug': logging.DEBUG, + 'info': logging.INFO, + 'warn': logging.WARN, + 'fatal': logging.FATAL, +} class RevocationType(Enum): @@ -44,20 +55,144 @@ class RevocationType(Enum): TEST_NODE_URL_REST = "https://on.test-net.dcl.csa-iot.org" -def use_dcld(dcld, production, cmdlist): - return [dcld] + cmdlist + (['--node', PRODUCTION_NODE_URL] if production else []) - - def extract_single_integer_attribute(subject, oid): attribute_list = subject.get_attributes_for_oid(oid) if len(attribute_list) == 1: - if attribute_list[0].value.isdigit(): - return int(attribute_list[0].value) + return int(attribute_list[0].value, 16) return None +class DCLDClient: + ''' + A client for interacting with DCLD using either the REST API or command line interface (CLI). + + ''' + + def __init__(self, use_rest: bool, dcld_exe: str, production: bool, rest_node_url: str): + ''' + Initialize the client + + use_rest: bool + Use RESTful API with HTTPS against `rest_node_url` + dcld_exe: str + Path to `dcld` executable + production: bool + Use MainNet DCL URL with dcld executable + rest_node_url: str + RESTful API URL + ''' + + self.use_rest = use_rest + self.dcld_exe = dcld_exe + self.production = production + self.rest_node_url = rest_node_url + + def build_dcld_command_line(self, cmdlist: list[str]) -> list[str]: + ''' + Build command line for `dcld` executable. + + Parameters + ---------- + cmdlist: list[str] + List of command line arguments to append to some predefined arguments + + Returns + ------- + list[str] + The complete command list including the DCLD executable and node option if in production + ''' + + return [self.dcld_exe] + cmdlist + (['--node', PRODUCTION_NODE_URL] if self.production else []) + + def get_dcld_cmd_output_json(self, cmdlist: list[str]) -> dict: + ''' + Executes a DCLD CLI command and returns the JSON output. + + Parameters + ---------- + cmdlist: list[str] + List of command line arguments to append to some predefined arguments + + Returns + ------- + dict + The JSON output from the command + ''' + + # Set the output as JSON + subprocess.Popen([self.dcld_exe, 'config', 'output', 'json']) + + cmdpipe = subprocess.Popen(self.build_dcld_command_line(cmdlist), + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return json.loads(cmdpipe.stdout.read()) + + def get_revocation_points(self) -> list[dict]: + ''' + Get revocation points from DCL + + Returns + ------- + list[dict] + List of revocation points + ''' + + if self.use_rest: + response = requests.get(f"{self.rest_node_url}/dcl/pki/revocation-points").json() + else: + response = self.get_dcld_cmd_output_json(['query', 'pki', 'all-revocation-points']) + + return response["PkiRevocationDistributionPoint"] + + def get_paa_cert_for_crl_issuer(self, crl_signer_issuer_name_b64, crl_signer_authority_key_id) -> str: + ''' + Get PAA certificate for CRL issuer + + Parameters + ---------- + crl_signer_issuer_name_b64: str + The issuer name of the CRL signer. + crl_signer_authority_key_id: str + The authority key ID of the CRL signer. + + Returns + ------- + str + PAA certificate in PEM format + ''' + if self.use_rest: + response = requests.get( + f"{self.rest_node_url}/dcl/pki/certificates/{crl_signer_issuer_name_b64}/{crl_signer_authority_key_id}").json() + else: + response = self.get_dcld_cmd_output_json( + ['query', 'pki', 'x509-cert', '-u', crl_signer_issuer_name_b64, '-k', crl_signer_authority_key_id]) + + return response["approvedCertificates"]["certs"][0]["pemCert"] + + def get_revocations_points_by_skid(self, issuer_subject_key_id) -> list[dict]: + ''' + Get revocation points by subject key ID + + Parameters + ---------- + issuer_subject_key_id: str + Subject key ID + + Returns + ------- + list[dict] + List of revocation points + ''' + if self.use_rest: + response = requests.get(f"{self.rest_node_url}/dcl/pki/revocation-points/{issuer_subject_key_id}").json() + else: + response = self.get_dcld_cmd_output_json(['query', 'pki', 'revocation-points', + '--issuer-subject-key-id', issuer_subject_key_id]) + + return response["pkiRevocationDistributionPointsByIssuerSubjectKeyID"]["points"] + + @click.command() @click.help_option('-h', '--help') @optgroup.group('Input data sources', cls=RequiredMutuallyExclusiveOptionGroup) @@ -66,9 +201,19 @@ def extract_single_integer_attribute(subject, oid): @optgroup.option('--use-main-net-http', is_flag=True, type=str, help="Use RESTful API with HTTPS against public MainNet observer.") @optgroup.option('--use-test-net-http', is_flag=True, type=str, help="Use RESTful API with HTTPS against public TestNet observer.") @optgroup.group('Optional arguments') -@optgroup.option('--output', default='sample_revocation_set_list.json', type=str, metavar='FILEPATH', help="Output filename (default: sample_revocation_set_list.json)") -def main(use_main_net_dcld, use_test_net_dcld, use_main_net_http, use_test_net_http, output): - """DCL PAA mirroring tools""" +@optgroup.option('--output', default='sample_revocation_set_list.json', type=str, metavar='FILEPATH', + help="Output filename (default: sample_revocation_set_list.json)") +@optgroup.option('--log-level', default='INFO', show_default=True, type=click.Choice(__LOG_LEVELS__.keys(), + case_sensitive=False), callback=lambda c, p, v: __LOG_LEVELS__[v], + help='Determines the verbosity of script output') +def main(use_main_net_dcld: str, use_test_net_dcld: str, use_main_net_http: bool, use_test_net_http: bool, output: str, log_level: str): + """Tool to construct revocation set from DCL""" + + logging.basicConfig( + level=log_level, + format='%(asctime)s %(name)s %(levelname)-7s %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' + ) production = False dcld = use_test_net_dcld @@ -83,28 +228,20 @@ def main(use_main_net_dcld, use_test_net_dcld, use_main_net_http, use_test_net_h rest_node_url = PRODUCTION_NODE_URL_REST if production else TEST_NODE_URL_REST - # TODO: Extract this to a helper function - if use_rest: - revocation_point_list = requests.get(f"{rest_node_url}/dcl/pki/revocation-points").json()["PkiRevocationDistributionPoint"] - else: - cmdlist = ['config', 'output', 'json'] - subprocess.Popen([dcld] + cmdlist) - - cmdlist = ['query', 'pki', 'all-revocation-points'] - - cmdpipe = subprocess.Popen(use_dcld(dcld, production, cmdlist), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + dcld_client = DCLDClient(use_rest, dcld, production, rest_node_url) - revocation_point_list = json.loads(cmdpipe.stdout.read())["PkiRevocationDistributionPoint"] + revocation_point_list = dcld_client.get_revocation_points() revocation_set = [] for revocation_point in revocation_point_list: # 1. Validate Revocation Type - if revocation_point["revocationType"] != RevocationType.CRL: + if revocation_point["revocationType"] != RevocationType.CRL.value: + logging.warning("Revocation Type is not CRL, continue...") continue # 2. Parse the certificate - crl_signer_certificate = x509.load_pem_x509_certificate(revocation_point["crlSignerCertificate"]) + crl_signer_certificate = x509.load_pem_x509_certificate(bytes(revocation_point["crlSignerCertificate"], 'utf-8')) vid = revocation_point["vid"] pid = revocation_point["pid"] @@ -119,12 +256,15 @@ def main(use_main_net_dcld, use_test_net_dcld, use_main_net_http, use_test_net_h if crl_vid is not None: if vid != crl_vid: # TODO: Need to log all situations where a continue is called + logging.warning("VID is not CRL VID, continue...") continue else: if crl_vid is None or vid != crl_vid: + logging.warning("VID is not CRL VID, continue...") continue if crl_pid is not None: if pid != crl_pid: + logging.warning("PID is not CRL PID, continue...") continue # 5. Validate the certification path containing CRLSignerCertificate. @@ -133,99 +273,110 @@ def main(use_main_net_dcld, use_test_net_dcld, use_main_net_http, use_test_net_h crl_signer_authority_key_id = crl_signer_certificate.extensions.get_extension_for_oid( x509.OID_AUTHORITY_KEY_IDENTIFIER).value.key_identifier - paa_certificate = None + # Convert CRL Signer AKID to colon separated hex + crl_signer_authority_key_id = crl_signer_authority_key_id.hex().upper() + crl_signer_authority_key_id = ':'.join([crl_signer_authority_key_id[i:i+2] + for i in range(0, len(crl_signer_authority_key_id), 2)]) - # TODO: Extract this to a helper function - if use_rest: - response = requests.get( - f"{rest_node_url}/dcl/pki/certificates/{crl_signer_issuer_name}/{crl_signer_authority_key_id}").json()["approvedCertificates"]["certs"][0] - paa_certificate = response["pemCert"] - else: - cmdlist = ['query', 'pki', 'x509-cert', '-u', crl_signer_issuer_name, '-k', crl_signer_authority_key_id] - cmdpipe = subprocess.Popen(use_dcld(dcld, production, cmdlist), stdout=subprocess.PIPE, stderr=subprocess.PIPE) - paa_certificate = json.loads(cmdpipe.stdout.read())["approvedCertificates"]["certs"][0]["pemCert"] + paa_certificate = dcld_client.get_paa_cert_for_crl_issuer(crl_signer_issuer_name, crl_signer_authority_key_id) if paa_certificate is None: + logging.warning("PAA Certificate not found, continue...") continue - paa_certificate_object = x509.load_pem_x509_certificate(paa_certificate) + paa_certificate_object = x509.load_pem_x509_certificate(bytes(paa_certificate, 'utf-8')) + + # TODO: use verify_directly_issued_by() method when we upgrade cryptography to v40.0.0 + # Verify issuer matches with subject + if crl_signer_certificate.issuer != paa_certificate_object.subject: + logging.warning("CRL Signer Certificate issuer does not match with PAA Certificate subject, continue...") + continue + # Check crl signers AKID matches with SKID of paa_certificate_object's AKID + paa_skid = paa_certificate_object.extensions.get_extension_for_oid(x509.OID_SUBJECT_KEY_IDENTIFIER).value.key_identifier + crl_akid = crl_signer_certificate.extensions.get_extension_for_oid(x509.OID_AUTHORITY_KEY_IDENTIFIER).value.key_identifier + if paa_skid != crl_akid: + logging.warning("CRL Signer's AKID does not match with PAA Certificate SKID, continue...") + continue + + # verify if PAA singed the crl signer certificate try: - crl_signer_certificate.verify_directly_issued_by(paa_certificate_object) + paa_certificate_object.public_key().verify(crl_signer_certificate.signature, + crl_signer_certificate.tbs_certificate_bytes, + ec.ECDSA(crl_signer_certificate.signature_hash_algorithm)) except Exception: + logging.warning("CRL Signer Certificate is not signed by PAA Certificate, continue...") continue # 6. Obtain the CRL - r = requests.get(revocation_point["dataURL"]) - crl_file = x509.load_der_x509_crl(r.content) + logging.debug(f"Fetching CRL from {revocation_point['dataURL']}") + try: + r = requests.get(revocation_point["dataURL"], timeout=5) + except Exception: + logging.error('Failed to fetch CRL') + continue + + try: + crl_file = x509.load_der_x509_crl(r.content) + except Exception: + logging.error('Failed to load CRL') + continue # 7. Perform CRL File Validation crl_authority_key_id = crl_file.extensions.get_extension_for_oid(x509.OID_AUTHORITY_KEY_IDENTIFIER).value.key_identifier crl_signer_subject_key_id = crl_signer_certificate.extensions.get_extension_for_oid( x509.OID_SUBJECT_KEY_IDENTIFIER).value.key_identifier if crl_authority_key_id != crl_signer_subject_key_id: + logging.warning("CRL Authority Key ID is not CRL Signer Subject Key ID, continue...") continue issuer_subject_key_id = ''.join('{:02X}'.format(x) for x in crl_authority_key_id) - same_issuer_points = None + # b. + same_issuer_points = dcld_client.get_revocations_points_by_skid(issuer_subject_key_id) + count_with_matching_vid_issuer_skid = sum(item.get('vid') == vid for item in same_issuer_points) - # TODO: Extract this to a helper function - if use_rest: - response = requests.get( - f"{rest_node_url}/dcl/pki/revocation-points/{issuer_subject_key_id}").json()["pkiRevocationDistributionPointsByIssuerSubjectKeyID"] - same_issuer_points = response["points"] - else: - cmdlist = ['query', 'pki', 'revocation-points', '--issuer-subject-key-id', issuer_subject_key_id] - cmdpipe = subprocess.Popen(use_dcld(dcld, production, cmdlist), stdout=subprocess.PIPE, stderr=subprocess.PIPE) - same_issuer_points = json.loads(cmdpipe.stdout.read())[ - "pkiRevocationDistributionPointsByIssuerSubjectKeyID"]["points"] - - matching_entries = False - for same_issuer_point in same_issuer_points: - if same_issuer_point["vid"] == vid: - matching_entries = True - break - - if matching_entries: + if count_with_matching_vid_issuer_skid > 1: try: issuing_distribution_point = crl_file.extensions.get_extension_for_oid( x509.OID_ISSUING_DISTRIBUTION_POINT).value except Exception: + logging.warning("CRL Issuing Distribution Point not found, continue...") continue uri_list = issuing_distribution_point.full_name if len(uri_list) == 1 and isinstance(uri_list[0], x509.UniformResourceIdentifier): if uri_list[0].value != revocation_point["dataURL"]: + logging.warning("CRL Issuing Distribution Point URI is not CRL URL, continue...") continue else: + logging.warning("CRL Issuing Distribution Point URI is not CRL URL, continue...") continue # 9. Assign CRL File Issuer certificate_authority_name = base64.b64encode(crl_file.issuer.public_bytes()).decode('utf-8') + logging.debug(f"CRL File Issuer: {certificate_authority_name}") serialnumber_list = [] # 10. Iterate through the Revoked Certificates List for revoked_cert in crl_file: + # a. try: revoked_cert_issuer = revoked_cert.extensions.get_extension_for_oid( x509.CRLEntryExtensionOID.CERTIFICATE_ISSUER).value.get_values_for_type(x509.DirectoryName).value if revoked_cert_issuer is not None: if revoked_cert_issuer != certificate_authority_name: + logging.warning("CRL Issuer is not CRL File Issuer, continue...") continue except Exception: pass # b. - try: - revoked_cert_authority_key_id = revoked_cert.extensions.get_extension_for_oid( - x509.OID_AUTHORITY_KEY_IDENTIFIER).value.key_identifier - - if revoked_cert_authority_key_id is None or revoked_cert_authority_key_id != crl_signer_subject_key_id: - continue - except Exception: - continue + # TODO: Verify that the certificate chain of the entry is linking to the same PAA + # that issued the CRLSignerCertificate for this entry, including path through + # CRLSignerDelegator if present. If the PAAs under which were issued the certificate + # and the CRLSignerCertificate are different, ignore the entry. # c. and d. serialnumber_list.append(bytes(str('{:02X}'.format(revoked_cert.serial_number)), 'utf-8').decode('utf-8')) diff --git a/docs/QUICK_START.md b/docs/QUICK_START.md index 97f0f76b3b0436..10833e0c0c1ed7 100644 --- a/docs/QUICK_START.md +++ b/docs/QUICK_START.md @@ -10,7 +10,7 @@ and platforms. |
Controller / Admin
|
Node
| Description | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [**chip-tool**](https://github.com/project-chip/connectedhomeip/blob/master/examples/chip-tool/README.md) (Linux / Mac)
Includes docs for all the cluster commands supported
| **all-clusters-app**
  • [M5Stack](https://github.com/project-chip/connectedhomeip/blob/master/examples/all-clusters-app/esp32/README.md) (ESP)
  • [Linux](https://github.com/project-chip/connectedhomeip/tree/master/examples/all-clusters-app/linux) simulation | Use the command line tool on a laptop to pair with and control an embedded Wi-Fi platform. This demo supports the “all-clusters-app”, so it provides the basic onoff light test and more. | -| [**chip-device-ctrl.py**](https://github.com/project-chip/connectedhomeip/blob/master/src/controller/python/README.md) | **all-clusters-app**
  • [M5Stack](https://github.com/project-chip/connectedhomeip/blob/master/examples/all-clusters-app/esp32/README.md) (ESP)
  • [Linux](https://github.com/project-chip/connectedhomeip/tree/master/examples/all-clusters-app/linux) simulation | Same as above, but uses the pychip tool as Controller Node. | +| [**chip-repl**](https://github.com/project-chip/connectedhomeip/blob/master/src/controller/python/README.md) | **all-clusters-app**
  • [M5Stack](https://github.com/project-chip/connectedhomeip/blob/master/examples/all-clusters-app/esp32/README.md) (ESP)
  • [Linux](https://github.com/project-chip/connectedhomeip/tree/master/examples/all-clusters-app/linux) simulation | Same as above, but uses the Python CHIP REPL as Controller Node. | ## Thread Nodes diff --git a/docs/getting_started/first_example.md b/docs/getting_started/first_example.md new file mode 100644 index 00000000000000..4aba0d4e480204 --- /dev/null +++ b/docs/getting_started/first_example.md @@ -0,0 +1,123 @@ +# An SDK example + +The SDK provides a number of example devices and controllers that can be used to +familiarize yourself with the SDK and the Matter ecosystem. + +## Example Devices + +The example devices (occasionally referred to as "apps") are located in the +[examples](../../examples/) directory. The examples often implement one +particular device type. Some have implementations for various platforms. + +The linux platform examples are provided as examples, and are used in the CI. +These can be used for preliminary testing. + +The all-clusters-app is used by the QA team for testing. This app implements +nearly all the available clusters and does not conform to a specific device +type. This app is not a good starting place for product development. + +## Example Controllers + +The SDK has two example controllers that can be used to interact with devices +for testing. + +[chip-tool](../../examples/chip-tool/) is a C++ command line controller with an +interactive shell. More information on chip-tool can be found in the +[chip-tool guide](../guides/chip_tool_guide.md). + +[chip-repl](../../src/controller/python/chip-repl.py) is a shell for the python +controller. The chip-repl is part of the python controller framework, often used +for testing. More information about the python controller can be found in the +[python testing](../testing/python.md) documentation. + +## Building your first demo app (lighting) + +The examples directory contains a set of apps using an example device +composition \.zap file. For more information about device composition and zap, +see [ZAP documentation](./zap.md). + +This quick start guide will walk you through + +- Building an app (lighting app) for the host platform +- Interacting with the app using chip\-tool \(controller\) + +### Building the lighting app + +- Install prerequisites from docs/guides/BUILDING\.md +- Run bootstrap or activate to install all the required tools etc. + - `. scripts/bootstrap.sh` \- run this first\, or if builds fail + - `. scripts/activate.sh` \- faster\, use if you’ve already bootstrapped + and are just starting a new terminal + +The build system we use is Ninja / GN. You can use a standard gn gen / ninja to +build as normal, or use the scripts to build specific variants. More information +about the build system can be found at [BUILDING.md](../guides/BUILDING.md). The +official quickstart guide for the build system is located ag +https://gn.googlesource.com/gn/+/master/docs/quick_start.md and a full reference +can be found at https://gn.googlesource.com/gn/+/main/docs/reference.md. + +To build with the scripts, use scripts/build/build_examples\.py - +`scripts/build/build_examples.py targets` - +`scripts/build/build_examples.py --target build` - builds to +`out//` + +Scripts can be used to build both the lighting app and chip tool + +- Lighting app \(device\) + - `./scripts/build/build_examples.py --target linux-x64-light-no-ble build` + - This will build an executable to + `./out/linux-x64-light-no-ble/chip-lighting-app` + +* NOTE that the host name (linux-x64 here) may be different on different + systems ex. darwin + +- chip-tool (controller) + - `./scripts/build/build_examples.py --target linux-x64-chip-tool build` + - This will build an executable to `./out/linux-x64-chip-tool/chip-tool` + +### Building / Interacting with Matter Examples + +The first thing you need to do is to commission the device. First start up the +app in one terminal. By default it will start up with the default discriminator +(3840) and passcode (20202021) and save its non-volatile information in a KVS in +/temp/chip_kvs. You can change these, and multiple other options on the command +line. For a full description, use the `--help` command. + +Start the lighting app in one terminal using + +`./out/linux-x64-light-no-ble/chip-lighting-app` + +The lighting app will print out all its setup information. You can get the setup +codes, discriminator and passcode from the logs. + +Open a new terminal to use chip tool. Commission the device using: + +`./out/linux-x64-chip-tool/chip-tool pairing code 0x12344321 MT:-24J0AFN00KA0648G0` + +NOTE: pairing is the old name for commissioning. 0x12344321 is the node ID you +want to assign to the node. 0x12344321 is the default for testing. +MT:-24J0AFN00KA0648G0 is the QR code for a device with the default discriminator +and passcode. If you have changed these, the code will be different. + +#### Basic device interactions - Sending a command + +`./chip-tool onoff on 0x12344321 1` + +where: + +- onoff is the cluster name +- on is the command name +- 0x12344321 is the node ID you used for commissioning +- 1 is the endpoint + +#### Basic device interactions - Reading an attribute + +`./chip-tool onoff read on-off 0x12344321 1` + +where: + +- onoff is the cluster name +- read is the desired action +- on is the attribute name +- 0x12344321 is the node ID you used for commissioning +- 1 is the endpoint diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md index b961701cc98bc8..cca67e19b0c8fc 100644 --- a/docs/getting_started/index.md +++ b/docs/getting_started/index.md @@ -11,6 +11,7 @@ The following docs are a brief introduction to SDK development. ``` +- [Running your first example](./first_example.md) - [SDK Basics](./SDKBasics.md) - [ZAP](./zap.md) - [Discover from a host computer](./discovery_from_a_host_computer.md) diff --git a/docs/guides/python_chip_controller_advanced_usage.md b/docs/guides/python_chip_controller_advanced_usage.md index c3d3f55ddc5095..2eee5472fdda23 100644 --- a/docs/guides/python_chip_controller_advanced_usage.md +++ b/docs/guides/python_chip_controller_advanced_usage.md @@ -7,8 +7,9 @@ tool or Matter accessories on Linux.
    -- [Bluetooth LE virtualization on Linux](#bluetooth-le-virtualization-on-linux) -- [Debugging with gdb](#debugging-with-gdb) +- [Using Python CHIP Controller advanced features](#using-python-chip-controller-advanced-features) + - [Bluetooth LE virtualization on Linux](#bluetooth-le-virtualization-on-linux) + - [Debugging with gdb](#debugging-with-gdb)
    @@ -62,38 +63,38 @@ interfaces working as Bluetooth LE central and peripheral, respectively. TX bytes:3488 acl:95 sco:0 commands:110 errors:0 ``` -4. Run the Python CHIP Controller with Bluetooth LE adapter defined from a +4. Run the Python CHIP Controller REPL with Bluetooth LE adapter defined from a command line: - For example, add `--bluetooth-adapter=hci2` to use the virtual interface - `hci2` listed above. + For example, add `--ble-adapter=2` to use the virtual interface `hci2` + listed above. ``` - chip-device-ctrl --bluetooth-adapter=hci2 + chip-repl --ble-adapter=2 ```
    ## Debugging with gdb -You can run the chip-device-ctrl under GDB for debugging, however, since the -Matter core support library is a dynamic library, you cannot read the symbols -unless it is fully loaded. +You can run the chip-repl under GDB for debugging, however, since the Matter SDK +library is a dynamic library, you cannot read the symbols unless it is fully +loaded. The following block is a example debug session using GDB: ``` # GDB cannot run scripts directly -# so you need to run Python3 with the path of device controller -# Here, we use the feature from bash to get the path of chip-device-ctrl without typing it. -$ gdb --args python3 `which chip-device-ctrl` -GNU gdb (Ubuntu 10.1-2ubuntu2) 10.1.90.20210411-git -Copyright (C) 2021 Free Software Foundation, Inc. +# so you need to run Python3 with the path of device controller REPL +# Here, we use the feature from bash to get the path of chip-repl without typing it. +$ gdb --args python3 `which chip-repl` +GNU gdb (GDB) 14.2 +Copyright (C) 2023 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. -This GDB was configured as "aarch64-linux-gnu". +This GDB was configured as "x86_64-pc-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: . @@ -103,6 +104,12 @@ Find the GDB manual and other documentation resources online at: For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from python3... + +This GDB supports auto-downloading debuginfo from the following URLs: + +Enable debuginfod for this session? (y or [n]) n +Debuginfod has been disabled. +To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit. (No debugging symbols found in python3) (gdb) ``` @@ -119,38 +126,68 @@ library, let run the Matter device controller first. ``` (gdb) run -Starting program: /usr/bin/python3 /home/ubuntu/.local/bin/chip-device-ctrl +Starting program: /home/sag/projects/project-chip/connectedhomeip/out/venv/bin/python3 /home/sag/projects/project-chip/connectedhomeip/out/venv/bin/chip-repl [Thread debugging using libthread_db enabled] -Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1". -CHIP:DIS: Init admin pairing table with server storage. -CHIP:IN: local node id is 0x000000000001b669 -CHIP:DL: MDNS failed to join multicast group on wpan0 for address type IPv4: Inet Error 1016 (0x000003F8): Address not found -CHIP:ZCL: Using ZAP configuration... -CHIP:ZCL: deactivate report event -CHIP:CTL: Getting operational keys -CHIP:CTL: Generating operational certificate for the controller -CHIP:CTL: Getting root certificate for the controller from the issuer -CHIP:CTL: Generating credentials -CHIP:CTL: Loaded credentials successfully -CHIP:DL: Platform main loop started. -Chip Device Controller Shell +Using host libthread_db library "/usr/lib/libthread_db.so.1". +Python 3.11.9 (main, Apr 29 2024, 11:59:58) [GCC 13.2.1 20240417] +Type 'copyright', 'credits' or 'license' for more information +IPython 8.24.0 -- An enhanced Interactive Python. Type '?' for help. +[1716395111.775747][364405:364405] CHIP:CTL: Setting attestation nonce to random value +[1716395111.776196][364405:364405] CHIP:CTL: Setting CSR nonce to random value +InitBLE 0[1716395111.776809][364405:364405] CHIP:DL: writing settings to file (/tmp/chip_counters.ini-T7hX27) +[1716395111.776854][364405:364405] CHIP:DL: renamed tmp file to file (/tmp/chip_counters.ini) +[1716395111.776860][364405:364405] CHIP:DL: NVS set: chip-counters/reboot-count = 9 (0x9) +[1716395111.777261][364405:364405] CHIP:DL: Got Ethernet interface: eno2 +[1716395111.777555][364405:364405] CHIP:DL: Found the primary Ethernet interface:eno2 +[1716395111.777868][364405:364405] CHIP:DL: Got WiFi interface: wlp7s0 +[1716395111.777877][364405:364405] CHIP:DL: Failed to reset WiFi statistic counts +────────────────────────────────────────────────────────────────────────────────────────────────────────── Matter REPL ────────────────────────────────────────────────────────────────────────────────────────────────────────── + + + + Welcome to the Matter Python REPL! + + For help, please type matterhelp() + + To get more information on a particular object/class, you can pass + that into matterhelp() as well. + + +───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +2024-05-22 18:25:11 allenwind PersistentStorage[364405] WARNING Initializing persistent storage from file: /tmp/repl-storage.json +2024-05-22 18:25:11 allenwind PersistentStorage[364405] WARNING Loading configuration from /tmp/repl-storage.json... +2024-05-22 18:25:11 allenwind CertificateAuthorityManager[364405] WARNING Loading certificate authorities from storage... +2024-05-22 18:25:11 allenwind CertificateAuthority[364405] WARNING New CertificateAuthority at index 1 +2024-05-22 18:25:11 allenwind CertificateAuthority[364405] WARNING Loading fabric admins from storage... +2024-05-22 18:25:11 allenwind FabricAdmin[364405] WARNING New FabricAdmin: FabricId: 0x0000000000000001, VendorId = 0xFFF1 +2024-05-22 18:25:11 allenwind FabricAdmin[364405] WARNING Allocating new controller with CaIndex: 1, FabricId: 0x0000000000000001, NodeId: 0x000000000001B669, CatTags: [] + + +The following objects have been created: + certificateAuthorityManager: Manages a list of CertificateAuthority instances. + caList: The list of CertificateAuthority instances. + caList: A specific FabricAdmin object at index m for the nth CertificateAuthority instance. -chip-device-ctrl > + +Default CHIP Device Controller (NodeId: 112233): has been initialized to manage caList[0].adminList[0] (FabricId = 1), and is available as devCtrl + +In [1]: ``` -The prompt `chip-device-ctrl >` indicates that the Matter core library is loaded -by Python, you can browse the symbols in the Matter core library, setting -breakpoints on functions and many other functions provided by GDB. +The prompt `In [1]:` indicates that the Matter SDK library has been loaded and +initialized by the Python Controller REPL, you can browse the symbols in the +Matter core library, setting breakpoints on functions and many other functions +provided by GDB. -You can use `Ctrl-C` to send SIGINT to the controller anytime you want so you -can set breakpoints. +You can use `Ctrl-Z` to send `SIGTSTP` to the Python 3 REPL process anytime you +want so you can set breakpoints (unfortunately Ctrl+C seems to be captured by +the REPL). -> (`Ctrl-C` pressed here.) +In [1]: (`Ctrl-Z` pressed here.) ``` -Thread 1 "python3" received signal SIGINT, Interrupt. -0x0000fffff7db79ec in __GI___select (nfds=, readfds=0xffffffffe760, writefds=0x0, exceptfds=0x0, timeout=) at ../sysdeps/unix/sysv/linux/select.c:49 -49 ../sysdeps/unix/sysv/linux/select.c: No such file or directory. +Thread 1 "python3" received signal SIGTSTP, Stopped (user). +0x00007ffff7650ceb in kill () from /usr/lib/libc.so.6 (gdb) ``` @@ -159,40 +196,27 @@ command in GDB (`b` for short) ``` (gdb) b DeviceCommissioner::PairDevice -Breakpoint 1 at 0xfffff5b0f6b4 (2 locations) +Breakpoint 1 at 0x7fffed453943: DeviceCommissioner::PairDevice. (4 locations) (gdb) ``` -Type `continue` (`c` for short) to continue the device controller, you may need -another hit of `Enter` to see the prompt. +Type `signal SIGCONT` to continue the device controller after stopping it with +signal stop, you may need another hit of `Enter` to see the prompt. ``` -(gdb) c -Continuing. - -chip-device-ctrl > +(gdb) signal SIGCONT +Continuing with signal SIGCONT. +In [1]: ``` Let do pairing over IP to see the effect of the breakpoint we just set. ``` -chip-device-ctrl > connect -ip 192.168.50.5 20202021 1 -Device is assigned with nodeid = 1 - -Thread 1 "python3" hit Breakpoint 1, 0x0000fffff5b0f6b4 in chip::Controller::DeviceCommissioner::PairDevice(unsigned long, chip::RendezvousParameters&)@plt () - from /home/ubuntu/.local/lib/python3.9/site-packages/chip/_ChipDeviceCtrl.so -(gdb) -``` - -The `@plt` symbol means it is a symbol used by dynamic library loader, type `c` -(for `continue`) and it will break on the real function. +In [1]: devCtrl.CommissionWithCode("MT:-24J0AFN00KA0648G00", 1234) -``` -(gdb) c -Continuing. - -Thread 1 "python3" hit Breakpoint 1, chip::Controller::DeviceCommissioner::PairDevice (this=0xd28540, remoteDeviceId=1, params=...) at ../../src/controller/CHIPDeviceController.cpp:827 -827 { +Thread 5 "python3" hit Breakpoint 1.1, chip::Controller::DeviceCommissioner::PairDevice (this=0x7fffd8003a90, remoteDeviceId=1234, setUpCode=0x7ffff453d490 "MT:-24J0AFN00KA0648G00", params=..., + discoveryType=chip::Controller::DiscoveryType::kAll, resolutionData=...) at ../../src/controller/CHIPDeviceController.cpp:646 +646 { (gdb) ``` @@ -201,46 +225,44 @@ then you can use `bt` (for `backtrace`) to see the backtrace of the call stack. ``` (gdb) bt -#0 chip::Controller::DeviceCommissioner::PairDevice(unsigned long, chip::RendezvousParameters&) (this=0xd28540, remoteDeviceId=1, params=...) - at ../../src/controller/CHIPDeviceController.cpp:827 -#1 0x0000fffff5b3095c in pychip_DeviceController_ConnectIP(chip::Controller::DeviceCommissioner*, char const*, uint32_t, chip::NodeId) - (devCtrl=0xd28540, peerAddrStr=0xfffff467ace0 "192.168.50.5", setupPINCode=20202021, nodeid=1) at ../../src/controller/python/ChipDeviceController-ScriptBinding.cpp:234 -#2 0x0000fffff7639148 in () at /lib/aarch64-linux-gnu/libffi.so.8 -#3 0x0000fffff7638750 in () at /lib/aarch64-linux-gnu/libffi.so.8 -#4 0x0000fffff7665a44 in () at /usr/lib/python3.9/lib-dynload/_ctypes.cpython-39-aarch64-linux-gnu.so -#5 0x0000fffff7664c7c in () at /usr/lib/python3.9/lib-dynload/_ctypes.cpython-39-aarch64-linux-gnu.so -#6 0x00000000004a54f0 in _PyObject_MakeTpCall () -#7 0x000000000049cb10 in _PyEval_EvalFrameDefault () -#8 0x0000000000496d1c in () -#9 0x00000000004b1eb0 in _PyFunction_Vectorcall () -#10 0x0000000000498264 in _PyEval_EvalFrameDefault () -#11 0x00000000004b1cb8 in _PyFunction_Vectorcall () -#12 0x0000000000498418 in _PyEval_EvalFrameDefault () -#13 0x0000000000496d1c in () -#14 0x00000000004b1eb0 in _PyFunction_Vectorcall () -#15 0x0000000000498418 in _PyEval_EvalFrameDefault () -#16 0x00000000004b1cb8 in _PyFunction_Vectorcall () -#17 0x00000000004c6bc8 in () -#18 0x0000000000498264 in _PyEval_EvalFrameDefault () -#19 0x00000000004b1cb8 in _PyFunction_Vectorcall () -#20 0x0000000000498418 in _PyEval_EvalFrameDefault () -#21 0x00000000004966f8 in () -#22 0x00000000004b1f18 in _PyFunction_Vectorcall () -#23 0x0000000000498418 in _PyEval_EvalFrameDefault () -#24 0x00000000004b1cb8 in _PyFunction_Vectorcall () -#25 0x0000000000498264 in _PyEval_EvalFrameDefault () -#26 0x00000000004966f8 in () -#27 0x0000000000496490 in _PyEval_EvalCodeWithName () -#28 0x0000000000595b7c in PyEval_EvalCode () -#29 0x00000000005c6a5c in () -#30 0x00000000005c0a70 in () -#31 0x00000000005c69a8 in () -#32 0x00000000005c6148 in PyRun_SimpleFileExFlags () -#33 0x00000000005b60bc in Py_RunMain () -#34 0x0000000000585a08 in Py_BytesMain () -#35 0x0000fffff7d0c9d4 in __libc_start_main (main= - 0x5858fc <_start+60>, argc=2, argv=0xfffffffff498, init=, fini=, rtld_fini=, stack_end=) at ../csu/libc-start.c:332 -#36 0x00000000005858f8 in _start () +(gdb) bt +#0 chip::Controller::DeviceCommissioner::PairDevice + (this=0x7fffd8003a90, remoteDeviceId=1234, setUpCode=0x7fffef2555d0 "MT:-24J0AFN00KA0648G00", params=..., discoveryType=chip::Controller::DiscoveryType::kAll, resolutionData=...) + at ../../src/controller/CHIPDeviceController.cpp:646 +#1 0x00007fffed040825 in pychip_DeviceController_ConnectWithCode (devCtrl=0x7fffd8003a90, onboardingPayload=0x7fffef2555d0 "MT:-24J0AFN00KA0648G00", nodeid=1234, discoveryType=2 '\002') + at ../../src/controller/python/ChipDeviceController-ScriptBinding.cpp:395 +#2 0x00007ffff6ad5596 in ??? () at /usr/lib/libffi.so.8 +#3 0x00007ffff6ad200e in ??? () at /usr/lib/libffi.so.8 +#4 0x00007ffff6ad4bd3 in ffi_call () at /usr/lib/libffi.so.8 +#5 0x00007ffff6aeaffc in ??? () at /usr/lib/python3.11/lib-dynload/_ctypes.cpython-311-x86_64-linux-gnu.so +#6 0x00007ffff6aeb4b4 in ??? () at /usr/lib/python3.11/lib-dynload/_ctypes.cpython-311-x86_64-linux-gnu.so +#7 0x00007ffff794a618 in _PyObject_MakeTpCall () at /usr/lib/libpython3.11.so.1.0 +#8 0x00007ffff78f3d03 in _PyEval_EvalFrameDefault () at /usr/lib/libpython3.11.so.1.0 +#9 0x00007ffff7adef90 in ??? () at /usr/lib/libpython3.11.so.1.0 +#10 0x00007ffff79ebc0b in _PyObject_FastCallDictTstate () at /usr/lib/libpython3.11.so.1.0 +#11 0x00007ffff79ebe02 in _PyObject_Call_Prepend () at /usr/lib/libpython3.11.so.1.0 +#12 0x00007ffff79ec114 in ??? () at /usr/lib/libpython3.11.so.1.0 +#13 0x00007ffff794a618 in _PyObject_MakeTpCall () at /usr/lib/libpython3.11.so.1.0 +#14 0x00007ffff78f3d03 in _PyEval_EvalFrameDefault () at /usr/lib/libpython3.11.so.1.0 +#15 0x00007ffff7adef90 in ??? () at /usr/lib/libpython3.11.so.1.0 +#16 0x00007ffff7955b97 in PyObject_Vectorcall () at /usr/lib/libpython3.11.so.1.0 +#17 0x00007ffff6aea174 in ??? () at /usr/lib/python3.11/lib-dynload/_ctypes.cpython-311-x86_64-linux-gnu.so +#18 0x00007ffff6aea28c in ??? () at /usr/lib/python3.11/lib-dynload/_ctypes.cpython-311-x86_64-linux-gnu.so +#19 0x00007ffff6ad5152 in ??? () at /usr/lib/libffi.so.8 +#20 0x00007ffff6ad57b8 in ??? () at /usr/lib/libffi.so.8 +#21 0x00007fffed5de848 in chip::DeviceLayer::Internal::GenericPlatformManagerImpl::_DispatchEvent + (this=0x7fffed88dc90 , event=0x7fffe6fffe30) at ../../src/include/platform/internal/GenericPlatformManagerImpl.ipp:304 +#22 0x00007fffed5dd90d in chip::DeviceLayer::PlatformManager::DispatchEvent (this=0x7fffed88dc80 , event=0x7fffe6fffe30) at ../../src/include/platform/PlatformManager.h:503 +#23 0x00007fffed5df45b in chip::DeviceLayer::Internal::GenericPlatformManagerImpl_POSIX::ProcessDeviceEvents + (this=0x7fffed88dc90 ) at ../../src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp:185 +#24 0x00007fffed5dee64 in chip::DeviceLayer::Internal::GenericPlatformManagerImpl_POSIX::_RunEventLoop (this=0x7fffed88dc90 ) +--Type for more, q to quit, c to continue without paging-- + at ../../src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp:227 +#25 0x00007fffed5dd888 in chip::DeviceLayer::PlatformManager::RunEventLoop (this=0x7fffed88dc80 ) at ../../src/include/platform/PlatformManager.h:403 +#26 0x00007fffed5df3fe in chip::DeviceLayer::Internal::GenericPlatformManagerImpl_POSIX::EventLoopTaskMain (arg=0x7fffed88dc90 ) + at ../../src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp:256 +#27 0x00007ffff76a6ded in ??? () at /usr/lib/libc.so.6 +#28 0x00007ffff772a0dc in ??? () at /usr/lib/libc.so.6 (gdb) ``` diff --git a/docs/guides/python_chip_controller_building.md b/docs/guides/python_chip_controller_building.md index c940f2c92575e7..8a7acc884ab2fa 100644 --- a/docs/guides/python_chip_controller_building.md +++ b/docs/guides/python_chip_controller_building.md @@ -1,25 +1,20 @@ -# Deprecation notice - -chip-device-ctrl is no longer maintained and should not be used. - -Matter-repl is the current python controller implementation. - # Working with Python CHIP Controller -The Python CHIP Controller is a tool that allows to commission a Matter device -into the network and to communicate with it using the Zigbee Cluster Library -(ZCL) messages. +The Python CHIP controller is a library that allows to create a Matter fabric +and commission Matter devices with it. -> The chip-device-ctrl tool will be deprecated, and will be replaced by -> chip-repl. Continue reading to see how to do the same thing with chip-repl. +The `chip-repl` is a REPl which sets up a Python CHIP Controller and allows to +explore the Python CHIP Controller API and communicate with devices from the +command line.
    - [Source files](#source-files) -- [Building Android CHIPTool](#building-and-installing) -- [Running the tool](#running-the-tool) -- [Using Python CHIP Controller for Matter accessory testing](#using-python-chip-controller-for-matter-accessory-testing) -- [List of commands](#list-of-commands) +- [Building Python CHIP Controller](#building-and-installing) +- [Running the CHIP REPL](#running-the-chip-repl) +- [Using Python CHIP Controller REPL for Matter accessory testing](#using-python-chip-controller-repl-for-matter-accessory-testing) +- [Example usage of the Python CHIP Controller REPL](#example-usage-of-the-python-chip-controller-repl) +- [Explore Clusters, Attributes and Commands](#explore-clusters-attributes-and-commands)
    @@ -85,35 +80,31 @@ To build and run the Python CHIP controller: scripts/build_python.sh -m platform -i separate ``` - > Note: To get more details about available build configurations, run the + > Note: This builds the Python CHIP Controller along with the CHIP REPL as + > Python wheels and installs it into a separate Python virtual environment. + > To get more details about available build configurations, run the > following command: `scripts/build_python.sh --help`
    -## Running the tool +## Running the CHIP REPL -1. Activate the Python virtual environment: +1. Activate the Python virtual environment with the Python CHIP Controller + installed: ``` source out/python_env/bin/activate ``` -2. Run the Python CHIP controller with root privileges, which is required to - obtain access to the Bluetooth interface: - - ``` - sudo out/python_env/bin/chip-device-ctrl - ``` - - You can also select the Bluetooth LE interface using command line argument: +2. Run the CHIP REPL to explore the API of the Python CHIP controller: ``` - sudo out/python_env/bin/chip-device-ctrl --bluetooth-adapter=hci2 + chip-repl ```
    -## Using Python CHIP Controller for Matter accessory testing +## Using Python CHIP Controller REPL for Matter accessory testing This section describes how to use Python CHIP controller to test the Matter accessory. Below steps depend on the application clusters that you implemented @@ -135,13 +126,14 @@ require physical trigger, for example pushing a button. Follow the documentation of the Matter accessory example to learn how Bluetooth LE advertising is enabled for the given example. -### Step 3: Discover Matter accessory device over Bluetooth LE +### Step 3: Discover commissionable Matter accessory device -An uncommissioned accessory device advertises over Bluetooth LE. Run the -following command to scan all advertised Matter devices: +An uncommissioned accessory device advertises over Bluetooth LE or via mDNS if +already on the network. Run the following command to scan all advertised Matter +devices: ``` -chip-device-ctrl > ble-scan +devCtrl.DiscoverCommissionableNodes() ``` ### Step 4: Set network pairing credentials @@ -177,11 +169,12 @@ network interface, such as Thread or Wi-Fi. datasets directly from the Thread Border Router, you might also use a different out-of-band method. -2. Set the previously obtained Active Operational Dataset as a hex-encoded value - using the following command: +2. Set the previously obtained Active Operational Dataset as a byte array using + the following command: ``` - chip-device-ctrl > set-pairing-thread-credential 0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8 + thread_dataset = bytes.fromhex("0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8") + devCtrl.SetThreadOperationalDataset(thread_dataset) ``` #### Setting Wi-Fi network credentials @@ -190,11 +183,9 @@ Assuming your Wi-Fi SSID is _TESTSSID_, and your Wi-Fi password is _P455W4RD_, set the credentials to the controller by executing the following command: ``` -chip-device-ctrl > set-pairing-wifi-credential TESTSSID P455W4RD +devCtrl.SetWiFiCredentials(, ) ``` -**REPL Command**: `devCtrl.SetWiFiCredentials(, )` - ### Step 5: Commission the Matter accessory device over Bluetooth LE The controller uses a 12-bit value called **discriminator** to discern between @@ -222,16 +213,26 @@ with the following assumptions for the Matter accessory device: - The temporary Node ID is _1234_ ``` -chip-device-ctrl > connect -ble 3840 20202021 1234 +devCtrl.ConnectBLE(3840, 20202021, 1234) ``` -**REPL Command:** -`devCtrl.ConnectBLE(, , )` - You can skip the last parameter, the Node ID, in the command. If you skip it, the controller will assign it randomly. In that case, note down the Node ID, because it is required later in the configuration process. +It is also possible to use the QR setup code instead. It typically is shown on +the terminal of the device as well. For example: + +``` +CHIP:SVR: SetupQRCode: [MT:-24J0AFN00KA0648G00] +``` + +Use the following command to commission the device with the QR code: + +``` +devCtrl.CommissionWithCode("MT:-24J0AFN00KA0648G00", 1234) +``` + After connecting the device over Bluetooth LE, the controller will go through the following stages: @@ -255,429 +256,155 @@ the following stages: finished and the Python CHIP controller is now using only the IPv6 traffic to reach the device. -### Step 6: Control application ZCL clusters. +### Step 6: Control application clusters. For the light bulb example, execute the following command to toggle the LED state: ``` -chip-device-ctrl > zcl OnOff Toggle 1234 1 0 +await devCtrl.SendCommand(1234, 1, Clusters.OnOff.Commands.Toggle()) ``` -**REPL Command:** -`await devCtrl.SendCommand(1234, 1, Clusters.OnOff.Commands.Toggle())` - To change the brightness of the LED, use the following command, with the level value somewhere between 0 and 255. ``` -chip-device-ctrl > zcl LevelControl MoveToLevel 1234 1 0 level=50 +commandToSend = LevelControl.Commands.MoveToLevel(level=50, transitionTime=Null, optionsMask=0, optionsOverride=0) +await devCtrl.SendCommand(1234, 1, commandToSend) ``` -**REPL Command:** -`await devCtrl.SendCommand(1234, 1, LevelControl.Commands.MoveToLevel(level=50, transitionTime=Null, optionsMask=0, optionsOverride=0))` - ### Step 7: Read basic information out of the accessory. Every Matter accessory device supports a Basic Information Cluster, which maintains collection of attributes that a controller can obtain from a device, -such as the vendor name, the product name, or software version. Use `zclread` -command to read those values from the device: +such as the vendor name, the product name, or software version. Use +`ReadAttribute()` command to read those values from the device: ``` -chip-device-ctrl > zclread BasicInformation VendorName 1234 1 0 -chip-device-ctrl > zclread BasicInformation ProductName 1234 1 0 -chip-device-ctrl > zclread BasicInformation SoftwareVersion 1234 1 0 +attributes = [ + (0, Clusters.BasicInformation.Attributes.VendorName), + (0, Clusters.BasicInformation.Attributes.ProductName), + (0, Clusters.BasicInformation.Attributes.SoftwareVersion), +] +await devCtrl.ReadAttribute(1234, attributes) ``` -**REPL Command:** -`await devCtrl.ReadAttribute(1234, [(1, Clusters.BasicInformation.Attributes.VendorName)])` - -> Use the `zcl ? BasicInformation` command to list all available commands for -> Basic Information Cluster. -> > In REPL, you can type `Clusters.BasicInformation.Attributes.` and then use the > TAB key.
    -## List of commands - -### `ble-adapter-print` +## Example usage of the Python CHIP Controller REPL -> BLE adapter operations is not yet supported in REPL +These section covers a few useful commands of the Python CHIP Controller along +with examples demonstrating how they can be called from the REPL. -Print the available Bluetooth adapters on device. Takes no arguments: - -``` -chip-device-ctrl > ble-adapter-print -2021-03-04 16:09:40,930 ChipBLEMgr INFO AdapterName: hci0 AdapterAddress: 00:AA:01:00:00:23 -``` +The +[CHIP Device Controller API documentation offer](https://project-chip.github.io/connectedhomeip-doc/testing/ChipDeviceCtrlAPI.html#chip-chipdevicectrl) +the full list of available commands. -### `ble-debug-log` - -> BLE adapter operations is not yet supported in REPL - -Enable the Bluetooth LE debug logs. - -``` -chip-device-ctrl > ble-debug-log 1 -``` - -### `ble-scan [-t ] [identifier]` - -> BLE adapter operations is not yet supported in REPL - -Start a scan action to search for valid CHIP devices over Bluetooth LE (for at -most _timeout_ seconds). Stop when the device is matching the identifier or the -counter times out. - -``` -chip-device-ctrl > ble-scan -2021-05-29 22:28:05,461 ChipBLEMgr INFO scanning started -2021-05-29 22:28:07,206 ChipBLEMgr INFO Name = ChipLight -2021-05-29 22:28:07,206 ChipBLEMgr INFO ID = f016e23d-0d00-35d5-93e7-588acdbc7e54 -2021-05-29 22:28:07,207 ChipBLEMgr INFO RSSI = -79 -2021-05-29 22:28:07,207 ChipBLEMgr INFO Address = E0:4D:84:3C:BB:C3 -2021-05-29 22:28:07,209 ChipBLEMgr INFO Pairing State = 0 -2021-05-29 22:28:07,209 ChipBLEMgr INFO Discriminator = 3840 -2021-05-29 22:28:07,209 ChipBLEMgr INFO Vendor Id = 9050 -2021-05-29 22:28:07,209 ChipBLEMgr INFO Product Id = 20044 -2021-05-29 22:28:07,210 ChipBLEMgr INFO Adv UUID = 0000fff6-0000-1000-8000-00805f9b34fb -2021-05-29 22:28:07,210 ChipBLEMgr INFO Adv Data = 00000f5a234c4e -2021-05-29 22:28:07,210 ChipBLEMgr INFO -2021-05-29 22:28:16,246 ChipBLEMgr INFO scanning stopped -``` - -### `set-pairing-thread-credential ` +### `SetThreadOperationalDataset()` Provides the controller with Thread network credentials that will be used in the device commissioning procedure to configure the device with a Thread interface. ``` -chip-device-ctrl > set-pairing-thread-credential 0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8 +thread_dataset = bytes.fromhex("0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8") +devCtrl.SetThreadOperationalDataset(thread_dataset) ``` -**REPL Commands:** -`devCtrl.SetThreadOperationalDataset(bytes.FromHex("0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8"))` - -### `set-pairing-wifi-credential ` +### `SetWiFiCredentials(: str, : str)` Provides the controller with Wi-Fi network credentials that will be used in the device commissioning procedure to configure the device with a Wi-Fi interface. ``` -chip-device-ctrl > set-pairing-wifi-credential TESTSSID P455W4RD +devCtrl.SetWiFiCredentials('TESTSSID', 'P455W4RD') ``` -**REPL Commands:** `devCtrl.SetWiFiCredentials('TESTSSID', 'P455W4RD')` - -### `connect -ip
    []` - -Do key exchange and establish a secure session between controller and device -using IP transport. - -The Node ID will be used by controller to distinguish multiple devices. This -does not match the spec and will be removed later. The nodeid will not be -persisted by controller / device. - -If no nodeid given, a random Node ID will be used. - -**REPL Commands:** -`devCtrl.CommissionIP(b'', , )` - -### `connect -ble []` - -Do key exchange and establish a secure session between controller and device -using Bluetooth LE transport. - -The Node ID will be used by controller to distinguish multiple devices. This -does not match the spec and will be removed later. The nodeid will not be -persisted by controller / device. - -If no nodeid given, a random Node ID will be used. - -**REPL Commands:** -`devCtrl.ConnectBLE(, , )` +### `CommissionWithCode(: str, : int, : DiscoveryType)` -### `close-session ` +Commission with the given nodeid from the setupPayload. setupPayload may be a QR +or the manual setup code. -If case there exists an open session (PASE or CASE) to the device with a given -Node ID, mark it as expired. - -**REPL Commands:** `devCtrl.CloseSession()` - -### `discover` - -> To be implemented in REPL - -Discover available Matter accessory devices: - -``` -chip-device-ctrl > discover -all ``` - -### `resolve ` - -> To be implemented in REPL - -Resolve DNS-SD name corresponding with the given Node ID and update address of -the node in the device controller: - -``` -chip-device-ctrl > resolve 1234 +devCtrl.CommissionWithCode("MT:-24J0AFN00KA0648G00", 1234) ``` -### `setup-payload generate [-v ] [-p ] [-cf ] [-dc ] [-dv ] [-ps ]` - -> To be implemented in REPL +### `SendCommand(: int, : int, Clusters..Commands.())` -Print the generated Onboarding Payload Contents in human-readable (Manual -Pairing Code) and machine-readable (QR Code) format: +Send a Matter command to the device. For example: +```python +commandToSend = Clusters.LevelControl.Commands.MoveWithOnOff(moveMode=1, rate=2, optionsMask=0, optionsOverride=0) +await devCtrl.SendCommand(1234, 1, commandToSend) ``` -chip-device-ctrl > setup-payload generate -v 9050 -p 65279 -cf 0 -dc 2 -dv 2976 -ps 34567890 -Manual pairing code: [26318621095] -SetupQRCode: [MT:YNJV7VSC00CMVH7SR00] -``` - -### `setup-payload parse-manual ` - -> To be implemented in REPL - -Print the commissioning information encoded in the Manual Pairing Code: - -``` -chip-device-ctrl > setup-payload parse-manual 34970112332 -Version: 0 -VendorID: 0 -ProductID: 0 -CommissioningFlow: 0 -RendezvousInformation: 0 -Discriminator: 3840 -SetUpPINCode: 20202021 -``` - -### `setup-payload parse-qr ` -> To be implemented in REPL - -Print the commissioning information encoded in the QR Code payload: +To see available arguments just create a command object without argument: ``` -chip-device-ctrl > setup-payload parse-qr "VP:vendorpayload%MT:W0GU2OTB00KA0648G00" -Version: 0 -VendorID: 9050 -ProductID: 20043 -CommissioningFlow: 0 -RendezvousInformation: 2 [BLE] -Discriminator: 3840 -SetUpPINCode: 20202021 +Clusters.LevelControl.Commands.MoveWithOnOff() ``` -### `zcl [arguments]` - -Send a ZCL command to the device. For example: +Shows which arguments are available: ``` -chip-device-ctrl > zcl LevelControl MoveWithOnOff 12344321 1 0 moveMode=1 rate=2 +MoveWithOnOff( +│ moveMode=0, +│ rate=Null, +│ optionsMask=0, +│ optionsOverride=0 +) ``` -**Format of arguments** +### `ReadAttribute(: int, [(: int, Clusters..Attributes.)])` -For any integer and char string (null terminated) types, just use `key=value`, -for example: `rate=2`, `string=123`, `string_2="123 456"` - -For byte string type, use `key=encoding:value`, currently, we support `str` and -`hex` encoding, the `str` encoding will encode a NULL terminated string. For -example, `networkId=hex:0123456789abcdef` (for -`[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]`), `ssid=str:Test` (for -`['T', 'e', 's', 't', 0x00]`). - -For boolean type, use `key=True` or `key=False`. - -**REPL Commands:** +Read the value of an attribute. For example: ```python -# await devCtrl.SendCommand(, , Clusters..Commands.()) -# e.g. -await devCtrl.SendCommand(12344321, 1, Clusters.LevelControl.Commands.MoveWithOnOff(moveMode=1, rate=2, optionsMask=0, optionsOverride=0)) -``` - -### `zcl ?` - -List available clusters: - -``` -chip-device-ctrl > zcl ? -AccountLogin -ApplicationBasic -ApplicationLauncher -AudioOutput -BarrierControl -BasicInformation -Binding -BridgedDeviceBasicInformation -ColorControl -ContentLaunch -Descriptor -DoorLock -EthernetNetworkDiagnostics -FixedLabel -GeneralCommissioning -GeneralDiagnostics -GroupKeyManagement -Groups -Identify -KeypadInput -LevelControl -LowPower -MediaInput -MediaPlayback -NetworkCommissioning -OnOff -OperationalCredentials -PumpConfigurationAndControl -RelativeHumidityMeasurement -ScenesManagement -SoftwareDiagnostics -Switch -Channel -TargetNavigator -TemperatureMeasurement -TestCluster -Thermostat -TrustedRootCertificates -WakeOnLan -WindowCovering -``` - -**REPL Commands** - -Type `Clusters.` and hit TAB - -### `zcl ? ` - -List available commands in cluster. For example, for _Basic Information_ -cluster: - -``` -chip-device-ctrl > zcl ? BasicInformation -DataModelRevision -VendorName -VendorID -ProductName -ProductID -UserLabel -Location -HardwareVersion -HardwareVersionString -SoftwareVersion -SoftwareVersionString -ManufacturingDate -PartNumber -ProductURL -ProductLabel -SerialNumber -LocalConfigDisabled -ClusterRevision -``` - -**REPL Commands** - -Type `Clusters.(cluster name).Commands.` and hit TAB - -### `zclread [arguments]` - -Read the value of ZCL attribute. For example: - -``` -chip-device-ctrl > zclread BasicInformation VendorName 1234 1 0 -``` - -**REPL Commands** - -```python -# devCtrl.ReadAttribute(, [(, Clusters..Attributes.)]) -# e.g. -await devCtrl.ReadAttribute(1234, [(1, Clusters.BasicInformation.Attributes.VendorName)]) -``` - -### `zclwrite ` - -Write the value to a ZCL attribute. For example: - -``` -chip-device-ctrl > zclwrite TestCluster Int8u 1 1 0 1 -chip-device-ctrl > zclwrite TestCluster Boolean 1 1 0 True -chip-device-ctrl > zclwrite TestCluster OctetString 1 1 0 str:123123 -chip-device-ctrl > zclwrite TestCluster CharString 1 1 0 233233 +await devCtrl.ReadAttribute(1234, [(0, Clusters.BasicInformation.Attributes.VendorName)]) ``` -Note: The format of the value is the same as the format of argument values for -ZCL cluster commands. +### `WriteAttribute(: int, [(: int, Clusters..Attributes.(value=))])` -**REPL Commands** +Write a value to an attribute. For example: ```python -# devCtrl.WriteAttribute(, [(, Clusters..Attributes.(value=))]) -# e.g. -await devCtrl.WriteAttribute(1, [(1, Clusters.UnitTesting.Attributes.Int8u(value=1))]) -await devCtrl.WriteAttribute(1, [(1, Clusters.UnitTesting.Attributes.Boolean(value=True))]) -await devCtrl.WriteAttribute(1, [(1, Clusters.UnitTesting.Attributes.OctetString(value=b'123123\x00'))]) -await devCtrl.WriteAttribute(1, [(1, Clusters.UnitTesting.Attributes.CharString(value='233233'))]) +await devCtrl.WriteAttribute(1234, [(1, Clusters.UnitTesting.Attributes.Int8u(value=1))]) +await devCtrl.WriteAttribute(1234, [(1, Clusters.UnitTesting.Attributes.Boolean(value=True))]) +await devCtrl.WriteAttribute(1234, [(1, Clusters.UnitTesting.Attributes.OctetString(value=b'123123\x00'))]) +await devCtrl.WriteAttribute(1234, [(1, Clusters.UnitTesting.Attributes.CharString(value='233233'))]) ``` -### `zclsubscribe ` +### `ReadAttribute(: int, [(: int, Clusters..Attributes.)], reportInterval=(: int, : int))` -Configure ZCL attribute reporting settings. For example: - -``` -chip-device-ctrl > zclsubscribe OccupancySensing Occupancy 1234 1 10 20 -``` - -**REPL Commands** +Configure Matter attribute reporting settings. For example: ```python -# devCtrl.ReadAttribute(, [(, Clusters..Attributes.)], reportInterval=(, )) -# e.g. -await devCtrl.ReadAttribute(1, [(1, Clusters.OccupancySensing.Attributes.Occupancy)], reportInterval=(10, 20)) +await devCtrl.ReadAttribute(1234, [(1, Clusters.OccupancySensing.Attributes.Occupancy)], reportInterval=(10, 20)) ``` -### `zclsubscribe -shutdown ` - -Shutdown an existing attribute subscription. +To shutdown an existing attribute subscription use the `Shutdown()` function on +the returned subscription object: -``` -chip-device-ctrl > zclsubscribe -shutdown 0xdeadbeefcafe +```python +sub = await devCtrl.ReadAttribute(1234, [(1, Clusters.OccupancySensing.Attributes.Occupancy)], reportInterval=(10, 20)) +sub.Shutdown() ``` -The subscription id can be obtained from previous subscription messages: +## Explore Clusters, Attributes and Commands -``` -chip-device-ctrl > zclsubscribe OnOff OnOff 1 1 10 20 -(omitted messages) -[1633922898.965587][1117858:1117866] CHIP:DMG: SubscribeResponse = -[1633922898.965599][1117858:1117866] CHIP:DMG: { -[1633922898.965610][1117858:1117866] CHIP:DMG: SubscriptionId = 0xdeadbeefcafe, -[1633922898.965622][1117858:1117866] CHIP:DMG: MinIntervalFloorSeconds = 0xa, -[1633922898.965633][1117858:1117866] CHIP:DMG: MaxIntervalCeilingSeconds = 0x14, -[1633922898.965644][1117858:1117866] CHIP:DMG: } -[1633922898.965662][1117858:1117866] CHIP:ZCL: SubscribeResponse: -[1633922898.965673][1117858:1117866] CHIP:ZCL: SubscriptionId: 0xdeadbeefcafe -[1633922898.965683][1117858:1117866] CHIP:ZCL: ApplicationIdentifier: 0 -[1633922898.965694][1117858:1117866] CHIP:ZCL: status: EMBER_ZCL_STATUS_SUCCESS (0x00) -[1633922898.965709][1117858:1117866] CHIP:ZCL: attributeValue: false -(omitted messages) -``` +In the Python REPL the Clusters and Attributes are classes. The `Clusters` +module contains all clusters. Tab completion can be used to explore available +clusters, attributes and commands. -The subscription id is `0xdeadbeefcafe` in this case +For example, to get a list of Clusters, type `Clusters.` and hit tab. Continue +to hit tab to cycle through the available Clusters. Pressing return will select +the Cluster. -**REPL Commands** +To explore Attributes, use the same technique but with the Attributes sub-class +of the Clusters class, for example, type `Clusters.(cluster name).Attributes.` +and hit tab. -```python -# SubscriptionTransaction.Shutdown() -# e.g. -sub = await devCtrl.ReadAttribute(1, [(1, Clusters.OccupancySensing.Attributes.Occupancy)], reportInterval=(10, 20)) -sub.Shutdown() -``` +The same is true for Commands, use the Commands sub-class. type +`Clusters.(cluster name).Commands.` and hit tab. diff --git a/docs/testing/yaml.md b/docs/testing/yaml.md index 107c761fbef71e..e7353d09697e30 100644 --- a/docs/testing/yaml.md +++ b/docs/testing/yaml.md @@ -279,6 +279,17 @@ function can be use. See [TestEqualities](https://github.com/project-chip/connectedhomeip/blob/master/src/app/tests/suites/TestEqualities.yaml) for an example of how to use this pseudo-cluster. +#### Setting step timeouts + +The timeout argument can be used for each individual test step to set the time +the runner will wait for a test step to complete before reporting a failure. + +Note that this timeout is different than the subscription report timeout and the +subscription report timeout is not currently adjustable in YAML. + +There several other options for configuring test steps as shown in the +[YAML schema](./yaml_schema.md) document. + ## Running YAML tests YAML scripts are parsed and run using a python-based runner program that parses @@ -304,6 +315,24 @@ There are several options for running tests locally. Because the YAML runner uses python, it is necessary to compile and install the chip python package before using any YAML runner script. +First activate the matter environment using either + +``` +. ./scripts/bootstrap.sh +``` + +or + +``` +. ./scripts/activate.sh +``` + +bootstrap.sh should be used for for the first setup, activate.sh may be used for +subsequent setups as it is faster. + +Next build the python wheels and create a venv (called `py` here, but any name +may be used) + ``` ./scripts/build_python.sh -i py source py/bin/activate diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index 2c54d75c7e6b06..719293ab5f877a 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -2609,7 +2609,7 @@ cluster BooleanState = 69 { } /** This cluster supports remotely monitoring and, where supported, changing the operational state of an Oven. */ -provisional cluster OvenCavityOperationalState = 72 { +cluster OvenCavityOperationalState = 72 { revision 1; enum ErrorStateEnum : enum8 { @@ -2675,7 +2675,7 @@ provisional cluster OvenCavityOperationalState = 72 { } /** Attributes and commands for selecting a mode from a list of supported options. */ -provisional cluster OvenMode = 73 { +cluster OvenMode = 73 { revision 1; enum ModeTag : enum16 { @@ -3752,7 +3752,7 @@ cluster ActivatedCarbonFilterMonitoring = 114 { } /** This cluster is used to configure a boolean sensor. */ -provisional cluster BooleanStateConfiguration = 128 { +cluster BooleanStateConfiguration = 128 { revision 1; bitmap AlarmModeBitmap : bitmap8 { @@ -3810,7 +3810,7 @@ provisional cluster BooleanStateConfiguration = 128 { } /** This cluster is used to configure a valve. */ -provisional cluster ValveConfigurationAndControl = 129 { +cluster ValveConfigurationAndControl = 129 { revision 1; enum StatusCodeEnum : enum8 { @@ -3876,7 +3876,7 @@ provisional cluster ValveConfigurationAndControl = 129 { } /** This cluster provides a mechanism for querying data about electrical power as measured by the server. */ -provisional cluster ElectricalPowerMeasurement = 144 { +cluster ElectricalPowerMeasurement = 144 { revision 1; enum MeasurementTypeEnum : enum16 { @@ -3981,7 +3981,7 @@ provisional cluster ElectricalPowerMeasurement = 144 { } /** This cluster provides a mechanism for querying data about the electrical energy imported or provided by the server. */ -provisional cluster ElectricalEnergyMeasurement = 145 { +cluster ElectricalEnergyMeasurement = 145 { revision 1; enum MeasurementTypeEnum : enum16 { @@ -4278,7 +4278,7 @@ provisional cluster DeviceEnergyManagement = 152 { } /** Electric Vehicle Supply Equipment (EVSE) is equipment used to charge an Electric Vehicle (EV) or Plug-In Hybrid Electric Vehicle. This cluster provides an interface to the functionality of Electric Vehicle Supply Equipment (EVSE) management. */ -provisional cluster EnergyEvse = 153 { +cluster EnergyEvse = 153 { revision 2; enum EnergyTransferStoppedReasonEnum : enum8 { @@ -4490,7 +4490,7 @@ provisional cluster EnergyPreference = 155 { } /** The Power Topology Cluster provides a mechanism for expressing how power is flowing between endpoints. */ -provisional cluster PowerTopology = 156 { +cluster PowerTopology = 156 { revision 1; bitmap Feature : bitmap32 { @@ -4511,7 +4511,7 @@ provisional cluster PowerTopology = 156 { } /** Attributes and commands for selecting a mode from a list of supported options. */ -provisional cluster EnergyEvseMode = 157 { +cluster EnergyEvseMode = 157 { revision 1; enum ModeTag : enum16 { diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt index f5a451f240f0e3..2b2078dde5e688 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt @@ -238,6 +238,16 @@ class CHIPToolActivity : } } + override fun onResume() { + super.onResume() + ChipClient.startDnssd(this) + } + + override fun onPause() { + ChipClient.stopDnssd(this) + super.onPause() + } + companion object { private const val TAG = "CHIPToolActivity" private const val ADDRESS_COMMISSIONING_FRAGMENT_TAG = "address_commissioning_fragment" diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt index bee06caf2b50d6..de8e598ee0ed9c 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt @@ -114,6 +114,21 @@ object ChipClient { icdCheckInCallback = callback } + fun startDnssd(context: Context) { + if (!this::chipDeviceController.isInitialized) { + getDeviceController(context) + } else { + chipDeviceController.startDnssd() + } + } + + fun stopDnssd(context: Context) { + if (!this::chipDeviceController.isInitialized) { + getDeviceController(context) + } + chipDeviceController.stopDnssd() + } + /** * Wrapper around [ChipDeviceController.getConnectedDevicePointer] to return the value directly. */ diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/AddressUpdateFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/AddressUpdateFragment.kt index 43eeb2b892c51c..81693c94d355cf 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/AddressUpdateFragment.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/AddressUpdateFragment.kt @@ -230,6 +230,10 @@ class AddressUpdateFragment : ICDCheckInCallback, Fragment() { val runnable = object : Runnable { override fun run() { + if (!isAdded) { + Log.d(TAG, "Fragment is not attached") + return + } if (icdTotalRemainStayActiveTimeMs >= ICD_PROGRESS_STEP) { icdDeviceRemainStayActiveTimeMs -= ICD_PROGRESS_STEP icdTotalRemainStayActiveTimeMs -= ICD_PROGRESS_STEP diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/setuppayloadscanner/CHIPDeviceDetailsFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/setuppayloadscanner/CHIPDeviceDetailsFragment.kt index ea131c0e726bcb..411f51eae3de73 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/setuppayloadscanner/CHIPDeviceDetailsFragment.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/setuppayloadscanner/CHIPDeviceDetailsFragment.kt @@ -61,7 +61,6 @@ class CHIPDeviceDetailsFragment : Fragment() { binding.discriminatorEd.setText(deviceInfo.discriminator.toString()) binding.serialNumberEd.setText(deviceInfo.serialNumber) binding.discoveryCapabilitiesTv.text = "${deviceInfo.discoveryCapabilities}" - if (deviceInfo.optionalQrCodeInfoMap.isEmpty()) { binding.vendorTagsLabelTv.visibility = View.GONE binding.vendorTagsContainer.visibility = View.GONE diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/setuppayloadscanner/CHIPDeviceInfo.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/setuppayloadscanner/CHIPDeviceInfo.kt index 28cfaa11f162c6..b2afd8a9985474 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/setuppayloadscanner/CHIPDeviceInfo.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/setuppayloadscanner/CHIPDeviceInfo.kt @@ -24,6 +24,7 @@ import kotlinx.parcelize.Parcelize import matter.onboardingpayload.DiscoveryCapability import matter.onboardingpayload.OnboardingPayload import matter.onboardingpayload.OnboardingPayloadException +import matter.onboardingpayload.OptionalQRCodeInfoType /** Class to hold the CHIP device information. */ @Parcelize @@ -57,6 +58,13 @@ data class CHIPDeviceInfo( if (serialNumber.isNotEmpty()) { onboardingPayload.addSerialNumber(serialNumber) } + optionalQrCodeInfoMap.forEach { (_, info) -> + if (info.type == OptionalQRCodeInfoType.TYPE_STRING && info.data != null) { + onboardingPayload.addOptionalVendorData(info.tag, info.data) + } else { + onboardingPayload.addOptionalVendorData(info.tag, info.intDataValue) + } + } return onboardingPayload } @@ -78,8 +86,8 @@ data class CHIPDeviceInfo( setupPayload.getLongDiscriminatorValue(), setupPayload.setupPinCode, setupPayload.commissioningFlow, - setupPayload.optionalQRCodeInfo.mapValues { (_, info) -> - QrCodeInfo(info.tag, info.type, info.data, info.int32) + setupPayload.getAllOptionalVendorData().associate { info -> + info.tag to QrCodeInfo(info.tag, info.type, info.data, info.int32) }, setupPayload.discoveryCapabilities, setupPayload.hasShortDiscriminator, diff --git a/examples/android/CHIPTool/app/src/main/res/layout/chip_device_info_fragment.xml b/examples/android/CHIPTool/app/src/main/res/layout/chip_device_info_fragment.xml index 4997681993ca29..a1deab648adc9f 100644 --- a/examples/android/CHIPTool/app/src/main/res/layout/chip_device_info_fragment.xml +++ b/examples/android/CHIPTool/app/src/main/res/layout/chip_device_info_fragment.xml @@ -151,7 +151,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" - android:layout_below="@id/vendorTagsLabelTv" + android:layout_below="@id/vendorTagsContainer" android:layout_alignParentStart="true" android:textSize="20sp"/> + android:textSize="12sp"/> diff --git a/examples/bridge-app/linux/main.cpp b/examples/bridge-app/linux/main.cpp index 3643f5ea1eb02a..a894b5d0150ca6 100644 --- a/examples/bridge-app/linux/main.cpp +++ b/examples/bridge-app/linux/main.cpp @@ -899,13 +899,11 @@ void ApplicationInit() // Setup Mock Devices Light1.SetReachable(true); Light2.SetReachable(true); - Light1.SetChangeCallback(&HandleDeviceOnOffStatusChanged); Light2.SetChangeCallback(&HandleDeviceOnOffStatusChanged); TempSensor1.SetReachable(true); - TempSensor1.SetReachable(true); - + TempSensor2.SetReachable(true); TempSensor1.SetChangeCallback(&HandleDeviceTempSensorStatusChanged); TempSensor2.SetChangeCallback(&HandleDeviceTempSensorStatusChanged); @@ -914,7 +912,6 @@ void ApplicationInit() ActionLight2.SetReachable(true); ActionLight3.SetReachable(true); ActionLight4.SetReachable(true); - ActionLight1.SetChangeCallback(&HandleDeviceOnOffStatusChanged); ActionLight2.SetChangeCallback(&HandleDeviceOnOffStatusChanged); ActionLight3.SetChangeCallback(&HandleDeviceOnOffStatusChanged); @@ -929,7 +926,6 @@ void ApplicationInit() ComposedTempSensor2.SetReachable(true); ComposedPowerSource.SetReachable(true); ComposedPowerSource.SetBatChargeLevel(58); - ComposedTempSensor1.SetChangeCallback(&HandleDeviceTempSensorStatusChanged); ComposedTempSensor2.SetChangeCallback(&HandleDeviceTempSensorStatusChanged); ComposedPowerSource.SetChangeCallback(&HandleDevicePowerSourceStatusChanged); diff --git a/examples/chef/chef.py b/examples/chef/chef.py index 816840f8584a45..d3b9fae0ec06ff 100755 --- a/examples/chef/chef.py +++ b/examples/chef/chef.py @@ -696,11 +696,20 @@ def main() -> int: if options.build_target == "esp32": shell.run_cmd(f"cd {_CHEF_SCRIPT_PATH}/esp32") if options.enable_ipv4: - shell.run_cmd( - "sed -i 's/CONFIG_DISABLE_IPV4=y/#\\ CONFIG_DISABLE_IPV4\\ is\\ not\\ set/g' sdkconfig ") + if sys.platform == "darwin": + shell.run_cmd( + "sed -i '' 's/CONFIG_DISABLE_IPV4=y/#\\ CONFIG_DISABLE_IPV4\\ is\\ not\\ set/g' sdkconfig ") + else: + shell.run_cmd( + "sed -i 's/CONFIG_DISABLE_IPV4=y/#\\ CONFIG_DISABLE_IPV4\\ is\\ not\\ set/g' sdkconfig ") else: - shell.run_cmd( - "sed -i 's/#\\ CONFIG_DISABLE_IPV4\\ is\\ not\\ set/CONFIG_DISABLE_IPV4=y/g' sdkconfig ") + if sys.platform == "darwin": + shell.run_cmd( + "sed -i '' 's/#\\ CONFIG_DISABLE_IPV4\\ is\\ not\\ set/CONFIG_DISABLE_IPV4=y/g' sdkconfig ") + else: + shell.run_cmd( + "sed -i 's/#\\ CONFIG_DISABLE_IPV4\\ is\\ not\\ set/CONFIG_DISABLE_IPV4=y/g' sdkconfig ") + shell.run_cmd("idf.py build") shell.run_cmd("idf.py build flashing_script") shell.run_cmd( diff --git a/examples/chef/common/chef-descriptor-namespace.h b/examples/chef/common/chef-descriptor-namespace.h new file mode 100644 index 00000000000000..e7e8e85df5a551 --- /dev/null +++ b/examples/chef/common/chef-descriptor-namespace.h @@ -0,0 +1,51 @@ +/* + * + * 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. + */ + +#pragma once + +// Please refer to https://github.com/CHIP-Specifications/connectedhomeip-spec/blob/master/src/namespaces +constexpr const uint8_t kNamespaceCommonLevel = 5; +// Common Number Namespace: 5, tag 0 (Low) +constexpr const uint8_t kTagCommonLow = 0; +// Common Number Namespace: 5, tag 1 (Medium) +constexpr const uint8_t kTagCommonMedium = 1; +// Common Number Namespace: 5, tag 2 (High) +constexpr const uint8_t kTagCommonHigh = 2; + +constexpr const uint8_t kNamespaceCommonNumber = 7; +// Common Number Namespace: 7, tag 0 (Zero) +constexpr const uint8_t kTagCommonZero = 0; +// Common Number Namespace: 7, tag 1 (One) +constexpr const uint8_t kTagCommonOne = 1; +// Common Number Namespace: 7, tag 2 (Two) +constexpr const uint8_t kTagCommonTwo = 2; + +constexpr const uint8_t kNamespacePosition = 8; +// Common Position Namespace: 8, tag: 0 (Left) +constexpr const uint8_t kTagPositionLeft = 0; +// Common Position Namespace: 8, tag: 1 (Right) +constexpr const uint8_t kTagPositionRight = 1; +// Common Position Namespace: 8, tag: 2 (Top) +constexpr const uint8_t kTagPositionTop = 2; +// Common Position Namespace: 8, tag: 3 (Bottom) +constexpr const uint8_t kTagPositionBottom = 3; +// Common Position Namespace: 8, tag: 4 (Middle) +constexpr const uint8_t kTagPositionMiddle = 4; +// Common Position Namespace: 8, tag: 5 (Row) +constexpr const uint8_t kTagPositionRow = 5; +// Common Position Namespace: 8, tag: 6 (Column) +constexpr const uint8_t kTagPositionColumn = 6; diff --git a/examples/chef/common/chef-rpc-actions-worker.cpp b/examples/chef/common/chef-rpc-actions-worker.cpp new file mode 100644 index 00000000000000..98bba768e7f4d8 --- /dev/null +++ b/examples/chef/common/chef-rpc-actions-worker.cpp @@ -0,0 +1,158 @@ +/* + * + * 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 "chef-rpc-actions-worker.h" +#include +#include +#include +#include +#include +#include +#include + +using chip::app::DataModel::Nullable; + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::rpc; + +static std::map gActionsDelegateMap{}; + +ActionsDelegate * RpcFindActionsDelegate(ClusterId clusterId) +{ + if (gActionsDelegateMap.find(clusterId) != gActionsDelegateMap.end()) + { + return gActionsDelegateMap[clusterId]; + } + + return nullptr; +} + +static void RpcActionsTaskCallback(System::Layer * systemLayer, void * data) +{ + ChefRpcActionsWorker * worker = (ChefRpcActionsWorker *) data; + + worker->ProcessActionQueue(); +} + +bool ChefRpcActionsCallback(EndpointId endpointId, ClusterId clusterId, uint8_t type, uint32_t delayMs, uint32_t actionId, + std::vector args) +{ + ActionTask task(endpointId, clusterId, static_cast(type), delayMs, actionId, args); + + return ChefRpcActionsWorker::Instance().EnqueueAction(task); +} + +bool ChefRpcActionsWorker::EnqueueAction(ActionTask task) +{ + bool kickTimer = false; + + if (queue.empty()) + { + kickTimer = true; // kick timer when the first task is adding to the queue + } + + queue.push(task); + + if (kickTimer) + { + (void) DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(task.delayMs), RpcActionsTaskCallback, this); + } + return true; +} + +void ChefRpcActionsWorker::ProcessActionQueue() +{ + // Dequeue the first item + ActionTask task = queue.front(); + queue.pop(); + + ActionsDelegate * delegate = RpcFindActionsDelegate(task.clusterId); + if (nullptr == delegate) + { + ChipLogError(NotSpecified, + "Cannot run action due to not finding delegate: endpointId=%d, clusterId=%04lx, attributeId=%04lx", + task.endpointId, static_cast(task.clusterId), static_cast(task.actionId)); + return; + } + + ActionType type = static_cast(task.type); + + switch (type) + { + case ActionType::WRITE_ATTRIBUTE: { + ChipLogProgress(NotSpecified, "Writing Attribute: endpointId=%d, clusterId=%04lx, attributeId=%04lx, args.size=%lu", + task.endpointId, static_cast(task.clusterId), static_cast(task.actionId), + static_cast(task.args.size())); + delegate->AttributeWriteHandler(task.endpointId, static_cast(task.actionId), task.args); + } + break; + case ActionType::RUN_COMMAND: { + ChipLogProgress(NotSpecified, "Running Command: endpointId=%d, clusterId=%04lx, commandId=%04lx, args.size=%lu", + task.endpointId, static_cast(task.clusterId), static_cast(task.actionId), + static_cast(task.args.size())); + delegate->CommandHandler(task.endpointId, static_cast(task.actionId), task.args); + } + break; + case ActionType::EMIT_EVENT: { + ChipLogProgress(NotSpecified, "Emitting Event: endpointId=%d, clusterId=%04lx, eventIdId=%04lx, args.size=%lu", + task.endpointId, static_cast(task.clusterId), static_cast(task.actionId), + static_cast(task.args.size())); + delegate->EventHandler(task.endpointId, static_cast(task.actionId), task.args); + } + break; + default: + break; + } + + if (queue.empty()) + { + // Return due to no more actions in queue + return; + } + + // Run next action + task = queue.front(); + ChipLogProgress(NotSpecified, "StartTimer: endpointId=%d, clusterId=%04lx, eventIdId=%04lx, task.delyMs=%lu", task.endpointId, + static_cast(task.clusterId), static_cast(task.actionId), + static_cast(task.delayMs)); + (void) DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(task.delayMs), RpcActionsTaskCallback, this); +} + +void ChefRpcActionsWorker::RegisterRpcActionsDelegate(ClusterId clusterId, ActionsDelegate * delegate) +{ + // Register by cluster + if (nullptr == RpcFindActionsDelegate(clusterId)) + { + gActionsDelegateMap[clusterId] = delegate; + return; + } +} + +ChefRpcActionsWorker::ChefRpcActionsWorker() +{ + chip::rpc::SubscribeActions(ChefRpcActionsCallback); +} + +static ChefRpcActionsWorker instance; + +ChefRpcActionsWorker & ChefRpcActionsWorker::Instance() +{ + return instance; +} diff --git a/examples/chef/common/chef-rpc-actions-worker.h b/examples/chef/common/chef-rpc-actions-worker.h new file mode 100644 index 00000000000000..3ca16f4c8a716e --- /dev/null +++ b/examples/chef/common/chef-rpc-actions-worker.h @@ -0,0 +1,79 @@ +/* + * + * 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 "Rpc.h" + +namespace chip { +namespace app { + +class ActionsDelegate +{ +public: + ActionsDelegate(ClusterId clusterId) : mClusterId(clusterId){}; + + virtual ~ActionsDelegate() = default; + + virtual void AttributeWriteHandler(chip::EndpointId endpointId, chip::AttributeId attributeId, std::vector args) = 0; + virtual void CommandHandler(chip::EndpointId endpointId, chip::CommandId commandId, std::vector args) = 0; + virtual void EventHandler(chip::EndpointId endpointId, chip::EventId eventId, std::vector args) = 0; + +protected: + ClusterId mClusterId; +}; + +struct ActionTask +{ + chip::EndpointId endpointId; + chip::ClusterId clusterId; + chip::rpc::ActionType type; // Aligned with Storage buf + uint32_t delayMs; + uint32_t actionId; + std::vector args; + ActionTask(chip::EndpointId endpoint, chip::ClusterId cluster, chip::rpc::ActionType actionType, uint32_t delay, uint32_t id, + std::vector arg) : + endpointId(endpoint), + clusterId(cluster), type(actionType), delayMs(delay), actionId(id), args(arg){}; + ~ActionTask(){}; +}; + +class ChefRpcActionsWorker +{ +public: + static ChefRpcActionsWorker & Instance(); + + ChefRpcActionsWorker(); + + bool EnqueueAction(ActionTask task); + void ProcessActionQueue(); + void RegisterRpcActionsDelegate(ClusterId clusterId, ActionsDelegate * delegate); + +private: + std::queue queue; +}; + +} // namespace app +} // namespace chip diff --git a/examples/chef/common/clusters/switch/SwitchEventHandler.cpp b/examples/chef/common/clusters/switch/SwitchEventHandler.cpp new file mode 100644 index 00000000000000..dd32e2b907bb5a --- /dev/null +++ b/examples/chef/common/clusters/switch/SwitchEventHandler.cpp @@ -0,0 +1,82 @@ +/* + * + * Copyright (c) 2022 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 +#ifdef MATTER_DM_PLUGIN_SWITCH_SERVER +#include +#include +#include +#include + +#include "SwitchEventHandler.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::Switch; +using namespace chip::DeviceLayer; + +void SwitchEventHandler::OnSwitchLatched(EndpointId endpointId, uint8_t newPosition) +{ + ChipLogDetail(NotSpecified, "%s: endpointId=%d, newPosition=%d", __func__, endpointId, newPosition); + + Clusters::SwitchServer::Instance().OnSwitchLatch(endpointId, newPosition); +} + +void SwitchEventHandler::OnInitialPress(EndpointId endpointId, uint8_t newPosition) +{ + ChipLogDetail(NotSpecified, "%s: endpointId=%d, newPosition=%d", __func__, endpointId, newPosition); + + Clusters::SwitchServer::Instance().OnInitialPress(endpointId, newPosition); +} + +void SwitchEventHandler::OnLongPress(EndpointId endpointId, uint8_t newPosition) +{ + ChipLogDetail(NotSpecified, "%s: endpointId=%d, newPosition=%d", __func__, endpointId, newPosition); + + Clusters::SwitchServer::Instance().OnLongPress(endpointId, newPosition); +} + +void SwitchEventHandler::OnShortRelease(EndpointId endpointId, uint8_t previousPosition) +{ + ChipLogDetail(NotSpecified, "%s: endpointId=%d, previousPosition=%d", __func__, endpointId, previousPosition); + + Clusters::SwitchServer::Instance().OnShortRelease(endpointId, previousPosition); +} + +void SwitchEventHandler::OnLongRelease(EndpointId endpointId, uint8_t previousPosition) +{ + ChipLogDetail(NotSpecified, "%s: endpointId=%d, previousPosition=%d", __func__, endpointId, previousPosition); + + Clusters::SwitchServer::Instance().OnLongRelease(endpointId, previousPosition); +} + +void SwitchEventHandler::OnMultiPressOngoing(EndpointId endpointId, uint8_t newPosition, uint8_t count) +{ + ChipLogDetail(NotSpecified, "%s: endpointId=%d, newPosition=%d, count=%d", __func__, endpointId, newPosition, count); + + Clusters::SwitchServer::Instance().OnMultiPressOngoing(endpointId, newPosition, count); +} + +void SwitchEventHandler::OnMultiPressComplete(EndpointId endpointId, uint8_t previousPosition, uint8_t count) +{ + ChipLogDetail(NotSpecified, "%s: endpointId=%d, previousPosition=%d, count=%d", __func__, endpointId, previousPosition, count); + + Clusters::SwitchServer::Instance().OnMultiPressComplete(endpointId, previousPosition, count); +} +#endif // MATTER_DM_PLUGIN_SWITCH_SERVER diff --git a/examples/chef/common/clusters/switch/SwitchEventHandler.h b/examples/chef/common/clusters/switch/SwitchEventHandler.h new file mode 100644 index 00000000000000..1648e19cc829c5 --- /dev/null +++ b/examples/chef/common/clusters/switch/SwitchEventHandler.h @@ -0,0 +1,80 @@ +/* + * + * Copyright (c) 2022 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 + +namespace chip { +namespace app { +namespace Clusters { + +namespace Switch { + +class SwitchEventHandler +{ +public: + SwitchEventHandler(){}; + + /** + * Should be called when the latching switch is moved to a new position. + */ + void OnSwitchLatched(EndpointId endpointId, uint8_t newPosition); + + /** + * Should be called when the momentary switch starts to be pressed. + */ + void OnInitialPress(EndpointId endpointId, uint8_t newPosition); + + /** + * Should be called when the momentary switch has been pressed for a "long" time. + */ + void OnLongPress(EndpointId endpointId, uint8_t newPosition); + + /** + * Should be called when the momentary switch has been released. + */ + void OnShortRelease(EndpointId endpointId, uint8_t previousPosition); + + /** + * Should be called when the momentary switch has been released after having been pressed for a long time. + */ + void OnLongRelease(EndpointId endpointId, uint8_t previousPosition); + + /** + * Should be called to indicate how many times the momentary switch has been pressed in a multi-press + * sequence, during that sequence. + */ + void OnMultiPressOngoing(EndpointId endpointId, uint8_t newPosition, uint8_t count); + + /** + * Should be called to indicate how many times the momentary switch has been pressed in a multi-press + * sequence, after it has been detected that the sequence has ended. + */ + void OnMultiPressComplete(EndpointId endpointId, uint8_t previousPosition, uint8_t count); + +private: +}; + +} // namespace Switch +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/chef/common/clusters/switch/SwitchManager.cpp b/examples/chef/common/clusters/switch/SwitchManager.cpp new file mode 100644 index 00000000000000..45d39dd4bce1a4 --- /dev/null +++ b/examples/chef/common/clusters/switch/SwitchManager.cpp @@ -0,0 +1,172 @@ +/* + * + * Copyright (c) 2022 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. + */ + +#ifdef MATTER_DM_PLUGIN_SWITCH_SERVER +#include "SwitchEventHandler.h" +#include +#include +#include +#include +#include +#include + +#include "chef-descriptor-namespace.h" +#include "chef-rpc-actions-worker.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::Switch; +using namespace chip::DeviceLayer; + +using namespace chip::rpc; +using namespace chip::app; + +class SwitchActionsDelegate : public chip::app::ActionsDelegate +{ +public: + SwitchActionsDelegate(ClusterId clusterId, SwitchEventHandler * eventHandler) : + ActionsDelegate(clusterId), mEventHandler(eventHandler){}; + ~SwitchActionsDelegate() override{}; + + void AttributeWriteHandler(chip::EndpointId endpointId, chip::AttributeId attributeId, std::vector args) override; + void CommandHandler(chip::EndpointId endpointId, chip::AttributeId attributeId, std::vector args) override{}; + void EventHandler(chip::EndpointId endpointId, chip::EventId eventId, std::vector args) override; + +private: + SwitchEventHandler * mEventHandler; +}; + +void SwitchActionsDelegate::AttributeWriteHandler(chip::EndpointId endpointId, chip::AttributeId attributeId, + std::vector args) +{ + if (args.empty()) + { + ChipLogError(NotSpecified, "Queue is empty "); + return; + } + + switch (attributeId) + { + case Switch::Attributes::NumberOfPositions::Id: { + uint8_t data = static_cast(args[0]); + app::Clusters::Switch::Attributes::NumberOfPositions::Set(endpointId, data); + } + break; + case Switch::Attributes::CurrentPosition::Id: { + uint8_t data = static_cast(args[0]); + app::Clusters::Switch::Attributes::CurrentPosition::Set(endpointId, data); + } + break; + case Switch::Attributes::MultiPressMax::Id: { + uint8_t data = static_cast(args[0]); + app::Clusters::Switch::Attributes::MultiPressMax::Set(endpointId, data); + } + break; + default: + break; + } +} + +void SwitchActionsDelegate::EventHandler(chip::EndpointId endpointId, chip::EventId eventId, std::vector args) +{ + if (args.empty()) + { + ChipLogError(NotSpecified, "Queue is empty "); + return; + } + switch (eventId) + { + case Events::SwitchLatched::Id: { + uint8_t newPosition = static_cast(args[0]); + mEventHandler->OnSwitchLatched(endpointId, newPosition); + } + break; + case Events::InitialPress::Id: { + uint8_t newPosition = static_cast(args[0]); + mEventHandler->OnInitialPress(endpointId, newPosition); + } + break; + case Events::LongPress::Id: { + uint8_t newPosition = static_cast(args[0]); + mEventHandler->OnLongPress(endpointId, newPosition); + } + break; + case Events::ShortRelease::Id: { + uint8_t previousPosition = static_cast(args[0]); + mEventHandler->OnShortRelease(endpointId, previousPosition); + } + break; + case Events::LongRelease::Id: { + uint8_t previousPosition = static_cast(args[0]); + mEventHandler->OnLongRelease(endpointId, previousPosition); + } + break; + case Events::MultiPressOngoing::Id: { + if (args.size() < 2) + { + ChipLogError(NotSpecified, "MultiPressOngoing has too few arguments"); + return; + } + uint8_t newPosition = static_cast(args[0]); + uint8_t currentNumberOfPressesCounted = static_cast(args[1]); + mEventHandler->OnMultiPressOngoing(endpointId, newPosition, currentNumberOfPressesCounted); + } + break; + case Events::MultiPressComplete::Id: { + if (args.size() < 2) + { + ChipLogError(NotSpecified, "MultiPressComplete has too few arguments"); + return; + } + uint8_t previousPosition = static_cast(args[0]); + uint8_t totalNumberOfPressesCounted = static_cast(args[1]); + mEventHandler->OnMultiPressComplete(endpointId, previousPosition, totalNumberOfPressesCounted); + } + break; + default: + break; + } +}; + +const Clusters::Descriptor::Structs::SemanticTagStruct::Type gLatchingSwitch[] = { + { .namespaceID = kNamespaceCommonLevel, + .tag = kTagCommonLow, + .label = chip::Optional>( + { chip::app::DataModel::MakeNullable(chip::CharSpan("Low", 3)) }) }, + { .namespaceID = kNamespaceCommonLevel, + .tag = kTagCommonMedium, + .label = chip::Optional>( + { chip::app::DataModel::MakeNullable(chip::CharSpan("Medium", 6)) }) }, + { .namespaceID = kNamespaceCommonLevel, + .tag = kTagCommonHigh, + .label = chip::Optional>( + { chip::app::DataModel::MakeNullable(chip::CharSpan("High", 4)) }) } +}; + +static SwitchEventHandler * gSwitchEventHandler = new SwitchEventHandler(); +static SwitchActionsDelegate * gSwitchActionsDelegate = new SwitchActionsDelegate(Clusters::Switch::Id, gSwitchEventHandler); + +void emberAfSwitchClusterInitCallback(EndpointId endpointId) +{ + ChipLogProgress(Zcl, "Chef: emberAfSwitchClusterInitCallback"); + + ChefRpcActionsWorker::Instance().RegisterRpcActionsDelegate(Clusters::Switch::Id, gSwitchActionsDelegate); + SetTagList(/* endpoint= */ 1, Span(gLatchingSwitch)); +} +#endif // MATTER_DM_PLUGIN_SWITCH_SERVER diff --git a/examples/chef/common/stubs.cpp b/examples/chef/common/stubs.cpp index 5756aaa35b7e26..437dfe9e6221be 100644 --- a/examples/chef/common/stubs.cpp +++ b/examples/chef/common/stubs.cpp @@ -236,6 +236,16 @@ void emberAfWakeOnLanClusterInitCallback(EndpointId endpoint) } #endif +void ApplicationInit() +{ + ChipLogProgress(NotSpecified, "Chef Application Init !!!") +} + +void ApplicationShutdown() +{ + ChipLogProgress(NotSpecified, "Chef Application Down !!!") +} + // No-op function, used to force linking this file, // instead of the weak functions from other files extern "C" void chef_include_stubs_impl(void) {} diff --git a/examples/chef/devices/rootnode_genericswitch_2dfff6e516.matter b/examples/chef/devices/rootnode_genericswitch_2dfff6e516.matter new file mode 100644 index 00000000000000..89319a4b61b4e2 --- /dev/null +++ b/examples/chef/devices/rootnode_genericswitch_2dfff6e516.matter @@ -0,0 +1,1498 @@ +// This IDL was generated automatically by ZAP. +// It is for view/code review purposes only. + +/** Attributes and commands for putting a device into Identification mode (e.g. flashing a light). */ +cluster Identify = 3 { + revision 4; + + enum EffectIdentifierEnum : enum8 { + kBlink = 0; + kBreathe = 1; + kOkay = 2; + kChannelChange = 11; + kFinishEffect = 254; + kStopEffect = 255; + } + + enum EffectVariantEnum : enum8 { + kDefault = 0; + } + + enum IdentifyTypeEnum : enum8 { + kNone = 0; + kLightOutput = 1; + kVisibleIndicator = 2; + kAudibleBeep = 3; + kDisplay = 4; + kActuator = 5; + } + + attribute int16u identifyTime = 0; + readonly attribute IdentifyTypeEnum identifyType = 1; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct IdentifyRequest { + int16u identifyTime = 0; + } + + request struct TriggerEffectRequest { + EffectIdentifierEnum effectIdentifier = 0; + EffectVariantEnum effectVariant = 1; + } + + /** Command description for Identify */ + command access(invoke: manage) Identify(IdentifyRequest): DefaultSuccess = 0; + /** Command description for TriggerEffect */ + command access(invoke: manage) TriggerEffect(TriggerEffectRequest): DefaultSuccess = 64; +} + +/** The Descriptor Cluster is meant to replace the support from the Zigbee Device Object (ZDO) for describing a node, its endpoints and clusters. */ +cluster Descriptor = 29 { + revision 2; + + bitmap Feature : bitmap32 { + kTagList = 0x1; + } + + struct DeviceTypeStruct { + devtype_id deviceType = 0; + int16u revision = 1; + } + + struct SemanticTagStruct { + nullable vendor_id mfgCode = 0; + enum8 namespaceID = 1; + enum8 tag = 2; + optional nullable char_string label = 3; + } + + readonly attribute DeviceTypeStruct deviceTypeList[] = 0; + readonly attribute cluster_id serverList[] = 1; + readonly attribute cluster_id clientList[] = 2; + readonly attribute endpoint_no partsList[] = 3; + readonly attribute optional SemanticTagStruct tagList[] = 4; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; +} + +/** The Access Control Cluster exposes a data model view of a + Node's Access Control List (ACL), which codifies the rules used to manage + and enforce Access Control for the Node's endpoints and their associated + cluster instances. */ +cluster AccessControl = 31 { + revision 1; // NOTE: Default/not specifically set + + enum AccessControlEntryAuthModeEnum : enum8 { + kPASE = 1; + kCASE = 2; + kGroup = 3; + } + + enum AccessControlEntryPrivilegeEnum : enum8 { + kView = 1; + kProxyView = 2; + kOperate = 3; + kManage = 4; + kAdminister = 5; + } + + enum ChangeTypeEnum : enum8 { + kChanged = 0; + kAdded = 1; + kRemoved = 2; + } + + struct AccessControlTargetStruct { + nullable cluster_id cluster = 0; + nullable endpoint_no endpoint = 1; + nullable devtype_id deviceType = 2; + } + + fabric_scoped struct AccessControlEntryStruct { + fabric_sensitive AccessControlEntryPrivilegeEnum privilege = 1; + fabric_sensitive AccessControlEntryAuthModeEnum authMode = 2; + nullable fabric_sensitive int64u subjects[] = 3; + nullable fabric_sensitive AccessControlTargetStruct targets[] = 4; + fabric_idx fabricIndex = 254; + } + + fabric_scoped struct AccessControlExtensionStruct { + fabric_sensitive octet_string<128> data = 1; + fabric_idx fabricIndex = 254; + } + + fabric_sensitive info event access(read: administer) AccessControlEntryChanged = 0 { + nullable node_id adminNodeID = 1; + nullable int16u adminPasscodeID = 2; + ChangeTypeEnum changeType = 3; + nullable AccessControlEntryStruct latestValue = 4; + fabric_idx fabricIndex = 254; + } + + fabric_sensitive info event access(read: administer) AccessControlExtensionChanged = 1 { + nullable node_id adminNodeID = 1; + nullable int16u adminPasscodeID = 2; + ChangeTypeEnum changeType = 3; + nullable AccessControlExtensionStruct latestValue = 4; + fabric_idx fabricIndex = 254; + } + + attribute access(read: administer, write: administer) AccessControlEntryStruct acl[] = 0; + attribute access(read: administer, write: administer) optional AccessControlExtensionStruct extension[] = 1; + readonly attribute int16u subjectsPerAccessControlEntry = 2; + readonly attribute int16u targetsPerAccessControlEntry = 3; + readonly attribute int16u accessControlEntriesPerFabric = 4; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; +} + +/** This cluster provides attributes and events for determining basic information about Nodes, which supports both + Commissioning and operational determination of Node characteristics, such as Vendor ID, Product ID and serial number, + which apply to the whole Node. Also allows setting user device information such as location. */ +cluster BasicInformation = 40 { + revision 3; + + enum ColorEnum : enum8 { + kBlack = 0; + kNavy = 1; + kGreen = 2; + kTeal = 3; + kMaroon = 4; + kPurple = 5; + kOlive = 6; + kGray = 7; + kBlue = 8; + kLime = 9; + kAqua = 10; + kRed = 11; + kFuchsia = 12; + kYellow = 13; + kWhite = 14; + kNickel = 15; + kChrome = 16; + kBrass = 17; + kCopper = 18; + kSilver = 19; + kGold = 20; + } + + enum ProductFinishEnum : enum8 { + kOther = 0; + kMatte = 1; + kSatin = 2; + kPolished = 3; + kRugged = 4; + kFabric = 5; + } + + struct CapabilityMinimaStruct { + int16u caseSessionsPerFabric = 0; + int16u subscriptionsPerFabric = 1; + } + + struct ProductAppearanceStruct { + ProductFinishEnum finish = 0; + nullable ColorEnum primaryColor = 1; + } + + critical event StartUp = 0 { + int32u softwareVersion = 0; + } + + critical event ShutDown = 1 { + } + + info event Leave = 2 { + fabric_idx fabricIndex = 0; + } + + info event ReachableChanged = 3 { + boolean reachableNewValue = 0; + } + + readonly attribute int16u dataModelRevision = 0; + readonly attribute char_string<32> vendorName = 1; + readonly attribute vendor_id vendorID = 2; + readonly attribute char_string<32> productName = 3; + readonly attribute int16u productID = 4; + attribute access(write: manage) char_string<32> nodeLabel = 5; + attribute access(write: administer) char_string<2> location = 6; + readonly attribute int16u hardwareVersion = 7; + readonly attribute char_string<64> hardwareVersionString = 8; + readonly attribute int32u softwareVersion = 9; + readonly attribute char_string<64> softwareVersionString = 10; + readonly attribute optional char_string<16> manufacturingDate = 11; + readonly attribute optional char_string<32> partNumber = 12; + readonly attribute optional long_char_string<256> productURL = 13; + readonly attribute optional char_string<64> productLabel = 14; + readonly attribute optional char_string<32> serialNumber = 15; + attribute access(write: manage) optional boolean localConfigDisabled = 16; + readonly attribute optional boolean reachable = 17; + readonly attribute optional char_string<32> uniqueID = 18; + readonly attribute CapabilityMinimaStruct capabilityMinima = 19; + readonly attribute optional ProductAppearanceStruct productAppearance = 20; + readonly attribute int32u specificationVersion = 21; + readonly attribute int16u maxPathsPerInvoke = 22; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + command MfgSpecificPing(): DefaultSuccess = 0; +} + +/** This cluster is used to describe the configuration and capabilities of a physical power source that provides power to the Node. */ +cluster PowerSource = 47 { + revision 1; // NOTE: Default/not specifically set + + enum BatApprovedChemistryEnum : enum16 { + kUnspecified = 0; + kAlkaline = 1; + kLithiumCarbonFluoride = 2; + kLithiumChromiumOxide = 3; + kLithiumCopperOxide = 4; + kLithiumIronDisulfide = 5; + kLithiumManganeseDioxide = 6; + kLithiumThionylChloride = 7; + kMagnesium = 8; + kMercuryOxide = 9; + kNickelOxyhydride = 10; + kSilverOxide = 11; + kZincAir = 12; + kZincCarbon = 13; + kZincChloride = 14; + kZincManganeseDioxide = 15; + kLeadAcid = 16; + kLithiumCobaltOxide = 17; + kLithiumIon = 18; + kLithiumIonPolymer = 19; + kLithiumIronPhosphate = 20; + kLithiumSulfur = 21; + kLithiumTitanate = 22; + kNickelCadmium = 23; + kNickelHydrogen = 24; + kNickelIron = 25; + kNickelMetalHydride = 26; + kNickelZinc = 27; + kSilverZinc = 28; + kSodiumIon = 29; + kSodiumSulfur = 30; + kZincBromide = 31; + kZincCerium = 32; + } + + enum BatChargeFaultEnum : enum8 { + kUnspecified = 0; + kAmbientTooHot = 1; + kAmbientTooCold = 2; + kBatteryTooHot = 3; + kBatteryTooCold = 4; + kBatteryAbsent = 5; + kBatteryOverVoltage = 6; + kBatteryUnderVoltage = 7; + kChargerOverVoltage = 8; + kChargerUnderVoltage = 9; + kSafetyTimeout = 10; + } + + enum BatChargeLevelEnum : enum8 { + kOK = 0; + kWarning = 1; + kCritical = 2; + } + + enum BatChargeStateEnum : enum8 { + kUnknown = 0; + kIsCharging = 1; + kIsAtFullCharge = 2; + kIsNotCharging = 3; + } + + enum BatCommonDesignationEnum : enum16 { + kUnspecified = 0; + kAAA = 1; + kAA = 2; + kC = 3; + kD = 4; + k4v5 = 5; + k6v0 = 6; + k9v0 = 7; + k12AA = 8; + kAAAA = 9; + kA = 10; + kB = 11; + kF = 12; + kN = 13; + kNo6 = 14; + kSubC = 15; + kA23 = 16; + kA27 = 17; + kBA5800 = 18; + kDuplex = 19; + k4SR44 = 20; + k523 = 21; + k531 = 22; + k15v0 = 23; + k22v5 = 24; + k30v0 = 25; + k45v0 = 26; + k67v5 = 27; + kJ = 28; + kCR123A = 29; + kCR2 = 30; + k2CR5 = 31; + kCRP2 = 32; + kCRV3 = 33; + kSR41 = 34; + kSR43 = 35; + kSR44 = 36; + kSR45 = 37; + kSR48 = 38; + kSR54 = 39; + kSR55 = 40; + kSR57 = 41; + kSR58 = 42; + kSR59 = 43; + kSR60 = 44; + kSR63 = 45; + kSR64 = 46; + kSR65 = 47; + kSR66 = 48; + kSR67 = 49; + kSR68 = 50; + kSR69 = 51; + kSR516 = 52; + kSR731 = 53; + kSR712 = 54; + kLR932 = 55; + kA5 = 56; + kA10 = 57; + kA13 = 58; + kA312 = 59; + kA675 = 60; + kAC41E = 61; + k10180 = 62; + k10280 = 63; + k10440 = 64; + k14250 = 65; + k14430 = 66; + k14500 = 67; + k14650 = 68; + k15270 = 69; + k16340 = 70; + kRCR123A = 71; + k17500 = 72; + k17670 = 73; + k18350 = 74; + k18500 = 75; + k18650 = 76; + k19670 = 77; + k25500 = 78; + k26650 = 79; + k32600 = 80; + } + + enum BatFaultEnum : enum8 { + kUnspecified = 0; + kOverTemp = 1; + kUnderTemp = 2; + } + + enum BatReplaceabilityEnum : enum8 { + kUnspecified = 0; + kNotReplaceable = 1; + kUserReplaceable = 2; + kFactoryReplaceable = 3; + } + + enum PowerSourceStatusEnum : enum8 { + kUnspecified = 0; + kActive = 1; + kStandby = 2; + kUnavailable = 3; + } + + enum WiredCurrentTypeEnum : enum8 { + kAC = 0; + kDC = 1; + } + + enum WiredFaultEnum : enum8 { + kUnspecified = 0; + kOverVoltage = 1; + kUnderVoltage = 2; + } + + bitmap Feature : bitmap32 { + kWired = 0x1; + kBattery = 0x2; + kRechargeable = 0x4; + kReplaceable = 0x8; + } + + struct BatChargeFaultChangeType { + BatChargeFaultEnum current[] = 0; + BatChargeFaultEnum previous[] = 1; + } + + struct BatFaultChangeType { + BatFaultEnum current[] = 0; + BatFaultEnum previous[] = 1; + } + + struct WiredFaultChangeType { + WiredFaultEnum current[] = 0; + WiredFaultEnum previous[] = 1; + } + + info event WiredFaultChange = 0 { + WiredFaultEnum current[] = 0; + WiredFaultEnum previous[] = 1; + } + + info event BatFaultChange = 1 { + BatFaultEnum current[] = 0; + BatFaultEnum previous[] = 1; + } + + info event BatChargeFaultChange = 2 { + BatChargeFaultEnum current[] = 0; + BatChargeFaultEnum previous[] = 1; + } + + readonly attribute PowerSourceStatusEnum status = 0; + readonly attribute int8u order = 1; + readonly attribute char_string<60> description = 2; + readonly attribute optional nullable int32u wiredAssessedInputVoltage = 3; + readonly attribute optional nullable int16u wiredAssessedInputFrequency = 4; + readonly attribute optional WiredCurrentTypeEnum wiredCurrentType = 5; + readonly attribute optional nullable int32u wiredAssessedCurrent = 6; + readonly attribute optional int32u wiredNominalVoltage = 7; + readonly attribute optional int32u wiredMaximumCurrent = 8; + readonly attribute optional boolean wiredPresent = 9; + readonly attribute optional WiredFaultEnum activeWiredFaults[] = 10; + readonly attribute optional nullable int32u batVoltage = 11; + readonly attribute optional nullable int8u batPercentRemaining = 12; + readonly attribute optional nullable int32u batTimeRemaining = 13; + readonly attribute optional BatChargeLevelEnum batChargeLevel = 14; + readonly attribute optional boolean batReplacementNeeded = 15; + readonly attribute optional BatReplaceabilityEnum batReplaceability = 16; + readonly attribute optional boolean batPresent = 17; + readonly attribute optional BatFaultEnum activeBatFaults[] = 18; + readonly attribute optional char_string<60> batReplacementDescription = 19; + readonly attribute optional BatCommonDesignationEnum batCommonDesignation = 20; + readonly attribute optional char_string<20> batANSIDesignation = 21; + readonly attribute optional char_string<20> batIECDesignation = 22; + readonly attribute optional BatApprovedChemistryEnum batApprovedChemistry = 23; + readonly attribute optional int32u batCapacity = 24; + readonly attribute optional int8u batQuantity = 25; + readonly attribute optional BatChargeStateEnum batChargeState = 26; + readonly attribute optional nullable int32u batTimeToFullCharge = 27; + readonly attribute optional boolean batFunctionalWhileCharging = 28; + readonly attribute optional nullable int32u batChargingCurrent = 29; + readonly attribute optional BatChargeFaultEnum activeBatChargeFaults[] = 30; + readonly attribute endpoint_no endpointList[] = 31; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; +} + +/** This cluster is used to manage global aspects of the Commissioning flow. */ +cluster GeneralCommissioning = 48 { + revision 1; // NOTE: Default/not specifically set + + enum CommissioningErrorEnum : enum8 { + kOK = 0; + kValueOutsideRange = 1; + kInvalidAuthentication = 2; + kNoFailSafe = 3; + kBusyWithOtherAdmin = 4; + } + + enum RegulatoryLocationTypeEnum : enum8 { + kIndoor = 0; + kOutdoor = 1; + kIndoorOutdoor = 2; + } + + struct BasicCommissioningInfo { + int16u failSafeExpiryLengthSeconds = 0; + int16u maxCumulativeFailsafeSeconds = 1; + } + + attribute access(write: administer) int64u breadcrumb = 0; + readonly attribute BasicCommissioningInfo basicCommissioningInfo = 1; + readonly attribute RegulatoryLocationTypeEnum regulatoryConfig = 2; + readonly attribute RegulatoryLocationTypeEnum locationCapability = 3; + readonly attribute boolean supportsConcurrentConnection = 4; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct ArmFailSafeRequest { + int16u expiryLengthSeconds = 0; + int64u breadcrumb = 1; + } + + response struct ArmFailSafeResponse = 1 { + CommissioningErrorEnum errorCode = 0; + char_string<128> debugText = 1; + } + + request struct SetRegulatoryConfigRequest { + RegulatoryLocationTypeEnum newRegulatoryConfig = 0; + char_string<2> countryCode = 1; + int64u breadcrumb = 2; + } + + response struct SetRegulatoryConfigResponse = 3 { + CommissioningErrorEnum errorCode = 0; + char_string debugText = 1; + } + + response struct CommissioningCompleteResponse = 5 { + CommissioningErrorEnum errorCode = 0; + char_string debugText = 1; + } + + /** Arm the persistent fail-safe timer with an expiry time of now + ExpiryLengthSeconds using device clock */ + command access(invoke: administer) ArmFailSafe(ArmFailSafeRequest): ArmFailSafeResponse = 0; + /** Set the regulatory configuration to be used during commissioning */ + command access(invoke: administer) SetRegulatoryConfig(SetRegulatoryConfigRequest): SetRegulatoryConfigResponse = 2; + /** Signals the Server that the Client has successfully completed all steps of Commissioning/Recofiguration needed during fail-safe period. */ + fabric command access(invoke: administer) CommissioningComplete(): CommissioningCompleteResponse = 4; +} + +/** Functionality to configure, enable, disable network credentials and access on a Matter device. */ +cluster NetworkCommissioning = 49 { + revision 1; // NOTE: Default/not specifically set + + enum NetworkCommissioningStatusEnum : enum8 { + kSuccess = 0; + kOutOfRange = 1; + kBoundsExceeded = 2; + kNetworkIDNotFound = 3; + kDuplicateNetworkID = 4; + kNetworkNotFound = 5; + kRegulatoryError = 6; + kAuthFailure = 7; + kUnsupportedSecurity = 8; + kOtherConnectionFailure = 9; + kIPV6Failed = 10; + kIPBindFailed = 11; + kUnknownError = 12; + } + + enum WiFiBandEnum : enum8 { + k2G4 = 0; + k3G65 = 1; + k5G = 2; + k6G = 3; + k60G = 4; + k1G = 5; + } + + bitmap Feature : bitmap32 { + kWiFiNetworkInterface = 0x1; + kThreadNetworkInterface = 0x2; + kEthernetNetworkInterface = 0x4; + kPerDeviceCredentials = 0x8; + } + + bitmap ThreadCapabilitiesBitmap : bitmap16 { + kIsBorderRouterCapable = 0x1; + kIsRouterCapable = 0x2; + kIsSleepyEndDeviceCapable = 0x4; + kIsFullThreadDevice = 0x8; + kIsSynchronizedSleepyEndDeviceCapable = 0x10; + } + + bitmap WiFiSecurityBitmap : bitmap8 { + kUnencrypted = 0x1; + kWEP = 0x2; + kWPAPersonal = 0x4; + kWPA2Personal = 0x8; + kWPA3Personal = 0x10; + kWPA3MatterPDC = 0x20; + } + + struct NetworkInfoStruct { + octet_string<32> networkID = 0; + boolean connected = 1; + optional nullable octet_string<20> networkIdentifier = 2; + optional nullable octet_string<20> clientIdentifier = 3; + } + + struct ThreadInterfaceScanResultStruct { + int16u panId = 0; + int64u extendedPanId = 1; + char_string<16> networkName = 2; + int16u channel = 3; + int8u version = 4; + octet_string<8> extendedAddress = 5; + int8s rssi = 6; + int8u lqi = 7; + } + + struct WiFiInterfaceScanResultStruct { + WiFiSecurityBitmap security = 0; + octet_string<32> ssid = 1; + octet_string<6> bssid = 2; + int16u channel = 3; + WiFiBandEnum wiFiBand = 4; + int8s rssi = 5; + } + + readonly attribute access(read: administer) int8u maxNetworks = 0; + readonly attribute access(read: administer) NetworkInfoStruct networks[] = 1; + readonly attribute optional int8u scanMaxTimeSeconds = 2; + readonly attribute optional int8u connectMaxTimeSeconds = 3; + attribute access(write: administer) boolean interfaceEnabled = 4; + readonly attribute access(read: administer) nullable NetworkCommissioningStatusEnum lastNetworkingStatus = 5; + readonly attribute access(read: administer) nullable octet_string<32> lastNetworkID = 6; + readonly attribute access(read: administer) nullable int32s lastConnectErrorValue = 7; + readonly attribute optional WiFiBandEnum supportedWiFiBands[] = 8; + readonly attribute optional ThreadCapabilitiesBitmap supportedThreadFeatures = 9; + readonly attribute optional int16u threadVersion = 10; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct ScanNetworksRequest { + optional nullable octet_string<32> ssid = 0; + optional int64u breadcrumb = 1; + } + + response struct ScanNetworksResponse = 1 { + NetworkCommissioningStatusEnum networkingStatus = 0; + optional char_string debugText = 1; + optional WiFiInterfaceScanResultStruct wiFiScanResults[] = 2; + optional ThreadInterfaceScanResultStruct threadScanResults[] = 3; + } + + request struct AddOrUpdateWiFiNetworkRequest { + octet_string<32> ssid = 0; + octet_string<64> credentials = 1; + optional int64u breadcrumb = 2; + optional octet_string<140> networkIdentity = 3; + optional octet_string<20> clientIdentifier = 4; + optional octet_string<32> possessionNonce = 5; + } + + request struct AddOrUpdateThreadNetworkRequest { + octet_string<254> operationalDataset = 0; + optional int64u breadcrumb = 1; + } + + request struct RemoveNetworkRequest { + octet_string<32> networkID = 0; + optional int64u breadcrumb = 1; + } + + response struct NetworkConfigResponse = 5 { + NetworkCommissioningStatusEnum networkingStatus = 0; + optional char_string<512> debugText = 1; + optional int8u networkIndex = 2; + optional octet_string<140> clientIdentity = 3; + optional octet_string<64> possessionSignature = 4; + } + + request struct ConnectNetworkRequest { + octet_string<32> networkID = 0; + optional int64u breadcrumb = 1; + } + + response struct ConnectNetworkResponse = 7 { + NetworkCommissioningStatusEnum networkingStatus = 0; + optional char_string debugText = 1; + nullable int32s errorValue = 2; + } + + request struct ReorderNetworkRequest { + octet_string<32> networkID = 0; + int8u networkIndex = 1; + optional int64u breadcrumb = 2; + } + + request struct QueryIdentityRequest { + octet_string<20> keyIdentifier = 0; + optional octet_string<32> possessionNonce = 1; + } + + response struct QueryIdentityResponse = 10 { + octet_string<140> identity = 0; + optional octet_string<64> possessionSignature = 1; + } + + /** Detemine the set of networks the device sees as available. */ + command access(invoke: administer) ScanNetworks(ScanNetworksRequest): ScanNetworksResponse = 0; + /** Add or update the credentials for a given Wi-Fi network. */ + command access(invoke: administer) AddOrUpdateWiFiNetwork(AddOrUpdateWiFiNetworkRequest): NetworkConfigResponse = 2; + /** Add or update the credentials for a given Thread network. */ + command access(invoke: administer) AddOrUpdateThreadNetwork(AddOrUpdateThreadNetworkRequest): NetworkConfigResponse = 3; + /** Remove the definition of a given network (including its credentials). */ + command access(invoke: administer) RemoveNetwork(RemoveNetworkRequest): NetworkConfigResponse = 4; + /** Connect to the specified network, using previously-defined credentials. */ + command access(invoke: administer) ConnectNetwork(ConnectNetworkRequest): ConnectNetworkResponse = 6; + /** Modify the order in which networks will be presented in the Networks attribute. */ + command access(invoke: administer) ReorderNetwork(ReorderNetworkRequest): NetworkConfigResponse = 8; + /** Retrieve details about and optionally proof of possession of a network client identity. */ + command access(invoke: administer) QueryIdentity(QueryIdentityRequest): QueryIdentityResponse = 9; +} + +/** The cluster provides commands for retrieving unstructured diagnostic logs from a Node that may be used to aid in diagnostics. */ +cluster DiagnosticLogs = 50 { + revision 1; // NOTE: Default/not specifically set + + enum IntentEnum : enum8 { + kEndUserSupport = 0; + kNetworkDiag = 1; + kCrashLogs = 2; + } + + enum StatusEnum : enum8 { + kSuccess = 0; + kExhausted = 1; + kNoLogs = 2; + kBusy = 3; + kDenied = 4; + } + + enum TransferProtocolEnum : enum8 { + kResponsePayload = 0; + kBDX = 1; + } + + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct RetrieveLogsRequestRequest { + IntentEnum intent = 0; + TransferProtocolEnum requestedProtocol = 1; + optional char_string<32> transferFileDesignator = 2; + } + + response struct RetrieveLogsResponse = 1 { + StatusEnum status = 0; + long_octet_string logContent = 1; + optional epoch_us UTCTimeStamp = 2; + optional systime_us timeSinceBoot = 3; + } + + /** Retrieving diagnostic logs from a Node */ + command RetrieveLogsRequest(RetrieveLogsRequestRequest): RetrieveLogsResponse = 0; +} + +/** The General Diagnostics Cluster, along with other diagnostics clusters, provide a means to acquire standardized diagnostics metrics that MAY be used by a Node to assist a user or Administrative Node in diagnosing potential problems. */ +cluster GeneralDiagnostics = 51 { + revision 2; + + enum BootReasonEnum : enum8 { + kUnspecified = 0; + kPowerOnReboot = 1; + kBrownOutReset = 2; + kSoftwareWatchdogReset = 3; + kHardwareWatchdogReset = 4; + kSoftwareUpdateCompleted = 5; + kSoftwareReset = 6; + } + + enum HardwareFaultEnum : enum8 { + kUnspecified = 0; + kRadio = 1; + kSensor = 2; + kResettableOverTemp = 3; + kNonResettableOverTemp = 4; + kPowerSource = 5; + kVisualDisplayFault = 6; + kAudioOutputFault = 7; + kUserInterfaceFault = 8; + kNonVolatileMemoryError = 9; + kTamperDetected = 10; + } + + enum InterfaceTypeEnum : enum8 { + kUnspecified = 0; + kWiFi = 1; + kEthernet = 2; + kCellular = 3; + kThread = 4; + } + + enum NetworkFaultEnum : enum8 { + kUnspecified = 0; + kHardwareFailure = 1; + kNetworkJammed = 2; + kConnectionFailed = 3; + } + + enum RadioFaultEnum : enum8 { + kUnspecified = 0; + kWiFiFault = 1; + kCellularFault = 2; + kThreadFault = 3; + kNFCFault = 4; + kBLEFault = 5; + kEthernetFault = 6; + } + + bitmap Feature : bitmap32 { + kDataModelTest = 0x1; + } + + struct NetworkInterface { + char_string<32> name = 0; + boolean isOperational = 1; + nullable boolean offPremiseServicesReachableIPv4 = 2; + nullable boolean offPremiseServicesReachableIPv6 = 3; + octet_string<8> hardwareAddress = 4; + octet_string IPv4Addresses[] = 5; + octet_string IPv6Addresses[] = 6; + InterfaceTypeEnum type = 7; + } + + critical event HardwareFaultChange = 0 { + HardwareFaultEnum current[] = 0; + HardwareFaultEnum previous[] = 1; + } + + critical event RadioFaultChange = 1 { + RadioFaultEnum current[] = 0; + RadioFaultEnum previous[] = 1; + } + + critical event NetworkFaultChange = 2 { + NetworkFaultEnum current[] = 0; + NetworkFaultEnum previous[] = 1; + } + + critical event BootReason = 3 { + BootReasonEnum bootReason = 0; + } + + readonly attribute NetworkInterface networkInterfaces[] = 0; + readonly attribute int16u rebootCount = 1; + readonly attribute optional int64u upTime = 2; + readonly attribute optional int32u totalOperationalHours = 3; + readonly attribute optional BootReasonEnum bootReason = 4; + readonly attribute optional HardwareFaultEnum activeHardwareFaults[] = 5; + readonly attribute optional RadioFaultEnum activeRadioFaults[] = 6; + readonly attribute optional NetworkFaultEnum activeNetworkFaults[] = 7; + readonly attribute boolean testEventTriggersEnabled = 8; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct TestEventTriggerRequest { + octet_string<16> enableKey = 0; + int64u eventTrigger = 1; + } + + response struct TimeSnapshotResponse = 2 { + systime_ms systemTimeMs = 0; + nullable posix_ms posixTimeMs = 1; + } + + request struct PayloadTestRequestRequest { + octet_string<16> enableKey = 0; + int8u value = 1; + int16u count = 2; + } + + response struct PayloadTestResponse = 4 { + octet_string payload = 0; + } + + /** Provide a means for certification tests to trigger some test-plan-specific events */ + command access(invoke: manage) TestEventTrigger(TestEventTriggerRequest): DefaultSuccess = 0; + /** Take a snapshot of system time and epoch time. */ + command TimeSnapshot(): TimeSnapshotResponse = 1; + /** Request a variable length payload response. */ + command PayloadTestRequest(PayloadTestRequestRequest): PayloadTestResponse = 3; +} + +/** This cluster exposes interactions with a switch device, for the purpose of using those interactions by other devices. +Two types of switch devices are supported: latching switch (e.g. rocker switch) and momentary switch (e.g. push button), distinguished with their feature flags. +Interactions with the switch device are exposed as attributes (for the latching switch) and as events (for both types of switches). An interested party MAY subscribe to these attributes/events and thus be informed of the interactions, and can perform actions based on this, for example by sending commands to perform an action such as controlling a light or a window shade. */ +cluster Switch = 59 { + revision 1; + + bitmap Feature : bitmap32 { + kLatchingSwitch = 0x1; + kMomentarySwitch = 0x2; + kMomentarySwitchRelease = 0x4; + kMomentarySwitchLongPress = 0x8; + kMomentarySwitchMultiPress = 0x10; + } + + info event SwitchLatched = 0 { + int8u newPosition = 0; + } + + info event InitialPress = 1 { + int8u newPosition = 0; + } + + info event LongPress = 2 { + int8u newPosition = 0; + } + + info event ShortRelease = 3 { + int8u previousPosition = 0; + } + + info event LongRelease = 4 { + int8u previousPosition = 0; + } + + info event MultiPressOngoing = 5 { + int8u newPosition = 0; + int8u currentNumberOfPressesCounted = 1; + } + + info event MultiPressComplete = 6 { + int8u previousPosition = 0; + int8u totalNumberOfPressesCounted = 1; + } + + readonly attribute int8u numberOfPositions = 0; + readonly attribute int8u currentPosition = 1; + readonly attribute optional int8u multiPressMax = 2; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; +} + +/** Commands to trigger a Node to allow a new Administrator to commission it. */ +cluster AdministratorCommissioning = 60 { + revision 1; // NOTE: Default/not specifically set + + enum CommissioningWindowStatusEnum : enum8 { + kWindowNotOpen = 0; + kEnhancedWindowOpen = 1; + kBasicWindowOpen = 2; + } + + enum StatusCode : enum8 { + kBusy = 2; + kPAKEParameterError = 3; + kWindowNotOpen = 4; + } + + bitmap Feature : bitmap32 { + kBasic = 0x1; + } + + readonly attribute CommissioningWindowStatusEnum windowStatus = 0; + readonly attribute nullable fabric_idx adminFabricIndex = 1; + readonly attribute nullable vendor_id adminVendorId = 2; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct OpenCommissioningWindowRequest { + int16u commissioningTimeout = 0; + octet_string PAKEPasscodeVerifier = 1; + int16u discriminator = 2; + int32u iterations = 3; + octet_string<32> salt = 4; + } + + request struct OpenBasicCommissioningWindowRequest { + int16u commissioningTimeout = 0; + } + + /** This command is used by a current Administrator to instruct a Node to go into commissioning mode using enhanced commissioning method. */ + timed command access(invoke: administer) OpenCommissioningWindow(OpenCommissioningWindowRequest): DefaultSuccess = 0; + /** This command is used by a current Administrator to instruct a Node to go into commissioning mode using basic commissioning method, if the node supports it. */ + timed command access(invoke: administer) OpenBasicCommissioningWindow(OpenBasicCommissioningWindowRequest): DefaultSuccess = 1; + /** This command is used by a current Administrator to instruct a Node to revoke any active Open Commissioning Window or Open Basic Commissioning Window command. */ + timed command access(invoke: administer) RevokeCommissioning(): DefaultSuccess = 2; +} + +/** This cluster is used to add or remove Operational Credentials on a Commissionee or Node, as well as manage the associated Fabrics. */ +cluster OperationalCredentials = 62 { + revision 1; // NOTE: Default/not specifically set + + enum CertificateChainTypeEnum : enum8 { + kDACCertificate = 1; + kPAICertificate = 2; + } + + enum NodeOperationalCertStatusEnum : enum8 { + kOK = 0; + kInvalidPublicKey = 1; + kInvalidNodeOpId = 2; + kInvalidNOC = 3; + kMissingCsr = 4; + kTableFull = 5; + kInvalidAdminSubject = 6; + kFabricConflict = 9; + kLabelConflict = 10; + kInvalidFabricIndex = 11; + } + + fabric_scoped struct FabricDescriptorStruct { + octet_string<65> rootPublicKey = 1; + vendor_id vendorID = 2; + fabric_id fabricID = 3; + node_id nodeID = 4; + char_string<32> label = 5; + fabric_idx fabricIndex = 254; + } + + fabric_scoped struct NOCStruct { + fabric_sensitive octet_string noc = 1; + nullable fabric_sensitive octet_string icac = 2; + fabric_idx fabricIndex = 254; + } + + readonly attribute access(read: administer) NOCStruct NOCs[] = 0; + readonly attribute FabricDescriptorStruct fabrics[] = 1; + readonly attribute int8u supportedFabrics = 2; + readonly attribute int8u commissionedFabrics = 3; + readonly attribute octet_string trustedRootCertificates[] = 4; + readonly attribute int8u currentFabricIndex = 5; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct AttestationRequestRequest { + octet_string<32> attestationNonce = 0; + } + + response struct AttestationResponse = 1 { + octet_string<900> attestationElements = 0; + octet_string<64> attestationSignature = 1; + } + + request struct CertificateChainRequestRequest { + CertificateChainTypeEnum certificateType = 0; + } + + response struct CertificateChainResponse = 3 { + octet_string<600> certificate = 0; + } + + request struct CSRRequestRequest { + octet_string<32> CSRNonce = 0; + optional boolean isForUpdateNOC = 1; + } + + response struct CSRResponse = 5 { + octet_string NOCSRElements = 0; + octet_string attestationSignature = 1; + } + + request struct AddNOCRequest { + octet_string<400> NOCValue = 0; + optional octet_string<400> ICACValue = 1; + octet_string<16> IPKValue = 2; + int64u caseAdminSubject = 3; + vendor_id adminVendorId = 4; + } + + request struct UpdateNOCRequest { + octet_string NOCValue = 0; + optional octet_string ICACValue = 1; + } + + response struct NOCResponse = 8 { + NodeOperationalCertStatusEnum statusCode = 0; + optional fabric_idx fabricIndex = 1; + optional char_string<128> debugText = 2; + } + + request struct UpdateFabricLabelRequest { + char_string<32> label = 0; + } + + request struct RemoveFabricRequest { + fabric_idx fabricIndex = 0; + } + + request struct AddTrustedRootCertificateRequest { + octet_string rootCACertificate = 0; + } + + /** Sender is requesting attestation information from the receiver. */ + command access(invoke: administer) AttestationRequest(AttestationRequestRequest): AttestationResponse = 0; + /** Sender is requesting a device attestation certificate from the receiver. */ + command access(invoke: administer) CertificateChainRequest(CertificateChainRequestRequest): CertificateChainResponse = 2; + /** Sender is requesting a certificate signing request (CSR) from the receiver. */ + command access(invoke: administer) CSRRequest(CSRRequestRequest): CSRResponse = 4; + /** Sender is requesting to add the new node operational certificates. */ + command access(invoke: administer) AddNOC(AddNOCRequest): NOCResponse = 6; + /** Sender is requesting to update the node operational certificates. */ + fabric command access(invoke: administer) UpdateNOC(UpdateNOCRequest): NOCResponse = 7; + /** This command SHALL be used by an Administrative Node to set the user-visible Label field for a given Fabric, as reflected by entries in the Fabrics attribute. */ + fabric command access(invoke: administer) UpdateFabricLabel(UpdateFabricLabelRequest): NOCResponse = 9; + /** This command is used by Administrative Nodes to remove a given fabric index and delete all associated fabric-scoped data. */ + command access(invoke: administer) RemoveFabric(RemoveFabricRequest): NOCResponse = 10; + /** This command SHALL add a Trusted Root CA Certificate, provided as its CHIP Certificate representation. */ + command access(invoke: administer) AddTrustedRootCertificate(AddTrustedRootCertificateRequest): DefaultSuccess = 11; +} + +/** The Group Key Management Cluster is the mechanism by which group keys are managed. */ +cluster GroupKeyManagement = 63 { + revision 1; // NOTE: Default/not specifically set + + enum GroupKeySecurityPolicyEnum : enum8 { + kTrustFirst = 0; + kCacheAndSync = 1; + } + + bitmap Feature : bitmap32 { + kCacheAndSync = 0x1; + } + + fabric_scoped struct GroupInfoMapStruct { + group_id groupId = 1; + endpoint_no endpoints[] = 2; + optional char_string<16> groupName = 3; + fabric_idx fabricIndex = 254; + } + + fabric_scoped struct GroupKeyMapStruct { + group_id groupId = 1; + int16u groupKeySetID = 2; + fabric_idx fabricIndex = 254; + } + + struct GroupKeySetStruct { + int16u groupKeySetID = 0; + GroupKeySecurityPolicyEnum groupKeySecurityPolicy = 1; + nullable octet_string<16> epochKey0 = 2; + nullable epoch_us epochStartTime0 = 3; + nullable octet_string<16> epochKey1 = 4; + nullable epoch_us epochStartTime1 = 5; + nullable octet_string<16> epochKey2 = 6; + nullable epoch_us epochStartTime2 = 7; + } + + attribute access(write: manage) GroupKeyMapStruct groupKeyMap[] = 0; + readonly attribute GroupInfoMapStruct groupTable[] = 1; + readonly attribute int16u maxGroupsPerFabric = 2; + readonly attribute int16u maxGroupKeysPerFabric = 3; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct KeySetWriteRequest { + GroupKeySetStruct groupKeySet = 0; + } + + request struct KeySetReadRequest { + int16u groupKeySetID = 0; + } + + response struct KeySetReadResponse = 2 { + GroupKeySetStruct groupKeySet = 0; + } + + request struct KeySetRemoveRequest { + int16u groupKeySetID = 0; + } + + response struct KeySetReadAllIndicesResponse = 5 { + int16u groupKeySetIDs[] = 0; + } + + /** Write a new set of keys for the given key set id. */ + fabric command access(invoke: administer) KeySetWrite(KeySetWriteRequest): DefaultSuccess = 0; + /** Read the keys for a given key set id. */ + fabric command access(invoke: administer) KeySetRead(KeySetReadRequest): KeySetReadResponse = 1; + /** Revoke a Root Key from a Group */ + fabric command access(invoke: administer) KeySetRemove(KeySetRemoveRequest): DefaultSuccess = 3; + /** Return the list of Group Key Sets associated with the accessing fabric */ + fabric command access(invoke: administer) KeySetReadAllIndices(): KeySetReadAllIndicesResponse = 4; +} + +endpoint 0 { + device type ma_rootdevice = 22, version 1; + + + server cluster Descriptor { + callback attribute deviceTypeList; + callback attribute serverList; + callback attribute clientList; + callback attribute partsList; + callback attribute featureMap; + callback attribute clusterRevision; + } + + server cluster AccessControl { + emits event AccessControlEntryChanged; + emits event AccessControlExtensionChanged; + callback attribute acl; + callback attribute extension; + callback attribute subjectsPerAccessControlEntry; + callback attribute targetsPerAccessControlEntry; + callback attribute accessControlEntriesPerFabric; + callback attribute attributeList; + ram attribute featureMap default = 0; + callback attribute clusterRevision; + } + + server cluster BasicInformation { + emits event StartUp; + emits event ShutDown; + emits event Leave; + callback attribute dataModelRevision; + callback attribute vendorName; + callback attribute vendorID; + callback attribute productName; + callback attribute productID; + persist attribute nodeLabel; + callback attribute location; + callback attribute hardwareVersion; + callback attribute hardwareVersionString; + callback attribute softwareVersion; + callback attribute softwareVersionString; + callback attribute manufacturingDate; + callback attribute partNumber; + callback attribute productURL; + callback attribute productLabel; + callback attribute serialNumber; + persist attribute localConfigDisabled default = 0; + callback attribute uniqueID; + callback attribute capabilityMinima; + callback attribute specificationVersion; + callback attribute maxPathsPerInvoke; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 3; + } + + server cluster GeneralCommissioning { + ram attribute breadcrumb default = 0x0000000000000000; + callback attribute basicCommissioningInfo; + callback attribute regulatoryConfig; + callback attribute locationCapability; + callback attribute supportsConcurrentConnection; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 0x0001; + + handle command ArmFailSafe; + handle command ArmFailSafeResponse; + handle command SetRegulatoryConfig; + handle command SetRegulatoryConfigResponse; + handle command CommissioningComplete; + handle command CommissioningCompleteResponse; + } + + server cluster NetworkCommissioning { + ram attribute maxNetworks; + callback attribute networks; + ram attribute scanMaxTimeSeconds; + ram attribute connectMaxTimeSeconds; + ram attribute interfaceEnabled; + ram attribute lastNetworkingStatus; + ram attribute lastNetworkID; + ram attribute lastConnectErrorValue; + ram attribute featureMap default = 1; + ram attribute clusterRevision default = 0x0001; + + handle command ScanNetworks; + handle command ScanNetworksResponse; + handle command AddOrUpdateWiFiNetwork; + handle command AddOrUpdateThreadNetwork; + handle command RemoveNetwork; + handle command NetworkConfigResponse; + handle command ConnectNetwork; + handle command ConnectNetworkResponse; + handle command ReorderNetwork; + } + + server cluster DiagnosticLogs { + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + + handle command RetrieveLogsRequest; + } + + server cluster GeneralDiagnostics { + emits event BootReason; + callback attribute networkInterfaces; + callback attribute rebootCount; + callback attribute upTime; + callback attribute totalOperationalHours; + callback attribute bootReason; + callback attribute activeHardwareFaults; + callback attribute activeRadioFaults; + callback attribute activeNetworkFaults; + callback attribute testEventTriggersEnabled default = false; + callback attribute featureMap; + callback attribute clusterRevision; + + handle command TestEventTrigger; + handle command TimeSnapshot; + handle command TimeSnapshotResponse; + } + + server cluster AdministratorCommissioning { + callback attribute windowStatus; + callback attribute adminFabricIndex; + callback attribute adminVendorId; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 0x0001; + + handle command OpenCommissioningWindow; + handle command OpenBasicCommissioningWindow; + handle command RevokeCommissioning; + } + + server cluster OperationalCredentials { + callback attribute NOCs; + callback attribute fabrics; + callback attribute supportedFabrics; + callback attribute commissionedFabrics; + callback attribute trustedRootCertificates; + callback attribute currentFabricIndex; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 0x0001; + + handle command AttestationRequest; + handle command AttestationResponse; + handle command CertificateChainRequest; + handle command CertificateChainResponse; + handle command CSRRequest; + handle command CSRResponse; + handle command AddNOC; + handle command UpdateNOC; + handle command NOCResponse; + handle command UpdateFabricLabel; + handle command RemoveFabric; + handle command AddTrustedRootCertificate; + } + + server cluster GroupKeyManagement { + callback attribute groupKeyMap; + callback attribute groupTable; + callback attribute maxGroupsPerFabric; + callback attribute maxGroupKeysPerFabric; + callback attribute featureMap; + callback attribute clusterRevision; + + handle command KeySetWrite; + handle command KeySetRead; + handle command KeySetReadResponse; + handle command KeySetRemove; + handle command KeySetReadAllIndices; + handle command KeySetReadAllIndicesResponse; + } +} +endpoint 1 { + device type ma_powersource = 17, version 1; + device type ma_genericswitch = 15, version 1; + + + server cluster Identify { + ram attribute identifyTime default = 0x0; + ram attribute identifyType default = 0x00; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 4; + + handle command Identify; + handle command TriggerEffect; + } + + server cluster Descriptor { + callback attribute deviceTypeList; + callback attribute serverList; + callback attribute clientList; + callback attribute partsList; + callback attribute tagList; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + callback attribute featureMap; + callback attribute clusterRevision; + } + + server cluster PowerSource { + persist attribute status default = 1; + persist attribute order default = 1; + persist attribute description default = "Battery"; + persist attribute batVoltage; + persist attribute batPercentRemaining default = 100; + persist attribute batTimeRemaining; + persist attribute batChargeLevel; + ram attribute batReplacementNeeded; + ram attribute batReplaceability; + ram attribute batPresent; + ram attribute batReplacementDescription; + persist attribute batQuantity default = 1; + callback attribute endpointList; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0x0A; + ram attribute clusterRevision default = 1; + } + + server cluster Switch { + emits event InitialPress; + emits event LongPress; + emits event ShortRelease; + emits event LongRelease; + persist attribute numberOfPositions default = 3; + persist attribute currentPosition default = 0; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0xE; + ram attribute clusterRevision default = 1; + } +} + + diff --git a/examples/chef/devices/rootnode_genericswitch_2dfff6e516.zap b/examples/chef/devices/rootnode_genericswitch_2dfff6e516.zap new file mode 100644 index 00000000000000..1f84a7aab45a71 --- /dev/null +++ b/examples/chef/devices/rootnode_genericswitch_2dfff6e516.zap @@ -0,0 +1,2804 @@ +{ + "fileFormat": 2, + "featureLevel": 102, + "creator": "zap", + "keyValuePairs": [ + { + "key": "commandDiscovery", + "value": "1" + }, + { + "key": "defaultResponsePolicy", + "value": "always" + }, + { + "key": "manufacturerCodes", + "value": "0x1002" + } + ], + "package": [ + { + "pathRelativity": "relativeToZap", + "path": "../../../src/app/zap-templates/zcl/zcl.json", + "type": "zcl-properties", + "category": "matter", + "version": 1, + "description": "Matter SDK ZCL data" + }, + { + "pathRelativity": "relativeToZap", + "path": "../../../src/app/zap-templates/app-templates.json", + "type": "gen-templates-json", + "category": "matter", + "version": "chip-v1" + } + ], + "endpointTypes": [ + { + "id": 1, + "name": "MA-rootdevice", + "deviceTypeRef": { + "code": 22, + "profileId": 259, + "label": "MA-rootdevice", + "name": "MA-rootdevice" + }, + "deviceTypes": [ + { + "code": 22, + "profileId": 259, + "label": "MA-rootdevice", + "name": "MA-rootdevice" + } + ], + "deviceVersions": [ + 1 + ], + "deviceIdentifiers": [ + 22 + ], + "deviceTypeName": "MA-rootdevice", + "deviceTypeCode": 22, + "deviceTypeProfileId": 259, + "clusters": [ + { + "name": "Descriptor", + "code": 29, + "mfgCode": null, + "define": "DESCRIPTOR_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "DeviceTypeList", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ServerList", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClientList", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "PartsList", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Access Control", + "code": 31, + "mfgCode": null, + "define": "ACCESS_CONTROL_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "ACL", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "Extension", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SubjectsPerAccessControlEntry", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TargetsPerAccessControlEntry", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AccessControlEntriesPerFabric", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ], + "events": [ + { + "name": "AccessControlEntryChanged", + "code": 0, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "AccessControlExtensionChanged", + "code": 1, + "mfgCode": null, + "side": "server", + "included": 1 + } + ] + }, + { + "name": "Basic Information", + "code": 40, + "mfgCode": null, + "define": "BASIC_INFORMATION_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "DataModelRevision", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "VendorName", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "VendorID", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "vendor_id", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "ProductName", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "ProductID", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "NodeLabel", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "NVM", + "singleton": 1, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "Location", + "code": 6, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "HardwareVersion", + "code": 7, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "HardwareVersionString", + "code": 8, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "SoftwareVersion", + "code": 9, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "SoftwareVersionString", + "code": 10, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "ManufacturingDate", + "code": 11, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "PartNumber", + "code": 12, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "ProductURL", + "code": 13, + "mfgCode": null, + "side": "server", + "type": "long_char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "ProductLabel", + "code": 14, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "SerialNumber", + "code": 15, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "LocalConfigDisabled", + "code": 16, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "NVM", + "singleton": 1, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "UniqueID", + "code": 18, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "CapabilityMinima", + "code": 19, + "mfgCode": null, + "side": "server", + "type": "CapabilityMinimaStruct", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SpecificationVersion", + "code": 21, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "MaxPathsPerInvoke", + "code": 22, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 1, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 1, + "bounded": 0, + "defaultValue": "3", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + } + ], + "events": [ + { + "name": "StartUp", + "code": 0, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "ShutDown", + "code": 1, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "Leave", + "code": 2, + "mfgCode": null, + "side": "server", + "included": 1 + } + ] + }, + { + "name": "General Commissioning", + "code": 48, + "mfgCode": null, + "define": "GENERAL_COMMISSIONING_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "ArmFailSafe", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ArmFailSafeResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "SetRegulatoryConfig", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "SetRegulatoryConfigResponse", + "code": 3, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "CommissioningComplete", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "CommissioningCompleteResponse", + "code": 5, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "Breadcrumb", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int64u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0000000000000000", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "BasicCommissioningInfo", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "BasicCommissioningInfo", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "RegulatoryConfig", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "RegulatoryLocationTypeEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "LocationCapability", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "RegulatoryLocationTypeEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SupportsConcurrentConnection", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0001", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + } + ] + }, + { + "name": "Network Commissioning", + "code": 49, + "mfgCode": null, + "define": "NETWORK_COMMISSIONING_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "ScanNetworks", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ScanNetworksResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "AddOrUpdateWiFiNetwork", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "AddOrUpdateThreadNetwork", + "code": 3, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "RemoveNetwork", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "NetworkConfigResponse", + "code": 5, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "ConnectNetwork", + "code": 6, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ConnectNetworkResponse", + "code": 7, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "ReorderNetwork", + "code": 8, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "MaxNetworks", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "Networks", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ScanMaxTimeSeconds", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ConnectMaxTimeSeconds", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "InterfaceEnabled", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "LastNetworkingStatus", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "NetworkCommissioningStatusEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "LastNetworkID", + "code": 6, + "mfgCode": null, + "side": "server", + "type": "octet_string", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "LastConnectErrorValue", + "code": 7, + "mfgCode": null, + "side": "server", + "type": "int32s", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0001", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + } + ] + }, + { + "name": "Diagnostic Logs", + "code": 50, + "mfgCode": null, + "define": "DIAGNOSTIC_LOGS_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "RetrieveLogsRequest", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "General Diagnostics", + "code": 51, + "mfgCode": null, + "define": "GENERAL_DIAGNOSTICS_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "TestEventTrigger", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "TimeSnapshot", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "TimeSnapshotResponse", + "code": 2, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "NetworkInterfaces", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "RebootCount", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "UpTime", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int64u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TotalOperationalHours", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BootReason", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "BootReasonEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ActiveHardwareFaults", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ActiveRadioFaults", + "code": 6, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ActiveNetworkFaults", + "code": 7, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TestEventTriggersEnabled", + "code": 8, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "false", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + } + ], + "events": [ + { + "name": "BootReason", + "code": 3, + "mfgCode": null, + "side": "server", + "included": 1 + } + ] + }, + { + "name": "Administrator Commissioning", + "code": 60, + "mfgCode": null, + "define": "ADMINISTRATOR_COMMISSIONING_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "OpenCommissioningWindow", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "OpenBasicCommissioningWindow", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "RevokeCommissioning", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "WindowStatus", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "CommissioningWindowStatusEnum", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AdminFabricIndex", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "fabric_idx", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AdminVendorId", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "vendor_id", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0001", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + } + ] + }, + { + "name": "Operational Credentials", + "code": 62, + "mfgCode": null, + "define": "OPERATIONAL_CREDENTIALS_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "AttestationRequest", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "AttestationResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "CertificateChainRequest", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "CertificateChainResponse", + "code": 3, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "CSRRequest", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "CSRResponse", + "code": 5, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "AddNOC", + "code": 6, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "UpdateNOC", + "code": 7, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "NOCResponse", + "code": 8, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "UpdateFabricLabel", + "code": 9, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "RemoveFabric", + "code": 10, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "AddTrustedRootCertificate", + "code": 11, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "NOCs", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "Fabrics", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "SupportedFabrics", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "CommissionedFabrics", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "TrustedRootCertificates", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + }, + { + "name": "CurrentFabricIndex", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0001", + "reportable": 1, + "minInterval": 0, + "maxInterval": 65344, + "reportableChange": 0 + } + ] + }, + { + "name": "Group Key Management", + "code": 63, + "mfgCode": null, + "define": "GROUP_KEY_MANAGEMENT_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "KeySetWrite", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "KeySetRead", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "KeySetReadResponse", + "code": 2, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "KeySetRemove", + "code": 3, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "KeySetReadAllIndices", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "KeySetReadAllIndicesResponse", + "code": 5, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "GroupKeyMap", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GroupTable", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "MaxGroupsPerFabric", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "MaxGroupKeysPerFabric", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + } + ] + }, + { + "id": 2, + "name": "Anonymous Endpoint Type", + "deviceTypeRef": { + "code": 15, + "profileId": 259, + "label": "MA-genericswitch", + "name": "MA-genericswitch" + }, + "deviceTypes": [ + { + "code": 15, + "profileId": 259, + "label": "MA-genericswitch", + "name": "MA-genericswitch" + }, + { + "code": 17, + "profileId": 259, + "label": "MA-powersource", + "name": "MA-powersource" + } + ], + "deviceVersions": [ + 1, + 1 + ], + "deviceIdentifiers": [ + 15, + 17 + ], + "deviceTypeName": "MA-genericswitch", + "deviceTypeCode": 15, + "deviceTypeProfileId": 259, + "clusters": [ + { + "name": "Identify", + "code": 3, + "mfgCode": null, + "define": "IDENTIFY_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "Identify", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "TriggerEffect", + "code": 64, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "IdentifyTime", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "IdentifyType", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "IdentifyTypeEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x00", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "4", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Descriptor", + "code": 29, + "mfgCode": null, + "define": "DESCRIPTOR_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "DeviceTypeList", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ServerList", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClientList", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "PartsList", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TagList", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Power Source", + "code": 47, + "mfgCode": null, + "define": "POWER_SOURCE_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "Status", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "PowerSourceStatusEnum", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "Order", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "Description", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "Battery", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatVoltage", + "code": 11, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatPercentRemaining", + "code": 12, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "100", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatTimeRemaining", + "code": 13, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatChargeLevel", + "code": 14, + "mfgCode": null, + "side": "server", + "type": "BatChargeLevelEnum", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatReplacementNeeded", + "code": 15, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatReplaceability", + "code": 16, + "mfgCode": null, + "side": "server", + "type": "BatReplaceabilityEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatPresent", + "code": 17, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatReplacementDescription", + "code": 19, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatQuantity", + "code": 25, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EndpointList", + "code": 31, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0A", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, + { + "name": "Switch", + "code": 59, + "mfgCode": null, + "define": "SWITCH_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "NumberOfPositions", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "3", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentPosition", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0xE", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ], + "events": [ + { + "name": "InitialPress", + "code": 1, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "LongPress", + "code": 2, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "ShortRelease", + "code": 3, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "LongRelease", + "code": 4, + "mfgCode": null, + "side": "server", + "included": 1 + } + ] + } + ] + } + ], + "endpoints": [ + { + "endpointTypeName": "MA-rootdevice", + "endpointTypeIndex": 0, + "profileId": 259, + "endpointId": 0, + "networkId": 0, + "parentEndpointIdentifier": null + }, + { + "endpointTypeName": "Anonymous Endpoint Type", + "endpointTypeIndex": 1, + "profileId": 259, + "endpointId": 1, + "networkId": 0, + "parentEndpointIdentifier": null + } + ] +} \ No newline at end of file diff --git a/examples/chef/devices/rootnode_genericswitch_9866e35d0b.matter b/examples/chef/devices/rootnode_genericswitch_9866e35d0b.matter index 8e2d0166cc44a9..cda268e6d211f0 100644 --- a/examples/chef/devices/rootnode_genericswitch_9866e35d0b.matter +++ b/examples/chef/devices/rootnode_genericswitch_9866e35d0b.matter @@ -256,6 +256,265 @@ cluster BasicInformation = 40 { command MfgSpecificPing(): DefaultSuccess = 0; } +/** This cluster is used to describe the configuration and capabilities of a physical power source that provides power to the Node. */ +cluster PowerSource = 47 { + revision 1; // NOTE: Default/not specifically set + + enum BatApprovedChemistryEnum : enum16 { + kUnspecified = 0; + kAlkaline = 1; + kLithiumCarbonFluoride = 2; + kLithiumChromiumOxide = 3; + kLithiumCopperOxide = 4; + kLithiumIronDisulfide = 5; + kLithiumManganeseDioxide = 6; + kLithiumThionylChloride = 7; + kMagnesium = 8; + kMercuryOxide = 9; + kNickelOxyhydride = 10; + kSilverOxide = 11; + kZincAir = 12; + kZincCarbon = 13; + kZincChloride = 14; + kZincManganeseDioxide = 15; + kLeadAcid = 16; + kLithiumCobaltOxide = 17; + kLithiumIon = 18; + kLithiumIonPolymer = 19; + kLithiumIronPhosphate = 20; + kLithiumSulfur = 21; + kLithiumTitanate = 22; + kNickelCadmium = 23; + kNickelHydrogen = 24; + kNickelIron = 25; + kNickelMetalHydride = 26; + kNickelZinc = 27; + kSilverZinc = 28; + kSodiumIon = 29; + kSodiumSulfur = 30; + kZincBromide = 31; + kZincCerium = 32; + } + + enum BatChargeFaultEnum : enum8 { + kUnspecified = 0; + kAmbientTooHot = 1; + kAmbientTooCold = 2; + kBatteryTooHot = 3; + kBatteryTooCold = 4; + kBatteryAbsent = 5; + kBatteryOverVoltage = 6; + kBatteryUnderVoltage = 7; + kChargerOverVoltage = 8; + kChargerUnderVoltage = 9; + kSafetyTimeout = 10; + } + + enum BatChargeLevelEnum : enum8 { + kOK = 0; + kWarning = 1; + kCritical = 2; + } + + enum BatChargeStateEnum : enum8 { + kUnknown = 0; + kIsCharging = 1; + kIsAtFullCharge = 2; + kIsNotCharging = 3; + } + + enum BatCommonDesignationEnum : enum16 { + kUnspecified = 0; + kAAA = 1; + kAA = 2; + kC = 3; + kD = 4; + k4v5 = 5; + k6v0 = 6; + k9v0 = 7; + k12AA = 8; + kAAAA = 9; + kA = 10; + kB = 11; + kF = 12; + kN = 13; + kNo6 = 14; + kSubC = 15; + kA23 = 16; + kA27 = 17; + kBA5800 = 18; + kDuplex = 19; + k4SR44 = 20; + k523 = 21; + k531 = 22; + k15v0 = 23; + k22v5 = 24; + k30v0 = 25; + k45v0 = 26; + k67v5 = 27; + kJ = 28; + kCR123A = 29; + kCR2 = 30; + k2CR5 = 31; + kCRP2 = 32; + kCRV3 = 33; + kSR41 = 34; + kSR43 = 35; + kSR44 = 36; + kSR45 = 37; + kSR48 = 38; + kSR54 = 39; + kSR55 = 40; + kSR57 = 41; + kSR58 = 42; + kSR59 = 43; + kSR60 = 44; + kSR63 = 45; + kSR64 = 46; + kSR65 = 47; + kSR66 = 48; + kSR67 = 49; + kSR68 = 50; + kSR69 = 51; + kSR516 = 52; + kSR731 = 53; + kSR712 = 54; + kLR932 = 55; + kA5 = 56; + kA10 = 57; + kA13 = 58; + kA312 = 59; + kA675 = 60; + kAC41E = 61; + k10180 = 62; + k10280 = 63; + k10440 = 64; + k14250 = 65; + k14430 = 66; + k14500 = 67; + k14650 = 68; + k15270 = 69; + k16340 = 70; + kRCR123A = 71; + k17500 = 72; + k17670 = 73; + k18350 = 74; + k18500 = 75; + k18650 = 76; + k19670 = 77; + k25500 = 78; + k26650 = 79; + k32600 = 80; + } + + enum BatFaultEnum : enum8 { + kUnspecified = 0; + kOverTemp = 1; + kUnderTemp = 2; + } + + enum BatReplaceabilityEnum : enum8 { + kUnspecified = 0; + kNotReplaceable = 1; + kUserReplaceable = 2; + kFactoryReplaceable = 3; + } + + enum PowerSourceStatusEnum : enum8 { + kUnspecified = 0; + kActive = 1; + kStandby = 2; + kUnavailable = 3; + } + + enum WiredCurrentTypeEnum : enum8 { + kAC = 0; + kDC = 1; + } + + enum WiredFaultEnum : enum8 { + kUnspecified = 0; + kOverVoltage = 1; + kUnderVoltage = 2; + } + + bitmap Feature : bitmap32 { + kWired = 0x1; + kBattery = 0x2; + kRechargeable = 0x4; + kReplaceable = 0x8; + } + + struct BatChargeFaultChangeType { + BatChargeFaultEnum current[] = 0; + BatChargeFaultEnum previous[] = 1; + } + + struct BatFaultChangeType { + BatFaultEnum current[] = 0; + BatFaultEnum previous[] = 1; + } + + struct WiredFaultChangeType { + WiredFaultEnum current[] = 0; + WiredFaultEnum previous[] = 1; + } + + info event WiredFaultChange = 0 { + WiredFaultEnum current[] = 0; + WiredFaultEnum previous[] = 1; + } + + info event BatFaultChange = 1 { + BatFaultEnum current[] = 0; + BatFaultEnum previous[] = 1; + } + + info event BatChargeFaultChange = 2 { + BatChargeFaultEnum current[] = 0; + BatChargeFaultEnum previous[] = 1; + } + + readonly attribute PowerSourceStatusEnum status = 0; + readonly attribute int8u order = 1; + readonly attribute char_string<60> description = 2; + readonly attribute optional nullable int32u wiredAssessedInputVoltage = 3; + readonly attribute optional nullable int16u wiredAssessedInputFrequency = 4; + readonly attribute optional WiredCurrentTypeEnum wiredCurrentType = 5; + readonly attribute optional nullable int32u wiredAssessedCurrent = 6; + readonly attribute optional int32u wiredNominalVoltage = 7; + readonly attribute optional int32u wiredMaximumCurrent = 8; + readonly attribute optional boolean wiredPresent = 9; + readonly attribute optional WiredFaultEnum activeWiredFaults[] = 10; + readonly attribute optional nullable int32u batVoltage = 11; + readonly attribute optional nullable int8u batPercentRemaining = 12; + readonly attribute optional nullable int32u batTimeRemaining = 13; + readonly attribute optional BatChargeLevelEnum batChargeLevel = 14; + readonly attribute optional boolean batReplacementNeeded = 15; + readonly attribute optional BatReplaceabilityEnum batReplaceability = 16; + readonly attribute optional boolean batPresent = 17; + readonly attribute optional BatFaultEnum activeBatFaults[] = 18; + readonly attribute optional char_string<60> batReplacementDescription = 19; + readonly attribute optional BatCommonDesignationEnum batCommonDesignation = 20; + readonly attribute optional char_string<20> batANSIDesignation = 21; + readonly attribute optional char_string<20> batIECDesignation = 22; + readonly attribute optional BatApprovedChemistryEnum batApprovedChemistry = 23; + readonly attribute optional int32u batCapacity = 24; + readonly attribute optional int8u batQuantity = 25; + readonly attribute optional BatChargeStateEnum batChargeState = 26; + readonly attribute optional nullable int32u batTimeToFullCharge = 27; + readonly attribute optional boolean batFunctionalWhileCharging = 28; + readonly attribute optional nullable int32u batChargingCurrent = 29; + readonly attribute optional BatChargeFaultEnum activeBatChargeFaults[] = 30; + readonly attribute endpoint_no endpointList[] = 31; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; +} + /** This cluster is used to manage global aspects of the Commissioning flow. */ cluster GeneralCommissioning = 48 { revision 1; // NOTE: Default/not specifically set @@ -1166,6 +1425,7 @@ endpoint 0 { } } endpoint 1 { + device type ma_powersource = 17, version 1; device type ma_genericswitch = 15, version 1; @@ -1188,6 +1448,7 @@ endpoint 1 { callback attribute serverList; callback attribute clientList; callback attribute partsList; + callback attribute tagList; callback attribute generatedCommandList; callback attribute acceptedCommandList; callback attribute eventList; @@ -1196,9 +1457,31 @@ endpoint 1 { callback attribute clusterRevision; } + server cluster PowerSource { + persist attribute status default = 1; + persist attribute order default = 1; + persist attribute description default = "Battery"; + persist attribute batVoltage; + persist attribute batPercentRemaining default = 100; + persist attribute batTimeRemaining; + persist attribute batChargeLevel default = 0; + ram attribute batReplacementNeeded; + ram attribute batReplaceability; + ram attribute batPresent; + ram attribute batReplacementDescription; + persist attribute batQuantity default = 1; + callback attribute endpointList; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0x0A; + ram attribute clusterRevision default = 1; + } + server cluster Switch { emits event SwitchLatched; - persist attribute numberOfPositions default = 2; + persist attribute numberOfPositions default = 5; persist attribute currentPosition default = 0; callback attribute generatedCommandList; callback attribute acceptedCommandList; diff --git a/examples/chef/devices/rootnode_genericswitch_9866e35d0b.zap b/examples/chef/devices/rootnode_genericswitch_9866e35d0b.zap index 172c6d31772296..8e62878f7a4e40 100644 --- a/examples/chef/devices/rootnode_genericswitch_9866e35d0b.zap +++ b/examples/chef/devices/rootnode_genericswitch_9866e35d0b.zap @@ -1,6 +1,6 @@ { "fileFormat": 2, - "featureLevel": 100, + "featureLevel": 102, "creator": "zap", "keyValuePairs": [ { @@ -29,6 +29,7 @@ "pathRelativity": "relativeToZap", "path": "../../../src/app/zap-templates/app-templates.json", "type": "gen-templates-json", + "category": "matter", "version": "chip-v1" } ], @@ -1935,13 +1936,21 @@ "profileId": 259, "label": "MA-genericswitch", "name": "MA-genericswitch" + }, + { + "code": 17, + "profileId": 259, + "label": "MA-powersource", + "name": "MA-powersource" } ], "deviceVersions": [ + 1, 1 ], "deviceIdentifiers": [ - 15 + 15, + 17 ], "deviceTypeName": "MA-genericswitch", "deviceTypeCode": 15, @@ -2175,6 +2184,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "TagList", + "code": 4, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "GeneratedCommandList", "code": 65528, @@ -2273,6 +2298,320 @@ } ] }, + { + "name": "Power Source", + "code": 47, + "mfgCode": null, + "define": "POWER_SOURCE_CLUSTER", + "side": "server", + "enabled": 1, + "attributes": [ + { + "name": "Status", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "PowerSourceStatusEnum", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "Order", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "Description", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "Battery", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatVoltage", + "code": 11, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatPercentRemaining", + "code": 12, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "100", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatTimeRemaining", + "code": 13, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatChargeLevel", + "code": 14, + "mfgCode": null, + "side": "server", + "type": "BatChargeLevelEnum", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatReplacementNeeded", + "code": 15, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatReplaceability", + "code": 16, + "mfgCode": null, + "side": "server", + "type": "BatReplaceabilityEnum", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatPresent", + "code": 17, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatReplacementDescription", + "code": 19, + "mfgCode": null, + "side": "server", + "type": "char_string", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "BatQuantity", + "code": 25, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "NVM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EndpointList", + "code": 31, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0A", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, { "name": "Switch", "code": 59, @@ -2291,7 +2630,7 @@ "storageOption": "NVM", "singleton": 0, "bounded": 0, - "defaultValue": "2", + "defaultValue": "5", "reportable": 1, "minInterval": 1, "maxInterval": 65534, diff --git a/examples/chef/esp32/main/CMakeLists.txt b/examples/chef/esp32/main/CMakeLists.txt index ac436a7919637b..c6b37a0675fa4c 100644 --- a/examples/chef/esp32/main/CMakeLists.txt +++ b/examples/chef/esp32/main/CMakeLists.txt @@ -70,6 +70,7 @@ set(SRC_DIRS_LIST "${CMAKE_SOURCE_DIR}/../common/clusters/low-power/" "${CMAKE_SOURCE_DIR}/../common/clusters/media-input/" "${CMAKE_SOURCE_DIR}/../common/clusters/media-playback/" + "${CMAKE_SOURCE_DIR}/../common/clusters/switch/" "${CMAKE_SOURCE_DIR}/../common/clusters/target-navigator/" "${CMAKE_SOURCE_DIR}/../common/clusters/wake-on-lan/" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/zzz_generated/app-common/app-common/zap-generated/attributes" diff --git a/examples/chef/esp32/main/main.cpp b/examples/chef/esp32/main/main.cpp index c5f29dc0f847d8..b61fcf22bc1d19 100644 --- a/examples/chef/esp32/main/main.cpp +++ b/examples/chef/esp32/main/main.cpp @@ -152,6 +152,8 @@ void printQRCode() app::Clusters::NetworkCommissioning::Instance sWiFiNetworkCommissioningInstance(0 /* Endpoint Id */, &(NetworkCommissioning::ESPWiFiDriver::GetInstance())); +extern void ApplicationInit(); + void InitServer(intptr_t) { // Start IM server @@ -172,6 +174,10 @@ void InitServer(intptr_t) // Register a function to receive events from the CHIP device layer. Note that calls to // this function will happen on the CHIP event loop thread, not the app_main thread. PlatformMgr().AddEventHandler(DeviceEventCallback, reinterpret_cast(nullptr)); + + // Application code should always be initialised after the initialisation of + // server. + ApplicationInit(); } extern "C" void app_main(void) diff --git a/examples/chef/esp32/sdkconfig.defaults b/examples/chef/esp32/sdkconfig.defaults index 327e0d2538f022..fc09afaa38a00a 100644 --- a/examples/chef/esp32/sdkconfig.defaults +++ b/examples/chef/esp32/sdkconfig.defaults @@ -62,6 +62,7 @@ CONFIG_MBEDTLS_HKDF_C=y # IRAM optimizations CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y # Increase LwIP IPv6 address number CONFIG_LWIP_IPV6_NUM_ADDRESSES=6 diff --git a/examples/chef/esp32/sdkconfig_rpc.defaults b/examples/chef/esp32/sdkconfig_rpc.defaults index bf2bcc8a7b7bc7..1b9f9ddc30134f 100644 --- a/examples/chef/esp32/sdkconfig_rpc.defaults +++ b/examples/chef/esp32/sdkconfig_rpc.defaults @@ -61,5 +61,6 @@ CONFIG_MBEDTLS_HKDF_C=y # IRAM optimizations CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP=y diff --git a/examples/chef/linux/BUILD.gn b/examples/chef/linux/BUILD.gn index 264ced698b62e8..ce0bed596850ef 100644 --- a/examples/chef/linux/BUILD.gn +++ b/examples/chef/linux/BUILD.gn @@ -47,6 +47,7 @@ executable("${sample_name}") { "${project_dir}/common/chef-laundry-washer-mode.cpp", "${project_dir}/common/chef-operational-state-delegate-impl.cpp", "${project_dir}/common/chef-resource-monitoring-delegates.cpp", + "${project_dir}/common/chef-rpc-actions-worker.cpp", "${project_dir}/common/chef-rvc-mode-delegate.cpp", "${project_dir}/common/chef-rvc-operational-state-delegate.cpp", "${project_dir}/common/clusters/audio-output/AudioOutputManager.cpp", @@ -58,6 +59,8 @@ executable("${sample_name}") { "${project_dir}/common/clusters/low-power/LowPowerManager.cpp", "${project_dir}/common/clusters/media-input/MediaInputManager.cpp", "${project_dir}/common/clusters/media-playback/MediaPlaybackManager.cpp", + "${project_dir}/common/clusters/switch/SwitchEventHandler.cpp", + "${project_dir}/common/clusters/switch/SwitchManager.cpp", "${project_dir}/common/clusters/target-navigator/TargetNavigatorManager.cpp", "${project_dir}/common/clusters/wake-on-lan/WakeOnLanManager.cpp", "${project_dir}/common/stubs.cpp", @@ -80,6 +83,7 @@ executable("${sample_name}") { if (chip_enable_pw_rpc) { defines = [ "PW_RPC_ENABLED", + "PW_RPC_ACTIONS_SERVICE=1", "PW_RPC_ATTRIBUTE_SERVICE=1", "PW_RPC_BOOLEAN_STATE_SERVICE=1", "PW_RPC_BUTTON_SERVICE=1", @@ -107,6 +111,7 @@ executable("${sample_name}") { "$dir_pw_trace_tokenized", "$dir_pw_trace_tokenized:trace_rpc_service", "${chip_root}/config/linux/lib/pw_rpc:pw_rpc", + "${chip_root}/examples/common/pigweed:actions_service.nanopb_rpc", "${chip_root}/examples/common/pigweed:attributes_service.nanopb_rpc", "${chip_root}/examples/common/pigweed:boolean_state_service.nanopb_rpc", "${chip_root}/examples/common/pigweed:button_service.nanopb_rpc", diff --git a/examples/chef/linux/main.cpp b/examples/chef/linux/main.cpp index d799b4da3b53e6..7f54490eccf0de 100644 --- a/examples/chef/linux/main.cpp +++ b/examples/chef/linux/main.cpp @@ -29,10 +29,6 @@ using namespace chip::Shell; using namespace chip::app; using namespace chip::app::Clusters; -void ApplicationInit() {} - -void ApplicationShutdown() {} - int main(int argc, char * argv[]) { if (ChipLinuxAppInit(argc, argv) != 0) diff --git a/examples/chef/nrfconnect/CMakeLists.txt b/examples/chef/nrfconnect/CMakeLists.txt index 0e943cd90719f3..a22f6b6972bb15 100644 --- a/examples/chef/nrfconnect/CMakeLists.txt +++ b/examples/chef/nrfconnect/CMakeLists.txt @@ -99,6 +99,8 @@ target_sources(app PRIVATE ${CHEF}/common/clusters/low-power/LowPowerManager.cpp ${CHEF}/common/clusters/media-input/MediaInputManager.cpp ${CHEF}/common/clusters/media-playback/MediaPlaybackManager.cpp + ${CHEF}/common/clusters/switch/SwitchEventHandler.cpp + ${CHEF}/common/clusters/switch/SwitchManager.cpp ${CHEF}/common/clusters/target-navigator/TargetNavigatorManager.cpp ${CHEF}/common/clusters/wake-on-lan/WakeOnLanManager.cpp ${CHEF}/common/stubs.cpp diff --git a/examples/chef/nrfconnect/main.cpp b/examples/chef/nrfconnect/main.cpp index c79694e2e792a7..ca9d25655e3d52 100644 --- a/examples/chef/nrfconnect/main.cpp +++ b/examples/chef/nrfconnect/main.cpp @@ -64,6 +64,13 @@ chip::Crypto::PSAOperationalKeystore sPSAOperationalKeystore{}; #endif } // namespace +extern void ApplicationInit(); + +void InitServer(intptr_t) +{ + ApplicationInit(); +} + int main() { CHIP_ERROR err = CHIP_NO_ERROR; @@ -168,6 +175,8 @@ int main() cmd_app_server_init(); #endif + chip::DeviceLayer::PlatformMgr().ScheduleWork(InitServer); + #if CONFIG_CHIP_LIB_SHELL Engine::Root().RunMainLoop(); #endif diff --git a/examples/common/pigweed/BUILD.gn b/examples/common/pigweed/BUILD.gn index e056f2ef228a8a..c0178e419a1d5d 100644 --- a/examples/common/pigweed/BUILD.gn +++ b/examples/common/pigweed/BUILD.gn @@ -35,6 +35,14 @@ pw_proto_library("echo_service") { prefix = "echo_service" } +pw_proto_library("actions_service") { + sources = [ "protos/actions_service.proto" ] + inputs = [ "protos/actions_service.options" ] + deps = [ "$dir_pw_protobuf:common_protos" ] + strip_prefix = "protos" + prefix = "actions_service" +} + pw_proto_library("attributes_service") { sources = [ "protos/attributes_service.proto" ] inputs = [ "protos/attributes_service.options" ] diff --git a/examples/common/pigweed/protos/actions_service.options b/examples/common/pigweed/protos/actions_service.options new file mode 100644 index 00000000000000..24ce3e80648ae6 --- /dev/null +++ b/examples/common/pigweed/protos/actions_service.options @@ -0,0 +1 @@ +chip.rpc.ActionsRequest.actions max_count:16 // max action size diff --git a/examples/common/pigweed/protos/actions_service.proto b/examples/common/pigweed/protos/actions_service.proto new file mode 100644 index 00000000000000..dbf3a99ceb32ac --- /dev/null +++ b/examples/common/pigweed/protos/actions_service.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +package chip.rpc; + +import 'pw_protobuf_protos/common.proto'; + +enum ActionType { + WRITE_ATTRIBUTE = 0x00; // Write an cluster Attribute + RUN_COMMAND = 0x01; // Run a cluster Command + EMIT_EVENT = 0x02; // Emit a cluster Events +} + +message Action { + ActionType type = 1; // ActionType above + uint32 delayMs = 2; // Delay and run action after xx ms + uint32 actionId = 3; // Align with Cluster Attribute/Event/Command ID + optional uint32 arg1 = 4; // 1st attribute + optional uint32 arg2 = 5; // 2nd attribute + optional uint32 arg3 = 6; // 3rd attribute +} + +message ActionsRequest { + uint32 endpoint_id = 1; + uint32 cluster_id = 2; + repeated Action actions = 3; // Actions including Attribute Write / Event / Command +} + +service Actions { + rpc Set(ActionsRequest) returns (pw.protobuf.Empty){} +} diff --git a/examples/common/pigweed/rpc_console/py/BUILD.gn b/examples/common/pigweed/rpc_console/py/BUILD.gn index a050fb64747302..a03dc980872739 100644 --- a/examples/common/pigweed/rpc_console/py/BUILD.gn +++ b/examples/common/pigweed/rpc_console/py/BUILD.gn @@ -39,6 +39,7 @@ pw_python_package("chip_rpc") { "$dir_pw_rpc/py", "$dir_pw_system/py", "$dir_pw_tokenizer/py", + "${chip_root}/examples/common/pigweed:actions_service.python", "${chip_root}/examples/common/pigweed:attributes_service.python", "${chip_root}/examples/common/pigweed:boolean_state_service.python", "${chip_root}/examples/common/pigweed:button_service.python", diff --git a/examples/common/pigweed/rpc_console/py/chip_rpc/console.py b/examples/common/pigweed/rpc_console/py/chip_rpc/console.py index f5ed5b5ab4d582..1591722bfdbeab 100644 --- a/examples/common/pigweed/rpc_console/py/chip_rpc/console.py +++ b/examples/common/pigweed/rpc_console/py/chip_rpc/console.py @@ -46,6 +46,7 @@ # Protos # isort: off +from actions_service import actions_service_pb2 from attributes_service import attributes_service_pb2 from boolean_state_service import boolean_state_service_pb2 from button_service import button_service_pb2 @@ -128,6 +129,7 @@ def show_console(device: str, baudrate: int, # "set_wakeup_fd only works in main thread of the main interpreter" use_ipython=True, compiled_protos=[ + actions_service_pb2, attributes_service_pb2, boolean_state_service_pb2, button_service_pb2, diff --git a/examples/common/pigweed/rpc_services/Actions.h b/examples/common/pigweed/rpc_services/Actions.h new file mode 100644 index 00000000000000..a2cbba0cb49a7a --- /dev/null +++ b/examples/common/pigweed/rpc_services/Actions.h @@ -0,0 +1,127 @@ +/* + * + * 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 "actions_service/actions_service.rpc.pb.h" +#include "app/util/attribute-storage.h" +#include "pigweed/rpc_services/internal/StatusUtils.h" +#include +#include +#include + +namespace chip { +namespace rpc { + +class Actions final : public pw_rpc::nanopb::Actions::Service +{ + /* + * RPC Actions Service is a debugging interface for writting Attributes, emitting Events, or + * running Commands batchwisely. Each action will be execute one by one according to the + * delayed time in ms. + * + * An Action is a abstract type of Attribute, Command, Event + * type : this defines the action is either a Attribute, Command, or Event + * delayMs : this define the relative delayed time in ms after last action + * actionId : this defines the ID of Attribute, Command or Event + * arg1, arg2, arg3 : it has up to 3 optional arguments which could be mapped to Attribute/Command/Event aruments + * + * An Action Request is composed of a batch of Actions + * + * The usage of Actions Service: + * 1. Create an Action Request by protos.chip.rpc.ActionsWrite + * 2. Append Actions one by one to the action request + * 3. After all action added to the request, call rpcs.chip.rpc.Actions.Set to execute that + * + * e.g. In rpc_console: + * + * # Init a RPC Action request which is for endpoint 1 and Switch Cluster (0x003B) + * In [1]: request=protos.chip.rpc.ActionsWrite(endpoint_id=1, cluster_id=int("0x003B", 16)) + * + * # Add an action to write attribute id 0x1 (CurrentPosition) with data 2 after 1000 ms (meaning right away) + * In [2]: request.actions.append(protos.chip.rpc.Action(type=protos.chip.rpc.ActionType.WRITE_ATTRIBUTE, delayMs=1000, + * actionId=1, arg1=2)) + * + * # Define the follow action to emit an event id 0x1 (InitialPress) with data 2 (NewPosition) after 1000 ms + * In [3]: message.actions.append(protos.chip.rpc.Action(type=protos.chip.rpc.ActionType.EMIT_EVENT, delayMs=1000, + * actionId=1, arg1=2)) + * + * # Define the follow action to emit an event id 0x2 (LongPress) with data 2 (NewPosition) after 2000 ms + * In [4]: message.actions.append(protos.chip.rpc.Action(type=protos.chip.rpc.ActionType.EMIT_EVENT, delayMs=2000, + * actionId=2, arg1=2)) + * + * # Define an action to write attribute id 0x1 (CurrentPosition) with data 0 after 2000 ms (meaning button bouncing back) + * In [5]: message.actions.append(protos.chip.rpc.Action(type=protos.chip.rpc.ActionType.WRITE_ATTRIBUTE, delayMs=2000, + * actionId=1, arg1=0)) + * + * # Define the follow action to emit an event id 0x4 (LongRelease) with data 2 (PreviousPosition) after 1000 ms + * In [6]: message.actions.append(protos.chip.rpc.Action(type=protos.chip.rpc.ActionType.EMIT_EVENT, delayMs=1000, + * actionId=4, arg1=2)) + * + * # Set the actions to device + * In [7]: rpcs.chip.rpc.Actions.Set(message, pw_rpc_timeout_s=10000) + * + */ +public: + enum class Type : uint8_t + { + Attribute = 0, + Command = 1, + Event = 2, + }; + + ::pw::Status Set(const chip_rpc_ActionsRequest & request, ::pw_protobuf_Empty & response) + + { + DeviceLayer::StackLock lock; + ChipLogProgress(NotSpecified, " request.endpoint_id=%d, request.cluster_id=%d", request.endpoint_id, request.cluster_id); + + for (int i = 0; i < request.actions_count; i++) + { + chip_rpc_Action action = request.actions[i]; + std::vector args; + if (action.has_arg1) + args.push_back(action.arg1); + if (action.has_arg2) + args.push_back(action.arg2); + if (action.has_arg3) + args.push_back(action.arg3); + + mActionsSubscribeCallback(request.endpoint_id, request.cluster_id, static_cast(action.type), action.delayMs, + action.actionId, args); + } + + return pw::OkStatus(); + } + + using RpcActionsSubscribeCallback = bool (*)(EndpointId endpointId, ClusterId clusterId, uint8_t type, uint32_t delayMs, + uint32_t actionId, std::vector args); + + void SubscribeActions(RpcActionsSubscribeCallback subscriber) { mActionsSubscribeCallback = subscriber; }; + +private: + RpcActionsSubscribeCallback mActionsSubscribeCallback; +}; + +} // namespace rpc +} // namespace chip diff --git a/examples/common/pigweed/rpc_services/BooleanState.h b/examples/common/pigweed/rpc_services/BooleanState.h index bb4b524459d03d..2e0f15d6cb316d 100644 --- a/examples/common/pigweed/rpc_services/BooleanState.h +++ b/examples/common/pigweed/rpc_services/BooleanState.h @@ -28,7 +28,7 @@ namespace chip { namespace rpc { -class BooleanState final : public pw_rpc::nanopb::BooleanState::Service +class BooleanState : public pw_rpc::nanopb::BooleanState::Service { public: virtual ~BooleanState() = default; diff --git a/examples/common/pigweed/rpc_services/Locking.h b/examples/common/pigweed/rpc_services/Locking.h index 6d2e912858b47f..8f851592e8642b 100644 --- a/examples/common/pigweed/rpc_services/Locking.h +++ b/examples/common/pigweed/rpc_services/Locking.h @@ -27,7 +27,7 @@ namespace chip { namespace rpc { -class Locking final : public pw_rpc::nanopb::Locking::Service +class Locking : public pw_rpc::nanopb::Locking::Service { public: virtual ~Locking() = default; diff --git a/examples/contact-sensor-app/linux/README.md b/examples/contact-sensor-app/linux/README.md new file mode 100644 index 00000000000000..f99162f4fdee10 --- /dev/null +++ b/examples/contact-sensor-app/linux/README.md @@ -0,0 +1,144 @@ +# Matter Linux Contact Sensor Example + +An example showing the use of CHIP on the Linux. This document will describe how +to build and run Matter Linux Contact Sensor Example on Raspberry Pi. This +document is tested on **Ubuntu for Raspberry Pi Server 20.04 LTS (aarch64)** and +**Ubuntu for Raspberry Pi Desktop 20.10 (aarch64)** + +To cross-compile this example on an x64 host and run it on **NXP i.MX 8M Mini** +**EVK**, see the associated +[README document](../../../docs/guides/nxp/nxp_imx8m_linux_examples.md) for +details. + +
    + +- [Matter Linux Contact Sensor Example](#matter-linux-contact-sensor-example) + - [Building](#building) + - [Commandline Arguments](#commandline-arguments) + - [Running the Complete Example on Raspberry Pi 4](#running-the-complete-example-on-raspberry-pi-4) + - [Running RPC console](#running-rpc-console) + - [Device Tracing](#device-tracing) + +
    + +## Building + +- Install tool chain + + $ sudo apt-get install git gcc g++ python pkg-config libssl-dev libdbus-1-dev libglib2.0-dev ninja-build python3-venv python3-dev unzip + +- Build the example application: + + $ cd ~/connectedhomeip/examples/contact-sensor-app/linux + $ git submodule update --init + $ source third_party/connectedhomeip/scripts/activate.sh + $ gn gen out/debug + $ ninja -C out/debug + +- To delete generated executable, libraries and object files use: + + $ cd ~/connectedhomeip/examples/contact-sensor-app/linux + $ rm -rf out/ + +- Build the example with pigweed RPC + + $ cd ~/connectedhomeip/examples/contact-sensor-app/linux + $ git submodule update --init + $ source third_party/connectedhomeip/scripts/activate.sh + $ gn gen out/debug --args='import("//with_pw_rpc.gni")' + $ ninja -C out/debug + +## Commandline arguments + +- `--wifi` + + Enables WiFi management feature. Required for WiFi commissioning. + +- `--thread` + + Enables Thread management feature, requires ot-br-posix dbus daemon running. + Required for Thread commissioning. + +- `--ble-device ` + + Use specific bluetooth interface for BLE advertisement and connections. + + `interface id`: the number after `hci` when listing BLE interfaces by + `hciconfig` command, for example, `--ble-device 1` means using `hci1` + interface. Default: `0`. + +## Running the Complete Example on Raspberry Pi 4 + +> If you want to test Echo protocol, please enable Echo handler +> +> gn gen out/debug --args='chip_app_use_echo=true' +> ninja -C out/debug + +- Prerequisites + + 1. A Raspberry Pi 4 board + 2. A USB Bluetooth Dongle, Ubuntu desktop will send Bluetooth advertisement, + which will block CHIP from connecting via BLE. On Ubuntu server, you need + to install `pi-bluetooth` via APT. + 3. Ubuntu 20.04 or newer image for ARM64 platform. + +- Building + + Follow [Building](#building) section of this document. + +- Running + + - [Optional] Plug USB Bluetooth dongle + + - Plug USB Bluetooth dongle and find its bluetooth device number. The + number after `hci` is the bluetooth device number, `1` in this + example. + + $ hciconfig + hci1: Type: Primary Bus: USB + BD Address: 00:1A:7D:AA:BB:CC ACL MTU: 310:10 SCO MTU: 64:8 + UP RUNNING PSCAN ISCAN + RX bytes:20942 acl:1023 sco:0 events:1140 errors:0 + TX bytes:16559 acl:1011 sco:0 commands:121 errors:0 + + hci0: Type: Primary Bus: UART + BD Address: B8:27:EB:AA:BB:CC ACL MTU: 1021:8 SCO MTU: 64:1 + UP RUNNING PSCAN ISCAN + RX bytes:8609495 acl:14 sco:0 events:217484 errors:0 + TX bytes:92185 acl:20 sco:0 commands:5259 errors:0 + + - Run Linux Contact Sensor App + + $ cd ~/connectedhomeip/examples/contact-sensor-app/linux + $ sudo out/debug/chip-contact-sensor-app --ble-device [bluetooth device number] + # In this example, the device we want to use is hci1 + $ sudo out/debug/chip-contact-sensor-app --ble-device 1 + + - Test the device using ChipDeviceController on your laptop / + workstation etc. + +## Running RPC Console + +- As part of building the example with RPCs enabled the chip_rpc python + interactive console is installed into your venv. The python wheel files are + also created in the output folder: out/debug/chip_rpc_console_wheels. To + install the wheel files without rebuilding: + `pip3 install out/debug/chip_rpc_console_wheels/*.whl` + +- To use the chip-rpc console after it has been installed run: + `chip-console -s localhost:33000 -o //pw_log.out` + +- Then you can Get the contact sensor status using the RPCs: + `rpcs.chip.rpc.BooleanState.Get()` + +## Device Tracing + +Device tracing is available to analyze the device performance. To turn on +tracing, build with RPC enabled. See [Building with RPC enabled](#building). + +To obtain the tracing json file, run: + +``` + $ ./{PIGWEED_REPO}/pw_trace_tokenized/py/pw_trace_tokenized/get_trace.py -s localhost:33000 \ + -o {OUTPUT_FILE} -t {ELF_FILE} {PIGWEED_REPO}/pw_trace_tokenized/pw_trace_protos/trace_rpc.proto +``` diff --git a/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.h b/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.h index 447d2c2ae5ccf6..2deb7cfb4cac5e 100644 --- a/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.h +++ b/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.h @@ -38,7 +38,7 @@ class ModelCommand : public CHIPCommandBridge /////////// CHIPCommand Interface ///////// CHIP_ERROR RunCommand() override; - chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(10); } + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(20); } virtual CHIP_ERROR SendCommand(MTRBaseDevice * _Nonnull device, chip::EndpointId endPointId) = 0; diff --git a/examples/darwin-framework-tool/commands/pairing/DeviceControllerDelegateBridge.mm b/examples/darwin-framework-tool/commands/pairing/DeviceControllerDelegateBridge.mm index c8594a6f4fa87c..9723199ed007a4 100644 --- a/examples/darwin-framework-tool/commands/pairing/DeviceControllerDelegateBridge.mm +++ b/examples/darwin-framework-tool/commands/pairing/DeviceControllerDelegateBridge.mm @@ -22,7 +22,7 @@ @implementation CHIPToolDeviceControllerDelegate - (void)controller:(MTRDeviceController *)controller statusUpdate:(MTRCommissioningStatus)status { - NSLog(@"Pairing Status Update: %tu", status); + NSLog(@"Pairing Status Update: %ld", static_cast(status)); switch (status) { case MTRCommissioningStatusSuccess: ChipLogProgress(chipTool, "Secure Pairing Success"); diff --git a/examples/energy-management-app/energy-management-common/energy-management-app.matter b/examples/energy-management-app/energy-management-common/energy-management-app.matter index ea4e5db21a0b18..68db2eea271ec3 100644 --- a/examples/energy-management-app/energy-management-common/energy-management-app.matter +++ b/examples/energy-management-app/energy-management-common/energy-management-app.matter @@ -1237,7 +1237,7 @@ cluster GroupKeyManagement = 63 { } /** This cluster provides a mechanism for querying data about electrical power as measured by the server. */ -provisional cluster ElectricalPowerMeasurement = 144 { +cluster ElectricalPowerMeasurement = 144 { revision 1; enum MeasurementTypeEnum : enum16 { @@ -1342,7 +1342,7 @@ provisional cluster ElectricalPowerMeasurement = 144 { } /** This cluster provides a mechanism for querying data about the electrical energy imported or provided by the server. */ -provisional cluster ElectricalEnergyMeasurement = 145 { +cluster ElectricalEnergyMeasurement = 145 { revision 1; enum MeasurementTypeEnum : enum16 { @@ -1639,7 +1639,7 @@ provisional cluster DeviceEnergyManagement = 152 { } /** Electric Vehicle Supply Equipment (EVSE) is equipment used to charge an Electric Vehicle (EV) or Plug-In Hybrid Electric Vehicle. This cluster provides an interface to the functionality of Electric Vehicle Supply Equipment (EVSE) management. */ -provisional cluster EnergyEvse = 153 { +cluster EnergyEvse = 153 { revision 2; enum EnergyTransferStoppedReasonEnum : enum8 { @@ -1817,7 +1817,7 @@ provisional cluster EnergyEvse = 153 { } /** The Power Topology Cluster provides a mechanism for expressing how power is flowing between endpoints. */ -provisional cluster PowerTopology = 156 { +cluster PowerTopology = 156 { revision 1; bitmap Feature : bitmap32 { @@ -1838,7 +1838,7 @@ provisional cluster PowerTopology = 156 { } /** Attributes and commands for selecting a mode from a list of supported options. */ -provisional cluster EnergyEvseMode = 157 { +cluster EnergyEvseMode = 157 { revision 1; enum ModeTag : enum16 { diff --git a/examples/fabric-admin/BUILD.gn b/examples/fabric-admin/BUILD.gn index 79e175fb4a70c1..ddaa33483257b1 100644 --- a/examples/fabric-admin/BUILD.gn +++ b/examples/fabric-admin/BUILD.gn @@ -69,7 +69,6 @@ static_library("fabric-admin-utils") { sources += [ "commands/interactive/InteractiveCommands.cpp" ] deps += [ - "${chip_root}/examples/common/websocket-server", "${chip_root}/src/platform/logging:headers", "${editline_root}:editline", ] diff --git a/examples/fabric-admin/commands/clusters/JsonParser.h b/examples/fabric-admin/commands/clusters/JsonParser.h index 0871e767c21bd6..2a1cd62e97b028 100644 --- a/examples/fabric-admin/commands/clusters/JsonParser.h +++ b/examples/fabric-admin/commands/clusters/JsonParser.h @@ -18,7 +18,7 @@ #pragma once -#include "../common/CustomStringPrefix.h" +#include #include #include diff --git a/examples/fabric-admin/commands/clusters/ModelCommand.h b/examples/fabric-admin/commands/clusters/ModelCommand.h index c14d3c9952f3fc..185f6179db330f 100644 --- a/examples/fabric-admin/commands/clusters/ModelCommand.h +++ b/examples/fabric-admin/commands/clusters/ModelCommand.h @@ -20,7 +20,7 @@ #ifdef CONFIG_USE_LOCAL_STORAGE #include -#endif // CONFIG_USE_LOCAL_STORAGE +#endif #include "../common/CHIPCommand.h" #include diff --git a/examples/fabric-admin/commands/interactive/Commands.h b/examples/fabric-admin/commands/interactive/Commands.h index e324ddae2680ae..2b66a2d9c70f94 100644 --- a/examples/fabric-admin/commands/interactive/Commands.h +++ b/examples/fabric-admin/commands/interactive/Commands.h @@ -18,9 +18,9 @@ #pragma once -#include "commands/common/CHIPCommand.h" -#include "commands/common/Commands.h" -#include "commands/interactive/InteractiveCommands.h" +#include +#include +#include void registerCommandsInteractive(Commands & commands, CredentialIssuerCommands * credsIssuerConfig) { diff --git a/examples/fabric-admin/commands/interactive/InteractiveCommands.h b/examples/fabric-admin/commands/interactive/InteractiveCommands.h index 21c14a7a4c95ce..a0b490f3b668e5 100644 --- a/examples/fabric-admin/commands/interactive/InteractiveCommands.h +++ b/examples/fabric-admin/commands/interactive/InteractiveCommands.h @@ -18,11 +18,9 @@ #pragma once -#include "../clusters/DataModelLogger.h" -#include "../common/CHIPCommand.h" -#include "../common/Commands.h" - -#include +#include +#include +#include #include diff --git a/examples/fabric-admin/commands/pairing/Commands.h b/examples/fabric-admin/commands/pairing/Commands.h index 6fdfacef79e34f..af07536bfda052 100644 --- a/examples/fabric-admin/commands/pairing/Commands.h +++ b/examples/fabric-admin/commands/pairing/Commands.h @@ -18,15 +18,15 @@ #pragma once -#include "commands/common/Commands.h" -#include "commands/pairing/GetCommissionerNodeIdCommand.h" -#include "commands/pairing/GetCommissionerRootCertificateCommand.h" -#include "commands/pairing/IssueNOCChainCommand.h" -#include "commands/pairing/OpenCommissioningWindowCommand.h" -#include "commands/pairing/PairingCommand.h" +#include +#include +#include +#include +#include +#include +#include #include -#include #include class Unpair : public PairingCommand diff --git a/examples/fabric-admin/commands/pairing/GetCommissionerNodeIdCommand.h b/examples/fabric-admin/commands/pairing/GetCommissionerNodeIdCommand.h index 3234cfe456a956..10439830cb1345 100644 --- a/examples/fabric-admin/commands/pairing/GetCommissionerNodeIdCommand.h +++ b/examples/fabric-admin/commands/pairing/GetCommissionerNodeIdCommand.h @@ -18,8 +18,8 @@ #pragma once -#include "../common/CHIPCommand.h" -#include "../common/RemoteDataModelLogger.h" +#include +#include class GetCommissionerNodeIdCommand : public CHIPCommand { diff --git a/examples/fabric-admin/commands/pairing/GetCommissionerRootCertificateCommand.h b/examples/fabric-admin/commands/pairing/GetCommissionerRootCertificateCommand.h index 1d25efcc38224d..88afc9b6bace57 100644 --- a/examples/fabric-admin/commands/pairing/GetCommissionerRootCertificateCommand.h +++ b/examples/fabric-admin/commands/pairing/GetCommissionerRootCertificateCommand.h @@ -18,8 +18,8 @@ #pragma once -#include "../common/CHIPCommand.h" -#include "../common/RemoteDataModelLogger.h" +#include +#include #include "ToTLVCert.h" diff --git a/examples/fabric-admin/commands/pairing/IssueNOCChainCommand.h b/examples/fabric-admin/commands/pairing/IssueNOCChainCommand.h index 0103b26977136d..efed6738204008 100644 --- a/examples/fabric-admin/commands/pairing/IssueNOCChainCommand.h +++ b/examples/fabric-admin/commands/pairing/IssueNOCChainCommand.h @@ -18,8 +18,8 @@ #pragma once -#include "../common/CHIPCommand.h" -#include "../common/RemoteDataModelLogger.h" +#include +#include #include "ToTLVCert.h" diff --git a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h index 99b179d8753125..2c1d62f31c566b 100644 --- a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h +++ b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h @@ -18,8 +18,7 @@ #pragma once -#include "../common/CHIPCommand.h" - +#include #include #include diff --git a/examples/fabric-admin/commands/pairing/PairingCommand.cpp b/examples/fabric-admin/commands/pairing/PairingCommand.cpp index 80775f0853d110..379b56431379e4 100644 --- a/examples/fabric-admin/commands/pairing/PairingCommand.cpp +++ b/examples/fabric-admin/commands/pairing/PairingCommand.cpp @@ -17,14 +17,14 @@ */ #include "PairingCommand.h" -#include "platform/PlatformManager.h" + #include #include #include #include #include +#include #include - #include #include diff --git a/examples/fabric-admin/commands/pairing/PairingCommand.h b/examples/fabric-admin/commands/pairing/PairingCommand.h index 4ff3903253be4e..8de34a0ba989ed 100644 --- a/examples/fabric-admin/commands/pairing/PairingCommand.h +++ b/examples/fabric-admin/commands/pairing/PairingCommand.h @@ -18,11 +18,10 @@ #pragma once -#include "../common/CHIPCommand.h" +#include +#include #include #include - -#include #include #include diff --git a/examples/fabric-admin/main.cpp b/examples/fabric-admin/main.cpp index cf1122bda1ec8a..a1002d83170d5b 100644 --- a/examples/fabric-admin/main.cpp +++ b/examples/fabric-admin/main.cpp @@ -16,11 +16,10 @@ * */ -#include "commands/common/Commands.h" - -#include "commands/clusters/SubscriptionsCommands.h" -#include "commands/interactive/Commands.h" -#include "commands/pairing/Commands.h" +#include +#include +#include +#include #include #include diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp index d2350cad37c717..d639cea1ca0697 100644 --- a/examples/fabric-bridge-app/linux/main.cpp +++ b/examples/fabric-bridge-app/linux/main.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -39,16 +40,14 @@ #include #include -#include -#include - #include "CommissionableInit.h" #include "Device.h" -#include #include #include #include +#include +#include using namespace chip; using namespace chip::app; @@ -58,12 +57,41 @@ using namespace chip::Transport; using namespace chip::DeviceLayer; using namespace chip::app::Clusters; +#define POLL_INTERVAL_MS (100) + namespace { EndpointId gCurrentEndpointId; EndpointId gFirstDynamicEndpointId; Device * gDevices[CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT + 1]; +bool KeyboardHit() +{ + int bytesWaiting; + ioctl(0, FIONREAD, &bytesWaiting); + return bytesWaiting > 0; +} + +void BridgePollingThread() +{ + while (true) + { + if (KeyboardHit()) + { + int ch = getchar(); + if (ch == 'e') + { + ChipLogProgress(DeviceLayer, "Exiting....."); + exit(0); + } + continue; + } + + // Sleep to avoid tight loop reading commands + usleep(POLL_INTERVAL_MS * 1000); + } +} + } // namespace // REVISION DEFINITIONS: @@ -245,42 +273,6 @@ Protocols::InteractionModel::Status emberAfExternalAttributeWriteCallback(Endpoi return ret; } -#define POLL_INTERVAL_MS (100) -uint8_t poll_prescale = 0; - -bool kbhit() -{ - int byteswaiting; - ioctl(0, FIONREAD, &byteswaiting); - return byteswaiting > 0; -} - -const int16_t oneDegree = 100; - -void * bridge_polling_thread(void * context) -{ - while (true) - { - if (kbhit()) - { - int ch = getchar(); - - // Commands used for the actions bridge test plan. - if (ch == 'e') - { - ChipLogProgress(DeviceLayer, "Exiting....."); - exit(0); - } - continue; - } - - // Sleep to avoid tight loop reading commands - usleep(POLL_INTERVAL_MS * 1000); - } - - return nullptr; -} - void ApplicationInit() { // Clear out the device database @@ -292,15 +284,9 @@ void ApplicationInit() static_cast(emberAfEndpointFromIndex(static_cast(emberAfFixedEndpointCount() - 1))) + 1); gCurrentEndpointId = gFirstDynamicEndpointId; - { - pthread_t poll_thread; - int res = pthread_create(&poll_thread, nullptr, bridge_polling_thread, nullptr); - if (res) - { - printf("Error creating polling thread: %d\n", res); - exit(1); - } - } + // Start a thread for bridge polling + std::thread pollingThread(BridgePollingThread); + pollingThread.detach(); } void ApplicationShutdown() {} diff --git a/examples/kotlin-matter-controller/README.md b/examples/kotlin-matter-controller/README.md index c4e84cbd5982a4..71a954e711e0b5 100644 --- a/examples/kotlin-matter-controller/README.md +++ b/examples/kotlin-matter-controller/README.md @@ -109,7 +109,7 @@ the top Matter directory: ``` The Java executable file `kotlin-matter-controller` will be generated at -`out/android-x86-kotlin-matter-controller/bin/` +`out/linux-x64-kotlin-matter-controller/bin/` Run the kotlin-matter-controller diff --git a/examples/light-switch-app/infineon/cyw30739/README.md b/examples/light-switch-app/infineon/cyw30739/README.md index 530cfde2501498..70d650cfef97a3 100644 --- a/examples/light-switch-app/infineon/cyw30739/README.md +++ b/examples/light-switch-app/infineon/cyw30739/README.md @@ -18,6 +18,7 @@ An example showing the use of Matter on the Infineon CYW30739 platform. - [Commissionable Data](#commissionable-data) - [Device Information](#device-information) - [DAC / DAC Key / PAI Certificate / Certificate Declaration](#dac--dac-key--pai-certificate--certificate-declaration) + - [Use Provisioned Optiga Trust M](#use-provisioned-optiga-trust-m) - [Flashing the Application](#flashing-the-application) - [Enter Recovery Mode](#enter-recovery-mode) - [Run Flash Script](#run-flash-script) @@ -163,6 +164,29 @@ keys, and CD by the following arguments: 'matter_cd="/path/to/cd.der"' ``` +### Use Provisioned Optiga Trust M + +For boards supported by Optiga Trust M, CYW30739 will provision factory data to +the Optiga Trust M by default for easy development. + +The Optiga Trust M on a production board should come with provisioned factory +data. To ensure its optimal use, please configure the Optiga using the following +arguments: + +- `use_provisioned_optiga`, `optiga_dac_object_id`, + `optiga_dac_key_object_id`, `optiga_pai_cert_object_id` + + ```bash + $ cd ~/connectedhomeip + $ scripts/examples/gn_build_example.sh examples/light-switch-app/infineon/cyw30739 out/cyw30739-light-switch \ + 'optiga_dac_object_id="0xe0e0"' \ + 'optiga_dac_key_object_id="0xe0f0"' \ + 'optiga_pai_cert_object_id="0xe0e8"' + ``` + +The developer must set the object IDs to corresponding values matching the +configurations used in the Optiga provisioning procedure. + ## Flashing the Application ### Enter Recovery Mode diff --git a/examples/light-switch-app/qpg/BUILD.gn b/examples/light-switch-app/qpg/BUILD.gn index 055cfd46f95abb..84ec9dd64173ac 100644 --- a/examples/light-switch-app/qpg/BUILD.gn +++ b/examples/light-switch-app/qpg/BUILD.gn @@ -137,7 +137,7 @@ qpg_executable("light_switch_app") { } } - ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}/QorvoStack_${qpg_target_ic}.ld" + ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}${qpg_flavour}/QorvoStack_${qpg_target_ic}${qpg_flavour}.ld" inputs = [ ldscript ] diff --git a/examples/light-switch-app/qpg/args.gni b/examples/light-switch-app/qpg/args.gni index 64db0987b3d7d6..9a69cd16959f6d 100644 --- a/examples/light-switch-app/qpg/args.gni +++ b/examples/light-switch-app/qpg/args.gni @@ -30,7 +30,7 @@ chip_enable_icd_lit = true chip_stack_lock_tracking = "none" matter_device_vid = "0xFFF1" -matter_device_pid = "0x8006" +matter_device_pid = "0x8004" pw_log_BACKEND = "${chip_root}/src/lib/support/pw_log_chip" pw_assert_BACKEND = "$dir_pw_assert_log:check_backend" diff --git a/examples/light-switch-app/qpg/include/CHIPProjectConfig.h b/examples/light-switch-app/qpg/include/CHIPProjectConfig.h index b6d7572839ec75..09df64a412938e 100644 --- a/examples/light-switch-app/qpg/include/CHIPProjectConfig.h +++ b/examples/light-switch-app/qpg/include/CHIPProjectConfig.h @@ -40,9 +40,18 @@ * CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION * * A uint32_t identifying the software version running on the device. + * First two bytes are reflecting the Matter standard + * Last two bytes are reflecting the SDK version of which the first nibble of the first byte represents the major + * version and the second nibble of the first byte has the minor number. The last byte holds the patch number. + * example for SDK v0.1.5 with Matter v1.2 standard: + * 0x01020105 */ #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION -#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x0003 // Can't be removed, needed for OTA file generation. +#ifndef OTA_TEST_IMAGE +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x01020105 +#else +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x01020106 +#endif #endif /** @@ -53,7 +62,11 @@ * {MAJOR_VERSION}.0d{MINOR_VERSION} */ #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING -#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.1" // Can't be removed, needed for OTA file generation. +#ifndef OTA_TEST_IMAGE +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.2-0.1.5" +#else +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.2-0.1.6" +#endif #endif /** diff --git a/examples/light-switch-app/qpg/include/SwitchManager.h b/examples/light-switch-app/qpg/include/SwitchManager.h index 3f429f0c79a54e..41eedd10af4f13 100644 --- a/examples/light-switch-app/qpg/include/SwitchManager.h +++ b/examples/light-switch-app/qpg/include/SwitchManager.h @@ -54,8 +54,8 @@ class SwitchManager }; void Init(void); - static void GenericSwitchInitialPress(void); - static void GenericSwitchReleasePress(void); + static void GenericSwitchInitialPressHandler(AppEvent * aEvent); + static void GenericSwitchReleasePressHandler(AppEvent * aEvent); static void ToggleHandler(AppEvent * aEvent); static void LevelHandler(AppEvent * aEvent); static void ColorHandler(AppEvent * aEvent); diff --git a/examples/light-switch-app/qpg/src/AppTask.cpp b/examples/light-switch-app/qpg/src/AppTask.cpp index 318e61b1e875c8..4be2f4eeefbf39 100644 --- a/examples/light-switch-app/qpg/src/AppTask.cpp +++ b/examples/light-switch-app/qpg/src/AppTask.cpp @@ -297,13 +297,13 @@ void AppTask::ButtonEventHandler(uint8_t btnIdx, bool btnPressed) case APP_FUNCTION2_SWITCH: { if (!btnPressed) { - ChipLogProgress(NotSpecified, "Switch initial press"); - SwitchMgr().GenericSwitchInitialPress(); + ChipLogProgress(NotSpecified, "Switch release press"); + button_event.Handler = SwitchMgr().GenericSwitchReleasePressHandler; } else { - ChipLogProgress(NotSpecified, "Switch release press"); - SwitchMgr().GenericSwitchReleasePress(); + ChipLogProgress(NotSpecified, "Switch initial press"); + button_event.Handler = SwitchMgr().GenericSwitchInitialPressHandler; } break; } @@ -516,11 +516,15 @@ void AppTask::UpdateLEDs(void) // If the system has ble connection(s) uptill the stage above, THEN blink // the LEDs at an even rate of 100ms. // - // Otherwise, blink the LED ON for a very short time. + // Otherwise, turn the LED OFF. if (sIsThreadProvisioned && sIsThreadEnabled) { qvIO_LedSet(SYSTEM_STATE_LED, true); } + else if (sIsThreadProvisioned && !sIsThreadEnabled) + { + qvIO_LedBlink(SYSTEM_STATE_LED, 950, 50); + } else if (sHaveBLEConnections) { qvIO_LedBlink(SYSTEM_STATE_LED, 100, 100); @@ -532,7 +536,7 @@ void AppTask::UpdateLEDs(void) else { // not commissioned yet - qvIO_LedBlink(SYSTEM_STATE_LED, 50, 950); + qvIO_LedSet(SYSTEM_STATE_LED, false); } } diff --git a/examples/light-switch-app/qpg/src/SwitchManager.cpp b/examples/light-switch-app/qpg/src/SwitchManager.cpp index 45d7079b578598..58b2f1d9bd8889 100644 --- a/examples/light-switch-app/qpg/src/SwitchManager.cpp +++ b/examples/light-switch-app/qpg/src/SwitchManager.cpp @@ -107,20 +107,40 @@ void SwitchManager::ColorHandler(AppEvent * aEvent) DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); } -void SwitchManager::GenericSwitchInitialPress(void) +void SwitchManager::GenericSwitchInitialPressHandler(AppEvent * aEvent) { // Press moves Position from 0 (idle) to 1 (press) uint8_t newPosition = 1; - SystemLayer().ScheduleLambda( - [newPosition] { chip::app::Clusters::Switch::Attributes::CurrentPosition::Set(GENERICSWITCH_ENDPOINT_ID, newPosition); }); + if (aEvent->Type != AppEvent::kEventType_Button) + { + ChipLogError(NotSpecified, "Event type not supported!"); + return; + } + + ChipLogProgress(NotSpecified, "GenericSwitchInitialPress new position %d", newPosition); + SystemLayer().ScheduleLambda([newPosition] { + chip::app::Clusters::Switch::Attributes::CurrentPosition::Set(GENERICSWITCH_ENDPOINT_ID, newPosition); + // InitialPress event takes newPosition as event data + chip::app::Clusters::SwitchServer::Instance().OnInitialPress(GENERICSWITCH_ENDPOINT_ID, newPosition); + }); } -void SwitchManager::GenericSwitchReleasePress(void) +void SwitchManager::GenericSwitchReleasePressHandler(AppEvent * aEvent) { // Release moves Position from 1 (press) to 0 uint8_t newPosition = 0; - SystemLayer().ScheduleLambda( - [newPosition] { chip::app::Clusters::Switch::Attributes::CurrentPosition::Set(GENERICSWITCH_ENDPOINT_ID, newPosition); }); + if (aEvent->Type != AppEvent::kEventType_Button) + { + ChipLogError(NotSpecified, "Event type not supported!"); + return; + } + + ChipLogProgress(NotSpecified, "GenericSwitchReleasePress new position %d", newPosition); + SystemLayer().ScheduleLambda([newPosition] { + chip::app::Clusters::Switch::Attributes::CurrentPosition::Set(GENERICSWITCH_ENDPOINT_ID, newPosition); + // Short Release event takes newPosition as event data + chip::app::Clusters::SwitchServer::Instance().OnShortRelease(GENERICSWITCH_ENDPOINT_ID, newPosition); + }); } diff --git a/examples/light-switch-app/qpg/zap/switch.matter b/examples/light-switch-app/qpg/zap/switch.matter index e3da4bf1d979f6..25937577420272 100644 --- a/examples/light-switch-app/qpg/zap/switch.matter +++ b/examples/light-switch-app/qpg/zap/switch.matter @@ -2404,6 +2404,8 @@ endpoint 0 { ram attribute lastNetworkingStatus; ram attribute lastNetworkID; ram attribute lastConnectErrorValue; + callback attribute supportedThreadFeatures; + callback attribute threadVersion; callback attribute generatedCommandList; callback attribute acceptedCommandList; callback attribute eventList; @@ -2441,19 +2443,21 @@ endpoint 0 { callback attribute networkInterfaces; callback attribute rebootCount; callback attribute upTime; + callback attribute totalOperationalHours; callback attribute bootReason; callback attribute activeHardwareFaults; callback attribute activeRadioFaults; callback attribute activeNetworkFaults; - ram attribute testEventTriggersEnabled default = 0; + callback attribute testEventTriggersEnabled default = false; callback attribute generatedCommandList; callback attribute acceptedCommandList; - callback attribute eventList; callback attribute attributeList; callback attribute featureMap; callback attribute clusterRevision; handle command TestEventTrigger; + handle command TimeSnapshot; + handle command TimeSnapshotResponse; } server cluster SoftwareDiagnostics { @@ -2729,13 +2733,15 @@ endpoint 2 { } server cluster Switch { + emits event InitialPress; + emits event ShortRelease; ram attribute numberOfPositions default = 2; ram attribute currentPosition default = 0; callback attribute generatedCommandList; callback attribute acceptedCommandList; callback attribute eventList; callback attribute attributeList; - ram attribute featureMap default = 1; + ram attribute featureMap default = 6; ram attribute clusterRevision default = 1; } } diff --git a/examples/light-switch-app/qpg/zap/switch.zap b/examples/light-switch-app/qpg/zap/switch.zap index 9a33d739328304..b04ff644b1192c 100644 --- a/examples/light-switch-app/qpg/zap/switch.zap +++ b/examples/light-switch-app/qpg/zap/switch.zap @@ -1622,6 +1622,38 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "SupportedThreadFeatures", + "code": 9, + "mfgCode": null, + "side": "server", + "type": "ThreadCapabilitiesBitmap", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ThreadVersion", + "code": 10, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "GeneratedCommandList", "code": 65528, @@ -1859,6 +1891,22 @@ "source": "client", "isIncoming": 1, "isEnabled": 1 + }, + { + "name": "TimeSnapshot", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "TimeSnapshotResponse", + "code": 2, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 } ], "attributes": [ @@ -1874,8 +1922,8 @@ "bounded": 0, "defaultValue": null, "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, + "minInterval": 0, + "maxInterval": 65344, "reportableChange": 0 }, { @@ -1890,8 +1938,8 @@ "bounded": 0, "defaultValue": null, "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, + "minInterval": 0, + "maxInterval": 65344, "reportableChange": 0 }, { @@ -1910,6 +1958,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "TotalOperationalHours", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x00000000", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "BootReason", "code": 4, @@ -1981,10 +2045,10 @@ "side": "server", "type": "boolean", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "0", + "defaultValue": "false", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2022,22 +2086,6 @@ "maxInterval": 65534, "reportableChange": 0 }, - { - "name": "EventList", - "code": 65530, - "mfgCode": null, - "side": "server", - "type": "array", - "included": 1, - "storageOption": "External", - "singleton": 0, - "bounded": 0, - "defaultValue": null, - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, { "name": "AttributeList", "code": 65531, @@ -2082,8 +2130,8 @@ "bounded": 0, "defaultValue": null, "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, + "minInterval": 0, + "maxInterval": 65344, "reportableChange": 0 } ], @@ -6045,7 +6093,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "1", + "defaultValue": "6", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -6067,6 +6115,22 @@ "maxInterval": 65534, "reportableChange": 0 } + ], + "events": [ + { + "name": "InitialPress", + "code": 1, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "ShortRelease", + "code": 3, + "mfgCode": null, + "side": "server", + "included": 1 + } ] } ] diff --git a/examples/lighting-app/infineon/cyw30739/README.md b/examples/lighting-app/infineon/cyw30739/README.md index 9dbfc23fe9e52f..b1ff7aa5304902 100644 --- a/examples/lighting-app/infineon/cyw30739/README.md +++ b/examples/lighting-app/infineon/cyw30739/README.md @@ -18,6 +18,7 @@ An example showing the use of Matter on the Infineon CYW30739 platform. - [Commissionable Data](#commissionable-data) - [Device Information](#device-information) - [DAC / DAC Key / PAI Certificate / Certificate Declaration](#dac--dac-key--pai-certificate--certificate-declaration) + - [Use Provisioned Optiga Trust M](#use-provisioned-optiga-trust-m) - [Flashing the Application](#flashing-the-application) - [Enter Recovery Mode](#enter-recovery-mode) - [Run Flash Script](#run-flash-script) @@ -163,6 +164,29 @@ keys, and CD by the following arguments: 'matter_cd="/path/to/cd.der"' ``` +### Use Provisioned Optiga Trust M + +For boards supported by Optiga Trust M, CYW30739 will provision factory data to +the Optiga Trust M by default for easy development. + +The Optiga Trust M on a production board should come with provisioned factory +data. To ensure its optimal use, please configure the Optiga using the following +arguments: + +- `use_provisioned_optiga`, `optiga_dac_object_id`, + `optiga_dac_key_object_id`, `optiga_pai_cert_object_id` + + ```bash + $ cd ~/connectedhomeip + $ scripts/examples/gn_build_example.sh examples/lighting-app/infineon/cyw30739 out/cyw30739-light \ + 'optiga_dac_object_id="0xe0e0"' \ + 'optiga_dac_key_object_id="0xe0f0"' \ + 'optiga_pai_cert_object_id="0xe0e8"' + ``` + +The developer must set the object IDs to corresponding values matching the +configurations used in the Optiga provisioning procedure. + ## Flashing the Application ### Enter Recovery Mode @@ -190,19 +214,7 @@ Put the CYW30739 in to the recovery mode before running the flash script. [Openthread_border_router](https://github.com/project-chip/connectedhomeip/blob/master/docs/guides/openthread_border_router_pi.md) for more information on how to setup a border router on a raspberryPi. -- You can provision and control the Chip device using the python controller, - Chip tool standalone, Android or iOS app +- You can provision and control the device using the Python controller REPL, + chip-tool standalone, Android or iOS app [Python Controller](https://github.com/project-chip/connectedhomeip/blob/master/src/controller/python/README.md) - - Here is an example with the Python controller: - - ```bash - $ chip-device-ctrl - chip-device-ctrl > connect -ble 3840 20202021 1234 - chip-device-ctrl > zcl NetworkCommissioning AddThreadNetwork 1234 0 0 operationalDataset=hex:0e080000000000000000000300000b35060004001fffe00208dead00beef00cafe0708fddead00beef000005108e11d8ea8ffaa875713699f59e8807e0030a4f70656e5468726561640102c2980410edc641eb63b100b87e90a9980959befc0c0402a0fff8 breadcrumb=0 timeoutMs=1000 - chip-device-ctrl > zcl NetworkCommissioning EnableNetwork 1234 0 0 networkID=hex:dead00beef00cafe breadcrumb=0 timeoutMs=1000 - chip-device-ctrl > close-ble - chip-device-ctrl > resolve 1234 - chip-device-ctrl > zcl OnOff Toggle 1234 1 0 - ``` diff --git a/examples/lighting-app/python/README.md b/examples/lighting-app/python/README.md index 8e34e59dea5847..a935a165c93e4c 100644 --- a/examples/lighting-app/python/README.md +++ b/examples/lighting-app/python/README.md @@ -32,17 +32,6 @@ cd examples/lighting-app/python python lighting.py ``` -Control the Python lighting matter device: +Control the Python lighting matter device using the Python controller REPL: -```shell -source ./out/python_env/bin/activate - -chip-device-ctrl - -chip-device-ctrl > connect -ble 3840 20202021 12344321 -chip-device-ctrl > zcl NetworkCommissioning AddOrUpdateWiFiNetwork 12344321 0 0 ssid=str:YOUR_SSID credentials=str:YOUR_PASSWORD breadcrumb=0 -chip-device-ctrl > zcl NetworkCommissioning ConnectNetwork 12344321 0 0 networkID=str:YOUR_SSID breadcrumb=0 -chip-device-ctrl > close-ble -chip-device-ctrl > resolve 5544332211 1 (pass appropriate fabric ID and node ID, you can get this from get-fabricid) -chip-device-ctrl > zcl OnOff Toggle 12344321 1 0 -``` +[Python Controller](https://github.com/project-chip/connectedhomeip/blob/master/src/controller/python/README.md) diff --git a/examples/lighting-app/qpg/BUILD.gn b/examples/lighting-app/qpg/BUILD.gn index c835e0bf3cdaed..2677357a2f21e1 100644 --- a/examples/lighting-app/qpg/BUILD.gn +++ b/examples/lighting-app/qpg/BUILD.gn @@ -140,7 +140,7 @@ qpg_executable("lighting_app") { } } - ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}/QorvoStack_${qpg_target_ic}.ld" + ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}${qpg_flavour}/QorvoStack_${qpg_target_ic}${qpg_flavour}.ld" inputs = [ ldscript ] diff --git a/examples/lighting-app/qpg/include/AppTask.h b/examples/lighting-app/qpg/include/AppTask.h index 1021b7af7dbdcc..52023c2bde2125 100644 --- a/examples/lighting-app/qpg/include/AppTask.h +++ b/examples/lighting-app/qpg/include/AppTask.h @@ -63,6 +63,7 @@ class AppTask static void LightingActionEventHandler(AppEvent * aEvent); static void TimerEventHandler(chip::System::Layer * aLayer, void * aAppState); + static void TotalHoursTimerHandler(chip::System::Layer * aLayer, void * aAppState); static void MatterEventHandler(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg); static void UpdateLEDs(void); diff --git a/examples/lighting-app/qpg/include/CHIPProjectConfig.h b/examples/lighting-app/qpg/include/CHIPProjectConfig.h index 2e23b214716be1..f20fde352c199e 100644 --- a/examples/lighting-app/qpg/include/CHIPProjectConfig.h +++ b/examples/lighting-app/qpg/include/CHIPProjectConfig.h @@ -40,9 +40,18 @@ * CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION * * A uint32_t identifying the software version running on the device. + * First two bytes are reflecting the Matter standard + * Last two bytes are reflecting the SDK version of which the first nibble of the first byte represents the major + * version and the second nibble of the first byte has the minor number. The last byte holds the patch number. + * example for SDK v0.1.5 with Matter v1.2 standard: + * 0x01020105 */ #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION -#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x0003 +#ifndef OTA_TEST_IMAGE +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x01020105 +#else +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x01020106 +#endif #endif /** @@ -53,8 +62,13 @@ * {MAJOR_VERSION}.0d{MINOR_VERSION} */ #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING -#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.1" +#ifndef OTA_TEST_IMAGE +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.2-0.1.5" +#else +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.2-0.1.6" +#endif #endif + /** * CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE * diff --git a/examples/lighting-app/qpg/src/AppTask.cpp b/examples/lighting-app/qpg/src/AppTask.cpp index 2bc0ffafec5d2a..d870ea24deeb13 100644 --- a/examples/lighting-app/qpg/src/AppTask.cpp +++ b/examples/lighting-app/qpg/src/AppTask.cpp @@ -28,7 +28,6 @@ #include "AppEvent.h" #include "AppTask.h" #include "ota.h" -#include "powercycle_counting.h" #include @@ -70,6 +69,7 @@ using namespace ::chip::DeviceLayer; #define APP_TASK_PRIORITY 2 #define APP_EVENT_QUEUE_SIZE 10 #define QPG_LIGHT_ENDPOINT_ID (1) +#define TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS (1 * 3600) // this value must be multiplication of 3600 static uint8_t countdown = 0; @@ -96,18 +96,19 @@ StaticTask_t appTaskStruct; Clusters::Identify::EffectIdentifierEnum sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect; chip::DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider; -// Define a custom attribute persister which makes actual write of the attribute value +// Define a custom attribute persister which makes actual write of the ColorX attribute value // to the non-volatile storage only when it has remained constant for 5 seconds. This is to reduce -// the flash wearout when the attribute changes frequently as a result of commands. +// the flash wearout when the attribute changes frequently as a result of MoveToLevel command. // DeferredAttribute object describes a deferred attribute, but also holds a buffer with a value to // be written, so it must live so long as the DeferredAttributePersistenceProvider object. // -DeferredAttribute gPersisters[] = { DeferredAttribute(ConcreteAttributePath(kLightEndpointId, Clusters::ColorControl::Id, - Clusters::ColorControl::Attributes::CurrentX::Id)), - DeferredAttribute(ConcreteAttributePath(kLightEndpointId, Clusters::ColorControl::Id, - Clusters::ColorControl::Attributes::CurrentY::Id)), - DeferredAttribute(ConcreteAttributePath(kLightEndpointId, Clusters::LevelControl::Id, - Clusters::LevelControl::Attributes::CurrentLevel::Id)) +DeferredAttribute gPersisters[] = { + DeferredAttribute( + ConcreteAttributePath(kLightEndpointId, Clusters::ColorControl::Id, Clusters::ColorControl::Attributes::CurrentHue::Id)), + DeferredAttribute(ConcreteAttributePath(kLightEndpointId, Clusters::ColorControl::Id, + Clusters::ColorControl::Attributes::CurrentSaturation::Id)), + DeferredAttribute( + ConcreteAttributePath(kLightEndpointId, Clusters::LevelControl::Id, Clusters::LevelControl::Attributes::CurrentLevel::Id)) }; @@ -342,6 +343,14 @@ CHIP_ERROR AppTask::Init() sIsBLEAdvertisingEnabled = ConnectivityMgr().IsBLEAdvertisingEnabled(); UpdateLEDs(); + err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS), + TotalHoursTimerHandler, this); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); + } + return err; } @@ -438,6 +447,32 @@ void AppTask::TimerEventHandler(chip::System::Layer * aLayer, void * aAppState) sAppTask.PostEvent(&event); } +void AppTask::TotalHoursTimerHandler(chip::System::Layer * aLayer, void * aAppState) +{ + ChipLogProgress(NotSpecified, "HourlyTimer"); + + CHIP_ERROR err; + uint32_t totalOperationalHours = 0; + + if (ConfigurationMgr().GetTotalOperationalHours(totalOperationalHours) == CHIP_NO_ERROR) + { + ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + + (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / 3600)); + } + else + { + ChipLogError(DeviceLayer, "Failed to get total operational hours of the Node"); + } + + err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS), + TotalHoursTimerHandler, nullptr); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); + } +} + void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) { if (aEvent->Type != AppEvent::kEventType_Timer) @@ -670,11 +705,15 @@ void AppTask::UpdateLEDs(void) // If the system has ble connection(s) uptill the stage above, THEN blink // the LEDs at an even rate of 100ms. // - // Otherwise, blink the LED ON for a very short time. + // Otherwise, turn the LED OFF. if (sIsThreadProvisioned && sIsThreadEnabled) { qvIO_LedSet(SYSTEM_STATE_LED, true); } + else if (sIsThreadProvisioned && !sIsThreadEnabled) + { + qvIO_LedBlink(SYSTEM_STATE_LED, 950, 50); + } else if (sHaveBLEConnections) { qvIO_LedBlink(SYSTEM_STATE_LED, 100, 100); @@ -686,7 +725,7 @@ void AppTask::UpdateLEDs(void) else { // not commisioned yet - qvIO_LedBlink(SYSTEM_STATE_LED, 50, 950); + qvIO_LedSet(SYSTEM_STATE_LED, false); } } diff --git a/examples/lighting-app/qpg/src/ZclCallbacks.cpp b/examples/lighting-app/qpg/src/ZclCallbacks.cpp index e2255bd4e76684..319ea28f7ffa2d 100644 --- a/examples/lighting-app/qpg/src/ZclCallbacks.cpp +++ b/examples/lighting-app/qpg/src/ZclCallbacks.cpp @@ -160,8 +160,6 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & */ void emberAfOnOffClusterInitCallback(EndpointId endpoint) { - uint8_t levelValue; - XyColor_t xy; bool onOffValue = false; app::DataModel::Nullable currentLevel; Protocols::InteractionModel::Status status; @@ -181,20 +179,11 @@ void emberAfOnOffClusterInitCallback(EndpointId endpoint) return; } - levelValue = currentLevel.Value(); - - status = ColorControl::Attributes::CurrentY::Get(endpoint, &xy.y); - if (status != Protocols::InteractionModel::Status::Success) - { - return; - } - status = ColorControl::Attributes::CurrentX::Get(endpoint, &xy.x); - if (status != Protocols::InteractionModel::Status::Success) - { - return; - } - ChipLogProgress(Zcl, "restore level: %u", levelValue); - LightingMgr().InitiateAction(LightingManager::LEVEL_ACTION, 0, 1, &levelValue); - ChipLogProgress(Zcl, "restore XY color: %u|%u", xy.x, xy.y); - LightingMgr().InitiateAction(LightingManager::COLOR_ACTION_XY, 0, sizeof(xy), (uint8_t *) &xy); + HsvColor_t hsv; + status = ColorControl::Attributes::CurrentHue::Get(endpoint, &hsv.h); + assert(status == Protocols::InteractionModel::Status::Success); + status = ColorControl::Attributes::CurrentSaturation::Get(endpoint, &hsv.s); + assert(status == Protocols::InteractionModel::Status::Success); + ChipLogProgress(Zcl, "restore HSV color: %u|%u", hsv.h, hsv.s); + LightingMgr().InitiateAction(LightingManager::COLOR_ACTION_HSV, 0, sizeof(hsv), (uint8_t *) &hsv); } diff --git a/examples/lighting-app/qpg/zap/light.matter b/examples/lighting-app/qpg/zap/light.matter index 0fb3d8b9dcf7bc..72387fd4d25ad8 100644 --- a/examples/lighting-app/qpg/zap/light.matter +++ b/examples/lighting-app/qpg/zap/light.matter @@ -2035,6 +2035,8 @@ endpoint 0 { ram attribute lastNetworkingStatus; ram attribute lastNetworkID; ram attribute lastConnectErrorValue; + callback attribute supportedThreadFeatures; + callback attribute threadVersion; callback attribute generatedCommandList; callback attribute acceptedCommandList; callback attribute attributeList; @@ -2070,6 +2072,7 @@ endpoint 0 { callback attribute networkInterfaces; callback attribute rebootCount; callback attribute upTime; + callback attribute totalOperationalHours; callback attribute bootReason; callback attribute activeHardwareFaults; callback attribute activeRadioFaults; @@ -2296,7 +2299,7 @@ endpoint 1 { callback attribute acceptedCommandList; callback attribute attributeList; ram attribute featureMap default = 1; - ram attribute clusterRevision default = 5; + ram attribute clusterRevision default = 6; handle command Off; handle command On; @@ -2343,11 +2346,11 @@ endpoint 1 { } server cluster ColorControl { - ram attribute currentHue default = 0x00; - ram attribute currentSaturation default = 0x00; + persist attribute currentHue default = 0x00; + persist attribute currentSaturation default = 0x00; ram attribute remainingTime default = 0x0000; - persist attribute currentX default = 0x616B; - persist attribute currentY default = 0x607D; + ram attribute currentX default = 0x616B; + ram attribute currentY default = 0x607D; ram attribute colorTemperatureMireds default = 0x00FA; ram attribute colorMode default = 0x01; ram attribute options default = 0x00; diff --git a/examples/lighting-app/qpg/zap/light.zap b/examples/lighting-app/qpg/zap/light.zap index 21dbe0235bc755..2c0a76acead00e 100644 --- a/examples/lighting-app/qpg/zap/light.zap +++ b/examples/lighting-app/qpg/zap/light.zap @@ -1542,6 +1542,38 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "SupportedThreadFeatures", + "code": 9, + "mfgCode": null, + "side": "server", + "type": "ThreadCapabilitiesBitmap", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ThreadVersion", + "code": 10, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "GeneratedCommandList", "code": 65528, @@ -1814,6 +1846,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "TotalOperationalHours", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x00000000", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "BootReason", "code": 4, @@ -4663,7 +4711,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "5", + "defaultValue": "6", "reportable": 1, "minInterval": 0, "maxInterval": 65344, @@ -5262,7 +5310,7 @@ "side": "server", "type": "int8u", "included": 1, - "storageOption": "RAM", + "storageOption": "NVM", "singleton": 0, "bounded": 0, "defaultValue": "0x00", @@ -5278,7 +5326,7 @@ "side": "server", "type": "int8u", "included": 1, - "storageOption": "RAM", + "storageOption": "NVM", "singleton": 0, "bounded": 0, "defaultValue": "0x00", @@ -5310,7 +5358,7 @@ "side": "server", "type": "int16u", "included": 1, - "storageOption": "NVM", + "storageOption": "RAM", "singleton": 0, "bounded": 0, "defaultValue": "0x616B", @@ -5326,7 +5374,7 @@ "side": "server", "type": "int16u", "included": 1, - "storageOption": "NVM", + "storageOption": "RAM", "singleton": 0, "bounded": 0, "defaultValue": "0x607D", diff --git a/examples/lighting-app/stm32/BUILD.gn b/examples/lighting-app/stm32/BUILD.gn index 6adecf2d04c1f5..3ac2fd365f11fa 100644 --- a/examples/lighting-app/stm32/BUILD.gn +++ b/examples/lighting-app/stm32/BUILD.gn @@ -54,24 +54,22 @@ if (stm32_board == "STM32WB5MM-DK") { } stm32_sdk("sdk") { - if (stm32_board == "STM32WB5MM-DK") { - sources = [ - "${examples_plat_dir}/config_files/STM32WB5/FreeRTOSConfig.h", - "${examples_plat_dir}/config_files/STM32WB5/matter_config.h", - "${stm32_project_dir}/include/STM32WB5/CHIPProjectConfig.h", - ] - } - include_dirs = [ "${chip_root}/src/platform/stm32", "${examples_plat_dir}", + "${stm32_project_dir}/include/STM32WB5", "${chip_root}/src/lib", ] if (stm32_board == "STM32WB5MM-DK") { + sources = [ + "${examples_plat_dir}/config_files/STM32WB5/FreeRTOSConfig.h", + "${examples_plat_dir}/config_files/STM32WB5/matter_config.h", + "${stm32_project_dir}/include/STM32WB5/CHIPProjectConfig.h", + ] + include_dirs += [ "${stm32_project_dir}/include/STM32WB5", - "${examples_plat_dir}/config_files/STM32WB5", "${chip_root}/src/include", ] } @@ -100,25 +98,29 @@ stm32_executable("lighting_app") { "${stm32_board_src}/STM32_WPAN/App/app_matter.c", "${stm32_board_src}/STM32_WPAN/App/app_thread.c", "${stm32_board_src}/STM32_WPAN/App/custom_stm.c", + + #"${stm32_board_src}/STM32_WPAN/Target/hw_ipcc.c", "${stm32_board_src}/Src/app_entry.cpp", "${stm32_board_src}/Src/main.cpp", + "${stm32_board_src}/Src/ota.cpp", "src/STM32WB5/AppTask.cpp", + "src/STM32WB5/IdentifierEffect.cpp", "src/STM32WB5/LightingManager.cpp", "src/STM32WB5/ZclCallbacks.cpp", ] + + deps = [ + ":sdk", + "${chip_root}/examples/lighting-app/lighting-common", + "${chip_root}/examples/providers:device_info_provider", + "${chip_root}/src/lib", + "${chip_root}/src/setup_payload", + ] } # Add the startup file to the target sources += [ "${examples_plat_dir}/startup_files/startup_${stm32_mcu}.s" ] - deps = [ - ":sdk", - "${chip_root}/examples/lighting-app/lighting-common", - "${chip_root}/examples/providers:device_info_provider", - "${chip_root}/src/lib", - "${chip_root}/src/setup_payload", - ] - defines += [ "DEBUG", "USE_HAL_DRIVER", diff --git a/examples/lighting-app/stm32/include/STM32WB5/AppTask.h b/examples/lighting-app/stm32/include/STM32WB5/AppTask.h index 5c0344cc8c687a..641233581872d3 100644 --- a/examples/lighting-app/stm32/include/STM32WB5/AppTask.h +++ b/examples/lighting-app/stm32/include/STM32WB5/AppTask.h @@ -26,6 +26,7 @@ #include "LightingManager.h" #include "app_entry.h" +#include #include #include #define APP_NAME "Lighting-app" @@ -55,9 +56,10 @@ class AppTask static void FunctionHandler(AppEvent * aEvent); static void LightingActionEventHandler(AppEvent * aEvent); static void TimerEventHandler(TimerHandle_t xTimer); - static void DelayNvmHandler(TimerHandle_t xTimer); static void MatterEventHandler(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg); +#if (OTA_SUPPORT == 0) static void UpdateLCD(void); +#endif static void UpdateNvmEventHandler(AppEvent * aEvent); enum Function_t diff --git a/examples/lighting-app/stm32/include/STM32WB5/CHIPProjectConfig.h b/examples/lighting-app/stm32/include/STM32WB5/CHIPProjectConfig.h index a911c565e95a1b..6540d718328090 100644 --- a/examples/lighting-app/stm32/include/STM32WB5/CHIPProjectConfig.h +++ b/examples/lighting-app/stm32/include/STM32WB5/CHIPProjectConfig.h @@ -23,16 +23,26 @@ */ #ifndef CHIPPROJECTCONFIG_H #define CHIPPROJECTCONFIG_H +#include "app_conf.h" // Use a default pairing code if one hasn't been provisioned in flash. -#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE #define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE 20202021 -#endif #ifndef CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR #define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR 0xF00 #endif +// Use a default pairing code if one hasn't been provisioned in flash. +#define CHIP_DEVICE_CONFIG_USE_TEST_PAIRING_CODE "CHIPUS" + +// For convenience, Chip Security Test Mode can be enabled and the +// requirement for authentication in various protocols can be disabled. +// +// WARNING: These options make it possible to circumvent basic Chip security functionality, +// including message encryption. Because of this they MUST NEVER BE ENABLED IN PRODUCTION BUILDS. +// +#define CHIP_CONFIG_SECURITY_TEST_MODE 0 + /** * CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID * @@ -73,6 +83,25 @@ */ #define CHIP_DEVICE_CONFIG_ENABLE_CHIP_TIME_SERVICE_TIME_SYNC 0 +/** + * CHIP_DEVICE_CONFIG_ENABLE_TEST_DEVICE_IDENTITY + * + * Enables the use of a hard-coded default Chip device id and credentials if no device id + * is found in Chip NV storage. + * + * This option is for testing only and should be disabled in production releases. + */ +#define CHIP_DEVICE_CONFIG_ENABLE_TEST_DEVICE_IDENTITY 34 + +// For convenience, enable Chip Security Test Mode and disable the requirement for +// authentication in various protocols. +// +// WARNING: These options make it possible to circumvent basic Chip security functionality, +// including message encryption. Because of this they MUST NEVER BE ENABLED IN PRODUCTION BUILDS. +// +#define CHIP_CONFIG_SECURITY_TEST_MODE 0 +#define CHIP_CONFIG_REQUIRE_AUTH 1 + /** * CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING * @@ -84,6 +113,15 @@ #define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.1" #endif +/** + * CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION + * + * A monothonic number identifying the software version running on the device. + */ +#ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 1 +#endif + /** * CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_REVISION * diff --git a/examples/lighting-app/stm32/include/STM32WB5/FreeRTOSConfig.h b/examples/lighting-app/stm32/include/STM32WB5/FreeRTOSConfig.h index aeb0ac1b8913da..e1ff4e30648486 100644 --- a/examples/lighting-app/stm32/include/STM32WB5/FreeRTOSConfig.h +++ b/examples/lighting-app/stm32/include/STM32WB5/FreeRTOSConfig.h @@ -76,7 +76,7 @@ extern uint32_t SystemCoreClock; #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_COUNTING_SEMAPHORES 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 -#define configUSE_TICKLESS_IDLE 0 +#define configUSE_TICKLESS_IDLE 2 /* USER CODE BEGIN MESSAGE_BUFFER_LENGTH_TYPE */ /* Defaults to size_t for backward compatibility, but can be changed if lengths will always be less than the number of bytes in a size_t. */ @@ -172,19 +172,20 @@ standard names. */ /* USER CODE BEGIN Defines */ /* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */ // #define configOVERRIDE_DEFAULT_TICK_CONFIGURATION 1 /* required only for Keil but does not hurt otherwise */ -#define configGENERATE_RUN_TIME_STATS 1 +/*#define configGENERATE_RUN_TIME_STATS 1 -#if (configGENERATE_RUN_TIME_STATS == 1) +#if( configGENERATE_RUN_TIME_STATS == 1 ) -extern void RTOS_AppConfigureTimerForRuntimeStats(); + extern void RTOS_AppConfigureTimerForRuntimeStats(); -extern uint32_t RTOS_AppGetRuntimeCounterValueFromISR(); + extern uint32_t RTOS_AppGetRuntimeCounterValueFromISR(); -#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() RTOS_AppConfigureTimerForRuntimeStats() + #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() RTOS_AppConfigureTimerForRuntimeStats() -#define portGET_RUN_TIME_COUNTER_VALUE() RTOS_AppGetRuntimeCounterValueFromISR() + #define portGET_RUN_TIME_COUNTER_VALUE() RTOS_AppGetRuntimeCounterValueFromISR() #endif +*/ /* USER CODE END Defines */ diff --git a/examples/lighting-app/stm32/src/STM32WB5/AppTask.cpp b/examples/lighting-app/stm32/src/STM32WB5/AppTask.cpp index d102f8a9dfd421..5841f15264bad4 100644 --- a/examples/lighting-app/stm32/src/STM32WB5/AppTask.cpp +++ b/examples/lighting-app/stm32/src/STM32WB5/AppTask.cpp @@ -24,12 +24,12 @@ #include "cmsis_os.h" #include "dbg_trace.h" #include "flash_wb.h" +#include "ota.h" #include "ssd1315.h" #include "stm32_lcd.h" #include "stm32_lpm.h" #include "stm32wb5mm_dk_lcd.h" -#include "stm_logging.h" #if HIGHWATERMARK #include "memory_buffer_alloc.h" #endif @@ -65,16 +65,12 @@ using chip::DeviceLayer::PersistedStorage::KeyValueStoreMgr; AppTask AppTask::sAppTask; chip::DeviceLayer::FactoryDataProvider mFactoryDataProvider; -#define APP_FUNCTION_BUTTON BUTTON_USER1 -#define STM32ThreadDataSet "STM32DataSet" #define APP_EVENT_QUEUE_SIZE 10 #define NVM_TIMEOUT 1000 // timer to handle PB to save data in nvm or do a factory reset -#define DELAY_NVM 5000 // save data in nvm after commissioning with a delay of 5 sec #define STM32_LIGHT_ENDPOINT_ID 1 static QueueHandle_t sAppEventQueue; TimerHandle_t sPushButtonTimeoutTimer; -TimerHandle_t DelayNvmTimer; const osThreadAttr_t AppTask_attr = { .name = APPTASK_NAME, .attr_bits = APP_ATTR_BITS, .cb_mem = APP_CB_MEM, @@ -92,6 +88,8 @@ static bool sHaveFabric = false; static uint8_t NvmTimerCpt = 0; static uint8_t NvmButtonStateCpt = 0; +chip::DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider; + CHIP_ERROR AppTask::StartAppTask() { sAppEventQueue = xQueueCreate(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent)); @@ -134,15 +132,7 @@ CHIP_ERROR AppTask::Init() TimerEventHandler // timer callback handler ); - DelayNvmTimer = xTimerCreate("Delay_NVM", // Just a text name, not used by the RTOS kernel - DELAY_NVM, // == default timer period (mS) - pdFALSE, // timer reload - 0, // init timer - DelayNvmHandler // timer callback handler - ); - ThreadStackMgr().InitThreadStack(); - ConnectivityMgr().SetThreadDeviceType(ConnectivityManager::kThreadDeviceType_Router); PlatformMgr().AddEventHandler(MatterEventHandler, 0); @@ -174,6 +164,9 @@ CHIP_ERROR AppTask::Init() initParams.endpointNativeParams = static_cast(&nativeParams); chip::Server::GetInstance().Init(initParams); + gExampleDeviceInfoProvider.SetStorageDelegate(&Server::GetInstance().GetPersistentStorage()); + chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); + ConfigurationMgr().LogDeviceConfig(); // Open commissioning after boot if no fabric was available @@ -186,23 +179,13 @@ CHIP_ERROR AppTask::Init() } else { // try to attach to the thread network - uint8_t datasetBytes[Thread::kSizeOperationalDataset]; - size_t datasetLength = 0; + sHaveFabric = true; +#if (CFG_LCD_SUPPORTED == 1) char Message[20]; snprintf(Message, sizeof(Message), "Fabric Found: %d", chip::Server::GetInstance().GetFabricTable().FabricCount()); - APP_BLE_Init_Dyn_3(); UTIL_LCD_DisplayStringAt(0, LINE(1), (uint8_t *) Message, LEFT_MODE); BSP_LCD_Refresh(0); - CHIP_ERROR error = KeyValueStoreMgr().Get(STM32ThreadDataSet, datasetBytes, sizeof(datasetBytes), &datasetLength); - if (error == CHIP_NO_ERROR) - { - ThreadStackMgr().SetThreadProvision(ByteSpan(datasetBytes, datasetLength)); - ThreadStackMgr().SetThreadEnabled(true); - } - else - { - APP_DBG("Thread network Data set was not found"); - } +#endif } err = PlatformMgr().StartEventLoopTask(); @@ -225,7 +208,6 @@ CHIP_ERROR AppTask::InitMatter() } else { - APP_DBG("Init CHIP stack"); err = PlatformMgr().InitChipStack(); if (err != CHIP_NO_ERROR) { @@ -248,14 +230,14 @@ void AppTask::AppTaskMain(void * pvParameter) #endif // endif HIGHWATERMARK if (err != CHIP_NO_ERROR) { - APP_DBG("App task init failled "); + APP_DBG("App task init failed "); } APP_DBG("App Task started"); while (true) { - BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, pdMS_TO_TICKS(10)); + BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, portMAX_DELAY); while (eventReceived == pdTRUE) { sAppTask.DispatchEvent(&event); @@ -309,7 +291,7 @@ void AppTask::ButtonEventHandler(Push_Button_st * Button) button_event.ButtonEvent.ButtonIdx = Button->Pushed_Button; button_event.ButtonEvent.Action = Button->State; - if (Button->Pushed_Button == APP_FUNCTION_BUTTON) + if (Button->Pushed_Button == BUTTON_USER1) { // Hand off to Functionality handler - depends on duration of press button_event.Handler = FunctionHandler; @@ -344,12 +326,16 @@ void AppTask::TimerEventHandler(TimerHandle_t xTimer) } else if ((NvmTimerCpt > NvmButtonStateCpt) && (NvmTimerCpt <= 2)) { - AppEvent event; - event.Type = AppEvent::kEventType_Timer; - event.Handler = UpdateNvmEventHandler; xTimerStop(sPushButtonTimeoutTimer, 0); - sAppTask.mFunction = kFunction_SaveNvm; - sAppTask.PostEvent(&event); + if (sHaveFabric == true) + { + AppEvent event; + event.Type = AppEvent::kEventType_Timer; + event.Handler = UpdateNvmEventHandler; + xTimerStop(sPushButtonTimeoutTimer, 0); + sAppTask.mFunction = kFunction_SaveNvm; + sAppTask.PostEvent(&event); + } } } @@ -366,30 +352,40 @@ void AppTask::FunctionHandler(AppEvent * aEvent) void AppTask::ActionInitiated(LightingManager::Action_t aAction) { // Placeholder for light action +#if (CFG_LCD_SUPPORTED == 1) UTIL_LCD_ClearStringLine(2); +#endif if (aAction == LightingManager::ON_ACTION) { APP_DBG("Light goes on"); +#if (CFG_LCD_SUPPORTED == 1) char Message[11]; snprintf(Message, sizeof(Message), "LED ON %d", LightingMgr().GetLevel()); UTIL_LCD_DisplayStringAt(0, LINE(2), (uint8_t *) Message, CENTER_MODE); +#endif } else if (aAction == LightingManager::OFF_ACTION) { APP_DBG("Light goes off "); +#if (CFG_LCD_SUPPORTED == 1) UTIL_LCD_ClearStringLine(2); +#endif } else if (aAction == LightingManager::LEVEL_ACTION) { if (LightingMgr().IsTurnedOn()) { +#if (CFG_LCD_SUPPORTED == 1) char Message[11]; snprintf(Message, sizeof(Message), "LED ON %d", LightingMgr().GetLevel()); UTIL_LCD_DisplayStringAt(0, LINE(2), (uint8_t *) Message, CENTER_MODE); +#endif APP_DBG("Update level control %d", LightingMgr().GetLevel()); } } +#if (CFG_LCD_SUPPORTED == 1) BSP_LCD_Refresh(0); +#endif } void AppTask::ActionCompleted(LightingManager::Action_t aAction) @@ -459,15 +455,7 @@ void AppTask::UpdateClusterState(void) } } -void AppTask::DelayNvmHandler(TimerHandle_t xTimer) -{ - AppEvent event; - event.Type = AppEvent::kEventType_Timer; - event.Handler = UpdateNvmEventHandler; - sAppTask.mFunction = kFunction_SaveNvm; - sAppTask.PostEvent(&event); -} - +#if (CFG_LCD_SUPPORTED == 1) void AppTask::UpdateLCD(void) { if (sIsThreadProvisioned && sIsThreadEnabled) @@ -498,6 +486,7 @@ void AppTask::UpdateLCD(void) } BSP_LCD_Refresh(0); } +#endif void AppTask::UpdateNvmEventHandler(AppEvent * aEvent) { @@ -505,13 +494,6 @@ void AppTask::UpdateNvmEventHandler(AppEvent * aEvent) if (sAppTask.mFunction == kFunction_SaveNvm) { - if (sIsThreadProvisioned && sIsThreadEnabled) - { - chip::Thread::OperationalDataset dataset{}; - DeviceLayer::ThreadStackMgrImpl().GetThreadProvision(dataset); - ByteSpan datasetbyte = dataset.AsByteSpan(); - KeyValueStoreMgr().Put(STM32ThreadDataSet, datasetbyte.data(), datasetbyte.size()); - } err = NM_Dump(); if (err == 0) { @@ -520,8 +502,6 @@ void AppTask::UpdateNvmEventHandler(AppEvent * aEvent) else { APP_DBG("Failed to SAVE NVM"); - // restart timer to save nvm later - xTimerStart(DelayNvmTimer, 0); } } else if (sAppTask.mFunction == kFunction_FactoryReset) @@ -537,32 +517,42 @@ void AppTask::MatterEventHandler(const ChipDeviceEvent * event, intptr_t) { case DeviceEventType::kServiceProvisioningChange: { sIsThreadProvisioned = event->ServiceProvisioningChange.IsServiceProvisioned; +#if (CFG_LCD_SUPPORTED == 1) UpdateLCD(); +#endif break; } case DeviceEventType::kThreadConnectivityChange: { sIsThreadEnabled = (event->ThreadConnectivityChange.Result == kConnectivity_Established); +#if (CFG_LCD_SUPPORTED == 1) UpdateLCD(); +#endif break; } case DeviceEventType::kCHIPoBLEConnectionEstablished: { sHaveBLEConnections = true; APP_DBG("kCHIPoBLEConnectionEstablished"); +#if (CFG_LCD_SUPPORTED == 1) UpdateLCD(); +#endif break; } case DeviceEventType::kCHIPoBLEConnectionClosed: { sHaveBLEConnections = false; APP_DBG("kCHIPoBLEConnectionClosed"); +#if (CFG_LCD_SUPPORTED == 1) UpdateLCD(); +#endif if (sFabricNeedSaved) { - APP_DBG("Start timer to save nvm after commissioning finish"); - // timer is used to avoid to much traffic on m0 side after the end of a commissioning - xTimerStart(DelayNvmTimer, 0); + AppEvent event; + event.Type = AppEvent::kEventType_Timer; + event.Handler = UpdateNvmEventHandler; + sAppTask.mFunction = kFunction_SaveNvm; + sAppTask.PostEvent(&event); sFabricNeedSaved = false; } break; @@ -574,18 +564,30 @@ void AppTask::MatterEventHandler(const ChipDeviceEvent * event, intptr_t) // check if ble is on, since before save in nvm we need to stop m0, Better to write in nvm when m0 is less busy if (sHaveBLEConnections == false) { - APP_DBG("Start timer to save nvm after commissioning finish"); - xTimerStart(DelayNvmTimer, 0); sFabricNeedSaved = false; // put to false to avoid save in nvm 2 times + AppEvent event; + event.Type = AppEvent::kEventType_Timer; + event.Handler = UpdateNvmEventHandler; + sAppTask.mFunction = kFunction_SaveNvm; + sAppTask.PostEvent(&event); } +#if (CFG_LCD_SUPPORTED == 1) UpdateLCD(); +#endif break; } case DeviceEventType::kFailSafeTimerExpired: { +#if (CFG_LCD_SUPPORTED == 1) UpdateLCD(); +#endif sFailCommissioning = true; break; } + case DeviceEventType::kDnssdInitialized: +#if (OTA_SUPPORT == 1) + InitializeOTARequestor(); +#endif + break; default: break; } diff --git a/examples/lighting-app/stm32/src/STM32WB5/IdentifierEffect.cpp b/examples/lighting-app/stm32/src/STM32WB5/IdentifierEffect.cpp new file mode 100644 index 00000000000000..ebed8fd6c6e39c --- /dev/null +++ b/examples/lighting-app/stm32/src/STM32WB5/IdentifierEffect.cpp @@ -0,0 +1,66 @@ + + +#include "AppEvent.h" +#include "AppTask.h" +#include + +using namespace ::chip::app; +using namespace ::chip::DeviceLayer; + +Clusters::Identify::EffectIdentifierEnum sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect; + +/********************************************************** + * Identify Callbacks + *********************************************************/ + +namespace { +void OnTriggerIdentifyEffectCompleted(chip::System::Layer * systemLayer, void * appState) +{ + sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect; +} +} // namespace + +void OnTriggerIdentifyEffect(Identify * identify) +{ + sIdentifyEffect = identify->mCurrentEffectIdentifier; + + if (identify->mEffectVariant != Clusters::Identify::EffectVariantEnum::kDefault) + { + ChipLogDetail(AppServer, "Identify Effect Variant unsupported. Using default"); + } + + switch (sIdentifyEffect) + { + case Clusters::Identify::EffectIdentifierEnum::kBlink: + case Clusters::Identify::EffectIdentifierEnum::kBreathe: + case Clusters::Identify::EffectIdentifierEnum::kOkay: + case Clusters::Identify::EffectIdentifierEnum::kChannelChange: + SystemLayer().ScheduleLambda([identify] { + (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(5), OnTriggerIdentifyEffectCompleted, + identify); + }); + break; + case Clusters::Identify::EffectIdentifierEnum::kFinishEffect: + SystemLayer().ScheduleLambda([identify] { + (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify); + (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(1), OnTriggerIdentifyEffectCompleted, + identify); + }); + break; + case Clusters::Identify::EffectIdentifierEnum::kStopEffect: + SystemLayer().ScheduleLambda( + [identify] { (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify); }); + sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect; + break; + default: + ChipLogProgress(Zcl, "No identifier effect"); + } +} + +Identify gIdentify = { + chip::EndpointId{ 1 }, + [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStart"); }, + [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStop"); }, + Clusters::Identify::IdentifyTypeEnum::kVisibleIndicator, + OnTriggerIdentifyEffect, +}; diff --git a/examples/lighting-app/stm32/src/STM32WB5/ZclCallbacks.cpp b/examples/lighting-app/stm32/src/STM32WB5/ZclCallbacks.cpp index 762ad402274cf6..02002c9250227c 100644 --- a/examples/lighting-app/stm32/src/STM32WB5/ZclCallbacks.cpp +++ b/examples/lighting-app/stm32/src/STM32WB5/ZclCallbacks.cpp @@ -1,7 +1,7 @@ +/* USER CODE BEGIN Header */ /* * - * Copyright (c) 2021 Project CHIP Authors - * Copyright (c) 2019 Google LLC. + * Copyright (c) 2020 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,6 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* USER CODE END Header */ #include "AppTask.h" #include "LightingManager.h" diff --git a/examples/lock-app/infineon/cyw30739/README.md b/examples/lock-app/infineon/cyw30739/README.md index 6623f77c88c1c4..2b7bd2094154df 100644 --- a/examples/lock-app/infineon/cyw30739/README.md +++ b/examples/lock-app/infineon/cyw30739/README.md @@ -18,6 +18,7 @@ An example showing the use of Matter on the Infineon CYW30739 platform. - [Commissionable Data](#commissionable-data) - [Device Information](#device-information) - [DAC / DAC Key / PAI Certificate / Certificate Declaration](#dac--dac-key--pai-certificate--certificate-declaration) + - [Use Provisioned Optiga Trust M](#use-provisioned-optiga-trust-m) - [Flashing the Application](#flashing-the-application) - [Enter Recovery Mode](#enter-recovery-mode) - [Run Flash Script](#run-flash-script) @@ -163,6 +164,29 @@ keys, and CD by the following arguments: 'matter_cd="/path/to/cd.der"' ``` +### Use Provisioned Optiga Trust M + +For boards supported by Optiga Trust M, CYW30739 will provision factory data to +the Optiga Trust M by default for easy development. + +The Optiga Trust M on a production board should come with provisioned factory +data. To ensure its optimal use, please configure the Optiga using the following +arguments: + +- `use_provisioned_optiga`, `optiga_dac_object_id`, + `optiga_dac_key_object_id`, `optiga_pai_cert_object_id` + + ```bash + $ cd ~/connectedhomeip + $ scripts/examples/gn_build_example.sh examples/lock-app/infineon/cyw30739 out/cyw30739-lock \ + 'optiga_dac_object_id="0xe0e0"' \ + 'optiga_dac_key_object_id="0xe0f0"' \ + 'optiga_pai_cert_object_id="0xe0e8"' + ``` + +The developer must set the object IDs to corresponding values matching the +configurations used in the Optiga provisioning procedure. + ## Flashing the Application ### Enter Recovery Mode @@ -190,19 +214,7 @@ Put the CYW30739 in to the recovery mode before running the flash script. [Openthread_border_router](https://github.com/project-chip/connectedhomeip/blob/master/docs/guides/openthread_border_router_pi.md) for more information on how to setup a border router on a raspberryPi. -- You can provision and control the Chip device using the python controller, - Chip tool standalone, Android or iOS app +- You can provision and control the device using the Python controller REPL, + chip-tool standalone, Android or iOS app [Python Controller](https://github.com/project-chip/connectedhomeip/blob/master/src/controller/python/README.md) - - Here is an example with the Python controller: - - ```bash - $ chip-device-ctrl - chip-device-ctrl > connect -ble 3840 20202021 1234 - chip-device-ctrl > zcl NetworkCommissioning AddThreadNetwork 1234 0 0 operationalDataset=hex:0e080000000000000000000300000b35060004001fffe00208dead00beef00cafe0708fddead00beef000005108e11d8ea8ffaa875713699f59e8807e0030a4f70656e5468726561640102c2980410edc641eb63b100b87e90a9980959befc0c0402a0fff8 breadcrumb=0 timeoutMs=1000 - chip-device-ctrl > zcl NetworkCommissioning EnableNetwork 1234 0 0 networkID=hex:dead00beef00cafe breadcrumb=0 timeoutMs=1000 - chip-device-ctrl > close-ble - chip-device-ctrl > resolve 1234 - chip-device-ctrl > zcl OnOff Toggle 1234 1 0 - ``` diff --git a/examples/lock-app/qpg/BUILD.gn b/examples/lock-app/qpg/BUILD.gn index 30794878fd1ae8..3db4d0cd1ee86d 100644 --- a/examples/lock-app/qpg/BUILD.gn +++ b/examples/lock-app/qpg/BUILD.gn @@ -54,6 +54,7 @@ qpg_executable("lock_app") { "${chip_root}/src/app/clusters/general-diagnostics-server/GenericFaultTestEventTriggerHandler.cpp", "${examples_plat_dir}/app/main.cpp", "${examples_plat_dir}/ota/ota.cpp", + "${examples_plat_dir}/powercycle_counting.c", "src/AppTask.cpp", "src/BoltLockManager.cpp", "src/ZclCallbacks.cpp", @@ -83,7 +84,7 @@ qpg_executable("lock_app") { "${examples_plat_dir}/ota", ] - defines = [] + defines = [ "GP_APP_DIVERSITY_POWERCYCLECOUNTING" ] if (chip_enable_pw_rpc) { defines += [ @@ -137,7 +138,7 @@ qpg_executable("lock_app") { } } - ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}/QorvoStack_${qpg_target_ic}.ld" + ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}${qpg_flavour}/QorvoStack_${qpg_target_ic}${qpg_flavour}.ld" inputs = [ ldscript ] diff --git a/examples/lock-app/qpg/include/AppTask.h b/examples/lock-app/qpg/include/AppTask.h index b4ede0e6abde0d..a743a1c224850d 100644 --- a/examples/lock-app/qpg/include/AppTask.h +++ b/examples/lock-app/qpg/include/AppTask.h @@ -50,6 +50,7 @@ class AppTask void UpdateClusterState(); static void ButtonEventHandler(uint8_t btnIdx, bool btnPressed); + static void OpenCommissioning(intptr_t arg); private: friend AppTask & GetAppTask(void); @@ -68,6 +69,7 @@ class AppTask static void LockActionEventHandler(AppEvent * aEvent); static void JammedLockEventHandler(AppEvent * aEvent); static void TimerEventHandler(chip::System::Layer * aLayer, void * aAppState); + static void TotalHoursTimerHandler(chip::System::Layer * aLayer, void * aAppState); static void MatterEventHandler(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg); static void UpdateLEDs(void); diff --git a/examples/lock-app/qpg/include/CHIPProjectConfig.h b/examples/lock-app/qpg/include/CHIPProjectConfig.h index 826224654239ca..a35cfc0795697b 100644 --- a/examples/lock-app/qpg/include/CHIPProjectConfig.h +++ b/examples/lock-app/qpg/include/CHIPProjectConfig.h @@ -40,9 +40,18 @@ * CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION * * A uint32_t identifying the software version running on the device. + * First two bytes are reflecting the Matter standard + * Last two bytes are reflecting the SDK version of which the first nibble of the first byte represents the major + * version and the second nibble of the first byte has the minor number. The last byte holds the patch number. + * example for SDK v0.1.5 with Matter v1.2 standard: + * 0x01020105 */ #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION -#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x0003 +#ifndef OTA_TEST_IMAGE +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x01020105 +#else +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x01020106 +#endif #endif /** @@ -53,8 +62,13 @@ * {MAJOR_VERSION}.0d{MINOR_VERSION} */ #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING -#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.1" +#ifndef OTA_TEST_IMAGE +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.2-0.1.5" +#else +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.2-0.1.6" +#endif #endif + /** * CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE * diff --git a/examples/lock-app/qpg/src/AppTask.cpp b/examples/lock-app/qpg/src/AppTask.cpp index 7f9d922cc8558d..d2f3298db9b779 100644 --- a/examples/lock-app/qpg/src/AppTask.cpp +++ b/examples/lock-app/qpg/src/AppTask.cpp @@ -16,6 +16,11 @@ * limitations under the License. */ +#if !defined(GP_APP_DIVERSITY_POWERCYCLECOUNTING) +#error This application requires powercycle counting. +#endif + +#include "powercycle_counting.h" #include "qvIO.h" #include "AppConfig.h" @@ -59,6 +64,9 @@ using namespace ::chip::DeviceLayer; #define APP_TASK_PRIORITY 2 #define APP_EVENT_QUEUE_SIZE 10 #define QPG_LOCK_ENDPOINT_ID (1) +#define TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS (1 * 3600) // this value must be multiplication of 3600 + +#define NMBR_OF_RESETS_BLE_ADVERTISING (3) namespace { TaskHandle_t sAppTaskHandle; @@ -114,15 +122,26 @@ void OnTriggerIdentifyEffect(Identify * identify) switch (sIdentifyEffect) { case Clusters::Identify::EffectIdentifierEnum::kBlink: + ChipLogProgress(Zcl, "kBlink"); + qvIO_LedBlink(LOCK_STATE_LED, 100, 100); + break; case Clusters::Identify::EffectIdentifierEnum::kBreathe: + ChipLogProgress(Zcl, "kBreathe"); + qvIO_LedBlink(LOCK_STATE_LED, 500, 500); + break; case Clusters::Identify::EffectIdentifierEnum::kOkay: + ChipLogProgress(Zcl, "kOkay"); + qvIO_LedBlink(LOCK_STATE_LED, 1000, 1000); + break; case Clusters::Identify::EffectIdentifierEnum::kChannelChange: + ChipLogProgress(Zcl, "kChannelChange"); SystemLayer().ScheduleLambda([identify] { (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(5), OnTriggerIdentifyEffectCompleted, identify); }); break; case Clusters::Identify::EffectIdentifierEnum::kFinishEffect: + ChipLogProgress(Zcl, "kFinishEffect"); SystemLayer().ScheduleLambda([identify] { (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify); (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(1), OnTriggerIdentifyEffectCompleted, @@ -130,9 +149,11 @@ void OnTriggerIdentifyEffect(Identify * identify) }); break; case Clusters::Identify::EffectIdentifierEnum::kStopEffect: + ChipLogProgress(Zcl, "kStopEffect"); SystemLayer().ScheduleLambda( [identify] { (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify); }); sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect; + qvIO_LedSet(LOCK_STATE_LED, false); break; default: ChipLogProgress(Zcl, "No identifier effect"); @@ -204,6 +225,21 @@ void AppTask::InitServer(intptr_t arg) chip::app::DnssdServer::Instance().SetExtendedDiscoveryTimeoutSecs(extDiscTimeoutSecs); #endif } + +void AppTask::OpenCommissioning(intptr_t arg) +{ + // Enable BLE advertisements + + SystemLayer().ScheduleLambda([] { + CHIP_ERROR err; + err = chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow(); + if (err == CHIP_NO_ERROR) + { + ChipLogProgress(NotSpecified, "BLE advertising started. Waiting for Pairing."); + } + }); +} + CHIP_ERROR AppTask::Init() { CHIP_ERROR err = CHIP_NO_ERROR; @@ -246,6 +282,14 @@ CHIP_ERROR AppTask::Init() sIsBLEAdvertisingEnabled = ConnectivityMgr().IsBLEAdvertisingEnabled(); UpdateLEDs(); + err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS), + TotalHoursTimerHandler, this); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); + } + return err; } @@ -363,6 +407,32 @@ void AppTask::TimerEventHandler(chip::System::Layer * aLayer, void * aAppState) sAppTask.PostEvent(&event); } +void AppTask::TotalHoursTimerHandler(chip::System::Layer * aLayer, void * aAppState) +{ + ChipLogProgress(NotSpecified, "HourlyTimer"); + + CHIP_ERROR err; + uint32_t totalOperationalHours = 0; + + if (ConfigurationMgr().GetTotalOperationalHours(totalOperationalHours) == CHIP_NO_ERROR) + { + ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + + (TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS / 3600)); + } + else + { + ChipLogError(DeviceLayer, "Failed to get total operational hours of the Node"); + } + + err = chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(TOTAL_OPERATIONAL_HOURS_SAVE_INTERVAL_SECONDS), + TotalHoursTimerHandler, nullptr); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err)); + } +} + void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) { if (aEvent->Type != AppEvent::kEventType_Timer) @@ -660,11 +730,15 @@ void AppTask::UpdateLEDs(void) // If the system has ble connection(s) uptill the stage above, THEN blink // the LEDs at an even rate of 100ms. // - // Otherwise, blink the LED ON for a very short time. + // Otherwise, turn the LED OFF. if (sIsThreadProvisioned && sIsThreadEnabled) { qvIO_LedSet(SYSTEM_STATE_LED, true); } + else if (sIsThreadProvisioned && !sIsThreadEnabled) + { + qvIO_LedBlink(SYSTEM_STATE_LED, 950, 50); + } else if (sHaveBLEConnections) { qvIO_LedBlink(SYSTEM_STATE_LED, 100, 100); @@ -676,7 +750,7 @@ void AppTask::UpdateLEDs(void) else { // not commisioned yet - qvIO_LedBlink(SYSTEM_STATE_LED, 50, 950); + qvIO_LedSet(SYSTEM_STATE_LED, false); } } @@ -718,3 +792,21 @@ void AppTask::MatterEventHandler(const ChipDeviceEvent * event, intptr_t) break; } } + +extern "C" { +void gpAppFramework_Reset_cbTriggerResetCountCompleted(void) +{ + uint8_t resetCount = gpAppFramework_Reset_GetResetCount(); + + ChipLogProgress(NotSpecified, "%d resets so far", resetCount); + if (resetCount >= NMBR_OF_RESETS_BLE_ADVERTISING) + { + // Open commissioning if no fabric was available + if (chip::Server::GetInstance().GetFabricTable().FabricCount() == 0) + { + ChipLogProgress(NotSpecified, "No fabrics, starting commissioning."); + AppTask::OpenCommissioning((intptr_t) 0); + } + } +} +} diff --git a/examples/lock-app/qpg/zap/lock.matter b/examples/lock-app/qpg/zap/lock.matter index cf9816993e89b4..aa523a53539c13 100644 --- a/examples/lock-app/qpg/zap/lock.matter +++ b/examples/lock-app/qpg/zap/lock.matter @@ -2258,6 +2258,8 @@ endpoint 0 { ram attribute lastNetworkingStatus; ram attribute lastNetworkID; ram attribute lastConnectErrorValue; + callback attribute supportedThreadFeatures; + callback attribute threadVersion; callback attribute generatedCommandList; callback attribute acceptedCommandList; callback attribute attributeList; @@ -2293,6 +2295,7 @@ endpoint 0 { callback attribute networkInterfaces; callback attribute rebootCount; callback attribute upTime; + callback attribute totalOperationalHours; callback attribute bootReason; callback attribute activeHardwareFaults; callback attribute activeRadioFaults; @@ -2480,7 +2483,7 @@ endpoint 0 { callback attribute acceptedCommandList; callback attribute eventList; callback attribute attributeList; - ram attribute featureMap default = 0x0001; + ram attribute featureMap default = 0x0000; ram attribute clusterRevision default = 2; } } @@ -2558,7 +2561,7 @@ endpoint 1 { callback attribute acceptedCommandList; callback attribute attributeList; ram attribute featureMap default = 0x181; - ram attribute clusterRevision default = 6; + ram attribute clusterRevision default = 7; handle command LockDoor; handle command UnlockDoor; diff --git a/examples/lock-app/qpg/zap/lock.zap b/examples/lock-app/qpg/zap/lock.zap index 9d13502644b3d0..d4116f8b9d7602 100644 --- a/examples/lock-app/qpg/zap/lock.zap +++ b/examples/lock-app/qpg/zap/lock.zap @@ -1542,6 +1542,38 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "SupportedThreadFeatures", + "code": 9, + "mfgCode": null, + "side": "server", + "type": "ThreadCapabilitiesBitmap", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ThreadVersion", + "code": 10, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "GeneratedCommandList", "code": 65528, @@ -1814,6 +1846,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "TotalOperationalHours", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int32u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x00000000", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "BootReason", "code": 4, @@ -4224,7 +4272,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "0x0001", + "defaultValue": "0x0000", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -5179,7 +5227,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "6", + "defaultValue": "7", "reportable": 1, "minInterval": 0, "maxInterval": 65344, diff --git a/examples/lock-app/silabs/include/AppConfig.h b/examples/lock-app/silabs/include/AppConfig.h index 2045ce3ef4d947..2a98805fa0c8d3 100644 --- a/examples/lock-app/silabs/include/AppConfig.h +++ b/examples/lock-app/silabs/include/AppConfig.h @@ -31,6 +31,10 @@ // state to another. #define ACTUATOR_MOVEMENT_PERIOS_MS 10 +// Time the device will be left in the unlatched state before sending it back to the unlocked state. +// Left at 100 ms for testing purposes. +#define UNLATCH_TIME_MS 100 + #define ON_DEMO_BITMAP \ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, \ diff --git a/examples/lock-app/silabs/include/AppTask.h b/examples/lock-app/silabs/include/AppTask.h index b0040ac5bf6586..fb911e3ff02b5c 100644 --- a/examples/lock-app/silabs/include/AppTask.h +++ b/examples/lock-app/silabs/include/AppTask.h @@ -119,6 +119,13 @@ class AppTask : public BaseApplication */ static void UpdateClusterState(intptr_t context); + /** + * @brief Update Cluster State After Unlatch + * + * @param context current context + */ + static void UpdateClusterStateAfterUnlatch(intptr_t context); + /** * @brief Handle lock update event * diff --git a/examples/lock-app/silabs/include/LockManager.h b/examples/lock-app/silabs/include/LockManager.h index fb73d1d1218d23..7345782d695194 100644 --- a/examples/lock-app/silabs/include/LockManager.h +++ b/examples/lock-app/silabs/include/LockManager.h @@ -119,6 +119,7 @@ class LockManager { LOCK_ACTION = 0, UNLOCK_ACTION, + UNLATCH_ACTION, INVALID_ACTION } Action; @@ -128,7 +129,9 @@ class LockManager kState_LockInitiated = 0, kState_LockCompleted, kState_UnlockInitiated, + kState_UnlatchInitiated, kState_UnlockCompleted, + kState_UnlatchCompleted, } State; CHIP_ERROR Init(chip::app::DataModel::Nullable state, @@ -191,7 +194,30 @@ class LockManager bool ReadConfigValues(); + void UnlockAfterUnlatch(); + private: + struct UnlatchContext + { + chip::EndpointId mEndpointId; + Nullable mFabricIdx; + Nullable mNodeId; + Optional mPin; + OperationErrorEnum mErr; + + void Update(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pin, OperationErrorEnum & err) + { + mEndpointId = endpointId; + mFabricIdx = fabricIdx; + mNodeId = nodeId; + mPin = pin; + mErr = err; + } + }; + UnlatchContext mUnlatchContext; + chip::EndpointId mCurrentEndpointId; + friend LockManager & LockMgr(); State_t mState; diff --git a/examples/lock-app/silabs/openthread.gni b/examples/lock-app/silabs/openthread.gni index 3423049a9a8f79..970d3a05c3aefc 100644 --- a/examples/lock-app/silabs/openthread.gni +++ b/examples/lock-app/silabs/openthread.gni @@ -28,7 +28,7 @@ openthread_external_platform = sl_enable_test_event_trigger = true # ICD Default configurations -chip_enable_icd_server = true +chip_enable_icd_server = false chip_subscription_timeout_resumption = false sl_use_subscription_syncing = true diff --git a/examples/lock-app/silabs/src/AppTask.cpp b/examples/lock-app/silabs/src/AppTask.cpp index 6937dbb78e8aa5..4e4d05a810acb8 100644 --- a/examples/lock-app/silabs/src/AppTask.cpp +++ b/examples/lock-app/silabs/src/AppTask.cpp @@ -70,6 +70,45 @@ using namespace EFR32DoorLock::LockInitParams; namespace { LEDWidget sLockLED; +TimerHandle_t sUnlatchTimer; + +void UpdateClusterStateAfterUnlatch(intptr_t context) +{ + LockMgr().UnlockAfterUnlatch(); +} + +void UnlatchTimerCallback(TimerHandle_t xTimer) +{ + chip::DeviceLayer::PlatformMgr().ScheduleWork(UpdateClusterStateAfterUnlatch, reinterpret_cast(nullptr)); +} + +void CancelUnlatchTimer(void) +{ + if (xTimerStop(sUnlatchTimer, pdMS_TO_TICKS(0)) == pdFAIL) + { + SILABS_LOG("sUnlatchTimer stop() failed"); + appError(APP_ERROR_STOP_TIMER_FAILED); + } +} + +void StartUnlatchTimer(uint32_t timeoutMs) +{ + if (xTimerIsTimerActive(sUnlatchTimer)) + { + SILABS_LOG("app timer already started!"); + CancelUnlatchTimer(); + } + + // timer is not active, change its period to required value (== restart). + // FreeRTOS- Block for a maximum of 100 ms if the change period command + // cannot immediately be sent to the timer command queue. + if (xTimerStart(sUnlatchTimer, pdMS_TO_TICKS(timeoutMs)) != pdPASS) + { + SILABS_LOG("sUnlatchTimer timer start() failed"); + appError(APP_ERROR_START_TIMER_FAILED); + } +} + } // namespace using namespace chip::TLV; @@ -180,6 +219,8 @@ CHIP_ERROR AppTask::Init() sLockLED.Init(LOCK_STATE_LED); sLockLED.Set(state.Value() == DlLockState::kUnlocked); + sUnlatchTimer = xTimerCreate("UnlatchTimer", pdMS_TO_TICKS(UNLATCH_TIME_MS), pdFALSE, (void *) 0, UnlatchTimerCallback); + // Update the LCD with the Stored value. Show QR Code if not provisioned #ifdef DISPLAY_ENABLED GetLCD().WriteDemoUI(state.Value() != DlLockState::kUnlocked); @@ -309,6 +350,10 @@ void AppTask::ActionInitiated(LockManager::Action_t aAction, int32_t aActor) sAppTask.GetLCD().WriteDemoUI(locked); #endif // DISPLAY_ENABLED } + else if (aAction == LockManager::UNLATCH_ACTION) + { + SILABS_LOG("Unlatch Action has been initiated"); + } if (aActor == AppEvent::kEventType_Button) { @@ -325,6 +370,11 @@ void AppTask::ActionCompleted(LockManager::Action_t aAction) { SILABS_LOG("Lock Action has been completed") } + else if (aAction == LockManager::UNLATCH_ACTION) + { + SILABS_LOG("Unlatch Action has been completed") + StartUnlatchTimer(UNLATCH_TIME_MS); + } else if (aAction == LockManager::UNLOCK_ACTION) { SILABS_LOG("Unlock Action has been completed") diff --git a/examples/lock-app/silabs/src/LockManager.cpp b/examples/lock-app/silabs/src/LockManager.cpp index 3c376a237ad81f..04527e53ac7ab1 100644 --- a/examples/lock-app/silabs/src/LockManager.cpp +++ b/examples/lock-app/silabs/src/LockManager.cpp @@ -186,22 +186,24 @@ bool LockManager::InitiateAction(int32_t aActor, Action_t aAction) State_t new_state; // Initiate Turn Lock/Unlock Action only when the previous one is complete. - if (mState == kState_LockCompleted && aAction == UNLOCK_ACTION) + if ((mState == kState_LockCompleted || mState == kState_UnlatchCompleted) && (aAction == UNLOCK_ACTION)) { action_initiated = true; - - new_state = kState_UnlockInitiated; + new_state = kState_UnlockInitiated; + } + else if ((mState == kState_LockCompleted || mState == kState_UnlockCompleted) && (aAction == UNLATCH_ACTION)) + { + action_initiated = true; + new_state = kState_UnlatchInitiated; } else if (mState == kState_UnlockCompleted && aAction == LOCK_ACTION) { action_initiated = true; - - new_state = kState_LockInitiated; + new_state = kState_LockInitiated; } if (action_initiated) { - StartTimer(ACTUATOR_MOVEMENT_PERIOS_MS); // Since the timer started successfully, update the state and trigger callback @@ -249,6 +251,23 @@ void LockManager::TimerEventHandler(void * timerCbArg) event.Handler = ActuatorMovementTimerEventHandler; AppTask::GetAppTask().PostEvent(&event); } +void LockManager::UnlockAfterUnlatch() +{ + // write the new lock value + bool succes = false; + if (mUnlatchContext.mEndpointId != kInvalidEndpointId) + { + succes = setLockState(mUnlatchContext.mEndpointId, mUnlatchContext.mFabricIdx, mUnlatchContext.mNodeId, + DlLockState::kUnlocked, mUnlatchContext.mPin, mUnlatchContext.mErr); + } + + if (!succes) + { + SILABS_LOG("Failed to update the lock state after Unlatch"); + } + + InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLOCK_ACTION); +} void LockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent) { @@ -261,6 +280,11 @@ void LockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent) lock->mState = kState_LockCompleted; actionCompleted = LOCK_ACTION; } + else if (lock->mState == kState_UnlatchInitiated) + { + lock->mState = kState_UnlatchCompleted; + actionCompleted = UNLATCH_ACTION; + } else if (lock->mState == kState_UnlockInitiated) { lock->mState = kState_UnlockCompleted; @@ -285,6 +309,29 @@ bool LockManager::Lock(chip::EndpointId endpointId, const Nullable & fabricIdx, const Nullable & nodeId, const Optional & pin, OperationErrorEnum & err) { + if (DoorLockServer::Instance().SupportsUnbolt(endpointId)) + { + // TODO: Our current implementation does not support multiple endpoint. This needs to be fixed in the future. + if (endpointId != mUnlatchContext.mEndpointId) + { + // If we get a request to unlock on a different endpoint while the current endpoint is in the middle of an action, + // we return false for now. This needs to be fixed in the future. + if (mState != kState_UnlockCompleted && mState != kState_LockCompleted) + { + ChipLogError(Zcl, "Cannot unlock while unlatch on another endpoint is in progress on anotther endpoint"); + return false; + } + else + { + mUnlatchContext.Update(endpointId, fabricIdx, nodeId, pin, err); + return setLockState(endpointId, fabricIdx, nodeId, DlLockState::kUnlatched, pin, err); + } + } + else + { + return setLockState(endpointId, fabricIdx, nodeId, DlLockState::kUnlatched, pin, err); + } + } return setLockState(endpointId, fabricIdx, nodeId, DlLockState::kUnlocked, pin, err); } diff --git a/examples/lock-app/silabs/src/ZclCallbacks.cpp b/examples/lock-app/silabs/src/ZclCallbacks.cpp index 2aa0e9d0f6cef2..f7e333f3acb350 100644 --- a/examples/lock-app/silabs/src/ZclCallbacks.cpp +++ b/examples/lock-app/silabs/src/ZclCallbacks.cpp @@ -89,7 +89,14 @@ bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const bool status = LockMgr().Unlock(endpointId, fabricIdx, nodeId, pinCode, err); if (status == true) { - LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLOCK_ACTION); + if (DoorLockServer::Instance().SupportsUnbolt(endpointId)) + { + LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLATCH_ACTION); + } + else + { + LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLOCK_ACTION); + } } return status; diff --git a/examples/persistent-storage/qpg/BUILD.gn b/examples/persistent-storage/qpg/BUILD.gn index 683a5ede278cff..546ec94cf2c9b9 100644 --- a/examples/persistent-storage/qpg/BUILD.gn +++ b/examples/persistent-storage/qpg/BUILD.gn @@ -58,7 +58,7 @@ qpg_executable("persistent_storage_app") { output_dir = root_out_dir - ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}/QorvoStack_${qpg_target_ic}.ld" + ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}${qpg_flavour}/QorvoStack_${qpg_target_ic}${qpg_flavour}.ld" inputs = [ ldscript ] diff --git a/examples/platform/esp32/Rpc.cpp b/examples/platform/esp32/Rpc.cpp index 61efb842f089b7..a3b61830b5a46a 100644 --- a/examples/platform/esp32/Rpc.cpp +++ b/examples/platform/esp32/Rpc.cpp @@ -52,6 +52,10 @@ #include "pigweed/rpc_services/Device.h" #endif // defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE +#if defined(PW_RPC_EVENT_SERVICE) && PW_RPC_EVENT_SERVICE +#include "pigweed/rpc_services/Event.h" +#endif // defined(PW_RPC_EVENT_SERVICE) && PW_RPC_EVENT_SERVICE + #if defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE #include "pigweed/rpc_services/Lighting.h" #endif // defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE @@ -275,6 +279,10 @@ static TaskHandle_t sRpcTaskHandle; StaticTask_t sRpcTaskBuffer; StackType_t sRpcTaskStack[RPC_TASK_STACK_SIZE]; +#if defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE +Actions actions_service; +#endif // defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + #if defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE Attributes attributes_service; #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE @@ -295,6 +303,10 @@ Descriptor descriptor_service; Esp32Device device_service; #endif // defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE +#if defined(PW_RPC_EVENT_SERVICE) && PW_RPC_EVENT_SERVICE +Event event_service; +#endif // defined(PW_RPC_EVENT_SERVICE) && PW_RPC_EVENT_SERVICE + #if defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE Lighting lighting_service; #endif // defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE @@ -313,6 +325,10 @@ Esp32WiFi wifi_service; void RegisterServices(pw::rpc::Server & server) { +#if defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + server.RegisterService(actions_service); +#endif // defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + #if defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE server.RegisterService(attributes_service); #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE @@ -333,6 +349,10 @@ void RegisterServices(pw::rpc::Server & server) server.RegisterService(device_service); #endif // defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE +#if defined(PW_RPC_EVENT_SERVICE) && PW_RPC_EVENT_SERVICE + server.RegisterService(event_service); +#endif // defined(PW_RPC_EVENT_SERVICE) && PW_RPC_EVENT_SERVICE + #if defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE server.RegisterService(lighting_service); #endif // defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE @@ -353,6 +373,13 @@ void RegisterServices(pw::rpc::Server & server) } // namespace +#if defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE +void SubscribeActions(RpcActionsSubscribeCallback subscriber) +{ + actions_service.SubscribeActions(subscriber); +} +#endif // defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + void RunRpcService(void *) { Start(RegisterServices, &logger_mutex); diff --git a/examples/platform/esp32/Rpc.h b/examples/platform/esp32/Rpc.h index 9b73e5cafca96b..ff1a5f319ac975 100644 --- a/examples/platform/esp32/Rpc.h +++ b/examples/platform/esp32/Rpc.h @@ -20,6 +20,18 @@ namespace chip { namespace rpc { +enum class ActionType : uint8_t +{ + WRITE_ATTRIBUTE = 0x00, // Write an cluster Attribute + RUN_COMMAND = 0x01, // Run a cluster Command + EMIT_EVENT = 0x02, // Emit a cluster Events +}; + +using RpcActionsSubscribeCallback = bool (*)(EndpointId endpointId, ClusterId clusterId, uint8_t type, uint32_t delayMs, + uint32_t actionId, std::vector args); + +void SubscribeActions(RpcActionsSubscribeCallback subscriber); + void Init(); } // namespace rpc diff --git a/examples/platform/infineon/cyw30739/BUILD.gn b/examples/platform/infineon/cyw30739/BUILD.gn index 0673c55c351de3..8ec7003633cd40 100644 --- a/examples/platform/infineon/cyw30739/BUILD.gn +++ b/examples/platform/infineon/cyw30739/BUILD.gn @@ -20,8 +20,10 @@ import("${cyw30739_sdk_build_root}/cyw30739_sdk.gni") static_library("platform") { sources = [ + "EventManagementTestEventTriggerHandler.h", "LEDWidget.h", "OTAConfig.h", + "SoftwareDiagnostics.h", "main.cpp", ] diff --git a/examples/platform/infineon/cyw30739/EventManagementTestEventTriggerHandler.cpp b/examples/platform/infineon/cyw30739/EventManagementTestEventTriggerHandler.cpp new file mode 100644 index 00000000000000..c5320f17f64bc4 --- /dev/null +++ b/examples/platform/infineon/cyw30739/EventManagementTestEventTriggerHandler.cpp @@ -0,0 +1,47 @@ +/* + * + * 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 "EventManagementTestEventTriggerHandler.h" + +#include "SoftwareDiagnostics.h" + +namespace chip { +namespace DeviceLayer { +namespace Infineon { +namespace CYW30739 { + +CHIP_ERROR EventManagementTestEventTriggerHandler::HandleEventTrigger(uint64_t eventTrigger) +{ + switch (eventTrigger) + { + case kFillUpEventLoggingBuffer: + return HandleFillUpEventLoggingBufferEventTriger(); + default: + return CHIP_ERROR_INVALID_ARGUMENT; + } +} + +void EventManagementTestEventTriggerHandler::TriggerSoftwareFaultEvent(const char * faultRecordString) +{ + OnSoftwareFaultEventHandler(faultRecordString); +} + +} // namespace CYW30739 +} // namespace Infineon +} // namespace DeviceLayer +} // namespace chip diff --git a/examples/platform/infineon/cyw30739/EventManagementTestEventTriggerHandler.h b/examples/platform/infineon/cyw30739/EventManagementTestEventTriggerHandler.h new file mode 100644 index 00000000000000..b93109dae03dfa --- /dev/null +++ b/examples/platform/infineon/cyw30739/EventManagementTestEventTriggerHandler.h @@ -0,0 +1,42 @@ +/* + * + * 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 + +namespace chip { +namespace DeviceLayer { +namespace Infineon { +namespace CYW30739 { + +class EventManagementTestEventTriggerHandler : public app::GenericEventManagementTestEventTriggerHandler +{ +public: + static constexpr uint64_t kFillUpEventLoggingBuffer = 0xffff'ffff'1388'0000; + + CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override; + +private: + virtual void TriggerSoftwareFaultEvent(const char * faultRecordString) override; +}; + +} // namespace CYW30739 +} // namespace Infineon +} // namespace DeviceLayer +} // namespace chip diff --git a/examples/platform/infineon/cyw30739/SoftwareDiagnostics.cpp b/examples/platform/infineon/cyw30739/SoftwareDiagnostics.cpp new file mode 100644 index 00000000000000..ae41f82d14d526 --- /dev/null +++ b/examples/platform/infineon/cyw30739/SoftwareDiagnostics.cpp @@ -0,0 +1,62 @@ +/* + * + * 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 "SoftwareDiagnostics.h" + +#include +#include +#include + +namespace chip { +namespace DeviceLayer { +namespace Infineon { +namespace CYW30739 { + +using namespace chip::app::Clusters; + +void OnSoftwareFaultEventHandler(const char * faultRecordString) +{ +#ifdef MATTER_DM_PLUGIN_SOFTWARE_DIAGNOSTICS_SERVER + SoftwareDiagnostics::Events::SoftwareFault::Type softwareFault; + + /* Unable to access thread ID in the application layer. */ + softwareFault.id = 0; + + if (DeviceLayer::PlatformMgrImpl().IsCurrentTask()) + { + softwareFault.name.SetValue("Matter"_span); + } + else if (DeviceLayer::ThreadStackMgrImpl().IsCurrentTask()) + { + softwareFault.name.SetValue("Thread"_span); + } + else + { + softwareFault.name.SetValue("App"_span); + } + + softwareFault.faultRecording.SetValue(ByteSpan(Uint8::from_const_char(faultRecordString), strlen(faultRecordString))); + + SoftwareDiagnosticsServer::Instance().OnSoftwareFaultDetect(softwareFault); +#endif // MATTER_DM_PLUGIN_SOFTWARE_DIAGNOSTICS_SERVER +} + +} // namespace CYW30739 +} // namespace Infineon +} // namespace DeviceLayer +} // namespace chip diff --git a/examples/platform/infineon/cyw30739/SoftwareDiagnostics.h b/examples/platform/infineon/cyw30739/SoftwareDiagnostics.h new file mode 100644 index 00000000000000..b076d6e1bfe44c --- /dev/null +++ b/examples/platform/infineon/cyw30739/SoftwareDiagnostics.h @@ -0,0 +1,29 @@ +/* + * + * 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. + */ + +namespace chip { +namespace DeviceLayer { +namespace Infineon { +namespace CYW30739 { + +void OnSoftwareFaultEventHandler(const char * faultRecordString); + +} // namespace CYW30739 +} // namespace Infineon +} // namespace DeviceLayer +} // namespace chip diff --git a/examples/platform/infineon/cyw30739/cyw30739_example.gni b/examples/platform/infineon/cyw30739/cyw30739_example.gni index df8e50ee68bf2f..ff07ff6eb28633 100644 --- a/examples/platform/infineon/cyw30739/cyw30739_example.gni +++ b/examples/platform/infineon/cyw30739/cyw30739_example.gni @@ -24,8 +24,10 @@ template("cyw30739_example") { static_library(target_name) { sources = [ + "${cyw30739_example_dir}/EventManagementTestEventTriggerHandler.cpp", "${cyw30739_example_dir}/LEDWidget.cpp", "${cyw30739_example_dir}/OTAConfig.cpp", + "${cyw30739_example_dir}/SoftwareDiagnostics.cpp", "${cyw30739_example_dir}/matter_config.cpp", ] diff --git a/examples/platform/infineon/cyw30739/matter_config.cpp b/examples/platform/infineon/cyw30739/matter_config.cpp index 6b0b56730edecd..c30d55e55bc623 100644 --- a/examples/platform/infineon/cyw30739/matter_config.cpp +++ b/examples/platform/infineon/cyw30739/matter_config.cpp @@ -19,6 +19,7 @@ #include "matter_config.h" #include "AppTask.h" +#include "EventManagementTestEventTriggerHandler.h" #ifdef BOARD_ENABLE_DISPLAY #include "GUI.h" #endif @@ -44,14 +45,21 @@ #include #include #include -#include #include #include -#ifdef BOARD_ENABLE_OPTIGA -#include "wiced_optiga.h" -#endif #include +#ifdef BOARD_USE_OPTIGA +#include "wiced_optiga.h" +#ifdef USE_PROVISIONED_OPTIGA +#include +#else /* !USE_PROVISIONED_OPTIGA */ +#include +#endif /* USE_PROVISIONED_OPTIGA */ +#else /* !BOARD_USE_OPTIGA */ +#include +#endif /* BOARD_USE_OPTIGA */ + using namespace ::chip; using namespace ::chip::Inet; using namespace ::chip::Credentials; @@ -60,7 +68,15 @@ using namespace ::chip::Shell; using namespace ::chip::app; static DeviceInfoProviderImpl sExampleDeviceInfoProvider; +#ifdef BOARD_USE_OPTIGA +#ifdef USE_PROVISIONED_OPTIGA +static OptigaFactoryDataProvider sFactoryDataProvider; +#else /* !USE_PROVISIONED_OPTIGA */ +static UnprovisionedOptigaFactoryDataProvider sFactoryDataProvider; +#endif /* USE_PROVISIONED_OPTIGA */ +#else /* !BOARD_USE_OPTIGA */ static FactoryDataProvider sFactoryDataProvider; +#endif /* BOARD_USE_OPTIGA */ // NOTE! This key is for test/certification only and should not be available in production devices! uint8_t sTestEventTriggerEnableKey[chip::TestEventTriggerDelegate::kEnableKeyLength] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, @@ -135,7 +151,7 @@ void CYW30739MatterConfig::InitBoard(void) #ifdef BOARD_ENABLE_DISPLAY GUI_Init(); #endif -#ifdef BOARD_ENABLE_OPTIGA +#ifdef BOARD_USE_OPTIGA wiced_optiga_init(); #endif } @@ -186,13 +202,17 @@ void CYW30739MatterConfig::InitApp(void) LogAppInit(); ConfigurationMgr().LogDeviceConfig(); + sFactoryDataProvider.Init(); + // Print QR Code URL PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE)); /* Start CHIP datamodel server */ static chip::SimpleTestEventTriggerDelegate sTestEventTriggerDelegate{}; static chip::OTATestEventTriggerHandler sOtaTestEventTriggerHandler{}; + static Infineon::CYW30739::EventManagementTestEventTriggerHandler sEventManagementTestEventTriggerHandler{}; VerifyOrDie(sTestEventTriggerDelegate.Init(chip::ByteSpan(sTestEventTriggerEnableKey)) == CHIP_NO_ERROR); VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sOtaTestEventTriggerHandler) == CHIP_NO_ERROR); + VerifyOrDie(sTestEventTriggerDelegate.AddHandler(&sEventManagementTestEventTriggerHandler) == CHIP_NO_ERROR); // Create initParams with SDK example defaults here static chip::CommonCaseDeviceServerInitParams initParams; (void) initParams.InitializeStaticResourcesBeforeServerInit(); diff --git a/examples/platform/linux/Rpc.cpp b/examples/platform/linux/Rpc.cpp index da6b73b9135c13..6e20e2aecf61b9 100644 --- a/examples/platform/linux/Rpc.cpp +++ b/examples/platform/linux/Rpc.cpp @@ -20,8 +20,16 @@ #include "pw_rpc_system_server/rpc_server.h" #include "pw_rpc_system_server/socket.h" +#include #include +#include "Rpc.h" +#include + +#if defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE +#include "pigweed/rpc_services/Actions.h" +#endif // defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + #if defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE #include "pigweed/rpc_services/Attributes.h" #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE @@ -68,6 +76,10 @@ namespace chip { namespace rpc { namespace { +#if defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE +Actions actions_service; +#endif // defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + #if defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE Attributes attributes_service; #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE @@ -94,6 +106,10 @@ pw::trace::TraceService trace_service(pw::trace::GetTokenizedTracer()); void RegisterServices(pw::rpc::Server & server) { +#if defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + server.RegisterService(actions_service); +#endif // defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + #if defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE server.RegisterService(attributes_service); #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE @@ -122,6 +138,13 @@ void RegisterServices(pw::rpc::Server & server) } // namespace +#if defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE +void SubscribeActions(RpcActionsSubscribeCallback subscriber) +{ + actions_service.SubscribeActions(subscriber); +} +#endif // defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + void RunRpcService() { pw::rpc::system_server::Init(); diff --git a/examples/platform/linux/Rpc.h b/examples/platform/linux/Rpc.h index fbcfc6ed63f4f2..51f3f2e473bacb 100644 --- a/examples/platform/linux/Rpc.h +++ b/examples/platform/linux/Rpc.h @@ -18,9 +18,24 @@ #pragma once +#include +#include + namespace chip { namespace rpc { +enum class ActionType : uint8_t +{ + WRITE_ATTRIBUTE = 0x00, // Write an cluster Attribute + RUN_COMMAND = 0x01, // Run a cluster Command + EMIT_EVENT = 0x02, // Emit a cluster Events +}; + +using RpcActionsSubscribeCallback = bool (*)(EndpointId endpointId, ClusterId clusterId, uint8_t type, uint32_t delayMs, + uint32_t actionId, std::vector args); + +void SubscribeActions(RpcActionsSubscribeCallback subscriber); + int Init(uint16_t rpcServerPort); } // namespace rpc diff --git a/examples/platform/nrfconnect/Rpc.cpp b/examples/platform/nrfconnect/Rpc.cpp index 8b47215d6a0680..510b0ac8ac4c64 100644 --- a/examples/platform/nrfconnect/Rpc.cpp +++ b/examples/platform/nrfconnect/Rpc.cpp @@ -28,6 +28,10 @@ LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); +#if defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE +#include "pigweed/rpc_services/Actions.h" +#endif // defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + #if defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE #include "pigweed/rpc_services/Attributes.h" #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE @@ -48,6 +52,10 @@ LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); #include "pigweed/rpc_services/Device.h" #endif // defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE +#if defined(PW_RPC_EVENT_SERVICE) && PW_RPC_EVENT_SERVICE +#include "pigweed/rpc_services/Event.h" +#endif // defined(PW_RPC_EVENT_SERVICE) && PW_RPC_EVENT_SERVICE + #if defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE #include "pigweed/rpc_services/Lighting.h" #endif // defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE @@ -85,6 +93,10 @@ size_t pw_trace_GetTraceTimeTicksPerSecond() namespace chip { namespace rpc { +#if defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE +Actions actions_service; +#endif // defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + #if defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE namespace { @@ -159,6 +171,10 @@ Descriptor descriptor_service; NrfDevice device_service; #endif // defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE +#if defined(PW_RPC_EVENT_SERVICE) && PW_RPC_EVENT_SERVICE +Event event_service; +#endif // defined(PW_RPC_EVENT_SERVICE) && PW_RPC_EVENT_SERVICE + #if defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE Lighting lighting_service; #endif // defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE @@ -181,6 +197,10 @@ pw::trace::TraceService trace_service(pw::trace::GetTokenizedTracer()); void RegisterServices(pw::rpc::Server & server) { +#if defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + server.RegisterService(actions_service); +#endif // defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + #if defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE server.RegisterService(attributes_service); #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE @@ -201,6 +221,10 @@ void RegisterServices(pw::rpc::Server & server) server.RegisterService(device_service); #endif // defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE +#if defined(PW_RPC_EVENT_SERVICE) && PW_RPC_EVENT_SERVICE + server.RegisterService(event_service); +#endif // defined(PW_RPC_EVENT_SERVICE) && PW_RPC_EVENT_SERVICE + #if defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE server.RegisterService(lighting_service); #endif // defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE @@ -225,6 +249,13 @@ void RegisterServices(pw::rpc::Server & server) } // namespace +#if defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE +void SubscribeActions(RpcActionsSubscribeCallback subscriber) +{ + actions_service.SubscribeActions(subscriber); +} +#endif // defined(PW_RPC_ACTIONS_SERVICE) && PW_RPC_ACTIONS_SERVICE + void RunRpcService(void *, void *, void *) { Start(RegisterServices, &logger_mutex); diff --git a/examples/platform/nrfconnect/Rpc.h b/examples/platform/nrfconnect/Rpc.h index f0aeaf45b61fba..40efcb0750ca2b 100644 --- a/examples/platform/nrfconnect/Rpc.h +++ b/examples/platform/nrfconnect/Rpc.h @@ -23,6 +23,18 @@ namespace chip { namespace rpc { +enum class ActionType : uint8_t +{ + WRITE_ATTRIBUTE = 0x00, // Write an cluster Attribute + RUN_COMMAND = 0x01, // Run a cluster Command + EMIT_EVENT = 0x02, // Emit a cluster Events +}; + +using RpcActionsSubscribeCallback = bool (*)(EndpointId endpointId, ClusterId clusterId, uint8_t type, uint32_t delayMs, + uint32_t actionId, std::vector args); + +void SubscribeActions(RpcActionsSubscribeCallback subscriber); + class NrfButton; void RunRpcService(void *, void *, void *); diff --git a/examples/platform/qpg/app/main.cpp b/examples/platform/qpg/app/main.cpp index d7ba22f4adb232..647d248d14b95f 100644 --- a/examples/platform/qpg/app/main.cpp +++ b/examples/platform/qpg/app/main.cpp @@ -192,7 +192,7 @@ CHIP_ERROR CHIP_Init(void) qvIO_EnableSleep(true); #elif CHIP_DEVICE_CONFIG_THREAD_FTD ret = ConnectivityMgr().SetThreadDeviceType(ConnectivityManager::kThreadDeviceType_Router); - qvIO_EnableSleep(true); + qvIO_EnableSleep(false); #else ret = ConnectivityMgr().SetThreadDeviceType(ConnectivityManager::kThreadDeviceType_MinimalEndDevice); qvIO_EnableSleep(false); diff --git a/examples/platform/qpg/powercycle_counting.c b/examples/platform/qpg/powercycle_counting.c index d9e1165a28ef7b..83177e7ca779d9 100644 --- a/examples/platform/qpg/powercycle_counting.c +++ b/examples/platform/qpg/powercycle_counting.c @@ -118,7 +118,8 @@ UInt8 gpAppFramework_Reset_GetResetCount(void) void gpAppFramework_Reset_Init(void) { - if (gpReset_GetResetReason() == gpReset_ResetReason_HW_Por) + if ((gpReset_GetResetReason() == gpReset_ResetReason_HW_Por) || + (gpReset_GetResetReason() == gpReset_ResetReason_UnSpecified)) // Use this reset reason for JLink resets { gpAppFramework_HardwareResetTriggered(); } diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/app_conf.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/app_conf.h index f439fd72f7393f..74903fa4d4d6dd 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/app_conf.h +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/app_conf.h @@ -87,109 +87,10 @@ extern "C" { #define RX_1M 0x01 #define RX_2M 0x02 -/* freertos defines */ -#define CFG_SHCI_USER_EVT_PROCESS_NAME "SHCI_USER_EVT_PROCESS" -#define CFG_SHCI_USER_EVT_PROCESS_ATTR_BITS (0) -#define CFG_SHCI_USER_EVT_PROCESS_CB_MEM (0) -#define CFG_SHCI_USER_EVT_PROCESS_CB_SIZE (0) -#define CFG_SHCI_USER_EVT_PROCESS_STACK_MEM (0) -#define CFG_SHCI_USER_EVT_PROCESS_PRIORITY osPriorityNormal -#define CFG_SHCI_USER_EVT_PROCESS_STACK_SIZE (128 * 20) - -#define CFG_PUSH_BUTTON_EVT_PROCESS_NAME "PUSH_BUTTON_EVT_PROCESS" -#define CFG_PUSH_BUTTON_EVT_PROCESS_ATTR_BITS (0) -#define CFG_PUSH_BUTTON_EVT_PROCESS_CB_MEM (0) -#define CFG_PUSH_BUTTON_EVT_PROCESS_CB_SIZE (0) -#define CFG_PUSH_BUTTON_EVT_PROCESS_STACK_MEM (0) -#define CFG_PUSH_BUTTON_EVT_PROCESS_PRIORITY osPriorityNormal -#define CFG_PUSH_BUTTON_EVT_PROCESS_STACK_SIZE (128 * 4) - -#define CFG_SEND_COAP_NAME "SEND_COAP_EVT_PROCESS" - -#define CFG_SWITCH_PROTOCOL_EVT_PROCESS_NAME "SWITCH_PROTCOL_EVT_PROCESS" -#define CFG_SWITCH_PROTOCOL_EVT_PROCESS_ATTR_BITS (0) -#define CFG_SWITCH_PROTOCOL_EVT_PROCESS_CB_MEM (0) -#define CFG_SWITCH_PROTOCOL_EVT_PROCESS_CB_SIZE (0) -#define CFG_SWITCH_PROTOCOL_EVT_PROCESS_STACK_MEM (0) -#define CFG_SWITCH_PROTOCOL_EVT_PROCESS_PRIORITY osPriorityNormal -#define CFG_SWITCH_PROTOCOL_EVT_PROCESS_STACK_SIZE (128 * 8) - -#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_NAME "THREAD_MSG_M0_TO_M4_PROCESS" -#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_ATTR_BITS (0) -#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_CB_MEM (0) -#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_CB_SIZE (0) -#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_STACK_MEM (0) -#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_PRIORITY osPriorityNormal -#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_STACK_SIZE (128 * 8) - -#define CFG_THREAD_CLI_PROCESS_NAME "THREAD_CLI_PROCESS" -#define CFG_THREAD_CLI_PROCESS_ATTR_BITS (0) -#define CFG_THREAD_CLI_PROCESS_CB_MEM (0) -#define CFG_THREAD_CLI_PROCESS_CB_SIZE (0) -#define CFG_THREAD_CLI_PROCESS_STACK_MEM (0) -#define CFG_THREAD_CLI_PROCESS_PRIORITY osPriorityNormal -#define CFG_THREAD_CLI_PROCESS_STACK_SIZE (128 * 8) - -#define CFG_THREAD_SEND_COAP_MSG_PROCESS_NAME "THREAD_SEND_COAP_MSG_PROCESS" -#define CFG_THREAD_SEND_COAP_MSG_PROCESS_ATTR_BITS (0) -#define CFG_THREAD_SEND_COAP_MSG_PROCESS_CB_MEM (0) -#define CFG_THREAD_SEND_COAP_MSG_PROCESS_CB_SIZE (0) -#define CFG_THREAD_SEND_COAP_MSG_PROCESS_STACK_MEM (0) -#define CFG_THREAD_SEND_COAP_MSG_PROCESS_PRIORITY osPriorityNormal -#define CFG_THREAD_SEND_COAP_MSG_PROCESS_STACk_SIZE (128 * 8) - -#define CFG_THREAD_SET_SED_MODE_PROCESS_NAME "THREAD_SET_SED_MODE_PROCESS" -#define CFG_THREAD_SET_SED_MODE_PROCESS_ATTR_BITS (0) -#define CFG_THREAD_SET_SED_MODE_PROCESS_CB_MEM (0) -#define CFG_THREAD_SET_SED_MODE_PROCESS_CB_SIZE (0) -#define CFG_THREAD_SET_SED_MODE_PROCESS_STACK_MEM (0) -#define CFG_THREAD_SET_SED_MODE_PROCESS_PRIORITY osPriorityNormal -#define CFG_THREAD_SET_SED_MODE_PROCESS_STACk_SIZE (128 * 8) - -#define CFG_HCI_USER_EVT_PROCESS_NAME "HCI_USER_EVT_PROCESS" -#define CFG_HCI_USER_EVT_PROCESS_ATTR_BITS (0) -#define CFG_HCI_USER_EVT_PROCESS_CB_MEM (0) -#define CFG_HCI_USER_EVT_PROCESS_CB_SIZE (0) -#define CFG_HCI_USER_EVT_PROCESS_STACK_MEM (0) -#define CFG_HCI_USER_EVT_PROCESS_PRIORITY osPriorityNormal -#define CFG_HCI_USER_EVT_PROCESS_STACK_SIZE (128 * 40) - -#define CFG_ADV_UPDATE_PROCESS_NAME "ADV_UPDATE_PROCESS" -#define CFG_ADV_UPDATE_PROCESS_ATTR_BITS (0) -#define CFG_ADV_UPDATE_PROCESS_CB_MEM (0) -#define CFG_ADV_UPDATE_PROCESS_CB_SIZE (0) -#define CFG_ADV_UPDATE_PROCESS_STACK_MEM (0) -#define CFG_ADV_UPDATE_PROCESS_PRIORITY osPriorityNormal -#define CFG_ADV_UPDATE_PROCESS_STACK_SIZE (128 * 20) - -#define CFG_P2P_SERVER_PROCESS_NAME "P2P_SERVER_PROCESS" -#define CFG_P2P_SERVER_PROCESS_ATTR_BITS (0) -#define CFG_P2P_SERVER_PROCESS_CB_MEM (0) -#define CFG_P2P_SERVER_PROCESS_CB_SIZE (0) -#define CFG_P2P_SERVER_PROCESS_STACK_MEM (0) -#define CFG_P2P_SERVER_PROCESS_PRIORITY osPriorityNormal -#define CFG_P2P_SERVER_PROCESS_STACK_SIZE (128 * 20) - -#define LED_PROCESS_NAME "LED_CUBE_PROCESS" -#define LED_PROCESS_ATTR_BITS (0) -#define LED_PROCESS_CB_MEM (0) -#define LED_PROCESS_CB_SIZE (0) -#define LED_PROCESS_STACK_MEM (0) -#define LED_PROCESS_PRIORITY osPriorityNormal -#define LED_PROCESS_STACK_SIZE (128 * 10) - -#define APPTASK_NAME "APPTASK" -#define APP_ATTR_BITS (0) -#define APP_CB_MEM (0) -#define APP_CB_SIZE (0) -#define APP_STACK_MEM (0) -#define APP_PRIORITY osPriorityNormal -#define APP_STACK_SIZE (1024 * 6) - /** * Identity root key used to derive LTK and CSRK */ -#define CFG_BLE_IRK \ +#define CFG_BLE_IR \ { \ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 \ } @@ -197,7 +98,7 @@ extern "C" { /** * Encryption root key used to derive LTK and CSRK */ -#define CFG_BLE_ERK \ +#define CFG_BLE_ER \ { \ 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21 \ } @@ -236,11 +137,11 @@ extern "C" { #define CONN_P(x) ((int) ((x) / 1.25f)) /* L2CAP Connection Update request parameters used for test only with smart Phone */ -#define L2CAP_REQUEST_NEW_CONN_PARAM 1 +#define L2CAP_REQUEST_NEW_CONN_PARAM 0 #define L2CAP_INTERVAL_MIN CONN_P(1000) /* 1s */ #define L2CAP_INTERVAL_MAX CONN_P(1000) /* 1s */ -#define L2CAP_SLAVE_LATENCY 0x0000 +#define L2CAP_PERIPHERAL_LATENCY 0x0000 #define L2CAP_TIMEOUT_MULTIPLIER 0x1F4 /****************************************************************************** @@ -301,12 +202,12 @@ extern "C" { #define CFG_BLE_DATA_LENGTH_EXTENSION 1 /** - * Sleep clock accuracy in Slave mode (ppm value) + * Sleep clock accuracy in Peripheral mode (ppm value) */ -#define CFG_BLE_SLAVE_SCA 500 +#define CFG_BLE_PERIPHERAL_SCA 500 /** - * Sleep clock accuracy in Master mode + * Sleep clock accuracy in Central mode * 0 : 251 ppm to 500 ppm * 1 : 151 ppm to 250 ppm * 2 : 101 ppm to 150 ppm @@ -316,14 +217,14 @@ extern "C" { * 6 : 21 ppm to 30 ppm * 7 : 0 ppm to 20 ppm */ -#define CFG_BLE_MASTER_SCA 0 +#define CFG_BLE_CENTRAL_SCA 0 /** * Source for the 32 kHz slow speed clock * 1 : internal RO * 0 : external crystal ( no calibration ) */ -#define CFG_BLE_LSE_SOURCE 0 +#define CFG_BLE_LS_SOURCE 0 /** * Start up time of the high speed (16 or 32 MHz) crystal oscillator in units of 625/256 us (~2.44 us) @@ -331,7 +232,7 @@ extern "C" { #define CFG_BLE_HSE_STARTUP_TIME 0x148 /** - * Maximum duration of the connection event when the device is in Slave mode in units of 625/256 us (~2.44 us) + * Maximum duration of the connection event when the device is in Peripheral mode in units of 625/256 us (~2.44 us) */ #define CFG_BLE_MAX_CONN_EVENT_LENGTH (0xFFFFFFFF) @@ -512,6 +413,7 @@ extern "C" { /** tick timer value in us */ #define CFG_TS_TICK_VAL DIVR((CFG_RTCCLK_DIV * 1000000), LSE_VALUE) +#define CFG_TS_TICK_VAL_PS DIVR(((uint64_t) CFG_RTCCLK_DIV * 1e12), (uint64_t) LSE_VALUE) typedef enum { @@ -631,6 +533,16 @@ typedef enum #endif /* CFG_FULL_LOW_POWER */ /* USER CODE END Defines */ +#if (L2CAP_REQUEST_NEW_CONN_PARAM != 0) +#define CFG_CONN_INT_UPD_REQ_PROCESS_NAME "CONN_INT_UPD_REQ" +#define CFG_CONN_INT_UPD_REQ_PROCESS_ATTR_BITS (0) +#define CFG_CONN_INT_UPD_REQ_PROCESS_CB_MEM (0) +#define CFG_CONN_INT_UPD_REQ_PROCESS_CB_SIZE (0) +#define CFG_CONN_INT_UPD_REQ_PROCESS_STACK_MEM (0) +#define CFG_CONN_INT_UPD_REQ_PROCESS_PRIORITY osPriorityNormal +#define CFG_CONN_INT_UPD_REQ_PROCESS_STACK_SIZE (128 * 8) +#endif + /****************************************************************************** * LOW POWER ******************************************************************************/ @@ -648,12 +560,124 @@ typedef enum /* USER CODE END CFG_LPM_Id_t */ } CFG_LPM_Id_t; +/****************************************************************************** + * FreeRTOS + ******************************************************************************/ +/* USER CODE BEGIN FreeRTOS */ + +#define CFG_SHCI_USER_EVT_PROCESS_NAME "SHCI_USER_EVT_PROCESS" +#define CFG_SHCI_USER_EVT_PROCESS_ATTR_BITS (0) +#define CFG_SHCI_USER_EVT_PROCESS_CB_MEM (0) +#define CFG_SHCI_USER_EVT_PROCESS_CB_SIZE (0) +#define CFG_SHCI_USER_EVT_PROCESS_STACK_MEM (0) +#define CFG_SHCI_USER_EVT_PROCESS_PRIORITY osPriorityNone +#define CFG_SHCI_USER_EVT_PROCESS_STACK_SIZE (128 * 20) + +#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_NAME "THREAD_MSG_M0_TO_M4_PROCESS" +#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_ATTR_BITS (0) +#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_CB_MEM (0) +#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_CB_SIZE (0) +#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_STACK_MEM (0) +#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_PRIORITY osPriorityLow +#define CFG_THREAD_MSG_M0_TO_M4_PROCESS_STACK_SIZE (128 * 8) + +#define CFG_THREAD_CLI_PROCESS_NAME "THREAD_CLI_PROCESS" +#define CFG_THREAD_CLI_PROCESS_ATTR_BITS (0) +#define CFG_THREAD_CLI_PROCESS_CB_MEM (0) +#define CFG_THREAD_CLI_PROCESS_CB_SIZE (0) +#define CFG_THREAD_CLI_PROCESS_STACK_MEM (0) +#define CFG_THREAD_CLI_PROCESS_PRIORITY osPriorityNormal +#define CFG_THREAD_CLI_PROCESS_STACK_SIZE (128 * 8) + +#define CFG_HCI_USER_EVT_PROCESS_NAME "HCI_USER_EVT_PROCESS" +#define CFG_HCI_USER_EVT_PROCESS_ATTR_BITS (0) +#define CFG_HCI_USER_EVT_PROCESS_CB_MEM (0) +#define CFG_HCI_USER_EVT_PROCESS_CB_SIZE (0) +#define CFG_HCI_USER_EVT_PROCESS_STACK_MEM (0) +#define CFG_HCI_USER_EVT_PROCESS_PRIORITY osPriorityNone +#define CFG_HCI_USER_EVT_PROCESS_STACK_SIZE (128 * 40) + +#define CFG_ADV_CANCEL_PROCESS_NAME "ADV_CANCEL_PROCESS" +#define CFG_ADV_CANCEL_PROCESS_ATTR_BITS (0) +#define CFG_ADV_CANCEL_PROCESS_CB_MEM (0) +#define CFG_ADV_CANCEL_PROCESS_CB_SIZE (0) +#define CFG_ADV_CANCEL_PROCESS_STACK_MEM (0) +#define CFG_ADV_CANCEL_PROCESS_PRIORITY osPriorityNormal +#define CFG_ADV_CANCEL_PROCESS_STACK_SIZE (128 * 8) + +#define CFG_P2PS_SEND_NOTIF_PROCESS_NAME "P2PS_SEND_NOTIF_PROCESS" +#define CFG_P2PS_SEND_NOTIF_PROCESS_ATTR_BITS (0) +#define CFG_P2PS_SEND_NOTIF_PROCESS_CB_MEM (0) +#define CFG_P2PS_SEND_NOTIF_PROCESS_CB_SIZE (0) +#define CFG_P2PS_SEND_NOTIF_PROCESS_STACK_MEM (0) +#define CFG_P2PS_SEND_NOTIF_PROCESS_PRIORITY osPriorityNormal +#define CFG_P2PS_SEND_NOTIF_PROCESS_STACK_SIZE (128 * 8) + +#if (L2CAP_REQUEST_NEW_CONN_PARAM != 0) +#define CFG_CONN_INT_UPD_REQ_PROCESS_NAME "CONN_INT_UPD_REQ" +#define CFG_CONN_INT_UPD_REQ_PROCESS_ATTR_BITS (0) +#define CFG_CONN_INT_UPD_REQ_PROCESS_CB_MEM (0) +#define CFG_CONN_INT_UPD_REQ_PROCESS_CB_SIZE (0) +#define CFG_CONN_INT_UPD_REQ_PROCESS_STACK_MEM (0) +#define CFG_CONN_INT_UPD_REQ_PROCESS_PRIORITY osPriorityNormal +#define CFG_CONN_INT_UPD_REQ_PROCESS_STACK_SIZE (128 * 8) +#endif + +#define APPTASK_NAME "APPTASK" +#define APP_ATTR_BITS (0) +#define APP_CB_MEM (0) +#define APP_CB_SIZE (0) +#define APP_STACK_MEM (0) +#define APP_PRIORITY osPriorityNormal +#define APP_STACK_SIZE (1024 * 6) + +#define CFG_PUSH_BUTTON_EVT_PROCESS_NAME "PUSH_BUTTON_EVT_PROCESS" +#define CFG_PUSH_BUTTON_EVT_PROCESS_ATTR_BITS (0) +#define CFG_PUSH_BUTTON_EVT_PROCESS_CB_MEM (0) +#define CFG_PUSH_BUTTON_EVT_PROCESS_CB_SIZE (0) +#define CFG_PUSH_BUTTON_EVT_PROCESS_STACK_MEM (0) +#define CFG_PUSH_BUTTON_EVT_PROCESS_PRIORITY osPriorityNormal +#define CFG_PUSH_BUTTON_EVT_PROCESS_STACK_SIZE (128 * 4) +/* USER CODE END FreeRTOS_Defines */ + /****************************************************************************** * OTP manager ******************************************************************************/ #define CFG_OTP_BASE_ADDRESS OTP_AREA_BASE -#define CFG_OTP_END_ADDRESS OTP_AREA_END_ADDR +#define CFG_OTP_END_ADRESS OTP_AREA_END_ADDR + +/****************************************************************************** + * OTA support + ******************************************************************************/ +#define OTA_SUPPORT 0 + +/****************************************************************************** + * LCD support + ******************************************************************************/ +#define CFG_LCD_SUPPORTED 1 + +#if ((OTA_SUPPORT == 1) || (CFG_FULL_LOW_POWER == 1)) +/****************************************************************************** + * LCD support + ******************************************************************************/ +#undef CFG_LCD_SUPPORTED +#define CFG_LCD_SUPPORTED 0 +#endif + +/****************************************************************************** + * versions + ******************************************************************************/ +#define X_CUBE_MATTER_VERSION "1.0.3" +#define PRODUCT_NAME "Dimmable Light" +#define VENDOR_NAME "STMicroelectronics" +#define HARDWARE_VERSION "STM32WB5MM-DK" +#define MATTER_SDK_VERSION "github CSA" + +/****************************************************************************** + * Matter Factory data + ******************************************************************************/ +#define CONFIG_STM32_FACTORY_DATA_ENABLE 0 typedef enum { diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/app_debug.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/app_debug.h new file mode 100644 index 00000000000000..6ea86e05ffa9f8 --- /dev/null +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/app_debug.h @@ -0,0 +1,67 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file app_debug.h + * @author MCD Application Team + * @brief Header for app_debug.c module + ****************************************************************************** + * @attention + * + * Copyright (c) 2020-2021 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef APP_DEBUG_H +#define APP_DEBUG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ + +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Exported types ------------------------------------------------------------*/ +/* USER CODE BEGIN ET */ + +/* USER CODE END ET */ + +/* Exported constants --------------------------------------------------------*/ +/* USER CODE BEGIN EC */ + +/* USER CODE END EC */ + +/* Exported variables --------------------------------------------------------*/ +/* USER CODE BEGIN EV */ + +/* USER CODE END EV */ + +/* Exported macros ------------------------------------------------------------*/ +/* USER CODE BEGIN EM */ + +/* USER CODE END EM */ + +/* Exported functions ---------------------------------------------*/ +void APPD_Init(void); +void APPD_EnableCPU2(void); +/* USER CODE BEGIN EF */ + +/* USER CODE END EF */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*APP_DEBUG_H */ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/app_entry.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/app_entry.h index 9d0378c18792c1..ff90bc72025585 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/app_entry.h +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/app_entry.h @@ -48,7 +48,6 @@ void APP_ENTRY_ProcessMsgM0ToM4(void); void APP_ENTRY_Init_CFG_CLI_UART(void); void APP_ENTRY_TL_THREAD_INIT(void); void APP_ENTRY_PBSetReceiveCallback(PushButtonCallback aCallback); -void APP_ENTRY_LedBlink(uint8_t LedStatus); #ifdef __cplusplus } diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/flash_driver.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/flash_driver.h deleted file mode 100644 index 24f4cfc6253572..00000000000000 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/flash_driver.h +++ /dev/null @@ -1,181 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file flash_driver.h - * @author MCD Application Team - * @brief Dual core Flash driver interface - ****************************************************************************** - * @attention - * - * Copyright (c) 2020-2021 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef FLASH_DRIVER_H -#define FLASH_DRIVER_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include -/* Private includes ----------------------------------------------------------*/ -/* USER CODE BEGIN Includes */ - -/* USER CODE END Includes */ - -/* Exported types ------------------------------------------------------------*/ -typedef enum -{ - SINGLE_FLASH_OPERATION_DONE, - SINGLE_FLASH_OPERATION_NOT_EXECUTED, -} SingleFlashOperationStatus_t; - -typedef enum -{ - WAITED_SEM_BUSY, - WAITED_SEM_FREE, -} WaitedSemStatus_t; - -typedef enum -{ - WAIT_FOR_SEM_BLOCK_FLASH_REQ_BY_CPU1, - WAIT_FOR_SEM_BLOCK_FLASH_REQ_BY_CPU2, -} WaitedSemId_t; - -typedef enum -{ - ReadyToWrite, - NotReadyToWrite, - -} StatusReadyToWrite; -/* Exported functions ------------------------------------------------------- */ - -/** - * @brief Implements the Dual core algorithm to erase multiple sectors in flash with CPU1 - * It calls for each sector to be erased the API FD_EraseSingleSector() - * - * @param FirstSector: The first sector to be erased - * This parameter must be a value between 0 and (SFSA - 1) - * @param NbrOfSectors: The number of sectors to erase - * This parameter must be a value between 1 and (SFSA - FirstSector) - * @retval Number of sectors not erased: - * Depending on the implementation of FD_WaitForSemAvailable(), - * it may still have some sectors not erased when the timing protection has been - * enabled by either CPU1 or CPU2. When the value returned is not 0, the application - * should wait until both timing protection before retrying to erase the last missing sectors. - * - * In addition, When the returned value is not 0: - * - The Sem2 is NOT released - * - The FLASH is NOT locked - * - SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF) is NOT called - * It is expected that the user will call one more time this function to finish the process - */ -uint32_t FD_EraseSectors(uint32_t FirstSector, uint32_t NbrOfSectors); - -/** - * @brief Implements the Dual core algorithm to write multiple 64bits data in flash with CPU1 - * The user shall first make sure the location to be written has been first erase. - * Otherwise, the API will loop for ever as it will be not able to write in flash - * The only value that can be written even though the destination is not erased is 0. - * It calls for each 64bits to be written the API FD_WriteSingleData() - * - * @param DestAddress: Address of the flash to write the first data. It shall be 64bits aligned - * @param pSrcBuffer: Address of the buffer holding the 64bits data to be written in flash - * @param NbrOfData: Number of 64bits data to be written - * @retval Number of 64bits data not written: - * Depending on the implementation of FD_WaitForSemAvailable(), - * it may still have 64bits data not written when the timing protection has been - * enabled by either CPU1 or CPU2. When the value returned is not 0, the application - * should wait until both timing protection before retrying to write the last missing 64bits data. - * - * In addition, When the returned value is not 0: - * - The Sem2 is NOT released - * - The FLASH is NOT locked - * It is expected that the user will call one more time this function to finish the process - */ -uint32_t FD_WriteData(uint32_t DestAddress, uint64_t * pSrcBuffer, uint32_t NbrOfData); - -/** - * @brief Implements the Dual core algorithm to erase one sector in flash with CPU1 - * - * It expects the following point before calling this API: - * - The Sem2 is taken - * - The FLASH is unlocked - * - SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON) has been called - * It expects the following point to be done when no more sectors need to be erased - * - The Sem2 is released - * - The FLASH is locked - * - SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF) is called - * - * The two point above are implemented in FD_EraseSectors() - * This API needs to be used instead of FD_EraseSectors() in case a provided library is taking - * care of these two points and request only a single operation. - * - * @param FirstSector: The sector to be erased - * This parameter must be a value between 0 and (SFSA - 1) - * @retval: SINGLE_FLASH_OPERATION_DONE -> The data has been written - * SINGLE_FLASH_OPERATION_NOT_EXECUTED -> The data has not been written due to timing protection - * from either CPU1 or CPU2. On a failure status, the user should check - * both timing protection before retrying. - */ -SingleFlashOperationStatus_t FD_EraseSingleSector(uint32_t SectorNumber); - -/** - * @brief Implements the Dual core algorithm to write one 64bits data in flash with CPU1 - * The user shall first make sure the location to be written has been first erase. - * Otherwise, the API will loop for ever as it will be not able to write in flash - * The only value that can be written even though the destination is not erased is 0. - * - * It expects the following point before calling this API: - * - The Sem2 is taken - * - The FLASH is unlocked - * It expects the following point to be done when no more sectors need to be erased - * - The Sem2 is released - * - The FLASH is locked - * - * The two point above are implemented in FD_WriteData() - * This API needs to be used instead of FD_WriteData() in case a provided library is taking - * care of these two points and request only a single operation. - * - * @param DestAddress: Address of the flash to write the data. It shall be 64bits aligned - * @param Data: 64bits Data to be written - * @retval: SINGLE_FLASH_OPERATION_DONE -> The data has been written - * SINGLE_FLASH_OPERATION_NOT_EXECUTED -> The data has not been written due to timing protection - * from either CPU1 or CPU2. On a failure status, the user should check - * both timing protection before retrying. - */ -SingleFlashOperationStatus_t FD_WriteSingleData(uint32_t DestAddress, uint64_t Data); - -/** - * By default, this function is implemented weakly in flash_driver.c to return WAITED_SEM_BUSY. - * When the semaphore is busy, this will result in either FD_WriteSingleData() or FD_EraseSingleSector() - * to loop until the semaphore is free. - * - * This function may be implemented so that when using either an OS or the UTIL_SEQ_WaitEvt() API from the sequencer, - * it could possible to run other tasks or enter idle mode until the waited semaphore is free. - * This function shall not take the waited semaphore but just return when it is free. - * - * @param WaitedSemId: The semaphore ID this function should not return until it is free - * @retval: WAITED_SEM_BUSY -> The function returned before waiting for the semaphore to be free. This will exit the loop - * from either FD_EraseSingleSector() or FD_WriteSingleData() and the number of actions left to - * be processed are reported to the user - * WAITED_SEM_FREE -> The semaphore has been checked as free. Both FD_EraseSingleSector() and FD_WriteSingleData() - * try again to process one more time the flash. - */ -WaitedSemStatus_t FD_WaitForSemAvailable(WaitedSemId_t WaitedSemId); - -#ifdef __cplusplus -} -#endif - -#endif /*FLASH_DRIVER_H */ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/flash_wb.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/flash_wb.h index 5a866b3edf7fbe..010881188f6feb 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/flash_wb.h +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/flash_wb.h @@ -40,6 +40,7 @@ typedef enum NVM_BLOCK_SIZE_OVERFLOW, NVM_ERROR_BLOCK_ALIGN, NVM_FLASH_CORRUPTION, + NVM_PARAM_ERROR, NVM_BUFFER_TOO_SMALL } NVM_StatusTypeDef; @@ -57,19 +58,13 @@ typedef enum * @brief Copy Flash to RAM NVM */ -void NM_Init(void); +NVM_StatusTypeDef NM_Init(void); /** * @brief Copy RAM NVM to Flash */ NVM_StatusTypeDef NM_Dump(void); -/** - * @brief check the nvm if it s corrupted or not - * @retval return NVM_OK if nvm is empty or NVM_FLASH_CORRUPTION if it s not empty - */ -NVM_StatusTypeDef NM_Check_Validity(void); - /** * @brief Get KeyName in RAM NVM and return the value of Key in KeyValue * @@ -86,7 +81,6 @@ NVM_StatusTypeDef NM_GetKeyValue(void * KeyValue, const char * KeyName, uint32_t * * @param KeyValue: Address of the buffer * @param KeyName: Name of Key needed - * @param KeyAddr: TODO DELETED this param * @param KeySize: size of KeyValue * @param read_by_size: return size of KeyValue found * @retval return state of function @@ -103,11 +97,16 @@ NVM_StatusTypeDef NM_SetKeyValue(char * KeyValue, char * KeyName, uint32_t KeySi NVM_StatusTypeDef NM_DeleteKey(const char * Keyname, NVM_Sector sector); /** - * @brief Erase all persistent and reboot program + * @brief Get the address of the OT NVM buffer + * @param Addr: return the Address of the OT buffer + * @retval return state of function */ +NVM_StatusTypeDef NM_GetOtNVMAddr(uint32_t * NVMAddr); +/** + * @brief Erase all persistent and reboot program + */ void NM_ResetFactory(void); -void NM_FullErase(void); #ifdef __cplusplus } diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/hw_conf.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/hw_conf.h index 109084d298637f..4614e33b58a817 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/hw_conf.h +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/hw_conf.h @@ -20,6 +20,7 @@ #ifndef __HW_CONF_H #define __HW_CONF_H +#include "FreeRTOSConfig.h" /****************************************************************************** * Semaphores * THIS SHALL NO BE CHANGED AS THESE SEMAPHORES ARE USED AS WELL ON THE CM0+ @@ -81,7 +82,7 @@ * wakeup timer. * This setting is the preemptpriority part of the NVIC. */ -#define CFG_HW_TS_NVIC_RTC_WAKEUP_IT_PREEMPTPRIO 3 +#define CFG_HW_TS_NVIC_RTC_WAKEUP_IT_PREEMPTPRIO (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1) /* FreeRTOS requirement */ /** * The user may define the priority in the NVIC of the RTC_WKUP interrupt handler that is used to manage the diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/main.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/main.h index 6ea2f076e6a8ae..ca1b5ffa4bea36 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/main.h +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/main.h @@ -35,7 +35,7 @@ #include "x_nucleo_epd.h" #endif #ifdef USE_STM32WB5M_DK -#include "./../../../../../Drivers/BSP/STM32WB5MM-DK/stm32wb5mm_dk.h" +#include "stm32wb5mm_dk.h" #endif diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/ota.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/ota.h new file mode 100644 index 00000000000000..dbce1040104dd4 --- /dev/null +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/ota.h @@ -0,0 +1,50 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/* USER CODE END Header */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef OTA_H +#define OTA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/*! Attribute structure */ +typedef struct +{ + uint16_t vendorId; /*! VendorId info from image header */ + uint16_t productId; /*! ProductId info from image header */ + uint32_t softwareVersion; /*! Software version of the binary */ + uint32_t minApplicableVersion; /*! Minimum running software version to be compatible with the OTA image */ + uint32_t maxApplicableVersion; /*! Maximum running software version to be compatible with the OTA image */ +} Ota_ImageHeader_t; + +void InitializeOTARequestor(void); +bool OtaHeaderValidation(Ota_ImageHeader_t imageHeader); +void TriggerOTAQuery(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* OTA_H */ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/s25fl128s_conf.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/s25fl128s_conf.h new file mode 100644 index 00000000000000..009853e8989420 --- /dev/null +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/s25fl128s_conf.h @@ -0,0 +1,77 @@ +/** + ****************************************************************************** + * @file s25fl128s_conf_template.h + * @author MCD Application Team + * @brief This file contains the configurations of the S25FL128S QSPI memory. + ****************************************************************************** + * @attention + * + * Copyright (c) 2021 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef S25FL128S_CONF_H +#define S25FL128S_CONF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32wbxx_hal.h" + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup Components + * @{ + */ + +/** @addtogroup S25FL128S + * @brief This file provides a set of definitions for the Spansion + * S25FL128S memory configuration. + * @{ + */ + +/** @addtogroup S25FL128S_Exported_Constants + * @{ + */ + +#define CONF_S25FL128S_READ_ENHANCE 0 /* MMP performance enhance read enable/disable */ +#define CONF_QSPI_DUMMY_CLOCK 8U + +/* Dummy cycles for STR read mode */ +#define S25FL128S_DUMMY_CYCLES_READ_QUAD 8U +#define S25FL128S_DUMMY_CYCLES_READ 8U +#define S25FL128S_DUMMY_CYCLES_READ_DUAL_INOUT 4U +#define S25FL128S_DUMMY_CYCLES_READ_QUAD_INOUT 6U + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* S25FL128S_CONF_H */ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm32_factorydata.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm32_factorydata.h new file mode 100644 index 00000000000000..ddf384fa03e2e3 --- /dev/null +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm32_factorydata.h @@ -0,0 +1,78 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file flash_wb.h + * @author MCD Application Team + * @brief Header file for flash_wb.c + ****************************************************************************** + * @attention + * + * Copyright (c) 2023 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM32_FACTORYDATA_H +#define STM32_FACTORYDATA_H + +/* Includes ------------------------------------------------------------------*/ +#include "utilities_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PRIVATE_KEY_LEN 32 +#define PUBLIC_KEY_LEN 65 +typedef enum +{ + DATAFACTORY_OK, + DATAFACTORY_DATA_NOT_FOUND, + DATAFACTORY_BUFFER_TOO_SMALL, + DATAFACTORY_PARAM_ERROR, + +} FACTORYDATA_StatusTypeDef; + +typedef enum +{ + /* DeviceAttestationCredentialsProvider */ + TAG_ID_CERTIFICATION_DECLARATION = 1, + TAG_ID_FIRMWARE_INFORMATION = 2, + TAG_ID_DEVICE_ATTESTATION_CERTIFICATE = 3, + TAG_ID_PRODUCT_ATTESTATION_INTERMEDIATE_CERTIFICATE = 4, + TAG_ID_DEVICE_ATTESTATION_PRIVATE_KEY = 5, + TAG_ID_DEVICE_ATTESTATION_PUBLIC_KEY = 6, + /* CommissionableDataProvider */ + TAG_ID_SETUP_DISCRIMINATOR = 11, + TAG_ID_SPAKE2_ITERATION_COUNT = 12, + TAG_ID_SPAKE2_SALT = 13, + TAG_ID_SPAKE2_VERIFIER = 14, + TAG_ID_SPAKE2_SETUP_PASSCODE = 15, + /* DeviceInstanceInfoProvider */ + TAG_ID_VENDOR_NAME = 21, + TAG_ID_VENDOR_ID = 22, + TAG_ID_PRODUCT_NAME = 23, + TAG_ID_PRODUCT_ID = 24, + TAG_ID_SERIAL_NUMBER = 25, + TAG_ID_MANUFACTURING_DATE = 26, + TAG_ID_HARDWARE_VERSION = 27, + TAG_ID_HARDWARE_VERSION_STRING = 28, + TAG_ID_ROTATING_DEVICE_ID = 29, + /* Platform specific */ + TAG_ID_ENABLE_KEY = 41, + +} FACTORYDATA_TagId; + +FACTORYDATA_StatusTypeDef FACTORYDATA_GetValue(FACTORYDATA_TagId tag, uint8_t * data, uint32_t size, uint32_t * out_datalength); + +#ifdef __cplusplus +} +#endif +#endif /*STM32_FACTORYDATA_H*/ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm32wb5mm_dk_qspi.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm32wb5mm_dk_qspi.h new file mode 100644 index 00000000000000..f525f6e1c7ed65 --- /dev/null +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm32wb5mm_dk_qspi.h @@ -0,0 +1,213 @@ +/** + ****************************************************************************** + * @file stm32wb5mm_dk_qspi.h + * @author MCD Application Team + * @brief This file contains the common defines and functions prototypes for + * the stm32wb5mm_dk_qspi.c driver. + ****************************************************************************** + * @attention + * + * Copyright (c) 2021 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM32WB5MM_DK_QSPI_H +#define STM32WB5MM_DK_QSPI_H + +#ifdef __cplusplus +extern "C" { +#endif +/* Includes ------------------------------------------------------------------*/ +#include "../Components/s25fl128s/s25fl128s.h" +#include "stm32wb5mm_dk_conf.h" +#include "stm32wb5mm_dk_errno.h" + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup STM32WB5MM_DK + * @{ + */ + +/** @addtogroup STM32WB5MM_DK_QSPI + * @{ + */ +/* Exported types ------------------------------------------------------------*/ +/** @defgroup STM32WB5MM_DK_QSPI_Exported_Types Exported Types + * @{ + */ +#define BSP_QSPI_Info_t S25FL128S_Info_t +#define BSP_QSPI_Interface_t S25FL128S_Interface_t +#define BSP_QSPI_Transfer_t S25FL128S_Transfer_t +#define BSP_QSPI_DualFlash_t S25FL128S_DualFlash_t +#define BSP_QSPI_Erase_t S25FL128S_Erase_t + +typedef enum +{ + QSPI_ACCESS_NONE = 0, /*!< Instance not initialized, */ + QSPI_ACCESS_INDIRECT, /*!< Instance use indirect mode access */ + QSPI_ACCESS_MMP /*!< Instance use Memory Mapped Mode read */ +} BSP_QSPI_Access_t; + +typedef struct +{ + BSP_QSPI_Access_t IsInitialized; /*!< Instance access Flash method */ + BSP_QSPI_Interface_t InterfaceMode; /*!< Flash Interface mode of Instance */ + BSP_QSPI_Transfer_t TransferRate; /*!< Flash Transfer mode of Instance */ + uint32_t DualFlashMode; /*!< Flash dual mode */ + uint32_t IsMspCallbacksValid; +} BSP_QSPI_Ctx_t; + +typedef struct +{ + BSP_QSPI_Interface_t InterfaceMode; /*!< Current Flash Interface mode */ + BSP_QSPI_Transfer_t TransferRate; /*!< Current Flash Transfer mode */ + BSP_QSPI_DualFlash_t DualFlashMode; /*!< Dual Flash mode */ +} BSP_QSPI_Init_t; + +typedef struct +{ + uint32_t FlashSize; + uint32_t ClockPrescaler; + uint32_t SampleShifting; + uint32_t DualFlashMode; +} MX_QSPI_Init_t; +#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1) +typedef struct +{ + void (*pMspInitCb)(pQSPI_CallbackTypeDef); + void (*pMspDeInitCb)(pQSPI_CallbackTypeDef); +} BSP_QSPI_Cb_t; +#endif /* (USE_HAL_QSPI_REGISTER_CALLBACKS == 1) */ + +/** + * @} + */ + +/* Exported constants --------------------------------------------------------*/ +/** @defgroup STM32WB5MM_DK_QSPI_Exported_Constants Exported Constants + * @{ + */ +/* QSPI instances number */ +#define QSPI_INSTANCES_NUMBER 1U + +/* Definition for QSPI modes */ +#define BSP_QSPI_SPI_MODE (BSP_QSPI_Interface_t) S25FL128S_SPI_MODE /* 1 Cmd Line, 1 Address Line and 1 Data Line */ +#define BSP_QSPI_SPI_1I2O_MODE (BSP_QSPI_Interface_t) S25FL128S_SPI_1I2O_MODE /* 1 Cmd Line, 1 Address Line and 2 Data Lines */ +#define BSP_QSPI_SPI_2IO_MODE (BSP_QSPI_Interface_t) S25FL128S_SPI_2IO_MODE /* 1 Cmd Line, 2 Address Lines and 2 Data Lines */ +#define BSP_QSPI_SPI_1I4O_MODE (BSP_QSPI_Interface_t) S25FL128S_SPI_1I4O_MODE /* 1 Cmd Line, 1 Address Line and 4 Data Lines */ +#define BSP_QSPI_SPI_4IO_MODE (BSP_QSPI_Interface_t) S25FL128S_SPI_4IO_MODE /* 1 Cmd Line, 4 Address Lines and 4 Data Lines */ +#define BSP_QSPI_QPI_MODE (BSP_QSPI_Interface_t) S25FL128S_QPI_MODE /* 4 Cmd Lines, 4 Address Lines and 4 Data Lines */ + +/* Definition for QSPI transfer rates */ +#define BSP_QSPI_STR_TRANSFER (BSP_QSPI_Transfer_t) S25FL128S_STR_TRANSFER /* Single Transfer Rate */ + +/* Definition for QSPI dual flash mode */ +#define BSP_QSPI_DUALFLASH_DISABLE (BSP_QSPI_DualFlash_t) S25FL128S_DUALFLASH_DISABLE /* Single flash mode */ + +/* QSPI erase types */ +#define BSP_QSPI_ERASE_4K (BSP_QSPI_Erase_t) S25FL128S_ERASE_4K +#define BSP_QSPI_ERASE_64K (BSP_QSPI_Erase_t) S25FL128S_ERASE_64K +#define BSP_QSPI_ERASE_CHIP (BSP_QSPI_Erase_t) S25FL128S_ERASE_CHIP + +/* QSPI block sizes */ +#define BSP_QSPI_BLOCK_4K S25FL128S_SECTOR_4K +#define BSP_QSPI_BLOCK_64K S25FL128S_BLOCK_64K + +/* Definition for QSPI clock resources */ +#define QSPI_CLK_ENABLE() __HAL_RCC_QSPI_CLK_ENABLE() +#define QSPI_CLK_DISABLE() __HAL_RCC_QSPI_CLK_DISABLE() +#define QSPI_CS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() +#define QSPI_CLK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() +#define QSPI_D0_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() +#define QSPI_D1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() +#define QSPI_D2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() +#define QSPI_D3_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() + +#define QSPI_FORCE_RESET() __HAL_RCC_QSPI_FORCE_RESET() +#define QSPI_RELEASE_RESET() __HAL_RCC_QSPI_RELEASE_RESET() + +/* Definition for QSPI Pins */ +#define QSPI_CS_PIN GPIO_PIN_3 +#define QSPI_CS_GPIO_PORT GPIOD +#define QSPI_CLK_PIN GPIO_PIN_3 +#define QSPI_CLK_GPIO_PORT GPIOA +#define QSPI_D0_PIN GPIO_PIN_9 +#define QSPI_D0_GPIO_PORT GPIOB +#define QSPI_D1_PIN GPIO_PIN_5 +#define QSPI_D1_GPIO_PORT GPIOD +#define QSPI_D2_PIN GPIO_PIN_6 +#define QSPI_D2_GPIO_PORT GPIOD +#define QSPI_D3_PIN GPIO_PIN_7 +#define QSPI_D3_GPIO_PORT GPIOD + +/* S25FL128S memory */ +/* Size of the flash */ +#define QSPI_FLASH_SIZE 26 +#define QSPI_PAGE_SIZE 256 + +/** + * @} + */ + +/** @addtogroup STM32WB5MM_DK_QSPI_Exported_Variables + * @{ + */ +extern QSPI_HandleTypeDef hqspi; +extern BSP_QSPI_Ctx_t QSPI_Ctx[QSPI_INSTANCES_NUMBER]; +/** + * @} + */ + +/* Exported functions --------------------------------------------------------*/ +/** @addtogroup STM32WB5MM_DK_QSPI_Exported_Functions + * @{ + */ +int32_t BSP_QSPI_Init(uint32_t Instance, BSP_QSPI_Init_t * Init); +int32_t BSP_QSPI_DeInit(uint32_t Instance); +#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1) +int32_t BSP_QSPI_RegisterMspCallbacks(uint32_t Instance, BSP_QSPI_Cb_t * CallBacks); +int32_t BSP_QSPI_RegisterDefaultMspCallbacks(uint32_t Instance); +#endif /* (USE_HAL_QSPI_REGISTER_CALLBACKS == 1) */ +int32_t BSP_QSPI_Read(uint32_t Instance, uint8_t * pData, uint32_t ReadAddr, uint32_t Size); +int32_t BSP_QSPI_Write(uint32_t Instance, uint8_t * pData, uint32_t WriteAddr, uint32_t Size); +int32_t BSP_QSPI_EraseBlock(uint32_t Instance, uint32_t BlockAddress, BSP_QSPI_Erase_t BlockSize); +int32_t BSP_QSPI_EraseChip(uint32_t Instance); +int32_t BSP_QSPI_GetStatus(uint32_t Instance); +int32_t BSP_QSPI_GetInfo(uint32_t Instance, BSP_QSPI_Info_t * pInfo); +int32_t BSP_QSPI_EnableMemoryMappedMode(uint32_t Instance); +int32_t BSP_QSPI_DisableMemoryMappedMode(uint32_t Instance); +int32_t BSP_QSPI_ReadID(uint32_t Instance, uint8_t * Id); + +/* These functions can be modified in case the current settings + need to be changed for specific application needs */ +HAL_StatusTypeDef MX_QSPI_Init(QSPI_HandleTypeDef * hQspi, MX_QSPI_Init_t * Config); + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* STM32WB5MM_DK_QSPI_H */ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm32wbxx_it.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm32wbxx_it.h index fbba7c4af75667..b5b6fe06460491 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm32wbxx_it.h +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm32wbxx_it.h @@ -37,20 +37,21 @@ void BusFault_Handler(void); void UsageFault_Handler(void); void SVC_Handler(void); void DebugMon_Handler(void); -void PendSV_Handler(void); -void SysTick_Handler(void); void IPCC_C1_RX_IRQHandler(void); void IPCC_C1_TX_IRQHandler(void); -void EXTI4_IRQHandler(void); -void EXTI0_IRQHandler(void); -void EXTI1_IRQHandler(void); +#if (CFG_HW_USART1_ENABLED == 1) void USART1_IRQHandler(void); -void DMA2_Channel4_IRQHandler(void); +#endif +#if (CFG_HW_USART1_DMA_TX_SUPPORTED == 1) +void CFG_HW_USART1_DMA_TX_IRQHandler(void); +#endif +#if (CFG_HW_LPUART1_DMA_TX_SUPPORTED == 1) void CFG_HW_USART1_DMA_TX_IRQHandler(void); +#endif void RTC_WKUP_IRQHandler(void); +#if (CFG_HW_LPUART1_ENABLED == 1) void LPUART1_IRQHandler(void); -void DMA1_Channel4_IRQHandler(void); -void QUADSPI_IRQHandler(void); +#endif #ifdef __cplusplus } diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm_ext_flash.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm_ext_flash.h new file mode 100644 index 00000000000000..f4bf139e2a3c37 --- /dev/null +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm_ext_flash.h @@ -0,0 +1,73 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file stm_ext_flash.h + * @author MCD Application Team + * @brief Header file for stm_ext_flash.c + ****************************************************************************** + * @attention + * + * Copyright (c) 2023 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM_EXT_FLASH_H +#define STM_EXT_FLASH_H + +/* Includes ------------------------------------------------------------------*/ +#include "utilities_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define EXTERNAL_FLASH_ADDRESS 0x90000000U +#define OTA_MAX_SIZE 0x100000 // 1 Mbytes + +typedef enum +{ + STM_EXT_FLASH_OK, + STM_EXT_FLASH_INIT_FAILED, + STM_EXT_FLASH_WRITE_FAILED, + STM_EXT_FLASH_READ_FAILED, + STM_EXT_FLASH_DELETE_FAILED, + STM_EXT_FLASH_INVALID_PARAM, + STM_EXT_FLASH_SIZE_FULL +} STM_OTA_StatusTypeDef; + +/* Exported variables ------------------------------------------------------- */ +/* Exported functions ------------------------------------------------------- */ + +/** + * @brief init ota fw + */ +STM_OTA_StatusTypeDef STM_EXT_FLASH_Init(void); + +/** + * @brief Delete old image in external flash + */ +STM_OTA_StatusTypeDef STM_EXT_FLASH_Delete_Image(uint32_t Address, uint32_t Length); + +/** + * @brief Write chunk of data in external flash + */ +STM_OTA_StatusTypeDef STM_EXT_FLASH_WriteChunk(uint32_t DestAddress, uint8_t * pSrcBuffer, uint32_t Length); + +/** + * @brief Read chunk of data in external flash + */ +STM_OTA_StatusTypeDef STM_EXT_FLASH_ReadChunk(uint32_t DestAddress, uint8_t * pSrcBuffer, uint32_t Length); + +#ifdef __cplusplus +} +#endif + +#endif /*STM_EXT_FLASH_H */ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm_logging.h b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm_logging.h index 21400abd1df463..e34783ebc1307b 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm_logging.h +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Inc/stm_logging.h @@ -66,6 +66,6 @@ typedef uint8_t appliLogLevel_t; void logApplication(appliLogLevel_t aLogLevel, appliLogRegion_t aLogRegion, const char * aFormat, ...); #ifdef __cplusplus -} /* extern "C" */ +} #endif #endif /* STM_LOGGING_H_ */ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_ble.c b/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_ble.c index 4200191a38a0e9..7863775d427146 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_ble.c +++ b/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_ble.c @@ -161,11 +161,6 @@ typedef struct { BleGlobalContext_t BleApplicationContext_legacy; APP_BLE_ConnStatus_t Device_Connection_Status; - /** - * ID of the Advertising Timeout - */ - uint8_t Advertising_mgr_timer_Id; - uint8_t SwitchOffGPIO_timer_Id; } BleApplicationContext_t; /* USER CODE BEGIN PTD */ @@ -174,8 +169,8 @@ typedef struct /* Private defines -----------------------------------------------------------*/ #define APPBLE_GAP_DEVICE_NAME_LENGTH 7 -#define FAST_ADV_TIMEOUT (30 * 1000 * 1000 / CFG_TS_TICK_VAL) /**< 30s */ -#define INITIAL_ADV_TIMEOUT (60 * 1000 * 1000 / CFG_TS_TICK_VAL) /**< 60s */ +#define FAST_ADV_TIMEOUT (30 * 1000 * 1000 / CFG_TS_TICK_VAL) /**< 30s */ +#define INITIAL_ADV_TIMEOUT (1 * 1000 * 1000 / CFG_TS_TICK_VAL) /**< 60s */ #define BD_ADDR_SIZE_LOCAL 6 @@ -188,20 +183,6 @@ typedef struct /* USER CODE END PM */ -osMutexId_t MtxHciId; -osSemaphoreId_t SemHciId; -osThreadId_t HciUserEvtProcessId; -// FreeeRTOS sw timer -TimerHandle_t sbleWorkaroundAdvTimeoutTimer; - -const osThreadAttr_t HciUserEvtProcess_attr = { .name = CFG_HCI_USER_EVT_PROCESS_NAME, - .attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS, - .cb_mem = CFG_HCI_USER_EVT_PROCESS_CB_MEM, - .cb_size = CFG_HCI_USER_EVT_PROCESS_CB_SIZE, - .stack_mem = CFG_HCI_USER_EVT_PROCESS_STACK_MEM, - .priority = CFG_HCI_USER_EVT_PROCESS_PRIORITY, - .stack_size = CFG_HCI_USER_EVT_PROCESS_STACK_SIZE }; - /* Private variables ---------------------------------------------------------*/ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer; @@ -213,17 +194,17 @@ static const uint8_t M_bd_addr[BD_ADDR_SIZE_LOCAL] = { static uint8_t bd_addr_udn[BD_ADDR_SIZE_LOCAL]; /** - * Identity root key used to derive LTK and CSRK + * Identity root key used to derive IRK and DHK(Legacy) */ -static const uint8_t BLE_CFG_IR_VALUE[16] = CFG_BLE_IRK; +static const uint8_t BLE_CFG_IR_VALUE[16] = CFG_BLE_IR; /** - * Encryption root key used to derive LTK and CSRK + * Encryption root key used to derive LTK(Legacy) and CSRK */ -static const uint8_t BLE_CFG_ER_VALUE[16] = CFG_BLE_ERK; +static const uint8_t BLE_CFG_ER_VALUE[16] = CFG_BLE_ER; -PLACE_IN_SECTION("BLE_APP_CONTEXT") static BleApplicationContext_t BleApplicationContext; -PLACE_IN_SECTION("BLE_APP_CONTEXT") static uint16_t AdvIntervalMin, AdvIntervalMax; +static BleApplicationContext_t BleApplicationContext; +static uint16_t AdvIntervalMin, AdvIntervalMax; MATTER_App_Notification_evt_t handleNotification; @@ -245,18 +226,45 @@ uint8_t manuf_data[15] = { /* USER CODE END PV */ +/* Global variables ----------------------------------------------------------*/ +osMutexId_t MtxHciId; +osSemaphoreId_t SemHciId; +osThreadId_t HciUserEvtProcessId; +#if (L2CAP_REQUEST_NEW_CONN_PARAM != 0) +osThreadId_t ConnIntUpdateReqProcessId; +#endif + +const osThreadAttr_t HciUserEvtProcess_attr = { .name = CFG_HCI_USER_EVT_PROCESS_NAME, + .attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS, + .cb_mem = CFG_HCI_USER_EVT_PROCESS_CB_MEM, + .cb_size = CFG_HCI_USER_EVT_PROCESS_CB_SIZE, + .stack_mem = CFG_HCI_USER_EVT_PROCESS_STACK_MEM, + .priority = CFG_HCI_USER_EVT_PROCESS_PRIORITY, + .stack_size = CFG_HCI_USER_EVT_PROCESS_STACK_SIZE }; + +#if (L2CAP_REQUEST_NEW_CONN_PARAM != 0) +const osThreadAttr_t ConnIntUpdateReqProcess_attr = { .name = CFG_CONN_INT_UPD_REQ_PROCESS_NAME, + .attr_bits = CFG_CONN_INT_UPD_REQ_PROCESS_ATTR_BITS, + .cb_mem = CFG_CONN_INT_UPD_REQ_PROCESS_CB_MEM, + .cb_size = CFG_CONN_INT_UPD_REQ_PROCESS_CB_SIZE, + .stack_mem = CFG_CONN_INT_UPD_REQ_PROCESS_STACK_MEM, + .priority = CFG_CONN_INT_UPD_REQ_PROCESS_PRIORITY, + .stack_size = CFG_CONN_INT_UPD_REQ_PROCESS_STACK_SIZE }; +#endif + /* Private function prototypes -----------------------------------------------*/ static void BLE_UserEvtRx(void * pPayload); static void BLE_StatusNot(HCI_TL_CmdStatus_t status); static void Ble_Tl_Init(void); static void Ble_Hci_Gap_Gatt_Init(void); static const uint8_t * BleGetBdAddress(void); +static void HciUserEvtProcess(void * argument); static void Switch_OFF_GPIO(void); #if (L2CAP_REQUEST_NEW_CONN_PARAM != 0) +static void ConnIntUpdateReqProcess(void * argument); static void BLE_SVC_L2CAP_Conn_Update(uint16_t Connection_Handle); #endif -static void HciUserEvtProcess(void * argument); -void BleAdvWorkaroundTimeoutHandler(TimerHandle_t xTimer); + /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ @@ -267,6 +275,7 @@ void APP_BLE_Init_Dyn_1(void) /* USER CODE BEGIN APP_BLE_Init_1 */ /* USER CODE END APP_BLE_Init_1 */ + SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { { { 0, 0, 0 } }, /**< Header unused */ { 0, /** pBleBufferAddress not used */ 0, /** BleBufferSize not used */ @@ -278,9 +287,9 @@ void APP_BLE_Init_Dyn_1(void) CFG_BLE_PREPARE_WRITE_LIST_SIZE, CFG_BLE_MBLOCK_COUNT, CFG_BLE_MAX_ATT_MTU, - CFG_BLE_SLAVE_SCA, - CFG_BLE_MASTER_SCA, - CFG_BLE_LSE_SOURCE, + CFG_BLE_PERIPHERAL_SCA, + CFG_BLE_CENTRAL_SCA, + CFG_BLE_LS_SOURCE, CFG_BLE_MAX_CONN_EVENT_LENGTH, CFG_BLE_HSE_STARTUP_TIME, CFG_BLE_VITERBI_MODE, @@ -300,8 +309,6 @@ void APP_BLE_Init_Dyn_1(void) */ UTIL_LPM_SetOffMode(1 << CFG_LPM_APP_BLE, UTIL_LPM_DISABLE); - MtxHciId = osMutexNew(NULL); - SemHciId = osSemaphoreNew(1, 0, NULL); /*< Create the semaphore and make it busy at initialization */ /** * Register the hci transport layer to handle BLE User Asynchronous Events */ @@ -327,27 +334,26 @@ void APP_BLE_Init_Dyn_1(void) */ BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE; BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0xFFFF; + /** * Initialization of ADV - Ad Manufacturer Element - Support OTA Bit Mask */ - #if (RADIO_ACTIVITY_EVENT != 0) aci_hal_set_radio_activity_mask(0x0006); #endif #if (L2CAP_REQUEST_NEW_CONN_PARAM != 0) + ConnIntUpdateReqProcessId = osThreadNew(ConnIntUpdateReqProcess, NULL, &ConnIntUpdateReqProcess_attr); + index_con_int = 0; mutex = 1; #endif /** - * Initialize P2P Server Application + * Initialize Matter Application */ APP_MATTER_Init(); - /** - * Create timer to handle the Led Switch OFF - */ - HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(BleApplicationContext.SwitchOffGPIO_timer_Id), hw_ts_SingleShot, Switch_OFF_GPIO); + return; } void APP_BLE_Init_Dyn_2(void) @@ -361,26 +367,16 @@ void APP_BLE_Init_Dyn_2(void) AdvIntervalMin = CFG_FAST_CONN_ADV_INTERVAL_MIN; AdvIntervalMax = CFG_FAST_CONN_ADV_INTERVAL_MAX; + /** + * Start to Advertise to be connected by P2P Client + */ +#if (CFG_LPM_SUPPORTED == 1) + APP_BLE_Adv_Request(APP_BLE_LP_ADV); +#endif /* USER CODE BEGIN APP_BLE_Init_2 */ /* USER CODE END APP_BLE_Init_2 */ -} - -void APP_BLE_Init_Dyn_3(void) -{ - - sbleWorkaroundAdvTimeoutTimer = xTimerCreate("BleAdvWorkaroundTimer", // Just a text name, not used by the RTOS kernel - pdMS_TO_TICKS(2000), // == default timer period (mS) - 0, // no timer reload (==one-shot) - NULL, // init timer id = ble obj context - BleAdvWorkaroundTimeoutHandler // timer callback handler - ); - if (xTimerStart(sbleWorkaroundAdvTimeoutTimer, 0) != pdPASS) - { - /* The timer could not be set into the Active - state. */ - } - APP_BLE_Adv_Request(APP_BLE_FAST_ADV); + return; } SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void * pckt) @@ -437,8 +433,6 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void * pckt) */ connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data; - // HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id); - APP_DBG_MSG("EVT_LE_CONN_COMPLETE for connection handle 0x%x\n", connection_complete_event->Connection_Handle); if (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_CONNECTING) @@ -486,15 +480,13 @@ APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status(void) return BleApplicationContext.Device_Connection_Status; } +/* USER CODE BEGIN FD*/ +void APP_BLE_Key_Button1_Action(void) {} + void APP_BLE_Key_Button2_Action(void) { #if (L2CAP_REQUEST_NEW_CONN_PARAM != 0) - if (BleApplicationContext.Device_Connection_Status != APP_BLE_FAST_ADV && - BleApplicationContext.Device_Connection_Status != APP_BLE_IDLE) - { - BLE_SVC_L2CAP_Conn_Update(BleApplicationContext.BleApplicationContext_legacy.connectionHandle); - } - return; + osThreadFlagsSet(ConnIntUpdateReqProcessId, 1); #endif } @@ -502,9 +494,7 @@ void APP_BLE_Key_Button3_Action(void) {} void APP_BLE_Stop(void) { - /* Stop Advertising Timer */ - // HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id); - // HW_TS_Delete(BleApplicationContext.Advertising_mgr_timer_Id); + /* BLE STOP Procedure */ aci_hal_stack_reset(); } @@ -518,6 +508,8 @@ static void Ble_Tl_Init(void) { HCI_TL_HciInitConf_t Hci_Tl_Init_Conf; + MtxHciId = osMutexNew(NULL); + SemHciId = osSemaphoreNew(1, 0, NULL); /*< Create the semaphore and make it busy at initialization */ Hci_Tl_Init_Conf.p_cmdbuffer = (uint8_t *) &BleCmdBuffer; Hci_Tl_Init_Conf.StatusNotCallBack = BLE_StatusNot; hci_init(BLE_UserEvtRx, (void *) &Hci_Tl_Init_Conf); @@ -549,13 +541,13 @@ static void Ble_Hci_Gap_Gatt_Init(void) aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET, CONFIG_DATA_PUBADDR_LEN, (uint8_t *) bd_addr); /* BLE MAC in ADV Packet */ - // manuf_data[ sizeof(manuf_data)-6] = bd_addr[5]; - // manuf_data[ sizeof(manuf_data)-5] = bd_addr[4]; - // manuf_data[ sizeof(manuf_data)-4] = bd_addr[3]; - // manuf_data[ sizeof(manuf_data)-3] = bd_addr[2]; - // manuf_data[ sizeof(manuf_data)-2] = bd_addr[1]; - // manuf_data[ sizeof(manuf_data)-1] = bd_addr[0]; - // + // manuf_data[ sizeof(manuf_data)-6] = bd_addr[5]; + // manuf_data[ sizeof(manuf_data)-5] = bd_addr[4]; + // manuf_data[ sizeof(manuf_data)-4] = bd_addr[3]; + // manuf_data[ sizeof(manuf_data)-3] = bd_addr[2]; + // manuf_data[ sizeof(manuf_data)-2] = bd_addr[1]; + // manuf_data[ sizeof(manuf_data)-1] = bd_addr[0]; + /** * Static random Address * The two upper bits shall be set to 1 @@ -567,7 +559,7 @@ static void Ble_Hci_Gap_Gatt_Init(void) aci_hal_write_config_data(CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t *) srd_bd_addr); /** - * Write Identity root key used to derive LTK and CSRK + * Write Identity root key used to derive IRK and DHK(Legacy) */ aci_hal_write_config_data(CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t *) BLE_CFG_IR_VALUE); @@ -642,7 +634,7 @@ static void Ble_Hci_Gap_Gatt_Init(void) } aci_gap_set_authentication_requirement(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode, - BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode, 1, 0, + BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode, 0, 0, BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin, BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax, BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin, @@ -700,7 +692,6 @@ void APP_BLE_Adv_Request(APP_BLE_ConnStatus_t New_Status) /* Update Advertising data */ ret = aci_gap_update_adv_data(sizeof(manuf_data), (uint8_t *) manuf_data); - APP_DBG_MSG("check set discoverable , result: %d \n", ret); if (ret == BLE_STATUS_SUCCESS) { if (New_Status == APP_BLE_FAST_ADV) @@ -709,14 +700,14 @@ void APP_BLE_Adv_Request(APP_BLE_ConnStatus_t New_Status) } else { - APP_DBG_MSG("Successfully Start Low Power Advertising \n"); + APP_DBG_MSG("\n\rSuccessfully Start Low Power Advertising \n\r"); } } else { if (New_Status == APP_BLE_FAST_ADV) { - APP_DBG_MSG("Start Fast Advertising Failed , result: %d \n", ret); + APP_DBG_MSG("Start Fast Advertising Failed , result: %d \n\r", ret); } else { @@ -785,15 +776,9 @@ const uint8_t * BleGetBdAddress(void) *SPECIFIC FUNCTIONS FOR P2P SERVER * *************************************************************/ -void BleAdvWorkaroundTimeoutHandler(TimerHandle_t xTimer) -{ - APP_BLE_Adv_Cancel(); -} - void APP_BLE_Adv_Cancel(void) { /* USER CODE BEGIN Adv_Cancel_1 */ - // BSP_LED_Off(LED_GREEN); /* USER CODE END Adv_Cancel_1 */ if (BleApplicationContext.Device_Connection_Status != APP_BLE_CONNECTED_SERVER) @@ -825,7 +810,6 @@ void APP_BLE_Adv_Cancel(void) static void Switch_OFF_GPIO() { /* USER CODE BEGIN Switch_OFF_GPIO */ - // BSP_LED_Off(LED_GREEN); /* USER CODE END Switch_OFF_GPIO */ } @@ -841,12 +825,12 @@ void BLE_SVC_L2CAP_Conn_Update(uint16_t Connection_Handle) index_con_int = (index_con_int + 1) % SIZE_TAB_CONN_INT; uint16_t interval_min = CONN_P(tab_conn_interval[index_con_int]); uint16_t interval_max = CONN_P(tab_conn_interval[index_con_int]); - uint16_t slave_latency = L2CAP_SLAVE_LATENCY; + uint16_t peripheral_latency = L2CAP_PERIPHERAL_LATENCY; uint16_t timeout_multiplier = L2CAP_TIMEOUT_MULTIPLIER; tBleStatus result; result = aci_l2cap_connection_parameter_update_req(BleApplicationContext.BleApplicationContext_legacy.connectionHandle, - interval_min, interval_max, slave_latency, timeout_multiplier); + interval_min, interval_max, peripheral_latency, timeout_multiplier); if (result == BLE_STATUS_SUCCESS) { APP_DBG_MSG("BLE_SVC_L2CAP_Conn_Update(), Successfully \r\n\r"); @@ -861,16 +845,21 @@ void BLE_SVC_L2CAP_Conn_Update(uint16_t Connection_Handle) /* USER CODE END BLE_SVC_L2CAP_Conn_Update_2 */ return; } -#endif - -/* USER CODE BEGIN FD_SPECIFIC_FUNCTIONS */ -/* USER CODE END FD_SPECIFIC_FUNCTIONS */ -/************************************************************* - * - * WRAP FUNCTIONS - * - *************************************************************/ +static void ConnIntUpdateReqProcess(void * argument) +{ + UNUSED(argument); + for (;;) + { + osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever); + if (BleApplicationContext.Device_Connection_Status != APP_BLE_FAST_ADV && + BleApplicationContext.Device_Connection_Status != APP_BLE_IDLE) + { + BLE_SVC_L2CAP_Conn_Update(BleApplicationContext.BleApplicationContext_legacy.connectionHandle); + } + } +} +#endif static void HciUserEvtProcess(void * argument) { @@ -883,39 +872,56 @@ static void HciUserEvtProcess(void * argument) } } +/* USER CODE BEGIN FD_SPECIFIC_FUNCTIONS */ + +/* USER CODE END FD_SPECIFIC_FUNCTIONS */ +/************************************************************* + * + * WRAP FUNCTIONS + * + *************************************************************/ void hci_notify_asynch_evt(void * pdata) { + UNUSED(pdata); + osThreadFlagsSet(HciUserEvtProcessId, 1); + return; } void hci_cmd_resp_release(uint32_t flag) { + UNUSED(flag); + osSemaphoreRelease(SemHciId); + return; } void hci_cmd_resp_wait(uint32_t timeout) { + UNUSED(timeout); + osSemaphoreAcquire(SemHciId, osWaitForever); + return; } static void BLE_UserEvtRx(void * pPayload) { SVCCTL_UserEvtFlowStatus_t svctl_return_status; - tHCI_UserEvtRxParam * pParam; + tHCI_UserEvtRxParam * p_param; - pParam = (tHCI_UserEvtRxParam *) pPayload; + p_param = (tHCI_UserEvtRxParam *) pPayload; - svctl_return_status = SVCCTL_UserEvtRx((void *) &(pParam->pckt->evtserial)); + svctl_return_status = SVCCTL_UserEvtRx((void *) &(p_param->pckt->evtserial)); if (svctl_return_status != SVCCTL_UserEvtFlowDisable) { - pParam->status = HCI_TL_UserEventFlow_Enable; + p_param->status = HCI_TL_UserEventFlow_Enable; } else { - pParam->status = HCI_TL_UserEventFlow_Disable; + p_param->status = HCI_TL_UserEventFlow_Disable; } } @@ -929,7 +935,6 @@ static void BLE_StatusNot(HCI_TL_CmdStatus_t status) * This is to prevent a new command is sent while one is already pending */ osMutexAcquire(MtxHciId, osWaitForever); - break; case HCI_TL_CmdAvailable: @@ -938,7 +943,6 @@ static void BLE_StatusNot(HCI_TL_CmdStatus_t status) * This is to prevent a new command is sent while one is already pending */ osMutexRelease(MtxHciId); - break; default: diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_ble.h b/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_ble.h index cb0b80a25628f6..1197ff8ecd27b8 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_ble.h +++ b/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_ble.h @@ -35,6 +35,7 @@ extern "C" { /* USER CODE END Includes */ /* Exported types ------------------------------------------------------------*/ + typedef enum { APP_BLE_IDLE, @@ -68,8 +69,6 @@ typedef enum /* Exported functions ---------------------------------------------*/ void APP_BLE_Init_Dyn_1(void); void APP_BLE_Init_Dyn_2(void); -void APP_BLE_Init_Dyn_3(void); - APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status(void); /* USER CODE BEGIN EF */ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_thread.c b/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_thread.c index f917b8542fc4bf..dd58d369187e1e 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_thread.c +++ b/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_thread.c @@ -44,15 +44,8 @@ /* Private defines -----------------------------------------------------------*/ #define C_SIZE_CMD_STRING 256U +#define THREAD_LINK_POLL_PERIOD (5 * 1000 * 1000 / CFG_TS_TICK_VAL) /**< 5s */ #define MO_NOTIF_QUEUE_SIZE 10 - -/* USER CODE BEGIN PD */ - -/* USER CODE END PD */ - -static osSemaphoreId_t TransferToM0Semaphore; -static osMutexId_t MtxThreadId; - /* FreeRtos stacks attributes */ const osThreadAttr_t ThreadMsgM0ToM4Process_attr = { .name = CFG_THREAD_MSG_M0_TO_M4_PROCESS_NAME, .attr_bits = CFG_THREAD_MSG_M0_TO_M4_PROCESS_ATTR_BITS, @@ -62,15 +55,13 @@ const osThreadAttr_t ThreadMsgM0ToM4Process_attr = { .name = CFG_THREAD_MS .priority = CFG_THREAD_MSG_M0_TO_M4_PROCESS_PRIORITY, .stack_size = CFG_THREAD_MSG_M0_TO_M4_PROCESS_STACK_SIZE }; -const osThreadAttr_t ThreadCliProcess_attr = { .name = CFG_THREAD_CLI_PROCESS_NAME, - .attr_bits = CFG_THREAD_CLI_PROCESS_ATTR_BITS, - .cb_mem = CFG_THREAD_CLI_PROCESS_CB_MEM, - .cb_size = CFG_THREAD_CLI_PROCESS_CB_SIZE, - .stack_mem = CFG_THREAD_CLI_PROCESS_STACK_MEM, - .priority = CFG_THREAD_CLI_PROCESS_PRIORITY, - .stack_size = CFG_THREAD_CLI_PROCESS_STACK_SIZE }; +static osSemaphoreId_t OtCmdProcessSem; +static osSemaphoreId_t OtCmdAckSem; + +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ -static volatile int FlagReceiveAckFromM0 = 0; /* Private macros ------------------------------------------------------------*/ /* USER CODE BEGIN PM */ @@ -78,38 +69,21 @@ static volatile int FlagReceiveAckFromM0 = 0; /* Private function prototypes -----------------------------------------------*/ static void APP_THREAD_CheckWirelessFirmwareInfo(void); +static void APP_THREAD_StateNotif(uint32_t NotifFlags, void * pContext); +static void APP_THREAD_DeviceConfig(void); static void APP_THREAD_TraceError(const char * pMess, uint32_t ErrCode); -#if (CFG_FULL_LOW_POWER == 0) -static void Send_CLI_To_M0(void); -#endif /* (CFG_FULL_LOW_POWER == 0) */ -static void Send_CLI_Ack_For_OT(void); -static void HostTxCb(void); static void Wait_Getting_Ack_From_M0(void); static void Receive_Ack_From_M0(void); static void Receive_Notification_From_M0(void); + +/* FreeRTos wrapper functions */ static void APP_THREAD_FreeRTOSProcessMsgM0ToM4Task(void * argument); -static void Ot_Cmd_Transfer_Common(void); -#if (CFG_FULL_LOW_POWER == 0) -static void APP_THREAD_FreeRTOSSendCLIToM0Task(void * argument); -#endif /* (CFG_FULL_LOW_POWER == 0) */ -#if (CFG_FULL_LOW_POWER == 0) -static void RxCpltCallback(void); -#endif /* (CFG_FULL_LOW_POWER == 0) */ /* USER CODE BEGIN PFP */ + /* USER CODE END PFP */ /* Private variables ---------------------------------------------------------*/ -#if (CFG_FULL_LOW_POWER == 0) -static uint8_t aRxBuffer[C_SIZE_CMD_STRING]; -#endif /* (CFG_FULL_LOW_POWER == 0) */ - -#if (CFG_FULL_LOW_POWER == 0) -static uint8_t CommandString[C_SIZE_CMD_STRING]; -#endif /* (CFG_FULL_LOW_POWER == 0) */ -static __IO uint16_t indexReceiveChar = 0; -static __IO uint16_t CptReceiveCmdFromUser = 0; - static TL_CmdPacket_t * p_thread_otcmdbuffer; static TL_EvtPacket_t * p_thread_notif_M0_to_M4; PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_TH_Config_t ThreadConfigBuffer; @@ -119,18 +93,20 @@ PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t ThreadCliCmdBuffer; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t ThreadCliNotBuffer; extern uint8_t g_ot_notification_allowed; -/* USER CODE BEGIN PV */ static QueueHandle_t MoNotifQueue; static osThreadId_t OsTaskMsgM0ToM4Id; /* Task managing the M0 to M4 messaging */ -#if (CFG_FULL_LOW_POWER == 0) -static osThreadId_t OsTaskCliId; /* Task used to manage CLI command */ -#endif /* (CFG_FULL_LOW_POWER == 0) */ -/* Debug */ +/* USER CODE BEGIN PV */ + /* USER CODE END PV */ /* Functions Definition ------------------------------------------------------*/ -void APP_THREAD_Init(void) +/** + * @brief Main entry point for the Thread Application + * @param none + * @retval None + */ +void APP_THREAD_Init_Dyn_1(void) { /* USER CODE BEGIN APP_THREAD_INIT_1 */ /* Do not allow stop mode before Thread is initialized */ @@ -153,32 +129,40 @@ void APP_THREAD_Init(void) /* Init config buffer and call TL_THREAD_Init */ APP_THREAD_TL_THREAD_INIT(); - /* Configure UART for sending CLI command from M4 */ - // APP_THREAD_Init_UART_CLI(); Conflict with qspi gpio /* Send Thread start system cmd to M0 */ ThreadInitStatus = SHCI_C2_THREAD_Init(); /* Prevent unused argument(s) compilation warning */ UNUSED(ThreadInitStatus); - /* Semaphore */ - TransferToM0Semaphore = osSemaphoreNew(1, 0, NULL); - /* Initialize the mutex */ - MtxThreadId = osMutexNew(NULL); + /* Create the different FreeRTOS tasks requested to run this Thread application*/ + OsTaskMsgM0ToM4Id = osThreadNew(APP_THREAD_FreeRTOSProcessMsgM0ToM4Task, NULL, &ThreadMsgM0ToM4Process_attr); + + /* Create binary semaphores for OT command handling */ + OtCmdProcessSem = osSemaphoreNew(1, 1, NULL); + OtCmdAckSem = osSemaphoreNew(1, 0, NULL); + /* USER CODE BEGIN APP_THREAD_INIT_FREERTOS */ MoNotifQueue = xQueueCreate(MO_NOTIF_QUEUE_SIZE, sizeof(uint8_t)); if (MoNotifQueue == NULL) { APP_DBG("Failed to allocate M0 notification queue"); } + /* USER CODE END APP_THREAD_INIT_FREERTOS */ +} - /* Create the different FreeRTOS tasks requested to run this Thread application*/ - OsTaskMsgM0ToM4Id = osThreadNew(APP_THREAD_FreeRTOSProcessMsgM0ToM4Task, NULL, &ThreadMsgM0ToM4Process_attr); +void APP_THREAD_Init_Dyn_2(void) +{ + /* Initialize and configure the Thread device*/ +#if (CFG_LPM_SUPPORTED == 1) + APP_THREAD_DeviceConfig(); - /* USER CODE BEGIN APP_THREAD_INIT_FREERTOS */ - /* USER CODE END APP_THREAD_INIT_FREERTOS */ - /* USER CODE BEGIN APP_THREAD_INIT_2 */ - /* USER CODE END APP_THREAD_INIT_2 */ + /* Allow the 800_15_4 IP to enter in low power mode */ + SHCI_C2_RADIO_AllowLowPower(THREAD_IP, TRUE); + + /* Allow stop mode after Thread initialization*/ + UTIL_LPM_SetStopMode(1 << CFG_LPM_APP_THREAD, UTIL_LPM_ENABLE); +#endif } /** @@ -215,10 +199,13 @@ void APP_THREAD_Error(uint32_t ErrId, uint32_t ErrCode) case ERR_THREAD_ERASE_PERSISTENT_INFO: APP_THREAD_TraceError("ERROR : ERR_THREAD_ERASE_PERSISTENT_INFO ", ErrCode); break; + case ERR_THREAD_SET_NETWORK_KEY: + APP_THREAD_TraceError("ERROR : ERR_THREAD_SET_NETWORK_KEY ", ErrCode); + break; case ERR_THREAD_CHECK_WIRELESS: APP_THREAD_TraceError("ERROR : ERR_THREAD_CHECK_WIRELESS ", ErrCode); break; - /* USER CODE BEGIN APP_THREAD_Error_2 */ + /* USER CODE BEGIN APP_THREAD_Error_2 */ case ERR_THREAD_COAP_START: APP_THREAD_TraceError("ERROR : ERR_THREAD_COAP_START ", ErrCode); break; @@ -243,164 +230,101 @@ void APP_THREAD_Error(uint32_t ErrId, uint32_t ErrCode) case ERR_TIMER_START: APP_THREAD_TraceError("ERROR : ERR_TIMER_START ", ErrCode); break; - /* USER CODE END APP_THREAD_Error_2 */ + /* USER CODE END APP_THREAD_Error_2 */ default: APP_THREAD_TraceError("ERROR Unknown ", 0); break; } } -/** - * @brief Perform initialization of CLI UART interface. - * @param None - * @retval None - */ -void APP_THREAD_Init_UART_CLI(void) -{ -#if (CFG_FULL_LOW_POWER == 0) - OsTaskCliId = osThreadNew(APP_THREAD_FreeRTOSSendCLIToM0Task, NULL, &ThreadCliProcess_attr); -#endif /* (CFG_FULL_LOW_POWER == 0) */ - -#if (CFG_FULL_LOW_POWER == 0) - HW_UART_Init(CFG_CLI_UART); - HW_UART_Receive_IT(CFG_CLI_UART, aRxBuffer, 1, RxCpltCallback); -#endif /* (CFG_FULL_LOW_POWER == 0) */ -} - -/** - * @brief Perform initialization of TL for THREAD. - * @param None - * @retval None - */ -void APP_THREAD_TL_THREAD_INIT(void) -{ - ThreadConfigBuffer.p_ThreadOtCmdRspBuffer = (uint8_t *) &ThreadOtCmdBuffer; - ThreadConfigBuffer.p_ThreadNotAckBuffer = (uint8_t *) ThreadNotifRspEvtBuffer; - ThreadConfigBuffer.p_ThreadCliRspBuffer = (uint8_t *) &ThreadCliCmdBuffer; - ThreadConfigBuffer.p_ThreadCliNotBuffer = (uint8_t *) &ThreadCliNotBuffer; - - TL_THREAD_Init(&ThreadConfigBuffer); -} - -/** - * @brief This function is used to transfer the Ot commands from the - * M4 to the M0. - * - * @param None - * @return None - */ -void Ot_Cmd_Transfer(void) -{ - Ot_Cmd_Transfer_Common(); -} - -/** - * @brief This function is used to transfer the Ot commands from the - * M4 to the M0 with Notification M0 to M4 allowed. +/************************************************************* * - * @param None - * @return None - */ -void Ot_Cmd_TransferWithNotif(void) -{ - /* Flag to specify to UTIL_SEQ_EvtIdle that M0 to M4 notifications are allowed */ - g_ot_notification_allowed = 1U; - - Ot_Cmd_Transfer_Common(); -} - -/** - * @brief This function is called when acknowledge from OT command is received from the M0+. + * LOCAL FUNCTIONS * - * @param Otbuffer : a pointer to TL_EvtPacket_t - * @return None - */ -void TL_OT_CmdEvtReceived(TL_EvtPacket_t * Otbuffer) -{ - /* Prevent unused argument(s) compilation warning */ - UNUSED(Otbuffer); - - Receive_Ack_From_M0(); - - /* Does not allow OpenThread M0 to M4 notification */ - g_ot_notification_allowed = 0U; -} + *************************************************************/ /** - * @brief This function is called when notification from M0+ is received. - * - * @param Notbuffer : a pointer to TL_EvtPacket_t - * @return None - */ -void TL_THREAD_NotReceived(TL_EvtPacket_t * Notbuffer) -{ - p_thread_notif_M0_to_M4 = Notbuffer; - - Receive_Notification_From_M0(); -} - /** - * @brief This function is called when notification on CLI TL Channel from M0+ is received. - * - * @param Notbuffer : a pointer to TL_EvtPacket_t - * @return None + * @brief Thread initialization. + * @param None + * @retval None */ -void TL_THREAD_CliNotReceived(TL_EvtPacket_t * Notbuffer) +static void APP_THREAD_DeviceConfig(void) { - TL_CmdPacket_t * l_CliBuffer = (TL_CmdPacket_t *) Notbuffer; - uint8_t l_size = l_CliBuffer->cmdserial.cmd.plen; - - /* WORKAROUND: if string to output is "> " then respond directly to M0 and do not output it */ - if (strcmp((const char *) l_CliBuffer->cmdserial.cmd.payload, "> ") != 0) + otError error = OT_ERROR_NONE; + otLinkModeConfig OT_LinkMode = { 0 }; + /* Set the pool period to 5 sec. It means that when the device will enter + * in 'sleepy end device' mode, it will send an ACK_Request every 5 sec. + * This message will act as keep alive message. + */ + error = otLinkSetPollPeriod(NULL, THREAD_LINK_POLL_PERIOD); + /* Set the sleepy end device mode */ + OT_LinkMode.mRxOnWhenIdle = 0; + OT_LinkMode.mDeviceType = 0; + OT_LinkMode.mNetworkData = 1U; + + error = otThreadSetLinkMode(NULL, OT_LinkMode); + if (error != OT_ERROR_NONE) { - /* Write to CLI UART */ - HW_UART_Transmit_IT(CFG_CLI_UART, l_CliBuffer->cmdserial.cmd.payload, l_size, HostTxCb); + APP_THREAD_Error(ERR_THREAD_LINK_MODE, error); } - else + error = otIp6SetEnabled(NULL, true); + if (error != OT_ERROR_NONE) + { + APP_THREAD_Error(ERR_THREAD_IPV6_ENABLE, error); + } + error = otThreadSetEnabled(NULL, true); + if (error != OT_ERROR_NONE) { - Send_CLI_Ack_For_OT(); + APP_THREAD_Error(ERR_THREAD_START, error); } } - -/** - * @brief This function is called before sending any ot command to the M0 - * core. The purpose of this function is to be able to check if - * there are no notifications coming from the M0 core which are - * pending before sending a new ot command. - * @param None +/* @brief Thread notification when the state changes. + * @param aFlags : Define the item that has been modified + * aContext: Context * @retval None */ -void Pre_OtCmdProcessing(void) +static void APP_THREAD_StateNotif(uint32_t NotifFlags, void * pContext) { - osMutexAcquire(MtxThreadId, osWaitForever); -} - -void APP_THREAD_RegisterCmdBuffer(TL_CmdPacket_t * p_buffer) -{ - p_thread_otcmdbuffer = p_buffer; -} + /* Prevent unused argument(s) compilation warning */ + UNUSED(pContext); -Thread_OT_Cmd_Request_t * THREAD_Get_OTCmdPayloadBuffer(void) -{ - return (Thread_OT_Cmd_Request_t *) p_thread_otcmdbuffer->cmdserial.cmd.payload; -} + /* USER CODE BEGIN APP_THREAD_STATENOTIF */ -Thread_OT_Cmd_Request_t * THREAD_Get_OTCmdRspPayloadBuffer(void) -{ - return (Thread_OT_Cmd_Request_t *) ((TL_EvtPacket_t *) p_thread_otcmdbuffer)->evtserial.evt.payload; -} + /* USER CODE END APP_THREAD_STATENOTIF */ -Thread_OT_Cmd_Request_t * THREAD_Get_NotificationPayloadBuffer(void) -{ - return (Thread_OT_Cmd_Request_t *) (p_thread_notif_M0_to_M4)->evtserial.evt.payload; + if ((NotifFlags & (uint32_t) OT_CHANGED_THREAD_ROLE) == (uint32_t) OT_CHANGED_THREAD_ROLE) + { + switch (otThreadGetDeviceRole(NULL)) + { + case OT_DEVICE_ROLE_DISABLED: + /* USER CODE BEGIN OT_DEVICE_ROLE_DISABLED */ + /* USER CODE END OT_DEVICE_ROLE_DISABLED */ + break; + case OT_DEVICE_ROLE_DETACHED: + /* USER CODE BEGIN OT_DEVICE_ROLE_DETACHED */ + /* USER CODE END OT_DEVICE_ROLE_DETACHED */ + break; + case OT_DEVICE_ROLE_CHILD: + /* USER CODE BEGIN OT_DEVICE_ROLE_CHILD */ + /* USER CODE END OT_DEVICE_ROLE_CHILD */ + break; + case OT_DEVICE_ROLE_ROUTER: + /* USER CODE BEGIN OT_DEVICE_ROLE_ROUTER */ + /* USER CODE END OT_DEVICE_ROLE_ROUTER */ + break; + case OT_DEVICE_ROLE_LEADER: + /* USER CODE BEGIN OT_DEVICE_ROLE_LEADER */ + /* USER CODE END OT_DEVICE_ROLE_LEADER */ + break; + default: + /* USER CODE BEGIN DEFAULT */ + /* USER CODE END DEFAULT */ + break; + } + } } -/************************************************************* - * - * LOCAL FUNCTIONS - * - *************************************************************/ - /** * @brief Warn the user that an error has occurred.In this case, * the LEDs on the Board will start blinking. @@ -438,30 +362,16 @@ static void APP_THREAD_CheckWirelessFirmwareInfo(void) else { APP_DBG("**********************************************************"); - APP_DBG("WIRELESS COPROCESSOR FW:"); - /* Print version */ - APP_DBG("VERSION ID = %d.%d.%d", p_wireless_info->VersionMajor, p_wireless_info->VersionMinor, p_wireless_info->VersionSub); + APP_DBG("PRODUCT NAME : " PRODUCT_NAME); + APP_DBG("VENDOR NAME : " VENDOR_NAME); + APP_DBG("HARDWARE : " HARDWARE_VERSION); + APP_DBG("SOFTWARE : X-CUBE-MATTER release revision " X_CUBE_MATTER_VERSION); + APP_DBG("Embedded SW components :"); + APP_DBG("- Matter SDK version : " MATTER_SDK_VERSION); + APP_DBG("- STM32WB Cube Firmware Version : %d.%d.%d.%d", p_wireless_info->VersionMajor, p_wireless_info->VersionMinor, + p_wireless_info->VersionSub, p_wireless_info->VersionBranch); + APP_DBG("**********************************************************"); - switch (p_wireless_info->StackType) - { - case INFO_STACK_TYPE_THREAD_FTD: - APP_DBG("FW Type : Thread FTD"); - break; - case INFO_STACK_TYPE_THREAD_MTD: - APP_DBG("FW Type : Thread MTD"); - break; - case INFO_STACK_TYPE_BLE_THREAD_FTD_DYAMIC: - APP_DBG("FW Type : Dynamic Concurrent Mode BLE/Thread"); - break; - // case INFO_STACK_TYPE_BLE_THREAD_FOR_MATTER: - // APP_DBG("FW Type : Dynamic Concurrent Mode BLE/Thread for Matter ") - // ; - // break; - default: - /* No Thread device supported ! */ - APP_THREAD_Error((uint32_t) ERR_THREAD_CHECK_WIRELESS, (uint32_t) ERR_INTERFACE_FATAL); - break; - } APP_DBG("**********************************************************"); } } @@ -478,7 +388,6 @@ static void APP_THREAD_FreeRTOSProcessMsgM0ToM4Task(void * argument) for (;;) { /* USER CODE BEGIN APP_THREAD_FREERTOS_PROCESS_MSG_M0_TO_M4_1 */ - /* USER END END APP_THREAD_FREERTOS_PROCESS_MSG_M0_TO_M4_1 */ xQueueReceive(MoNotifQueue, &NotUsed, portMAX_DELAY); @@ -496,25 +405,8 @@ static void APP_THREAD_FreeRTOSProcessMsgM0ToM4Task(void * argument) } } -#if (CFG_FULL_LOW_POWER == 0) -static void APP_THREAD_FreeRTOSSendCLIToM0Task(void * argument) -{ - UNUSED(argument); - for (;;) - { - /* USER CODE BEGIN APP_THREAD_FREERTOS_SEND_CLI_TO_M0_1 */ - - /* USER END END APP_THREAD_FREERTOS_SEND_CLI_TO_M0_1 */ - osThreadFlagsWait(1, osFlagsWaitAll, osWaitForever); - Send_CLI_To_M0(); - /* USER CODE BEGIN APP_THREAD_FREERTOS_SEND_CLI_TO_M0_2 */ - - /* USER END END APP_THREAD_FREERTOS_SEND_CLI_TO_M0_2 */ - } -} -#endif /* (CFG_FULL_LOW_POWER == 0) */ - /* USER CODE BEGIN FREERTOS_WRAPPER_FUNCTIONS */ + /* USER CODE END FREERTOS_WRAPPER_FUNCTIONS */ /* USER CODE BEGIN FD_LOCAL_FUNCTIONS */ @@ -526,6 +418,27 @@ static void APP_THREAD_FreeRTOSSendCLIToM0Task(void * argument) * WRAP FUNCTIONS * *************************************************************/ + +void APP_THREAD_RegisterCmdBuffer(TL_CmdPacket_t * p_buffer) +{ + p_thread_otcmdbuffer = p_buffer; +} + +Thread_OT_Cmd_Request_t * THREAD_Get_OTCmdPayloadBuffer(void) +{ + return (Thread_OT_Cmd_Request_t *) p_thread_otcmdbuffer->cmdserial.cmd.payload; +} + +Thread_OT_Cmd_Request_t * THREAD_Get_OTCmdRspPayloadBuffer(void) +{ + return (Thread_OT_Cmd_Request_t *) ((TL_EvtPacket_t *) p_thread_otcmdbuffer)->evtserial.evt.payload; +} + +Thread_OT_Cmd_Request_t * THREAD_Get_NotificationPayloadBuffer(void) +{ + return (Thread_OT_Cmd_Request_t *) (p_thread_notif_M0_to_M4)->evtserial.evt.payload; +} + static void Ot_Cmd_Transfer_Common(void) { /* OpenThread OT command cmdcode range 0x280 .. 0x3DF = 352 */ @@ -542,122 +455,159 @@ static void Ot_Cmd_Transfer_Common(void) } /** - * @brief This function waits for getting an acknowledgment from the M0. + * @brief This function is used to transfer the Ot commands from the + * M4 to the M0. * - * @param None - * @retval None + * @param None + * @return None */ -static void Wait_Getting_Ack_From_M0(void) +void Ot_Cmd_Transfer(void) { - while (FlagReceiveAckFromM0 == 0) - { - } - FlagReceiveAckFromM0 = 0; - osMutexRelease(MtxThreadId); - // osSemaphoreAcquire( TransferToM0Semaphore, osWaitForever ); + Ot_Cmd_Transfer_Common(); } /** - * @brief Receive an acknowledgment from the M0+ core. - * Each command send by the M4 to the M0 are acknowledged. - * This function is called under interrupt. - * @param None - * @retval None + * @brief This function is used to transfer the Ot commands from the + * M4 to the M0 with Notification M0 to M4 allowed. + * + * @param None + * @return None */ -static void Receive_Ack_From_M0(void) +void Ot_Cmd_TransferWithNotif(void) { - FlagReceiveAckFromM0 = 1; - // osSemaphoreRelease( TransferToM0Semaphore); + /* Flag to specify to UTIL_SEQ_EvtIdle that M0 to M4 notifications are allowed */ + g_ot_notification_allowed = 1U; + + Ot_Cmd_Transfer_Common(); } /** - * @brief Receive a notification from the M0+ through the IPCC. - * This function is called under interrupt. + * @brief Perform initialization of TL for THREAD. * @param None * @retval None */ -static void Receive_Notification_From_M0(void) +void APP_THREAD_TL_THREAD_INIT(void) { - /* The xHigherPriorityTaskWoken parameter must be initialized to pdFALSE as - it will get set to pdTRUE inside the interrupt safe API function if a - context switch is required. */ - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - uint8_t NotUsed = 0; - xQueueSendToFrontFromISR(MoNotifQueue, &NotUsed, &xHigherPriorityTaskWoken); + ThreadConfigBuffer.p_ThreadOtCmdRspBuffer = (uint8_t *) &ThreadOtCmdBuffer; + ThreadConfigBuffer.p_ThreadNotAckBuffer = (uint8_t *) ThreadNotifRspEvtBuffer; + ThreadConfigBuffer.p_ThreadCliRspBuffer = (uint8_t *) &ThreadCliCmdBuffer; + ThreadConfigBuffer.p_ThreadCliNotBuffer = (uint8_t *) &ThreadCliNotBuffer; - /* Pass the xHigherPriorityTaskWoken value into portEND_SWITCHING_ISR(). If - xHigherPriorityTaskWoken was set to pdTRUE inside xSemaphoreGiveFromISR() - then calling portEND_SWITCHING_ISR() will request a context switch. If - xHigherPriorityTaskWoken is still pdFALSE then calling - portEND_SWITCHING_ISR() will have no effect */ - portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); + TL_THREAD_Init(&ThreadConfigBuffer); } -#if (CFG_FULL_LOW_POWER == 0) -static void RxCpltCallback(void) +/** + * @brief This function is called when notification on CLI TL Channel from M0+ is received. + * + * @param Notbuffer : a pointer to TL_EvtPacket_t + * @return None + */ +void TL_THREAD_CliNotReceived(TL_EvtPacket_t * Notbuffer) { - /* Filling buffer and wait for '\r' char */ - if (indexReceiveChar < C_SIZE_CMD_STRING) - { - CommandString[indexReceiveChar++] = aRxBuffer[0]; - if (aRxBuffer[0] == '\r') - { - CptReceiveCmdFromUser = 1U; + TL_CmdPacket_t * l_CliBuffer = (TL_CmdPacket_t *) Notbuffer; + uint8_t l_size = l_CliBuffer->cmdserial.cmd.plen; - /* UART task scheduling*/ - osThreadFlagsSet(OsTaskCliId, 1); - } + /* WORKAROUND: if string to output is "> " then respond directly to M0 and do not output it */ + if (strcmp((const char *) l_CliBuffer->cmdserial.cmd.payload, "> ") != 0) + { + /* Do nothing CLI is not enable */ + } + else + { + /* Notify M0 that characters have been sent to UART */ + TL_THREAD_CliSendAck(); } +} + +/** + * @brief This function is called when acknowledge from OT command is received from the M0+. + * + * @param Otbuffer : a pointer to TL_EvtPacket_t + * @return None + */ +void TL_OT_CmdEvtReceived(TL_EvtPacket_t * Otbuffer) +{ + /* Prevent unused argument(s) compilation warning */ + UNUSED(Otbuffer); - /* Once a character has been sent, put back the device in reception mode */ - HW_UART_Receive_IT(CFG_CLI_UART, aRxBuffer, 1U, RxCpltCallback); + Receive_Ack_From_M0(); + + /* Does not allow OpenThread M0 to M4 notification */ + g_ot_notification_allowed = 0U; } -#endif /* (CFG_FULL_LOW_POWER == 0) */ -#if (CFG_FULL_LOW_POWER == 0) /** - * @brief Process sends receive CLI command to M0. - * @param None - * @retval None + * @brief This function is called when notification from M0+ is received. + * + * @param Notbuffer : a pointer to TL_EvtPacket_t + * @return None */ -static void Send_CLI_To_M0(void) +void TL_THREAD_NotReceived(TL_EvtPacket_t * Notbuffer) { - memset(ThreadCliCmdBuffer.cmdserial.cmd.payload, 0x0U, 255U); - memcpy(ThreadCliCmdBuffer.cmdserial.cmd.payload, CommandString, indexReceiveChar); - ThreadCliCmdBuffer.cmdserial.cmd.plen = indexReceiveChar; - ThreadCliCmdBuffer.cmdserial.cmd.cmdcode = 0x0; + p_thread_notif_M0_to_M4 = Notbuffer; - /* Clear receive buffer, character counter and command complete */ - CptReceiveCmdFromUser = 0; - indexReceiveChar = 0; - memset(CommandString, 0, C_SIZE_CMD_STRING); + Receive_Notification_From_M0(); +} - TL_CLI_SendCmd(); +/** + * @brief This function is called before sending any ot command to the M0 + * core. The purpose of this function is to be able to check if + * there are no notifications coming from the M0 core which are + * pending before sending a new ot command. + * @param None + * @retval None + */ +void Pre_OtCmdProcessing(void) +{ + osSemaphoreAcquire(OtCmdProcessSem, osWaitForever); } -#endif /* (CFG_FULL_LOW_POWER == 0) */ /** - * @brief Send notification for CLI TL Channel. + * @brief This function waits for getting an acknowledgment from the M0. + * * @param None * @retval None */ -static void Send_CLI_Ack_For_OT(void) +static void Wait_Getting_Ack_From_M0(void) { + osSemaphoreAcquire(OtCmdAckSem, osWaitForever); + osSemaphoreRelease(OtCmdProcessSem); +} - /* Notify M0 that characters have been sent to UART */ - TL_THREAD_CliSendAck(); +/** + * @brief Receive an acknowledgment from the M0+ core. + * Each command send by the M4 to the M0 are acknowledged. + * This function is called under interrupt. + * @param None + * @retval None + */ +static void Receive_Ack_From_M0(void) +{ + osSemaphoreRelease(OtCmdAckSem); } /** - * @brief End of transfer callback for CLI UART sending. - * - * @param Notbuffer : a pointer to TL_EvtPacket_t - * @return None + * @brief Receive a notification from the M0+ through the IPCC. + * This function is called under interrupt. + * @param None + * @retval None */ -static void HostTxCb(void) +static void Receive_Notification_From_M0(void) { - Send_CLI_Ack_For_OT(); + /* The xHigherPriorityTaskWoken parameter must be initialized to pdFALSE as + it will get set to pdTRUE inside the interrupt safe API function if a + context switch is required. */ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + uint8_t NotUsed = 0; + xQueueSendToFrontFromISR(MoNotifQueue, &NotUsed, &xHigherPriorityTaskWoken); + + /* Pass the xHigherPriorityTaskWoken value into portEND_SWITCHING_ISR(). If + xHigherPriorityTaskWoken was set to pdTRUE inside xSemaphoreGiveFromISR() + then calling portEND_SWITCHING_ISR() will request a context switch. If + xHigherPriorityTaskWoken is still pdFALSE then calling + portEND_SWITCHING_ISR() will have no effect */ + portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); } /* USER CODE BEGIN FD_WRAP_FUNCTIONS */ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_thread.h b/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_thread.h index 512b3bcd544538..b4b31bfd25ad1c 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_thread.h +++ b/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/app_thread.h @@ -110,16 +110,12 @@ typedef enum /* USER CODE END EM */ /* Exported functions ------------------------------------------------------- */ -void APP_THREAD_Init(void); +void APP_THREAD_Init_Dyn_1(void); +void APP_THREAD_Init_Dyn_2(void); void APP_THREAD_Error(uint32_t ErrId, uint32_t ErrCode); void APP_THREAD_RegisterCmdBuffer(TL_CmdPacket_t * p_buffer); void APP_THREAD_ProcessMsgM0ToM4(void); -void APP_THREAD_Init_UART_CLI(void); void APP_THREAD_TL_THREAD_INIT(void); -void APP_THREAD_SEND_MSG(void); -/* **** */ -void APP_THREAD_Stop(void); -void APP_THREAD_CleanCallbacks(void); /* USER CODE BEGIN EF */ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/custom_stm.c b/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/custom_stm.c index a4e4a492cbc360..9a7479e48d9302 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/custom_stm.c +++ b/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/custom_stm.c @@ -1,6 +1,6 @@ /** ****************************************************************************** - * @file custom_stm.c + * @file matter_stm.c * @author MCD Application Team * @brief matter Service using gatt(Custom STM) ****************************************************************************** @@ -234,6 +234,11 @@ static SVCCTL_EvtAckStatus_t Matter_Event_Handler(void * Event) Notification.DataTransfered.Length = attribute_modified->Attr_Data_Length; Notification.DataTransfered.pPayload = attribute_modified->Attr_Data; Notification.ConnectionHandle = attribute_modified->Connection_Handle; + if (Notification.DataTransfered.Length == 0) + { + /* Exit the function because of bad packet */ + break; + } APP_MATTER_Notification(&Notification); } } diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/custom_stm.h b/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/custom_stm.h index a4627c05ae9223..17240d11e36cbe 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/custom_stm.h +++ b/examples/platform/stm32/common/STM32WB5MM-DK/STM32_WPAN/App/custom_stm.h @@ -28,7 +28,7 @@ extern "C" { /* Includes ------------------------------------------------------------------*/ /* USER CODE BEGIN Includes */ -#include + /* USER CODE END Includes */ /* Exported types ------------------------------------------------------------*/ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/app_debug.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/app_debug.c new file mode 100644 index 00000000000000..49c95488297abf --- /dev/null +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/app_debug.c @@ -0,0 +1,345 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file app_debug.c + * @author MCD Application Team + * @brief Debug capabilities source file for STM32WPAN Middleware + ****************************************************************************** + * @attention + * + * Copyright (c) 2020-2021 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Includes ------------------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +#include "app_common.h" + +#include "app_debug.h" +#include "dbg_trace.h" +#include "shci.h" +#include "tl.h" +#include "utilities_common.h" +/* USER CODE END Includes */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN PTD */ +typedef PACKED_STRUCT +{ + GPIO_TypeDef * port; + uint16_t pin; + uint8_t enable; + uint8_t reserved; +} +APPD_GpioConfig_t; +/* USER CODE END PTD */ + +/* Private defines -----------------------------------------------------------*/ +/* USER CODE BEGIN PD */ +#define GPIO_NBR_OF_RF_SIGNALS 9 +#define GPIO_CFG_NBR_OF_FEATURES 43 +#define NBR_OF_TRACES_CONFIG_PARAMETERS 4 +#define NBR_OF_GENERAL_CONFIG_PARAMETERS 4 + +/** + * THIS SHALL BE SET TO A VALUE DIFFERENT FROM 0 ONLY ON REQUEST FROM ST SUPPORT + */ +#define BLE_DTB_CFG 0 + +/** + * System Debug Options flags to be configured with: + * - SHCI_C2_DEBUG_OPTIONS_IPCORE_LP + * - SHCI_C2_DEBUG_OPTIONS_IPCORE_NO_LP + * - SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN + * - SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_DIS + * which are used to set following configuration bits: + * - bit 0: 0: IP BLE core in LP mode 1: IP BLE core in run mode (no LP supported) + * - bit 1: 0: CPU2 STOP mode Enable 1: CPU2 STOP mode Disable + * - bit [2-7]: bits reserved ( shall be set to 0) + */ +#define SYS_DBG_CFG1 (SHCI_C2_DEBUG_OPTIONS_IPCORE_LP | SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN) +/* USER CODE END PD */ + +/* Private macros ------------------------------------------------------------*/ +/* USER CODE BEGIN PM */ +/* USER CODE END PM */ + +/* Private variables ---------------------------------------------------------*/ +/* USER CODE BEGIN PV */ +PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = { 0, 0, 0, 0 }; +PLACE_IN_SECTION("MB_MEM2") +ALIGN(4) static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = { BLE_DTB_CFG, SYS_DBG_CFG1, { 0, 0 } }; + +#ifdef CFG_DEBUG_TRACE_UART +#if (CFG_HW_LPUART1_ENABLED == 1) +extern void MX_LPUART1_UART_Init(void); +#endif +#if (CFG_HW_USART1_ENABLED == 1) +extern void MX_USART1_UART_Init(void); +#endif +#endif + +/** + * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT + * It provides timing information on the CPU2 activity. + * All configuration of (port, pin) is supported for each features and can be selected by the user + * depending on the availability + */ +static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] = { + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* BLE_ISR - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* BLE_STACK_TICK - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* BLE_CMD_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* BLE_ACL_DATA_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* SYS_CMD_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* RNG_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* NVM_PROCESS - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_GENERAL - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_BLE_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_BLE_EVT_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_BLE_ACL_DATA_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_SYS_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_SYS_EVT_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_CLI_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_OT_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_OT_ACK_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_CLI_ACK_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_MEM_MANAGER_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_TRACES_TX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* HARD_FAULT - Set on Entry / Reset on Exit */ + /* From v1.1.1 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */ + /* From v1.2.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* TIMER_SERVER_CALLBACK - Toggle on Entry */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* PES_ACTIVITY - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */ + /* From v1.3.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* BLE_NO_DELAY - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* BLE_STACK_STORE_NVM_CB - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* NVMA_WRITE_ONGOING - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* NVMA_WRITE_COMPLETE - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* NVMA_CLEANUP - Set on Entry / Reset on Exit */ + /* From v1.4.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* NVMA_START - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, + /* FLASH_EOP - Set on Entry / Reset on Exit */ /* The FLASH_EOP Debug GPIO trace is not supported since v1.5.0 */ + /* From v1.5.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* FLASH_WRITE - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* FLASH_ERASE - Set on Entry / Reset on Exit */ + /* From v1.6.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* BLE_RESCHEDULE_EVENT - Set on Entry / Reset on Exit */ + /* From v1.8.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_BLE_LLD_CMD_RX - Set on Entry / Reset on Exit */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* IPCC_BLE_LLD_ACK_TX - Set on Entry / Reset on Exit */ + /* From v1.9.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* BLE_ASYNCH_EVENT_NACKED - Set on Entry / Reset on Exit */ + /* From v1.17.0 */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* DYNAMIC CONCURRENT - RTSM_SFTIMER_IRQ */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* DYNAMIC CONCURRENT - RTSM_COMPC_WRAP */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* DYNAMIC CONCURRENT - RTSM_SWITCH_RADIO */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* DYNAMIC CONCURRENT - RTSM_PROG_SFTIMER */ + { GPIOA, LL_GPIO_PIN_0, 0, 0 }, /* DYNAMIC CONCURRENT - RTSM_RADIO_GRANTED_TO_15_4 */ +}; + +/** + * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT + * This table is relevant only for BLE + * It provides timing information on BLE RF activity. + * New signals may be allocated at any location when requested by ST + * The GPIO allocated to each signal depend on the BLE_DTB_CFG value and cannot be changed + */ +#if (BLE_DTB_CFG == 7) +static const APPD_GpioConfig_t aRfConfigList[GPIO_NBR_OF_RF_SIGNALS] = { + { GPIOB, LL_GPIO_PIN_2, 0, 0 }, /* DTB10 - Tx/Rx SPI */ + { GPIOB, LL_GPIO_PIN_7, 0, 0 }, /* DTB11 - Tx/Tx SPI Clk */ + { GPIOA, LL_GPIO_PIN_8, 0, 0 }, /* DTB12 - Tx/Rx Ready & SPI Select */ + { GPIOA, LL_GPIO_PIN_9, 0, 0 }, /* DTB13 - Tx/Rx Start */ + { GPIOA, LL_GPIO_PIN_10, 0, 0 }, /* DTB14 - FSM0 */ + { GPIOA, LL_GPIO_PIN_11, 0, 0 }, /* DTB15 - FSM1 */ + { GPIOB, LL_GPIO_PIN_8, 0, 0 }, /* DTB16 - FSM2 */ + { GPIOB, LL_GPIO_PIN_11, 0, 0 }, /* DTB17 - FSM3 */ + { GPIOB, LL_GPIO_PIN_10, 0, 0 }, /* DTB18 - FSM4 */ +}; +#endif +/* USER CODE END PV */ + +/* Global variables ----------------------------------------------------------*/ +/* USER CODE BEGIN GV */ +/* USER CODE END GV */ + +/* Private function prototypes -----------------------------------------------*/ +/* USER CODE BEGIN PFP */ +static void APPD_SetCPU2GpioConfig(void); +static void APPD_BleDtbCfg(void); +/* USER CODE END PFP */ + +/* Functions Definition ------------------------------------------------------*/ +void APPD_Init(void) +{ + /* USER CODE BEGIN APPD_Init */ + APPD_SetCPU2GpioConfig(); + APPD_BleDtbCfg(); + + /* USER CODE END APPD_Init */ + return; +} + +void APPD_EnableCPU2(void) +{ + /* USER CODE BEGIN APPD_EnableCPU2 */ + SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket = { { { 0, 0, 0 } }, /**< Does not need to be initialized */ + { (uint8_t *) aGpioConfigList, (uint8_t *) &APPD_TracesConfig, + (uint8_t *) &APPD_GeneralConfig, GPIO_CFG_NBR_OF_FEATURES, + NBR_OF_TRACES_CONFIG_PARAMETERS, NBR_OF_GENERAL_CONFIG_PARAMETERS } }; + + /**< Traces channel initialization */ + TL_TRACES_Init(); + + /** GPIO DEBUG Initialization */ + SHCI_C2_DEBUG_Init(&DebugCmdPacket); + + /* USER CODE END APPD_EnableCPU2 */ + return; +} + +/************************************************************* + * + * LOCAL FUNCTIONS + * + *************************************************************/ +static void APPD_SetCPU2GpioConfig(void) +{ + /* USER CODE BEGIN APPD_SetCPU2GpioConfig */ + GPIO_InitTypeDef gpio_config = { 0 }; + uint8_t local_loop; + uint16_t gpioa_pin_list; + uint16_t gpiob_pin_list; + uint16_t gpioc_pin_list; + + gpioa_pin_list = 0; + gpiob_pin_list = 0; + gpioc_pin_list = 0; + + for (local_loop = 0; local_loop < GPIO_CFG_NBR_OF_FEATURES; local_loop++) + { + if (aGpioConfigList[local_loop].enable != 0) + { + switch ((uint32_t) aGpioConfigList[local_loop].port) + { + case (uint32_t) GPIOA: + gpioa_pin_list |= aGpioConfigList[local_loop].pin; + break; + + case (uint32_t) GPIOB: + gpiob_pin_list |= aGpioConfigList[local_loop].pin; + break; + + case (uint32_t) GPIOC: + gpioc_pin_list |= aGpioConfigList[local_loop].pin; + break; + + default: + break; + } + } + } + + gpio_config.Pull = GPIO_NOPULL; + gpio_config.Mode = GPIO_MODE_OUTPUT_PP; + gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + + if (gpioa_pin_list != 0) + { + gpio_config.Pin = gpioa_pin_list; + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_C2GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOA, &gpio_config); + HAL_GPIO_WritePin(GPIOA, gpioa_pin_list, GPIO_PIN_RESET); + } + + if (gpiob_pin_list != 0) + { + gpio_config.Pin = gpiob_pin_list; + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_C2GPIOB_CLK_ENABLE(); + HAL_GPIO_Init(GPIOB, &gpio_config); + HAL_GPIO_WritePin(GPIOB, gpiob_pin_list, GPIO_PIN_RESET); + } + + if (gpioc_pin_list != 0) + { + gpio_config.Pin = gpioc_pin_list; + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_C2GPIOC_CLK_ENABLE(); + HAL_GPIO_Init(GPIOC, &gpio_config); + HAL_GPIO_WritePin(GPIOC, gpioc_pin_list, GPIO_PIN_RESET); + } + + /* USER CODE END APPD_SetCPU2GpioConfig */ + return; +} + +static void APPD_BleDtbCfg(void) +{ +/* USER CODE BEGIN APPD_BleDtbCfg */ +#if (BLE_DTB_CFG != 0) + GPIO_InitTypeDef gpio_config = { 0 }; + uint8_t local_loop; + uint16_t gpioa_pin_list; + uint16_t gpiob_pin_list; + + gpioa_pin_list = 0; + gpiob_pin_list = 0; + + for (local_loop = 0; local_loop < GPIO_NBR_OF_RF_SIGNALS; local_loop++) + { + if (aRfConfigList[local_loop].enable != 0) + { + switch ((uint32_t) aRfConfigList[local_loop].port) + { + case (uint32_t) GPIOA: + gpioa_pin_list |= aRfConfigList[local_loop].pin; + break; + + case (uint32_t) GPIOB: + gpiob_pin_list |= aRfConfigList[local_loop].pin; + break; + + default: + break; + } + } + } + + gpio_config.Pull = GPIO_NOPULL; + gpio_config.Mode = GPIO_MODE_AF_PP; + gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + gpio_config.Alternate = GPIO_AF6_RF_DTB7; + + if (gpioa_pin_list != 0) + { + gpio_config.Pin = gpioa_pin_list; + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_C2GPIOA_CLK_ENABLE(); + HAL_GPIO_Init(GPIOA, &gpio_config); + } + + if (gpiob_pin_list != 0) + { + gpio_config.Pin = gpiob_pin_list; + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_C2GPIOB_CLK_ENABLE(); + HAL_GPIO_Init(GPIOB, &gpio_config); + } +#endif + + /* USER CODE END APPD_BleDtbCfg */ + return; +} diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/app_entry.cpp b/examples/platform/stm32/common/STM32WB5MM-DK/Src/app_entry.cpp index f631165467e749..cf0cb741c54f10 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Src/app_entry.cpp +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/app_entry.cpp @@ -19,12 +19,15 @@ /* Includes ------------------------------------------------------------------*/ #include "app_entry.h" +#include "AppTask.h" #include "app_ble.h" #include "app_common.h" #include "app_conf.h" +#include "app_debug.h" #include "app_thread.h" #include "cmsis_os.h" #include "dbg_trace.h" +#include "flash_wb.h" #include "hw_conf.h" #include "main.h" #include "shci.h" @@ -33,9 +36,9 @@ #include "stm32_lcd.h" #include "stm32_lpm.h" #include "stm32wb5mm_dk_lcd.h" +#include "stm_ext_flash.h" #include "stm_logging.h" -#include "AppTask.h" /* Private includes -----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ @@ -48,7 +51,7 @@ /* Private defines -----------------------------------------------------------*/ /* POOL_SIZE = 2(TL_PacketHeader_t) + 258 (3(TL_EVT_HDR_SIZE) + 255(Payload size)) */ -#define POOL_SIZE (CFG_TLBLE_EVT_QUEUE_LENGTH * 4 * DIVC((sizeof(TL_PacketHeader_t) + TL_BLE_EVENT_FRAME_SIZE), 4)) +#define POOL_SIZE (CFG_TLBLE_EVT_QUEUE_LENGTH * 4U * DIVC((sizeof(TL_PacketHeader_t) + TL_BLE_EVENT_FRAME_SIZE), 4U)) /* USER CODE BEGIN PD */ /* USER CODE END PD */ @@ -70,17 +73,11 @@ PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t SystemCmdBuffer; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t SystemSpareEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255]; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t BleSpareEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255]; uint8_t g_ot_notification_allowed = 0U; -/* Global variables ----------------------------------------------------------*/ -/* Global function prototypes -----------------------------------------------*/ -#if (CFG_DEBUG_TRACE != 0) -size_t DbgTraceWrite(int handle, const unsigned char * buf, size_t bufSize); -#endif - -/* USER CODE BEGIN GFP */ +/* Global variables ----------------------------------------------------------*/ +osMutexId_t MtxShciId; osSemaphoreId_t SemShciId; -osSemaphoreId_t SemShciUserEvtProcessId; -osThreadId_t OsShciUserEvtProcessId; +osThreadId_t ShciUserEvtProcessId; osThreadId_t OsPushButtonProcessId; const osThreadAttr_t ShciUserEvtProcess_attr = { .name = CFG_SHCI_USER_EVT_PROCESS_NAME, @@ -99,6 +96,13 @@ const osThreadAttr_t PushButtonProcess_attr = { .name = CFG_PUSH_BUTTON_EV .stack_size = CFG_PUSH_BUTTON_EVT_PROCESS_STACK_SIZE, .priority = CFG_PUSH_BUTTON_EVT_PROCESS_PRIORITY }; +/* Global function prototypes -----------------------------------------------*/ +#if (CFG_DEBUG_TRACE != 0) +size_t DbgTraceWrite(int handle, const unsigned char * buf, size_t bufSize); +#endif + +/* USER CODE BEGIN GFP */ + /* USER CODE END GFP */ /* Private functions prototypes-----------------------------------------------*/ @@ -108,6 +112,9 @@ static void APPE_SysStatusNot(SHCI_TL_CmdStatus_t status); static void APPE_SysUserEvtRx(void * pPayload); static void APPE_SysEvtReadyProcessing(void); static void APPE_SysEvtError(SCHI_SystemErrCode_t ErrorCode); +static void ShciUserEvtProcess(void * argument); +static void PushButtonEvtProcess(void * argument); + static void appe_Tl_Init(void); /* USER CODE BEGIN PFP */ static void Led_Init(void); @@ -115,12 +122,8 @@ static void Button_Init(void); #if (CFG_HW_EXTPA_ENABLED == 1) static void ExtPA_Init(void); #endif -static void ShciUserEvtProcess(void * argument); -static void PushButtonEvtProcess(void * argument); /* USER CODE END PFP */ -static void displayConcurrentMode(void); - // Callback function to handle pushbutton to apptask PushButtonCallback PbCb = NULL; @@ -128,7 +131,6 @@ void APP_ENTRY_PBSetReceiveCallback(PushButtonCallback aCallback) { PbCb = aCallback; } - /* Functions Definition ------------------------------------------------------*/ void APPE_Init(void) { @@ -142,8 +144,12 @@ void APPE_Init(void) /* initialize debugger module if supported and debug trace if activated */ Init_Debug(); - /* Display Dynamic concurrent mode (BLE and Thread) */ - displayConcurrentMode(); + // Init qspi and external flash + STM_EXT_FLASH_Init(); + // Init nvm + NM_Init(); + + APPD_Init(); /** * The Standby mode should not be entered before the initialization is over @@ -151,12 +157,7 @@ void APPE_Init(void) */ UTIL_LPM_SetOffMode(1 << CFG_LPM_APP, UTIL_LPM_DISABLE); - /** init freertos semaphore */ - SemShciId = osSemaphoreNew(1, 0, NULL); /*< Create the semaphore and make it busy at initialization */ - SemShciUserEvtProcessId = osSemaphoreNew(1, 0, NULL); /*< Create the semaphore and make it busy at initialization */ - OsShciUserEvtProcessId = osThreadNew(ShciUserEvtProcess, NULL, &ShciUserEvtProcess_attr); - OsPushButtonProcessId = osThreadNew(PushButtonEvtProcess, NULL, &PushButtonProcess_attr); - + OsPushButtonProcessId = osThreadNew(PushButtonEvtProcess, NULL, &PushButtonProcess_attr); Led_Init(); Button_Init(); @@ -164,6 +165,7 @@ void APPE_Init(void) /* Initialize all transport layers and start CPU2 which will send back a ready event to CPU1 */ appe_Tl_Init(); +#if (CFG_LCD_SUPPORTED == 1) BSP_LCD_Init(0, LCD_ORIENTATION_LANDSCAPE); /* Set LCD Foreground Layer */ UTIL_LCD_SetFuncDriver(&LCD_Driver); /* SetFunc before setting device */ @@ -177,9 +179,9 @@ void APPE_Init(void) UTIL_LCD_SetBackColor(SSD1315_COLOR_BLACK); BSP_LCD_Clear(0, SSD1315_COLOR_BLACK); BSP_LCD_Refresh(0); - UTIL_LCD_DisplayStringAt(0, 0, (uint8_t *) "Matter LightingApp", CENTER_MODE); + UTIL_LCD_DisplayStringAt(0, 0, (uint8_t *) APP_NAME, CENTER_MODE); BSP_LCD_Refresh(0); - +#endif /** * From now, the application is waiting for the ready event ( VS_HCI_C2_Ready ) * received on the system channel before starting the Stack @@ -194,11 +196,6 @@ void APPE_Init(void) return; } -static void displayConcurrentMode() -{ - APP_DBG("Matter Over Thread Lighting-App starting..."); -} - /************************************************************* * * LOCAL FUNCTIONS @@ -292,6 +289,12 @@ static void appe_Tl_Init(void) /**< Reference table initialization */ TL_Init(); + MtxShciId = osMutexNew(NULL); + SemShciId = osSemaphoreNew(1, 0, NULL); /*< Create the semaphore and make it busy at initialization */ + + /** FreeRTOS system task creation */ + ShciUserEvtProcessId = osThreadNew(ShciUserEvtProcess, NULL, &ShciUserEvtProcess_attr); + /**< System channel initialization */ SHci_Tl_Init_Conf.p_cmdbuffer = (uint8_t *) &SystemCmdBuffer; SHci_Tl_Init_Conf.StatusNotCallBack = APPE_SysStatusNot; @@ -311,7 +314,19 @@ static void appe_Tl_Init(void) static void APPE_SysStatusNot(SHCI_TL_CmdStatus_t status) { - UNUSED(status); + switch (status) + { + case SHCI_TL_CmdBusy: + osMutexAcquire(MtxShciId, osWaitForever); + break; + + case SHCI_TL_CmdAvailable: + osMutexRelease(MtxShciId); + break; + + default: + break; + } return; } @@ -321,7 +336,7 @@ static void APPE_SysStatusNot(SHCI_TL_CmdStatus_t status) * - a ready event (subevtcode = SHCI_SUB_EVT_CODE_READY) * - reported by the FUS (sysevt_ready_rsp == FUS_FW_RUNNING) * The buffer shall not be released - * ( eg ((tSHCI_UserEvtRxParam*)pPayload)->status shall be set to SHCI_TL_UserEventFlow_Disable ) + * (eg ((tSHCI_UserEvtRxParam*)pPayload)->status shall be set to SHCI_TL_UserEventFlow_Disable ) * When the status is not filled, the buffer is released by default */ static void APPE_SysUserEvtRx(void * pPayload) @@ -373,31 +388,64 @@ static void APPE_SysEvtError(SCHI_SystemErrCode_t ErrorCode) static void APPE_SysEvtReadyProcessing(void) { /* Traces channel initialization */ - TL_TRACES_Init(); + APPD_EnableCPU2(); + /* Configuration to CPU2 */ + SHCI_C2_CONFIG_Cmd_Param_t config_param = { 0 }; + uint32_t Ot_NVMAddr = 0; /* In the Context of Dynamic Concurrent mode, the Init and start of each stack must be split and executed * in the following order : - * APP_BLE_Init : BLE Stack Init until it's ready to start ADV + * APP_BLE_Init_Dyn_1() : BLE Stack Init until it's ready to start ADV * APP_THREAD_Init_Dyn_1() : Thread Stack Init until it's ready to be configured (default channel, PID, etc...) + * APP_BLE_Init_Dyn_2() : Start ADV + * APP_THREAD_Init_Dyn_2() : Thread Stack configuration (default channel, PID, etc...) to be able to start scanning + * or joining a Thread Network */ APP_DBG("1- Initialisation of BLE Stack..."); APP_BLE_Init_Dyn_1(); + /* Set the address that will be used by OT stack for NVM data management */ + if (NM_GetOtNVMAddr(&Ot_NVMAddr) == NVM_OK) + { + config_param.ThreadNvmRamAddress = Ot_NVMAddr; + (void) SHCI_C2_Config(&config_param); + } + APP_DBG("2- Initialisation of OpenThread Stack. FW info :"); - APP_THREAD_Init(); + APP_THREAD_Init_Dyn_1(); APP_BLE_Init_Dyn_2(); - + APP_THREAD_Init_Dyn_2(); APP_DBG("Start init matter"); GetAppTask().StartAppTask(); - #if (CFG_LPM_SUPPORTED == 1) /* Thread stack is initialized, low power mode can be enabled */ UTIL_LPM_SetOffMode(1U << CFG_LPM_APP, UTIL_LPM_ENABLE); UTIL_LPM_SetStopMode(1U << CFG_LPM_APP, UTIL_LPM_ENABLE); #endif - return; } +/************************************************************* + * + * FREERTOS WRAPPER FUNCTIONS + * + *************************************************************/ +static void ShciUserEvtProcess(void * argument) +{ + UNUSED(argument); + for (;;) + { + /* USER CODE BEGIN SHCI_USER_EVT_PROCESS_1 */ + + /* USER CODE END SHCI_USER_EVT_PROCESS_1 */ + osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever); + shci_user_evt_proc(); + /* USER CODE BEGIN SHCI_USER_EVT_PROCESS_2 */ + + /* USER CODE END SHCI_USER_EVT_PROCESS_2 */ + } +} + +/* USER CODE BEGIN FD_LOCAL_FUNCTIONS */ static void Led_Init(void) { #if (CFG_LED_SUPPORTED == 1U) @@ -413,12 +461,14 @@ static void Led_Init(void) static void Button_Init(void) { -#if (CFG_BUTTON_SUPPORTED == 1U) +#if ((CFG_BUTTON_SUPPORTED == 1U) || (CFG_HW_EXTPA_ENABLED == 1)) /** * Button Initialization */ BSP_PB_Init(BUTTON_USER1, BUTTON_MODE_EXTI); + BSP_PB_Init(BUTTON_USER2, BUTTON_MODE_EXTI); + #endif return; @@ -457,15 +507,24 @@ static void ExtPA_Init(void) static void PushButtonEvtProcess(void * argument) { UNUSED(argument); + uint32_t ButtonPressed = 0; + for (;;) { /* USER CODE BEGIN SHCI_USER_EVT_PROCESS_1 */ - /* USER CODE END SHCI_USER_EVT_PROCESS_1 */ - osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever); + ButtonPressed = osThreadFlagsWait(3, osFlagsWaitAny, osWaitForever); Push_Button_st Message; - Message.Pushed_Button = BUTTON_USER1; - Message.State = 1; + if (1 == ButtonPressed) + { + Message.Pushed_Button = BUTTON_USER1; + Message.State = 1; + } + if (2 == ButtonPressed) + { + Message.Pushed_Button = BUTTON_USER2; + Message.State = 2; + } PbCb(&Message); // call matter callback to handle push button /* USER CODE BEGIN SHCI_USER_EVT_PROCESS_2 */ @@ -473,27 +532,10 @@ static void PushButtonEvtProcess(void * argument) } } -static void ShciUserEvtProcess(void * argument) -{ - UNUSED(argument); - for (;;) - { - /* USER CODE BEGIN SHCI_USER_EVT_PROCESS_1 */ - - /* USER CODE END SHCI_USER_EVT_PROCESS_1 */ - // osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever); - osSemaphoreAcquire(SemShciUserEvtProcessId, osWaitForever); - shci_user_evt_proc(); - /* USER CODE BEGIN SHCI_USER_EVT_PROCESS_2 */ - - /* USER CODE END SHCI_USER_EVT_PROCESS_2 */ - } -} - void shci_notify_asynch_evt(void * pdata) { UNUSED(pdata); - osSemaphoreRelease(SemShciUserEvtProcessId); + osThreadFlagsSet(ShciUserEvtProcessId, 1); return; } @@ -506,8 +548,7 @@ void shci_cmd_resp_release(uint32_t flag) void shci_cmd_resp_wait(uint32_t timeout) { - UNUSED(timeout); - osSemaphoreAcquire(SemShciId, osWaitForever); + osSemaphoreAcquire(SemShciId, pdMS_TO_TICKS(timeout)); return; } @@ -569,7 +610,7 @@ void BSP_PB_Callback(Button_TypeDef Button) case BUTTON_USER2: APP_DBG("BUTTON 2 PUSHED !"); - /* Set "Switch Protocol" Task */ + osThreadFlagsSet(OsPushButtonProcessId, 2); break; default: @@ -578,6 +619,7 @@ void BSP_PB_Callback(Button_TypeDef Button) return; } + #ifdef __cplusplus } #endif diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/entropy_hardware_poll.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/entropy_hardware_poll.c index db25dccdf97cf0..3adaf861b574a3 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Src/entropy_hardware_poll.c +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/entropy_hardware_poll.c @@ -1,4 +1,3 @@ - /* USER CODE BEGIN Header */ /** ****************************************************************************** diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/flash_driver.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/flash_driver.c deleted file mode 100644 index fcf4534a327003..00000000000000 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Src/flash_driver.c +++ /dev/null @@ -1,322 +0,0 @@ -/** - ****************************************************************************** - * @file : flash_driver.c - * @author : MCD Application Team - * @brief : Dual core Flash driver - ****************************************************************************** - * @attention - * - * Copyright (c) 2019-2021 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "flash_driver.h" -#include "app_common.h" -#include "main.h" -#include "shci.h" -#include "utilities_conf.h" - -/* Private typedef -----------------------------------------------------------*/ -typedef enum -{ - SEM_LOCK_SUCCESSFUL, - SEM_LOCK_BUSY, -} SemStatus_t; - -typedef enum -{ - FLASH_ERASE, - FLASH_WRITE, -} FlashOperationType_t; - -/* Private defines -----------------------------------------------------------*/ -/* Private macros ------------------------------------------------------------*/ -/* Private variables ---------------------------------------------------------*/ -/* Global variables ----------------------------------------------------------*/ -/* Private function prototypes -----------------------------------------------*/ -static SingleFlashOperationStatus_t ProcessSingleFlashOperation(FlashOperationType_t FlashOperationType, - uint32_t SectorNumberOrDestAddress, uint64_t Data); -/* Public functions ----------------------------------------------------------*/ -uint32_t FD_EraseSectors(uint32_t FirstSector, uint32_t NbrOfSectors) -{ - uint32_t loop_flash; - uint32_t return_value; - SingleFlashOperationStatus_t single_flash_operation_status; - - single_flash_operation_status = SINGLE_FLASH_OPERATION_DONE; - - /** - * Take the semaphore to take ownership of the Flash IP - */ - while (LL_HSEM_1StepLock(HSEM, CFG_HW_FLASH_SEMID)) - ; - - HAL_FLASH_Unlock(); - - /** - * Notify the CPU2 that some flash erase activity may be executed - * On reception of this command, the CPU2 enables the BLE timing protection versus flash erase processing - * The Erase flash activity will be executed only when the BLE RF is idle for at least 25ms - * The CPU2 will prevent all flash activity (write or erase) in all cases when the BL RF Idle is shorter than 25ms. - */ - SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON); - - for (loop_flash = 0; (loop_flash < NbrOfSectors) && (single_flash_operation_status == SINGLE_FLASH_OPERATION_DONE); - loop_flash++) - { - single_flash_operation_status = FD_EraseSingleSector(FirstSector + loop_flash); - } - - if (single_flash_operation_status != SINGLE_FLASH_OPERATION_DONE) - { - return_value = NbrOfSectors - loop_flash + 1; - } - else - { - /** - * Notify the CPU2 there will be no request anymore to erase the flash - * On reception of this command, the CPU2 will disables the BLE timing protection versus flash erase processing - * The protection is active until next end of radio event. - */ - SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF); - - HAL_FLASH_Lock(); - - /** - * Release the ownership of the Flash IP - */ - LL_HSEM_ReleaseLock(HSEM, CFG_HW_FLASH_SEMID, 0); - - return_value = 0; - } - - return return_value; -} - -uint32_t FD_WriteData(uint32_t DestAddress, uint64_t * pSrcBuffer, uint32_t NbrOfData) -{ - uint32_t loop_flash; - uint32_t return_value; - SingleFlashOperationStatus_t single_flash_operation_status; - - single_flash_operation_status = SINGLE_FLASH_OPERATION_DONE; - - /** - * Take the semaphore to take ownership of the Flash IP - */ - while (LL_HSEM_1StepLock(HSEM, CFG_HW_FLASH_SEMID)) - ; - - HAL_FLASH_Unlock(); - - for (loop_flash = 0; (loop_flash < NbrOfData) && (single_flash_operation_status == SINGLE_FLASH_OPERATION_DONE); loop_flash++) - { - single_flash_operation_status = FD_WriteSingleData(DestAddress + (8 * loop_flash), *(pSrcBuffer + loop_flash)); - } - - if (single_flash_operation_status != SINGLE_FLASH_OPERATION_DONE) - { - return_value = NbrOfData - loop_flash + 1; - } - else - { - HAL_FLASH_Lock(); - - /** - * Release the ownership of the Flash IP - */ - LL_HSEM_ReleaseLock(HSEM, CFG_HW_FLASH_SEMID, 0); - - return_value = 0; - } - - return return_value; -} - -SingleFlashOperationStatus_t FD_EraseSingleSector(uint32_t SectorNumber) -{ - SingleFlashOperationStatus_t return_value; - - /* The last parameter is unused in that case and set to 0 */ - return_value = ProcessSingleFlashOperation(FLASH_ERASE, SectorNumber, 0); - - return return_value; -} - -SingleFlashOperationStatus_t FD_WriteSingleData(uint32_t DestAddress, uint64_t Data) -{ - SingleFlashOperationStatus_t return_value; - - return_value = ProcessSingleFlashOperation(FLASH_WRITE, DestAddress, Data); - - return return_value; -} - -/************************************************************* - * - * LOCAL FUNCTIONS - * - *************************************************************/ -static SingleFlashOperationStatus_t ProcessSingleFlashOperation(FlashOperationType_t FlashOperationType, - uint32_t SectorNumberOrDestAddress, uint64_t Data) -{ - SemStatus_t cpu1_sem_status; - SemStatus_t cpu2_sem_status; - WaitedSemStatus_t waited_sem_status; - SingleFlashOperationStatus_t return_status; - - uint32_t page_error; - FLASH_EraseInitTypeDef p_erase_init; - - waited_sem_status = WAITED_SEM_FREE; - - p_erase_init.TypeErase = FLASH_TYPEERASE_PAGES; - p_erase_init.NbPages = 1; - p_erase_init.Page = SectorNumberOrDestAddress; - - do - { - /** - * When the PESD bit mechanism is used by CPU2 to protect its timing, the PESD bit should be polled here. - * If the PESD is set, the CPU1 will be stalled when reading literals from an ISR that may occur after - * the flash processing has been requested but suspended due to the PESD bit. - * - * Note: This code is required only when the PESD mechanism is used to protect the CPU2 timing. - * However, keeping that code make it compatible with the two mechanisms. - */ - while (LL_FLASH_IsActiveFlag_OperationSuspended()) - ; - - UTILS_ENTER_CRITICAL_SECTION(); - - /** - * Depending on the application implementation, in case a multitasking is possible with an OS, - * it should be checked here if another task in the application disallowed flash processing to protect - * some latency in critical code execution - * When flash processing is ongoing, the CPU cannot access the flash anymore. - * Trying to access the flash during that time stalls the CPU. - * The only way for CPU1 to disallow flash processing is to take CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID. - */ - cpu1_sem_status = (SemStatus_t) LL_HSEM_GetStatus(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID); - if (cpu1_sem_status == SEM_LOCK_SUCCESSFUL) - { - /** - * Check now if the CPU2 disallows flash processing to protect its timing. - * If the semaphore is locked, the CPU2 does not allow flash processing - * - * Note: By default, the CPU2 uses the PESD mechanism to protect its timing, - * therefore, it is useless to get/release the semaphore. - * - * However, keeping that code make it compatible with the two mechanisms. - * The protection by semaphore is enabled on CPU2 side with the command SHCI_C2_SetFlashActivityControl() - * - */ - cpu2_sem_status = (SemStatus_t) LL_HSEM_1StepLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID); - if (cpu2_sem_status == SEM_LOCK_SUCCESSFUL) - { - /** - * When CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID is taken, it is allowed to only erase one sector or - * write one single 64bits data - * When either several sectors need to be erased or several 64bits data need to be written, - * the application shall first exit from the critical section and try again. - */ - if (FlashOperationType == FLASH_ERASE) - { - HAL_FLASHEx_Erase(&p_erase_init, &page_error); - } - else - { - HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, SectorNumberOrDestAddress, Data); - } - /** - * Release the semaphore to give the opportunity to CPU2 to protect its timing versus the next flash operation - * by taking this semaphore. - * Note that the CPU2 is polling on this semaphore so CPU1 shall release it as fast as possible. - * This is why this code is protected by a critical section. - */ - LL_HSEM_ReleaseLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID, 0); - } - } - - UTILS_EXIT_CRITICAL_SECTION(); - - if (cpu1_sem_status != SEM_LOCK_SUCCESSFUL) - { - /** - * To avoid looping in ProcessSingleFlashOperation(), FD_WaitForSemAvailable() should implement a mechanism to - * continue only when CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID is free - */ - waited_sem_status = FD_WaitForSemAvailable(WAIT_FOR_SEM_BLOCK_FLASH_REQ_BY_CPU1); - } - else if (cpu2_sem_status != SEM_LOCK_SUCCESSFUL) - { - /** - * To avoid looping in ProcessSingleFlashOperation(), FD_WaitForSemAvailable() should implement a mechanism to - * continue only when CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID is free - */ - waited_sem_status = FD_WaitForSemAvailable(WAIT_FOR_SEM_BLOCK_FLASH_REQ_BY_CPU2); - } - } while (((cpu2_sem_status != SEM_LOCK_SUCCESSFUL) || (cpu1_sem_status != SEM_LOCK_SUCCESSFUL)) && - (waited_sem_status != WAITED_SEM_BUSY)); - - /** - * In most BLE application, the flash should not be blocked by the CPU2 longer than FLASH_TIMEOUT_VALUE (1000ms) - * However, it could be that for some marginal application, this time is longer. - * In that case either HAL_FLASHEx_Erase() or HAL_FLASH_Program() will exit with FLASH_TIMEOUT_VALUE value. - * This is not a failing case and there is no other way than waiting the operation to be completed. - * If for any reason this test is never passed, this means there is a failure in the system and there is no other - * way to recover than applying a device reset. - * - * Note: This code is required only when the PESD mechanism is used to protect the CPU2 timing. - * However, keeping that code make it compatible with the two mechanisms. - */ - while (__HAL_FLASH_GET_FLAG(FLASH_FLAG_CFGBSY)) - ; - - if (waited_sem_status != WAITED_SEM_BUSY) - { - /** - * The flash processing has been done. It has not been checked whether it has been successful or not. - * The only commitment is that it is possible to request a new flash processing - */ - return_status = SINGLE_FLASH_OPERATION_DONE; - } - else - { - /** - * The flash processing has not been executed due to timing protection from either the CPU1 or the CPU2. - * This status is reported up to the user that should retry after checking that each CPU do not - * protect its timing anymore. - */ - return_status = SINGLE_FLASH_OPERATION_NOT_EXECUTED; - } - - return return_status; -} - -/************************************************************* - * - * WEAK FUNCTIONS - * - *************************************************************/ -__WEAK WaitedSemStatus_t FD_WaitForSemAvailable(WaitedSemId_t WaitedSemId) -{ - /** - * The timing protection is enabled by either CPU1 or CPU2. It should be decided here if the driver shall - * keep trying to erase/write the flash until successful or if it shall exit and report to the user that the action - * has not been executed. - * WAITED_SEM_BUSY returns to the user - * WAITED_SEM_FREE keep looping in the driver until the action is executed. This will result in the current stack looping - * until this is done. In a bare metal implementation, only the code within interrupt handler can be executed. With an OS, - * only task with higher priority can be processed - * - */ - return WAITED_SEM_BUSY; -} diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/flash_wb.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/flash_wb.c index 4c24d6f71c9103..6fb60c16f87609 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Src/flash_wb.c +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/flash_wb.c @@ -19,29 +19,36 @@ /* Includes ------------------------------------------------------------------*/ #include "flash_wb.h" -#include "flash_driver.h" +#include "stm_ext_flash.h" + +#if (OTA_SUPPORT == 1) +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) +#include "mapping_fwimg.h" +#include "mapping_sbsfu.h" +#elif defined(__ICCARM__) || defined(__GNUC__) +#include "mapping_export.h" +#endif /* __CC_ARM || __ARMCC_VERSION */ +#endif #include #include #include /* Private defines -----------------------------------------------------------*/ -#define MATTER_KEY_NAME_MAX_LENGTH (15 * 2) // ADD Max key name string size is 30 "keyType...;KeyName..." -// ^ STM32STORE_MAX_KEY_SIZE +#if (OTA_SUPPORT == 1) +#define NVM_MATTER_ADDR_INIT_SECURE BACKUP_END + 1 // start after back up slot +#else +#define NVM_MATTER_ADDR_INIT_SECURE EXTERNAL_FLASH_ADDRESS +#endif +// #define NVM_MATTER_ADDR_INIT_SECURE EXTERNAL_FLASH_ADDRESS + OTA_MAX_SIZE // start after back up slot +#define MATTER_KEY_NAME_MAX_LENGTH (16 * 2) // ADD Max key name string size is 32 "keyType...;KeyName..." #define NVM_OFFSET_KEY 512 -#define NVM_END_FLASH #define NVM_BLOCK_SIZE NVM_OFFSET_KEY -#define FLASH_START 0x08000000 #define DEFAULT_VALUE 0xFF -#define NB_SECTOR 3 -#define NVM_MATTER_ADDR_INIT_SECURE 0x08082000 -#define NVM_MATTER_ADDR_INIT_SECURE_PTR ((void * const) NVM_MATTER_ADDR_INIT_SECURE) -#define SECTOR_SIZE_SECURE 4096 * 2 +#define SECTOR_SIZE_SECURE 4096 * 5 #define NVM_MATTER_ADDR_INIT_NO_SECURE NVM_MATTER_ADDR_INIT_SECURE + SECTOR_SIZE_SECURE -#define NVM_MATTER_ADDR_INIT_NOSECURE_PTR ((void * const) NVM_MATTER_ADDR_INIT_NO_SECURE) #define SECTOR_SIZE_NO_SECURE 4096 #define NVM_SIZE_FLASH (SECTOR_SIZE_SECURE + SECTOR_SIZE_NO_SECURE) -#define NVM_MAX_KEY NVM_SIZE_FLASH / NVM_OFFSET_KEY typedef struct { @@ -63,7 +70,6 @@ const NVM_Sector_Struct sector_no_secure = { .id_sector = SECTOR_NO_SECURE, //*SIMULATE TO EXAMPLE* const NVM_Sector_Struct sector_secure = { .id_sector = SECTOR_SECURE, .ram_ptr = ram_nvm, .sector_size = SECTOR_SIZE_SECURE }; -uint8_t CheckSanity = 0; /* Global variables ----------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ @@ -72,28 +78,17 @@ static uint8_t flash_update(const NVM_Sector_Struct select_sector, uint8_t * Key static NVM_StatusTypeDef flash_replace(const NVM_Sector_Struct select_sector, uint8_t * PtKeyfind, uint8_t * KeyName, uint8_t * KeyValue, size_t KeySize); static NVM_StatusTypeDef flash_write(uint8_t * PtKeyFree, uint8_t * key, uint8_t * value, size_t value_size); -static uint8_t * SearchKey(uint8_t * PtPage, uint8_t * KeyName); +static uint8_t * SearchKey(uint8_t * PtPage, uint8_t * KeyName, size_t nvm_size); static NVM_StatusTypeDef delete_key(const NVM_Sector_Struct select_sector, uint8_t * PtkeyFind); /* Public functions ----------------------------------------------------------*/ -void NM_Init(void) -{ - // Copy Nvm flash to ram, it used one time for boot - // copy no secure nvm to no secure ram - memcpy(sector_no_secure.ram_ptr, NVM_MATTER_ADDR_INIT_NOSECURE_PTR, sector_no_secure.sector_size); - - // copy secure nvm to secure ram *SIMULATE TO EXAMPLE* - memcpy(sector_secure.ram_ptr, NVM_MATTER_ADDR_INIT_SECURE_PTR, sector_secure.sector_size); -} - -NVM_StatusTypeDef NM_Check_Validity(void) +NVM_StatusTypeDef NM_Init(void) { NVM_StatusTypeDef err = NVM_OK; - if (CheckSanity != 0) - { - err = NVM_FLASH_CORRUPTION; - } + + memset(ram_nvm, DEFAULT_VALUE, sizeof(ram_nvm)); + err = STM_EXT_FLASH_ReadChunk(NVM_MATTER_ADDR_INIT_SECURE, ram_nvm, sizeof(ram_nvm)); return err; } @@ -101,24 +96,15 @@ NVM_StatusTypeDef NM_Dump(void) { NVM_StatusTypeDef err = NVM_DELETE_FAILED; - err = FD_EraseSectors((NVM_MATTER_ADDR_INIT_SECURE - FLASH_START) / (NVM_SIZE_FLASH / NB_SECTOR), NB_SECTOR); - if (err == 0) + if (STM_EXT_FLASH_Delete_Image(NVM_MATTER_ADDR_INIT_SECURE, sizeof(ram_nvm)) == STM_EXT_FLASH_OK) { - err = FD_WriteData(NVM_MATTER_ADDR_INIT_SECURE, (uint64_t *) ram_nvm, (uint32_t) (NVM_SIZE_FLASH / sizeof(uint64_t))); - if (err != 0) + if (STM_EXT_FLASH_WriteChunk(NVM_MATTER_ADDR_INIT_SECURE, ram_nvm, sizeof(ram_nvm)) == STM_EXT_FLASH_OK) { - err = NVM_WRITE_FAILED; + err = NVM_OK; } else { - if (memcmp(ram_nvm, (void *) NVM_MATTER_ADDR_INIT_SECURE, (size_t) NVM_SIZE_FLASH)) - { - err = NVM_WRITE_FAILED; - } - else - { - err = NVM_OK; - } + err = NVM_WRITE_FAILED; } } return err; @@ -127,6 +113,10 @@ NVM_StatusTypeDef NM_Dump(void) NVM_StatusTypeDef NM_GetKeyValue(void * KeyValue, const char * KeyName, uint32_t KeySize, size_t * read_by_size, NVM_Sector sector) { + if ((KeyValue == NULL) || (read_by_size == NULL)) + { + return NVM_PARAM_ERROR; + } NVM_Sector_Struct select_nvm = { 0 }; switch (sector) { @@ -142,7 +132,7 @@ NVM_StatusTypeDef NM_GetKeyValue(void * KeyValue, const char * KeyName, uint32_t return NVM_WRITE_FAILED; } - uint8_t * key_search = SearchKey(select_nvm.ram_ptr, (uint8_t *) KeyName); + uint8_t * key_search = SearchKey(select_nvm.ram_ptr, (uint8_t *) KeyName, select_nvm.sector_size); if (key_search != NULL) { // copy Keyname's value in KeyValue and copy the size of KeyValue in read_by_size @@ -151,9 +141,23 @@ NVM_StatusTypeDef NM_GetKeyValue(void * KeyValue, const char * KeyName, uint32_t return NVM_KEY_NOT_FOUND; } +NVM_StatusTypeDef NM_GetOtNVMAddr(uint32_t * NVMAddr) +{ + if (NVMAddr == NULL) + { + return NVM_PARAM_ERROR; + } + *NVMAddr = (uint32_t) ram_nvm + SECTOR_SIZE_SECURE; + return NVM_OK; +} + NVM_StatusTypeDef NM_SetKeyValue(char * KeyValue, char * KeyName, uint32_t KeySize, NVM_Sector sector) { + if ((KeyValue == NULL) || (KeyName == NULL)) + { + return NVM_PARAM_ERROR; + } NVM_Sector_Struct select_nvm = { 0 }; void * Ptkey = NULL; @@ -176,7 +180,7 @@ NVM_StatusTypeDef NM_SetKeyValue(char * KeyValue, char * KeyName, uint32_t KeySi return NVM_BLOCK_SIZE_OVERFLOW; } // call function to search the pointer of key if it exist else return null - Ptkey = SearchKey(select_nvm.ram_ptr, (uint8_t *) KeyName); + Ptkey = SearchKey(select_nvm.ram_ptr, (uint8_t *) KeyName, select_nvm.sector_size); if (Ptkey == NULL) { @@ -192,9 +196,13 @@ NVM_StatusTypeDef NM_SetKeyValue(char * KeyValue, char * KeyName, uint32_t KeySi return NVM_WRITE_FAILED; } -uint8_t NM_DeleteKey(const char * Keyname, NVM_Sector sector) +NVM_StatusTypeDef NM_DeleteKey(const char * Keyname, NVM_Sector sector) { + if (Keyname == NULL) + { + return NVM_PARAM_ERROR; + } NVM_Sector_Struct select_nvm = { 0 }; switch (sector) { @@ -209,7 +217,7 @@ uint8_t NM_DeleteKey(const char * Keyname, NVM_Sector sector) default: return NVM_WRITE_FAILED; } - uint8_t * Ptkey = SearchKey(select_nvm.ram_ptr, (uint8_t *) Keyname); + uint8_t * Ptkey = SearchKey(select_nvm.ram_ptr, (uint8_t *) Keyname, select_nvm.sector_size); if (Ptkey != NULL) { return delete_key(select_nvm, Ptkey); @@ -221,7 +229,7 @@ void NM_ResetFactory(void) { while (1) { - FD_EraseSectors((NVM_MATTER_ADDR_INIT_SECURE - FLASH_START) / (NVM_SIZE_FLASH / NB_SECTOR), NB_SECTOR); + STM_EXT_FLASH_Delete_Image(NVM_MATTER_ADDR_INIT_SECURE, sizeof(ram_nvm)); NVIC_SystemReset(); } } @@ -232,24 +240,29 @@ void NM_ResetFactory(void) * *************************************************************/ -static uint8_t * SearchKey(uint8_t * PtPage, uint8_t * KeyName) +static uint8_t * SearchKey(uint8_t * PtPage, uint8_t * KeyName, size_t nvm_size) { uint8_t * i = PtPage; size_t read_by_size = 0; - while ((i >= PtPage) || (i < (PtPage + NVM_SIZE_FLASH))) + while ((i >= PtPage) || (i < (PtPage + nvm_size))) { if (*i != DEFAULT_VALUE) { - if (strcmp((char *) KeyName, (char *) i) == 0) + if (strcmp(KeyName, (uint8_t *) i) == 0) { return i; } read_by_size = *(size_t *) ((uint8_t *) i + MATTER_KEY_NAME_MAX_LENGTH); + // ensure that the size of the data being read does not exceed the remaining size of the buffer + if (read_by_size + sizeof(size_t) + MATTER_KEY_NAME_MAX_LENGTH > nvm_size - (i - PtPage)) + { + return NULL; + } i += read_by_size + sizeof(size_t) + MATTER_KEY_NAME_MAX_LENGTH; // Flash is corrupted - if ((i < PtPage) || (i > (PtPage + NVM_SIZE_FLASH))) + if ((i < PtPage) || (i > (PtPage + nvm_size))) { NM_ResetFactory(); } @@ -327,14 +340,14 @@ static NVM_StatusTypeDef delete_key(const NVM_Sector_Struct select_sector, uint8 size_key = *(size_t *) ((uint8_t *) PtkeyFind + MATTER_KEY_NAME_MAX_LENGTH); PtKeyNext = PtkeyFind + size_key + MATTER_KEY_NAME_MAX_LENGTH + sizeof(size_key); PtKeyCpy = PtkeyFind; - while ((*PtKeyNext != 0xFF) && (PtKeyNext < (ram_nvm + NVM_SIZE_FLASH))) + while ((*PtKeyNext != 0xFF) && (PtKeyNext < (select_sector.ram_ptr + select_sector.sector_size))) { size_key = *(size_t *) ((uint8_t *) PtKeyNext + MATTER_KEY_NAME_MAX_LENGTH); memcpy(PtKeyCpy, PtKeyNext, size_key + sizeof(size_t) + MATTER_KEY_NAME_MAX_LENGTH); PtKeyCpy += size_key + sizeof(size_t) + MATTER_KEY_NAME_MAX_LENGTH; PtKeyNext += size_key + MATTER_KEY_NAME_MAX_LENGTH + sizeof(size_key); } - memset(PtKeyCpy, DEFAULT_VALUE, (ram_nvm + NVM_SIZE_FLASH - PtKeyCpy)); + memset(PtKeyCpy, DEFAULT_VALUE, (select_sector.ram_ptr + select_sector.sector_size - PtKeyCpy)); return NVM_OK; } return NVM_DELETE_FAILED; diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/main.cpp b/examples/platform/stm32/common/STM32WB5MM-DK/Src/main.cpp index a48e8b88dc6101..06481af913c99c 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Src/main.cpp +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/main.cpp @@ -45,9 +45,7 @@ #include "app_thread.h" #include "cmsis_os.h" #include "dbg_trace.h" -#include "flash_wb.h" #include "stm32_lpm.h" - /* Private typedef -----------------------------------------------------------*/ /* Private defines -----------------------------------------------------------*/ /* Private macros ------------------------------------------------------------*/ @@ -96,6 +94,7 @@ int main(void) * The OPTVERR flag is wrongly set at power on * It shall be cleared before using any HAL_FLASH_xxx() api */ + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR); /** @@ -126,9 +125,7 @@ int main(void) MX_GPIO_Init(); /* IPCC initialisation */ MX_IPCC_Init(); - NM_Init(); freertos_mbedtls_init(); - APPE_Init(); GetAppTask().InitMatter(); osKernelStart(); diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/ota.cpp b/examples/platform/stm32/common/STM32WB5MM-DK/Src/ota.cpp new file mode 100644 index 00000000000000..7af8c69807fbf7 --- /dev/null +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/ota.cpp @@ -0,0 +1,113 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/***************************************************************************** + * Includes Definitions + *****************************************************************************/ + +#if (OTA_SUPPORT == 1) +#include "ota.h" +#include + +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::DeviceLayer; + +/***************************************************************************** + * Static Data Definitions + *****************************************************************************/ + +DefaultOTARequestor gRequestorCore; +DefaultOTARequestorStorage gRequestorStorage; +DefaultOTARequestorDriver gRequestorUser; +BDXDownloader gDownloader; +OTAImageProcessorImpl gImageProcessor; +FactoryDataProvider gFactoryDataProvider; +chip::ota::DefaultOTARequestorUserConsent gUserConsentProvider; +static chip::ota::UserConsentState gUserConsentState = chip::ota::UserConsentState::kGranted; + +/***************************************************************************** + * Application Function Definitions + *****************************************************************************/ + +bool OtaHeaderValidation(Ota_ImageHeader_t imageHeader) +{ + + uint16_t vendorId = 0; + uint16_t productId = 0; + + if (gFactoryDataProvider.GetVendorId(vendorId) != CHIP_NO_ERROR) + { + return false; + } + if (gFactoryDataProvider.GetProductId(productId) != CHIP_NO_ERROR) + { + return false; + } + + // Check that the image matches vendor and product ID and that the version is higher than what we currently have + if (imageHeader.vendorId != vendorId || imageHeader.productId != productId || + imageHeader.softwareVersion <= CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION) + { + return false; + } + + return true; +} + +void InitializeOTARequestor(void) +{ + ChipLogProgress(DeviceLayer, "Initialising OTA Requestor"); + // Initialize and interconnect the Requestor and Image Processor objects + SetRequestorInstance(&gRequestorCore); + + gRequestorStorage.Init(chip::Server::GetInstance().GetPersistentStorage()); + gRequestorCore.Init(chip::Server::GetInstance(), gRequestorStorage, gRequestorUser, gDownloader); + gImageProcessor.SetOTADownloader(&gDownloader); + gDownloader.SetImageProcessorDelegate(&gImageProcessor); + gRequestorUser.Init(&gRequestorCore, &gImageProcessor); + gUserConsentProvider.SetUserConsentState(gUserConsentState); + // Test to trigger ota. this function can be trigger by a Push Button + TriggerOTAQuery(); +} + +void TriggerOTAQuery(void) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + OTARequestorInterface * requestor = GetRequestorInstance(); + + if (requestor != nullptr) + { + err = requestor->TriggerImmediateQuery(kUndefinedFabricIndex); + + if (CHIP_NO_ERROR != err) + { + ChipLogError(DeviceLayer, "Failed trigger OTA query: %" CHIP_ERROR_FORMAT, err.Format()); + } + } + else + { + ChipLogProgress(DeviceLayer, "No OTA requestor instance, can't query OTA"); + } +} +#endif diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/otp.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/otp.c index ee2d71467514b5..f355df04fb803d 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Src/otp.c +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/otp.c @@ -33,7 +33,7 @@ uint8_t * OTP_Read(uint8_t id) { uint8_t * p_id; - p_id = (uint8_t *) (CFG_OTP_END_ADDRESS - 7); + p_id = (uint8_t *) (CFG_OTP_END_ADRESS - 7); while (((*(p_id + 7)) != id) && (p_id != (uint8_t *) CFG_OTP_BASE_ADDRESS)) { diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32_factorydata.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32_factorydata.c new file mode 100644 index 00000000000000..3ad44445b11908 --- /dev/null +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32_factorydata.c @@ -0,0 +1,132 @@ +/** + ****************************************************************************** + * @file stm32_factorydata.c + * @author MCD Application Team + * @brief Middleware between matter factory data and external flash , + * to manage factory data needed for Matter + ****************************************************************************** + * @attention + * + * Copyright (c) 2023 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32_factorydata.h" +#include "stm_ext_flash.h" + +/* Private defines -----------------------------------------------------------*/ +#define HEADER_SIZE 8 +#define FACTORY_DATA_START_ADDR 0x901C0000U +#define FACTORY_DATA_END_ADDR 0x901CFFFFU +#define DATA_MAX_LENGTH 603 + +/* Private variables ---------------------------------------------------------*/ +typedef struct +{ + uint32_t tag_id; + uint32_t data_length; +} header; + +/* Private functions prototypes-----------------------------------------------*/ +static FACTORYDATA_StatusTypeDef Get_TagLocation(FACTORYDATA_TagId tag, uint32_t * out_Location, uint32_t * out_length); + +/************************************************************* + * + * PUBLIC FUNCTIONS + * + *************************************************************/ + +FACTORYDATA_StatusTypeDef FACTORYDATA_GetValue(FACTORYDATA_TagId tag, uint8_t * data, uint32_t size, uint32_t * out_datalength) +{ + + FACTORYDATA_StatusTypeDef err = DATAFACTORY_DATA_NOT_FOUND; + uint32_t Location; + uint32_t datalength; + + if ((data == NULL) || (out_datalength == NULL)) + { + return DATAFACTORY_PARAM_ERROR; + } + + // search for tag location + err = Get_TagLocation(tag, &Location, &datalength); + if (err != DATAFACTORY_OK) + { + return err; + } + else + { + if (datalength > size) + { + return DATAFACTORY_BUFFER_TOO_SMALL; + } + // Read data + err = STM_EXT_FLASH_ReadChunk(Location, data, datalength); + if (err == DATAFACTORY_OK) + { + *out_datalength = datalength; + } + } + + return err; +} + +/************************************************************* + * + * LOCAL FUNCTIONS + * + *************************************************************/ + +static FACTORYDATA_StatusTypeDef Get_TagLocation(FACTORYDATA_TagId tag, uint32_t * out_Location, uint32_t * out_length) +{ + FACTORYDATA_StatusTypeDef err = DATAFACTORY_OK; + header Header_tag; + uint8_t Header_data[HEADER_SIZE]; + uint32_t location_tmp = FACTORY_DATA_START_ADDR; + + // read all header until find the right tag + do + { + memset(Header_data, 0, HEADER_SIZE); + err = STM_EXT_FLASH_ReadChunk(location_tmp, Header_data, HEADER_SIZE); + if (err != DATAFACTORY_OK) + { + return DATAFACTORY_PARAM_ERROR; + } + + // retrieve header with tag_id and data_length + Header_tag.tag_id = Header_data[0] + ((Header_data[1]) << 8) + ((Header_data[2]) << 16) + ((Header_data[3]) << 24); + Header_tag.data_length = Header_data[4] + ((Header_data[5]) << 8) + ((Header_data[6]) << 16) + ((Header_data[7]) << 24); + + // check if the length is valid + if ((Header_tag.data_length > 0) && (Header_tag.data_length < DATA_MAX_LENGTH)) + { + if (Header_tag.tag_id == tag) + { + *out_Location = location_tmp + HEADER_SIZE; + *out_length = Header_tag.data_length; + break; + } + // move to the next data + location_tmp += Header_tag.data_length + HEADER_SIZE; + } + else + { + return DATAFACTORY_PARAM_ERROR; + } + } while (location_tmp < FACTORY_DATA_END_ADDR); + + if (location_tmp > FACTORY_DATA_END_ADDR) + { + err = DATAFACTORY_DATA_NOT_FOUND; + } + + return err; +} diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32wb5mm_dk_qspi.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32wb5mm_dk_qspi.c new file mode 100644 index 00000000000000..6925269ea304a1 --- /dev/null +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32wb5mm_dk_qspi.c @@ -0,0 +1,922 @@ +/** + ****************************************************************************** + * @file stm32wb5mm_dk_qspi.c + * @author MCD Application Team + * @brief This file includes a standard driver for the S25FL128S QSPI + * memory mounted on STM32WB5MM-DK board. + ****************************************************************************** + * @attention + * + * Copyright (c) 2021 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + @verbatim + ============================================================================== + ##### How to use this driver ##### + ============================================================================== + [..] + (#) This driver is used to drive the S25FL128S QSPI external + memory mounted on STM32WB5MM-DK board. + + (#) This driver needs a specific component driver (S25FL128S) to be included with. + + (#) Initialization steps: + (++) Initialize the QPSI external memory using the BSP_QSPI_Init() function. This + function includes the MSP layer hardware resources initialization and the + QSPI interface with the external memory. + + (#) QSPI memory operations + (++) QSPI memory can be accessed with read/write operations once it is + initialized. + Read/write operation can be performed with AHB access using the functions + BSP_QSPI_Read()/BSP_QSPI_Write(). + (++) The function BSP_QSPI_GetInfo() returns the configuration of the QSPI memory. + (see the QSPI memory data sheet) + (++) Perform erase block operation using the function BSP_QSPI_Erase_Block() and by + specifying the block address. You can perform an erase operation of the whole + chip by calling the function BSP_QSPI_Erase_Chip(). + (++) The function BSP_QSPI_GetStatus() returns the current status of the QSPI memory. + (see the QSPI memory data sheet) + (++) The function BSP_QSPI_EnableMemoryMappedMode enables the QSPI memory mapped mode. + (++) The function BSP_QSPI_DisableMemoryMappedMode disables the QSPI memory mapped mode. + (++) The function BSP_QSPI_ReadID() returns 3 bytes memory IDs: Manufacturer ID, + Memory type, Memory density. + @endverbatim + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32wb5mm_dk_qspi.h" + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup STM32WB5MM_DK + * @{ + */ + +/** @defgroup STM32WB5MM_DK_QSPI STM32WB5MM_DK QSPI + * @{ + */ + +/** @defgroup STM32WB5MM_DK_QSPI_Exported_Variables Exported Variables + * @{ + */ +QSPI_HandleTypeDef hqspi; +BSP_QSPI_Ctx_t QSPI_Ctx[QSPI_INSTANCES_NUMBER]; +/** + * @} + */ + +/* Private functions ---------------------------------------------------------*/ + +/** @defgroup STM32WB5MM_DK_QSPI_Private_Functions Private Functions + * @{ + */ +static void QSPI_MspInit(QSPI_HandleTypeDef * hQspi); +static void QSPI_MspDeInit(QSPI_HandleTypeDef * hSspi); +static int32_t QSPI_ResetMemory(uint32_t Instance); +static int32_t QSPI_DummyCyclesCfg(uint32_t Instance); + +/** + * @} + */ + +/** @defgroup STM32WB5MM_DK_QSPI_Exported_Functions Exported Functions + * @{ + */ + +/** + * @brief Initializes the QSPI interface. + * @param Instance QSPI Instance + * @param Init QSPI Init structure + * @retval BSP status + */ +int32_t BSP_QSPI_Init(uint32_t Instance, BSP_QSPI_Init_t * Init) +{ + int32_t ret = BSP_ERROR_NONE; + BSP_QSPI_Info_t pInfo; + MX_QSPI_Init_t qspi_init; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + /* Check if instance is already initialized */ + if (QSPI_Ctx[Instance].IsInitialized == QSPI_ACCESS_NONE) + { +#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1) + /* Register the QSPI MSP Callbacks */ + if (QSPI_Ctx[Instance].IsMspCallbacksValid == 0UL) + { + if (BSP_QSPI_RegisterDefaultMspCallbacks(Instance) != BSP_ERROR_NONE) + { + ret = BSP_ERROR_PERIPH_FAILURE; + } + } +#else + /* Msp QSPI initialization */ + QSPI_MspInit(&hqspi); +#endif /* USE_HAL_QSPI_REGISTER_CALLBACKS == 1 */ + + if (ret == BSP_ERROR_NONE) + { + /* STM32 QSPI interface initialization */ + (void) S25FL128S_GetFlashInfo(&pInfo); + qspi_init.ClockPrescaler = 4; + qspi_init.DualFlashMode = S25FL128S_DUALFLASH_DISABLE; + qspi_init.FlashSize = (uint32_t) POSITION_VAL((uint32_t) pInfo.FlashSize) - 1U; + qspi_init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; + + if (MX_QSPI_Init(&hqspi, &qspi_init) != HAL_OK) + { + ret = BSP_ERROR_PERIPH_FAILURE; + } /* QSPI memory reset */ + else if (QSPI_ResetMemory(Instance) != BSP_ERROR_NONE) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } /* Force Flash enter 4 Byte address mode */ + else if (S25FL128S_AutoPollingMemReady(&hqspi, Init->InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + else if (S25FL128S_Enter4BytesAddressMode(&hqspi, Init->InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } /* Configuration of the dummy cycles on QSPI memory side */ + else if (QSPI_DummyCyclesCfg(Instance) != BSP_ERROR_NONE) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + else + { + QSPI_Ctx[Instance].InterfaceMode = Init->InterfaceMode; + } + } + } + } + + /* Return BSP status */ + return ret; +} + +/** + * @brief De-Initializes the QSPI interface. + * @param Instance QSPI Instance + * @retval BSP status + */ +int32_t BSP_QSPI_DeInit(uint32_t Instance) +{ + int32_t ret = BSP_ERROR_NONE; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + if (QSPI_Ctx[Instance].IsInitialized == QSPI_ACCESS_MMP) + { + if (BSP_QSPI_DisableMemoryMappedMode(Instance) != BSP_ERROR_NONE) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + } + + if (ret == BSP_ERROR_NONE) + { + /* Set default QSPI_Ctx values */ + QSPI_Ctx[Instance].IsInitialized = QSPI_ACCESS_NONE; + QSPI_Ctx[Instance].InterfaceMode = BSP_QSPI_SPI_MODE; + QSPI_Ctx[Instance].TransferRate = BSP_QSPI_STR_TRANSFER; + QSPI_Ctx[Instance].DualFlashMode = 0; + +#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 0) + QSPI_MspDeInit(&hqspi); +#endif /* (USE_HAL_QSPI_REGISTER_CALLBACKS == 0) */ + + /* Call the DeInit function to reset the driver */ + if (HAL_QSPI_DeInit(&hqspi) != HAL_OK) + { + ret = BSP_ERROR_PERIPH_FAILURE; + } + } + } + + /* Return BSP status */ + return ret; +} + +/** + * @brief Initializes the QSPI interface. + * @param hQspi QSPI handle + * @param Config QSPI configuration structure + * @retval BSP status + */ +__weak HAL_StatusTypeDef MX_QSPI_Init(QSPI_HandleTypeDef * hQspi, MX_QSPI_Init_t * Config) +{ + /* QSPI initialization */ + /* QSPI freq = SYSCLK /(1 + ClockPrescaler) Mhz */ + hQspi->Instance = QUADSPI; + hQspi->Init.ClockPrescaler = Config->ClockPrescaler; + hQspi->Init.FifoThreshold = 1; + hQspi->Init.SampleShifting = Config->SampleShifting; + hQspi->Init.FlashSize = Config->FlashSize; + hQspi->Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_8_CYCLE; + hQspi->Init.ClockMode = QSPI_CLOCK_MODE_0; + + return HAL_QSPI_Init(hQspi); +} + +#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1) +/** + * @brief Default BSP QSPI Msp Callbacks + * @param Instance QSPI Instance + * @retval BSP status + */ +int32_t BSP_QSPI_RegisterDefaultMspCallbacks(uint32_t Instance) +{ + int32_t ret = BSP_ERROR_NONE; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + /* Register MspInit/MspDeInit Callbacks */ + if (HAL_QSPI_RegisterCallback(&hqspi, HAL_QSPI_MSPINIT_CB_ID, QSPI_MspInit) != HAL_OK) + { + ret = BSP_ERROR_PERIPH_FAILURE; + } + else if (HAL_QSPI_RegisterCallback(&hqspi, HAL_QSPI_MSPDEINIT_CB_ID, QSPI_MspDeInit) != HAL_OK) + { + ret = BSP_ERROR_PERIPH_FAILURE; + } + else + { + QSPI_Ctx[Instance].IsMspCallbacksValid = 1U; + } + } + + /* Return BSP status */ + return ret; +} + +/** + * @brief BSP QSPI Msp Callback registering + * @param Instance QSPI Instance + * @param CallBacks pointer to MspInit/MspDeInit callbacks functions + * @retval BSP status + */ +int32_t BSP_QSPI_RegisterMspCallbacks(uint32_t Instance, BSP_QSPI_Cb_t * CallBacks) +{ + int32_t ret = BSP_ERROR_NONE; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + /* Register MspInit/MspDeInit Callbacks */ + if (HAL_QSPI_RegisterCallback(&hqspi, HAL_QSPI_MSPINIT_CB_ID, CallBacks->pMspInitCb) != HAL_OK) + { + ret = BSP_ERROR_PERIPH_FAILURE; + } + else if (HAL_QSPI_RegisterCallback(&hqspi, HAL_QSPI_MSPDEINIT_CB_ID, CallBacks->pMspDeInitCb) != HAL_OK) + { + ret = BSP_ERROR_PERIPH_FAILURE; + } + else + { + QSPI_Ctx[Instance].IsMspCallbacksValid = 1U; + } + } + + /* Return BSP status */ + return ret; +} +#endif /* (USE_HAL_QSPI_REGISTER_CALLBACKS == 1) */ + +/** + * @brief Reads an amount of data from the QSPI memory. + * @param Instance QSPI instance + * @param pData Pointer to data to be read + * @param ReadAddr Read start address + * @param Size Size of data to read + * @retval BSP status + */ +int32_t BSP_QSPI_Read(uint32_t Instance, uint8_t * pData, uint32_t ReadAddr, uint32_t Size) +{ + int32_t ret = BSP_ERROR_NONE; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + if (QSPI_Ctx[Instance].TransferRate == BSP_QSPI_STR_TRANSFER) + { + if (S25FL128S_ReadSTR(&hqspi, QSPI_Ctx[Instance].InterfaceMode, pData, ReadAddr, Size) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + } + } + + /* Return BSP status */ + return ret; +} + +/** + * @brief Writes an amount of data to the QSPI memory. + * @param Instance QSPI instance + * @param pData Pointer to data to be written + * @param WriteAddr Write start address + * @param Size Size of data to write + * @retval BSP status + */ +int32_t BSP_QSPI_Write(uint32_t Instance, uint8_t * pData, uint32_t WriteAddr, uint32_t Size) +{ + int32_t ret = BSP_ERROR_NONE; + uint32_t end_addr, current_size, current_addr; + uint8_t * write_data; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + /* Calculation of the size between the write address and the end of the page */ + current_size = S25FL128S_PAGE_SIZE - (WriteAddr % S25FL128S_PAGE_SIZE); + + /* Check if the size of the data is less than the remaining place in the page */ + if (current_size > Size) + { + current_size = Size; + } + + /* Initialize the address variables */ + current_addr = WriteAddr; + end_addr = WriteAddr + Size; + write_data = pData; + + /* Perform the write page by page */ + do + { + /* Check if Flash busy ? */ + if (S25FL128S_AutoPollingMemReady(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } /* Enable write operations */ + else if (S25FL128S_WriteEnable(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } /* Issue page program command */ + else if (S25FL128S_PageProgram(&hqspi, QSPI_Ctx[Instance].InterfaceMode, write_data, current_addr, current_size) != + S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } /* Configure automatic polling mode to wait for end of program */ + else if (S25FL128S_AutoPollingMemReady(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + else + { + /* Update the address and size variables for next page programming */ + current_addr += current_size; + write_data += current_size; + current_size = ((current_addr + S25FL128S_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : S25FL128S_PAGE_SIZE; + } + } while ((current_addr < end_addr) && (ret == BSP_ERROR_NONE)); + } + + /* Return BSP status */ + return ret; +} + +/** + * @brief Erases the specified block of the QSPI memory. + * S25FL128S support 4K, 64K size block erase commands. + * @param Instance QSPI instance + * @param BlockAddress Block address to erase + * @param BlockSize Erase Block size + * @retval BSP status + */ +int32_t BSP_QSPI_EraseBlock(uint32_t Instance, uint32_t BlockAddress, BSP_QSPI_Erase_t BlockSize) +{ + int32_t ret = BSP_ERROR_NONE; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + /* Check Flash busy ? */ + if (S25FL128S_AutoPollingMemReady(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } /* Enable write operations */ + else if (S25FL128S_WriteEnable(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + else + { + /* Issue Block Erase command */ + if (S25FL128S_BlockErase(&hqspi, QSPI_Ctx[Instance].InterfaceMode, BlockAddress, BlockSize) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + if (S25FL128S_AutoPollingMemReady(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + } + } + + /* Return BSP status */ + return ret; +} + +/** + * @brief Erases the entire QSPI memory. + * @param Instance QSPI instance + * @retval BSP status + */ +int32_t BSP_QSPI_EraseChip(uint32_t Instance) +{ + int32_t ret = BSP_ERROR_NONE; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + /* Check Flash busy ? */ + if (S25FL128S_AutoPollingMemReady(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } /* Enable write operations */ + else if (S25FL128S_WriteEnable(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + else + { + /* Issue Chip erase command */ + if (S25FL128S_ChipErase(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + } + } + + /* Return BSP status */ + return ret; +} + +/** + * @brief Reads current status of the QSPI memory. + * If WIP != 0 then return busy. + * @param Instance QSPI instance + * @retval QSPI memory status: whether busy or not + */ +int32_t BSP_QSPI_GetStatus(uint32_t Instance) +{ + int32_t ret = BSP_ERROR_NONE; + uint8_t reg; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + if (S25FL128S_ReadStatusRegister(&hqspi, QSPI_Ctx[Instance].InterfaceMode, ®) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + else + { + /* Check the value of the register */ + if ((reg & S25FL128S_SR1_WIP) != 0U) + { + ret = BSP_ERROR_BUSY; + } + } + } + + /* Return BSP status */ + return ret; +} + +/** + * @brief Return the configuration of the QSPI memory. + * @param Instance QSPI instance + * @param pInfo pointer on the configuration structure + * @retval BSP status + */ +int32_t BSP_QSPI_GetInfo(uint32_t Instance, BSP_QSPI_Info_t * pInfo) +{ + int32_t ret = BSP_ERROR_NONE; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + (void) S25FL128S_GetFlashInfo(pInfo); + } + + /* Return BSP status */ + return ret; +} + +/** + * @brief Configure the QSPI in memory-mapped mode + * Only 1 Instance can running MMP mode. And it will lock system at this mode. + * @param Instance QSPI instance + * @retval BSP status + */ +int32_t BSP_QSPI_EnableMemoryMappedMode(uint32_t Instance) +{ + int32_t ret = BSP_ERROR_NONE; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + if (QSPI_Ctx[Instance].TransferRate == BSP_QSPI_STR_TRANSFER) + { + if (S25FL128S_EnableMemoryMappedModeSTR(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + else /* Update QSPI context if all operations are well done */ + { + QSPI_Ctx[Instance].IsInitialized = QSPI_ACCESS_MMP; + } + } + else + { + /* Update QSPI context if all operations are well done */ + QSPI_Ctx[Instance].IsInitialized = QSPI_ACCESS_MMP; + } + } + + /* Return BSP status */ + return ret; +} + +/** + * @brief Exit form memory-mapped mode + * Only 1 Instance can run MMP mode. And it will lock system at this mode. + * @param Instance QSPI instance + * @retval BSP status + */ +int32_t BSP_QSPI_DisableMemoryMappedMode(uint32_t Instance) +{ + uint8_t Dummy; + int32_t ret = BSP_ERROR_NONE; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + if (QSPI_Ctx[Instance].IsInitialized != QSPI_ACCESS_MMP) + { + ret = BSP_ERROR_PERIPH_FAILURE; + } /* Abort MMP back to indirect mode */ + else if (HAL_QSPI_Abort(&hqspi) != HAL_OK) + { + ret = BSP_ERROR_PERIPH_FAILURE; + } + else + { + /* Force QSPI interface Sampling Shift to half cycle */ + hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; + + if (HAL_QSPI_Init(&hqspi) != HAL_OK) + { + ret = BSP_ERROR_PERIPH_FAILURE; + } + /* Dummy read for exit from Performance Enhance mode */ + else if (S25FL128S_ReadSTR(&hqspi, QSPI_Ctx[Instance].InterfaceMode, &Dummy, 0, 1) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + else /* Update QSPI context if all operations are well done */ + { + QSPI_Ctx[Instance].IsInitialized = QSPI_ACCESS_INDIRECT; + } + } + } + /* Return BSP status */ + return ret; +} + +/** + * @brief Get flash ID, 3 Bytes + * Manufacturer ID, Memory type, Memory density + * @param Instance QSPI instance + * @param Id QSPI Identifier + * @retval BSP status + */ +int32_t BSP_QSPI_ReadID(uint32_t Instance, uint8_t * Id) +{ + int32_t ret = BSP_ERROR_NONE; + + /* Check if the instance is supported */ + if (Instance >= QSPI_INSTANCES_NUMBER) + { + ret = BSP_ERROR_WRONG_PARAM; + } + else + { + if (S25FL128S_ReadID(&hqspi, QSPI_Ctx[Instance].InterfaceMode, Id) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + } + + /* Return BSP status */ + return ret; +} + +/** + * @} + */ + +/** @defgroup STM32WB5MM_DK_QSPI_Private_Functions Private Functions + * @{ + */ + +/** + * @brief QSPI MSP Initialization + * @param hQspi : QSPI handle + * This function configures the hardware resources used in this example: + * - Peripheral's clock enable + * - Peripheral's GPIO Configuration + * - NVIC configuration for QSPI interrupt + * @retval None + */ +static void QSPI_MspInit(QSPI_HandleTypeDef * hQspi) +{ + /* Prevent unused argument(s) compilation warning */ + UNUSED(hQspi); + GPIO_InitTypeDef gpio_init_structure; + + /*##-1- Enable peripherals and GPIO Clocks #################################*/ + /* Enable the QuadSPI memory interface clock */ + QSPI_CLK_ENABLE(); + /* Reset the QuadSPI memory interface */ + QSPI_FORCE_RESET(); + QSPI_RELEASE_RESET(); + /* Enable GPIO clocks */ + QSPI_CS_GPIO_CLK_ENABLE(); + QSPI_CLK_GPIO_CLK_ENABLE(); + QSPI_D0_GPIO_CLK_ENABLE(); + QSPI_D1_GPIO_CLK_ENABLE(); + QSPI_D2_GPIO_CLK_ENABLE(); + QSPI_D3_GPIO_CLK_ENABLE(); + + /*##-2- Configure peripheral GPIO ##########################################*/ + /* QSPI CS GPIO pin configuration */ + gpio_init_structure.Pin = QSPI_CS_PIN; + gpio_init_structure.Mode = GPIO_MODE_AF_PP; + gpio_init_structure.Pull = GPIO_PULLUP; + gpio_init_structure.Speed = GPIO_SPEED_FREQ_HIGH; + gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; + HAL_GPIO_Init(QSPI_CS_GPIO_PORT, &gpio_init_structure); + + /* QSPI CLK GPIO pin configuration */ + gpio_init_structure.Pin = QSPI_CLK_PIN; + gpio_init_structure.Pull = GPIO_PULLUP; + gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; + HAL_GPIO_Init(QSPI_CLK_GPIO_PORT, &gpio_init_structure); + + /* QSPI D0 GPIO pin configuration */ + gpio_init_structure.Pin = QSPI_D0_PIN; + gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; + HAL_GPIO_Init(QSPI_D0_GPIO_PORT, &gpio_init_structure); + + /* QSPI D1 GPIO pin configuration */ + gpio_init_structure.Pin = QSPI_D1_PIN; + gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; + HAL_GPIO_Init(QSPI_D1_GPIO_PORT, &gpio_init_structure); + + /* QSPI D2 GPIO pin configuration */ + gpio_init_structure.Pin = QSPI_D2_PIN; + gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; + HAL_GPIO_Init(QSPI_D2_GPIO_PORT, &gpio_init_structure); + + /* QSPI D3 GPIO pin configuration */ + gpio_init_structure.Pin = QSPI_D3_PIN; + gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; + HAL_GPIO_Init(QSPI_D3_GPIO_PORT, &gpio_init_structure); + + /*##-3- Configure the NVIC for QSPI #########################################*/ + /* NVIC configuration for QSPI interrupt */ + HAL_NVIC_SetPriority(QUADSPI_IRQn, 0x0F, 0); + HAL_NVIC_EnableIRQ(QUADSPI_IRQn); +} + +/** + * @brief QSPI MSP De-Initialization + * @param hQspi QSPI handle + * This function frees the hardware resources used in this example: + * - Disable the Peripheral's clock + * - Revert GPIO and NVIC configuration to their default state + * @retval None + */ +static void QSPI_MspDeInit(QSPI_HandleTypeDef * hQspi) +{ + /* Prevent unused argument(s) compilation warning */ + UNUSED(hQspi); + + /*##-2- Disable peripherals and GPIO Clocks ################################*/ + /* De-Configure QSPI pins */ + /* De-Configure QSPI pins */ + HAL_GPIO_DeInit(QSPI_CS_GPIO_PORT, QSPI_CS_PIN); + HAL_GPIO_DeInit(QSPI_CLK_GPIO_PORT, QSPI_CLK_PIN); + HAL_GPIO_DeInit(QSPI_D0_GPIO_PORT, QSPI_D0_PIN); + HAL_GPIO_DeInit(QSPI_D1_GPIO_PORT, QSPI_D1_PIN); + HAL_GPIO_DeInit(QSPI_D2_GPIO_PORT, QSPI_D2_PIN); + HAL_GPIO_DeInit(QSPI_D3_GPIO_PORT, QSPI_D3_PIN); + + /*##-3- Reset peripherals ##################################################*/ + /* Reset the QuadSPI memory interface */ + QSPI_FORCE_RESET(); + QSPI_RELEASE_RESET(); + + /* Disable the QuadSPI memory interface clock */ + QSPI_CLK_DISABLE(); +} + +/** + * @brief This function reset the QSPI Flash memory. + * For SPI reset to avoid system come from unknown status. + * Flash accept 1-1-1, 1-1-2, 1-2-2 commands after reset. + * @param Instance QSPI instance + * @retval BSP status + */ +static int32_t QSPI_ResetMemory(uint32_t Instance) +{ + int32_t ret = BSP_ERROR_NONE; + + /* Wait Flash ready */ + if (S25FL128S_AutoPollingMemReady(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } /* Send RESET ENABLE command in SPI mode (1-1-1) */ + else if (S25FL128S_ResetEnable(&hqspi, BSP_QSPI_SPI_MODE) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } /* Send RESET memory command in SPI mode (1-1-1) */ + else if (S25FL128S_ResetMemory(&hqspi, BSP_QSPI_SPI_MODE) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + else + { + QSPI_Ctx[Instance].IsInitialized = QSPI_ACCESS_INDIRECT; /* After reset S/W setting to indirect access */ + QSPI_Ctx[Instance].InterfaceMode = BSP_QSPI_SPI_MODE; /* After reset H/W back to SPI mode by default */ + QSPI_Ctx[Instance].TransferRate = BSP_QSPI_STR_TRANSFER; /* After reset S/W setting to STR mode */ + } + + /* Return BSP status */ + return ret; +} + +/** + * @brief This function configures the dummy cycles on memory side. + * Dummy cycle bit locate in Configuration Register[7:6] + * @param Instance QSPI instance + * @retval BSP status + */ +static int32_t QSPI_DummyCyclesCfg(uint32_t Instance) +{ + int32_t ret = BSP_ERROR_NONE; + QSPI_CommandTypeDef s_command; + uint8_t reg[2]; + + /* Initialize the read configuration register command */ + s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; + s_command.Instruction = S25FL128S_READ_CONFIGURATION_REG1_CMD; + s_command.AddressMode = QSPI_ADDRESS_NONE; + s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = QSPI_DATA_1_LINE; + s_command.DummyCycles = 0; + s_command.NbData = 1; + s_command.DdrMode = QSPI_DDR_MODE_DISABLE; + s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; + + /* Configure the command */ + if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + + /* Reception of the data */ + if (HAL_QSPI_Receive(&hqspi, ®[1], HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + + /* Initialize the read status register1 command */ + s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; + s_command.Instruction = S25FL128S_READ_STATUS_REG1_CMD; + s_command.AddressMode = QSPI_ADDRESS_NONE; + s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = QSPI_DATA_1_LINE; + s_command.DummyCycles = 0; + s_command.NbData = 1; + s_command.DdrMode = QSPI_DDR_MODE_DISABLE; + s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; + + /* Configure the command */ + if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + + /* Reception of the data */ + if (HAL_QSPI_Receive(&hqspi, ®[0], HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + + /* Enable write operations */ + if (S25FL128S_WriteEnable(&hqspi, QSPI_Ctx[Instance].InterfaceMode) != S25FL128S_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + + /* Update configuration register (with new Latency Code) */ + s_command.Instruction = S25FL128S_WRITE_STATUS_CMD_REG_CMD; + s_command.NbData = 2; + MODIFY_REG(reg[1], S25FL128S_CR1_LC_MASK, S25FL128S_CR1_LC1); + + /* Configure the write volatile configuration register command */ + if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + + /* Transmission of the data Status Register 1 */ + if (HAL_QSPI_Transmit(&hqspi, reg, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + ret = BSP_ERROR_COMPONENT_FAILURE; + } + + /* Return BSP status */ + return ret; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32wbxx_hal_msp.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32wbxx_hal_msp.c index b5fa5b234e778f..1ff1746dcd5ef1 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32wbxx_hal_msp.c +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32wbxx_hal_msp.c @@ -29,16 +29,7 @@ extern DMA_HandleTypeDef hdma_usart1_tx; /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN TD */ -void Error_Handler(void) -{ - /* USER CODE BEGIN Error_Handler_Debug */ - /* User can add his own implementation to report the HAL error return state */ - while (1) - { - HAL_Delay(100); - } - /* USER CODE END Error_Handler_Debug */ -} + /* USER CODE END TD */ /* Private define ------------------------------------------------------------*/ diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32wbxx_it.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32wbxx_it.c index aa13c0fb6ada54..bf28e5b4c3dff8 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32wbxx_it.c +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm32wbxx_it.c @@ -74,14 +74,6 @@ void MemManage_Handler(void) /* USER CODE END W1_MemoryManagement_IRQn 0 */ } } -/** - * @brief This function handles SVCall exception. - * @param None - * @retval None - */ -/*void SVC_Handler(void) -{ -}*/ /** * @brief This function handles Debug Monitor exception. @@ -90,25 +82,6 @@ void MemManage_Handler(void) */ void DebugMon_Handler(void) {} -/** - * @brief This function handles PendSVC exception. - * @param None - * @retval None - */ -/*void PendSV_Handler(void) -{ -}*/ - -/** - * @brief This function handles SysTick Handler. - * @param None - * @retval None - */ -/*void SysTick_Handler(void) -{ - HAL_IncTick(); -}*/ - void IPCC_C1_TX_IRQHandler(void) { HW_IPCC_Tx_Handler(); @@ -142,7 +115,6 @@ void TIM1_TRG_COM_TIM17_IRQHandler(void) * @param None * @retval None */ - void EXTI15_10_IRQHandler(void) { BSP_PB_IRQHandler(BUTTON_USER1); diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm_ext_flash.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm_ext_flash.c new file mode 100644 index 00000000000000..feb8e92041175e --- /dev/null +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm_ext_flash.c @@ -0,0 +1,257 @@ +/** + ****************************************************************************** + * @file stm_ota.c + * @author MCD Application Team + * @brief Write new image in external flash + ****************************************************************************** + * @attention + * + * Copyright (c) 2023 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm_ext_flash.h" +#include "cmsis_os.h" +#include "stm32wb5mm_dk_qspi.h" + +/* Private defines -----------------------------------------------------------*/ +#define ERASE_BLOC_SIZE 0x10000U /*!< 64 Kbytes */ + +/* Private macros ------------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +osSemaphoreId_t SemExtFlashId; + +/* Global variables ----------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +static STM_OTA_StatusTypeDef check_addr(uint32_t Address, uint32_t Length); +static void QSPI_Pin_LP(void); +static void QSPI_Pin_WakeUP(void); +/* Public functions ----------------------------------------------------------*/ + +STM_OTA_StatusTypeDef STM_EXT_FLASH_Init(void) +{ + BSP_QSPI_Init_t init; + + SemExtFlashId = osSemaphoreNew(1, 1, NULL); /*< Create the semaphore and make it available at initialization */ + + init.TransferRate = BSP_QSPI_STR_TRANSFER; + init.DualFlashMode = BSP_QSPI_DUALFLASH_DISABLE; + init.InterfaceMode = S25FL128S_QPI_MODE; + if (BSP_QSPI_Init(0, &init) != BSP_ERROR_NONE) + { + return STM_EXT_FLASH_INIT_FAILED; + } + else + { +#if CFG_FULL_LOW_POWER == 1 + QSPI_Pin_LP(); +#endif + return STM_EXT_FLASH_OK; + } +} + +STM_OTA_StatusTypeDef STM_EXT_FLASH_Delete_Image(uint32_t Address, uint32_t Length) +{ + uint32_t loop_flash; + + // check if the address is in the external flash and if the length is < flash size + if (check_addr(Address, Length) != STM_EXT_FLASH_OK) + { + return STM_EXT_FLASH_INVALID_PARAM; + } + + /* Do nothing if Length equal to 0 */ + if (Length == 0U) + { + return STM_EXT_FLASH_OK; + } + + /* flash address to erase is the offset from begin of external flash */ + Address -= EXTERNAL_FLASH_ADDRESS; + + osSemaphoreAcquire(SemExtFlashId, osWaitForever); +#if CFG_FULL_LOW_POWER == 1 + QSPI_Pin_WakeUP(); +#endif + /* Loop on 64KBytes block */ + for (loop_flash = 0U; loop_flash < (((Length - 1U) / ERASE_BLOC_SIZE) + 1U); loop_flash++) + { + if (BSP_QSPI_EraseBlock(0, Address, BSP_QSPI_ERASE_64K) != BSP_ERROR_NONE) + { +#if CFG_FULL_LOW_POWER == 1 + QSPI_Pin_LP(); +#endif + osSemaphoreRelease(SemExtFlashId); + return STM_EXT_FLASH_DELETE_FAILED; + } + + /* next 64KBytes block */ + Address += ERASE_BLOC_SIZE; + } +#if CFG_FULL_LOW_POWER == 1 + QSPI_Pin_LP(); +#endif + osSemaphoreRelease(SemExtFlashId); + return STM_EXT_FLASH_OK; +} + +STM_OTA_StatusTypeDef STM_EXT_FLASH_WriteChunk(uint32_t DestAddress, uint8_t * pSrcBuffer, uint32_t Length) +{ + int32_t error = 0; + if (pSrcBuffer == NULL) + { + return STM_EXT_FLASH_INVALID_PARAM; + } + // check if the address is in the external flash and if the length is < flash size + if (check_addr(DestAddress, Length) != STM_EXT_FLASH_OK) + { + return STM_EXT_FLASH_INVALID_PARAM; + } + /* Do nothing if Length equal to 0 */ + if (Length == 0U) + { + return STM_EXT_FLASH_OK; + } + osSemaphoreAcquire(SemExtFlashId, osWaitForever); +#if CFG_FULL_LOW_POWER == 1 + QSPI_Pin_WakeUP(); +#endif + error = BSP_QSPI_Write(0, pSrcBuffer, DestAddress - EXTERNAL_FLASH_ADDRESS, Length); + if (error != BSP_ERROR_NONE) + { +#if CFG_FULL_LOW_POWER == 1 + QSPI_Pin_LP(); +#endif + osSemaphoreRelease(SemExtFlashId); + return STM_EXT_FLASH_WRITE_FAILED; + } + else + { +#if CFG_FULL_LOW_POWER == 1 + QSPI_Pin_LP(); +#endif + osSemaphoreRelease(SemExtFlashId); + return STM_EXT_FLASH_OK; + } +} + +STM_OTA_StatusTypeDef STM_EXT_FLASH_ReadChunk(uint32_t DestAddress, uint8_t * pSrcBuffer, uint32_t Length) +{ + int32_t error = 0; + if (pSrcBuffer == NULL) + { + return STM_EXT_FLASH_INVALID_PARAM; + } + // check if the address is in the external flash and if the length is < flash size + if (check_addr(DestAddress, Length) != STM_EXT_FLASH_OK) + { + return STM_EXT_FLASH_INVALID_PARAM; + } + + /* Do nothing if Length equal to 0 */ + if (Length == 0U) + { + return STM_EXT_FLASH_OK; + } + osSemaphoreAcquire(SemExtFlashId, osWaitForever); +#if CFG_FULL_LOW_POWER == 1 + QSPI_Pin_WakeUP(); +#endif + error = BSP_QSPI_Read(0, pSrcBuffer, DestAddress - EXTERNAL_FLASH_ADDRESS, Length); + if (error != BSP_ERROR_NONE) + { +#if CFG_FULL_LOW_POWER == 1 + QSPI_Pin_LP(); +#endif + osSemaphoreRelease(SemExtFlashId); + return STM_EXT_FLASH_READ_FAILED; + } + else + { +#if CFG_FULL_LOW_POWER == 1 + QSPI_Pin_LP(); +#endif + osSemaphoreRelease(SemExtFlashId); + return STM_EXT_FLASH_OK; + } +} + +/************************************************************* + * + * LOCAL FUNCTIONS + * + *************************************************************/ +static STM_OTA_StatusTypeDef check_addr(uint32_t Address, uint32_t Length) +{ + // check if the address is in the external flash and if the length is < flash size + if ((Address < EXTERNAL_FLASH_ADDRESS) || (S25FL128S_FLASH_SIZE < Length) || + (Address + Length > EXTERNAL_FLASH_ADDRESS + S25FL128S_FLASH_SIZE)) + { + return STM_EXT_FLASH_INVALID_PARAM; + } + else + { + return STM_EXT_FLASH_OK; + } +} + +static void QSPI_Pin_WakeUP(void) +{ + + GPIO_InitTypeDef gpio_init_structure; + + /*##-1- Configure peripheral GPIO ##########################################*/ + /* QSPI CS GPIO pin configuration */ + gpio_init_structure.Pin = QSPI_CS_PIN; + gpio_init_structure.Mode = GPIO_MODE_AF_PP; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_FREQ_HIGH; + gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; + HAL_GPIO_Init(QSPI_CS_GPIO_PORT, &gpio_init_structure); + + /* QSPI CLK GPIO pin configuration */ + gpio_init_structure.Pin = QSPI_CLK_PIN; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; + HAL_GPIO_Init(QSPI_CLK_GPIO_PORT, &gpio_init_structure); + + /* QSPI D0 GPIO pin configuration */ + gpio_init_structure.Pin = QSPI_D0_PIN; + gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; + HAL_GPIO_Init(QSPI_D0_GPIO_PORT, &gpio_init_structure); + + /* QSPI D1 GPIO pin configuration */ + gpio_init_structure.Pin = QSPI_D1_PIN; + gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; + HAL_GPIO_Init(QSPI_D1_GPIO_PORT, &gpio_init_structure); + + /* QSPI D2 GPIO pin configuration */ + gpio_init_structure.Pin = QSPI_D2_PIN; + gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; + HAL_GPIO_Init(QSPI_D2_GPIO_PORT, &gpio_init_structure); + + /* QSPI D3 GPIO pin configuration */ + gpio_init_structure.Pin = QSPI_D3_PIN; + gpio_init_structure.Alternate = GPIO_AF10_QUADSPI; + HAL_GPIO_Init(QSPI_D3_GPIO_PORT, &gpio_init_structure); +} + +static void QSPI_Pin_LP(void) +{ + /*##-1- Disable peripherals ################################*/ + /* De-Configure QSPI pins */ + HAL_GPIO_DeInit(QSPI_CS_GPIO_PORT, QSPI_CS_PIN); + HAL_GPIO_DeInit(QSPI_CLK_GPIO_PORT, QSPI_CLK_PIN); + HAL_GPIO_DeInit(QSPI_D0_GPIO_PORT, QSPI_D0_PIN); + HAL_GPIO_DeInit(QSPI_D1_GPIO_PORT, QSPI_D1_PIN); + HAL_GPIO_DeInit(QSPI_D2_GPIO_PORT, QSPI_D2_PIN); + HAL_GPIO_DeInit(QSPI_D3_GPIO_PORT, QSPI_D3_PIN); +} diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm_logging.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm_logging.c index 910a401c70eef5..bfe1ed754c2c82 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm_logging.c +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/stm_logging.c @@ -1,4 +1,3 @@ - /** ****************************************************************************** * @file stm_logging.c diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/sysmem.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/sysmem.c index 7231d53efafcb2..319074d8d26eac 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Src/sysmem.c +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/sysmem.c @@ -35,7 +35,7 @@ register char * stack_ptr asm("sp"); _sbrk Increase program data space. Malloc and related functions depend on this **/ -caddr_t _sbrk(int incr) +void * _sbrk(int incr) { extern char end asm("end"); static char * heap_end; @@ -48,10 +48,10 @@ caddr_t _sbrk(int incr) if (heap_end + incr > stack_ptr) { errno = ENOMEM; - return (caddr_t) -1; + return (void *) -1; } heap_end += incr; - return (caddr_t) prev_heap_end; + return (void *) prev_heap_end; } diff --git a/examples/platform/stm32/common/STM32WB5MM-DK/Src/system_stm32wbxx.c b/examples/platform/stm32/common/STM32WB5MM-DK/Src/system_stm32wbxx.c index 533cc4f0aa9671..86bc4defd7edc4 100644 --- a/examples/platform/stm32/common/STM32WB5MM-DK/Src/system_stm32wbxx.c +++ b/examples/platform/stm32/common/STM32WB5MM-DK/Src/system_stm32wbxx.c @@ -124,7 +124,55 @@ /** * @} */ - +#if (OTA_SUPPORT == 1) +/* Note: Following vector table addresses must be defined in line with linker + configuration. */ +/*!< Uncomment the following line if you need to relocate CPU1 CM4 and/or CPU2 + CM0+ vector table anywhere in Sram or Flash. Else vector table will be kept + at address 0x00 which correspond to automatic remap of boot address selected */ +#define USER_VECT_TAB_ADDRESS +#if defined(USER_VECT_TAB_ADDRESS) +#ifdef CORE_CM0PLUS +/*!< Uncomment this line for user vector table remap in Sram else user remap + will be done in Flash. */ +/* #define VECT_TAB_SRAM */ +#if defined(VECT_TAB_SRAM) +#define VECT_TAB_BASE_ADDRESS \ + SRAM2_BASE /*!< Vector Table base address field. \ + This value must be a multiple of 0x100. */ +#define VECT_TAB_OFFSET \ + 0x00008000U /*!< Vector Table base offset field. \ + This value must be a multiple of 0x100. */ +#else +#define VECT_TAB_BASE_ADDRESS \ + FLASH_BASE /*!< Vector Table base address field. \ + This value must be a multiple of 0x100. */ +#define VECT_TAB_OFFSET \ + 0x00020000U /*!< Vector Table base offset field. \ + This value must be a multiple of 0x100. */ +#endif +#else +/*!< Uncomment this line for user vector table remap in Sram else user remap + will be done in Flash. */ +/* #define VECT_TAB_SRAM */ +#if defined(VECT_TAB_SRAM) +#define VECT_TAB_BASE_ADDRESS \ + SRAM1_BASE /*!< Vector Table base address field. \ + This value must be a multiple of 0x200. */ +#define VECT_TAB_OFFSET \ + 0x00000000U /*!< Vector Table base offset field. \ + This value must be a multiple of 0x200. */ +#else +#define VECT_TAB_BASE_ADDRESS \ + FLASH_BASE /*!< Vector Table base address field. \ + This value must be a multiple of 0x200. */ +#define VECT_TAB_OFFSET \ + 0x00000000U /*!< Vector Table base offset field. \ + This value must be a multiple of 0x200. */ +#endif +#endif +#endif +#endif /** @addtogroup STM32WBxx_System_Private_Macros * @{ */ @@ -137,12 +185,12 @@ * @{ */ /* The SystemCoreClock variable is updated in three ways: - 1) by calling CMSIS function SystemCoreClockUpdate() - 2) by calling HAL API function HAL_RCC_GetHCLKFreq() - 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency - Note: If you use this function to configure the system clock; then there - is no need to call the 2 first functions listed above, since SystemCoreClock - variable is updated automatically. + 1) by calling CMSIS function SystemCoreClockUpdate() + 2) by calling HAL API function HAL_RCC_GetHCLKFreq() + 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency + Note: If you use this function to configure the system clock; then there + is no need to call the 2 first functions listed above, since SystemCoreClock + variable is updated automatically. */ uint32_t SystemCoreClock = 4000000UL; /*CPU1: M4 on MSI clock after startup (4MHz)*/ @@ -177,6 +225,18 @@ const uint32_t SmpsPrescalerTable[4UL][6UL] = { { 1UL, 3UL, 2UL, 2UL, 1UL, 2UL } /** @addtogroup STM32WBxx_System_Private_Functions * @{ */ +#if (OTA_SUPPORT == 1) +#if defined(__ICCARM__) +extern uint32_t __vector_table; +#define INTVECT_START ((uint32_t) &__vector_table) +#elif defined(__CC_ARM) || defined(__ARMCC_VERSION) +extern void * __Vectors; +#define INTVECT_START ((uint32_t) &__Vectors) +#elif defined(__GNUC__) +extern void * g_pfnVectors; +#define INTVECT_START ((uint32_t) &g_pfnVectors) +#endif +#endif /** * @brief Setup the microcontroller system. @@ -187,10 +247,46 @@ void SystemInit(void) { OTP_ID0_t * p_otp; +#if (OTA_SUPPORT == 1) +#if defined(USER_VECT_TAB_ADDRESS) + /* Configure the Vector Table location add offset address ------------------*/ + /* Reuse information from map file */ + SCB->VTOR = INTVECT_START; /* Vector Table Relocation in Internal FLASH */ +#endif +#endif /* FPU settings ------------------------------------------------------------*/ #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) SCB->CPACR |= ((3UL << (10UL * 2UL)) | (3UL << (11UL * 2UL))); /* set CP10 and CP11 Full Access */ #endif + /* Reset the RCC clock configuration to the default reset state ------------*/ + /* Set MSION bit */ + RCC->CR |= RCC_CR_MSION; + + /* Reset CFGR register */ + RCC->CFGR = 0x00070000U; + + /* Reset PLLSAI1ON, PLLON, HSECSSON, HSEON, HSION, and MSIPLLON bits */ + RCC->CR &= (uint32_t) 0xFAF6FEFBU; + + /*!< Reset LSI1 and LSI2 bits */ + RCC->CSR &= (uint32_t) 0xFFFFFFFAU; + + /*!< Reset HSI48ON bit */ + RCC->CRRCR &= (uint32_t) 0xFFFFFFFEU; + + /* Reset PLLCFGR register */ + RCC->PLLCFGR = 0x22041000U; + +#if defined(STM32WB55xx) || defined(STM32WB5Mxx) + /* Reset PLLSAI1CFGR register */ + RCC->PLLSAI1CFGR = 0x22041000U; +#endif + + /* Reset HSEBYP bit */ + RCC->CR &= 0xFFFBFFFFU; + + /* Disable all interrupts */ + RCC->CIER = 0x00000000; /** * Read HSE_Tuning from OTP @@ -296,7 +392,7 @@ void SystemCoreClockUpdate(void) case 0x0C: /* PLL used as system clock source */ /* PLL_VCO = (HSE_VALUE or HSI_VALUE or MSI_VALUE/ PLLM) * PLLN - SYSCLK = PLL_VCO / PLLR + SYSCLK = PLL_VCO / PLLR */ pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC); pllm = ((RCC->PLLCFGR & RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1UL; diff --git a/examples/platform/stm32/config_files/STM32WB5/FreeRTOSConfig.h b/examples/platform/stm32/config_files/STM32WB5/FreeRTOSConfig.h index aeb0ac1b8913da..e1ff4e30648486 100644 --- a/examples/platform/stm32/config_files/STM32WB5/FreeRTOSConfig.h +++ b/examples/platform/stm32/config_files/STM32WB5/FreeRTOSConfig.h @@ -76,7 +76,7 @@ extern uint32_t SystemCoreClock; #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_COUNTING_SEMAPHORES 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 -#define configUSE_TICKLESS_IDLE 0 +#define configUSE_TICKLESS_IDLE 2 /* USER CODE BEGIN MESSAGE_BUFFER_LENGTH_TYPE */ /* Defaults to size_t for backward compatibility, but can be changed if lengths will always be less than the number of bytes in a size_t. */ @@ -172,19 +172,20 @@ standard names. */ /* USER CODE BEGIN Defines */ /* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */ // #define configOVERRIDE_DEFAULT_TICK_CONFIGURATION 1 /* required only for Keil but does not hurt otherwise */ -#define configGENERATE_RUN_TIME_STATS 1 +/*#define configGENERATE_RUN_TIME_STATS 1 -#if (configGENERATE_RUN_TIME_STATS == 1) +#if( configGENERATE_RUN_TIME_STATS == 1 ) -extern void RTOS_AppConfigureTimerForRuntimeStats(); + extern void RTOS_AppConfigureTimerForRuntimeStats(); -extern uint32_t RTOS_AppGetRuntimeCounterValueFromISR(); + extern uint32_t RTOS_AppGetRuntimeCounterValueFromISR(); -#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() RTOS_AppConfigureTimerForRuntimeStats() + #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() RTOS_AppConfigureTimerForRuntimeStats() -#define portGET_RUN_TIME_COUNTER_VALUE() RTOS_AppGetRuntimeCounterValueFromISR() + #define portGET_RUN_TIME_COUNTER_VALUE() RTOS_AppGetRuntimeCounterValueFromISR() #endif +*/ /* USER CODE END Defines */ diff --git a/examples/platform/stm32/config_files/STM32WB5/matter_config.h b/examples/platform/stm32/config_files/STM32WB5/matter_config.h index e89e44b64fda3b..987b9273e3af5d 100644 --- a/examples/platform/stm32/config_files/STM32WB5/matter_config.h +++ b/examples/platform/stm32/config_files/STM32WB5/matter_config.h @@ -1,19 +1,29 @@ -/** - ****************************************************************************** - * @file matter_config.h - * @author MCD Application Team - * @brief config file for mbedtls - ****************************************************************************** - * @attention - * - * Copyright (c) 2019-2021 STMicroelectronics. - * All rights reserved. +/* + * Copyright (c) 2020, The OpenThread Authors. + * All rights reserved. * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. * - ****************************************************************************** + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #pragma once @@ -67,10 +77,7 @@ extern "C" { #define MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED #define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED #define MBEDTLS_MD_C - #define MBEDTLS_NO_PLATFORM_ENTROPY -#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES - #define MBEDTLS_OID_C #define MBEDTLS_PEM_PARSE_C #define MBEDTLS_PEM_WRITE_C @@ -114,9 +121,9 @@ extern "C" { #define MBEDTLS_ENTROPY_MAX_SOURCES 2 /**< Maximum number of sources supported */ #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE -#define MBEDTLS_SSL_MAX_CONTENT_LEN 900 /**< Maxium fragment length in bytes */ +#define MBEDTLS_SSL_MAX_CONTENT_LEN 900 /**< Maximum fragment length in bytes */ #else -#define MBEDTLS_SSL_MAX_CONTENT_LEN 768 /**< Maxium fragment length in bytes */ +#define MBEDTLS_SSL_MAX_CONTENT_LEN 768 /**< Maximum fragment length in bytes */ #endif #define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 @@ -134,6 +141,7 @@ extern "C" { #include "mbedtls/check_config.h" #include "mbedtls/config_psa.h" +int mbedtls_ssl_safer_memcmp(const void * a, const void * b, size_t n); #ifdef __cplusplus } #endif diff --git a/examples/platform/stm32/ldscripts/STM32WB5MMGHX_FLASH.ld b/examples/platform/stm32/ldscripts/STM32WB5MMGHX_FLASH.ld index 56292a5b88ac1b..81e0246bd94d76 100644 --- a/examples/platform/stm32/ldscripts/STM32WB5MMGHX_FLASH.ld +++ b/examples/platform/stm32/ldscripts/STM32WB5MMGHX_FLASH.ld @@ -1,198 +1,198 @@ -/* -****************************************************************************** -** -** File : LinkerScript.ld -** -** Author : STM32CubeIDE -** -** Abstract : Linker script for STM32WB5MMG Device -** 1024Kbytes FLASH -** 256Kbytes RAM -** -** Set heap size, stack size and stack location according -** to application requirements. -** -** Set memory bank area and size if external memory is used. -** -** Target : STMicroelectronics STM32 -** -** Distribution: The file is distributed as is without any warranty -** of any kind. -** -***************************************************************************** -** @attention -** -** Copyright (c) 2020 STMicroelectronics. -** All rights reserved. -** -** This software is licensed under terms that can be found in the LICENSE file -** in the root directory of this software component. -** If no LICENSE file comes with this software, it is provided AS-IS. -** -***************************************************************************** -*/ - -/* Entry Point */ -ENTRY(Reset_Handler) - -/* Highest address of the user mode stack */ -_estack = 0x20026EC4; /* end of RAM */ -/* Generate a link error if heap and stack don't fit into RAM */ -_Min_Heap_Size = 0x400 ; /* required amount of heap */ -_Min_Stack_Size = 0x1000 ; /* required amount of stack */ - -/* Specify the memory areas */ -MEMORY -{ -FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 520K -NVM_MATTER : ORIGIN = 0x08082000, LENGTH = 0x3000 -RAM1 (xrw) : ORIGIN = 0x20000008, LENGTH = 0x26EC4 -RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K -} - -/* Define output sections */ -SECTIONS -{ - /* The startup code goes first into FLASH */ - .isr_vector : - { - . = ALIGN(4); - KEEP(*(.isr_vector)) /* Startup code */ - . = ALIGN(4); - } >FLASH - - /* The program code and other data goes into FLASH */ - .text : - { - . = ALIGN(4); - *(.text) /* .text sections (code) */ - *(.text*) /* .text* sections (code) */ - *(.glue_7) /* glue arm to thumb code */ - *(.glue_7t) /* glue thumb to arm code */ - *(.eh_frame) - - KEEP (*(.init)) - KEEP (*(.fini)) - - . = ALIGN(4); - _etext = .; /* define a global symbols at end of code */ - } >FLASH - - /* Constant data goes into FLASH */ - .rodata : - { - . = ALIGN(4); - *(.rodata) /* .rodata sections (constants, strings, etc.) */ - *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ - . = ALIGN(4); - } >FLASH - - .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH - .ARM : { - __exidx_start = .; - *(.ARM.exidx*) - __exidx_end = .; - } >FLASH - -_nvm_matter_init_base = LOADADDR(.nvm_matter); -_nvm_matter_init_length = SIZEOF(.nvm_matter); - - .nvm_matter : - { - . = ALIGN(4); - _nvm_matter_start = .; /* create a global symbol at nvm_matter start */ - *(.nvm_matter) /* .nvm_matter sections */ - *(.nvm_matter*) /* .nvm_matter* sections */ - . = ALIGN(4); - _nvm_matter_end = .; /* define a global symbols at end of nvm_matter */ - - } >NVM_MATTER - - - .preinit_array : - { - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP (*(.preinit_array*)) - PROVIDE_HIDDEN (__preinit_array_end = .); - } >FLASH - .init_array : - { - PROVIDE_HIDDEN (__init_array_start = .); - KEEP (*(SORT(.init_array.*))) - KEEP (*(.init_array*)) - PROVIDE_HIDDEN (__init_array_end = .); - } >FLASH - .fini_array : - { - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP (*(SORT(.fini_array.*))) - KEEP (*(.fini_array*)) - PROVIDE_HIDDEN (__fini_array_end = .); - } >FLASH - - /* used by the startup to initialize data */ - _sidata = LOADADDR(.data); - - /* Initialized data sections goes into RAM, load LMA copy after code */ - .data : - { - . = ALIGN(4); - _sdata = .; /* create a global symbol at data start */ - *(.data) /* .data sections */ - *(.data*) /* .data* sections */ - *(.RamFunc) /* .RamFunc sections */ - *(.RamFunc*) /* .RamFunc* sections */ - . = ALIGN(4); - _edata = .; /* define a global symbol at data end */ - } >RAM1 AT> FLASH - - /* Uninitialized data section */ - . = ALIGN(4); - .bss : - { - /* This is used by the startup in order to initialize the .bss section */ - _sbss = .; /* define a global symbol at bss start */ - __bss_start__ = _sbss; - *(.bss) - *(.bss*) - *(COMMON) - - . = ALIGN(4); - _ebss = .; /* define a global symbol at bss end */ - __bss_end__ = _ebss; - } >RAM1 - - /* User_heap_stack section, used to check that there is enough RAM left */ - ._user_heap_stack : - { - . = ALIGN(8); - PROVIDE ( end = . ); - PROVIDE ( _end = . ); - . = . + _Min_Heap_Size; - . = . + _Min_Stack_Size; - . = ALIGN(8); - } >RAM1 - - /* Remove information from the standard libraries */ - /DISCARD/ : - { - libc.a ( * ) - libm.a ( * ) - libgcc.a ( * ) - } - - .ARM.attributes 0 : { *(.ARM.attributes) } - MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM_SHARED - MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED - MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED - - - - - - -} - - - - +/* +****************************************************************************** +** +** File : LinkerScript.ld +** +** Author : STM32CubeIDE +** +** Abstract : Linker script for STM32WB5MMG Device +** 1024Kbytes FLASH +** 256Kbytes RAM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +** Distribution: The file is distributed as is without any warranty +** of any kind. +** +***************************************************************************** +** @attention +** +** Copyright (c) 2020 STMicroelectronics. +** All rights reserved. +** +** This software is licensed under terms that can be found in the LICENSE file +** in the root directory of this software component. +** If no LICENSE file comes with this software, it is provided AS-IS. +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = 0x20026EC4; /* end of RAM */ +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0x400 ; /* required amount of heap */ +_Min_Stack_Size = 0x1000 ; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ +FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 520K +NVM_MATTER : ORIGIN = 0x08082000, LENGTH = 0x3000 +RAM1 (xrw) : ORIGIN = 0x20000008, LENGTH = 0x26EC4 +RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + /* Constant data goes into FLASH */ + .rodata : + { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >FLASH + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + +_nvm_matter_init_base = LOADADDR(.nvm_matter); +_nvm_matter_init_length = SIZEOF(.nvm_matter); + + .nvm_matter : + { + . = ALIGN(4); + _nvm_matter_start = .; /* create a global symbol at nvm_matter start */ + *(.nvm_matter) /* .nvm_matter sections */ + *(.nvm_matter*) /* .nvm_matter* sections */ + . = ALIGN(4); + _nvm_matter_end = .; /* define a global symbols at end of nvm_matter */ + + } >NVM_MATTER + + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + *(.RamFunc) /* .RamFunc sections */ + *(.RamFunc*) /* .RamFunc* sections */ + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM1 AT> FLASH + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss section */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM1 + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } >RAM1 + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } + MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM_SHARED + MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED + MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED + + + + + + +} + + + + diff --git a/examples/shell/qpg/BUILD.gn b/examples/shell/qpg/BUILD.gn index d325be35e1dd2c..ec477e194bbf12 100644 --- a/examples/shell/qpg/BUILD.gn +++ b/examples/shell/qpg/BUILD.gn @@ -66,7 +66,7 @@ qpg_executable("shell_app") { defines = [] - ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}/QorvoStack_${qpg_target_ic}.ld" + ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}${qpg_flavour}/QorvoStack_${qpg_target_ic}${qpg_flavour}.ld" inputs = [ ldscript ] diff --git a/examples/shell/qpg/args.gni b/examples/shell/qpg/args.gni index cc3d97a0432e43..478e91d9cb05a6 100644 --- a/examples/shell/qpg/args.gni +++ b/examples/shell/qpg/args.gni @@ -29,7 +29,6 @@ chip_stack_lock_tracking = "none" matter_device_vid = "0xFFF1" matter_device_pid = "0x8006" -matter_device_software_version = "0x0001" -matter_device_software_version_string = "1.0" +matter_ota_test_image = false chip_openthread_ftd = false diff --git a/examples/thermostat/infineon/cyw30739/README.md b/examples/thermostat/infineon/cyw30739/README.md index 44052add4d0ed4..6d37d24eb920ef 100644 --- a/examples/thermostat/infineon/cyw30739/README.md +++ b/examples/thermostat/infineon/cyw30739/README.md @@ -18,6 +18,7 @@ An example showing the use of Matter on the Infineon CYW30739 platform. - [Commissionable Data](#commissionable-data) - [Device Information](#device-information) - [DAC / DAC Key / PAI Certificate / Certificate Declaration](#dac--dac-key--pai-certificate--certificate-declaration) + - [Use Provisioned Optiga Trust M](#use-provisioned-optiga-trust-m) - [Flashing the Application](#flashing-the-application) - [Enter Recovery Mode](#enter-recovery-mode) - [Run Flash Script](#run-flash-script) @@ -163,6 +164,29 @@ keys, and CD by the following arguments: 'matter_cd="/path/to/cd.der"' ``` +### Use Provisioned Optiga Trust M + +For boards supported by Optiga Trust M, CYW30739 will provision factory data to +the Optiga Trust M by default for easy development. + +The Optiga Trust M on a production board should come with provisioned factory +data. To ensure its optimal use, please configure the Optiga using the following +arguments: + +- `use_provisioned_optiga`, `optiga_dac_object_id`, + `optiga_dac_key_object_id`, `optiga_pai_cert_object_id` + + ```bash + $ cd ~/connectedhomeip + $ scripts/examples/gn_build_example.sh examples/thermostat/infineon/cyw30739 out/cyw30739-thermostat \ + 'optiga_dac_object_id="0xe0e0"' \ + 'optiga_dac_key_object_id="0xe0f0"' \ + 'optiga_pai_cert_object_id="0xe0e8"' + ``` + +The developer must set the object IDs to corresponding values matching the +configurations used in the Optiga provisioning procedure. + ## Flashing the Application ### Enter Recovery Mode diff --git a/examples/thermostat/qpg/BUILD.gn b/examples/thermostat/qpg/BUILD.gn index 9989e349fc6c86..11a0317a6abc0f 100644 --- a/examples/thermostat/qpg/BUILD.gn +++ b/examples/thermostat/qpg/BUILD.gn @@ -136,7 +136,7 @@ qpg_executable("thermostat") { } } - ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}/QorvoStack_${qpg_target_ic}.ld" + ldscript = "${qpg_sdk_root}/Libraries/Qorvo/QorvoStack/gen/QorvoStack_${qpg_target_ic}${qpg_flavour}/QorvoStack_${qpg_target_ic}${qpg_flavour}.ld" inputs = [ ldscript ] diff --git a/examples/thermostat/qpg/args.gni b/examples/thermostat/qpg/args.gni index d5dcdfe217c635..693375e4388593 100644 --- a/examples/thermostat/qpg/args.gni +++ b/examples/thermostat/qpg/args.gni @@ -29,7 +29,7 @@ chip_enable_icd_server = true chip_stack_lock_tracking = "none" matter_device_vid = "0xFFF1" -matter_device_pid = "0x8006" +matter_device_pid = "0x8003" pw_log_BACKEND = "${chip_root}/src/lib/support/pw_log_chip" pw_assert_BACKEND = "$dir_pw_assert_log:check_backend" diff --git a/examples/thermostat/qpg/include/CHIPProjectConfig.h b/examples/thermostat/qpg/include/CHIPProjectConfig.h index afab6cdbd8ee89..5a134c1b0addd8 100644 --- a/examples/thermostat/qpg/include/CHIPProjectConfig.h +++ b/examples/thermostat/qpg/include/CHIPProjectConfig.h @@ -40,9 +40,18 @@ * CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION * * A uint32_t identifying the software version running on the device. + * First two bytes are reflecting the Matter standard + * Last two bytes are reflecting the SDK version of which the first nibble of the first byte represents the major + * version and the second nibble of the first byte has the minor number. The last byte holds the patch number. + * example for SDK v0.1.5 with Matter v1.2 standard: + * 0x01020105 */ #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION -#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x0003 // Can't be removed, needed for OTA file generation. +#ifndef OTA_TEST_IMAGE +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x01020105 +#else +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x01020106 +#endif #endif /** @@ -53,7 +62,11 @@ * {MAJOR_VERSION}.0d{MINOR_VERSION} */ #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING -#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.1" // Can't be removed, needed for OTA file generation. +#ifndef OTA_TEST_IMAGE +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.2-0.1.5" +#else +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.2-0.1.6" +#endif #endif /** diff --git a/examples/thermostat/qpg/src/AppTask.cpp b/examples/thermostat/qpg/src/AppTask.cpp index 7297541df32d4e..5bd1e9cc29ad9a 100644 --- a/examples/thermostat/qpg/src/AppTask.cpp +++ b/examples/thermostat/qpg/src/AppTask.cpp @@ -460,11 +460,15 @@ void AppTask::UpdateLEDs(void) // If the system has ble connection(s) uptill the stage above, THEN blink // the LEDs at an even rate of 100ms. // - // Otherwise, blink the LED ON for a very short time. + // Otherwise, turn the LED OFF. if (sIsThreadProvisioned && sIsThreadEnabled) { qvIO_LedSet(SYSTEM_STATE_LED, true); } + else if (sIsThreadProvisioned && !sIsThreadEnabled) + { + qvIO_LedBlink(SYSTEM_STATE_LED, 950, 50); + } else if (sHaveBLEConnections) { qvIO_LedBlink(SYSTEM_STATE_LED, 100, 100); @@ -476,7 +480,7 @@ void AppTask::UpdateLEDs(void) else { // not commissioned yet - qvIO_LedBlink(SYSTEM_STATE_LED, 50, 950); + qvIO_LedSet(SYSTEM_STATE_LED, false); } } diff --git a/examples/thermostat/qpg/zap/thermostaticRadiatorValve.matter b/examples/thermostat/qpg/zap/thermostaticRadiatorValve.matter index 26053604ec2daa..b47ca948e6c72f 100644 --- a/examples/thermostat/qpg/zap/thermostaticRadiatorValve.matter +++ b/examples/thermostat/qpg/zap/thermostaticRadiatorValve.matter @@ -2027,6 +2027,8 @@ endpoint 0 { ram attribute lastNetworkingStatus; ram attribute lastNetworkID; ram attribute lastConnectErrorValue; + callback attribute supportedThreadFeatures; + callback attribute threadVersion; ram attribute featureMap default = 2; ram attribute clusterRevision default = 1; diff --git a/examples/thermostat/qpg/zap/thermostaticRadiatorValve.zap b/examples/thermostat/qpg/zap/thermostaticRadiatorValve.zap index 2b61c6bdc90c24..a7f0c9cded77f6 100644 --- a/examples/thermostat/qpg/zap/thermostaticRadiatorValve.zap +++ b/examples/thermostat/qpg/zap/thermostaticRadiatorValve.zap @@ -1502,6 +1502,38 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "SupportedThreadFeatures", + "code": 9, + "mfgCode": null, + "side": "server", + "type": "ThreadCapabilitiesBitmap", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ThreadVersion", + "code": 10, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "FeatureMap", "code": 65532, diff --git a/examples/tv-app/android/java/ContentAppCommandDelegate.cpp b/examples/tv-app/android/java/ContentAppCommandDelegate.cpp index a4a8d09396ae53..199ec7761be0c8 100644 --- a/examples/tv-app/android/java/ContentAppCommandDelegate.cpp +++ b/examples/tv-app/android/java/ContentAppCommandDelegate.cpp @@ -197,7 +197,7 @@ void ContentAppCommandDelegate::FormatResponseData(CommandHandlerInterface::Hand } else { - handlerContext.mCommandHandler.AddResponseData(handlerContext.mRequestPath, launchResponse); + handlerContext.mCommandHandler.AddResponse(handlerContext.mRequestPath, launchResponse); } break; } @@ -211,7 +211,7 @@ void ContentAppCommandDelegate::FormatResponseData(CommandHandlerInterface::Hand } else { - handlerContext.mCommandHandler.AddResponseData(handlerContext.mRequestPath, navigateTargetResponse); + handlerContext.mCommandHandler.AddResponse(handlerContext.mRequestPath, navigateTargetResponse); } break; } @@ -225,7 +225,7 @@ void ContentAppCommandDelegate::FormatResponseData(CommandHandlerInterface::Hand } else { - handlerContext.mCommandHandler.AddResponseData(handlerContext.mRequestPath, playbackResponse); + handlerContext.mCommandHandler.AddResponse(handlerContext.mRequestPath, playbackResponse); } break; } @@ -244,7 +244,7 @@ void ContentAppCommandDelegate::FormatResponseData(CommandHandlerInterface::Hand } else { - handlerContext.mCommandHandler.AddResponseData(handlerContext.mRequestPath, getSetupPINresponse); + handlerContext.mCommandHandler.AddResponse(handlerContext.mRequestPath, getSetupPINresponse); } break; } diff --git a/examples/tv-app/android/java/MyUserPrompter-JNI.cpp b/examples/tv-app/android/java/MyUserPrompter-JNI.cpp index 6d586d99c1c67f..4eb0abe25645da 100644 --- a/examples/tv-app/android/java/MyUserPrompter-JNI.cpp +++ b/examples/tv-app/android/java/MyUserPrompter-JNI.cpp @@ -227,6 +227,15 @@ bool JNIMyUserPrompter::DisplaysPasscodeAndQRCode() return false; } +/** + * Called to prompt the user for consent to allow the app commissioneeName/vendorId/productId to be installed. + * For example "[commissioneeName] is requesting permission to install app to this TV, approve?" + */ +void JNIMyUserPrompter::PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) +{ + ChipLogError(Zcl, "JNIMyUserPrompter::PromptForAppInstallOKPermission Needs Implementation"); +} + /** * Called to display the given setup passcode to the user, * for commissioning the given commissioneeName with the given vendorId and productId, diff --git a/examples/tv-app/android/java/MyUserPrompter-JNI.h b/examples/tv-app/android/java/MyUserPrompter-JNI.h index 408346326a6fd6..3d2e7f14afb75e 100644 --- a/examples/tv-app/android/java/MyUserPrompter-JNI.h +++ b/examples/tv-app/android/java/MyUserPrompter-JNI.h @@ -29,6 +29,7 @@ class JNIMyUserPrompter : public UserPrompter void PromptForCommissionOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override; void PromptForCommissionPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, uint16_t pairingHint, const char * pairingInstruction) override; + void PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override; void HidePromptsOnCancel(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override; bool DisplaysPasscodeAndQRCode() override; void PromptWithCommissionerPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, uint32_t passcode, diff --git a/examples/tv-app/linux/README.md b/examples/tv-app/linux/README.md index 5cd2bcfc48b5ed..2fc85a9a8f25c8 100644 --- a/examples/tv-app/linux/README.md +++ b/examples/tv-app/linux/README.md @@ -107,6 +107,11 @@ id): $ app add 9050 (vendor id 9050) $ app remove 1 +You can also install or uninstall the app by using commands: + + $ app install 65521 32768 + $ app uninstall 65521 32768 + As an app platform, local apps can be used to facilitate commissioning using their AccountLogin clusters. The dummy apps have hardcoded setup codes - on a real device, these apps would communicate with a cloud service to obtain the diff --git a/examples/tv-app/tv-common/include/AppTv.h b/examples/tv-app/tv-common/include/AppTv.h index bd056ae6813a5f..34f5bd8dc4c958 100644 --- a/examples/tv-app/tv-common/include/AppTv.h +++ b/examples/tv-app/tv-common/include/AppTv.h @@ -91,6 +91,11 @@ class DLL_EXPORT ContentAppImpl : public ContentApp KeypadInputDelegate * GetKeypadInputDelegate() override { return &mKeypadInputDelegate; }; MediaPlaybackDelegate * GetMediaPlaybackDelegate() override { return &mMediaPlaybackDelegate; }; TargetNavigatorDelegate * GetTargetNavigatorDelegate() override { return &mTargetNavigatorDelegate; }; + bool MatchesPidVid(uint16_t productId, uint16_t vendorId) + { + return vendorId == mApplicationBasicDelegate.HandleGetVendorId() && + productId == mApplicationBasicDelegate.HandleGetProductId(); + } protected: ApplicationBasicManager mApplicationBasicDelegate; @@ -138,15 +143,13 @@ class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory uint16_t productId) override; void AddAdminVendorId(uint16_t vendorId); + // Add the app to the list of mContentApps + void InstallContentApp(uint16_t vendorId, uint16_t productId); + // Remove the app from the list of mContentApps + bool UninstallContentApp(uint16_t vendorId, uint16_t productId); protected: - ContentAppImpl mContentApps[APP_LIBRARY_SIZE] = { - ContentAppImpl("Vendor1", 1, "exampleid", 11, "Version1", "34567890"), - ContentAppImpl("Vendor2", 65521, "exampleString", 32768, "Version2", "20202021"), - ContentAppImpl("Vendor3", 9050, "App3", 22, "Version3", "20202021"), - ContentAppImpl("TestSuiteVendor", 1111, "applicationId", 22, "v2", "20202021") - }; - + std::vector> mContentApps; std::vector mAdminVendorIds{}; }; diff --git a/examples/tv-app/tv-common/shell/AppTvShellCommands.cpp b/examples/tv-app/tv-common/shell/AppTvShellCommands.cpp index ee85cf099ff380..b39e84ad40f89a 100644 --- a/examples/tv-app/tv-common/shell/AppTvShellCommands.cpp +++ b/examples/tv-app/tv-common/shell/AppTvShellCommands.cpp @@ -243,6 +243,55 @@ static CHIP_ERROR AppPlatformHandler(int argc, char ** argv) return CHIP_NO_ERROR; } + else if (strcmp(argv[0], "install") == 0) + { + if (argc < 2) + { + return PrintAllCommands(); + } + char * eptr; + + uint16_t vid = (uint16_t) strtol(argv[1], &eptr, 10); + uint16_t pid = 0; + if (argc >= 3) + { + pid = (uint16_t) strtol(argv[2], &eptr, 10); + } + ContentAppFactoryImpl * factory = GetContentAppFactoryImpl(); + factory->InstallContentApp(vid, pid); + + ChipLogProgress(DeviceLayer, "installed an app"); + + return CHIP_NO_ERROR; + } + else if (strcmp(argv[0], "uninstall") == 0) + { + if (argc < 2) + { + return PrintAllCommands(); + } + char * eptr; + + uint16_t vid = (uint16_t) strtol(argv[1], &eptr, 10); + uint16_t pid = 0; + if (argc >= 3) + { + pid = (uint16_t) strtol(argv[2], &eptr, 10); + } + ContentAppFactoryImpl * factory = GetContentAppFactoryImpl(); + bool isAppUninstalled = factory->UninstallContentApp(vid, pid); + + if (isAppUninstalled) + { + ChipLogProgress(DeviceLayer, "uninstalled an app"); + } + else + { + ChipLogProgress(DeviceLayer, "app not found."); + } + + return CHIP_NO_ERROR; + } else if (strcmp(argv[0], "add") == 0) { if (argc < 2) diff --git a/examples/tv-app/tv-common/src/AppTv.cpp b/examples/tv-app/tv-common/src/AppTv.cpp index 2bed28e34d5fb8..9987b4cdd67d6e 100644 --- a/examples/tv-app/tv-common/src/AppTv.cpp +++ b/examples/tv-app/tv-common/src/AppTv.cpp @@ -98,6 +98,12 @@ class MyUserPrompter : public UserPrompter // tv should override this with a dialog prompt inline void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) override { return; } + + // tv should override this with a dialog prompt + inline void PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override + { + return; + } }; MyUserPrompter gMyUserPrompter; @@ -146,6 +152,16 @@ class MyPasscodeService : public PasscodeService }; MyPasscodeService gMyPasscodeService; +class MyAppInstallationService : public AppInstallationService +{ + bool LookupTargetContentApp(uint16_t vendorId, uint16_t productId) override + { + return ContentAppPlatform::GetInstance().LoadContentAppByClient(vendorId, productId) != nullptr; + } +}; + +MyAppInstallationService gMyAppInstallationService; + class MyPostCommissioningListener : public PostCommissioningListener { void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, Messaging::ExchangeManager & exchangeMgr, @@ -527,19 +543,23 @@ ContentApp * ContentAppFactoryImpl::LoadContentApp(const CatalogVendorApp & vend { ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl: LoadContentAppByAppId catalogVendorId=%d applicationId=%s ", vendorApp.catalogVendorId, vendorApp.applicationId); + int index = 0; - for (size_t i = 0; i < ArraySize(mContentApps); ++i) + for (auto & contentApp : mContentApps) { - auto & app = mContentApps[i]; - ChipLogProgress(DeviceLayer, " Looking next=%s ", app.GetApplicationBasicDelegate()->GetCatalogVendorApp()->applicationId); - if (app.GetApplicationBasicDelegate()->GetCatalogVendorApp()->Matches(vendorApp)) + auto app = contentApp.get(); + + ChipLogProgress(DeviceLayer, " Looking next=%s ", app->GetApplicationBasicDelegate()->GetCatalogVendorApp()->applicationId); + if (app->GetApplicationBasicDelegate()->GetCatalogVendorApp()->Matches(vendorApp)) { - ContentAppPlatform::GetInstance().AddContentApp(&app, &contentAppEndpoint, Span(gDataVersions[i]), + ContentAppPlatform::GetInstance().AddContentApp(app, &contentAppEndpoint, Span(gDataVersions[index]), Span(gContentAppDeviceType)); - return &app; + return app; } + index++; } + ChipLogProgress(DeviceLayer, "LoadContentAppByAppId NOT FOUND catalogVendorId=%d applicationId=%s ", vendorApp.catalogVendorId, vendorApp.applicationId); @@ -551,6 +571,62 @@ void ContentAppFactoryImpl::AddAdminVendorId(uint16_t vendorId) mAdminVendorIds.push_back(vendorId); } +void ContentAppFactoryImpl::InstallContentApp(uint16_t vendorId, uint16_t productId) +{ + ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl: InstallContentApp vendorId=%d productId=%d ", vendorId, productId); + if (vendorId == 1 && productId == 11) + { + mContentApps.emplace_back( + std::make_unique("Vendor1", vendorId, "exampleid", productId, "Version1", "34567890")); + } + else if (vendorId == 65521 && productId == 32768) + { + mContentApps.emplace_back( + std::make_unique("Vendor2", vendorId, "exampleString", productId, "Version2", "20202021")); + } + else if (vendorId == 9050 && productId == 22) + { + mContentApps.emplace_back(std::make_unique("Vendor3", vendorId, "App3", productId, "Version3", "20202021")); + } + else if (vendorId == 1111 && productId == 22) + { + mContentApps.emplace_back( + std::make_unique("TestSuiteVendor", vendorId, "applicationId", productId, "v2", "20202021")); + } + else + { + mContentApps.emplace_back( + std::make_unique("NewAppVendor", vendorId, "newAppApplicationId", productId, "v2", "20202021")); + } +} + +bool ContentAppFactoryImpl::UninstallContentApp(uint16_t vendorId, uint16_t productId) +{ + ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl: UninstallContentApp vendorId=%d productId=%d ", vendorId, productId); + + int index = 0; + for (auto & contentApp : mContentApps) + { + + auto app = contentApp.get(); + + ChipLogProgress(DeviceLayer, "Looking next vid=%d pid=%d", app->GetApplicationBasicDelegate()->HandleGetVendorId(), + app->GetApplicationBasicDelegate()->HandleGetProductId()); + + if (app->MatchesPidVid(productId, vendorId)) + { + ChipLogProgress(DeviceLayer, "Found an app vid=%d pid=%d. Uninstalling it.", + app->GetApplicationBasicDelegate()->HandleGetVendorId(), + app->GetApplicationBasicDelegate()->HandleGetProductId()); + mContentApps.erase(mContentApps.begin() + index); + return true; + } + + index++; + } + return false; +} + Access::Privilege ContentAppFactoryImpl::GetVendorPrivilege(uint16_t vendorId) { for (size_t i = 0; i < mAdminVendorIds.size(); ++i) @@ -607,6 +683,10 @@ CHIP_ERROR AppTvInit() #if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED ContentAppPlatform::GetInstance().SetupAppPlatform(); ContentAppPlatform::GetInstance().SetContentAppFactory(&gFactory); + gFactory.InstallContentApp((uint16_t) 1, (uint16_t) 11); + gFactory.InstallContentApp((uint16_t) 65521, (uint16_t) 32768); + gFactory.InstallContentApp((uint16_t) 9050, (uint16_t) 22); + gFactory.InstallContentApp((uint16_t) 1111, (uint16_t) 22); uint16_t value; if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(value) != CHIP_NO_ERROR) { @@ -623,6 +703,7 @@ CHIP_ERROR AppTvInit() if (cdc != nullptr) { cdc->SetPasscodeService(&gMyPasscodeService); + cdc->SetAppInstallationService(&gMyAppInstallationService); cdc->SetUserPrompter(&gMyUserPrompter); cdc->SetPostCommissioningListener(&gMyPostCommissioningListener); } diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/MatterCastingPlayer-JNI.cpp b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/MatterCastingPlayer-JNI.cpp index d0409c6a27945c..b2597c883406d8 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/MatterCastingPlayer-JNI.cpp +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/MatterCastingPlayer-JNI.cpp @@ -120,9 +120,8 @@ JNI_METHOD(jobject, verifyOrEstablishConnection) matter::casting::core::ConnectionCallbacks connectionCallbacks; connectionCallbacks.mOnConnectionComplete = connectCallback; - // TODO: Verify why commissioningWindowTimeoutSec is a "unsigned long long int" type. Seems too big. - castingPlayer->VerifyOrEstablishConnection(connectionCallbacks, - static_cast(commissioningWindowTimeoutSec), idOptions); + castingPlayer->VerifyOrEstablishConnection(connectionCallbacks, static_cast(commissioningWindowTimeoutSec), + idOptions); return support::convertMatterErrorFromCppToJava(CHIP_NO_ERROR); } diff --git a/examples/tv-casting-app/linux/simple-app-helper.cpp b/examples/tv-casting-app/linux/simple-app-helper.cpp index 49f006aa9cf4ab..beef624eea5ff8 100644 --- a/examples/tv-casting-app/linux/simple-app-helper.cpp +++ b/examples/tv-casting-app/linux/simple-app-helper.cpp @@ -33,6 +33,11 @@ // VendorId of the Endpoint on the CastingPlayer that the CastingApp desires to interact with after connection const uint16_t kDesiredEndpointVendorId = 65521; +// EndpointId of the Endpoint on the CastingPlayer that the CastingApp desires to interact with after connection using the +// Commissioner-Generated passcode commissioning flow +const uint8_t kDesiredEndpointId = 1; +// Indicates that the Commissioner-Generated passcode commissioning flow is in progress. +bool gCommissionerGeneratedPasscodeFlowRunning = false; DiscoveryDelegateImpl * DiscoveryDelegateImpl::_discoveryDelegateImpl = nullptr; bool gAwaitingCommissionerPasscodeInput = false; @@ -244,6 +249,16 @@ CHIP_ERROR InitCommissionableDataProvider(LinuxCommissionableDataProvider & prov options.payload.discriminator.GetLongValue()); } +void LogEndpointsDetails(const std::vector> & endpoints) +{ + ChipLogProgress(AppServer, "simple-app-helper.cpp::LogEndpointsDetails() Number of Endpoints: %d", + static_cast(endpoints.size())); + for (const auto & endpoint : endpoints) + { + endpoint->LogDetail(); + } +} + void ConnectionHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * castingPlayer) { ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler()"); @@ -256,16 +271,39 @@ void ConnectionHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * ca "simple-app-helper.cpp::ConnectionHandler(): Failed to connect to CastingPlayer (ID: %s) with err %" CHIP_ERROR_FORMAT, targetCastingPlayer->GetId(), err.Format())); - ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler(): Successfully connected to CastingPlayer (ID: %s)", - castingPlayer->GetId()); - ChipLogProgress(AppServer, - "simple-app-helper.cpp::ConnectionHandler(): Triggering demo interactions with CastingPlayer (ID: %s)", - castingPlayer->GetId()); + if (gCommissionerGeneratedPasscodeFlowRunning) + { + ChipLogProgress(AppServer, + "simple-app-helper.cpp::ConnectionHandler(): Successfully connected to CastingPlayer (ID: %s) using " + "Commissioner-Generated passcode", + castingPlayer->GetId()); + ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler(): Desired Endpoint ID for demo interactions: 1"); + } + else + { + ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler(): Successfully connected to CastingPlayer (ID: %s)", + castingPlayer->GetId()); + ChipLogProgress(AppServer, + "simple-app-helper.cpp::ConnectionHandler(): Desired Endpoint Vendor ID for demo interactions: %d", + kDesiredEndpointVendorId); + } + ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler(): Getting endpoints avaiable for demo interactions"); std::vector> endpoints = castingPlayer->GetEndpoints(); + LogEndpointsDetails(endpoints); + // Find the desired Endpoint and auto-trigger some Matter Casting demo interactions auto it = std::find_if(endpoints.begin(), endpoints.end(), [](const matter::casting::memory::Strong & endpoint) { + if (gCommissionerGeneratedPasscodeFlowRunning) + { + // For the example Commissioner-Generated passcode commissioning flow, run demo interactions with + // the Endpoint with ID 1. For this flow, we commissioned with the Target Content Application + // with Vendor ID 1111. Since this target content application does not report its Endpoint's + // Vendor IDs, we find the desired endpoint based on the Endpoint ID. See + // connectedhomeip/examples/tv-app/tv-common/include/AppTv.h. + return endpoint->GetId() == kDesiredEndpointId; + } return endpoint->GetVendorId() == kDesiredEndpointVendorId; }); if (it != endpoints.end()) @@ -273,6 +311,11 @@ void ConnectionHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * ca // The desired endpoint is endpoints[index] unsigned index = (unsigned int) std::distance(endpoints.begin(), it); + ChipLogProgress( + AppServer, + "simple-app-helper.cpp::ConnectionHandler(): Triggering demo interactions with CastingPlayer (ID: %s). Endpoint ID: %d", + castingPlayer->GetId(), endpoints[index]->GetId()); + // demonstrate invoking a command InvokeContentLauncherLaunchURL(endpoints[index]); @@ -286,8 +329,8 @@ void ConnectionHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * ca { ChipLogError( AppServer, - "simple-app-helper.cpp::ConnectionHandler():Desired Endpoint Vendor Id (%d) not found on the CastingPlayer (ID: %s)", - kDesiredEndpointVendorId, castingPlayer->GetId()); + "simple-app-helper.cpp::ConnectionHandler():Desired Endpoint Vendor ID not found on the CastingPlayer (ID: %s)", + castingPlayer->GetId()); } } @@ -350,9 +393,14 @@ CHIP_ERROR CommandHandler(int argc, char ** argv) ChipLogError(AppServer, "Invalid casting player index provided: %lu", index)); targetCastingPlayer = castingPlayers.at(index); + gCommissionerGeneratedPasscodeFlowRunning = false; matter::casting::core::IdentificationDeclarationOptions idOptions; + chip::Protocols::UserDirectedCommissioning::TargetAppInfo targetAppInfo; + targetAppInfo.vendorId = kDesiredEndpointVendorId; + if (argc == 3) { + if (strcmp(argv[2], "commissioner-generated-passcode") == 0) { // Attempt Commissioner-Generated Passcode (commissioner-generated-passcode) commissioning flow only if the @@ -364,6 +412,14 @@ CHIP_ERROR CommandHandler(int argc, char ** argv) "Commissioner-Generated Passcode commissioning flow", index); idOptions.mCommissionerPasscode = true; + + // For the example Commissioner-Generated passcode commissioning flow, override the default Target Content + // Application Vendor ID, which is configured on the tv-app. This Target Content Application Vendor ID (1111), + // does not implement the AccountLogin cluster, which would otherwise auto commission using the + // Commissionee-Generated passcode upon recieving the IdentificationDeclaration Message. See + // connectedhomeip/examples/tv-app/tv-common/include/AppTv.h. + targetAppInfo.vendorId = 1111; + gCommissionerGeneratedPasscodeFlowRunning = true; } else { @@ -374,9 +430,8 @@ CHIP_ERROR CommandHandler(int argc, char ** argv) } } } - chip::Protocols::UserDirectedCommissioning::TargetAppInfo targetAppInfo; - targetAppInfo.vendorId = kDesiredEndpointVendorId; - CHIP_ERROR result = idOptions.addTargetAppInfo(targetAppInfo); + + CHIP_ERROR result = idOptions.addTargetAppInfo(targetAppInfo); if (result != CHIP_NO_ERROR) { ChipLogError(AppServer, "CommandHandler() request, failed to add targetAppInfo: %" CHIP_ERROR_FORMAT, result.Format()); @@ -430,11 +485,8 @@ CHIP_ERROR CommandHandler(int argc, char ** argv) err.Format()); } - matter::casting::core::ConnectionCallbacks connectionCallbacks; - connectionCallbacks.mOnConnectionComplete = ConnectionHandler; - // Continue Connecting to the target CastingPlayer with the user entered Commissioner-generated Passcode. - targetCastingPlayer->ContinueConnecting(connectionCallbacks, matter::casting::core::kCommissioningWindowTimeoutSec); + targetCastingPlayer->ContinueConnecting(); } else { diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp b/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp index b3075be95304d8..ff204923da5d40 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp +++ b/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.cpp @@ -29,9 +29,7 @@ namespace core { CastingPlayer * CastingPlayer::mTargetCastingPlayer = nullptr; -// TODO: Verify why commissioningWindowTimeoutSec is a "unsigned long long int" type. Seems too big. -void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCallbacks, - unsigned long long int commissioningWindowTimeoutSec, +void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCallbacks, uint16_t commissioningWindowTimeoutSec, IdentificationDeclarationOptions idOptions) { ChipLogProgress(AppServer, "CastingPlayer::VerifyOrEstablishConnection() called"); @@ -72,6 +70,25 @@ void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCa "CastingPlayer::VerifyOrEstablishConnection() CommissionerDeclarationCallback not provided in ConnectionCallbacks"); } + ChipLogProgress(AppServer, "CastingPlayer::VerifyOrEstablishConnection() verifying User Directed Commissioning (UDC) state"); + mIdOptions.LogDetail(); + if (!GetSupportsCommissionerGeneratedPasscode() && mIdOptions.mCommissionerPasscode) + { + ChipLogError(AppServer, + "CastingPlayer::VerifyOrEstablishConnection() the target CastingPlayer doesn't support Commissioner-Generated " + "passcode yet IdentificationDeclarationOptions.mCommissionerPasscode is set to true"); + SuccessOrExit(err = CHIP_ERROR_INVALID_ARGUMENT); + } + if (!matter::casting::core::CommissionerDeclarationHandler::GetInstance()->HasCommissionerDeclarationCallback() && + mIdOptions.mCommissionerPasscode) + { + ChipLogError( + AppServer, + "CastingPlayer::VerifyOrEstablishConnection() the CommissionerDeclarationHandler CommissionerDeclaration message " + "callback has not been set, yet IdentificationDeclarationOptions.mCommissionerPasscode is set to true"); + SuccessOrExit(err = CHIP_ERROR_INVALID_ARGUMENT); + } + // If *this* CastingPlayer was previously connected to, its nodeId, fabricIndex and other attributes should be present // in the CastingStore cache. If that is the case, AND, the cached data contains the endpoint desired by the client, if any, // as per IdentificationDeclarationOptions.mTargetAppInfos, simply Find or Re-establish the CASE session and return early. @@ -138,28 +155,11 @@ void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCa } else { - ChipLogProgress(AppServer, - "CastingPlayer::VerifyOrEstablishConnection() verifying User Directed Commissioning (UDC) state"); - mIdOptions.LogDetail(); - SuccessOrExit(err = support::ChipDeviceEventHandler::SetUdcStatus(true)); - - if (!GetSupportsCommissionerGeneratedPasscode() && mIdOptions.mCommissionerPasscode) - { - ChipLogError( - AppServer, - "CastingPlayer::VerifyOrEstablishConnection() the target CastingPlayer doesn't support Commissioner-Generated " - "passcode yet IdentificationDeclarationOptions.mCommissionerPasscode is set to true"); - SuccessOrExit(err = CHIP_ERROR_INVALID_ARGUMENT); - } - if (!matter::casting::core::CommissionerDeclarationHandler::GetInstance()->HasCommissionerDeclarationCallback() && - mIdOptions.mCommissionerPasscode) - { - ChipLogError(AppServer, - "CastingPlayer::VerifyOrEstablishConnection() the CommissionerDeclaration message callback has not been " - "set yet IdentificationDeclarationOptions.mCommissionerPasscode is set to true"); - SuccessOrExit(err = CHIP_ERROR_INVALID_ARGUMENT); - } - + // We need to call OpenBasicCommissioningWindow() for both Commissionee-Generated passcode commissioning flow and + // Commissioner-Generated passcode commissioning flow. Per the Matter spec (UserDirectedCommissioning), even if the + // Commissionee sends an IdentificationDeclaration with CommissionerPasscode set to true, the Commissioner will first + // attempt to use AccountLogin in order to obtain Passcode using rotatingID. If no Passcode is obtained, Commissioner + // displays a Passcode. ChipLogProgress(AppServer, "CastingPlayer::VerifyOrEstablishConnection() calling OpenBasicCommissioningWindow()"); SuccessOrExit(err = chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow( chip::System::Clock::Seconds16(mCommissioningWindowTimeoutSec))); @@ -178,33 +178,19 @@ void CastingPlayer::VerifyOrEstablishConnection(ConnectionCallbacks connectionCa } } -void CastingPlayer::ContinueConnecting(ConnectionCallbacks connectionCallbacks, - unsigned long long int commissioningWindowTimeoutSec) +void CastingPlayer::ContinueConnecting() { ChipLogProgress(AppServer, "CastingPlayer::ContinueConnecting()"); CHIP_ERROR err = CHIP_NO_ERROR; - SuccessOrExit(err = support::ChipDeviceEventHandler::SetUdcStatus(true)); - VerifyOrExit( - connectionCallbacks.mOnConnectionComplete != nullptr, - ChipLogError(AppServer, "CastingPlayer::ContinueConnecting() ConnectionCallbacks.mOnConnectionComplete was not provided")); - mConnectionState = CASTING_PLAYER_CONNECTING; - mOnCompleted = connectionCallbacks.mOnConnectionComplete; - mCommissioningWindowTimeoutSec = commissioningWindowTimeoutSec; - mTargetCastingPlayer = this; - - // Register the handler for Commissioner's CommissionerDeclaration messages. The CommissionerDeclaration messages provide - // information indicating the Commissioner's pre-commissioning state. - if (connectionCallbacks.mCommissionerDeclarationCallback != nullptr) - { - matter::casting::core::CommissionerDeclarationHandler::GetInstance()->SetCommissionerDeclarationCallback( - connectionCallbacks.mCommissionerDeclarationCallback); - } - else + // Verify that mOnCompleted is not nullptr. + VerifyOrExit(mOnCompleted != nullptr, ChipLogError(AppServer, "CastingPlayer::ContinueConnecting() mOnCompleted == nullptr")); + if (!matter::casting::core::CommissionerDeclarationHandler::GetInstance()->HasCommissionerDeclarationCallback()) { - ChipLogProgress( - AppServer, - "CastingPlayer::VerifyOrEstablishConnection() CommissionerDeclarationCallback not provided in ConnectionCallbacks"); + ChipLogProgress(AppServer, + "CastingPlayer::ContinueConnecting() CommissionerDeclaration message callback has not been set."); } + mConnectionState = CASTING_PLAYER_CONNECTING; + mTargetCastingPlayer = this; ChipLogProgress(AppServer, "CastingPlayer::ContinueConnecting() calling OpenBasicCommissioningWindow()"); SuccessOrExit(err = chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow( @@ -272,6 +258,8 @@ CHIP_ERROR CastingPlayer::SendUserDirectedCommissioningRequest() chip::Protocols::UserDirectedCommissioning::IdentificationDeclaration id = mIdOptions.buildIdentificationDeclarationMessage(); + ReturnErrorOnFailure(support::ChipDeviceEventHandler::SetUdcStatus(true)); + ReturnErrorOnFailure(chip::Server::GetInstance().SendUserDirectedCommissioningRequest( chip::Transport::PeerAddress::UDP(*ipAddressToUse, mAttributes.port, mAttributes.interfaceId), id)); @@ -401,6 +389,26 @@ void CastingPlayer::LogDetail() const } } +CastingPlayer::CastingPlayer(const CastingPlayer & other) : + std::enable_shared_from_this(other), mEndpoints(other.mEndpoints), mConnectionState(other.mConnectionState), + mAttributes(other.mAttributes), mIdOptions(other.mIdOptions), + mCommissioningWindowTimeoutSec(other.mCommissioningWindowTimeoutSec), mOnCompleted(other.mOnCompleted) +{} + +CastingPlayer & CastingPlayer::operator=(const CastingPlayer & other) +{ + if (this != &other) + { + mAttributes = other.mAttributes; + mEndpoints = other.mEndpoints; + mConnectionState = other.mConnectionState; + mIdOptions = other.mIdOptions; + mCommissioningWindowTimeoutSec = other.mCommissioningWindowTimeoutSec; + mOnCompleted = other.mOnCompleted; + } + return *this; +} + ConnectionContext::ConnectionContext(void * clientContext, core::CastingPlayer * targetCastingPlayer, chip::OnDeviceConnected onDeviceConnectedFn, chip::OnDeviceConnectionFailure onDeviceConnectionFailureFn) diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.h b/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.h index 14a55e045fcb83..d5bbcd46006920 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.h +++ b/examples/tv-casting-app/tv-casting-common/core/CastingPlayer.h @@ -38,8 +38,8 @@ namespace core { const int kPortMaxLength = 5; // port is uint16_t // +1 for the : between the hostname and the port. -const int kIdMaxLength = chip::Dnssd::kHostNameMaxLength + kPortMaxLength + 1; -const unsigned long long int kCommissioningWindowTimeoutSec = 3 * 60; // 3 minutes +const int kIdMaxLength = chip::Dnssd::kHostNameMaxLength + kPortMaxLength + 1; +const uint16_t kCommissioningWindowTimeoutSec = 3 * 60; // 3 minutes /** * @brief Describes an Endpoint that the client wants to connect to @@ -111,46 +111,81 @@ class CastingPlayer : public std::enable_shared_from_this return (compareResult == 0) ? 1 : 0; } + /** + * @brief Define the copy constructor + */ + CastingPlayer(const CastingPlayer & other); + + /** + * @brief Define the assignment operator + */ + CastingPlayer & operator=(const CastingPlayer & other); + /** * @return true if this CastingPlayer is connected to the CastingApp */ bool IsConnected() const { return mConnectionState == CASTING_PLAYER_CONNECTED; } /** - * @brief Verifies that a connection exists with this CastingPlayer, or triggers a new session - * request. If the CastingApp does not have the nodeId and fabricIndex of this CastingPlayer cached on disk, - * this will execute the User Directed Commissioning (UDC) process. + * @brief Verifies that a connection exists with this CastingPlayer, or triggers a new commissioning session request. If the + * CastingApp does not have the nodeId and fabricIndex of this CastingPlayer cached on disk, this will execute the User Directed + * Commissioning (UDC) process by sending an IdentificationDeclaration message to the Commissioner. For certain UDC features, + * where a Commissioner reply is expected, this API needs to be followed up with the ContinueConnecting() API defiend below. See + * the Matter UDC specification or parameter class definitions for details on features not included in the description below. + * + * @param connectionCallbacks contains the ConnectCallback (Required) and CommissionerDeclarationCallback (Optional) defiend in + * ConnectCallbacks.h. + * + * ConnectCallback: The callback called when the connection process has ended, regardless of whether it was successful or not. + * + * CommissionerDeclarationCallback: The callback called when the Commissionee receives a CommissionerDeclaration message from + * the Commissioner. This callback is needed to support UDC features where a reply from the Commissioner is expected. It + * provides information indicating the Commissioner’s pre-commissioning state. + * + * For example: During Commissioner-Generated passcode commissioning, the Commissioner replies with a CommissionerDeclaration + * message with PasscodeDialogDisplayed and CommissionerPasscode set to true. Given these Commissioner state details, the client + * is expected to perform some actions, detailed in the ContinueConnecting() API below, and then call the ContinueConnecting() + * API to complete the process. * - * @param connectionCallbacks contains the ConnectCallback and CommissionerDeclarationCallback (Optional). * @param commissioningWindowTimeoutSec (Optional) time (in sec) to keep the commissioning window open, if commissioning is * required. Needs to be >= kCommissioningWindowTimeoutSec. + * * @param idOptions (Optional) Parameters in the IdentificationDeclaration message sent by the Commissionee to the Commissioner. * These parameters specify the information relating to the requested commissioning session. + * + * For example: To invoke the Commissioner-Generated passcode commissioning flow, the client would call this API with + * IdentificationDeclarationOptions containing CommissionerPasscode set to true. See IdentificationDeclarationOptions.h for a + * complete list of optional parameters. + * * Furthermore, attributes (such as VendorId) describe the TargetApp that the client wants to interact with after commissioning. * If this value is passed in, VerifyOrEstablishConnection() will force UDC, in case the desired * TargetApp is not found in the on-device CastingStore. */ void VerifyOrEstablishConnection(ConnectionCallbacks connectionCallbacks, - unsigned long long int commissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec, - IdentificationDeclarationOptions idOptions = IdentificationDeclarationOptions()); + uint16_t commissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec, + IdentificationDeclarationOptions idOptions = IdentificationDeclarationOptions()); /** - * @brief Continues the UDC process during the Commissioner-Generated passcode commissioning flow by sending a second - * IdentificationDeclaration to Commissioner containing CommissionerPasscode and CommissionerPasscodeReady set to true. At this - * point it is assumed that the following have occurred: - * 1. Client has handled the Commissioner's CommissionerDecelration message with PasscodeDialogDisplayed and - * CommissionerPasscode set to true. - * 2. Client prompted user to input Passcode from Commissioner. - * 3. Client has updated the commissioning session's PAKE verifier using the user input Passcode by updating the CastingApps + * @brief This is a continuation of the Commissioner-Generated passcode commissioning flow started via the + * VerifyOrEstablishConnection() API above. It continues the UDC process by sending a second IdentificationDeclaration message + * to Commissioner containing CommissionerPasscode and CommissionerPasscodeReady set to true. At this point it is assumed that + * the following have occurred: + * + * 1. Client (Commissionee) has sent the first IdentificationDeclaration message, via VerifyOrEstablishConnection(), to the + * Commissioner containing CommissionerPasscode set to true. + * 2. Commissioner generated and displayed a passcode. + * 3. The Commissioner replied with a CommissionerDecelration message with PasscodeDialogDisplayed and CommissionerPasscode set + * to true. + * 3. Client has handled the Commissioner's CommissionerDecelration message. + * 4. Client prompted user to input Passcode from Commissioner. + * 5. Client has updated the commissioning session's PAKE verifier using the user input Passcode by updating the CastingApps * CommissionableDataProvider * (matter::casting::core::CastingApp::GetInstance()->UpdateCommissionableDataProvider(CommissionableDataProvider)). * - * @param connectionCallbacks contains the ConnectCallback and CommissionerDeclarationCallback (Optional). - * @param commissioningWindowTimeoutSec (Optional) time (in sec) to keep the commissioning window open, if commissioning is - * required. Needs to be >= kCommissioningWindowTimeoutSec. + * The same connectionCallbacks and commissioningWindowTimeoutSec parameters passed into VerifyOrEstablishConnection() will be + * used. */ - void ContinueConnecting(ConnectionCallbacks connectionCallbacks, - unsigned long long int commissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec); + void ContinueConnecting(); /** * @brief Sets the internal connection state of this CastingPlayer to "disconnected" @@ -220,8 +255,8 @@ class CastingPlayer : public std::enable_shared_from_this CastingPlayerAttributes mAttributes; IdentificationDeclarationOptions mIdOptions; static CastingPlayer * mTargetCastingPlayer; - unsigned long long int mCommissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec; - ConnectCallback mOnCompleted = {}; + uint16_t mCommissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec; + ConnectCallback mOnCompleted = {}; /** * @brief resets this CastingPlayer's state and calls mOnCompleted with the CHIP_ERROR. Also, after calling mOnCompleted, it diff --git a/examples/tv-casting-app/tv-casting-common/core/CommissionerDeclarationHandler.cpp b/examples/tv-casting-app/tv-casting-common/core/CommissionerDeclarationHandler.cpp index bb9ecc3c4c57d9..22e7d49cdb7a5d 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CommissionerDeclarationHandler.cpp +++ b/examples/tv-casting-app/tv-casting-common/core/CommissionerDeclarationHandler.cpp @@ -38,7 +38,7 @@ CommissionerDeclarationHandler * CommissionerDeclarationHandler::GetInstance() } // TODO: In the following PRs. Implement setHandler() for CommissionerDeclaration messages and expose messages to higher layers for -// Linux(DONE), Android(pending) and iOS(pending). +// Linux, Android and iOS. void CommissionerDeclarationHandler::OnCommissionerDeclarationMessage( const chip::Transport::PeerAddress & source, chip::Protocols::UserDirectedCommissioning::CommissionerDeclaration cd) { diff --git a/examples/tv-casting-app/tv-casting-common/core/Endpoint.h b/examples/tv-casting-app/tv-casting-common/core/Endpoint.h index 4da8cd56210b07..fc18267cf466be 100644 --- a/examples/tv-casting-app/tv-casting-common/core/Endpoint.h +++ b/examples/tv-casting-app/tv-casting-common/core/Endpoint.h @@ -145,6 +145,12 @@ class Endpoint : public std::enable_shared_from_this } return nullptr; } + + void LogDetail() const + { + ChipLogProgress(AppServer, "Endpoint::LogDetail() Endpoint ID: %d, Vendor ID: %d, Product ID: %d", mAttributes.mId, + mAttributes.mVendorId, mAttributes.mProductId); + } }; }; // namespace core diff --git a/integrations/docker/images/base/chip-build/version b/integrations/docker/images/base/chip-build/version index 9a59e96c18355c..05ab2a03dbdb4f 100644 --- a/integrations/docker/images/base/chip-build/version +++ b/integrations/docker/images/base/chip-build/version @@ -1 +1 @@ -52 : [NXP] Update K32W0 SDK to 2.6.14 \ No newline at end of file +53 : [Tizen] Add libatomic.so to QEMU Docker image diff --git a/integrations/docker/images/stage-3/chip-build-tizen-qemu/Dockerfile b/integrations/docker/images/stage-3/chip-build-tizen-qemu/Dockerfile index ff63e5c1940a01..8c38d19c1b3ed8 100644 --- a/integrations/docker/images/stage-3/chip-build-tizen-qemu/Dockerfile +++ b/integrations/docker/images/stage-3/chip-build-tizen-qemu/Dockerfile @@ -84,6 +84,7 @@ RUN set -x \ # Add extra libraries to the root image && guestfish --rw -a $TIZEN_IOT_IMAGE_ROOT -m /dev/sda copy-in \ $TIZEN_SDK_TOOLCHAIN/arm-tizen-linux-gnueabi/lib/libasan.so.* \ + $TIZEN_SDK_TOOLCHAIN/arm-tizen-linux-gnueabi/lib/libatomic.so.* \ $TIZEN_SDK_TOOLCHAIN/arm-tizen-linux-gnueabi/lib/libubsan.so.* \ $TIZEN_SDK_SYSROOT/usr/lib/libbluetooth-api.so.* \ $TIZEN_SDK_SYSROOT/usr/lib/libcapi-network-bluetooth.so.* \ diff --git a/ruff.toml b/ruff.toml index fec7608d707987..a33a97ef467d3c 100644 --- a/ruff.toml +++ b/ruff.toml @@ -15,5 +15,5 @@ line-length = 132 [lint] select = ["E4", "E7", "E9", "F"] ignore = [ - "E721" # We use is for good reasons + "E721" # We use it for good reasons ] diff --git a/scripts/build/builders/qpg.py b/scripts/build/builders/qpg.py index e641b6f58df4ec..ef3b6745058e02 100644 --- a/scripts/build/builders/qpg.py +++ b/scripts/build/builders/qpg.py @@ -88,6 +88,16 @@ def GnArgName(self): raise Exception('Unknown board #: %r' % self) +class QpgFlavour(Enum): + EXT_FLASH = 1 + + def GnFlavourName(self): + if self == QpgFlavour.EXT_FLASH: + return '_ext_flash' + else: + raise Exception('Unknown flavour #: %r' % self) + + class QpgBuilder(GnBuilder): def __init__(self, @@ -95,6 +105,7 @@ def __init__(self, runner, app: QpgApp = QpgApp.LIGHT, board: QpgBoard = QpgBoard.QPG6105, + flavour: QpgFlavour = QpgFlavour.EXT_FLASH, enable_rpcs: bool = False, update_image: bool = False): super(QpgBuilder, self).__init__( @@ -102,15 +113,16 @@ def __init__(self, runner=runner) self.app = app self.board = board + self.flavour = flavour self.enable_rpcs = enable_rpcs self.update_image = update_image def GnBuildArgs(self): - args = ['qpg_target_ic=\"%s\"' % self.board.GnArgName()] + args = ['qpg_target_ic=\"%s\" qpg_flavour=\"%s\"' % (self.board.GnArgName(), self.flavour.GnFlavourName())] if self.enable_rpcs: args.append('import("//with_pw_rpc.gni")') if self.update_image: - args.append('matter_device_software_version_string=\"1.1_OTA_TEST\" matter_device_software_version=4') + args.append('matter_ota_test_image=true') return args def build_outputs(self): diff --git a/scripts/setup/requirements.infineon.txt b/scripts/setup/requirements.infineon.txt index bba66165a7654c..0a79ce1f3bfa63 100644 --- a/scripts/setup/requirements.infineon.txt +++ b/scripts/setup/requirements.infineon.txt @@ -1 +1,2 @@ leb128 +zcbor diff --git a/scripts/setup/requirements.nxp.txt b/scripts/setup/requirements.nxp.txt index 6db976c85da690..a78e1f0fc6230d 100644 --- a/scripts/setup/requirements.nxp.txt +++ b/scripts/setup/requirements.nxp.txt @@ -1,3 +1,4 @@ +crc>=7.0.0 jsonschema>=4.17.0 pycrypto>=2.6.1 pycryptodome>=3.20.0 diff --git a/scripts/setup/requirements.qpg.txt b/scripts/setup/requirements.qpg.txt index 3216862564e024..6670c1063d462d 100644 --- a/scripts/setup/requirements.qpg.txt +++ b/scripts/setup/requirements.qpg.txt @@ -1 +1,3 @@ -pylzma \ No newline at end of file +pylzma +intelhex +ecdsa \ No newline at end of file diff --git a/scripts/tests/run_tv_casting_test.py b/scripts/tests/run_tv_casting_test.py index c2492c5cb8818d..074d4592d59afe 100644 --- a/scripts/tests/run_tv_casting_test.py +++ b/scripts/tests/run_tv_casting_test.py @@ -16,6 +16,7 @@ import logging import os +import re import subprocess import sys import tempfile @@ -31,11 +32,14 @@ TV_APP_MAX_START_WAIT_SEC = 2 # The maximum amount of time to commission the Linux tv-casting-app and the tv-app before timeout. -COMMISSIONING_STAGE_MAX_WAIT_SEC = 10 +COMMISSIONING_STAGE_MAX_WAIT_SEC = 15 # The maximum amount of time to test that the launchURL is sent from the Linux tv-casting-app and received on the tv-app before timeout. TEST_LAUNCHURL_MAX_WAIT_SEC = 10 +# The maximum amount of time to verify the subscription state in the Linux tv-casting-app output before timeout. +VERIFY_SUBSCRIPTION_STATE_MAX_WAIT_SEC = 10 + # File names of logs for the Linux tv-casting-app and the Linux tv-app. LINUX_TV_APP_LOGS = 'Linux-tv-app-logs.txt' LINUX_TV_CASTING_APP_LOGS = 'Linux-tv-casting-app-logs.txt' @@ -46,6 +50,10 @@ PRODUCT_ID = 0x8001 # Test product id DEVICE_TYPE_CASTING_VIDEO_PLAYER = 0x23 # Device type library 10.3: Casting Video Player +# Values to verify the subscription state against from the `ReportDataMessage` in the Linux tv-casting-app output. +CLUSTER_MEDIA_PLAYBACK = '0x506' # Application Cluster Spec 6.10.3 Cluster ID: Media Playback +ATTRIBUTE_CURRENT_PLAYBACK_STATE = '0x0000_0000' # Application Cluster Spec 6.10.6 Attribute ID: Current State of Playback + class ProcessManager: """A context manager for managing subprocesses. @@ -68,10 +76,30 @@ def __exit__(self, exception_type, exception_value, traceback): self.process.wait() +class LogValueExtractor: + """A utility class for extracting values from log lines. + + This class provides a centralized way to extract values from log lines and manage the error handling and logging process. + """ + + def __init__(self, casting_state: str, log_paths: List[str]): + self.casting_state = casting_state + self.log_paths = log_paths + + def extract_from(self, line: str, value_name: str): + if value_name in line: + try: + return extract_value_from_string(line, value_name, self.casting_state, self.log_paths) + except ValueError: + logging.error(f'Failed to extract `{value_name}` value from line: {line}') + handle_casting_failure(self.casting_state, self.log_paths) + return None + + def dump_temporary_logs_to_console(log_file_path: str): """Dump log file to the console; log file will be removed once the function exits.""" """Write the entire content of `log_file_path` to the console.""" - print('\nDumping logs from: ', log_file_path) + print(f'\nDumping logs from: {log_file_path}') with open(log_file_path, 'r') as file: for line in file: @@ -80,36 +108,74 @@ def dump_temporary_logs_to_console(log_file_path: str): def handle_casting_failure(casting_state: str, log_file_paths: List[str]): """Log '{casting_state} failed!' as error, dump log files to console, exit on error.""" - logging.error(casting_state + ' failed!') + logging.error(f'{casting_state} failed!') for log_file_path in log_file_paths: try: dump_temporary_logs_to_console(log_file_path) except Exception as e: - logging.exception(f"Failed to dump {log_file_path}: {e}") + logging.exception(f'Failed to dump {log_file_path}: {e}') sys.exit(1) -def extract_value_from_string(line: str) -> str: +def extract_value_from_string(line: str, value_name: str, casting_state: str, log_paths) -> str: """Extract and return value from given input string. - The string is expected to be in the following format as it is received - from the Linux tv-casting-app output: - \x1b[0;34m[1713741926895] [7276:9521344] [DIS] Vendor ID: 65521\x1b[0m - The integer value to be extracted here is 65521. - Or: - \x1b[0;34m[1714583616179] [7029:2386956] [SVR] device Name: Test TV casting app\x1b[0m - The substring to be extracted here is 'Test TV casting app'. + Some string examples as they are received from the Linux tv-casting-app and/or tv-app output: + 1. On 'darwin' machines: + \x1b[0;34m[1715206773402] [20056:2842184] [DMG] Cluster = 0x506,\x1b[0m + The substring to be extracted here is '0x506'. + + Or: + \x1b[0;32m[1714582264602] [77989:2286038] [SVR] Discovered Commissioner #0\x1b[0m + The integer value to be extracted here is '0'. + + Or: + \x1b[0;34m[1713741926895] [7276:9521344] [DIS] Vendor ID: 65521\x1b[0m + The integer value to be extracted here is '65521'. + + Or: + \x1b[0;34m[1714583616179] [7029:2386956] [SVR] device Name: Test TV casting app\x1b[0m + The substring to be extracted here is 'Test TV casting app'. + + 2. On 'linux' machines: + [1716224960.316809][6906:6906] CHIP:DMG: \t\t\t\t\tCluster = 0x506,\n + [1716224958.576320][6906:6906] CHIP:SVR: Discovered Commissioner #0 + [1716224958.576407][6906:6906] CHIP:DIS: \tVendor ID: 65521\n + [1716224959.580746][6906:6906] CHIP:SVR: \tdevice Name: Test TV casting app\n """ - value = line.split(':')[-1].strip().replace('\x1b[0m', '') + log_line_pattern = '' + if sys.platform == 'darwin': + log_line_pattern = r'\x1b\[0;\d+m\[\d+\] \[\d+:\d+\] \[[A-Z]{1,3}\] (.+)\x1b\[0m' + elif sys.platform == 'linux': + log_line_pattern = r'\[\d+\.\d+\]\[\d+:\d+\] [A-Z]{1,4}:[A-Z]{1,3}: (.+)' - return value + log_line_match = re.search(log_line_pattern, line) + + if log_line_match: + log_text_of_interest = log_line_match.group(1) + + if '=' in log_text_of_interest: + delimiter = '=' + elif '#' in log_text_of_interest: + delimiter = '#' + else: + delimiter = ':' + + return log_text_of_interest.split(delimiter)[-1].strip(' ,') + else: + raise ValueError(f'Could not extract {value_name} from the following line: {line}') def validate_value(casting_state: str, expected_value: Union[str, int], log_paths: List[str], line: str, value_name: str) -> Optional[str]: """Validate a value in a string against an expected value during a given casting state.""" - value = extract_value_from_string(line) + log_value_extractor = LogValueExtractor(casting_state, log_paths) + value = log_value_extractor.extract_from(line, value_name) + if not value: + logging.error(f'Failed to extract {value_name} value from the following line: {line}') + logging.error(f'Failed to validate against the expected {value_name} value: {expected_value}!') + handle_casting_failure(casting_state, log_paths) if isinstance(expected_value, int): value = int(value) @@ -154,8 +220,8 @@ def initiate_cast_request_success(tv_casting_app_info: Tuple[subprocess.Popen, T while True: # Check if we exceeded the maximum wait time for initiating 'cast request' from the Linux tv-casting-app to the Linux tv-app. if time.time() - start_wait_time > COMMISSIONING_STAGE_MAX_WAIT_SEC: - logging.error('The command `cast request ' + valid_discovered_commissioner_number + - '` was not issued to the Linux tv-casting-app process within the timeout.') + logging.error( + f'The command `cast request {valid_discovered_commissioner_number}` was not issued to the Linux tv-casting-app process within the timeout.') return False tv_casting_app_output_line = tv_casting_app_process.stdout.readline() @@ -170,13 +236,16 @@ def initiate_cast_request_success(tv_casting_app_info: Tuple[subprocess.Popen, T next_line = tv_casting_app_process.stdout.readline() linux_tv_casting_app_log_file.write(next_line) linux_tv_casting_app_log_file.flush() - logging.info('Sent `' + next_line.rstrip('\n') + '` to the Linux tv-casting-app process.') + next_line = next_line.rstrip('\n') + logging.info(f'Sent `{next_line}` to the Linux tv-casting-app process.') + return True -def extract_device_info_from_tv_casting_app(tv_casting_app_info: Tuple[subprocess.Popen, TextIO]) -> Tuple[Optional[str], Optional[int], Optional[int]]: +def extract_device_info_from_tv_casting_app(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], casting_state: str, log_paths: List[str]) -> Tuple[Optional[str], Optional[int], Optional[int]]: """Extract device information from the 'Identification Declaration' block in the Linux tv-casting-app output.""" tv_casting_app_process, linux_tv_casting_app_log_file = tv_casting_app_info + log_value_extractor = LogValueExtractor(casting_state, log_paths) device_name = None vendor_id = None @@ -186,14 +255,12 @@ def extract_device_info_from_tv_casting_app(tv_casting_app_info: Tuple[subproces linux_tv_casting_app_log_file.write(line) linux_tv_casting_app_log_file.flush() - if 'device Name' in line: - device_name = extract_value_from_string(line) - elif 'vendor id' in line: - vendor_id = extract_value_from_string(line) - vendor_id = int(vendor_id) - elif 'product id' in line: - product_id = extract_value_from_string(line) - product_id = int(product_id) + if value := log_value_extractor.extract_from(line, 'device Name'): + device_name = value + elif value := log_value_extractor.extract_from(line, 'vendor id'): + vendor_id = int(value) + elif value := log_value_extractor.extract_from(line, 'product id'): + product_id = int(value) if device_name and vendor_id and product_id: break @@ -211,7 +278,8 @@ def validate_identification_declaration_message_on_tv_app(tv_app_info: Tuple[sub while True: # Check if we exceeded the maximum wait time for validating the device information from the Linux tv-app to the corresponding values from the Linux tv-app. if time.time() - start_wait_time > COMMISSIONING_STAGE_MAX_WAIT_SEC: - logging.erro('The device information from the Linux tv-app output was not validated against the corresponding values from the Linux tv-casting-app output within the timeout.') + logging.error( + 'The device information from the Linux tv-app output was not validated against the corresponding values from the Linux tv-casting-app output within the timeout.') return False tv_app_line = tv_app_process.stdout.readline() @@ -221,7 +289,7 @@ def validate_identification_declaration_message_on_tv_app(tv_app_info: Tuple[sub linux_tv_app_log_file.flush() if 'Identification Declaration Start' in tv_app_line: - logging.info('"Identification Declaration" block from the Linux tv-app output:') + logging.info('Found the `Identification Declaration` block in the Linux tv-app output:') logging.info(tv_app_line.rstrip('\n')) parsing_identification_block = True elif parsing_identification_block: @@ -266,8 +334,8 @@ def validate_tv_casting_request_approval(tv_app_info: Tuple[subprocess.Popen, Te tv_app_line = tv_app_process.stdout.readline() linux_tv_app_log_file.write(tv_app_line) linux_tv_app_log_file.flush() - - logging.info('Sent `' + tv_app_line.rstrip('\n') + '` to the Linux tv-app process.') + tv_app_line = tv_app_line.rstrip('\n') + logging.info(f'Sent `{tv_app_line}` to the Linux tv-app process.') return True @@ -310,6 +378,55 @@ def validate_commissioning_success(tv_casting_app_info: Tuple[subprocess.Popen, return True +def parse_tv_casting_app_for_report_data_msg(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]): + """Parse the Linux tv-casting-app for `ReportDataMessage` block and return the first message block with valid `Cluster` and `Attribute` values.""" + tv_casting_app_process, linux_tv_casting_app_log_file = tv_casting_app_info + log_value_extractor = LogValueExtractor('Testing subscription', log_paths) + + continue_parsing = False + report_data_message = [] + + start_wait_time = time.time() + + while True: + # Check if we exceeded the maximum wait time to parse the Linux tv-casting-app output for `ReportDataMessage` block. + if time.time() - start_wait_time > VERIFY_SUBSCRIPTION_STATE_MAX_WAIT_SEC: + logging.error( + 'The relevant `ReportDataMessage` block for the MediaPlayback:CurrentState subscription was not found in the Linux tv-casting-app process within the timeout.') + report_data_message.clear() + return report_data_message + + tv_casting_line = tv_casting_app_process.stdout.readline() + + if tv_casting_line: + linux_tv_casting_app_log_file.write(tv_casting_line) + linux_tv_casting_app_log_file.flush() + + if 'ReportDataMessage =' in tv_casting_line: + report_data_message.append(tv_casting_line.rstrip('\n')) + continue_parsing = True + elif continue_parsing: + report_data_message.append(tv_casting_line.rstrip('\n')) + + if cluster_value := log_value_extractor.extract_from(tv_casting_line, 'Cluster ='): + if cluster_value != CLUSTER_MEDIA_PLAYBACK: + report_data_message.clear() + continue_parsing = False + + elif attribute_value := log_value_extractor.extract_from(tv_casting_line, 'Attribute ='): + if attribute_value != ATTRIBUTE_CURRENT_PLAYBACK_STATE: + report_data_message.clear() + continue_parsing = False + + elif 'InteractionModelRevision' in tv_casting_line: + # Capture the closing brace `}` of the `ReportDataMessage` block. + tv_casting_line = tv_casting_app_process.stdout.readline() + linux_tv_casting_app_log_file.write(tv_casting_line) + linux_tv_casting_app_log_file.flush() + report_data_message.append(tv_casting_line.rstrip('\n')) + return report_data_message + + def parse_tv_app_output_for_launchUrl_msg_success(tv_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]): """Parse the Linux tv-app output for the relevant string indicating that the launchUrl was received.""" @@ -359,7 +476,7 @@ def parse_tv_casting_app_output_for_launchUrl_msg_success(tv_casting_app_info: T linux_tv_casting_app_log_file.flush() if 'InvokeResponseMessage =' in tv_casting_line: - logging.info('Found the InvokeResponseMessage block in the Linux tv-casting-app output:') + logging.info('Found the `InvokeResponseMessage` block in the Linux tv-casting-app output:') logging.info(tv_casting_line.rstrip('\n')) continue_parsing_invoke_response_msg_block = True @@ -395,11 +512,11 @@ def test_discovery_fn(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], log_ linux_tv_casting_app_log_file.flush() # Fail fast if "No commissioner discovered" string found. - if "No commissioner discovered" in line: + if 'No commissioner discovered' in line: logging.error(line.rstrip('\n')) handle_casting_failure('Discovery', log_paths) - elif "Discovered Commissioner" in line: + elif 'Discovered Commissioner' in line: valid_discovered_commissioner = line.rstrip('\n') elif valid_discovered_commissioner: @@ -415,7 +532,7 @@ def test_discovery_fn(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], log_ # A valid commissioner has VENDOR_ID, PRODUCT_ID, and DEVICE TYPE in its list of entries. if valid_vendor_id and valid_product_id and valid_device_type: - logging.info('Found a valid commissioner in the Linux tv-casting-app logs:') + logging.info('Found a valid commissioner in the Linux tv-casting-app output:') logging.info(valid_discovered_commissioner) logging.info(valid_vendor_id) logging.info(valid_product_id) @@ -433,7 +550,14 @@ def test_commissioning_fn(valid_discovered_commissioner_number, tv_casting_app_i handle_casting_failure('Commissioning', log_paths) # Extract the values from the 'Identification Declaration' block in the tv-casting-app output that we want to validate against. - expected_device_name, expected_vendor_id, expected_product_id = extract_device_info_from_tv_casting_app(tv_casting_app_info) + expected_device_name, expected_vendor_id, expected_product_id = extract_device_info_from_tv_casting_app( + tv_casting_app_info, 'Commissioning', log_paths) + + if not expected_device_name or not expected_vendor_id or not expected_product_id: + logging.error('There is an error with the expected device info values that were extracted from the `Identification Declaration` block.') + logging.error( + f'expected_device_name: {expected_device_name}, expected_vendor_id: {expected_vendor_id}, expected_product_id: {expected_product_id}') + handle_casting_failure('Commissioning', log_paths) if not validate_identification_declaration_message_on_tv_app(tv_app_info, expected_device_name, expected_vendor_id, expected_product_id, log_paths): handle_casting_failure('Commissioning', log_paths) @@ -445,6 +569,23 @@ def test_commissioning_fn(valid_discovered_commissioner_number, tv_casting_app_i handle_casting_failure('Commissioning', log_paths) +def test_subscription_fn(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]): + """Test the subscription state of the Linux tv-casting-app by validating the `ReportDataMessage` block.""" + + valid_report_data_msg = parse_tv_casting_app_for_report_data_msg(tv_casting_app_info, log_paths) + + if valid_report_data_msg: + logging.info('Found the `ReportDataMessage` block in the Linux tv-casting-app output:') + + for line in valid_report_data_msg: + logging.info(line) + + logging.info('Testing subscription success!\n') + valid_report_data_msg.clear() + else: + handle_casting_failure('Testing subscription', log_paths) + + def test_launchUrl_fn(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], tv_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]): """Test that the Linux tv-casting-app sent the launchUrl and that the Linux tv-app received the launchUrl.""" @@ -454,7 +595,7 @@ def test_launchUrl_fn(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], tv_a if not parse_tv_casting_app_output_for_launchUrl_msg_success(tv_casting_app_info, log_paths): handle_casting_failure('Testing launchUrl', log_paths) - logging.info('Testing launchUrl success!\n') + logging.info('Testing launchUrl success!') @click.command() @@ -499,12 +640,15 @@ def test_casting_fn(tv_app_rel_path, tv_casting_app_rel_path): handle_casting_failure('Discovery', log_paths) # We need the valid discovered commissioner number to continue with commissioning. - # Example string: \x1b[0;32m[1714582264602] [77989:2286038] [SVR] Discovered Commissioner #0\x1b[0m - # The value '0' will be extracted from the string. - valid_discovered_commissioner_number = valid_discovered_commissioner.split('#')[-1].replace('\x1b[0m', '') + log_value_extractor = LogValueExtractor('Commissioning', log_paths) + valid_discovered_commissioner_number = log_value_extractor.extract_from( + valid_discovered_commissioner, 'Discovered Commissioner #') + if not valid_discovered_commissioner_number: + logging.error(f'Failed to find `Discovered Commissioner #` in line: {valid_discovered_commissioner}') + handle_casting_failure('Commissioning', log_paths) test_commissioning_fn(valid_discovered_commissioner_number, tv_casting_app_info, tv_app_info, log_paths) - + test_subscription_fn(tv_casting_app_info, log_paths) test_launchUrl_fn(tv_casting_app_info, tv_app_info, log_paths) diff --git a/scripts/tools/bouffalolab/factory_qrcode.py b/scripts/tools/bouffalolab/factory_qrcode.py index 1a7f6303495775..0e12d8349d4c91 100644 --- a/scripts/tools/bouffalolab/factory_qrcode.py +++ b/scripts/tools/bouffalolab/factory_qrcode.py @@ -20,13 +20,13 @@ try: import qrcode - from generate_setup_payload import CommissioningFlow, SetupPayload + from SetupPayload import CommissioningFlow, SetupPayload except ImportError: SDK_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) sys.path.append(os.path.join(SDK_ROOT, "src/setup_payload/python")) try: import qrcode - from generate_setup_payload import CommissioningFlow, SetupPayload + from SetupPayload import CommissioningFlow, SetupPayload except ModuleNotFoundError or ImportError: no_onboarding_modules = True else: diff --git a/scripts/tools/generate_esp32_chip_factory_bin.py b/scripts/tools/generate_esp32_chip_factory_bin.py index 262be540a9bfed..eab2ec6b43c229 100755 --- a/scripts/tools/generate_esp32_chip_factory_bin.py +++ b/scripts/tools/generate_esp32_chip_factory_bin.py @@ -30,7 +30,7 @@ sys.path.insert(0, os.path.join(CHIP_TOPDIR, 'scripts', 'tools', 'spake2p')) from spake2p import generate_verifier # noqa: E402 isort:skip sys.path.insert(0, os.path.join(CHIP_TOPDIR, 'src', 'setup_payload', 'python')) -from generate_setup_payload import CommissioningFlow, SetupPayload # noqa: E402 isort:skip +from SetupPayload import CommissioningFlow, SetupPayload # noqa: E402 isort:skip if os.getenv('IDF_PATH'): sys.path.insert(0, os.path.join(os.getenv('IDF_PATH'), diff --git a/scripts/tools/linux_ip_namespace_setup.sh b/scripts/tools/linux_ip_namespace_setup.sh index 5d467b4b48c23e..f761eea3843fe8 100755 --- a/scripts/tools/linux_ip_namespace_setup.sh +++ b/scripts/tools/linux_ip_namespace_setup.sh @@ -124,7 +124,7 @@ function help() { echo "sudo /$file_name -r /" echo "" echo "Terminal 2:" - echo "/chip-device-ctrl" + echo "/chip-repl" echo "" echo "This script requires sudo for setup and requires access to ebtables-legacy" echo "to set up dual ipv4/ipv6 namespaces. Defaults to ipv6 only." diff --git a/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py b/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py index 78212cefd81e6e..ab70c69d7f9d53 100644 --- a/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py +++ b/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py @@ -32,13 +32,13 @@ try: import qrcode - from generate_setup_payload import CommissioningFlow, SetupPayload + from SetupPayload import CommissioningFlow, SetupPayload except ImportError: SDK_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) sys.path.append(os.path.join(SDK_ROOT, "src/setup_payload/python")) try: import qrcode - from generate_setup_payload import CommissioningFlow, SetupPayload + from SetupPayload import CommissioningFlow, SetupPayload except ModuleNotFoundError or ImportError: no_onboarding_modules = True else: diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 0eb7e1456e02e0..f49a40f398842a 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -179,9 +179,6 @@ static_library("interaction-model") { "ReadClient.h", # TODO: cpp is only included conditionally. Needs logic # fixing "ReadPrepareParams.h", - "RequiredPrivilege.h", - "StatusResponse.cpp", - "StatusResponse.h", "SubscriptionResumptionStorage.h", "TimedHandler.cpp", "TimedHandler.h", @@ -210,6 +207,7 @@ static_library("interaction-model") { public_deps = [ ":app_config", + ":command-handler", ":constants", ":paths", ":subscription-info-provider", @@ -248,8 +246,6 @@ static_library("interaction-model") { "dynamic_server/AccessControl.cpp", "dynamic_server/AccessControl.h", "dynamic_server/DynamicDispatcher.cpp", - "util/privilege-storage.cpp", - "util/privilege-storage.h", ] public_deps += [ @@ -301,6 +297,62 @@ static_library("attribute-access") { ] } +source_set("required-privileges") { + sources = [ "RequiredPrivilege.h" ] + + public_deps = [ + ":paths", + "${chip_root}/src/access:types", + ] + + if (chip_build_controller_dynamic_server) { + sources += [ + "util/privilege-storage.cpp", + "util/privilege-storage.h", + ] + + public_deps += [ + ":global-attributes", + "${chip_root}/src/access", + "${chip_root}/src/app/dynamic_server:mock-codegen-includes", + ] + + public_configs = [ ":config-controller-dynamic-server" ] + } +} + +source_set("status-response") { + sources = [ + "StatusResponse.cpp", + "StatusResponse.h", + ] + public_deps = [ + ":constants", + "${chip_root}/src/app/MessageDef", + "${chip_root}/src/messaging", + ] +} + +source_set("command-handler") { + sources = [ + "CommandHandler.cpp", + "CommandHandler.h", + "CommandHandlerExchangeInterface.h", + ] + + public_deps = [ + ":paths", + ":required-privileges", + ":status-response", + "${chip_root}/src/access:types", + "${chip_root}/src/app/MessageDef", + "${chip_root}/src/app/data-model", + "${chip_root}/src/app/util:callbacks", + "${chip_root}/src/lib/support", + "${chip_root}/src/messaging", + ] +} + # Note to developpers, instead of continuously adding files in the app librabry, it is recommand to create smaller source_sets that app can depend on. # This way, we can have a better understanding of dependencies and other componenets can depend on the different source_sets without needing to depend on the entire app library. static_library("app") { @@ -312,8 +364,6 @@ static_library("app") { "AttributePersistenceProvider.h", "ChunkedWriteCallback.cpp", "ChunkedWriteCallback.h", - "CommandHandler.cpp", - "CommandHandlerExchangeInterface.h", "CommandResponseHelper.h", "CommandResponseSender.cpp", "DefaultAttributePersistenceProvider.cpp", @@ -325,6 +375,8 @@ static_library("app") { "EventManagement.h", "FailSafeContext.cpp", "FailSafeContext.h", + "GenericEventManagementTestEventTriggerHandler.cpp", + "GenericEventManagementTestEventTriggerHandler.h", "OTAUserConsentCommon.h", "ReadHandler.cpp", "SafeAttributePersistenceProvider.h", @@ -336,7 +388,6 @@ static_library("app") { # (app depending on im and im including these headers): # Name with _ so that linter does not recognize it # "CommandResponseSender._h" - # "CommandHandler._h" # "ReadHandler._h", # "WriteHandler._h" ] diff --git a/src/app/CommandHandler.cpp b/src/app/CommandHandler.cpp index 86ef22ab886212..6077e0934721f7 100644 --- a/src/app/CommandHandler.cpp +++ b/src/app/CommandHandler.cpp @@ -15,21 +15,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/** - * @file - * This file defines object for a CHIP IM Invoke Command Handler - * - */ - -#include "CommandHandler.h" -#include "InteractionModelEngine.h" -#include "RequiredPrivilege.h" -#include "messaging/ExchangeContext.h" +#include #include #include #include +#include #include #include #include @@ -37,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/src/app/CommandHandler.h b/src/app/CommandHandler.h index cce19879a6c8a9..b06064c46a69be 100644 --- a/src/app/CommandHandler.h +++ b/src/app/CommandHandler.h @@ -30,9 +30,8 @@ #pragma once -#include "CommandPathRegistry.h" - #include +#include #include #include #include @@ -57,6 +56,32 @@ namespace chip { namespace app { +/// Defines an abstract class of something that can be encoded +/// into a TLV with a given data tag +class EncoderToTLV +{ +public: + virtual ~EncoderToTLV() = default; + + virtual CHIP_ERROR Encode(TLV::TLVWriter &, TLV::Tag tag) = 0; +}; + +/// An `EncoderToTLV` the uses `DataModel::Encode` to encode things. +/// +/// Generally useful to encode things like ::Commands::::Type +/// structures. +template +class DataModelEncoderToTLV : public EncoderToTLV +{ +public: + DataModelEncoderToTLV(const T & value) : mValue(value) {} + + virtual CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag) { return DataModel::Encode(writer, tag, mValue); } + +private: + const T & mValue; +}; + class CommandHandler { public: @@ -325,14 +350,37 @@ class CommandHandler * @param [in] aRequestCommandPath the concrete path of the command we are * responding to. * @param [in] aData the data for the response. + * + * NOTE: this is a convenience function for `AddResponseDataViaEncoder` */ template - CHIP_ERROR AddResponseData(const ConcreteCommandPath & aRequestCommandPath, const CommandData & aData) + inline CHIP_ERROR AddResponseData(const ConcreteCommandPath & aRequestCommandPath, const CommandData & aData) + { + DataModelEncoderToTLV encoder(aData); + return AddResponseDataViaEncoder(aRequestCommandPath, CommandData::GetCommandId(), encoder); + } + + /** + * API for adding a data response. The encoded is generally expected to encode + * a ClusterName::Commands::CommandName::Type struct, but any + * object should work. + * + * @param [in] aRequestCommandPath the concrete path of the command we are + * responding to. + * @param [in] commandId the command whose content is being encoded. + * @param [in] encoder - an encoder that places the command data structure for `commandId` + * into a TLV Writer. + * + * Most applications are likely to use `AddResponseData` as a more convenient + * one-call that auto-sets command ID and creates the underlying encoders. + */ + CHIP_ERROR AddResponseDataViaEncoder(const ConcreteCommandPath & aRequestCommandPath, CommandId commandId, + EncoderToTLV & encoder) { // Return early when response should not be sent out. VerifyOrReturnValue(ResponsesAccepted(), CHIP_NO_ERROR); - - return TryAddingResponse([&]() -> CHIP_ERROR { return TryAddResponseData(aRequestCommandPath, aData); }); + return TryAddingResponse( + [&]() -> CHIP_ERROR { return TryAddResponseDataViaEncoder(aRequestCommandPath, commandId, encoder); }); } /** @@ -350,9 +398,21 @@ class CommandHandler * @param [in] aData the data for the response. */ template - void AddResponse(const ConcreteCommandPath & aRequestCommandPath, const CommandData & aData) + inline void AddResponse(const ConcreteCommandPath & aRequestCommandPath, const CommandData & aData) { - if (AddResponseData(aRequestCommandPath, aData) != CHIP_NO_ERROR) + DataModelEncoderToTLV encoder(aData); + return AddResponseViaEncoder(aRequestCommandPath, CommandData::GetCommandId(), encoder); + } + + /** + * API for adding a response with a given encoder of TLV data. + * + * The encoder would generally encode a ClusterName::Commands::CommandName::Type with + * the corresponding `GetCommandId` call. + */ + void AddResponseViaEncoder(const ConcreteCommandPath & aRequestCommandPath, CommandId commandId, EncoderToTLV & encoder) + { + if (AddResponseDataViaEncoder(aRequestCommandPath, commandId, encoder) != CHIP_NO_ERROR) { AddStatus(aRequestCommandPath, Protocols::InteractionModel::Status::Failure); } @@ -630,7 +690,6 @@ class CommandHandler return PrepareInvokeResponseCommand(aResponseCommandPath, prepareParams); } - // TODO(#31627): It would be awesome if we could remove this template all together. /** * If this function fails, it may leave our TLV buffer in an inconsistent state. * Callers should snapshot as needed before calling this function, and roll back @@ -640,26 +699,14 @@ class CommandHandler * responding to. * @param [in] aData the data for the response. */ - template - CHIP_ERROR TryAddResponseData(const ConcreteCommandPath & aRequestCommandPath, const CommandData & aData) + CHIP_ERROR TryAddResponseDataViaEncoder(const ConcreteCommandPath & aRequestCommandPath, CommandId commandId, + EncoderToTLV & encoder) { - // This method, templated with CommandData, captures all the components needs - // from CommandData with as little code as possible. - // - // Previously, non-essential code was unnecessarily templated, leading to - // compilation and duplication N times. By isolating only the code segments - // that genuinely require templating, minimizes duplicate compiled code. - ConcreteCommandPath responseCommandPath = { aRequestCommandPath.mEndpointId, aRequestCommandPath.mClusterId, - CommandData::GetCommandId() }; + ConcreteCommandPath responseCommandPath = { aRequestCommandPath.mEndpointId, aRequestCommandPath.mClusterId, commandId }; ReturnErrorOnFailure(TryAddResponseDataPreEncode(aRequestCommandPath, responseCommandPath)); TLV::TLVWriter * writer = GetCommandDataIBTLVWriter(); VerifyOrReturnError(writer != nullptr, CHIP_ERROR_INCORRECT_STATE); - ReturnErrorOnFailure(DataModel::Encode(*writer, TLV::ContextTag(CommandDataIB::Tag::kFields), aData)); - - // FinishCommand technically should be refactored out as it is not a command that needs templating. - // But, because there is only a single function call, keeping it here takes less code. If there is - // ever more code between DataModel::Encode and the end of this function, it should be broken out into - // TryAddResponseDataPostEncode. + ReturnErrorOnFailure(encoder.Encode(*writer, TLV::ContextTag(CommandDataIB::Tag::kFields))); return FinishCommand(/* aEndDataStruct = */ false); } diff --git a/src/app/GenericEventManagementTestEventTriggerHandler.cpp b/src/app/GenericEventManagementTestEventTriggerHandler.cpp new file mode 100644 index 00000000000000..9c4cc11c8851a9 --- /dev/null +++ b/src/app/GenericEventManagementTestEventTriggerHandler.cpp @@ -0,0 +1,72 @@ +/* + * + * 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 "GenericEventManagementTestEventTriggerHandler.h" + +#include +#include + +namespace chip { +namespace app { + +using namespace chip::DeviceLayer; +using namespace chip::app::Clusters; + +CHIP_ERROR GenericEventManagementTestEventTriggerHandler::HandleFillUpEventLoggingBufferEventTriger() +{ + /* Create a fake hardware fault list. */ + GeneralFaults hardwareFaults; + for (uint8_t hardwareFault = to_underlying(GeneralDiagnostics::HardwareFaultEnum::kUnspecified); + hardwareFault < kMaxHardwareFaults; hardwareFault++) + { + hardwareFaults.add(hardwareFault); + } + + /* Fill up the critical logging buffer by 10 hardware faults. */ + constexpr uint8_t kHardwareFaultCountForCriticalBuffer = 10; + for (uint8_t i = 0; i < kHardwareFaultCountForCriticalBuffer; i++) + { + GeneralDiagnosticsServer::Instance().OnHardwareFaultsDetect(hardwareFaults, hardwareFaults); + } + + /* Fill up the info logging buffer. */ + FillUpEventLoggingBufferWithFakeSoftwareFault(CHIP_DEVICE_CONFIG_EVENT_LOGGING_INFO_BUFFER_SIZE); + + /* Fill up the debug logging buffer. */ + FillUpEventLoggingBufferWithFakeSoftwareFault(CHIP_DEVICE_CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE); + + return CHIP_NO_ERROR; +} + +void GenericEventManagementTestEventTriggerHandler::FillUpEventLoggingBufferWithFakeSoftwareFault(size_t bufferSize) +{ + /* Create a fake fault message. */ + constexpr size_t kEncodingOverhead = 0x40; + const size_t recordSize = bufferSize - kEncodingOverhead; + char * recordBuffer = static_cast(Platform::MemoryAlloc(recordSize)); + VerifyOrReturn(recordBuffer != nullptr); + std::unique_ptr recordString(recordBuffer, &Platform::MemoryFree); + memset(recordString.get(), 0x55, recordSize); + recordString.get()[recordSize - 1] = '\0'; + + /* Fill up the logging buffer by a software fault. */ + TriggerSoftwareFaultEvent(recordString.get()); +} + +} // namespace app +} // namespace chip diff --git a/src/app/GenericEventManagementTestEventTriggerHandler.h b/src/app/GenericEventManagementTestEventTriggerHandler.h new file mode 100644 index 00000000000000..da205a420bfaf3 --- /dev/null +++ b/src/app/GenericEventManagementTestEventTriggerHandler.h @@ -0,0 +1,37 @@ +/* + * + * 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 + +namespace chip { +namespace app { + +class GenericEventManagementTestEventTriggerHandler : public TestEventTriggerHandler +{ +protected: + CHIP_ERROR HandleFillUpEventLoggingBufferEventTriger(); + +private: + void FillUpEventLoggingBufferWithFakeSoftwareFault(size_t bufferSize); + virtual void TriggerSoftwareFaultEvent(const char * faultRecordString) = 0; +}; + +} // namespace app +} // namespace chip diff --git a/src/app/OperationalSessionSetup.cpp b/src/app/OperationalSessionSetup.cpp index 5733a423702226..9197a2edbddf70 100644 --- a/src/app/OperationalSessionSetup.cpp +++ b/src/app/OperationalSessionSetup.cpp @@ -792,6 +792,11 @@ CHIP_ERROR OperationalSessionSetup::ScheduleSessionSetupReattempt(System::Clock: // but in practice for old devices BUSY often sends some hardcoded value // that tells us nothing about when the other side will decide it has // timed out. + // + // Unfortunately, we do not have the MRP config for the other side here, + // but in practice if the other side is using its local config to + // compute Sigma2 response timeouts, then it's also returning useful + // values with BUSY, so we will wait long enough. auto additionalTimeout = CASESession::ComputeSigma2ResponseTimeout(GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig())); actualTimerDelay += additionalTimeout; } diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp index e8ada20a428c70..bb058e091858c7 100644 --- a/src/app/ReadClient.cpp +++ b/src/app/ReadClient.cpp @@ -938,24 +938,26 @@ CHIP_ERROR ReadClient::ComputeLivenessCheckTimerTimeout(System::Clock::Timeout * // // To calculate the duration we're willing to wait for a report to come to us, we take into account the maximum interval of - // the subscription AND the time it takes for the report to make it to us in the worst case. This latter bit involves - // computing the Ack timeout from the publisher for the ReportData message being sent to us using our IDLE interval as the - // basis for that computation. + // the subscription AND the time it takes for the report to make it to us in the worst case. // - // Make sure to use the retransmission computation that includes backoff. For purposes of that computation, treat us as - // active now (since we are right now sending/receiving messages), and use the default "how long are we guaranteed to stay - // active" threshold for now. + // We have no way to estimate what the network latency will be, but we do know the other side will time out its ReportData + // after its computed round-trip timeout plus the processing time it gives us (app::kExpectedIMProcessingTime). Once it + // times out, assuming it sent the report at all, there's no point in us thinking we still have a subscription. // - // TODO: We need to find a good home for this logic that will correctly compute this based on transport. For now, this will - // suffice since we don't use TCP as a transport currently and subscriptions over BLE aren't really a thing. + // We can't use ComputeRoundTripTimeout() on the session for two reasons: we want the roundtrip timeout from the point of + // view of the peer, not us, and we want to start off with the assumption the peer will likely have, which is that we are + // idle, whereas ComputeRoundTripTimeout() uses the current activity state of the peer. // - const auto & localMRPConfig = GetLocalMRPConfig(); - const auto & defaultMRPConfig = GetDefaultMRPConfig(); - const auto & ourMrpConfig = localMRPConfig.ValueOr(defaultMRPConfig); - auto publisherTransmissionTimeout = - GetRetransmissionTimeout(ourMrpConfig.mActiveRetransTimeout, ourMrpConfig.mIdleRetransTimeout, - System::SystemClock().GetMonotonicTimestamp(), ourMrpConfig.mActiveThresholdTime); - *aTimeout = System::Clock::Seconds16(mMaxInterval) + publisherTransmissionTimeout; + // So recompute the round-trip timeout directly. Assume MRP, since in practice that is likely what is happening. + auto & peerMRPConfig = mReadPrepareParams.mSessionHolder->GetRemoteMRPConfig(); + // Peer will assume we are idle (hence we pass kZero to GetMessageReceiptTimeout()), but will assume we treat it as active + // for the response, so to match the retransmission timeout computation for the message back to the peeer, we should treat + // it as active. + auto roundTripTimeout = mReadPrepareParams.mSessionHolder->GetMessageReceiptTimeout(System::Clock::kZero) + + kExpectedIMProcessingTime + + GetRetransmissionTimeout(peerMRPConfig.mActiveRetransTimeout, peerMRPConfig.mIdleRetransTimeout, + System::SystemClock().GetMonotonicTimestamp(), peerMRPConfig.mActiveThresholdTime); + *aTimeout = System::Clock::Seconds16(mMaxInterval) + roundTripTimeout; return CHIP_NO_ERROR; } diff --git a/src/app/tests/suites/TestAccessControlConstraints.yaml b/src/app/tests/suites/TestAccessControlConstraints.yaml index 1a54ea48f92365..ab7ba3cf8d5f0c 100644 --- a/src/app/tests/suites/TestAccessControlConstraints.yaml +++ b/src/app/tests/suites/TestAccessControlConstraints.yaml @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -name: User Label Cluster Tests +name: Test Access Control Constraints config: nodeId: 0x12344321 diff --git a/src/app/tests/suites/certification/Test_TC_OPCREDS_3_7.yaml b/src/app/tests/suites/certification/Test_TC_OPCREDS_3_7.yaml index f6751e03c3e283..67dc552f1b2215 100644 --- a/src/app/tests/suites/certification/Test_TC_OPCREDS_3_7.yaml +++ b/src/app/tests/suites/certification/Test_TC_OPCREDS_3_7.yaml @@ -63,14 +63,14 @@ tests: saveAs: TH1_Fabric_Index - label: - "Step 3.2: TH1 does a non-fabric-filtered read of the Fabrics - attribute from the Node Operational Credentials cluster. Save the - FabricIndex for TH1 as TH1_Fabric_Index for future use." + "Step 3.2: TH1 does a fabric-filtered read of the Fabrics attribute + from the Node Operational Credentials cluster. Save the FabricIndex + for TH1 as TH1_Fabric_Index for future use." identity: "alpha" command: "readAttribute" cluster: "Operational Credentials" attribute: "Fabrics" - fabricFiltered: false + fabricFiltered: true response: value: [{ "FabricIndex": TH1_Fabric_Index, "Label": "" }] constraints: @@ -237,20 +237,30 @@ tests: # verification: "" - label: - "Step 13: TH2 does a non-fabric-filtered read of the Fabrics attribute + "Step 13a: TH1 does a fabric-filtered read of the Fabrics attribute + from the Node Operational Credentials cluster" + nodeId: 0x43211234 + command: "readAttribute" + cluster: "Operational Credentials" + attribute: "Fabrics" + fabricFiltered: true + response: + value: [{ "FabricIndex": TH1_Fabric_Index, "Label": "" }] + constraints: + type: list + + # verification: "" + - label: + "Step 13b: TH2 does a fabric-filtered read of the Fabrics attribute from the Node Operational Credentials cluster" identity: "beta" nodeId: 0x43211234 command: "readAttribute" cluster: "Operational Credentials" attribute: "Fabrics" - fabricFiltered: false + fabricFiltered: true response: - value: - [ - { "FabricIndex": TH1_Fabric_Index, "Label": "" }, - { "FabricIndex": TH2_Fabric_Index, "Label": "" }, - ] + value: [{ "FabricIndex": TH2_Fabric_Index, "Label": "" }] constraints: type: list diff --git a/src/app/zap-templates/zcl/data-model/chip/boolean-state-configuration-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/boolean-state-configuration-cluster.xml index f60623455c8d24..4fb33dd9749a0e 100644 --- a/src/app/zap-templates/zcl/data-model/chip/boolean-state-configuration-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/boolean-state-configuration-cluster.xml @@ -28,7 +28,7 @@ limitations under the License. - + General Boolean State Configuration 0x0080 diff --git a/src/app/zap-templates/zcl/data-model/chip/electrical-energy-measurement-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/electrical-energy-measurement-cluster.xml index 0a4039badf7c97..eb05052538d2de 100644 --- a/src/app/zap-templates/zcl/data-model/chip/electrical-energy-measurement-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/electrical-energy-measurement-cluster.xml @@ -16,7 +16,7 @@ limitations under the License. --> - + Electrical Energy Measurement Measurement & Sensing 0x0091 @@ -52,25 +52,25 @@ limitations under the License. PeriodicEnergyExported CumulativeEnergyReset - + CumulativeEnergyMeasured - + PeriodicEnergyMeasured - + - + diff --git a/src/app/zap-templates/zcl/data-model/chip/electrical-power-measurement-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/electrical-power-measurement-cluster.xml index 69be8e08570f39..7f49a643d1b1f9 100644 --- a/src/app/zap-templates/zcl/data-model/chip/electrical-power-measurement-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/electrical-power-measurement-cluster.xml @@ -16,7 +16,7 @@ limitations under the License. --> - + Electrical Power Measurement Measurement & Sensing 0x0090 @@ -82,18 +82,18 @@ limitations under the License. PowerFactor NeutralCurrent - + MeasurementPeriodRanges - + - + @@ -107,7 +107,7 @@ limitations under the License. - + diff --git a/src/app/zap-templates/zcl/data-model/chip/energy-evse-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/energy-evse-cluster.xml index cbc0882ac1b3ac..5c22b8e93be8da 100644 --- a/src/app/zap-templates/zcl/data-model/chip/energy-evse-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/energy-evse-cluster.xml @@ -14,7 +14,7 @@ limitations under the License. - + @@ -25,7 +25,7 @@ limitations under the License. - + @@ -34,7 +34,7 @@ limitations under the License. - + @@ -55,7 +55,7 @@ limitations under the License. - + @@ -86,7 +86,7 @@ limitations under the License. - + Energy EVSE Energy Management 0x0099 @@ -177,7 +177,7 @@ limitations under the License. Allows a client to disable the EVSE from charging and discharging. - + @@ -188,7 +188,7 @@ limitations under the License. Allows a client to enable the EVSE to discharge an EV. - + Allows a client to put the EVSE into a self-diagnostics mode. @@ -205,41 +205,41 @@ limitations under the License. The GetTargetsResponse is sent in response to the GetTargets Command. - + EVConnected - + - + EVNotDetected - - - - + + + + - + EnergyTransferStarted - - - + + + - + EnergyTransferStopped - - - - + + + + - + Fault - - - - + + + + - + RFID - + diff --git a/src/app/zap-templates/zcl/data-model/chip/energy-evse-mode-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/energy-evse-mode-cluster.xml index 22e5562ed33f7f..c4b702b701d174 100644 --- a/src/app/zap-templates/zcl/data-model/chip/energy-evse-mode-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/energy-evse-mode-cluster.xml @@ -24,7 +24,7 @@ limitations under the License. - + General Energy EVSE Mode 0x009D diff --git a/src/app/zap-templates/zcl/data-model/chip/operational-state-oven-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/operational-state-oven-cluster.xml index f121b6357d605b..83f2fc8b7984d4 100644 --- a/src/app/zap-templates/zcl/data-model/chip/operational-state-oven-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/operational-state-oven-cluster.xml @@ -16,7 +16,7 @@ limitations under the License. --> - + Appliances Oven Cavity Operational State 0x0048 diff --git a/src/app/zap-templates/zcl/data-model/chip/oven-mode-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/oven-mode-cluster.xml index 2508b4741602b3..41f95e81cdd248 100644 --- a/src/app/zap-templates/zcl/data-model/chip/oven-mode-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/oven-mode-cluster.xml @@ -30,7 +30,7 @@ limitations under the License. - + General Oven Mode 0x0049 diff --git a/src/app/zap-templates/zcl/data-model/chip/power-topology-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/power-topology-cluster.xml index 2fdddcbe3560da..3bca0bd43ee8af 100644 --- a/src/app/zap-templates/zcl/data-model/chip/power-topology-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/power-topology-cluster.xml @@ -17,7 +17,7 @@ limitations under the License. - + Measurement & Sensing Power Topology 0x009C diff --git a/src/app/zap-templates/zcl/data-model/chip/valve-configuration-and-control-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/valve-configuration-and-control-cluster.xml index 4bb545112be846..80c1cdb60c8ccc 100644 --- a/src/app/zap-templates/zcl/data-model/chip/valve-configuration-and-control-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/valve-configuration-and-control-cluster.xml @@ -40,7 +40,7 @@ limitations under the License. - + HVAC Valve Configuration and Control 0x0081 diff --git a/src/controller/AutoCommissioner.cpp b/src/controller/AutoCommissioner.cpp index 5b37988a491260..a72543853fa2d1 100644 --- a/src/controller/AutoCommissioner.cpp +++ b/src/controller/AutoCommissioner.cpp @@ -125,6 +125,8 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam mParams = params; + mNeedIcdRegistration = false; + if (haveMaybeDanglingBufferPointers) { mParams.ClearExternalBufferDependentValues(); diff --git a/src/controller/CommissionerDiscoveryController.cpp b/src/controller/CommissionerDiscoveryController.cpp index 4df133c59e99f3..d1a5747491ad50 100644 --- a/src/controller/CommissionerDiscoveryController.cpp +++ b/src/controller/CommissionerDiscoveryController.cpp @@ -219,6 +219,34 @@ void CommissionerDiscoveryController::InternalOk() ChipLogError(AppServer, "UX InternalOk: could not find instance=%s", mCurrentInstance); return; } + + if (mAppInstallationService == nullptr) + { + ChipLogError(AppServer, "UX InternalOk: no app installation service"); + return; + } + + if (!mAppInstallationService->LookupTargetContentApp(client->GetVendorId(), client->GetProductId())) + { + ChipLogDetail(AppServer, "UX InternalOk: app not installed."); + + // notify client that app will be installed + CommissionerDeclaration cd; + cd.SetErrorCode(CommissionerDeclaration::CdError::kAppInstallConsentPending); + mUdcServer->SendCDCMessage(cd, Transport::PeerAddress::UDP(client->GetPeerAddress().GetIPAddress(), client->GetCdPort())); + + // dialog + ChipLogDetail(Controller, "------PROMPT USER: %s is requesting to install app on this TV. vendorId=%d, productId=%d", + client->GetDeviceName(), client->GetVendorId(), client->GetProductId()); + + if (mUserPrompter != nullptr) + { + mUserPrompter->PromptForAppInstallOKPermission(client->GetVendorId(), client->GetProductId(), client->GetDeviceName()); + } + ChipLogDetail(Controller, "------Via Shell Enter: app install "); + return; + } + if (client->GetUDCClientProcessingState() != UDCClientProcessingState::kPromptingUser) { ChipLogError(AppServer, "UX InternalOk: invalid state for ok"); @@ -244,6 +272,7 @@ void CommissionerDiscoveryController::InternalOk() CharSpan rotatingIdSpan(rotatingIdBuffer, 2 * rotatingIdLength); uint8_t targetAppCount = client->GetNumTargetAppInfos(); + if (targetAppCount > 0) { ChipLogDetail(AppServer, "UX InternalOk: checking for each target app specified"); @@ -583,6 +612,7 @@ void CommissionerDiscoveryController::Cancel() } client->SetUDCClientProcessingState(UDCClientProcessingState::kUserDeclined); mPendingConsent = false; + ResetState(); } void CommissionerDiscoveryController::CommissioningSucceeded(uint16_t vendorId, uint16_t productId, NodeId nodeId, diff --git a/src/controller/CommissionerDiscoveryController.h b/src/controller/CommissionerDiscoveryController.h index 521fe5c611dc3c..b2211641fd0ede 100644 --- a/src/controller/CommissionerDiscoveryController.h +++ b/src/controller/CommissionerDiscoveryController.h @@ -150,6 +150,21 @@ class DLL_EXPORT UserPrompter */ virtual void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) = 0; + /** + * @brief + * Called to prompt the user for consent to allow the app commissioneeName/vendorId/productId to be installed. + * For example "[commissioneeName] is requesting permission to install app to this TV, approve?" + * + * If user responds with OK then implementor should call CommissionerRespondOk(); + * If user responds with Cancel then implementor should call CommissionerRespondCancel(); + * + * @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee. + * @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee. + * @param[in] commissioneeName The commissioneeName in the DNS-SD advertisement of the requesting commissionee. + * + */ + virtual void PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) = 0; + virtual ~UserPrompter() = default; }; @@ -158,7 +173,7 @@ class DLL_EXPORT PasscodeService public: /** * @brief - * Called to determine if the given target app is available to the commissionee with the given given + * Called to determine if the given target app is available to the commissionee with the given * vendorId/productId, and if so, return the passcode. * * This will be called by the main chip thread so any blocking work should be moved to a separate thread. @@ -204,6 +219,25 @@ class DLL_EXPORT PasscodeService virtual ~PasscodeService() = default; }; +class DLL_EXPORT AppInstallationService +{ +public: + /** + * @brief + * Called to check if the given target app is available to the commissione with th given + * vendorId/productId + * + * This will be called by the main chip thread so any blocking work should be moved to a separate thread. + * + * @param[in] vendorId The vendorId in the DNS-SD advertisement of the requesting commissionee. + * @param[in] productId The productId in the DNS-SD advertisement of the requesting commissionee. + * + */ + virtual bool LookupTargetContentApp(uint16_t vendorId, uint16_t productId) = 0; + + virtual ~AppInstallationService() = default; +}; + class DLL_EXPORT PostCommissioningListener { public: @@ -392,6 +426,14 @@ class CommissionerDiscoveryController : public chip::Protocols::UserDirectedComm inline void SetPasscodeService(PasscodeService * passcodeService) { mPasscodeService = passcodeService; } inline PasscodeService * GetPasscodeService() { return mPasscodeService; } + /** + * Assign an AppInstallationService + */ + inline void SetAppInstallationService(AppInstallationService * appInstallationService) + { + mAppInstallationService = appInstallationService; + } + /** * Assign a Commissioner Callback to perform commissioning once user consent has been given */ @@ -430,6 +472,7 @@ class CommissionerDiscoveryController : public chip::Protocols::UserDirectedComm UserDirectedCommissioningServer * mUdcServer = nullptr; UserPrompter * mUserPrompter = nullptr; PasscodeService * mPasscodeService = nullptr; + AppInstallationService * mAppInstallationService = nullptr; CommissionerCallback * mCommissionerCallback = nullptr; PostCommissioningListener * mPostCommissioningListener = nullptr; }; diff --git a/src/controller/README.md b/src/controller/README.md index e46c191f55489b..70a3fa5e8f8352 100644 --- a/src/controller/README.md +++ b/src/controller/README.md @@ -26,7 +26,7 @@ The POSIX CLI chip-tool is located in ### Python -The Python chip-device-ctrl is located in +The Python CHIP Controller library is located in [../controller/python/](../controller/python). ## Feature Overview diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index 4e99c7ebe5d260..cd310219ea88d2 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -2804,7 +2804,7 @@ provisional cluster Timer = 71 { } /** This cluster supports remotely monitoring and, where supported, changing the operational state of an Oven. */ -provisional cluster OvenCavityOperationalState = 72 { +cluster OvenCavityOperationalState = 72 { revision 1; enum ErrorStateEnum : enum8 { @@ -2870,7 +2870,7 @@ provisional cluster OvenCavityOperationalState = 72 { } /** Attributes and commands for selecting a mode from a list of supported options. */ -provisional cluster OvenMode = 73 { +cluster OvenMode = 73 { revision 1; enum ModeTag : enum16 { @@ -3988,7 +3988,7 @@ cluster ActivatedCarbonFilterMonitoring = 114 { } /** This cluster is used to configure a boolean sensor. */ -provisional cluster BooleanStateConfiguration = 128 { +cluster BooleanStateConfiguration = 128 { revision 1; bitmap AlarmModeBitmap : bitmap8 { @@ -4046,7 +4046,7 @@ provisional cluster BooleanStateConfiguration = 128 { } /** This cluster is used to configure a valve. */ -provisional cluster ValveConfigurationAndControl = 129 { +cluster ValveConfigurationAndControl = 129 { revision 1; enum StatusCodeEnum : enum8 { @@ -4112,7 +4112,7 @@ provisional cluster ValveConfigurationAndControl = 129 { } /** This cluster provides a mechanism for querying data about electrical power as measured by the server. */ -provisional cluster ElectricalPowerMeasurement = 144 { +cluster ElectricalPowerMeasurement = 144 { revision 1; enum MeasurementTypeEnum : enum16 { @@ -4217,7 +4217,7 @@ provisional cluster ElectricalPowerMeasurement = 144 { } /** This cluster provides a mechanism for querying data about the electrical energy imported or provided by the server. */ -provisional cluster ElectricalEnergyMeasurement = 145 { +cluster ElectricalEnergyMeasurement = 145 { revision 1; enum MeasurementTypeEnum : enum16 { @@ -4803,7 +4803,7 @@ provisional cluster DeviceEnergyManagement = 152 { } /** Electric Vehicle Supply Equipment (EVSE) is equipment used to charge an Electric Vehicle (EV) or Plug-In Hybrid Electric Vehicle. This cluster provides an interface to the functionality of Electric Vehicle Supply Equipment (EVSE) management. */ -provisional cluster EnergyEvse = 153 { +cluster EnergyEvse = 153 { revision 2; enum EnergyTransferStoppedReasonEnum : enum8 { @@ -5015,7 +5015,7 @@ provisional cluster EnergyPreference = 155 { } /** The Power Topology Cluster provides a mechanism for expressing how power is flowing between endpoints. */ -provisional cluster PowerTopology = 156 { +cluster PowerTopology = 156 { revision 1; bitmap Feature : bitmap32 { @@ -5036,7 +5036,7 @@ provisional cluster PowerTopology = 156 { } /** Attributes and commands for selecting a mode from a list of supported options. */ -provisional cluster EnergyEvseMode = 157 { +cluster EnergyEvseMode = 157 { revision 1; enum ModeTag : enum16 { diff --git a/src/controller/java/AndroidDeviceControllerWrapper.cpp b/src/controller/java/AndroidDeviceControllerWrapper.cpp index 1576e5883ca256..526f6830844f13 100644 --- a/src/controller/java/AndroidDeviceControllerWrapper.cpp +++ b/src/controller/java/AndroidDeviceControllerWrapper.cpp @@ -25,6 +25,8 @@ #include +#include + #include #include #include @@ -1063,7 +1065,7 @@ void AndroidDeviceControllerWrapper::OnICDRegistrationComplete(chip::NodeId icdN CHIP_ERROR AndroidDeviceControllerWrapper::SyncGetKeyValue(const char * key, void * value, uint16_t & size) { - ChipLogProgress(chipTool, "KVS: Getting key %s", StringOrNullMarker(key)); + ChipLogProgress(Controller, "KVS: Getting key %s", StringOrNullMarker(key)); size_t read_size = 0; @@ -1076,12 +1078,25 @@ CHIP_ERROR AndroidDeviceControllerWrapper::SyncGetKeyValue(const char * key, voi CHIP_ERROR AndroidDeviceControllerWrapper::SyncSetKeyValue(const char * key, const void * value, uint16_t size) { - ChipLogProgress(chipTool, "KVS: Setting key %s", StringOrNullMarker(key)); + ChipLogProgress(Controller, "KVS: Setting key %s", StringOrNullMarker(key)); return chip::DeviceLayer::PersistedStorage::KeyValueStoreMgr().Put(key, value, size); } CHIP_ERROR AndroidDeviceControllerWrapper::SyncDeleteKeyValue(const char * key) { - ChipLogProgress(chipTool, "KVS: Deleting key %s", StringOrNullMarker(key)); + ChipLogProgress(Controller, "KVS: Deleting key %s", StringOrNullMarker(key)); return chip::DeviceLayer::PersistedStorage::KeyValueStoreMgr().Delete(key); } + +void AndroidDeviceControllerWrapper::StartDnssd() +{ + FabricTable * fabricTable = DeviceControllerFactory::GetInstance().GetSystemState()->Fabrics(); + VerifyOrReturn(fabricTable != nullptr, ChipLogError(Controller, "Fail to get fabricTable in StartDnssd")); + chip::app::DnssdServer::Instance().SetFabricTable(fabricTable); + chip::app::DnssdServer::Instance().StartServer(); +} + +void AndroidDeviceControllerWrapper::StopDnssd() +{ + chip::app::DnssdServer::Instance().StopServer(); +} diff --git a/src/controller/java/AndroidDeviceControllerWrapper.h b/src/controller/java/AndroidDeviceControllerWrapper.h index 51beae2d0eba48..1d26d31d112774 100644 --- a/src/controller/java/AndroidDeviceControllerWrapper.h +++ b/src/controller/java/AndroidDeviceControllerWrapper.h @@ -214,6 +214,10 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel CHIP_ERROR SetICDCheckInDelegate(jobject checkInDelegate); + void StartDnssd(); + + void StopDnssd(); + private: using ChipDeviceControllerPtr = std::unique_ptr; diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp index 9b3d10a2bf1d31..a6c93c55885070 100644 --- a/src/controller/java/CHIPDeviceController-JNI.cpp +++ b/src/controller/java/CHIPDeviceController-JNI.cpp @@ -2154,6 +2154,28 @@ JNI_METHOD(jbyteArray, validateAndExtractCSR)(JNIEnv * env, jclass clazz, jbyteA return javaCsr; } +JNI_METHOD(void, startDnssd)(JNIEnv * env, jobject self, jlong handle) +{ + ChipLogProgress(Controller, "startDnssd() called"); + chip::DeviceLayer::StackLock lock; + + AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); + VerifyOrReturn(wrapper != nullptr, + ChipLogError(Controller, "AndroidDeviceControllerWrapper::FromJNIHandle in startDnssd fails!")); + wrapper->StartDnssd(); +} + +JNI_METHOD(void, stopDnssd)(JNIEnv * env, jobject self, jlong handle) +{ + ChipLogProgress(Controller, "stopDnssd() called"); + chip::DeviceLayer::StackLock lock; + + AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); + VerifyOrReturn(wrapper != nullptr, + ChipLogError(Controller, "AndroidDeviceControllerWrapper::FromJNIHandle in stopDnssd fails!")); + wrapper->StopDnssd(); +} + void * IOThreadMain(void * arg) { JNIEnv * env; diff --git a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java index 653cebf89d1030..4cf4c9ee0f83cc 100644 --- a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java +++ b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java @@ -803,6 +803,14 @@ public byte[] getAttestationChallenge(long devicePtr) { return getAttestationChallenge(deviceControllerPtr, devicePtr); } + public void startDnssd() { + startDnssd(deviceControllerPtr); + } + + public void stopDnssd() { + stopDnssd(deviceControllerPtr); + } + /** * @brief Auto-Resubscribe to the given attribute path with keepSubscriptions and isFabricFiltered * @param SubscriptionEstablishedCallback Callback when a subscribe response has been received and @@ -1587,6 +1595,10 @@ private native void updateCommissioningICDRegistrationInfo( private native void shutdownCommissioning(long deviceControllerPtr); + private native void startDnssd(long deviceControllerPtr); + + private native void stopDnssd(long deviceControllerPtr); + static { System.loadLibrary("CHIPController"); } diff --git a/src/controller/java/src/matter/onboardingpayload/QRCodeBasicOnboardingPayloadGenerator.kt b/src/controller/java/src/matter/onboardingpayload/QRCodeBasicOnboardingPayloadGenerator.kt index a51fd8367be335..853b3308953afd 100644 --- a/src/controller/java/src/matter/onboardingpayload/QRCodeBasicOnboardingPayloadGenerator.kt +++ b/src/controller/java/src/matter/onboardingpayload/QRCodeBasicOnboardingPayloadGenerator.kt @@ -178,6 +178,6 @@ private fun populateTLVBits( for (i in 0 until tlvBufSizeInBytes) { val value = tlvBuf[i] - populateBits(bits, offset, value.toLong(), 8, totalPayloadDataSizeInBits) + populateBits(bits, offset, value.toUByte().toLong(), 8, totalPayloadDataSizeInBits) } } diff --git a/src/controller/python/BUILD.gn b/src/controller/python/BUILD.gn index 5fc2212098fea8..4676f89b362970 100644 --- a/src/controller/python/BUILD.gn +++ b/src/controller/python/BUILD.gn @@ -414,10 +414,7 @@ chip_python_wheel_action("chip-clusters") { } chip_python_wheel_action("chip-repl") { - py_scripts = [ - "chip-device-ctrl.py", - "chip-repl.py", - ] + py_scripts = [ "chip-repl.py" ] py_manifest_files = [ { diff --git a/src/controller/python/README.md b/src/controller/python/README.md index 34edea444650b1..e94a95e1585e0b 100644 --- a/src/controller/python/README.md +++ b/src/controller/python/README.md @@ -1,10 +1,14 @@ -# Python CHIP Device Controller +# Python CHIP Controller -The Python CHIP controller is a tool that allows to commission a Matter device -into the network and to communicate with it using the Zigbee Cluster Library -(ZCL) messages. The tool uses the generic [Chip Device Controller](../) library. +The Python CHIP controller is a library that allows to create a Matter fabric +and commission Matter devices with it, as well as communicate with commissioned +devices by reading/subscribing and writing Attributes and sending Commands. The +Python CHIP controller is based on the native [Chip Device Controller](../) +library. -To learn more about the tool, how to build it and use its commands and advanced +The Python CHIP Controller comes with a REPL which allows to explore and use the +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) diff --git a/src/controller/python/chip-device-ctrl.py b/src/controller/python/chip-device-ctrl.py deleted file mode 100755 index 469fb2c49fdfc8..00000000000000 --- a/src/controller/python/chip-device-ctrl.py +++ /dev/null @@ -1,1202 +0,0 @@ -#!/usr/bin/env python - -# -# Copyright (c) 2020-2021 Project CHIP Authors -# Copyright (c) 2013-2018 Nest Labs, Inc. -# All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# -# @file -# This file implements the Python-based Chip Device Controller Shell. -# - -from __future__ import absolute_import, print_function - -import argparse -import base64 -import ctypes -import logging -import os -import platform -import random -import shlex -import string -import sys -import textwrap -import time -import traceback -import warnings -from cmd import Cmd -from optparse import OptionParser, OptionValueError - -import chip.logging -import coloredlogs -from chip import ChipCommissionableNodeCtrl, ChipStack, exceptions, native -from chip.setup_payload import SetupPayload -from rich import pretty, print - -# Extend sys.path with one or more directories, relative to the location of the -# running script, in which the chip package might be found . This makes it -# possible to run the device manager shell from a non-standard install location, -# as well as directly from its location the CHIP source tree. -# -# Note that relative package locations are prepended to sys.path so as to give -# the local version of the package higher priority over any version installed in -# a standard location. -# -scriptDir = os.path.dirname(os.path.abspath(__file__)) -relChipPackageInstallDirs = [ - ".", - "../lib/python", - "../lib/python%s.%s" % (sys.version_info.major, sys.version_info.minor), - "../lib/Python%s%s" % (sys.version_info.major, sys.version_info.minor), -] -for relInstallDir in relChipPackageInstallDirs: - absInstallDir = os.path.realpath(os.path.join(scriptDir, relInstallDir)) - if os.path.isdir(os.path.join(absInstallDir, "chip")): - sys.path.insert(0, absInstallDir) - - -if platform.system() == 'Darwin': - from chip.ChipCoreBluetoothMgr import CoreBluetoothManager as BleManager -elif sys.platform.startswith('linux'): - from chip.ChipBluezMgr import BluezManager as BleManager - -# The exceptions for CHIP Device Controller CLI - - -class ChipDevCtrlException(exceptions.ChipStackException): - pass - - -class ParsingError(ChipDevCtrlException): - def __init__(self, msg=None): - self.msg = "Parsing Error: " + msg - - def __str__(self): - return self.msg - - -def DecodeBase64Option(option, opt, value): - try: - return base64.standard_b64decode(value) - except TypeError: - raise OptionValueError( - "option %s: invalid base64 value: %r" % (opt, value)) - - -def DecodeHexIntOption(option, opt, value): - try: - return int(value, 16) - except ValueError: - raise OptionValueError("option %s: invalid value: %r" % (opt, value)) - - -def ParseEncodedString(value): - if value.find(":") < 0: - raise ParsingError( - "value should be encoded in encoding:encodedvalue format") - enc, encValue = value.split(":", 1) - if enc == "str": - return encValue.encode("utf-8") + b'\x00' - elif enc == "hex": - return bytes.fromhex(encValue) - raise ParsingError("only str and hex encoding is supported") - - -def ParseValueWithType(value, type): - if type == 'int': - return int(value) - elif type == 'str': - return value - elif type == 'bytes': - return ParseEncodedString(value) - elif type == 'bool': - return (value.upper() not in ['F', 'FALSE', '0']) - else: - raise ParsingError('cannot recognize type: {}'.format(type)) - - -def FormatZCLArguments(args, command): - commandArgs = {} - for kvPair in args: - if kvPair.find("=") < 0: - raise ParsingError("Argument should in key=value format") - key, value = kvPair.split("=", 1) - valueType = command.get(key, None) - commandArgs[key] = ParseValueWithType(value, valueType) - return commandArgs - - -def ShowColoredWarnings(message, category, filename, lineno, file=None, line=None): - logging.warning(' %s:%s: %s:%s' % - (filename, lineno, category.__name__, message)) - return - - -class DeviceMgrCmd(Cmd): - def __init__(self, rendezvousAddr=None, controllerNodeId=1, bluetoothAdapter=None): - self.lastNetworkId = None - self.replHint = None - - pretty.install(indent_guides=True, expand_all=True) - - coloredlogs.install(level='DEBUG') - chip.logging.RedirectToPythonLogging() - - logging.getLogger().setLevel(logging.DEBUG) - warnings.showwarning = ShowColoredWarnings - - Cmd.__init__(self) - - Cmd.identchars = string.ascii_letters + string.digits + "-" - - if sys.stdin.isatty(): - self.prompt = "chip-device-ctrl > " - else: - self.use_rawinput = 0 - self.prompt = "" - - DeviceMgrCmd.command_names.sort() - - self.bleMgr = None - - self.chipStack = ChipStack.ChipStack( - bluetoothAdapter=bluetoothAdapter, persistentStoragePath='/tmp/chip-device-ctrl-storage.json') - self.certificateAuthorityManager = chip.CertificateAuthority.CertificateAuthorityManager(chipStack=self.chipStack) - self.certificateAuthority = self.certificateAuthorityManager.NewCertificateAuthority() - self.fabricAdmin = self.certificateAuthority.NewFabricAdmin(vendorId=0xFFF1, fabricId=1) - self.devCtrl = self.fabricAdmin.NewController( - nodeId=controllerNodeId, useTestCommissioner=True) - - self.commissionableNodeCtrl = ChipCommissionableNodeCtrl.ChipCommissionableNodeController( - self.chipStack) - - # If we are on Linux and user selects non-default bluetooth adapter. - if sys.platform.startswith("linux") and (bluetoothAdapter is not None): - try: - self.bleMgr = BleManager(self.devCtrl) - self.bleMgr.ble_adapter_select( - "hci{}".format(bluetoothAdapter)) - except Exception as ex: - traceback.print_exc() - print( - "Failed to initialize BLE, if you don't have BLE, run chip-device-ctrl with --no-ble") - raise ex - - self.historyFileName = os.path.expanduser( - "~/.chip-device-ctrl-history") - - try: - import readline - - if "libedit" in readline.__doc__: - readline.parse_and_bind("bind ^I rl_complete") - readline.set_completer_delims(" ") - try: - readline.read_history_file(self.historyFileName) - except IOError: - pass - except ImportError: - pass - - command_names = [ - "setup-payload", - - "ble-scan", - "ble-adapter-select", - "ble-adapter-print", - "ble-debug-log", - - "connect", - "close-ble", - "close-session", - "resolve", - "paseonly", - "commission", - "zcl", - "zclread", - "zclsubscribe", - - "discover", - - "set-pairing-wifi-credential", - "set-pairing-thread-credential", - - "open-commissioning-window", - - "get-fabricid", - ] - - def parseline(self, line): - cmd, arg, line = Cmd.parseline(self, line) - if cmd: - cmd = self.shortCommandName(cmd) - line = cmd + " " + arg - return cmd, arg, line - - def completenames(self, text, *ignored): - return [ - name + " " - for name in DeviceMgrCmd.command_names - if name.startswith(text) or self.shortCommandName(name).startswith(text) - ] - - def shortCommandName(self, cmd): - return cmd.replace("-", "") - - def precmd(self, line): - if not self.use_rawinput and line != "EOF" and line != "": - print(">>> " + line) - return line - - def postcmd(self, stop, line): - if self.replHint is not None: - print("Try the following command in repl: ") - print(self.replHint) - print("") - self.replHint = None - if not stop and self.use_rawinput: - self.prompt = "chip-device-ctrl > " - return stop - - def postloop(self): - try: - import readline - - try: - readline.write_history_file(self.historyFileName) - except IOError: - pass - except ImportError: - pass - - def do_help(self, line): - if line: - cmd, arg, unused = self.parseline(line) - try: - doc = getattr(self, "do_" + cmd).__doc__ - except AttributeError: - doc = None - if doc: - self.stdout.write("%s\n" % textwrap.dedent(doc)) - else: - self.stdout.write("No help on %s\n" % (line)) - else: - self.print_topics( - "\nAvailable commands (type help for more information):", - DeviceMgrCmd.command_names, - 15, - 80, - ) - - def do_closeble(self, line): - """ - close-ble - - Close the ble connection to the device. - """ - - warnings.warn( - "This method is being deprecated. " - "Please use the DeviceController.CloseBLEConnection method directly in the REPL", DeprecationWarning) - - args = shlex.split(line) - - if len(args) != 0: - print("Usage:") - self.do_help("close") - return - - try: - self.devCtrl.CloseBLEConnection() - except exceptions.ChipStackException as ex: - print(str(ex)) - - def do_setlogoutput(self, line): - """ - set-log-output [ none | error | progress | detail ] - - Set the level of Chip logging output. - """ - - warnings.warn( - "This method is being deprecated. " - "Please use the DeviceController.SetLogFilter method directly in the REPL", DeprecationWarning) - - args = shlex.split(line) - - if len(args) == 0: - print("Usage:") - self.do_help("set-log-output") - return - if len(args) > 1: - print("Unexpected argument: " + args[1]) - return - - category = args[0].lower() - if category == "none": - category = 0 - elif category == "error": - category = 1 - elif category == "progress": - category = 2 - elif category == "detail": - category = 3 - else: - print("Invalid argument: " + args[0]) - return - - try: - self.devCtrl.SetLogFilter(category) - except exceptions.ChipStackException as ex: - print(str(ex)) - return - - def do_setuppayload(self, line): - """ - setup-payload generate [options] - - Options: - -vr Version - -vi Vendor ID - -pi Product ID - -cf Custom Flow [Standard = 0, UserActionRequired = 1, Custom = 2] - -dc Discovery Capabilities [SoftAP = 1 | BLE = 2 | OnNetwork = 4] - -dv Discriminator Value - -ps Passcode - - setup-payload parse-manual - setup-payload parse-qr - """ - - warnings.warn( - "This method is being deprecated. " - "Please use the SetupPayload function in the chip.setup_payload package directly", DeprecationWarning) - - try: - arglist = shlex.split(line) - if arglist[0] not in ("generate", "parse-manual", "parse-qr"): - self.do_help("setup-payload") - return - - if arglist[0] == "generate": - parser = argparse.ArgumentParser() - parser.add_argument("-vr", type=int, default=0, dest='version') - parser.add_argument( - "-pi", type=int, default=0, dest='productId') - parser.add_argument( - "-vi", type=int, default=0, dest='vendorId') - parser.add_argument( - '-cf', type=int, default=0, dest='customFlow') - parser.add_argument( - "-dc", type=int, default=0, dest='capabilities') - parser.add_argument( - "-dv", type=int, default=0, dest='discriminator') - parser.add_argument("-ps", type=int, dest='passcode') - args = parser.parse_args(arglist[1:]) - - SetupPayload().PrintOnboardingCodes(args.passcode, args.vendorId, args.productId, - args.discriminator, args.customFlow, args.capabilities, args.version) - - if arglist[0] == "parse-manual": - SetupPayload().ParseManualPairingCode(arglist[1]).Print() - - if arglist[0] == "parse-qr": - SetupPayload().ParseQrCode(arglist[1]).Print() - - except exceptions.ChipStackException as ex: - print(str(ex)) - return - - def do_bleadapterselect(self, line): - """ - ble-adapter-select - - Start BLE adapter select, deprecated, you can select adapter by command line arguments. - """ - if sys.platform.startswith("linux"): - if not self.bleMgr: - self.bleMgr = BleManager(self.devCtrl) - - self.bleMgr.ble_adapter_select(line) - print( - "This change only applies to ble-scan\n" - "Please run device controller with --bluetooth-adapter= to select adapter\n" + - "e.g. chip-device-ctrl --bluetooth-adapter hci0" - ) - else: - print( - "ble-adapter-select only works in Linux, ble-adapter-select mac_address" - ) - - return - - def do_bleadapterprint(self, line): - """ - ble-adapter-print - - Print attached BLE adapter. - """ - if sys.platform.startswith("linux"): - if not self.bleMgr: - self.bleMgr = BleManager(self.devCtrl) - - self.bleMgr.ble_adapter_print() - else: - print("ble-adapter-print only works in Linux") - - return - - def do_bledebuglog(self, line): - """ - ble-debug-log 0:1 - 0: disable BLE debug log - 1: enable BLE debug log - """ - if not self.bleMgr: - self.bleMgr = BleManager(self.devCtrl) - - self.bleMgr.ble_debug_log(line) - - return - - def do_blescan(self, line): - """ - ble-scan - - Start BLE scanning operations. - """ - - if not self.bleMgr: - self.bleMgr = BleManager(self.devCtrl) - - self.bleMgr.scan(line) - - return - - def ConnectFromSetupPayload(self, setupPayload, nodeid): - # TODO(cecille): Get this from the C++ code? - ble = 1 << 1 - # Devices may be uncommissioned, or may already be on the network. Need to check both ways. - # TODO(cecille): implement soft-ap connection. - - # Any device that is already commissioned into a fabric needs to use on-network - # pairing, so look first on the network regardless of the QR code contents. - print("Attempting to find device on Network") - longDiscriminator = ctypes.c_uint16( - int(setupPayload.attributes['Discriminator'])) - self.devCtrl.DiscoverCommissionableNodesLongDiscriminator( - longDiscriminator) - print("Waiting for device responses...") - strlen = 100 - addrStrStorage = ctypes.create_string_buffer(strlen) - # If this device is on the network and we're looking specifically for 1 device, - # expect a quick response. - if self.wait_for_one_discovered_device(): - self.devCtrl.GetIPForDiscoveredDevice( - 0, addrStrStorage, strlen) - addrStr = addrStrStorage.value.decode('utf-8') - print("Connecting to device at " + addrStr) - pincode = ctypes.c_uint32( - int(setupPayload.attributes['SetUpPINCode'])) - try: - self.devCtrl.CommissionIP(addrStrStorage, pincode, nodeid) - print("Connected") - return 0 - except Exception as ex: - print(f"Unable to connect on network: {ex}") - else: - print("Unable to locate device on network") - - if int(setupPayload.attributes["RendezvousInformation"]) & ble: - print("Attempting to connect via BLE") - longDiscriminator = ctypes.c_uint16( - int(setupPayload.attributes['Discriminator'])) - pincode = ctypes.c_uint32( - int(setupPayload.attributes['SetUpPINCode'])) - try: - self.devCtrl.ConnectBLE(longDiscriminator, pincode, nodeid) - print("Connected") - return 0 - except Exception as ex: - print(f"Unable to connect: {ex}") - return -1 - - def do_paseonly(self, line): - """ - paseonly -ip [] - - TODO: Add more methods to connect to device (like cert for auth, and IP - for connection) - """ - - try: - args = shlex.split(line) - if len(args) <= 1: - print("Usage:") - self.do_help("paseonly") - return - nodeid = random.randint(1, 1000000) # Just a random number - if len(args) == 4: - nodeid = int(args[3]) - print("Device is assigned with nodeid = {}".format(nodeid)) - self.replHint = f"devCtrl.EstablishPASESessionIP({repr(args[1])}, {int(args[2])}, {nodeid})" - if args[0] == "-ip" and len(args) >= 3: - self.devCtrl.EstablishPASESessionIP(args[1], int(args[2]), nodeid) - else: - print("Usage:") - self.do_help("paseonly") - return - print( - "Device temporary node id (**this does not match spec**): {}".format(nodeid)) - except Exception as ex: - print(str(ex)) - return - - def do_commission(self, line): - """ - commission nodeid - - Runs commissioning on a device that has been connected with paseonly - """ - try: - args = shlex.split(line) - if len(args) != 1: - print("Usage:") - self.do_help("commission") - return - nodeid = int(args[0]) - self.replHint = f"devCtrl.Commission({nodeid})" - self.devCtrl.Commission(nodeid) - except Exception as ex: - print(str(ex)) - return - - def do_connect(self, line): - """ - connect -ip [] - connect -ble [] - connect -qr [] - connect -code [] - - connect command is used for establishing a rendezvous session to the device. - currently, only connect using setupPinCode is supported. - -qr option will connect to the first device with a matching long discriminator. - - TODO: Add more methods to connect to device (like cert for auth, and IP - for connection) - """ - - warnings.warn( - "This method is being deprecated. " - "Please use the DeviceController.[ConnectBLE|CommissionIP] methods directly in the REPL", DeprecationWarning) - - try: - args = shlex.split(line) - if len(args) <= 1: - print("Usage:") - self.do_help("connect SetupPinCode") - return - - nodeid = random.randint(1, 1000000) # Just a random number - if len(args) == 4: - nodeid = int(args[3]) - print("Device is assigned with nodeid = {}".format(nodeid)) - - if args[0] == "-ip" and len(args) >= 3: - self.replHint = f"devCtrl.CommissionIP({repr(args[1])}, {int(args[2])}, {nodeid})" - self.devCtrl.CommissionIP(args[1], int(args[2]), nodeid) - elif args[0] == "-ble" and len(args) >= 3: - self.replHint = f"devCtrl.ConnectBLE({int(args[1])}, {int(args[2])}, {nodeid})" - self.devCtrl.ConnectBLE(int(args[1]), int(args[2]), nodeid) - elif args[0] in ['-qr', '-code'] and len(args) >= 2: - if len(args) == 3: - nodeid = int(args[2]) - print("Parsing QR code {}".format(args[1])) - - setupPayload = None - if args[0] == '-qr': - setupPayload = SetupPayload().ParseQrCode(args[1]) - elif args[0] == '-code': - setupPayload = SetupPayload( - ).ParseManualPairingCode(args[1]) - - if not int(setupPayload.attributes.get("RendezvousInformation", 0)): - print("No rendezvous information provided, default to all.") - setupPayload.attributes["RendezvousInformation"] = 0b111 - setupPayload.Print() - self.replHint = f"devCtrl.CommissionWithCode(setupPayload={repr(setupPayload)}, nodeid={nodeid})" - self.ConnectFromSetupPayload(setupPayload, nodeid) - else: - print("Usage:") - self.do_help("connect SetupPinCode") - return - print( - "Device temporary node id (**this does not match spec**): {}".format(nodeid)) - except exceptions.ChipStackException as ex: - print(str(ex)) - return - - def do_closesession(self, line): - """ - close-session - - Close any session associated with a given node ID. - """ - try: - parser = argparse.ArgumentParser() - parser.add_argument('nodeid', type=int, help='Peer node ID') - args = parser.parse_args(shlex.split(line)) - self.replHint = f"devCtrl.CloseSession({args.nodeid})" - self.devCtrl.CloseSession(args.nodeid) - except exceptions.ChipStackException as ex: - print(str(ex)) - except Exception: - self.do_help("close-session") - - def do_resolve(self, line): - """ - resolve - - Resolve DNS-SD name corresponding with the given node ID and - update address of the node in the device controller. - """ - try: - args = shlex.split(line) - if len(args) == 1: - try: - self.replHint = f"devCtrl.ResolveNode({int(args[0])});devCtrl.GetAddressAndPort({int(args[0])})" - self.devCtrl.ResolveNode(int(args[0])) - address = self.devCtrl.GetAddressAndPort(int(args[0])) - address = "{}:{}".format( - *address) if address else "unknown" - print("Current address: " + address) - except exceptions.ChipStackException as ex: - print(str(ex)) - else: - self.do_help("resolve") - except exceptions.ChipStackException as ex: - print(str(ex)) - return - - def wait_for_one_discovered_device(self): - print("Waiting for device responses...") - strlen = 100 - addrStrStorage = ctypes.create_string_buffer(strlen) - count = 0 - maxWaitTime = 2 - while (not self.devCtrl.GetIPForDiscoveredDevice(0, addrStrStorage, strlen) and count < maxWaitTime): - time.sleep(0.2) - count = count + 0.2 - return count < maxWaitTime - - def wait_for_many_discovered_devices(self): - # Discovery happens through mdns, which means we need to wait for responses to come back. - # TODO(cecille): I suppose we could make this a command line arg. Or Add a callback when - # x number of responses are received. For now, just 2 seconds. We can all wait that long. - print("Waiting for device responses...") - time.sleep(2) - - def do_discover(self, line): - """ - discover -qr qrcode - discover -all - discover -l long_discriminator - discover -s short_discriminator - discover -v vendor_id - discover -t device_type - discover -c - - discover command is used to discover available devices. - """ - try: - arglist = shlex.split(line) - if len(arglist) < 1: - print("Usage:") - self.do_help("discover") - return - parser = argparse.ArgumentParser() - group = parser.add_mutually_exclusive_group() - group.add_argument( - '-all', help='discover all commissionable nodes and commissioners', action='store_true') - group.add_argument( - '-qr', help='discover commissionable nodes matching provided QR code', type=str) - group.add_argument( - '-l', help='discover commissionable nodes with given long discriminator', type=int) - group.add_argument( - '-s', help='discover commissionable nodes with given short discriminator', type=int) - group.add_argument( - '-v', help='discover commissionable nodes with given vendor ID', type=int) - group.add_argument( - '-t', help='discover commissionable nodes with given device type', type=int) - group.add_argument( - '-c', help='discover commissionable nodes in commissioning mode', action='store_true') - args = parser.parse_args(arglist) - if args.all: - self.commissionableNodeCtrl.DiscoverCommissioners() - self.wait_for_many_discovered_devices() - self.commissionableNodeCtrl.PrintDiscoveredCommissioners() - self.devCtrl.DiscoverAllCommissioning() - self.wait_for_many_discovered_devices() - elif args.qr is not None: - setupPayload = SetupPayload().ParseQrCode(args.qr) - longDiscriminator = ctypes.c_uint16( - int(setupPayload.attributes['Discriminator'])) - self.devCtrl.DiscoverCommissionableNodesLongDiscriminator( - longDiscriminator) - self.wait_for_one_discovered_device() - elif args.l is not None: - self.devCtrl.DiscoverCommissionableNodesLongDiscriminator( - ctypes.c_uint16(args.l)) - self.wait_for_one_discovered_device() - elif args.s is not None: - self.devCtrl.DiscoverCommissionableNodesShortDiscriminator( - ctypes.c_uint16(args.s)) - self.wait_for_one_discovered_device() - elif args.v is not None: - self.devCtrl.DiscoverCommissionableNodesVendor( - ctypes.c_uint16(args.v)) - self.wait_for_many_discovered_devices() - elif args.t is not None: - self.devCtrl.DiscoverCommissionableNodesDeviceType( - ctypes.c_uint16(args.t)) - self.wait_for_many_discovered_devices() - elif args.c is not None: - self.devCtrl.DiscoverCommissionableNodesCommissioningEnabled() - self.wait_for_many_discovered_devices() - else: - self.do_help("discover") - return - self.devCtrl.PrintDiscoveredDevices() - except exceptions.ChipStackException as ex: - print('exception') - print(str(ex)) - return - except Exception: - self.do_help("discover") - return - - def do_zcl(self, line): - """ - To send ZCL message to device: - zcl [key=value]... - To get a list of clusters: - zcl ? - To get a list of commands in cluster: - zcl ? - - Send ZCL command to device nodeid - """ - try: - args = shlex.split(line) - all_commands = self.devCtrl.ZCLCommandList() - if len(args) == 1 and args[0] == '?': - print('\n'.join(all_commands.keys())) - elif len(args) == 2 and args[0] == '?': - if args[1] not in all_commands: - raise exceptions.UnknownCluster(args[1]) - for commands in all_commands.get(args[1]).items(): - args = ", ".join(["{}: {}".format(argName, argType) - for argName, argType in commands[1].items()]) - print(commands[0]) - if commands[1]: - print(" ", args) - else: - print(" ") - elif len(args) > 4: - if args[0] not in all_commands: - raise exceptions.UnknownCluster(args[0]) - command = all_commands.get(args[0]).get(args[1], None) - # When command takes no arguments, (not command) is True - if command is None: - raise exceptions.UnknownCommand(args[0], args[1]) - req = eval(f"Clusters.{args[0]}.Commands.{args[1]}")(**FormatZCLArguments(args[5:], command)) - self.replHint = f"await devCtrl.SendCommand({int(args[2])}, {int(args[3])}, Clusters.{repr(req)})" - err, res = self.devCtrl.ZCLSend(args[0], args[1], int( - args[2]), int(args[3]), int(args[4]), FormatZCLArguments(args[5:], command), blocking=True) - if err != 0: - print("Failed to receive command response: {}".format(res)) - elif res is not None: - print("Received command status response:") - print(res) - else: - print("Success, no status code is attached with response.") - else: - self.do_help("zcl") - except exceptions.ChipStackException as ex: - print("An exception occurred during process ZCL command:") - print(str(ex)) - except Exception as ex: - print("An exception occurred during processing input:") - traceback.print_exc() - print(str(ex)) - - def do_zclread(self, line): - """ - To read ZCL attribute: - zclread - """ - try: - args = shlex.split(line) - all_attrs = self.devCtrl.ZCLAttributeList() - if len(args) == 1 and args[0] == '?': - print('\n'.join(all_attrs.keys())) - elif len(args) == 2 and args[0] == '?': - if args[1] not in all_attrs: - raise exceptions.UnknownCluster(args[1]) - print('\n'.join(all_attrs.get(args[1]).keys())) - elif len(args) == 5: - if args[0] not in all_attrs: - raise exceptions.UnknownCluster(args[0]) - self.replHint = (f"await devCtrl.ReadAttribute({int(args[2])}, [({int(args[3])}, " - f"Clusters.{args[0]}.Attributes.{args[1]})])") - res = self.devCtrl.ZCLReadAttribute(args[0], args[1], int( - args[2]), int(args[3]), int(args[4])) - if res is not None: - print(repr(res)) - else: - self.do_help("zclread") - except exceptions.ChipStackException as ex: - print("An exception occurred during reading ZCL attribute:") - print(str(ex)) - except Exception as ex: - print("An exception occurred during processing input:") - print(str(ex)) - - def do_zclwrite(self, line): - """ - To write ZCL attribute: - zclwrite - """ - try: - args = shlex.split(line) - all_attrs = self.devCtrl.ZCLAttributeList() - if len(args) == 1 and args[0] == '?': - print('\n'.join(all_attrs.keys())) - elif len(args) == 2 and args[0] == '?': - if args[1] not in all_attrs: - raise exceptions.UnknownCluster(args[1]) - cluster_attrs = all_attrs.get(args[1], {}) - print('\n'.join(["{}: {}".format(key, cluster_attrs[key]["type"]) - for key in cluster_attrs.keys() if cluster_attrs[key].get("writable", False)])) - elif len(args) == 6: - if args[0] not in all_attrs: - raise exceptions.UnknownCluster(args[0]) - attribute_type = all_attrs.get(args[0], {}).get( - args[1], {}).get("type", None) - self.replHint = ( - f"await devCtrl.WriteAttribute({int(args[2])}, [({int(args[3])}, " - f"Clusters.{args[0]}.Attributes.{args[1]}(value={repr(ParseValueWithType(args[5], attribute_type))}))])") - res = self.devCtrl.ZCLWriteAttribute(args[0], args[1], int( - args[2]), int(args[3]), int(args[4]), ParseValueWithType(args[5], attribute_type)) - print(repr(res)) - else: - self.do_help("zclwrite") - except exceptions.ChipStackException as ex: - print("An exception occurred during writing ZCL attribute:") - print(str(ex)) - except Exception as ex: - print("An exception occurred during processing input:") - print(str(ex)) - - def do_zclsubscribe(self, line): - """ - To subscribe ZCL attribute reporting: - zclsubscribe - - To shut down a subscription: - zclsubscribe -shutdown - """ - try: - args = shlex.split(line) - all_attrs = self.devCtrl.ZCLAttributeList() - if len(args) == 1 and args[0] == '?': - print('\n'.join(all_attrs.keys())) - elif len(args) == 2 and args[0] == '?': - if args[1] not in all_attrs: - raise exceptions.UnknownCluster(args[1]) - cluster_attrs = all_attrs.get(args[1], {}) - print('\n'.join([key for key in cluster_attrs.keys( - ) if cluster_attrs[key].get("reportable", False)])) - elif len(args) == 6: - if args[0] not in all_attrs: - raise exceptions.UnknownCluster(args[0]) - res = self.devCtrl.ZCLSubscribeAttribute(args[0], args[1], int( - args[2]), int(args[3]), int(args[4]), int(args[5])) - self.replHint = (f"sub = await devCtrl.ReadAttribute({int(args[2])}, [({int(args[3])}, " - f"Clusters.{args[0]}.Attributes.{args[1]})], reportInterval=({int(args[4])}, {int(args[5])}))") - print(res.GetAllValues()) - print(f"Subscription Established: {res}") - elif len(args) == 2 and args[0] == '-shutdown': - subscriptionId = int(args[1], base=0) - self.replHint = "You can call sub.Shutdown() (sub is the return value of ReadAttribute() called before)" - self.devCtrl.ZCLShutdownSubscription(subscriptionId) - else: - self.do_help("zclsubscribe") - except exceptions.ChipStackException as ex: - print("An exception occurred during configuring reporting of ZCL attribute:") - print(str(ex)) - except Exception as ex: - print("An exception occurred during processing input:") - print(str(ex)) - - def do_setpairingwificredential(self, line): - """ - set-pairing-wifi-credential ssid credentials - """ - try: - args = shlex.split(line) - if len(args) < 2: - print("Usage:") - self.do_help("set-pairing-wifi-credential") - return - self.devCtrl.SetWiFiCredentials( - args[0], args[1]) - self.replHint = f"devCtrl.SetWiFiCredentials({repr(args[0])}, {repr(args[1])})" - except Exception as ex: - print(str(ex)) - return - - def do_setpairingthreadcredential(self, line): - """ - set-pairing-thread-credential threadOperationalDataset - """ - try: - args = shlex.split(line) - if len(args) < 1: - print("Usage:") - self.do_help("set-pairing-thread-credential") - return - self.replHint = f"devCtrl.SetThreadOperationalDataset(bytes.fromhex({repr(args[0])}))" - self.devCtrl.SetThreadOperationalDataset(bytes.fromhex(args[0])) - except Exception as ex: - print(str(ex)) - return - - def do_opencommissioningwindow(self, line): - """ - open-commissioning-window [options] - - Options: - -t Timeout (in seconds) - -o Option [TokenWithRandomPIN = 1, TokenWithProvidedPIN = 2] - -d Discriminator Value - -i Iteration - - This command is used by a current Administrator to instruct a Node to go into commissioning mode - """ - try: - arglist = shlex.split(line) - - if len(arglist) <= 1: - print("Usage:") - self.do_help("open-commissioning-window") - return - parser = argparse.ArgumentParser() - parser.add_argument( - "-t", type=int, default=0, dest='timeout') - parser.add_argument( - "-o", type=int, default=1, dest='option') - parser.add_argument( - "-i", type=int, default=0, dest='iteration') - parser.add_argument( - "-d", type=int, default=0, dest='discriminator') - args = parser.parse_args(arglist[1:]) - - if args.option < 1 or args.option > 2: - print("Invalid option specified!") - raise ValueError("Invalid option specified") - - self.replHint = (f"devCtrl.OpenCommissioningWindow(nodeid={int(arglist[0])}, timeout={args.timeout}, " - f"iteration={args.iteration}, discriminator={args.discriminator}, option={args.option})") - - self.devCtrl.OpenCommissioningWindow( - int(arglist[0]), args.timeout, args.iteration, args.discriminator, args.option) - - except exceptions.ChipStackException as ex: - print(str(ex)) - return - except Exception: - self.do_help("open-commissioning-window") - return - - def do_getfabricid(self, line): - """ - get-fabricid - - Read the current Compressed Fabric Id of the controller device, return 0 if not available. - """ - try: - args = shlex.split(line) - - if (len(args) > 0): - print("Unexpected argument: " + args[1]) - return - - compressed_fabricid = self.devCtrl.GetCompressedFabricId() - raw_fabricid = self.devCtrl.fabricId - - self.replHint = "devCtrl.GetCompressedFabricId(), devCtrl.fabricId" - except exceptions.ChipStackException as ex: - print("An exception occurred during reading FabricID:") - print(str(ex)) - return - - print("Get fabric ID complete") - - print("Raw Fabric ID: 0x{:016x}".format(raw_fabricid) - + " (" + str(raw_fabricid) + ")") - - print("Compressed Fabric ID: 0x{:016x}".format(compressed_fabricid) - + " (" + str(compressed_fabricid) + ")") - - def do_history(self, line): - """ - history - - Show previously executed commands. - """ - - try: - import readline - - h = readline.get_current_history_length() - for n in range(1, h + 1): - print(readline.get_history_item(n)) - except ImportError: - pass - - def do_h(self, line): - self.do_history(line) - - def do_exit(self, line): - return True - - def do_quit(self, line): - return True - - def do_q(self, line): - return True - - def do_EOF(self, line): - print() - return True - - def emptyline(self): - pass - - -def main(): - optParser = OptionParser() - optParser.add_option( - "-r", - "--rendezvous-addr", - action="store", - dest="rendezvousAddr", - help="Device rendezvous address", - metavar="", - ) - optParser.add_option( - "-n", - "--controller-nodeid", - action="store", - dest="controllerNodeId", - default=1, - type='int', - help="Controller node ID", - metavar="", - ) - - if sys.platform.startswith("linux"): - optParser.add_option( - "-b", - "--bluetooth-adapter", - action="store", - dest="bluetoothAdapter", - default="hci0", - type="str", - help="Controller bluetooth adapter ID, use --no-ble to disable bluetooth functions.", - metavar="", - ) - optParser.add_option( - "--no-ble", - action="store_true", - dest="disableBluetooth", - help="Disable bluetooth, calling BLE related feature with this flag results in undefined behavior.", - ) - (options, remainingArgs) = optParser.parse_args(sys.argv[1:]) - - if len(remainingArgs) != 0: - print("Unexpected argument: %s" % remainingArgs[0]) - sys.exit(-1) - - adapterId = None - if sys.platform.startswith("linux"): - if options.disableBluetooth: - adapterId = None - elif not options.bluetoothAdapter.startswith("hci"): - print( - "Invalid bluetooth adapter: {}, adapter name looks like hci0, hci1 etc.") - sys.exit(-1) - else: - try: - adapterId = int(options.bluetoothAdapter[3:]) - except ValueError: - print( - "Invalid bluetooth adapter: {}, adapter name looks like hci0, hci1 etc.") - sys.exit(-1) - native.Init(bluetoothAdapter=adapterId) - try: - devMgrCmd = DeviceMgrCmd(rendezvousAddr=options.rendezvousAddr, - controllerNodeId=options.controllerNodeId, bluetoothAdapter=adapterId) - except Exception as ex: - print(ex) - print("Failed to bringup CHIPDeviceController CLI") - sys.exit(1) - - print("Chip Device Controller Shell") - if options.rendezvousAddr: - print("Rendezvous address set to %s" % options.rendezvousAddr) - - # Adapter ID will always be 0 - if adapterId != 0: - print("Bluetooth adapter set to hci{}".format(adapterId)) - print() - - try: - devMgrCmd.cmdloop() - except KeyboardInterrupt: - print("\nQuitting") - - sys.exit(0) - - -if __name__ == "__main__": - print(""" - chip-device-ctrl will be deprecated and will be removed in the future. Please try chip-repl, which provides a lot of features. - - - Multi-fabric support, - - Better complex type support for sending commands, - - Native command highlight, - - Parallel commands with asyncio, - - Writing complex logic inline. - - You can still use chip-device-ctrl as usual for now, and you will learn how to do the same thing in chip-repl. - - Feel free to file an issue if some features are not supported by chip-repl yet. - """) - main() diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py index d63a3772e62dc6..4fb8fb28bcaf90 100644 --- a/src/controller/python/chip/ChipDeviceCtrl.py +++ b/src/controller/python/chip/ChipDeviceCtrl.py @@ -50,12 +50,9 @@ from .clusters import Attribute as ClusterAttribute from .clusters import ClusterObjects as ClusterObjects from .clusters import Command as ClusterCommand -from .clusters import Objects as GeneratedObjects from .clusters.CHIPClusters import ChipClusters from .crypto import p256keypair -from .exceptions import UnknownAttribute, UnknownCommand -from .interaction_model import InteractionModelError, SessionParameters, SessionParametersStruct -from .interaction_model import delegate as im +from .interaction_model import SessionParameters, SessionParametersStruct from .native import PyChipError __all__ = ["ChipDeviceController", "CommissioningParameters"] @@ -1170,33 +1167,26 @@ def _parseAttributePathTuple(self, pathTuple: typing.Union[ # Concrete path typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]] ]): - endpoint = None - cluster = None - attribute = None - if pathTuple == ('*') or pathTuple == (): # Wildcard - pass + return ClusterAttribute.AttributePath() elif not isinstance(pathTuple, tuple): if isinstance(pathTuple, int): - endpoint = pathTuple + return ClusterAttribute.AttributePath(EndpointId=pathTuple) elif issubclass(pathTuple, ClusterObjects.Cluster): - cluster = pathTuple + return ClusterAttribute.AttributePath.from_cluster(EndpointId=None, Cluster=pathTuple) elif issubclass(pathTuple, ClusterObjects.ClusterAttributeDescriptor): - attribute = pathTuple + return ClusterAttribute.AttributePath.from_attribute(EndpointId=None, Attribute=pathTuple) else: raise ValueError("Unsupported Attribute Path") else: # endpoint + (cluster) attribute / endpoint + cluster - endpoint = pathTuple[0] if issubclass(pathTuple[1], ClusterObjects.Cluster): - cluster = pathTuple[1] + return ClusterAttribute.AttributePath.from_cluster(EndpointId=pathTuple[0], Cluster=pathTuple[1]) elif issubclass(pathTuple[1], ClusterAttribute.ClusterAttributeDescriptor): - attribute = pathTuple[1] + return ClusterAttribute.AttributePath.from_attribute(EndpointId=pathTuple[0], Attribute=pathTuple[1]) else: raise ValueError("Unsupported Attribute Path") - return ClusterAttribute.AttributePath( - EndpointId=endpoint, Cluster=cluster, Attribute=attribute) def _parseDataVersionFilterTuple(self, pathTuple: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]]): endpoint = None @@ -1209,7 +1199,7 @@ def _parseDataVersionFilterTuple(self, pathTuple: typing.List[typing.Tuple[int, else: raise ValueError("Unsupported Cluster Path") dataVersion = pathTuple[2] - return ClusterAttribute.DataVersionFilter( + return ClusterAttribute.DataVersionFilter.from_cluster( EndpointId=endpoint, Cluster=cluster, DataVersion=dataVersion) def _parseEventPathTuple(self, pathTuple: typing.Union[ @@ -1226,39 +1216,31 @@ def _parseEventPathTuple(self, pathTuple: typing.Union[ typing.Tuple[int, typing.Type[ClusterObjects.ClusterEvent], int] ]): - endpoint = None - cluster = None - event = None - urgent = False if pathTuple in [('*'), ()]: # Wildcard - pass + return ClusterAttribute.EventPath() elif not isinstance(pathTuple, tuple): logging.debug(type(pathTuple)) if isinstance(pathTuple, int): - endpoint = pathTuple + return ClusterAttribute.EventPath(EndpointId=pathTuple) elif issubclass(pathTuple, ClusterObjects.Cluster): - cluster = pathTuple + return ClusterAttribute.EventPath.from_cluster(EndpointId=None, Cluster=pathTuple) elif issubclass(pathTuple, ClusterObjects.ClusterEvent): - event = pathTuple + return ClusterAttribute.EventPath.from_event(EndpointId=None, Event=pathTuple) else: raise ValueError("Unsupported Event Path") else: if pathTuple[0] == '*': - urgent = pathTuple[-1] - pass + return ClusterAttribute.EventPath(Urgent=pathTuple[-1]) else: + urgent = bool(pathTuple[-1]) if len(pathTuple) > 2 else False # endpoint + (cluster) event / endpoint + cluster - endpoint = pathTuple[0] if issubclass(pathTuple[1], ClusterObjects.Cluster): - cluster = pathTuple[1] + return ClusterAttribute.EventPath.from_cluster(EndpointId=pathTuple[0], Cluster=pathTuple[1], Urgent=urgent) elif issubclass(pathTuple[1], ClusterAttribute.ClusterEvent): - event = pathTuple[1] + return ClusterAttribute.EventPath.from_event(EndpointId=pathTuple[0], Event=pathTuple[1], Urgent=urgent) else: raise ValueError("Unsupported Attribute Path") - urgent = bool(pathTuple[-1]) if len(pathTuple) > 2 else False - return ClusterAttribute.EventPath( - EndpointId=endpoint, Cluster=cluster, Event=event, Urgent=urgent) async def Read(self, nodeid: int, attributes: typing.List[typing.Union[ None, # Empty tuple, all wildcard @@ -1495,83 +1477,6 @@ async def ReadEvent(self, nodeid: int, events: typing.List[typing.Union[ else: return res.events - def ZCLSend(self, cluster, command, nodeid, endpoint, groupid, args, blocking=False): - ''' Wrapper over SendCommand that catches the exceptions - Returns a tuple of (errorCode, CommandResponse) - ''' - self.CheckIsActive() - - req = None - try: - req = eval( - f"GeneratedObjects.{cluster}.Commands.{command}")(**args) - except BaseException: - raise UnknownCommand(cluster, command) - try: - res = asyncio.run(self.SendCommand(nodeid, endpoint, req)) - logging.debug(f"CommandResponse {res}") - return (0, res) - except InteractionModelError as ex: - return (int(ex.status), None) - - def ZCLReadAttribute(self, cluster, attribute, nodeid, endpoint, groupid, blocking=True): - ''' Wrapper over ReadAttribute for a single attribute - Returns an AttributeReadResult - ''' - self.CheckIsActive() - - clusterType = getattr(GeneratedObjects, cluster) - - try: - attributeType = eval( - f"GeneratedObjects.{cluster}.Attributes.{attribute}") - except BaseException: - raise UnknownAttribute(cluster, attribute) - - result = asyncio.run(self.ReadAttribute( - nodeid, [(endpoint, attributeType)])) - path = ClusterAttribute.AttributePath( - EndpointId=endpoint, Attribute=attributeType) - return im.AttributeReadResult(path=im.AttributePath(nodeId=nodeid, endpointId=path.EndpointId, clusterId=path.ClusterId, attributeId=path.AttributeId), - status=0, value=result[endpoint][clusterType][attributeType], dataVersion=result[endpoint][clusterType][ClusterAttribute.DataVersion]) - - def ZCLWriteAttribute(self, cluster: str, attribute: str, nodeid, endpoint, groupid, value, dataVersion=0, blocking=True): - ''' Wrapper over WriteAttribute for a single attribute - return PyChipError - ''' - req = None - try: - req = eval( - f"GeneratedObjects.{cluster}.Attributes.{attribute}")(value) - except BaseException: - raise UnknownAttribute(cluster, attribute) - - return asyncio.run(self.WriteAttribute(nodeid, [(endpoint, req, dataVersion)])) - - def ZCLSubscribeAttribute(self, cluster, attribute, nodeid, endpoint, minInterval, maxInterval, blocking=True, - keepSubscriptions=False, autoResubscribe=True): - ''' Wrapper over ReadAttribute for a single attribute - Returns a SubscriptionTransaction. See ReadAttribute for more information. - ''' - self.CheckIsActive() - - req = None - try: - req = eval(f"GeneratedObjects.{cluster}.Attributes.{attribute}") - except BaseException: - raise UnknownAttribute(cluster, attribute) - return asyncio.run(self.ReadAttribute(nodeid, [(endpoint, req)], None, False, reportInterval=(minInterval, maxInterval), - keepSubscriptions=keepSubscriptions, autoResubscribe=autoResubscribe)) - - def ZCLCommandList(self): - self.CheckIsActive() - return self._Cluster.ListClusterCommands() - - def ZCLAttributeList(self): - self.CheckIsActive() - - return self._Cluster.ListClusterAttributes() - def SetBlockingCB(self, blockingCB): self.CheckIsActive() diff --git a/src/controller/python/chip/ChipReplStartup.py b/src/controller/python/chip/ChipReplStartup.py index a49638cb99e56d..b75c77efcd5b2b 100644 --- a/src/controller/python/chip/ChipReplStartup.py +++ b/src/controller/python/chip/ChipReplStartup.py @@ -94,6 +94,8 @@ def main(): "-d", "--debug", help="Set default logging level to debug.", action="store_true") parser.add_argument( "-t", "--trust-store", help="Path to the PAA trust store.", action="store", default="./credentials/development/paa-root-certs") + parser.add_argument( + "-b", "--ble-adapter", help="Set the Bluetooth adapter index.", type=int, default=None) args = parser.parse_args() if not os.path.exists(args.trust_store): @@ -128,7 +130,7 @@ def main(): # nothing we can do ... things will NOT work return - chip.native.Init() + chip.native.Init(bluetoothAdapter=args.ble_adapter) global certificateAuthorityManager global chipStack diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py index 9e46eed469d39f..a1a341ed46614a 100644 --- a/src/controller/python/chip/clusters/Attribute.py +++ b/src/controller/python/chip/clusters/Attribute.py @@ -54,62 +54,43 @@ class EventPriority(Enum): CRITICAL = 2 -@dataclass +@dataclass(frozen=True) class AttributePath: EndpointId: int = None ClusterId: int = None AttributeId: int = None - def __init__(self, EndpointId: int = None, Cluster=None, Attribute=None, ClusterId=None, AttributeId=None): - self.EndpointId = EndpointId - if Cluster is not None: - # Wildcard read for a specific cluster - if (Attribute is not None) or (ClusterId is not None) or (AttributeId is not None): - raise Warning( - "Attribute, ClusterId and AttributeId is ignored when Cluster is specified") - self.ClusterId = Cluster.id - return - if Attribute is not None: - if (ClusterId is not None) or (AttributeId is not None): - raise Warning( - "ClusterId and AttributeId is ignored when Attribute is specified") - self.ClusterId = Attribute.cluster_id - self.AttributeId = Attribute.attribute_id - return - self.ClusterId = ClusterId - self.AttributeId = AttributeId + @staticmethod + def from_cluster(EndpointId: int, Cluster: Cluster) -> AttributePath: + if Cluster is None: + raise ValueError("Cluster cannot be None") + return AttributePath(EndpointId=EndpointId, ClusterId=Cluster.id) + + @staticmethod + def from_attribute(EndpointId: int, Attribute: ClusterAttributeDescriptor) -> AttributePath: + if Attribute is None: + raise ValueError("Attribute cannot be None") + return AttributePath(EndpointId=EndpointId, ClusterId=Attribute.cluster_id, AttributeId=Attribute.attribute_id) def __str__(self) -> str: return f"{self.EndpointId}/{self.ClusterId}/{self.AttributeId}" - def __hash__(self): - return str(self).__hash__() - -@dataclass +@dataclass(frozen=True) class DataVersionFilter: EndpointId: int = None ClusterId: int = None DataVersion: int = None - def __init__(self, EndpointId: int = None, Cluster=None, ClusterId=None, DataVersion=None): - self.EndpointId = EndpointId - if Cluster is not None: - # Wildcard read for a specific cluster - if (ClusterId is not None): - raise Warning( - "Attribute, ClusterId and AttributeId is ignored when Cluster is specified") - self.ClusterId = Cluster.id - else: - self.ClusterId = ClusterId - self.DataVersion = DataVersion + @staticmethod + def from_cluster(EndpointId: int, Cluster: Cluster, DataVersion: int = None) -> AttributePath: + if Cluster is None: + raise ValueError("Cluster cannot be None") + return DataVersionFilter(EndpointId=EndpointId, ClusterId=Cluster.id, DataVersion=DataVersion) def __str__(self) -> str: return f"{self.EndpointId}/{self.ClusterId}/{self.DataVersion}" - def __hash__(self): - return str(self).__hash__() - @dataclass class TypedAttributePath: @@ -165,44 +146,28 @@ def __init__(self, ClusterType: Cluster = None, AttributeType: ClusterAttributeD self.AttributeId = self.AttributeType.attribute_id -@dataclass +@dataclass(frozen=True) class EventPath: EndpointId: int = None ClusterId: int = None EventId: int = None Urgent: int = None - def __init__(self, EndpointId: int = None, Cluster=None, Event=None, ClusterId=None, EventId=None, Urgent=None): - self.EndpointId = EndpointId - self.Urgent = Urgent - if Cluster is not None: - # Wildcard read for a specific cluster - if (Event is not None) or (ClusterId is not None) or (EventId is not None): - raise Warning( - "Event, ClusterId and AttributeId is ignored when Cluster is specified") - self.ClusterId = Cluster.id - return - if Event is not None: - if (ClusterId is not None) or (EventId is not None): - raise Warning( - "ClusterId and EventId is ignored when Event is specified") - self.ClusterId = Event.cluster_id - self.EventId = Event.event_id - return - self.ClusterId = ClusterId - self.EventId = EventId + @staticmethod + def from_cluster(EndpointId: int, Cluster: Cluster, EventId: int = None, Urgent: int = None) -> "EventPath": + if Cluster is None: + raise ValueError("Cluster cannot be None") + return EventPath(EndpointId=EndpointId, ClusterId=Cluster.id, EventId=EventId, Urgent=Urgent) + + @staticmethod + def from_event(EndpointId: int, Event: ClusterEvent, Urgent: int = None) -> "EventPath": + if Event is None: + raise ValueError("Event cannot be None") + return EventPath(EndpointId=EndpointId, ClusterId=Event.cluster_id, EventId=Event.event_id, Urgent=Urgent) def __str__(self) -> str: return f"{self.EndpointId}/{self.ClusterId}/{self.EventId}/{self.Urgent}" - def __hash__(self): - return str(self).__hash__() - - -@dataclass -class AttributePathWithListIndex(AttributePath): - ListIndex: int = None - @dataclass class EventHeader: @@ -688,7 +653,7 @@ def SetClientObjPointers(self, pReadClient, pReadCallback): def GetAllEventValues(self): return self._events - def handleAttributeData(self, path: AttributePathWithListIndex, dataVersion: int, status: int, data: bytes): + def handleAttributeData(self, path: AttributePath, dataVersion: int, status: int, data: bytes): try: imStatus = chip.interaction_model.Status(status) diff --git a/src/controller/python/test/test_scripts/cluster_objects.py b/src/controller/python/test/test_scripts/cluster_objects.py index 53516c1dc75e9b..37f6819cbe66a4 100644 --- a/src/controller/python/test/test_scripts/cluster_objects.py +++ b/src/controller/python/test/test_scripts/cluster_objects.py @@ -164,7 +164,7 @@ async def TestWriteRequest(cls, devCtrl): ] ) expectedRes = [ - AttributeStatus(Path=AttributePath( + AttributeStatus(Path=AttributePath.from_attribute( EndpointId=1, Attribute=Clusters.UnitTesting.Attributes.ListLongOctetString), Status=chip.interaction_model.Status.Success), ] diff --git a/src/crypto/tests/TestChipCryptoPAL.cpp b/src/crypto/tests/TestChipCryptoPAL.cpp index c7ad4f3f290522..b4e0fbb2558057 100644 --- a/src/crypto/tests/TestChipCryptoPAL.cpp +++ b/src/crypto/tests/TestChipCryptoPAL.cpp @@ -2804,8 +2804,8 @@ TEST_F(TestChipCryptoPAL, TestVIDPID_x509Extraction) AttestationCertVidPid vidpid; CHIP_ERROR result = ExtractVIDPIDFromX509Cert(testCase.cert, vidpid); EXPECT_EQ(result, testCase.expectedResult); - EXPECT_EQ(vidpid.mVendorId.HasValue(), testCase.expectedVidPresent); - EXPECT_EQ(vidpid.mProductId.HasValue(), testCase.expectedPidPresent); + ASSERT_EQ(vidpid.mVendorId.HasValue(), testCase.expectedVidPresent); + ASSERT_EQ(vidpid.mProductId.HasValue(), testCase.expectedPidPresent); // If present, make sure the VID matches expectation. if (testCase.expectedVidPresent) diff --git a/src/darwin/Framework/CHIP/MTRAsyncCallbackWorkQueue.mm b/src/darwin/Framework/CHIP/MTRAsyncCallbackWorkQueue.mm index 1e44b4a04ef702..62deb58f5008e6 100644 --- a/src/darwin/Framework/CHIP/MTRAsyncCallbackWorkQueue.mm +++ b/src/darwin/Framework/CHIP/MTRAsyncCallbackWorkQueue.mm @@ -77,7 +77,7 @@ - (NSString *)description std::lock_guard lock(_lock); return [NSString - stringWithFormat:@"MTRAsyncCallbackWorkQueue context: %@ items count: %lu", self.context, (unsigned long) self.items.count]; + stringWithFormat:@"MTRAsyncCallbackWorkQueue context: %@ items count: %lu", self.context, static_cast(self.items.count)]; } - (void)enqueueWorkItem:(MTRAsyncCallbackQueueWorkItem *)item diff --git a/src/darwin/Framework/CHIP/MTRAsyncWorkQueue.mm b/src/darwin/Framework/CHIP/MTRAsyncWorkQueue.mm index 53a719502306bd..4d82fded930b87 100644 --- a/src/darwin/Framework/CHIP/MTRAsyncWorkQueue.mm +++ b/src/darwin/Framework/CHIP/MTRAsyncWorkQueue.mm @@ -84,7 +84,7 @@ - (void)setDuplicateTypeID:(NSUInteger)opaqueDuplicateTypeID handler:(MTRAsyncWo - (void)assertMutable { - NSAssert(_state == MTRAsyncWorkItemMutable, @"work item is not mutable (%ld)", (long) _state); + NSAssert(_state == MTRAsyncWorkItemMutable, @"work item is not mutable (%ld)", static_cast(_state)); } #pragma mark Management by the work queue (queue lock held) @@ -109,7 +109,7 @@ - (NSInteger)retryCount - (void)callReadyHandlerWithContext:(id)context completion:(MTRAsyncWorkCompletionBlock)completion { - NSAssert(_state >= MTRAsyncWorkItemEnqueued, @"work item is not enqueued (%ld)", (long) _state); + NSAssert(_state >= MTRAsyncWorkItemEnqueued, @"work item is not enqueued (%ld)", static_cast(_state)); NSInteger retryCount = 0; if (_state == MTRAsyncWorkItemEnqueued) { _state = MTRAsyncWorkItemRunning; @@ -126,9 +126,9 @@ - (void)callReadyHandlerWithContext:(id)context completion:(MTRAsyncWorkCompleti auto readyHandler = _readyHandler; dispatch_async(_queue, ^{ if (!retryCount) { - MTR_LOG_DEFAULT("MTRAsyncWorkQueue<%@> executing work item [%llu]", context, uniqueID); + MTR_LOG("MTRAsyncWorkQueue<%@> executing work item [%llu]", context, uniqueID); } else { - MTR_LOG_DEFAULT("MTRAsyncWorkQueue<%@> executing work item [%llu] (retry %zd)", context, uniqueID, retryCount); + MTR_LOG("MTRAsyncWorkQueue<%@> executing work item [%llu] (retry %zd)", context, uniqueID, retryCount); } if (readyHandler) { readyHandler(context, retryCount, completion); @@ -161,7 +161,7 @@ - (BOOL)isComplete - (void)markComplete { - NSAssert(_state >= MTRAsyncWorkItemEnqueued, @"work item was not enqueued (%ld)", (long) _state); + NSAssert(_state >= MTRAsyncWorkItemEnqueued, @"work item was not enqueued (%ld)", static_cast(_state)); _state = MTRAsyncWorkItemComplete; // Clear all handlers in case any of them captured this object. @@ -185,7 +185,7 @@ - (NSString *)description state = @"enqueued"; break; default: - return [NSString stringWithFormat:@"<%@ %llu running retry: %tu>", self.class, _uniqueID, self.retryCount]; + return [NSString stringWithFormat:@"<%@ %llu running retry: %ld>", self.class, _uniqueID, static_cast(self.retryCount)]; } return [NSString stringWithFormat:@"<%@ %llu %@>", self.class, _uniqueID, state]; } @@ -236,7 +236,7 @@ - (NSString *)description { ContextSnapshot context(self); std::lock_guard lock(_lock); - return [NSString stringWithFormat:@"<%@ context: %@, items count: %tu>", self.class, context.description, _items.count]; + return [NSString stringWithFormat:@"<%@ context: %@, items count: %lu>", self.class, context.description, static_cast(_items.count)]; } - (void)enqueueWorkItem:(MTRAsyncWorkItem *)item @@ -268,9 +268,9 @@ - (void)enqueueWorkItem:(MTRAsyncWorkItem *)item // Logging the description once is enough because other log messages // related to the work item (execution, completion etc) can easily be // correlated using the unique id. - MTR_LOG_DEFAULT("MTRAsyncWorkQueue<%@, items count: %tu> enqueued work item [%llu]: %@", context.description, _items.count, item.uniqueID, description); + MTR_LOG("MTRAsyncWorkQueue<%@, items count: %lu> enqueued work item [%llu]: %@", context.description, static_cast(_items.count), item.uniqueID, description); } else { - MTR_LOG_DEFAULT("MTRAsyncWorkQueue<%@, items count: %tu> enqueued work item [%llu]", context.description, _items.count, item.uniqueID); + MTR_LOG("MTRAsyncWorkQueue<%@, items count: %lu> enqueued work item [%llu]", context.description, static_cast(_items.count), item.uniqueID); } [self _callNextReadyWorkItemWithContext:context]; @@ -280,7 +280,7 @@ - (void)invalidate { ContextSnapshot context(self); // outside of lock std::lock_guard lock(_lock); - MTR_LOG_INFO("MTRAsyncWorkQueue<%@> invalidate %tu items", context.description, _items.count); + MTR_LOG("MTRAsyncWorkQueue<%@> invalidate %lu items", context.description, static_cast(_items.count)); for (MTRAsyncWorkItem * item in _items) { [item cancel]; } @@ -309,14 +309,14 @@ - (void)_postProcessWorkItem:(MTRAsyncWorkItem *)workItem // already part of the running work items allowed by width - retry directly if (retry) { - MTR_LOG_DEFAULT("MTRAsyncWorkQueue<%@> retry needed for work item [%llu]", context.description, workItem.uniqueID); + MTR_LOG("MTRAsyncWorkQueue<%@> retry needed for work item [%llu]", context.description, workItem.uniqueID); [self _callWorkItem:workItem withContext:context]; return; } [workItem markComplete]; [_items removeObjectAtIndex:indexOfWorkItem]; - MTR_LOG_DEFAULT("MTRAsyncWorkQueue<%@, items count: %tu> completed work item [%llu]", context.description, _items.count, workItem.uniqueID); + MTR_LOG("MTRAsyncWorkQueue<%@, items count: %lu> completed work item [%llu]", context.description, static_cast(_items.count), workItem.uniqueID); // sanity check running work item count is positive if (_runningWorkItemCount == 0) { @@ -398,11 +398,11 @@ - (void)_callNextReadyWorkItemWithContext:(ContextSnapshot const &)context case MTRNotBatched: goto done; // can't merge anything else case MTRBatchedPartially: - MTR_LOG_DEFAULT("MTRAsyncWorkQueue<%@> partially merged work item [%llu] into %llu", + MTR_LOG("MTRAsyncWorkQueue<%@> partially merged work item [%llu] into %llu", context.description, nextWorkItem.uniqueID, workItem.uniqueID); goto done; // can't merge anything else case MTRBatchedFully: - MTR_LOG_DEFAULT("MTRAsyncWorkQueue<%@> fully merged work item [%llu] into %llu", + MTR_LOG("MTRAsyncWorkQueue<%@> fully merged work item [%llu] into %llu", context.description, nextWorkItem.uniqueID, workItem.uniqueID); [_items removeObjectAtIndex:1]; continue; // try to batch the next item (if any) diff --git a/src/darwin/Framework/CHIP/MTRBaseDevice.mm b/src/darwin/Framework/CHIP/MTRBaseDevice.mm index 0712e779c525e6..331d61129b9daf 100644 --- a/src/darwin/Framework/CHIP/MTRBaseDevice.mm +++ b/src/darwin/Framework/CHIP/MTRBaseDevice.mm @@ -571,7 +571,7 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue return _MakeDataValueDictionary(typeName, array, dataVersion); } default: - MTR_LOG_ERROR("Error: Unsupported TLV type for conversion: %u", (unsigned) data->GetType()); + MTR_LOG_ERROR("Error: Unsupported TLV type for conversion: %u", static_cast(data->GetType())); return nil; } } diff --git a/src/darwin/Framework/CHIP/MTRCertificates.mm b/src/darwin/Framework/CHIP/MTRCertificates.mm index 9444ae920cc44c..5837a50a3fe21a 100644 --- a/src/darwin/Framework/CHIP/MTRCertificates.mm +++ b/src/darwin/Framework/CHIP/MTRCertificates.mm @@ -42,7 +42,7 @@ + (MTRCertificateDERBytes _Nullable)createRootCertificate:(id)keypai validityPeriod:(NSDateInterval *)validityPeriod error:(NSError * __autoreleasing *)error { - MTR_LOG_DEFAULT("Generating root certificate"); + MTR_LOG("Generating root certificate"); NSData * rootCert = nil; CHIP_ERROR err = MTROperationalCredentialsDelegate::GenerateRootCertificate(keypair, issuerID, fabricID, validityPeriod, &rootCert); @@ -74,7 +74,7 @@ + (MTRCertificateDERBytes _Nullable)createIntermediateCertificate:(id validityPeriod:(NSDateInterval *)validityPeriod error:(NSError * __autoreleasing _Nullable * _Nullable)error { - MTR_LOG_DEFAULT("Generating operational certificate"); + MTR_LOG("Generating operational certificate"); NSData * opcert = nil; CHIP_ERROR err = MTROperationalCredentialsDelegate::GenerateOperationalCertificate( signingKeypair, signingCertificate, operationalPublicKey, fabricID, nodeID, caseAuthenticatedTags, validityPeriod, &opcert); @@ -250,7 +250,7 @@ + (MTRCertificateTLVBytes _Nullable)convertX509Certificate:(MTRCertificateDERByt return nil; } - MTR_LOG_INFO("convertX509Certificate: Success"); + MTR_LOG_DEBUG("convertX509Certificate: Success"); return AsData(chipCertBytes); } diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index 8228b2f12e24e9..63020950d3a6bd 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -101,6 +101,14 @@ - (id)strongObject return aNumber; } +/* BEGIN DRAGONS: Note methods here cannot be renamed, and are used by private callers, do not rename, remove or modify behavior here */ + +@interface NSObject (MatterPrivateForInternalDragonsDoNotFeed) +- (void)_deviceInternalStateChanged:(MTRDevice *)device; +@end + +/* END DRAGONS */ + #pragma mark - SubscriptionCallback class declaration using namespace chip; using namespace chip::app; @@ -141,25 +149,6 @@ - (id)strongObject } // anonymous namespace #pragma mark - MTRDevice -typedef NS_ENUM(NSUInteger, MTRInternalDeviceState) { - // Unsubscribed means we do not have a subscription and are not trying to set one up. - MTRInternalDeviceStateUnsubscribed = 0, - // Subscribing means we are actively trying to establish our initial subscription (e.g. doing - // DNS-SD discovery, trying to establish CASE to the peer, getting priming reports, etc). - MTRInternalDeviceStateSubscribing = 1, - // InitialSubscriptionEstablished means we have at some point finished setting up a - // subscription. That subscription may have dropped since then, but if so it's the ReadClient's - // responsibility to re-establish it. - MTRInternalDeviceStateInitialSubscriptionEstablished = 2, - // Resubscribing means we had established a subscription, but then - // detected a subscription drop due to not receiving a report on time. This - // covers all the actions that happen when re-subscribing (discovery, CASE, - // getting priming reports, etc). - MTRInternalDeviceStateResubscribing = 3, - // LaterSubscriptionEstablished meant that we had a subscription drop and - // then re-created a subscription. - MTRInternalDeviceStateLaterSubscriptionEstablished = 4, -}; // Utility methods for working with MTRInternalDeviceState, located near the // enum so it's easier to notice that they need to stay in sync. @@ -384,6 +373,7 @@ - (BOOL)unitTestForceAttributeReportsIfMatchingCache:(MTRDevice *)device; - (BOOL)unitTestPretendThreadEnabled:(MTRDevice *)device; - (void)unitTestSubscriptionPoolDequeue:(MTRDevice *)device; - (void)unitTestSubscriptionPoolWorkComplete:(MTRDevice *)device; +- (void)unitTestClusterDataPersisted:(MTRDevice *)device; @end #endif @@ -391,7 +381,11 @@ @implementation MTRDevice { #ifdef DEBUG NSUInteger _unitTestAttributesReportedSinceLastCheck; #endif - BOOL _delegateDeviceCachePrimedCalled; + + // _deviceCachePrimed is true if we have the data that comes from an initial + // subscription priming report (whether it came from storage or from our + // subscription). + BOOL _deviceCachePrimed; // _persistedClusterData stores data that we have already persisted (when we have // cluster data persistence enabled). Nil when we have no persistence enabled. @@ -424,6 +418,23 @@ @implementation MTRDevice { // Tracking of initial subscribe latency. When _initialSubscribeStart is // nil, we are not tracking the latency. NSDate * _Nullable _initialSubscribeStart; + + // Storage behavior configuration and variables to keep track of the logic + // _clusterDataPersistenceFirstScheduledTime is used to track the start time of the delay between + // report and persistence. + // _mostRecentReportTimes is a list of the most recent report timestamps used for calculating + // the running average time between reports. + // _deviceReportingExcessivelyStartTime tracks when a device starts reporting excessively. + // _reportToPersistenceDelayCurrentMultiplier is the current multiplier that is calculated when a + // report comes in. + MTRDeviceStorageBehaviorConfiguration * _storageBehaviorConfiguration; + NSDate * _Nullable _clusterDataPersistenceFirstScheduledTime; + NSMutableArray * _mostRecentReportTimes; + NSDate * _Nullable _deviceReportingExcessivelyStartTime; + double _reportToPersistenceDelayCurrentMultiplier; + + // System time change observer reference + id _systemTimeChangeObserverToken; } - (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller @@ -447,11 +458,27 @@ - (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceControlle } _clusterDataToPersist = nil; _persistedClusters = [NSMutableSet set]; - MTR_LOG_INFO("%@ init with hex nodeID 0x%016llX", self, _nodeID.unsignedLongLongValue); + + // If there is a data store, make sure we have an observer to + if (_persistedClusterData) { + mtr_weakify(self); + _systemTimeChangeObserverToken = [[NSNotificationCenter defaultCenter] addObserverForName:NSSystemClockDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull notification) { + mtr_strongify(self); + std::lock_guard lock(self->_lock); + [self _resetStorageBehaviorState]; + }]; + } + + MTR_LOG_DEBUG("%@ init with hex nodeID 0x%016llX", self, _nodeID.unsignedLongLongValue); } return self; } +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:_systemTimeChangeObserverToken]; +} + - (NSString *)description { return [NSString @@ -491,7 +518,7 @@ - (void)_setTimeOnDevice auto dstOffsetsMaxSize = [self readAttributeWithEndpointID:dstOffsetsMaxSizePath.endpoint clusterID:dstOffsetsMaxSizePath.cluster attributeID:dstOffsetsMaxSizePath.attribute params:nil]; if (dstOffsetsMaxSize == nil) { // This endpoint does not support TZ, so won't support SetDSTOffset. - MTR_LOG_DEFAULT("%@ Unable to SetDSTOffset on endpoint %@, since it does not support the TZ feature", self, endpoint); + MTR_LOG("%@ Unable to SetDSTOffset on endpoint %@, since it does not support the TZ feature", self, endpoint); continue; } auto attrReport = [[MTRAttributeReport alloc] initWithResponseValue:@{ @@ -690,13 +717,24 @@ - (void)_setDSTOffsets:(NSArray #define MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MIN (10 * 60) // 10 minutes (for now) #define MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MAX (60 * 60) // 60 minutes +- (BOOL)_subscriptionsAllowed +{ + os_unfair_lock_assert_owner(&self->_lock); + + // We should not allow a subscription for device controllers over XPC. + return ![_deviceController isKindOfClass:MTRDeviceControllerOverXPC.class]; +} + - (void)setDelegate:(id)delegate queue:(dispatch_queue_t)queue { - MTR_LOG_INFO("%@ setDelegate %@", self, delegate); + MTR_LOG("%@ setDelegate %@", self, delegate); - BOOL setUpSubscription = YES; + std::lock_guard lock(_lock); + + BOOL setUpSubscription = [self _subscriptionsAllowed]; - // For unit testing only + // For unit testing only. If this ever changes to not being for unit testing purposes, + // we would need to move the code outside of where we acquire the lock above. #ifdef DEBUG id testDelegate = delegate; if ([testDelegate respondsToSelector:@selector(unitTestShouldSetUpSubscriptionForDevice:)]) { @@ -704,16 +742,9 @@ - (void)setDelegate:(id)delegate queue:(dispatch_queue_t)queu } #endif - std::lock_guard lock(_lock); - _weakDelegate = [MTRWeakReference weakReferenceWithObject:delegate]; _delegateQueue = queue; - // If Check if cache is already primed and client hasn't been informed yet, call the -deviceCachePrimed: callback - if (!_delegateDeviceCachePrimedCalled && [self _isCachePrimedWithInitialConfigurationData]) { - [self _callDelegateDeviceCachePrimed]; - } - if (setUpSubscription) { _initialSubscribeStart = [NSDate now]; if ([self _deviceUsesThread]) { @@ -729,7 +760,7 @@ - (void)setDelegate:(id)delegate queue:(dispatch_queue_t)queu - (void)invalidate { - MTR_LOG_INFO("%@ invalidate", self); + MTR_LOG("%@ invalidate", self); [_asyncWorkQueue invalidate]; @@ -760,7 +791,7 @@ - (void)nodeMayBeAdvertisingOperational { assertChipStackLockedByCurrentThread(); - MTR_LOG_DEFAULT("%@ saw new operational advertisement", self); + MTR_LOG("%@ saw new operational advertisement", self); [self _triggerResubscribeWithReason:"operational advertisement seen" nodeLikelyReachable:YES]; @@ -828,13 +859,8 @@ - (BOOL)_subscriptionAbleToReport } #endif - // Unfortunately, we currently have no subscriptions over our hacked-up XPC - // setup. Try to detect that situation. - if ([_deviceController isKindOfClass:MTRDeviceControllerOverXPC.class]) { - return NO; - } - - return YES; + // Subscriptions are not able to report if they are not allowed. + return [self _subscriptionsAllowed]; } // Notification that read-through was skipped for an attribute read. @@ -893,7 +919,7 @@ - (BOOL)_callDelegateWithBlock:(void (^)(id))block - (void)_callDelegateDeviceCachePrimed { os_unfair_lock_assert_owner(&self->_lock); - _delegateDeviceCachePrimedCalled = [self _callDelegateWithBlock:^(id delegate) { + [self _callDelegateWithBlock:^(id delegate) { if ([delegate respondsToSelector:@selector(deviceCachePrimed:)]) { [delegate deviceCachePrimed:self]; } @@ -908,12 +934,12 @@ - (void)_changeState:(MTRDeviceState)state _state = state; if (lastState != state) { if (state != MTRDeviceStateReachable) { - MTR_LOG_INFO("%@ reachability state change %lu => %lu, set estimated start time to nil", self, static_cast(lastState), + MTR_LOG("%@ reachability state change %lu => %lu, set estimated start time to nil", self, static_cast(lastState), static_cast(state)); _estimatedStartTime = nil; _estimatedStartTimeFromGeneralDiagnosticsUpTime = nil; } else { - MTR_LOG_INFO( + MTR_LOG( "%@ reachability state change %lu => %lu", self, static_cast(lastState), static_cast(state)); } id delegate = _weakDelegate.strongObject; @@ -923,7 +949,7 @@ - (void)_changeState:(MTRDeviceState)state }); } } else { - MTR_LOG_INFO( + MTR_LOG( "%@ Not reporting reachability state change, since no change in state %lu => %lu", self, static_cast(lastState), static_cast(state)); } } @@ -934,10 +960,27 @@ - (void)_changeInternalState:(MTRInternalDeviceState)state MTRInternalDeviceState lastState = _internalDeviceState; _internalDeviceState = state; if (lastState != state) { - MTR_LOG_DEFAULT("%@ internal state change %lu => %lu", self, static_cast(lastState), static_cast(state)); + MTR_LOG("%@ internal state change %lu => %lu", self, static_cast(lastState), static_cast(state)); + + /* BEGIN DRAGONS: This is a huge hack for a specific use case, do not rename, remove or modify behavior here */ + id delegate = _weakDelegate.strongObject; + if ([delegate respondsToSelector:@selector(_deviceInternalStateChanged:)]) { + dispatch_async(_delegateQueue, ^{ + [(id) delegate _deviceInternalStateChanged:self]; + }); + } + /* END DRAGONS */ } } +#ifdef DEBUG +- (MTRInternalDeviceState)_getInternalState +{ + std::lock_guard lock(self->_lock); + return _internalDeviceState; +} +#endif + // First Time Sync happens 2 minutes after reachability (this can be changed in the future) #define MTR_DEVICE_TIME_UPDATE_INITIAL_WAIT_TIME_SEC (60 * 2) - (void)_handleSubscriptionEstablished @@ -955,11 +998,6 @@ - (void)_handleSubscriptionEstablished [self _changeInternalState:MTRInternalDeviceStateInitialSubscriptionEstablished]; } - // As subscription is established, check if the delegate needs to be informed - if (!_delegateDeviceCachePrimedCalled) { - [self _callDelegateDeviceCachePrimed]; - } - [self _changeState:MTRDeviceStateReachable]; // No need to monitor connectivity after subscription establishment @@ -1082,7 +1120,7 @@ - (void)_scheduleSubscriptionPoolWork:(dispatch_block_t)workBlock inNanoseconds: #endif if (self->_subscriptionPoolWorkCompletionBlock) { // This means a resubscription triggering event happened and is now in-progress - MTR_LOG_DEFAULT("%@ timer fired but already running in subscription pool - ignoring: %@", self, description); + MTR_LOG("%@ timer fired but already running in subscription pool - ignoring: %@", self, description); os_unfair_lock_unlock(&self->_lock); // call completion as complete to remove from queue @@ -1130,7 +1168,7 @@ - (void)_handleResubscriptionNeededWithDelay:(NSNumber *)resubscriptionDelayMs [self _triggerResubscribeWithReason:"ResubscriptionNeeded timer fired" nodeLikelyReachable:NO]; } errorHandler:^(NSError * _Nonnull error) { // If controller is not running, clear work item from the subscription queue - MTR_LOG_INFO("%@ could not dispatch to matter queue for resubscription - error %@", self, error); + MTR_LOG_ERROR("%@ could not dispatch to matter queue for resubscription - error %@", self, error); std::lock_guard lock(self->_lock); [self _clearSubscriptionPoolWork]; }]; @@ -1170,7 +1208,7 @@ - (void)_handleSubscriptionReset:(NSNumber * _Nullable)retryDelay // don't schedule multiple retries if (self.reattemptingSubscription) { - MTR_LOG_DEFAULT("%@ already reattempting subscription", self); + MTR_LOG("%@ already reattempting subscription", self); return; } @@ -1187,7 +1225,7 @@ - (void)_handleSubscriptionReset:(NSNumber * _Nullable)retryDelay // the device told us to use. _lastSubscriptionAttemptWait = 0; secondsToWait = retryDelay.doubleValue; - MTR_LOG_INFO("%@ resetting resubscribe attempt counter, and delaying by the server-provided delay: %f", + MTR_LOG("%@ resetting resubscribe attempt counter, and delaying by the server-provided delay: %f", self, secondsToWait); } else { _lastSubscriptionAttemptWait *= 2; @@ -1197,7 +1235,7 @@ - (void)_handleSubscriptionReset:(NSNumber * _Nullable)retryDelay secondsToWait = _lastSubscriptionAttemptWait; } - MTR_LOG_DEFAULT("%@ scheduling to reattempt subscription in %f seconds", self, secondsToWait); + MTR_LOG("%@ scheduling to reattempt subscription in %f seconds", self, secondsToWait); // If we started subscription or session establishment but failed, remove item from the subscription pool so we can re-queue. [self _clearSubscriptionPoolWork]; @@ -1227,7 +1265,7 @@ - (void)_reattemptSubscriptionNowIfNeeded return; } - MTR_LOG_DEFAULT("%@ reattempting subscription", self); + MTR_LOG("%@ reattempting subscription", self); self.reattemptingSubscription = NO; [self _setupSubscription]; } @@ -1263,7 +1301,7 @@ - (void)_markDeviceAsUnreachableIfNeverSubscribed return; } - MTR_LOG_DEFAULT("%@ still not subscribed, marking the device as unreachable", self); + MTR_LOG("%@ still not subscribed, marking the device as unreachable", self); [self _changeState:MTRDeviceStateUnreachable]; } @@ -1292,6 +1330,261 @@ - (void)_handleReportBegin return clusterDataToReturn; } +- (NSTimeInterval)_reportToPersistenceDelayTimeAfterMutiplier +{ + return _storageBehaviorConfiguration.reportToPersistenceDelayTime * _reportToPersistenceDelayCurrentMultiplier; +} + +- (NSTimeInterval)_reportToPersistenceDelayTimeMaxAfterMutiplier +{ + return _storageBehaviorConfiguration.reportToPersistenceDelayTimeMax * _reportToPersistenceDelayCurrentMultiplier; +} + +- (BOOL)_dataStoreExists +{ + os_unfair_lock_assert_owner(&self->_lock); + return _persistedClusterData != nil; +} + +- (void)_persistClusterData +{ + os_unfair_lock_assert_owner(&self->_lock); + + // Nothing to persist + if (!_clusterDataToPersist.count) { + return; + } + + MTR_LOG("%@ Storing cluster information (data version and attributes) count: %lu", self, static_cast(_clusterDataToPersist.count)); + // We're going to hand out these MTRDeviceClusterData objects to our + // storage implementation, which will try to read them later. Make sure + // we snapshot the state here instead of handing out live copies. + NSDictionary * clusterData = [self _clusterDataToPersistSnapshot]; + [_deviceController.controllerDataStore storeClusterData:clusterData forNodeID:_nodeID]; + for (MTRClusterPath * clusterPath in _clusterDataToPersist) { + [_persistedClusterData setObject:_clusterDataToPersist[clusterPath] forKey:clusterPath]; + [_persistedClusters addObject:clusterPath]; + } + + // TODO: There is one edge case not handled well here: if the + // storeClusterData call above fails somehow, and then the data gets + // evicted from _persistedClusterData, we could end up in a situation + // where when we page things in from storage we have stale values and + // hence effectively lose the delta that we failed to persist. + // + // The only way to handle this would be to detect it when it happens, + // then re-subscribe at that point, which would cause the relevant data + // to be sent to us via the priming read. + _clusterDataToPersist = nil; + +#ifdef DEBUG + id delegate = _weakDelegate.strongObject; + if (delegate) { + dispatch_async(_delegateQueue, ^{ + if ([delegate respondsToSelector:@selector(unitTestClusterDataPersisted:)]) { + [delegate unitTestClusterDataPersisted:self]; + } + }); + } +#endif +} + +- (BOOL)_deviceIsReportingExcessively +{ + os_unfair_lock_assert_owner(&self->_lock); + + if (!_deviceReportingExcessivelyStartTime) { + return NO; + } + + NSTimeInterval intervalSinceDeviceReportingExcessively = -[_deviceReportingExcessivelyStartTime timeIntervalSinceNow]; + BOOL deviceIsReportingExcessively = intervalSinceDeviceReportingExcessively > _storageBehaviorConfiguration.deviceReportingExcessivelyIntervalThreshold; + if (deviceIsReportingExcessively) { + MTR_LOG("%@ storage behavior: device has been reporting excessively for %.3lf seconds", self, intervalSinceDeviceReportingExcessively); + } + return deviceIsReportingExcessively; +} + +- (void)_persistClusterDataAsNeeded +{ + std::lock_guard lock(_lock); + + // Nothing to persist + if (!_clusterDataToPersist.count) { + return; + } + + // This is run with a dispatch_after, and need to check again if this device is reporting excessively + if ([self _deviceIsReportingExcessively]) { + return; + } + + NSDate * lastReportTime = [_mostRecentReportTimes lastObject]; + NSTimeInterval intervalSinceLastReport = -[lastReportTime timeIntervalSinceNow]; + if (intervalSinceLastReport < [self _reportToPersistenceDelayTimeAfterMutiplier]) { + // A report came in after this call was scheduled + + if (!_clusterDataPersistenceFirstScheduledTime) { + MTR_LOG_ERROR("%@ storage behavior: expects _clusterDataPersistenceFirstScheduledTime if _clusterDataToPersist exists", self); + return; + } + + NSTimeInterval intervalSinceFirstScheduledPersistence = -[_clusterDataPersistenceFirstScheduledTime timeIntervalSinceNow]; + if (intervalSinceFirstScheduledPersistence < [self _reportToPersistenceDelayTimeMaxAfterMutiplier]) { + MTR_LOG("%@ storage behavior: not persisting: intervalSinceLastReport %lf intervalSinceFirstScheduledPersistence %lf", self, intervalSinceLastReport, intervalSinceFirstScheduledPersistence); + // The max delay is also not reached - do not persist yet + return; + } + } + + // At this point, there is data to persist, and either _reportToPersistenceDelayTime was + // reached, or _reportToPersistenceDelayTimeMax was reached. Time to persist: + [self _persistClusterData]; + + _clusterDataPersistenceFirstScheduledTime = nil; +} + +#ifdef DEBUG +- (void)unitTestSetMostRecentReportTimes:(NSMutableArray *)mostRecentReportTimes +{ + _mostRecentReportTimes = mostRecentReportTimes; +} +#endif + +- (void)_scheduleClusterDataPersistence +{ + os_unfair_lock_assert_owner(&self->_lock); + + // No persisted data / lack of controller data store + if (![self _dataStoreExists]) { + MTR_LOG_DEBUG("%@ storage behavior: no data store", self); + return; + } + + // Nothing to persist + if (!_clusterDataToPersist.count) { + MTR_LOG_DEBUG("%@ storage behavior: nothing to persist", self); + return; + } + + // If there is no storage behavior configuration, make a default one + if (!_storageBehaviorConfiguration) { + _storageBehaviorConfiguration = [[MTRDeviceStorageBehaviorConfiguration alloc] init]; + [_storageBehaviorConfiguration checkValuesAndResetToDefaultIfNecessary]; + } + + // Directly store if the storage behavior optimization is disabled + if (_storageBehaviorConfiguration.disableStorageBehaviorOptimization) { + [self _persistClusterData]; + return; + } + + // If we have nothing stored at all yet, store directly, so we move into a + // primed state. + if (!_deviceCachePrimed) { + [self _persistClusterData]; + return; + } + + // Ensure there is an array to keep the most recent report times + if (!_mostRecentReportTimes) { + _mostRecentReportTimes = [NSMutableArray array]; + } + + // Mark when first report comes in to know when _reportToPersistenceDelayTimeMax is hit + if (!_clusterDataPersistenceFirstScheduledTime) { + _clusterDataPersistenceFirstScheduledTime = [NSDate now]; + } + + // Make sure there is space in the array, and note report time + while (_mostRecentReportTimes.count >= _storageBehaviorConfiguration.recentReportTimesMaxCount) { + [_mostRecentReportTimes removeObjectAtIndex:0]; + } + [_mostRecentReportTimes addObject:[NSDate now]]; + + // Calculate running average and update multiplier - need at least 2 items to calculate intervals + if (_mostRecentReportTimes.count > 2) { + NSTimeInterval cumulativeIntervals = 0; + for (int i = 1; i < _mostRecentReportTimes.count; i++) { + NSDate * lastDate = [_mostRecentReportTimes objectAtIndex:i - 1]; + NSDate * currentDate = [_mostRecentReportTimes objectAtIndex:i]; + NSTimeInterval intervalSinceLastReport = [currentDate timeIntervalSinceDate:lastDate]; + // Check to guard against clock change + if (intervalSinceLastReport > 0) { + cumulativeIntervals += intervalSinceLastReport; + } + } + NSTimeInterval averageTimeBetweenReports = cumulativeIntervals / (_mostRecentReportTimes.count - 1); + + if (averageTimeBetweenReports < _storageBehaviorConfiguration.timeBetweenReportsTooShortThreshold) { + // Multiplier goes from 1 to _reportToPersistenceDelayMaxMultiplier uniformly, as + // averageTimeBetweenReports go from timeBetweenReportsTooShortThreshold to + // timeBetweenReportsTooShortMinThreshold + + double intervalAmountBelowThreshold = _storageBehaviorConfiguration.timeBetweenReportsTooShortThreshold - averageTimeBetweenReports; + double intervalAmountBetweenThresholdAndMinThreshold = _storageBehaviorConfiguration.timeBetweenReportsTooShortThreshold - _storageBehaviorConfiguration.timeBetweenReportsTooShortMinThreshold; + double proportionTowardMinThreshold = intervalAmountBelowThreshold / intervalAmountBetweenThresholdAndMinThreshold; + if (proportionTowardMinThreshold > 1) { + // Clamp to 100% + proportionTowardMinThreshold = 1; + } + + // Set current multiplier to [1, MaxMultiplier] + _reportToPersistenceDelayCurrentMultiplier = 1 + (proportionTowardMinThreshold * (_storageBehaviorConfiguration.reportToPersistenceDelayMaxMultiplier - 1)); + MTR_LOG("%@ storage behavior: device reporting frequently - setting delay multiplier to %lf", self, _reportToPersistenceDelayCurrentMultiplier); + } else { + _reportToPersistenceDelayCurrentMultiplier = 1; + } + + // Also note when the running average first dips below the min threshold + if (averageTimeBetweenReports < _storageBehaviorConfiguration.timeBetweenReportsTooShortMinThreshold) { + if (!_deviceReportingExcessivelyStartTime) { + _deviceReportingExcessivelyStartTime = [NSDate now]; + MTR_LOG_DEBUG("%@ storage behavior: device is reporting excessively @%@", self, _deviceReportingExcessivelyStartTime); + } + } else { + _deviceReportingExcessivelyStartTime = nil; + } + } + + // Do not schedule persistence if device is reporting excessively + if ([self _deviceIsReportingExcessively]) { + return; + } + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) ([self _reportToPersistenceDelayTimeAfterMutiplier] * NSEC_PER_SEC)), self.queue, ^{ + [self _persistClusterDataAsNeeded]; + }); +} + +// Used to clear the storage behavior state when needed (system time change, or when new +// configuration is set. +// +// Also flushes unwritten cluster data to storage, if data store exists. +- (void)_resetStorageBehaviorState +{ + os_unfair_lock_assert_owner(&self->_lock); + + _clusterDataPersistenceFirstScheduledTime = nil; + _mostRecentReportTimes = nil; + _deviceReportingExcessivelyStartTime = nil; + _reportToPersistenceDelayCurrentMultiplier = 1; + + if (_persistedClusters) { + [self _persistClusterData]; + } +} + +- (void)setStorageBehaviorConfiguration:(MTRDeviceStorageBehaviorConfiguration *)storageBehaviorConfiguration +{ + MTR_LOG("%@ storage behavior: setStorageBehaviorConfiguration %@", self, storageBehaviorConfiguration); + std::lock_guard lock(_lock); + _storageBehaviorConfiguration = storageBehaviorConfiguration; + // Make sure the values are sane + [_storageBehaviorConfiguration checkValuesAndResetToDefaultIfNecessary]; + [self _resetStorageBehaviorState]; +} + - (void)_handleReportEnd { std::lock_guard lock(_lock); @@ -1299,30 +1592,7 @@ - (void)_handleReportEnd _receivingPrimingReport = NO; _estimatedStartTimeFromGeneralDiagnosticsUpTime = nil; - BOOL dataStoreExists = _deviceController.controllerDataStore != nil; - if (dataStoreExists && _clusterDataToPersist != nil && _clusterDataToPersist.count) { - MTR_LOG_DEFAULT("%@ Storing cluster information (data version and attributes) count: %lu", self, static_cast(_clusterDataToPersist.count)); - // We're going to hand out these MTRDeviceClusterData objects to our - // storage implementation, which will try to read them later. Make sure - // we snapshot the state here instead of handing out live copies. - NSDictionary * clusterData = [self _clusterDataToPersistSnapshot]; - [_deviceController.controllerDataStore storeClusterData:clusterData forNodeID:_nodeID]; - for (MTRClusterPath * clusterPath in _clusterDataToPersist) { - [_persistedClusterData setObject:_clusterDataToPersist[clusterPath] forKey:clusterPath]; - [_persistedClusters addObject:clusterPath]; - } - - // TODO: There is one edge case not handled well here: if the - // storeClusterData call above fails somehow, and then the data gets - // evicted from _persistedClusterData, we could end up in a situation - // where when we page things in from storage we have stale values and - // hence effectively lose the delta that we failed to persist. - // - // The only way to handle this would be to detect it when it happens, - // then re-subscribe at that point, which would cause the relevant data - // to be sent to us via the priming read. - _clusterDataToPersist = nil; - } + [self _scheduleClusterDataPersistence]; // After the handling of the report, if we detected a device configuration change, notify the delegate // of the same. @@ -1337,6 +1607,19 @@ - (void)_handleReportEnd _deviceConfigurationChanged = NO; } + // Do this after the _deviceConfigurationChanged check, so that we don't + // call deviceConfigurationChanged: immediately after telling our delegate + // we are now primed. + // + // TODO: Maybe we shouldn't dispatch deviceConfigurationChanged: for the + // initial priming bits? + if (!_deviceCachePrimed) { + // This is the end of the priming sequence of data reports, so we have + // all the data for the device now. + _deviceCachePrimed = YES; + [self _callDelegateDeviceCachePrimed]; + } + // For unit testing only #ifdef DEBUG id delegate = _weakDelegate.strongObject; @@ -1433,12 +1716,12 @@ - (void)_handleEventReport:(NSArray *> *)eventRepor if (isStartUpEvent) { if (_estimatedStartTimeFromGeneralDiagnosticsUpTime) { // If UpTime was received, make use of it as mark of system start time - MTR_LOG_INFO("%@ StartUp event: set estimated start time forward to %@", self, + MTR_LOG("%@ StartUp event: set estimated start time forward to %@", self, _estimatedStartTimeFromGeneralDiagnosticsUpTime); _estimatedStartTime = _estimatedStartTimeFromGeneralDiagnosticsUpTime; } else { // If UpTime was not received, reset estimated start time in case of reboot - MTR_LOG_INFO("%@ StartUp event: set estimated start time to nil", self); + MTR_LOG("%@ StartUp event: set estimated start time to nil", self); _estimatedStartTime = nil; } } @@ -1473,7 +1756,7 @@ - (void)_handleEventReport:(NSArray *> *)eventRepor [reportToReturn addObject:eventToReturn]; } if (oldEstimatedStartTime != _estimatedStartTime) { - MTR_LOG_DEFAULT("%@ updated estimated start time to %@", self, _estimatedStartTime); + MTR_LOG("%@ updated estimated start time to %@", self, _estimatedStartTime); } id delegate = _weakDelegate.strongObject; @@ -1492,7 +1775,7 @@ - (void)_handleEventReport:(NSArray *> *)eventRepor - (void)unitTestClearClusterData { std::lock_guard lock(_lock); - NSAssert(_persistedClusterData != nil, @"Test is not going to test what it thinks is testing!"); + NSAssert([self _dataStoreExists], @"Test is not going to test what it thinks is testing!"); [_persistedClusterData removeAllObjects]; } #endif @@ -1509,7 +1792,7 @@ - (nullable MTRDeviceClusterData *)_clusterDataForPath:(MTRClusterPath *)cluster } } - if (_persistedClusterData != nil) { + if ([self _dataStoreExists]) { MTRDeviceClusterData * data = [_persistedClusterData objectForKey:clusterPath]; if (data != nil) { return data; @@ -1559,7 +1842,7 @@ - (nullable MTRDeviceClusterData *)_clusterDataForPath:(MTRClusterPath *)cluster dataVersions[path] = [self _clusterDataForPath:path].dataVersion; } - MTR_LOG_INFO("%@ _getCachedDataVersions dataVersions count: %lu", self, static_cast(dataVersions.count)); + MTR_LOG_DEBUG("%@ _getCachedDataVersions dataVersions count: %lu", self, static_cast(dataVersions.count)); return dataVersions; } @@ -1661,7 +1944,7 @@ - (void)_setupConnectivityMonitoring // Get the required info before setting up the connectivity monitor NSNumber * compressedFabricID = [self->_deviceController syncGetCompressedFabricID]; if (!compressedFabricID) { - MTR_LOG_INFO("%@ could not get compressed fabricID", self); + MTR_LOG_ERROR("%@ could not get compressed fabricID", self); return; } @@ -1697,6 +1980,11 @@ - (void)_setupSubscription { os_unfair_lock_assert_owner(&self->_lock); + if (![self _subscriptionsAllowed]) { + MTR_LOG("_setupSubscription: Subscriptions not allowed. Do not set up subscription"); + return; + } + #ifdef DEBUG id delegate = _weakDelegate.strongObject; Optional maxIntervalOverride; @@ -1741,7 +2029,7 @@ - (void)_setupSubscription auto callback = std::make_unique( ^(NSArray * value) { - MTR_LOG_INFO("%@ got attribute report %@", self, value); + MTR_LOG("%@ got attribute report %@", self, value); dispatch_async(self.queue, ^{ // OnAttributeData [self _handleAttributeReport:value fromSubscription:YES]; @@ -1751,7 +2039,7 @@ - (void)_setupSubscription }); }, ^(NSArray * value) { - MTR_LOG_INFO("%@ got event report %@", self, value); + MTR_LOG("%@ got event report %@", self, value); dispatch_async(self.queue, ^{ // OnEventReport [self _handleEventReport:value]; @@ -1765,21 +2053,21 @@ - (void)_setupSubscription }); }, ^(NSError * error, NSNumber * resubscriptionDelayMs) { - MTR_LOG_DEFAULT("%@ got resubscription error %@ delay %@", self, error, resubscriptionDelayMs); + MTR_LOG_ERROR("%@ got resubscription error %@ delay %@", self, error, resubscriptionDelayMs); dispatch_async(self.queue, ^{ // OnResubscriptionNeeded [self _handleResubscriptionNeededWithDelay:resubscriptionDelayMs]; }); }, ^(void) { - MTR_LOG_DEFAULT("%@ got subscription established", self); + MTR_LOG("%@ got subscription established", self); dispatch_async(self.queue, ^{ // OnSubscriptionEstablished [self _handleSubscriptionEstablished]; }); }, ^(void) { - MTR_LOG_DEFAULT("%@ got subscription done", self); + MTR_LOG("%@ got subscription done", self); // Drop our pointer to the ReadClient immediately, since // it's about to be destroyed and we don't want to be // holding a dangling pointer. @@ -1793,20 +2081,20 @@ - (void)_setupSubscription }); }, ^(void) { - MTR_LOG_DEFAULT("%@ got unsolicited message from publisher", self); + MTR_LOG("%@ got unsolicited message from publisher", self); dispatch_async(self.queue, ^{ // OnUnsolicitedMessageFromPublisher [self _handleUnsolicitedMessageFromPublisher]; }); }, ^(void) { - MTR_LOG_DEFAULT("%@ got report begin", self); + MTR_LOG("%@ got report begin", self); dispatch_async(self.queue, ^{ [self _handleReportBegin]; }); }, ^(void) { - MTR_LOG_DEFAULT("%@ got report end", self); + MTR_LOG("%@ got report end", self); dispatch_async(self.queue, ^{ [self _handleReportEnd]; }); @@ -1901,7 +2189,7 @@ - (void)_setupSubscription return; } - MTR_LOG_DEFAULT("%@ Subscribe with data version list size %lu, reduced by %lu", self, (unsigned long) dataVersions.count, (unsigned long) dataVersionFilterListSizeReduction); + MTR_LOG("%@ Subscribe with data version list size %lu, reduced by %lu", self, static_cast(dataVersions.count), static_cast(dataVersionFilterListSizeReduction)); // Callback and ClusterStateCache and ReadClient will be deleted // when OnDone is called. @@ -2116,14 +2404,14 @@ static BOOL AttributeHasChangesOmittedQuality(MTRAttributePath * attributePath) while (readRequestsNext.count) { // Can only read up to 9 paths at a time, per spec if (readRequestsCurrent.count >= 9) { - MTR_LOG_INFO("Batching read attribute work item [%llu]: cannot add more work, item is full [0x%016llX:%@:0x%llx:0x%llx]", workItemID, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); + MTR_LOG("Batching read attribute work item [%llu]: cannot add more work, item is full [0x%016llX:%@:0x%llx:0x%llx]", workItemID, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); return outcome; } // if params don't match then they cannot be merged if (![readRequestsNext[0][MTRDeviceReadRequestFieldParamsIndex] isEqual:readRequestsCurrent[0][MTRDeviceReadRequestFieldParamsIndex]]) { - MTR_LOG_INFO("Batching read attribute work item [%llu]: cannot add more work, parameter mismatch [0x%016llX:%@:0x%llx:0x%llx]", workItemID, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); + MTR_LOG("Batching read attribute work item [%llu]: cannot add more work, parameter mismatch [0x%016llX:%@:0x%llx:0x%llx]", workItemID, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); return outcome; } @@ -2131,8 +2419,8 @@ static BOOL AttributeHasChangesOmittedQuality(MTRAttributePath * attributePath) auto readItem = readRequestsNext.firstObject; [readRequestsNext removeObjectAtIndex:0]; [readRequestsCurrent addObject:readItem]; - MTR_LOG_INFO("Batching read attribute work item [%llu]: added %@ (now %tu requests total) [0x%016llX:%@:0x%llx:0x%llx]", - workItemID, readItem, readRequestsCurrent.count, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); + MTR_LOG("Batching read attribute work item [%llu]: added %@ (now %lu requests total) [0x%016llX:%@:0x%llx:0x%llx]", + workItemID, readItem, static_cast(readRequestsCurrent.count), nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); outcome = MTRBatchedPartially; } NSCAssert(readRequestsNext.count == 0, @"should have batched everything or returned early"); @@ -2142,7 +2430,7 @@ static BOOL AttributeHasChangesOmittedQuality(MTRAttributePath * attributePath) mtr_hide(self); // don't capture self accidentally for (NSArray * readItem in readRequests) { if ([readItem isEqual:opaqueItemData]) { - MTR_LOG_DEFAULT("Read attribute work item [%llu] report duplicate %@ [0x%016llX:%@:0x%llx:0x%llx]", workItemID, readItem, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); + MTR_LOG("Read attribute work item [%llu] report duplicate %@ [0x%016llX:%@:0x%llx:0x%llx]", workItemID, readItem, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); *isDuplicate = YES; *stop = YES; return; @@ -2179,7 +2467,7 @@ static BOOL AttributeHasChangesOmittedQuality(MTRAttributePath * attributePath) if (values) { // Since the format is the same data-value dictionary, this looks like an // attribute report - MTR_LOG_INFO("Read attribute work item [%llu] result: %@ [0x%016llX:%@:0x%llX:0x%llX]", workItemID, values, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); + MTR_LOG("Read attribute work item [%llu] result: %@ [0x%016llX:%@:0x%llX:0x%llX]", workItemID, values, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); [self _handleAttributeReport:values fromSubscription:NO]; } @@ -2189,7 +2477,7 @@ static BOOL AttributeHasChangesOmittedQuality(MTRAttributePath * attributePath) completion(MTRAsyncWorkNeedsRetry); } else { if (error) { - MTR_LOG_DEFAULT("Read attribute work item [%llu] failed (giving up): %@ [0x%016llX:%@:0x%llx:0x%llx]", workItemID, error, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); + MTR_LOG("Read attribute work item [%llu] failed (giving up): %@ [0x%016llX:%@:0x%llx:0x%llx]", workItemID, error, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); } completion(MTRAsyncWorkComplete); } @@ -2259,7 +2547,7 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID if (writeRequestsCurrent.count != 1) { // Very unexpected! - MTR_LOG_ERROR("Batching write attribute work item [%llu]: Unexpected write request count %tu", workItemID, writeRequestsCurrent.count); + MTR_LOG_ERROR("Batching write attribute work item [%llu]: Unexpected write request count %lu", workItemID, static_cast(writeRequestsCurrent.count)); return MTRNotBatched; } @@ -2269,7 +2557,7 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID // with the later one. if (![writeRequestsNext[0][MTRDeviceWriteRequestFieldPathIndex] isEqual:writeRequestsCurrent[0][MTRDeviceWriteRequestFieldPathIndex]]) { - MTR_LOG_INFO("Batching write attribute work item [%llu]: cannot replace with next work item due to path mismatch", workItemID); + MTR_LOG("Batching write attribute work item [%llu]: cannot replace with next work item due to path mismatch", workItemID); return outcome; } @@ -2277,7 +2565,7 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID auto writeItem = writeRequestsNext.firstObject; [writeRequestsNext removeObjectAtIndex:0]; [writeRequestsCurrent replaceObjectAtIndex:0 withObject:writeItem]; - MTR_LOG_INFO("Batching write attribute work item [%llu]: replaced with new write value %@ [0x%016llX]", + MTR_LOG("Batching write attribute work item [%llu]: replaced with new write value %@ [0x%016llX]", workItemID, writeItem, nodeID.unsignedLongLongValue); outcome = MTRBatchedPartially; } @@ -2294,7 +2582,7 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID MTRBaseDevice * baseDevice = [self newBaseDevice]; // Make sure to use writeRequests here, because that's what our batching // handler will modify as needed. - NSCAssert(writeRequests.count == 1, @"Incorrect number of write requests: %tu", writeRequests.count); + NSCAssert(writeRequests.count == 1, @"Incorrect number of write requests: %lu", static_cast(writeRequests.count)); auto * request = writeRequests[0]; MTRAttributePath * path = request[MTRDeviceWriteRequestFieldPathIndex]; @@ -2461,7 +2749,7 @@ - (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { // Log the data at the INFO level (not usually persisted permanently), // but make sure we log the work completion at the DEFAULT level. - MTR_LOG_INFO("Invoke work item [%llu] received command response: %@ error: %@", workItemID, values, error); + MTR_LOG("Invoke work item [%llu] received command response: %@ error: %@", workItemID, values, error); // TODO: This 5-retry cap is very arbitrary. // TODO: Should there be some sort of backoff here? if (error != nil && error.domain == MTRInteractionErrorDomain && error.code == MTRInteractionErrorCodeBusy && retryCount < 5) { @@ -2608,7 +2896,7 @@ - (void)_checkExpiredExpectedValues } // log attribute paths - MTR_LOG_INFO("%@ report from expired expected values %@", self, attributePathsToReport); + MTR_LOG("%@ report from expired expected values %@", self, attributePathsToReport); [self _reportAttributes:attributesToReport]; // Have a reasonable minimum wait time for expiration timers @@ -2659,7 +2947,7 @@ - (void)_performScheduledExpirationCheck return cachedAttributeValue; } else { // TODO: when not found in cache, generated default values should be used - MTR_LOG_INFO("%@ _attributeValueDictionaryForAttributePath: could not find cached attribute values for attribute %@", self, + MTR_LOG("%@ _attributeValueDictionaryForAttributePath: could not find cached attribute values for attribute %@", self, attributePath); } @@ -2876,7 +3164,7 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray %@", self, upTime, + MTR_LOG("%@ General Diagnostics UpTime %.3lf: estimated start time %@ => %@", self, upTime, oldSystemStartTime, potentialSystemStartTime); _estimatedStartTime = potentialSystemStartTime; } @@ -2990,7 +3278,7 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray *)clusterData { - MTR_LOG_INFO("%@ setPersistedClusterData count: %lu", self, static_cast(clusterData.count)); + MTR_LOG("%@ setPersistedClusterData count: %lu", self, static_cast(clusterData.count)); if (!clusterData.count) { return; } std::lock_guard lock(_lock); - NSAssert(_persistedClusterData != nil, @"Why is controller setting persisted data when we shouldn't have it?"); + NSAssert([self _dataStoreExists], @"Why is controller setting persisted data when we shouldn't have it?"); for (MTRClusterPath * clusterPath in clusterData) { // The caller has mutable references to MTRDeviceClusterData and @@ -3027,10 +3315,9 @@ - (void)setPersistedClusterData:(NSDictionary *)data { - MTR_LOG_INFO("%@ setPersistedDeviceData: %@", self, data); + MTR_LOG_DEBUG("%@ setPersistedDeviceData: %@", self, data); std::lock_guard lock(_lock); @@ -3099,7 +3386,7 @@ - (BOOL)_clusterHasBeenPersisted:(MTRClusterPath *)path - (BOOL)deviceCachePrimed { std::lock_guard lock(_lock); - return [self _isCachePrimedWithInitialConfigurationData]; + return _deviceCachePrimed; } // If value is non-nil, associate with expectedValueID @@ -3199,7 +3486,7 @@ - (NSArray *)_getAttributesToReportWithNewExpectedValues:(NSArray *> *)values // since NSTimeInterval is in seconds, convert ms into seconds in double NSDate * expirationTime = [NSDate dateWithTimeIntervalSinceNow:expectedValueInterval.doubleValue / 1000]; - MTR_LOG_INFO( + MTR_LOG( "%@ Setting expected values %@ with expiration time %f seconds from now", self, values, [expirationTime timeIntervalSinceNow]); std::lock_guard lock(_lock); @@ -3262,7 +3549,7 @@ - (void)_removeExpectedValueForAttributePath:(MTRAttributePath *)attributePath e expectedValueID:expectedValueID previousValue:&previousValue]; - MTR_LOG_INFO("%@ remove expected value for path %@ should report %@", self, attributePath, shouldReportValue ? @"YES" : @"NO"); + MTR_LOG("%@ remove expected value for path %@ should report %@", self, attributePath, shouldReportValue ? @"YES" : @"NO"); if (shouldReportValue) { NSMutableDictionary * attribute = [NSMutableDictionary dictionaryWithObject:attributePath forKey:MTRAttributePathKey]; @@ -3276,47 +3563,6 @@ - (void)_removeExpectedValueForAttributePath:(MTRAttributePath *)attributePath e } } -// This method checks if there is a need to inform delegate that the attribute cache has been "primed" -- (BOOL)_isCachePrimedWithInitialConfigurationData -{ - os_unfair_lock_assert_owner(&self->_lock); - - // Check if root node descriptor exists - MTRDeviceDataValueDictionary rootDescriptorPartsListDataValue = [self _cachedAttributeValueForPath:[MTRAttributePath attributePathWithEndpointID:@(kRootEndpointId) clusterID:@(MTRClusterIDTypeDescriptorID) attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributePartsListID)]]; - if (!rootDescriptorPartsListDataValue || ![MTRArrayValueType isEqualToString:rootDescriptorPartsListDataValue[MTRTypeKey]]) { - return NO; - } - NSArray * partsList = rootDescriptorPartsListDataValue[MTRValueKey]; - if (![partsList isKindOfClass:[NSArray class]] || !partsList.count) { - MTR_LOG_ERROR("%@ unexpected type %@ for parts list %@", self, [partsList class], partsList); - return NO; - } - - // Check if we have cached descriptor clusters for each listed endpoint - for (NSDictionary * endpointDictionary in partsList) { - NSDictionary * endpointDataValue = endpointDictionary[MTRDataKey]; - if (![endpointDataValue isKindOfClass:[NSDictionary class]]) { - MTR_LOG_ERROR("%@ unexpected parts list dictionary %@ data value class %@", self, endpointDictionary, [endpointDataValue class]); - continue; - } - if (![MTRUnsignedIntegerValueType isEqual:endpointDataValue[MTRTypeKey]]) { - MTR_LOG_ERROR("%@ unexpected parts list data value %@ item type %@", self, endpointDataValue, endpointDataValue[MTRTypeKey]); - continue; - } - NSNumber * endpoint = endpointDataValue[MTRValueKey]; - if (![endpoint isKindOfClass:[NSNumber class]]) { - MTR_LOG_ERROR("%@ unexpected parts list item value class %@", self, [endpoint class]); - continue; - } - MTRDeviceDataValueDictionary descriptorDeviceTypeListDataValue = [self _cachedAttributeValueForPath:[MTRAttributePath attributePathWithEndpointID:endpoint clusterID:@(MTRClusterIDTypeDescriptorID) attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributeDeviceTypeListID)]]; - if (![MTRArrayValueType isEqualToString:descriptorDeviceTypeListDataValue[MTRTypeKey]] || !descriptorDeviceTypeListDataValue[MTRValueKey]) { - return NO; - } - } - - return YES; -} - - (MTRBaseDevice *)newBaseDevice { return [MTRBaseDevice deviceWithNodeID:self.nodeID controller:self.deviceController]; @@ -3404,6 +3650,31 @@ - (void)removeClientDataForKey:(NSString *)key endpointID:(NSNumber *)endpointID @end +/* BEGIN DRAGONS: Note methods here cannot be renamed, and are used by private callers, do not rename, remove or modify behavior here */ + +@implementation MTRDevice (MatterPrivateForInternalDragonsDoNotFeed) + +- (BOOL)_deviceHasActiveSubscription +{ + std::lock_guard lock(_lock); + + return HaveSubscriptionEstablishedRightNow(_internalDeviceState); +} + +- (void)_deviceMayBeReachable +{ + assertChipStackLockedByCurrentThread(); + + MTR_LOG("%@ _deviceMayBeReachable called", self); + + [self _triggerResubscribeWithReason:"SPI client indicated the device may now be reachable" + nodeLikelyReachable:YES]; +} + +/* END DRAGONS */ + +@end + @implementation MTRDevice (Deprecated) + (MTRDevice *)deviceWithNodeID:(uint64_t)nodeID deviceController:(MTRDeviceController *)deviceController diff --git a/src/darwin/Framework/CHIP/MTRDeviceAttestationDelegateBridge.mm b/src/darwin/Framework/CHIP/MTRDeviceAttestationDelegateBridge.mm index b044cb6502dceb..38e6ba08165fc0 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceAttestationDelegateBridge.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceAttestationDelegateBridge.mm @@ -28,7 +28,7 @@ chip::Credentials::AttestationVerificationResult attestationResult) { dispatch_async(mQueue, ^{ - MTR_LOG_DEFAULT("MTRDeviceAttestationDelegateBridge::OnDeviceAttestationFailed completed with result: %hu", + MTR_LOG("MTRDeviceAttestationDelegateBridge::OnDeviceAttestationFailed completed with result: %hu", chip::to_underlying(attestationResult)); mResult = attestationResult; diff --git a/src/darwin/Framework/CHIP/MTRDeviceConnectivityMonitor.mm b/src/darwin/Framework/CHIP/MTRDeviceConnectivityMonitor.mm index 80ad6f80d5be2b..3df270be9ce62e 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceConnectivityMonitor.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceConnectivityMonitor.mm @@ -154,7 +154,7 @@ - (void)handleResolvedHostname:(const char *)hostName port:(uint16_t)port error: if (self) { nw_path_status_t status = nw_path_get_status(path); if (status == nw_path_status_satisfied) { - MTR_LOG_INFO("%@ path is satisfied", self); + MTR_LOG("%@ path is satisfied", self); std::lock_guard lock(sConnectivityMonitorLock); [self _callHandler]; } @@ -165,7 +165,7 @@ - (void)handleResolvedHostname:(const char *)hostName port:(uint16_t)port error: if (self) { if (viable) { std::lock_guard lock(sConnectivityMonitorLock); - MTR_LOG_INFO("%@ connectivity now viable", self); + MTR_LOG("%@ connectivity now viable", self); [self _callHandler]; } } @@ -201,11 +201,11 @@ - (void)startMonitoringWithHandler:(MTRDeviceConnectivityMonitorHandler)handler // If there's already a resolver running, just return if (_resolvers.size() != 0) { - MTR_LOG_INFO("%@ connectivity monitor already running", self); + MTR_LOG("%@ connectivity monitor already running", self); return; } - MTR_LOG_INFO("%@ start connectivity monitoring for %@ (%lu monitoring objects)", self, _instanceName, static_cast(sConnectivityMonitorCount)); + MTR_LOG("%@ start connectivity monitoring for %@ (%lu monitoring objects)", self, _instanceName, static_cast(sConnectivityMonitorCount)); auto sharedConnection = [MTRDeviceConnectivityMonitor _sharedResolverConnection]; if (!sharedConnection) { @@ -259,7 +259,7 @@ - (void)_stopMonitoring std::lock_guard lock(sConnectivityMonitorLock); if (!sConnectivityMonitorCount) { - MTR_LOG_INFO("MTRDeviceConnectivityMonitor: Closing shared resolver connection"); + MTR_LOG("MTRDeviceConnectivityMonitor: Closing shared resolver connection"); DNSServiceRefDeallocate(sSharedResolverConnection); sSharedResolverConnection = NULL; sSharedResolverQueue = nil; @@ -273,7 +273,7 @@ - (void)stopMonitoring { // DNSServiceRefDeallocate must be called on the same queue set on the shared connection. dispatch_async(sSharedResolverQueue, ^{ - MTR_LOG_INFO("%@ stop connectivity monitoring for %@", self, self->_instanceName); + MTR_LOG("%@ stop connectivity monitoring for %@", self, self->_instanceName); std::lock_guard lock(sConnectivityMonitorLock); [self _stopMonitoring]; }); diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.h b/src/darwin/Framework/CHIP/MTRDeviceController.h index 09c480f0568754..eaae1fcf7d4c6a 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController.h @@ -54,7 +54,7 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) * if it has not already been started. * 2) Return nil or a running controller. * - * Once this returns non-nil, it's the caller's resposibility to call shutdown + * Once this returns non-nil, it's the caller's responsibility to call shutdown * on the controller to avoid leaking it. */ - (nullable instancetype)initWithParameters:(MTRDeviceControllerAbstractParameters *)parameters diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index 16b4d7f385e3aa..303cc2679e60d1 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -128,6 +128,8 @@ @implementation MTRDeviceController { // _serverEndpoints is only touched on the Matter queue. NSMutableArray * _serverEndpoints; + + MTRDeviceStorageBehaviorConfiguration * _storageBehaviorConfiguration; } - (nullable instancetype)initWithParameters:(MTRDeviceControllerAbstractParameters *)parameters error:(NSError * __autoreleasing *)error @@ -145,9 +147,6 @@ - (nullable instancetype)initWithParameters:(MTRDeviceControllerAbstractParamete return [MTRDeviceControllerFactory.sharedInstance initializeController:self withParameters:controllerParameters error:error]; } -static NSString * const kLocalTestUserDefaultDomain = @"org.csa-iot.matter.darwintest"; -static NSString * const kLocalTestUserDefaultSubscriptionPoolSizeOverrideKey = @"subscriptionPoolSizeOverride"; - - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory queue:(dispatch_queue_t)queue storageDelegate:(id _Nullable)storageDelegate @@ -156,6 +155,7 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory otaProviderDelegateQueue:(dispatch_queue_t _Nullable)otaProviderDelegateQueue uniqueIdentifier:(NSUUID *)uniqueIdentifier concurrentSubscriptionPoolSize:(NSUInteger)concurrentSubscriptionPoolSize + storageBehaviorConfiguration:(MTRDeviceStorageBehaviorConfiguration *)storageBehaviorConfiguration { if (self = [super init]) { // Make sure our storage is all set up to work as early as possible, @@ -256,22 +256,29 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory } // Provide a way to test different subscription pool sizes without code change - NSUserDefaults * defaults = [[NSUserDefaults alloc] initWithSuiteName:kLocalTestUserDefaultDomain]; - if ([defaults objectForKey:kLocalTestUserDefaultSubscriptionPoolSizeOverrideKey]) { - NSInteger subscriptionPoolSizeOverride = [defaults integerForKey:kLocalTestUserDefaultSubscriptionPoolSizeOverrideKey]; + NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; + if ([defaults objectForKey:kDefaultSubscriptionPoolSizeOverrideKey]) { + NSInteger subscriptionPoolSizeOverride = [defaults integerForKey:kDefaultSubscriptionPoolSizeOverrideKey]; if (subscriptionPoolSizeOverride < 1) { concurrentSubscriptionPoolSize = 1; } else { concurrentSubscriptionPoolSize = static_cast(subscriptionPoolSizeOverride); } + + MTR_LOG(" *** Overriding pool size of MTRDeviceController with: %lu", static_cast(concurrentSubscriptionPoolSize)); } if (!concurrentSubscriptionPoolSize) { concurrentSubscriptionPoolSize = 1; } + + MTR_LOG("Setting up pool size of MTRDeviceController with: %lu", static_cast(concurrentSubscriptionPoolSize)); + _concurrentSubscriptionPool = [[MTRAsyncWorkQueue alloc] initWithContext:self width:concurrentSubscriptionPoolSize]; _storedFabricIndex = chip::kUndefinedFabricIndex; + + _storageBehaviorConfiguration = storageBehaviorConfiguration; } return self; } @@ -595,13 +602,13 @@ - (BOOL)startup:(MTRDeviceControllerStartupParamsInternal *)startupParams if (_controllerDataStore) { // If the storage delegate supports the bulk read API, then a dictionary of nodeID => cluster data dictionary would be passed to the handler. Otherwise this would be a no-op, and stored attributes for MTRDevice objects will be loaded lazily in -deviceForNodeID:. [_controllerDataStore fetchAttributeDataForAllDevices:^(NSDictionary *> * _Nonnull clusterDataByNode) { - MTR_LOG_INFO("Loaded attribute values for %lu nodes from storage for controller uuid %@", static_cast(clusterDataByNode.count), self->_uniqueIdentifier); + MTR_LOG("Loaded attribute values for %lu nodes from storage for controller uuid %@", static_cast(clusterDataByNode.count), self->_uniqueIdentifier); std::lock_guard lock(self->_deviceMapLock); for (NSNumber * nodeID in clusterDataByNode) { NSDictionary * clusterData = clusterDataByNode[nodeID]; MTRDevice * device = [self _setupDeviceForNodeID:nodeID prefetchedClusterData:clusterData]; - MTR_LOG_INFO("Loaded %lu cluster data from storage for %@", static_cast(clusterData.count), device); + MTR_LOG("Loaded %lu cluster data from storage for %@", static_cast(clusterData.count), device); } }]; } @@ -631,7 +638,7 @@ - (BOOL)setupCommissioningSessionWithPayload:(MTRSetupPayload *)payload newNodeID:(NSNumber *)newNodeID error:(NSError * __autoreleasing *)error { - MTR_LOG_DEFAULT("Setting up commissioning session for device ID 0x%016llX with setup payload %@", newNodeID.unsignedLongLongValue, payload); + MTR_LOG("Setting up commissioning session for device ID 0x%016llX with setup payload %@", newNodeID.unsignedLongLongValue, payload); [[MTRMetricsCollector sharedInstance] resetMetrics]; @@ -684,7 +691,7 @@ - (BOOL)setupCommissioningSessionWithDiscoveredDevice:(MTRCommissionableBrowserR newNodeID:(NSNumber *)newNodeID error:(NSError * __autoreleasing *)error { - MTR_LOG_DEFAULT("Setting up commissioning session for already-discovered device %@ and device ID 0x%016llX with setup payload %@", discoveredDevice, newNodeID.unsignedLongLongValue, payload); + MTR_LOG("Setting up commissioning session for already-discovered device %@ and device ID 0x%016llX with setup payload %@", discoveredDevice, newNodeID.unsignedLongLongValue, payload); [[MTRMetricsCollector sharedInstance] resetMetrics]; @@ -942,7 +949,7 @@ - (MTRBaseDevice *)deviceBeingCommissionedWithNodeID:(NSNumber *)nodeID error:(N }; MTRBaseDevice * device = [self syncRunOnWorkQueueWithReturnValue:block error:error]; - MTR_LOG_DEFAULT("Getting device being commissioned with node ID 0x%016llX: %@ (error: %@)", + MTR_LOG("Getting device being commissioned with node ID 0x%016llX: %@ (error: %@)", nodeID.unsignedLongLongValue, device, (error ? *error : nil)); return device; } @@ -973,7 +980,7 @@ - (MTRDevice *)_setupDeviceForNodeID:(NSNumber *)nodeID prefetchedClusterData:(N } else if (_controllerDataStore) { // Load persisted cluster data if they exist. NSDictionary * clusterData = [_controllerDataStore getStoredClusterDataForNodeID:nodeID]; - MTR_LOG_INFO("Loaded %lu cluster data from storage for %@", static_cast(clusterData.count), deviceToReturn); + MTR_LOG("Loaded %lu cluster data from storage for %@", static_cast(clusterData.count), deviceToReturn); if (clusterData.count) { [deviceToReturn setPersistedClusterData:clusterData]; } @@ -987,6 +994,8 @@ - (MTRDevice *)_setupDeviceForNodeID:(NSNumber *)nodeID prefetchedClusterData:(N } } + [deviceToReturn setStorageBehaviorConfiguration:_storageBehaviorConfiguration]; + return deviceToReturn; } @@ -1126,7 +1135,7 @@ - (BOOL)addServerEndpoint:(MTRServerEndpoint *)endpoint [self asyncDispatchToMatterQueue:^() { [self->_serverEndpoints addObject:endpoint]; [endpoint registerMatterEndpoint]; - MTR_LOG_DEFAULT("Added server endpoint %u to controller %@", static_cast(endpoint.endpointID.unsignedLongLongValue), + MTR_LOG("Added server endpoint %u to controller %@", static_cast(endpoint.endpointID.unsignedLongLongValue), self->_uniqueIdentifier); } errorHandler:^(NSError * error) { @@ -1154,7 +1163,7 @@ - (void)removeServerEndpointInternal:(MTRServerEndpoint *)endpoint queue:(dispat // tearing it down. [self asyncDispatchToMatterQueue:^() { [self removeServerEndpointOnMatterQueue:endpoint]; - MTR_LOG_DEFAULT("Removed server endpoint %u from controller %@", static_cast(endpoint.endpointID.unsignedLongLongValue), + MTR_LOG("Removed server endpoint %u from controller %@", static_cast(endpoint.endpointID.unsignedLongLongValue), self->_uniqueIdentifier); if (queue != nil && completion != nil) { dispatch_async(queue, completion); @@ -1162,7 +1171,7 @@ - (void)removeServerEndpointInternal:(MTRServerEndpoint *)endpoint queue:(dispat } errorHandler:^(NSError * error) { // Error means we got shut down, so the endpoint is removed now. - MTR_LOG_DEFAULT("controller %@ already shut down, so endpoint %u has already been removed", self->_uniqueIdentifier, + MTR_LOG("controller %@ already shut down, so endpoint %u has already been removed", self->_uniqueIdentifier, static_cast(endpoint.endpointID.unsignedLongLongValue)); if (queue != nil && completion != nil) { dispatch_async(queue, completion); @@ -1833,7 +1842,7 @@ - (MTRBaseDevice *)getDeviceBeingCommissioned:(uint64_t)deviceId error:(NSError - (BOOL)openPairingWindow:(uint64_t)deviceID duration:(NSUInteger)duration error:(NSError * __autoreleasing *)error { if (duration > UINT16_MAX) { - MTR_LOG_ERROR("Error: Duration %tu is too large. Max value %d", duration, UINT16_MAX); + MTR_LOG_ERROR("Error: Duration %lu is too large. Max value %d", static_cast(duration), UINT16_MAX); if (error) { *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]; } @@ -1859,7 +1868,7 @@ - (NSString *)openPairingWindowWithPIN:(uint64_t)deviceID error:(NSError * __autoreleasing *)error { if (duration > UINT16_MAX) { - MTR_LOG_ERROR("Error: Duration %tu is too large. Max value %d", duration, UINT16_MAX); + MTR_LOG_ERROR("Error: Duration %lu is too large. Max value %d", static_cast(duration), UINT16_MAX); if (error) { *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]; } @@ -1867,7 +1876,7 @@ - (NSString *)openPairingWindowWithPIN:(uint64_t)deviceID } if (discriminator > 0xfff) { - MTR_LOG_ERROR("Error: Discriminator %tu is too large. Max value %d", discriminator, 0xfff); + MTR_LOG_ERROR("Error: Discriminator %lu is too large. Max value %d", static_cast(discriminator), 0xfff); if (error) { *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]; } diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.mm index ebec4a521eeb28..c05db43e610bc6 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.mm @@ -616,7 +616,7 @@ - (void)_pruneEmptyStoredClusterDataBranches } if (!success) { storeFailures++; - MTR_LOG_INFO("Store failed in _pruneEmptyStoredClusterDataBranches for clusterIndex (%lu) @ node 0x%016llX endpoint %u", static_cast(clusterIndexCopy.count), nodeID.unsignedLongLongValue, endpointID.unsignedShortValue); + MTR_LOG_ERROR("Store failed in _pruneEmptyStoredClusterDataBranches for clusterIndex (%lu) @ node 0x%016llX endpoint %u", static_cast(clusterIndexCopy.count), nodeID.unsignedLongLongValue, endpointID.unsignedShortValue); } } } @@ -631,7 +631,7 @@ - (void)_pruneEmptyStoredClusterDataBranches } if (!success) { storeFailures++; - MTR_LOG_INFO("Store failed in _pruneEmptyStoredClusterDataBranches for endpointIndex (%lu) @ node 0x%016llX", static_cast(endpointIndexCopy.count), nodeID.unsignedLongLongValue); + MTR_LOG_ERROR("Store failed in _pruneEmptyStoredClusterDataBranches for endpointIndex (%lu) @ node 0x%016llX", static_cast(endpointIndexCopy.count), nodeID.unsignedLongLongValue); } } } @@ -645,7 +645,7 @@ - (void)_pruneEmptyStoredClusterDataBranches } if (!success) { storeFailures++; - MTR_LOG_INFO("Store failed in _pruneEmptyStoredClusterDataBranches for nodeIndex (%lu)", static_cast(nodeIndexCopy.count)); + MTR_LOG_ERROR("Store failed in _pruneEmptyStoredClusterDataBranches for nodeIndex (%lu)", static_cast(nodeIndexCopy.count)); } } @@ -675,7 +675,7 @@ - (void)_clearStoredClusterDataForNodeID:(NSNumber *)nodeID for (NSNumber * clusterID in clusterIndex) { BOOL success = [self _deleteClusterDataForNodeID:nodeID endpointID:endpointID clusterID:clusterID]; if (!success) { - MTR_LOG_INFO("Delete failed for clusterData @ node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue, clusterID.unsignedLongValue); + MTR_LOG_ERROR("Delete failed for clusterData @ node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue, clusterID.unsignedLongValue); } else { clusterDataCleared++; } @@ -683,7 +683,7 @@ - (void)_clearStoredClusterDataForNodeID:(NSNumber *)nodeID BOOL success = [self _deleteClusterIndexForNodeID:nodeID endpointID:endpointID]; if (!success) { - MTR_LOG_INFO("Delete failed for clusterIndex @ node 0x%016llX endpoint %u", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue); + MTR_LOG_ERROR("Delete failed for clusterIndex @ node 0x%016llX endpoint %u", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue); } else { endpointsCleared++; } @@ -691,10 +691,10 @@ - (void)_clearStoredClusterDataForNodeID:(NSNumber *)nodeID BOOL success = [self _deleteEndpointIndexForNodeID:nodeID]; if (!success) { - MTR_LOG_INFO("Delete failed for endpointIndex @ node 0x%016llX", nodeID.unsignedLongLongValue); + MTR_LOG_ERROR("Delete failed for endpointIndex @ node 0x%016llX", nodeID.unsignedLongLongValue); } - MTR_LOG_INFO("clearStoredClusterDataForNodeID: deleted endpoints %lu/%lu clusters %lu/%lu", static_cast(endpointsCleared), static_cast(endpointsClearAttempts), static_cast(clusterDataCleared), static_cast(clusterDataClearAttempts)); + MTR_LOG("clearStoredClusterDataForNodeID: deleted endpoints %lu/%lu clusters %lu/%lu", static_cast(endpointsCleared), static_cast(endpointsClearAttempts), static_cast(clusterDataCleared), static_cast(clusterDataClearAttempts)); } - (void)clearStoredClusterDataForNodeID:(NSNumber *)nodeID @@ -712,7 +712,7 @@ - (void)clearStoredClusterDataForNodeID:(NSNumber *)nodeID success = [self _deleteNodeIndex]; } if (!success) { - MTR_LOG_INFO("Store failed in clearStoredAttributesForNodeID for nodeIndex (%lu)", static_cast(nodeIndexCopy.count)); + MTR_LOG_ERROR("Store failed in clearStoredAttributesForNodeID for nodeIndex (%lu)", static_cast(nodeIndexCopy.count)); } } }); @@ -769,7 +769,7 @@ - (void)clearAllStoredClusterData BOOL success = [self _deleteNodeIndex]; if (!success) { - MTR_LOG_INFO("Delete failed for nodeIndex"); + MTR_LOG_ERROR("Delete failed for nodeIndex"); } }); } @@ -787,7 +787,7 @@ - (void)clearAllStoredClusterData NSArray * nodeIndex = [self _fetchNodeIndex]; #if ATTRIBUTE_CACHE_VERBOSE_LOGGING - MTR_LOG_INFO("Fetch got %lu values for nodeIndex", static_cast(nodeIndex.count)); + MTR_LOG("Fetch got %lu values for nodeIndex", static_cast(nodeIndex.count)); #endif if (![nodeIndex containsObject:nodeID]) { @@ -801,7 +801,7 @@ - (void)clearAllStoredClusterData [self _clearStoredClusterDataForNodeID:nodeID]; } - MTR_LOG_INFO("Fetch got no value for endpointIndex @ node 0x%016llX", nodeID.unsignedLongLongValue); + MTR_LOG("Fetch got no value for endpointIndex @ node 0x%016llX", nodeID.unsignedLongLongValue); clusterDataToReturn = nil; return; } @@ -810,7 +810,7 @@ - (void)clearAllStoredClusterData NSArray * endpointIndex = [self _fetchEndpointIndexForNodeID:nodeID]; #if ATTRIBUTE_CACHE_VERBOSE_LOGGING - MTR_LOG_INFO("Fetch got %lu values for endpointIndex @ node 0x%016llX", static_cast(endpointIndex.count), nodeID.unsignedLongLongValue); + MTR_LOG("Fetch got %lu values for endpointIndex @ node 0x%016llX", static_cast(endpointIndex.count), nodeID.unsignedLongLongValue); #endif for (NSNumber * endpointID in endpointIndex) { @@ -818,19 +818,19 @@ - (void)clearAllStoredClusterData NSArray * clusterIndex = [self _fetchClusterIndexForNodeID:nodeID endpointID:endpointID]; #if ATTRIBUTE_CACHE_VERBOSE_LOGGING - MTR_LOG_INFO("Fetch got %lu values for clusterIndex @ node 0x%016llX %u", static_cast(clusterIndex.count), nodeID.unsignedLongLongValue, endpointID.unsignedShortValue); + MTR_LOG("Fetch got %lu values for clusterIndex @ node 0x%016llX %u", static_cast(clusterIndex.count), nodeID.unsignedLongLongValue, endpointID.unsignedShortValue); #endif for (NSNumber * clusterID in clusterIndex) { // Fetch cluster data MTRDeviceClusterData * clusterData = [self _fetchClusterDataForNodeID:nodeID endpointID:endpointID clusterID:clusterID]; if (!clusterData) { - MTR_LOG_INFO("Fetch got no value for clusterData @ node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue, clusterID.unsignedLongValue); + MTR_LOG("Fetch got no value for clusterData @ node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue, clusterID.unsignedLongValue); continue; } #if ATTRIBUTE_CACHE_VERBOSE_LOGGING - MTR_LOG_INFO("Fetch got clusterData @ node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue, clusterID.unsignedLongValue); + MTR_LOG("Fetch got clusterData @ node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue, clusterID.unsignedLongValue); #endif MTRClusterPath * path = [MTRClusterPath clusterPathWithEndpointID:endpointID clusterID:clusterID]; @@ -954,7 +954,7 @@ - (void)storeClusterData:(NSDictionary for (MTRClusterPath * path in clusterData) { MTRDeviceClusterData * data = clusterData[path]; #if ATTRIBUTE_CACHE_VERBOSE_LOGGING - MTR_LOG_INFO("Attempt to store clusterData @ node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, path.endpoint.unsignedShortValue, path.cluster.unsignedLongValue); + MTR_LOG("Attempt to store clusterData @ node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, path.endpoint.unsignedShortValue, path.cluster.unsignedLongValue); #endif if (bulkValuesToStore) { @@ -964,7 +964,7 @@ - (void)storeClusterData:(NSDictionary BOOL storeFailed = ![self _storeClusterData:data forNodeID:nodeID endpointID:path.endpoint clusterID:path.cluster]; if (storeFailed) { storeFailures++; - MTR_LOG_INFO("Store failed for clusterData @ node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, path.endpoint.unsignedShortValue, path.cluster.unsignedLongValue); + MTR_LOG_ERROR("Store failed for clusterData @ node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, path.endpoint.unsignedShortValue, path.cluster.unsignedLongValue); } } @@ -1020,7 +1020,7 @@ - (void)storeClusterData:(NSDictionary BOOL storeFailed = ![self _storeClusterIndex:clusterIndexToStore forNodeID:nodeID endpointID:endpointID]; if (storeFailed) { storeFailures++; - MTR_LOG_INFO("Store failed for clusterIndex @ node 0x%016llX endpoint %u", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue); + MTR_LOG_ERROR("Store failed for clusterIndex @ node 0x%016llX endpoint %u", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue); continue; } } @@ -1035,7 +1035,7 @@ - (void)storeClusterData:(NSDictionary BOOL storeFailed = ![self _storeEndpointIndex:endpointIndexToStore forNodeID:nodeID]; if (storeFailed) { storeFailures++; - MTR_LOG_INFO("Store failed for endpointIndex @ node 0x%016llX", nodeID.unsignedLongLongValue); + MTR_LOG_ERROR("Store failed for endpointIndex @ node 0x%016llX", nodeID.unsignedLongLongValue); } } } @@ -1057,7 +1057,7 @@ - (void)storeClusterData:(NSDictionary BOOL storeFailed = ![self _storeNodeIndex:nodeIndexToStore]; if (storeFailed) { storeFailures++; - MTR_LOG_INFO("Store failed for nodeIndex"); + MTR_LOG_ERROR("Store failed for nodeIndex"); } } } @@ -1066,7 +1066,7 @@ - (void)storeClusterData:(NSDictionary BOOL storeFailed = ![self _bulkStoreAttributeCacheValues:bulkValuesToStore]; if (storeFailed) { storeFailures++; - MTR_LOG_INFO("Store failed for bulk values count %lu", static_cast(bulkValuesToStore.count)); + MTR_LOG_ERROR("Store failed for bulk values count %lu", static_cast(bulkValuesToStore.count)); } } diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm index 4c50f7fd2a7fa4..5972b17de804e2 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm @@ -62,7 +62,7 @@ void MTRDeviceControllerDelegateBridge::OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status) { - MTR_LOG_DEFAULT("DeviceControllerDelegate status updated: %d", status); + MTR_LOG("DeviceControllerDelegate status updated: %d", status); // If pairing failed, PASE failed. However, since OnPairingComplete(failure_code) might not be invoked in all cases, mark // end of PASE with timeout as assumed failure. If OnPairingComplete is invoked, the right error code will be updated in @@ -94,7 +94,7 @@ void MTRDeviceControllerDelegateBridge::OnPairingComplete(CHIP_ERROR error) { if (error == CHIP_NO_ERROR) { - MTR_LOG_DEFAULT("MTRDeviceControllerDelegate PASE session establishment succeeded."); + MTR_LOG("MTRDeviceControllerDelegate PASE session establishment succeeded."); } else { MTR_LOG_ERROR("MTRDeviceControllerDelegate PASE session establishment failed: %" CHIP_ERROR_FORMAT, error.Format()); } @@ -114,7 +114,7 @@ void MTRDeviceControllerDelegateBridge::OnPairingDeleted(CHIP_ERROR error) { - MTR_LOG_DEFAULT("DeviceControllerDelegate Pairing deleted. Status %s", chip::ErrorStr(error)); + MTR_LOG("DeviceControllerDelegate Pairing deleted. Status %s", chip::ErrorStr(error)); // This is never actually called; just do nothing. } @@ -124,7 +124,7 @@ chip::VendorId vendorId = info.basic.vendorId; uint16_t productId = info.basic.productId; - MTR_LOG_DEFAULT("DeviceControllerDelegate Read Commissioning Info. VendorId %u ProductId %u", vendorId, productId); + MTR_LOG("DeviceControllerDelegate Read Commissioning Info. VendorId %u ProductId %u", vendorId, productId); id strongDelegate = mDelegate; MTRDeviceController * strongController = mController; @@ -140,7 +140,7 @@ void MTRDeviceControllerDelegateBridge::OnCommissioningComplete(chip::NodeId nodeId, CHIP_ERROR error) { - MTR_LOG_DEFAULT("DeviceControllerDelegate Commissioning complete. NodeId %llu Status %s", nodeId, chip::ErrorStr(error)); + MTR_LOG("DeviceControllerDelegate Commissioning complete. NodeId %llu Status %s", nodeId, chip::ErrorStr(error)); MATTER_LOG_METRIC_END(kMetricDeviceCommissioning, error); id strongDelegate = mDelegate; @@ -149,7 +149,7 @@ // Always collect the metrics to avoid unbounded growth of the stats in the collector MTRMetrics * metrics = [[MTRMetricsCollector sharedInstance] metricSnapshot:TRUE]; - MTR_LOG_INFO("Device commissioning complete with metrics %@", metrics); + MTR_LOG("Device commissioning complete with metrics %@", metrics); if ([strongDelegate respondsToSelector:@selector(controller:commissioningComplete:nodeID:)] || [strongDelegate respondsToSelector:@selector(controller:commissioningComplete:nodeID:metrics:)]) { diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm index 0ca254acb34115..662a228bdd994e 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm @@ -72,7 +72,7 @@ static bool sExitHandlerRegistered = false; static void ShutdownOnExit() { - MTR_LOG_INFO("ShutdownOnExit invoked on exit"); + MTR_LOG("ShutdownOnExit invoked on exit"); [[MTRDeviceControllerFactory sharedInstance] stopControllerFactory]; } @@ -239,7 +239,7 @@ - (void)_assertCurrentQueueIsNotMatterQueue - (void)cleanupStartupObjects { assertChipStackLockedByCurrentThread(); - MTR_LOG_INFO("Cleaning startup objects in controller factory"); + MTR_LOG("Cleaning startup objects in controller factory"); // Make sure the deinit order here is the reverse of the init order in // startControllerFactory: @@ -441,7 +441,7 @@ - (void)stopControllerFactory dispatch_sync(_chipWorkQueue, ^{ VerifyOrReturn(_running); - MTR_LOG_INFO("Shutting down the Matter controller factory"); + MTR_LOG("Shutting down the Matter controller factory"); _controllerFactory->Shutdown(); [self cleanupStartupObjects]; _running = NO; @@ -473,6 +473,7 @@ - (MTRDeviceController * _Nullable)_startDeviceController:(MTRDeviceController * id _Nullable otaProviderDelegate; dispatch_queue_t _Nullable otaProviderDelegateQueue; NSUInteger concurrentSubscriptionPoolSize = 0; + MTRDeviceStorageBehaviorConfiguration * storageBehaviorConfiguration = nil; if ([startupParams isKindOfClass:[MTRDeviceControllerParameters class]]) { MTRDeviceControllerParameters * params = startupParams; storageDelegate = params.storageDelegate; @@ -481,6 +482,7 @@ - (MTRDeviceController * _Nullable)_startDeviceController:(MTRDeviceController * otaProviderDelegate = params.otaProviderDelegate; otaProviderDelegateQueue = params.otaProviderDelegateQueue; concurrentSubscriptionPoolSize = params.concurrentSubscriptionEstablishmentsAllowedOnThread; + storageBehaviorConfiguration = params.storageBehaviorConfiguration; } else if ([startupParams isKindOfClass:[MTRDeviceControllerStartupParams class]]) { MTRDeviceControllerStartupParams * params = startupParams; storageDelegate = nil; @@ -498,7 +500,7 @@ - (MTRDeviceController * _Nullable)_startDeviceController:(MTRDeviceController * if (!_running) { // Note: reading _running from outside of the Matter work queue if (storageDelegate != nil) { - MTR_LOG_DEFAULT("Auto-starting Matter controller factory in per-controller storage mode"); + MTR_LOG("Auto-starting Matter controller factory in per-controller storage mode"); auto * params = [[MTRDeviceControllerFactoryParams alloc] initWithoutStorage]; if (![self _startControllerFactory:params startingController:YES error:error]) { return nil; @@ -542,7 +544,8 @@ - (MTRDeviceController * _Nullable)_startDeviceController:(MTRDeviceController * otaProviderDelegate:otaProviderDelegate otaProviderDelegateQueue:otaProviderDelegateQueue uniqueIdentifier:uniqueIdentifier - concurrentSubscriptionPoolSize:concurrentSubscriptionPoolSize]; + concurrentSubscriptionPoolSize:concurrentSubscriptionPoolSize + storageBehaviorConfiguration:storageBehaviorConfiguration]; if (controller == nil) { if (error != nil) { *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_ARGUMENT]; @@ -766,7 +769,7 @@ - (void)preWarmCommissioningSession if (!self->_running) { MTR_LOG_ERROR("Can't pre-warm, Matter controller factory is not running"); } else { - MTR_LOG_DEFAULT("Pre-warming commissioning session"); + MTR_LOG("Pre-warming commissioning session"); self->_controllerFactory->EnsureAndRetainSystemState(); err = DeviceLayer::PlatformMgrImpl().StartBleScan(&self->_preWarmingDelegate, DeviceLayer::BleScanMode::kPreWarm); if (err != CHIP_NO_ERROR) { @@ -781,7 +784,7 @@ - (void)preWarmCommissioningSession - (void)preWarmCommissioningSessionDone { assertChipStackLockedByCurrentThread(); - MTR_LOG_DEFAULT("Pre-warming done"); + MTR_LOG("Pre-warming done"); self->_controllerFactory->ReleaseSystemState(); } @@ -881,24 +884,21 @@ - (MTRDeviceController * _Nullable)maybeInitializeOTAProvider:(MTRDeviceControll - (void)resetOperationalAdvertising { - if (!_advertiseOperational) { - // No need to reset anything; we are not advertising the things that - // would need to get reset. - return; - } + assertChipStackLockedByCurrentThread(); - std::lock_guard lock(_controllersLock); - if (_controllers.count != 0) { - // We have a running controller. That means we likely need to reset - // operational advertising for that controller. - dispatch_async(_chipWorkQueue, ^{ - // StartServer() is the only API we have for resetting DNS-SD - // advertising. It sure would be nice if there were a "restart" - // that was a no-op if the DNS-SD server was not already - // running. - app::DnssdServer::Instance().StartServer(); - }); + // If we're not advertising, then there's no need to reset anything. + VerifyOrReturn(_advertiseOperational); + + // If there are no running controllers there will be no advertisements to reset. + { + std::lock_guard lock(_controllersLock); + VerifyOrReturn(_controllers.count > 0); } + + // StartServer() is the only API we have for resetting DNS-SD advertising. + // It sure would be nice if there were a "restart" that was a no-op if the + // DNS-SD server was not already running. + app::DnssdServer::Instance().StartServer(); } - (void)controllerShuttingDown:(MTRDeviceController *)controller @@ -1165,6 +1165,45 @@ - (MTRDeviceController * _Nullable)initializeController:(MTRDeviceController *)c error:error]; } +- (void)setMessageReliabilityProtocolIdleRetransmitMs:(nullable NSNumber *)idleRetransmitMs + activeRetransmitMs:(nullable NSNumber *)activeRetransmitMs + activeThresholdMs:(nullable NSNumber *)activeThresholdMs + additionalRetransmitDelayMs:(nullable NSNumber *)additionalRetransmitDelayMs +{ + [self _assertCurrentQueueIsNotMatterQueue]; + dispatch_async(_chipWorkQueue, ^{ + bool resetAdvertising; + if (idleRetransmitMs == nil && activeRetransmitMs == nil && activeThresholdMs == nil && additionalRetransmitDelayMs == nil) { + Messaging::ReliableMessageMgr::SetAdditionalMRPBackoffTime(NullOptional); + resetAdvertising = ReliableMessageProtocolConfig::SetLocalMRPConfig(NullOptional); + } else { + if (additionalRetransmitDelayMs != nil) { + System::Clock::Timeout additionalBackoff(additionalRetransmitDelayMs.unsignedLongValue); + Messaging::ReliableMessageMgr::SetAdditionalMRPBackoffTime(MakeOptional(additionalBackoff)); + } + + // Get current MRP parameters, then override the things we were asked to + // override. + ReliableMessageProtocolConfig mrpConfig = GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig()); + if (idleRetransmitMs != nil) { + mrpConfig.mIdleRetransTimeout = System::Clock::Milliseconds32(idleRetransmitMs.unsignedLongValue); + } + if (activeRetransmitMs != nil) { + mrpConfig.mActiveRetransTimeout = System::Clock::Milliseconds32(activeRetransmitMs.unsignedLongValue); + } + if (activeThresholdMs != nil) { + mrpConfig.mActiveThresholdTime = System::Clock::Milliseconds32(activeThresholdMs.unsignedLongValue); + } + + resetAdvertising = ReliableMessageProtocolConfig::SetLocalMRPConfig(MakeOptional(mrpConfig)); + } + + if (resetAdvertising) { + [self resetOperationalAdvertising]; + } + }); +} + - (PersistentStorageDelegate *)storageDelegate { return _persistentStorageDelegate; @@ -1327,33 +1366,8 @@ void MTRSetMessageReliabilityParameters(NSNumber * _Nullable idleRetransmitMs, NSNumber * _Nullable activeThresholdMs, NSNumber * _Nullable additionalRetransmitDelayMs) { - bool resetAdvertising = false; - if (idleRetransmitMs == nil && activeRetransmitMs == nil && activeThresholdMs == nil && additionalRetransmitDelayMs == nil) { - Messaging::ReliableMessageMgr::SetAdditionalMRPBackoffTime(NullOptional); - resetAdvertising = ReliableMessageProtocolConfig::SetLocalMRPConfig(NullOptional); - } else { - if (additionalRetransmitDelayMs != nil) { - System::Clock::Timeout additionalBackoff(additionalRetransmitDelayMs.unsignedLongValue); - Messaging::ReliableMessageMgr::SetAdditionalMRPBackoffTime(MakeOptional(additionalBackoff)); - } - - // Get current MRP parameters, then override the things we were asked to - // override. - ReliableMessageProtocolConfig mrpConfig = GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig()); - if (idleRetransmitMs != nil) { - mrpConfig.mIdleRetransTimeout = System::Clock::Milliseconds32(idleRetransmitMs.unsignedLongValue); - } - if (activeRetransmitMs != nil) { - mrpConfig.mActiveRetransTimeout = System::Clock::Milliseconds32(activeRetransmitMs.unsignedLongValue); - } - if (activeThresholdMs != nil) { - mrpConfig.mActiveThresholdTime = System::Clock::Milliseconds32(activeThresholdMs.unsignedLongValue); - } - - resetAdvertising = ReliableMessageProtocolConfig::SetLocalMRPConfig(MakeOptional(mrpConfig)); - } - - if (resetAdvertising) { - [[MTRDeviceControllerFactory sharedInstance] resetOperationalAdvertising]; - } + [MTRDeviceControllerFactory.sharedInstance setMessageReliabilityProtocolIdleRetransmitMs:idleRetransmitMs + activeRetransmitMs:activeThresholdMs + activeThresholdMs:activeThresholdMs + additionalRetransmitDelayMs:additionalRetransmitDelayMs]; } diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerLocalTestStorage.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerLocalTestStorage.mm index f51dae7bbbcaff..fa4a0e456f5626 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerLocalTestStorage.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerLocalTestStorage.mm @@ -16,27 +16,25 @@ */ #import "MTRDeviceControllerLocalTestStorage.h" +#import "MTRDevice_Internal.h" #import "MTRLogging_Internal.h" -static NSString * const kLocalTestUserDefaultDomain = @"org.csa-iot.matter.darwintest"; -static NSString * const kLocalTestUserDefaultEnabledKey = @"enableTestStorage"; - @implementation MTRDeviceControllerLocalTestStorage { id _passThroughStorage; } + (BOOL)localTestStorageEnabled { - NSUserDefaults * defaults = [[NSUserDefaults alloc] initWithSuiteName:kLocalTestUserDefaultDomain]; - return [defaults boolForKey:kLocalTestUserDefaultEnabledKey]; + NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; + return [defaults boolForKey:kTestStorageUserDefaultEnabledKey]; } + (void)setLocalTestStorageEnabled:(BOOL)localTestStorageEnabled { - NSUserDefaults * defaults = [[NSUserDefaults alloc] initWithSuiteName:kLocalTestUserDefaultDomain]; - [defaults setBool:localTestStorageEnabled forKey:kLocalTestUserDefaultEnabledKey]; - MTR_LOG_INFO("MTRDeviceControllerLocalTestStorage setLocalTestStorageEnabled %d", localTestStorageEnabled); - BOOL storedLocalTestStorageEnabled = [defaults boolForKey:kLocalTestUserDefaultEnabledKey]; + NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; + [defaults setBool:localTestStorageEnabled forKey:kTestStorageUserDefaultEnabledKey]; + MTR_LOG("MTRDeviceControllerLocalTestStorage setLocalTestStorageEnabled %d", localTestStorageEnabled); + BOOL storedLocalTestStorageEnabled = [defaults boolForKey:kTestStorageUserDefaultEnabledKey]; if (storedLocalTestStorageEnabled != localTestStorageEnabled) { MTR_LOG_ERROR("MTRDeviceControllerLocalTestStorage setLocalTestStorageEnabled %d failed", localTestStorageEnabled); } @@ -47,7 +45,7 @@ - (instancetype)initWithPassThroughStorage:(id> *)valuesForController:(MTRDeviceController *)controller securityLevel:(MTRStorageSecurityLevel)securityLevel sharingType:(MTRStorageSharingType)sharingType { if (sharingType == MTRStorageSharingTypeNotShared) { - NSUserDefaults * defaults = [[NSUserDefaults alloc] initWithSuiteName:kLocalTestUserDefaultDomain]; + NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; return [defaults dictionaryRepresentation]; } else { if (_passThroughStorage && [_passThroughStorage respondsToSelector:@selector(valuesForController:securityLevel:sharingType:)]) { return [_passThroughStorage valuesForController:controller securityLevel:securityLevel sharingType:sharingType]; } else { - MTR_LOG_INFO("MTRDeviceControllerLocalTestStorage valuesForController: shared type but no pass-through storage"); + MTR_LOG_ERROR("MTRDeviceControllerLocalTestStorage valuesForController: shared type but no pass-through storage"); return nil; } } @@ -136,13 +134,13 @@ - (BOOL)controller:(MTRDeviceController *)controller - (BOOL)controller:(MTRDeviceController *)controller storeValues:(NSDictionary> *)values securityLevel:(MTRStorageSecurityLevel)securityLevel sharingType:(MTRStorageSharingType)sharingType { if (sharingType == MTRStorageSharingTypeNotShared) { - NSUserDefaults * defaults = [[NSUserDefaults alloc] initWithSuiteName:kLocalTestUserDefaultDomain]; + NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; BOOL success = YES; for (NSString * key in values) { NSError * error = nil; NSData * data = [NSKeyedArchiver archivedDataWithRootObject:values[key] requiringSecureCoding:YES error:&error]; if (error) { - MTR_LOG_INFO("MTRDeviceControllerLocalTestStorage storeValues: failed to convert value object to data %@", error); + MTR_LOG_ERROR("MTRDeviceControllerLocalTestStorage storeValues: failed to convert value object to data %@", error); success = NO; continue; } @@ -153,7 +151,7 @@ - (BOOL)controller:(MTRDeviceController *)controller storeValues:(NSDictionary #import +#import #import NS_ASSUME_NONNULL_BEGIN @@ -85,6 +86,13 @@ MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) */ @property (nonatomic, assign) NSUInteger concurrentSubscriptionEstablishmentsAllowedOnThread MTR_NEWLY_AVAILABLE; +/** + * Sets the storage behavior configuration - see MTRDeviceStorageBehaviorConfiguration.h for details + * + * If this value is nil, a default storage behavior configuration will be used. + */ +@property (nonatomic, copy, nullable) MTRDeviceStorageBehaviorConfiguration * storageBehaviorConfiguration; + @end MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm index a97bae4ece6c6c..f393d48d0a99c1 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm @@ -614,7 +614,7 @@ - (instancetype)initForNewController:(MTRDeviceController *)controller // Our NOC has changed in a way that would affect ACL checks. Clear // out our session resumption storage, because resuming those CASE // sessions will end up doing ACL checks against our old NOC. - MTR_LOG_DEFAULT("Node ID or CATs changed. Clearing CASE resumption storage."); + MTR_LOG("Node ID or CATs changed. Clearing CASE resumption storage."); [controller.controllerDataStore clearAllResumptionInfo]; } } diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h index 8aefa481ba7616..8fb61fba9bab80 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h @@ -32,6 +32,7 @@ #import "MTRBaseDevice.h" #import "MTRDeviceController.h" #import "MTRDeviceControllerDataStore.h" +#import "MTRDeviceStorageBehaviorConfiguration.h" #import #import @@ -113,7 +114,8 @@ NS_ASSUME_NONNULL_BEGIN otaProviderDelegate:(id _Nullable)otaProviderDelegate otaProviderDelegateQueue:(dispatch_queue_t _Nullable)otaProviderDelegateQueue uniqueIdentifier:(NSUUID *)uniqueIdentifier - concurrentSubscriptionPoolSize:(NSUInteger)concurrentSubscriptionPoolSize; + concurrentSubscriptionPoolSize:(NSUInteger)concurrentSubscriptionPoolSize + storageBehaviorConfiguration:(MTRDeviceStorageBehaviorConfiguration *)storageBehaviorConfiguration; /** * Check whether this controller is running on the given fabric, as represented diff --git a/src/darwin/Framework/CHIP/MTRDeviceStorageBehaviorConfiguration.h b/src/darwin/Framework/CHIP/MTRDeviceStorageBehaviorConfiguration.h new file mode 100644 index 00000000000000..30ea957cd3987d --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRDeviceStorageBehaviorConfiguration.h @@ -0,0 +1,98 @@ +/** + * 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 + +NS_ASSUME_NONNULL_BEGIN + +/** + * Class that configures how MTRDevice objects persist its attributes to storage, so as to not + * overwhelm the underlying storage system. + */ +MTR_NEWLY_AVAILABLE +@interface MTRDeviceStorageBehaviorConfiguration : NSObject + +/** + * Create configuration with a default set of values. See description below for details. + */ ++ (instancetype)configurationWithDefaultStorageBehavior; + +/** + * Create configuration that disables storage behavior optimizations. + */ ++ (instancetype)configurationWithStorageBehaviorOptimizationDisabled; + +/** + * Create configuration with specified values. See description below for details, and the list of + * properties below for valid ranges of these values. + */ ++ (instancetype)configurationWithReportToPersistenceDelayTime:(NSTimeInterval)reportToPersistenceDelayTime + reportToPersistenceDelayTimeMax:(NSTimeInterval)reportToPersistenceDelayTimeMax + recentReportTimesMaxCount:(NSUInteger)recentReportTimesMaxCount + timeBetweenReportsTooShortThreshold:(NSTimeInterval)timeBetweenReportsTooShortThreshold + timeBetweenReportsTooShortMinThreshold:(NSTimeInterval)timeBetweenReportsTooShortMinThreshold + reportToPersistenceDelayMaxMultiplier:(double)reportToPersistenceDelayMaxMultiplier + deviceReportingExcessivelyIntervalThreshold:(NSTimeInterval)deviceReportingExcessivelyIntervalThreshold; + +/** + * Storage behavior with values in the allowed range: + * + * Each time a report comes in, MTRDevice will wait reportToPersistDelayTime before persisting the + * changes to storage. If another report comes in during this internal, MTRDevice will wait another + * reportToPersistDelayTime interval, until reportToPersistDelayTimeMax is reached, at which + * point all the changes so far will be written to storage. + * + * MTRDevice will also track recentReportTimesMaxCount number of report times. If the running + * average time between reports dips below timeBetweenReportsTooShortThreshold, a portion of the + * reportToPersistenceDelayMaxMultiplier will be applied to both the reportToPersistenceDelayTime + * and reportToPersistenceDelayTimeMax. The multiplier will reach the max when the average time + * between reports reach timeBetweenReportsTooShortMinThreshold. + * + * When the running average time between reports dips below timeBetweenReportsTooShortMinThreshold + * for the first time, the time will be noted. If the device remains in this state for longer than + * deviceReportingExcessivelyIntervalThreshold, persistence will stop until the average time between + * reports go back above timeBetweenReportsTooShortMinThreshold. + */ + +/** + * If disableStorageBehaviorOptimization is set to YES, then all the waiting mechanism as described above + * is disabled. + */ +@property (nonatomic, assign) BOOL disableStorageBehaviorOptimization; + +/** + * If any of these properties are set to be out of the documented limits, these default values will + * be used to replace all of them: + * + * reportToPersistenceDelayTimeDefault (15) + * reportToPersistenceDelayTimeMaxDefault (20 * kReportToPersistenceDelayTimeDefault) + * recentReportTimesMaxCountDefault (12) + * timeBetweenReportsTooShortThresholdDefault (15) + * timeBetweenReportsTooShortMinThresholdDefault (5) + * reportToPersistenceDelayMaxMultiplierDefault (10) + * deviceReportingExcessivelyIntervalThresholdDefault (5 * 60) + */ +@property (nonatomic, assign) NSTimeInterval reportToPersistenceDelayTime; /* must be > 0 */ +@property (nonatomic, assign) NSTimeInterval reportToPersistenceDelayTimeMax; /* must be larger than reportToPersistenceDelayTime */ +@property (nonatomic, assign) NSUInteger recentReportTimesMaxCount; /* must be >= 2 */ +@property (nonatomic, assign) NSTimeInterval timeBetweenReportsTooShortThreshold; /* must be > 0 */ +@property (nonatomic, assign) NSTimeInterval timeBetweenReportsTooShortMinThreshold; /* must be > 0 and smaller than timeBetweenReportsTooShortThreshold */ +@property (nonatomic, assign) double reportToPersistenceDelayMaxMultiplier; /* must be > 1 */ +@property (nonatomic, assign) NSTimeInterval deviceReportingExcessivelyIntervalThreshold; /* must be > 0 */ +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRDeviceStorageBehaviorConfiguration.mm b/src/darwin/Framework/CHIP/MTRDeviceStorageBehaviorConfiguration.mm new file mode 100644 index 00000000000000..4522c7e69b4ad6 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRDeviceStorageBehaviorConfiguration.mm @@ -0,0 +1,105 @@ +/** + * 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 "MTRDeviceStorageBehaviorConfiguration.h" + +#import "MTRLogging_Internal.h" + +#define kReportToPersistenceDelayTimeDefault (15) +#define kReportToPersistenceDelayTimeMaxDefault (20 * kReportToPersistenceDelayTimeDefault) +#define kRecentReportTimesMaxCountDefault (12) +#define kTimeBetweenReportsTooShortThresholdDefault (15) +#define kTimeBetweenReportsTooShortMinThresholdDefault (5) +#define kReportToPersistenceDelayMaxMultiplierDefault (10) +#define kDeviceReportingExcessivelyIntervalThresholdDefault (5 * 60) + +@implementation MTRDeviceStorageBehaviorConfiguration + ++ (instancetype)configurationWithReportToPersistenceDelayTime:(NSTimeInterval)reportToPersistenceDelayTime + reportToPersistenceDelayTimeMax:(NSTimeInterval)reportToPersistenceDelayTimeMax + recentReportTimesMaxCount:(NSUInteger)recentReportTimesMaxCount + timeBetweenReportsTooShortThreshold:(NSTimeInterval)timeBetweenReportsTooShortThreshold + timeBetweenReportsTooShortMinThreshold:(NSTimeInterval)timeBetweenReportsTooShortMinThreshold + reportToPersistenceDelayMaxMultiplier:(double)reportToPersistenceDelayMaxMultiplier + deviceReportingExcessivelyIntervalThreshold:(NSTimeInterval)deviceReportingExcessivelyIntervalThreshold +{ + auto newConfiguration = [[MTRDeviceStorageBehaviorConfiguration alloc] init]; + newConfiguration.reportToPersistenceDelayTime = reportToPersistenceDelayTime; + newConfiguration.reportToPersistenceDelayTimeMax = reportToPersistenceDelayTimeMax; + newConfiguration.recentReportTimesMaxCount = recentReportTimesMaxCount; + newConfiguration.timeBetweenReportsTooShortThreshold = timeBetweenReportsTooShortThreshold; + newConfiguration.timeBetweenReportsTooShortMinThreshold = timeBetweenReportsTooShortMinThreshold; + newConfiguration.reportToPersistenceDelayMaxMultiplier = reportToPersistenceDelayMaxMultiplier; + newConfiguration.deviceReportingExcessivelyIntervalThreshold = deviceReportingExcessivelyIntervalThreshold; + + return newConfiguration; +} + ++ (instancetype)configurationWithDefaultStorageBehavior +{ + auto newConfiguration = [[MTRDeviceStorageBehaviorConfiguration alloc] init]; + [newConfiguration checkValuesAndResetToDefaultIfNecessary]; + return newConfiguration; +} + ++ (instancetype)configurationWithStorageBehaviorOptimizationDisabled +{ + auto newConfiguration = [[MTRDeviceStorageBehaviorConfiguration alloc] init]; + newConfiguration.disableStorageBehaviorOptimization = YES; + return newConfiguration; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"(_recentReportTimesMaxCount), _timeBetweenReportsTooShortThreshold, _timeBetweenReportsTooShortMinThreshold, _reportToPersistenceDelayMaxMultiplier, _deviceReportingExcessivelyIntervalThreshold]; +} + +- (void)checkValuesAndResetToDefaultIfNecessary +{ + if (_disableStorageBehaviorOptimization) { + return; + } + + // Sanity check all the values, and if any is out of range, reset to default values + if ((_reportToPersistenceDelayTime <= 0) || (_reportToPersistenceDelayTimeMax <= 0) || (_reportToPersistenceDelayTimeMax < _reportToPersistenceDelayTime) || (_recentReportTimesMaxCount < 2) || (_timeBetweenReportsTooShortThreshold <= 0) || (_timeBetweenReportsTooShortMinThreshold <= 0) || (_timeBetweenReportsTooShortMinThreshold > _timeBetweenReportsTooShortThreshold) || (_reportToPersistenceDelayMaxMultiplier <= 1) || (_deviceReportingExcessivelyIntervalThreshold <= 0)) { + MTR_LOG_ERROR("%@ storage behavior: MTRDeviceStorageBehaviorConfiguration values out of bounds - resetting to default", self); + + _reportToPersistenceDelayTime = kReportToPersistenceDelayTimeDefault; + _reportToPersistenceDelayTimeMax = kReportToPersistenceDelayTimeMaxDefault; + _recentReportTimesMaxCount = kRecentReportTimesMaxCountDefault; + _timeBetweenReportsTooShortThreshold = kTimeBetweenReportsTooShortThresholdDefault; + _timeBetweenReportsTooShortMinThreshold = kTimeBetweenReportsTooShortMinThresholdDefault; + _reportToPersistenceDelayMaxMultiplier = kReportToPersistenceDelayMaxMultiplierDefault; + _deviceReportingExcessivelyIntervalThreshold = kDeviceReportingExcessivelyIntervalThresholdDefault; + } +} + +- (id)copyWithZone:(NSZone *)zone +{ + auto newConfiguration = [[MTRDeviceStorageBehaviorConfiguration alloc] init]; + newConfiguration.disableStorageBehaviorOptimization = _disableStorageBehaviorOptimization; + newConfiguration.reportToPersistenceDelayTime = _reportToPersistenceDelayTime; + newConfiguration.reportToPersistenceDelayTimeMax = _reportToPersistenceDelayTimeMax; + newConfiguration.recentReportTimesMaxCount = _recentReportTimesMaxCount; + newConfiguration.timeBetweenReportsTooShortThreshold = _timeBetweenReportsTooShortThreshold; + newConfiguration.timeBetweenReportsTooShortMinThreshold = _timeBetweenReportsTooShortMinThreshold; + newConfiguration.reportToPersistenceDelayMaxMultiplier = _reportToPersistenceDelayMaxMultiplier; + newConfiguration.deviceReportingExcessivelyIntervalThreshold = _deviceReportingExcessivelyIntervalThreshold; + + return newConfiguration; +} + +@end diff --git a/src/darwin/Framework/CHIP/MTRDeviceStorageBehaviorConfiguration_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceStorageBehaviorConfiguration_Internal.h new file mode 100644 index 00000000000000..f3c8cf992f7bbc --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRDeviceStorageBehaviorConfiguration_Internal.h @@ -0,0 +1,21 @@ +/** + * 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 "MTRDeviceStorageBehaviorConfiguration.h" + +@interface MTRDeviceStorageBehaviorConfiguration () +- (void)checkValuesAndResetToDefaultIfNecessary; +@end diff --git a/src/darwin/Framework/CHIP/MTRDevice_Internal.h b/src/darwin/Framework/CHIP/MTRDevice_Internal.h index 12ff1306185ed9..2ad2f6422dd9e7 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDevice_Internal.h @@ -21,6 +21,7 @@ #import "MTRAsyncWorkQueue.h" #import "MTRDefines_Internal.h" +#import "MTRDeviceStorageBehaviorConfiguration_Internal.h" NS_ASSUME_NONNULL_BEGIN @@ -30,6 +31,26 @@ typedef NSDictionary * MTRDeviceDataValueDictionary; typedef void (^MTRDevicePerformAsyncBlock)(MTRBaseDevice * baseDevice); +typedef NS_ENUM(NSUInteger, MTRInternalDeviceState) { + // Unsubscribed means we do not have a subscription and are not trying to set one up. + MTRInternalDeviceStateUnsubscribed = 0, + // Subscribing means we are actively trying to establish our initial subscription (e.g. doing + // DNS-SD discovery, trying to establish CASE to the peer, getting priming reports, etc). + MTRInternalDeviceStateSubscribing = 1, + // InitialSubscriptionEstablished means we have at some point finished setting up a + // subscription. That subscription may have dropped since then, but if so it's the ReadClient's + // responsibility to re-establish it. + MTRInternalDeviceStateInitialSubscriptionEstablished = 2, + // Resubscribing means we had established a subscription, but then + // detected a subscription drop due to not receiving a report on time. This + // covers all the actions that happen when re-subscribing (discovery, CASE, + // getting priming reports, etc). + MTRInternalDeviceStateResubscribing = 3, + // LaterSubscriptionEstablished meant that we had a subscription drop and + // then re-created a subscription. + MTRInternalDeviceStateLaterSubscriptionEstablished = 4, +}; + /** * Information about a cluster: data version and known attribute values. */ @@ -94,6 +115,8 @@ MTR_TESTABLE - (NSUInteger)unitTestAttributeCount; #endif +- (void)setStorageBehaviorConfiguration:(MTRDeviceStorageBehaviorConfiguration *)storageBehaviorConfiguration; + @end #pragma mark - Utility for clamping numbers @@ -101,4 +124,12 @@ MTR_TESTABLE // Returns min or max, if it is below or above, respectively. NSNumber * MTRClampedNumber(NSNumber * aNumber, NSNumber * min, NSNumber * max); +#pragma mark - Constants + +static NSString * const kDefaultSubscriptionPoolSizeOverrideKey = @"subscriptionPoolSizeOverride"; +static NSString * const kTestStorageUserDefaultEnabledKey = @"enableTestStorage"; + +// Declared inside platform, but noting here for reference +// static NSString * const kSRPTimeoutInMsecsUserDefaultKey = @"SRPTimeoutInMSecsOverride"; + NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.mm b/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.mm index 782ff30ba5763f..d412f5a497c3ce 100644 --- a/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.mm +++ b/src/darwin/Framework/CHIP/MTRDiagnosticLogsDownloader.mm @@ -399,7 +399,7 @@ - (void)downloadLogFromNodeWithID:(NSNumber *)nodeID if (timeout <= 0) { timeoutInSeconds = 0; } else if (timeout > UINT16_MAX) { - MTR_LOG_INFO("Warning: timeout is too large. It will be truncated to UINT16_MAX."); + MTR_LOG("Warning: timeout is too large. It will be truncated to UINT16_MAX."); timeoutInSeconds = UINT16_MAX; } else { timeoutInSeconds = static_cast(timeout); @@ -448,7 +448,7 @@ - (void)handleBDXTransferSessionBeginForFileDesignator:(NSString *)fileDesignato abortHandler:(AbortHandler)abortHandler; { assertChipStackLockedByCurrentThread(); - MTR_LOG_DEFAULT("BDX Transfer Session Begin: %@", fileDesignator); + MTR_LOG("BDX Transfer Session Begin: %@", fileDesignator); auto * download = [_downloads get:fileDesignator fabricIndex:fabricIndex nodeID:nodeID]; VerifyOrReturn(nil != download, completion([MTRError errorForCHIPErrorCode:CHIP_ERROR_NOT_FOUND])); @@ -464,7 +464,7 @@ - (void)handleBDXTransferSessionDataForFileDesignator:(NSString *)fileDesignator completion:(MTRStatusCompletion)completion { assertChipStackLockedByCurrentThread(); - MTR_LOG_DEFAULT("BDX Transfer Session Data: %@: %@", fileDesignator, data); + MTR_LOG("BDX Transfer Session Data: %@: %@", fileDesignator, data); auto * download = [_downloads get:fileDesignator fabricIndex:fabricIndex nodeID:nodeID]; VerifyOrReturn(nil != download, completion([MTRError errorForCHIPErrorCode:CHIP_ERROR_NOT_FOUND])); @@ -482,7 +482,7 @@ - (void)handleBDXTransferSessionEndForFileDesignator:(NSString *)fileDesignator error:(NSError * _Nullable)error { assertChipStackLockedByCurrentThread(); - MTR_LOG_DEFAULT("BDX Transfer Session End: %@: %@", fileDesignator, error); + MTR_LOG("BDX Transfer Session End: %@: %@", fileDesignator, error); auto * download = [_downloads get:fileDesignator fabricIndex:fabricIndex nodeID:nodeID]; VerifyOrReturn(nil != download); diff --git a/src/darwin/Framework/CHIP/MTRLogging_Internal.h b/src/darwin/Framework/CHIP/MTRLogging_Internal.h index 9b2d2fce418822..99892acf9631df 100644 --- a/src/darwin/Framework/CHIP/MTRLogging_Internal.h +++ b/src/darwin/Framework/CHIP/MTRLogging_Internal.h @@ -20,7 +20,6 @@ #include +#define MTR_LOG(msg, ...) ChipLogProgress(NotSpecified, msg, ##__VA_ARGS__) #define MTR_LOG_ERROR(msg, ...) ChipLogError(NotSpecified, msg, ##__VA_ARGS__) -#define MTR_LOG_DEFAULT(msg, ...) ChipLogProgress(NotSpecified, msg, ##__VA_ARGS__) -#define MTR_LOG_INFO(msg, ...) ChipLogDetail(NotSpecified, msg, ##__VA_ARGS__) #define MTR_LOG_DEBUG(msg, ...) ChipLogDetail(NotSpecified, msg, ##__VA_ARGS__) // same as INFO diff --git a/src/darwin/Framework/CHIP/MTRMetricsCollector.mm b/src/darwin/Framework/CHIP/MTRMetricsCollector.mm index 0927da858f1298..92a9ab4b6d373c 100644 --- a/src/darwin/Framework/CHIP/MTRMetricsCollector.mm +++ b/src/darwin/Framework/CHIP/MTRMetricsCollector.mm @@ -113,7 +113,7 @@ - (void)unregisterTracingBackend; void StartupMetricsCollection() { if ([MTRMetricsCollector sharedInstance]) { - MTR_LOG_INFO("Initialized metrics collection backend for Darwin"); + MTR_LOG("Initialized metrics collection backend for Darwin"); [[MTRMetricsCollector sharedInstance] registerTracingBackend]; } @@ -167,7 +167,7 @@ - (void)registerTracingBackend // Register only once if (!_tracingBackendRegistered) { chip::Tracing::Register(_tracingBackend); - MTR_LOG_INFO("Registered tracing backend with the registry"); + MTR_LOG("Registered tracing backend with the registry"); _tracingBackendRegistered = TRUE; } } @@ -179,7 +179,7 @@ - (void)unregisterTracingBackend // Unregister only if registered before if (_tracingBackendRegistered) { chip::Tracing::Unregister(_tracingBackend); - MTR_LOG_INFO("Unregistered tracing backend with the registry"); + MTR_LOG("Unregistered tracing backend with the registry"); _tracingBackendRegistered = FALSE; } } diff --git a/src/darwin/Framework/CHIP/MTRThreadOperationalDataset.mm b/src/darwin/Framework/CHIP/MTRThreadOperationalDataset.mm index a547321d88c2e0..a9d1fcdc18ba67 100644 --- a/src/darwin/Framework/CHIP/MTRThreadOperationalDataset.mm +++ b/src/darwin/Framework/CHIP/MTRThreadOperationalDataset.mm @@ -102,7 +102,7 @@ - (BOOL)_populateCppOperationalDataset - (BOOL)_checkDataLength:(NSData *)data expectedLength:(size_t)expectedLength { if (data.length != expectedLength) { - MTR_LOG_ERROR("Length Check Failed. Length:%tu is incorrect, must be %tu", data.length, expectedLength); + MTR_LOG_ERROR("Length Check Failed. Length:%lu is incorrect, must be %tu", static_cast(data.length), expectedLength); return NO; } return YES; diff --git a/src/darwin/Framework/CHIP/Matter.h b/src/darwin/Framework/CHIP/Matter.h index ebbe1b3765917c..3c0ec41443303c 100644 --- a/src/darwin/Framework/CHIP/Matter.h +++ b/src/darwin/Framework/CHIP/Matter.h @@ -47,6 +47,7 @@ #import #import #import +#import #import #import #import diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute.mm b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute.mm index d4bf1c7b9cd595..619ca98b2f4b47 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute.mm +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute.mm @@ -136,14 +136,14 @@ - (BOOL)setValueInternal:(NSDictionary *)value logIfNotAssociate _value = [value copy]; - MTR_LOG_DEFAULT("Attribute value updated: %@", [self _descriptionWhileLocked]); // Logs new value as part of our description. + MTR_LOG("Attribute value updated: %@", [self _descriptionWhileLocked]); // Logs new value as part of our description. MTRDeviceController * deviceController = _deviceController; if (deviceController == nil) { // We're not bound to a controller, so safe to directly update // _serializedValue. if (logIfNotAssociated) { - MTR_LOG_DEFAULT("Not publishing value for attribute " ChipLogFormatMEI "; not bound to a controller", + MTR_LOG("Not publishing value for attribute " ChipLogFormatMEI "; not bound to a controller", ChipLogValueMEI(static_cast(_attributeID.unsignedLongLongValue))); } _serializedValue = serializedValue; @@ -181,7 +181,7 @@ - (BOOL)associateWithController:(nullable MTRDeviceController *)controller _deviceController = controller; - MTR_LOG_DEFAULT("Associated %@ with controller", [self _descriptionWhileLocked]); + MTR_LOG("Associated %@ with controller", [self _descriptionWhileLocked]); return YES; } diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm index 02b5605e8c3388..e00bd10b5dc6c7 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm @@ -346,7 +346,7 @@ - (BOOL)associateWithController:(nullable MTRDeviceController *)controller _deviceController = controller; - MTR_LOG_DEFAULT("Associated %@, attribute count %llu, with controller", [self _descriptionWhileLocked], + MTR_LOG("Associated %@, attribute count %llu, with controller", [self _descriptionWhileLocked], static_cast(attributeCount)); return YES; diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm index 44da6a6cb6016a..d9e54bce58d395 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm @@ -311,7 +311,7 @@ - (BOOL)finishAssociationWithController:(nullable MTRDeviceController *)controll _deviceController = controller; - MTR_LOG_DEFAULT("Associated %@, cluster count %llu, with controller", + MTR_LOG("Associated %@, cluster count %llu, with controller", self, static_cast(clusterCount)); return YES; diff --git a/src/darwin/Framework/CHIPTests/MTRCommissionableBrowserTests.m b/src/darwin/Framework/CHIPTests/MTRCommissionableBrowserTests.m index 4253625af80ff7..047ce4f8588816 100644 --- a/src/darwin/Framework/CHIPTests/MTRCommissionableBrowserTests.m +++ b/src/darwin/Framework/CHIPTests/MTRCommissionableBrowserTests.m @@ -34,13 +34,8 @@ static const uint16_t kTestProductId2 = 0x8001u; static const uint16_t kTestDiscriminator1 = 3840u; static const uint16_t kTestDiscriminator2 = 3839u; -static const uint16_t kTestDiscriminator3 = 101u; -static const uint16_t kTestDiscriminator4 = 102u; -static const uint16_t kTestDiscriminator5 = 103u; -static const uint16_t kTestDiscriminator6 = 104u; -static const uint16_t kTestDiscriminator7 = 105u; static const uint16_t kDiscoverDeviceTimeoutInSeconds = 10; -static const uint16_t kExpectedDiscoveredDevicesCount = 7; +static const uint16_t kExpectedDiscoveredDevicesCount = 2; // Singleton controller we use. static MTRDeviceController * sController = nil; @@ -102,7 +97,7 @@ - (void)controller:(MTRDeviceController *)controller didFindCommissionableDevice XCTAssertEqual(instanceName.length, 16); // The instance name is random, so just ensure the len is right. XCTAssertEqualObjects(vendorId, @(kTestVendorId)); XCTAssertTrue([productId isEqual:@(kTestProductId1)] || [productId isEqual:@(kTestProductId2)]); - XCTAssertTrue([discriminator isEqual:@(kTestDiscriminator1)] || [discriminator isEqual:@(kTestDiscriminator2)] || [discriminator isEqual:@(kTestDiscriminator3)] || [discriminator isEqual:@(kTestDiscriminator4)] || [discriminator isEqual:@(kTestDiscriminator5)] || [discriminator isEqual:@(kTestDiscriminator6)] || [discriminator isEqual:@(kTestDiscriminator7)]); + XCTAssertTrue([discriminator isEqual:@(kTestDiscriminator1)] || [discriminator isEqual:@(kTestDiscriminator2)]); XCTAssertEqual(commissioningMode, YES); NSLog(@"Found Device (%@) with discriminator: %@ (vendor: %@, product: %@)", instanceName, discriminator, vendorId, productId); diff --git a/src/darwin/Framework/CHIPTests/MTRControllerTests.m b/src/darwin/Framework/CHIPTests/MTRControllerTests.m index 975625ea55df4b..436a0df230e3d9 100644 --- a/src/darwin/Framework/CHIPTests/MTRControllerTests.m +++ b/src/darwin/Framework/CHIPTests/MTRControllerTests.m @@ -1552,4 +1552,15 @@ - (void)testControllerCATs XCTAssertFalse([factory isRunning]); } +- (void)testSetMRPParameters +{ + // Can be called before starting the factory + XCTAssertFalse(MTRDeviceControllerFactory.sharedInstance.running); + MTRSetMessageReliabilityParameters(@2000, @2000, @2000, @2000); + + // Now reset back to the default state, so timings in other tests are not + // affected. + MTRSetMessageReliabilityParameters(nil, nil, nil, nil); +} + @end diff --git a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m index 1c2d5ddfe39a9a..f0d6aee1a8556b 100644 --- a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m +++ b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m @@ -26,6 +26,7 @@ #import "MTRCommandPayloadExtensions_Internal.h" #import "MTRDeviceControllerLocalTestStorage.h" +#import "MTRDeviceStorageBehaviorConfiguration.h" #import "MTRDeviceTestDelegate.h" #import "MTRDevice_Internal.h" #import "MTRErrorTestUtils.h" @@ -35,6 +36,7 @@ #import "MTRTestStorage.h" #import // For INFINITY +#import // system dependencies #import @@ -3023,8 +3025,21 @@ - (void)test031_MTRDeviceAttributeCacheLocalTestStorage // Get the subscription primed __auto_type * device = [MTRDevice deviceWithNodeID:@(kDeviceId) controller:sController]; + + NSTimeInterval baseTestDelayTime = 1; + MTRDeviceStorageBehaviorConfiguration * config = [MTRDeviceStorageBehaviorConfiguration + configurationWithReportToPersistenceDelayTime:baseTestDelayTime + reportToPersistenceDelayTimeMax:baseTestDelayTime * 2 + recentReportTimesMaxCount:5 + timeBetweenReportsTooShortThreshold:baseTestDelayTime * 0.4 + timeBetweenReportsTooShortMinThreshold:baseTestDelayTime * 0.2 + reportToPersistenceDelayMaxMultiplier:baseTestDelayTime * 5 + deviceReportingExcessivelyIntervalThreshold:baseTestDelayTime * 10]; + [device setStorageBehaviorConfiguration:config]; + XCTestExpectation * gotReportsExpectation = [self expectationWithDescription:@"Attribute and Event reports have been received"]; XCTestExpectation * gotDeviceCachePrimed = [self expectationWithDescription:@"Device cache primed for the first time"]; + XCTestExpectation * gotClusterDataPersisted1 = [self expectationWithDescription:@"Cluster data persisted 1"]; __auto_type * delegate = [[MTRDeviceTestDelegate alloc] init]; __weak __auto_type weakDelegate = delegate; delegate.onReportEnd = ^{ @@ -3035,9 +3050,12 @@ - (void)test031_MTRDeviceAttributeCacheLocalTestStorage delegate.onDeviceCachePrimed = ^{ [gotDeviceCachePrimed fulfill]; }; + delegate.onClusterDataPersisted = ^{ + [gotClusterDataPersisted1 fulfill]; + }; [device setDelegate:delegate queue:queue]; - [self waitForExpectations:@[ gotReportsExpectation, gotDeviceCachePrimed ] timeout:60]; + [self waitForExpectations:@[ gotReportsExpectation, gotDeviceCachePrimed, gotClusterDataPersisted1 ] timeout:60]; NSUInteger attributesReportedWithFirstSubscription = [device unitTestAttributesReportedSinceLastCheck]; @@ -3049,6 +3067,7 @@ - (void)test031_MTRDeviceAttributeCacheLocalTestStorage device = [MTRDevice deviceWithNodeID:@(kDeviceId) controller:sController]; XCTestExpectation * resubGotReportsExpectation = [self expectationWithDescription:@"Attribute and Event reports have been received for resubscription"]; + XCTestExpectation * gotClusterDataPersisted2 = [self expectationWithDescription:@"Cluster data persisted 2"]; delegate.onReportEnd = ^{ [resubGotReportsExpectation fulfill]; __strong __auto_type strongDelegate = weakDelegate; @@ -3058,9 +3077,12 @@ - (void)test031_MTRDeviceAttributeCacheLocalTestStorage delegate.onDeviceCachePrimed = ^{ onDeviceCachePrimedCalled = YES; }; + delegate.onClusterDataPersisted = ^{ + [gotClusterDataPersisted2 fulfill]; + }; [device setDelegate:delegate queue:queue]; - [self waitForExpectations:@[ resubGotReportsExpectation ] timeout:60]; + [self waitForExpectations:@[ resubGotReportsExpectation, gotClusterDataPersisted2 ] timeout:60]; // Make sure that the new callback is only ever called once, the first time subscription was primed XCTAssertFalse(onDeviceCachePrimedCalled); @@ -3618,6 +3640,271 @@ - (void)test034_TestMTRDeviceHistoricalEvents XCTAssertTrue(eventReportsReceived > 0); } +- (void)test035_TestMTRDeviceSubscriptionNotEstablishedOverXPC +{ + NSString * const MTRDeviceControllerId = @"MTRController"; + __auto_type remoteController = [MTRDeviceController + sharedControllerWithID:MTRDeviceControllerId + xpcConnectBlock:^NSXPCConnection * _Nonnull { + return nil; + }]; + + __auto_type * device = [MTRDevice deviceWithNodeID:kDeviceId deviceController:remoteController]; + dispatch_queue_t queue = dispatch_get_main_queue(); + + XCTestExpectation * subscriptionExpectation = [self expectationWithDescription:@"Subscription has been set up"]; + subscriptionExpectation.inverted = YES; + + __auto_type * delegate = [[MTRDeviceTestDelegate alloc] init]; + + XCTAssertEqual([device _getInternalState], MTRInternalDeviceStateUnsubscribed); + + delegate.onAttributeDataReceived = ^(NSArray *> * attributeReport) { + [subscriptionExpectation fulfill]; + }; + + [device setDelegate:delegate queue:queue]; + [self waitForExpectations:@[ subscriptionExpectation ] timeout:5]; + + XCTAssertEqual([device _getInternalState], MTRInternalDeviceStateUnsubscribed); +} + +- (NSArray *> *)_testAttributeReportWithValue:(unsigned int)testValue +{ + return @[ @{ + MTRAttributePathKey : [MTRAttributePath attributePathWithEndpointID:@(0) clusterID:@(MTRClusterIDTypeLevelControlID) attributeID:@(MTRAttributeIDTypeClusterLevelControlAttributeCurrentLevelID)], + MTRDataKey : @ { + MTRDataVersionKey : @(testValue), + MTRTypeKey : MTRUnsignedIntegerValueType, + MTRValueKey : @(testValue), + } + } ]; +} + +- (void)test036_TestStorageBehaviorConfiguration +{ + // Use separate queue for timing sensitive test + dispatch_queue_t queue = dispatch_queue_create("storage-behavior-queue", DISPATCH_QUEUE_SERIAL); + + NSDictionary * storedClusterDataAfterClear = [sController.controllerDataStore getStoredClusterDataForNodeID:@(kDeviceId)]; + XCTAssertEqual(storedClusterDataAfterClear.count, 0); + + __auto_type * device = [MTRDevice deviceWithNodeID:kDeviceId deviceController:sController]; + + __auto_type * delegate = [[MTRDeviceTestDelegateWithSubscriptionSetupOverride alloc] init]; + __block os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; + __block NSDate * reportEndTime = nil; + __block NSDate * dataPersistedTime = nil; + + XCTestExpectation * dataPersistedInitial = [self expectationWithDescription:@"data persisted initial"]; + delegate.onReportEnd = ^() { + os_unfair_lock_lock(&lock); + if (!reportEndTime) { + reportEndTime = [NSDate now]; + } + os_unfair_lock_unlock(&lock); + }; + + delegate.onClusterDataPersisted = ^{ + os_unfair_lock_lock(&lock); + if (!dataPersistedTime) { + dataPersistedTime = [NSDate now]; + } + os_unfair_lock_unlock(&lock); + [dataPersistedInitial fulfill]; + }; + + // Do not subscribe - only inject sequence of reports to control the timing + delegate.skipSetupSubscription = YES; + + NSTimeInterval baseTestDelayTime = 3; + + // Set up a config of relatively short timers so this test doesn't take too long + MTRDeviceStorageBehaviorConfiguration * config = [MTRDeviceStorageBehaviorConfiguration + configurationWithReportToPersistenceDelayTime:baseTestDelayTime + reportToPersistenceDelayTimeMax:baseTestDelayTime * 2 + recentReportTimesMaxCount:5 + timeBetweenReportsTooShortThreshold:baseTestDelayTime * 0.4 + timeBetweenReportsTooShortMinThreshold:baseTestDelayTime * 0.2 + reportToPersistenceDelayMaxMultiplier:baseTestDelayTime * 5 + deviceReportingExcessivelyIntervalThreshold:baseTestDelayTime * 7]; + [device setStorageBehaviorConfiguration:config]; + + [device setDelegate:delegate queue:queue]; + + // Use a counter that will be incremented for each report as the value. + unsigned int currentTestValue = 1; + + // Initial setup: Inject report and see that the attribute persisted. No delay is + // expected for the first (priming) report. + [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES]; + + [self waitForExpectations:@[ dataPersistedInitial ] timeout:60]; + + XCTestExpectation * dataPersisted1 = [self expectationWithDescription:@"data persisted 1"]; + delegate.onClusterDataPersisted = ^{ + os_unfair_lock_lock(&lock); + if (!dataPersistedTime) { + dataPersistedTime = [NSDate now]; + } + os_unfair_lock_unlock(&lock); + [dataPersisted1 fulfill]; + }; + + // Test 1: Inject report and see that the attribute persisted, with a delay + reportEndTime = nil; + dataPersistedTime = nil; + [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES]; + + [self waitForExpectations:@[ dataPersisted1 ] timeout:60]; + + os_unfair_lock_lock(&lock); + NSTimeInterval reportToPersistenceDelay = [dataPersistedTime timeIntervalSinceDate:reportEndTime]; + os_unfair_lock_unlock(&lock); + // Check delay exists + XCTAssertGreaterThan(reportToPersistenceDelay, baseTestDelayTime / 2); + // Check delay is expectd - use base delay plus small fudge in case of CPU slowness with dispatch_after + XCTAssertLessThan(reportToPersistenceDelay, baseTestDelayTime * 1.3); + + XCTestExpectation * dataPersisted2 = [self expectationWithDescription:@"data persisted 2"]; + + delegate.onClusterDataPersisted = ^{ + os_unfair_lock_lock(&lock); + if (!dataPersistedTime) { + dataPersistedTime = [NSDate now]; + } + os_unfair_lock_unlock(&lock); + [dataPersisted2 fulfill]; + }; + + // Test 2: Inject multiple reports with delay and see that the attribute persisted eventually + reportEndTime = nil; + dataPersistedTime = nil; + [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES]; + + double frequentReportMultiplier = 0.5; + usleep((useconds_t) (baseTestDelayTime * frequentReportMultiplier * USEC_PER_SEC)); + [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES]; + + usleep((useconds_t) (baseTestDelayTime * frequentReportMultiplier * USEC_PER_SEC)); + [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES]; + + usleep((useconds_t) (baseTestDelayTime * frequentReportMultiplier * USEC_PER_SEC)); + [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES]; + + usleep((useconds_t) (baseTestDelayTime * frequentReportMultiplier * USEC_PER_SEC)); + [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES]; + + // At this point, the threshold for reportToPersistenceDelayTimeMax should have hit, and persistence + // should have happened with timer running down to persist again with the 5th report above. Need to + // wait for expectation and immediately clear the onClusterDataPersisted callback + + [self waitForExpectations:@[ dataPersisted2 ] timeout:60]; + + os_unfair_lock_lock(&lock); + reportToPersistenceDelay = [dataPersistedTime timeIntervalSinceDate:reportEndTime]; + os_unfair_lock_unlock(&lock); + // Check delay exists and approximately reportToPersistenceDelayTimeMax, which is base delay times 2 + XCTAssertGreaterThan(reportToPersistenceDelay, baseTestDelayTime * 2 * 0.9); + XCTAssertLessThan(reportToPersistenceDelay, baseTestDelayTime * 2 * 1.3); // larger upper limit in case machine is slow + + delegate.onClusterDataPersisted = nil; + + // sleep the base delay interval to allow the onClusterDataPersisted callback to happen. + usleep((useconds_t) (baseTestDelayTime * 1.1 * USEC_PER_SEC)); + + // Test 3: test reporting frequently, and see that the delay time increased + reportEndTime = nil; + dataPersistedTime = nil; + XCTestExpectation * dataPersisted3 = [self expectationWithDescription:@"data persisted 3"]; + delegate.onClusterDataPersisted = ^{ + os_unfair_lock_lock(&lock); + if (!dataPersistedTime) { + dataPersistedTime = [NSDate now]; + } + os_unfair_lock_unlock(&lock); + [dataPersisted3 fulfill]; + }; + + // Set report times with short delay and check that the multiplier is engaged + [device unitTestSetMostRecentReportTimes:[NSMutableArray arrayWithArray:@[ + [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.3 * 4)], + [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.3 * 3)], + [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.3 * 2)], + [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.3)], + ]]]; + + // Inject final report that makes MTRDevice recalculate delay with multiplier + [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES]; + + [self waitForExpectations:@[ dataPersisted3 ] timeout:60]; + + // 0.3 is between 0.4 and 0.2, which should get us at least 50% of the multiplier. + // The multiplier is 5, which is +400% of the base delay, and so 50% of the multiplier + // is +200% of the base delay, meaning 3x the base delay. + + os_unfair_lock_lock(&lock); + reportToPersistenceDelay = [dataPersistedTime timeIntervalSinceDate:reportEndTime]; + os_unfair_lock_unlock(&lock); + // Check delay exists and at least base delay times 3 + XCTAssertGreaterThan(reportToPersistenceDelay, baseTestDelayTime * 3 * 0.9); + // upper limit at most max delay times full multiplier + extra in case machine is slow + XCTAssertLessThan(reportToPersistenceDelay, baseTestDelayTime * 2 * 5 * 1.3); + + // Test 4: test reporting excessively, and see that persistence does not happen until + // reporting frequency goes back above the threshold + reportEndTime = nil; + dataPersistedTime = nil; + XCTestExpectation * dataPersisted4 = [self expectationWithDescription:@"data persisted 4"]; + delegate.onClusterDataPersisted = ^{ + os_unfair_lock_lock(&lock); + if (!dataPersistedTime) { + dataPersistedTime = [NSDate now]; + } + os_unfair_lock_unlock(&lock); + [dataPersisted4 fulfill]; + }; + + // Set report times with short delay and check that the multiplier is engaged + [device unitTestSetMostRecentReportTimes:[NSMutableArray arrayWithArray:@[ + [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.1 * 4)], + [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.1 * 3)], + [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.1 * 2)], + [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 0.1)], + ]]]; + + // Inject report that makes MTRDevice detect the device is reporting excessively + [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES]; + + // Now keep reporting excessively for base delay time max times max multiplier, plus a bit more + NSDate * excessiveStartTime = [NSDate now]; + for (;;) { + usleep((useconds_t) (baseTestDelayTime * 0.1 * USEC_PER_SEC)); + [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES]; + NSTimeInterval elapsed = -[excessiveStartTime timeIntervalSinceNow]; + if (elapsed > (baseTestDelayTime * 2 * 5 * 1.2)) { + break; + } + } + + // Check that persistence has not happened because it's now turned off + XCTAssertNil(dataPersistedTime); + + // Now force report times to large number, to simulate time passage + [device unitTestSetMostRecentReportTimes:[NSMutableArray arrayWithArray:@[ + [NSDate dateWithTimeIntervalSinceNow:-(baseTestDelayTime * 10)], + ]]]; + + // And inject a report to trigger MTRDevice to recalculate that this device is no longer + // reporting excessively + [device unitTestInjectAttributeReport:[self _testAttributeReportWithValue:currentTestValue++] fromSubscription:YES]; + + [self waitForExpectations:@[ dataPersisted4 ] timeout:60]; + + delegate.onReportEnd = nil; + delegate.onClusterDataPersisted = nil; +} + @end @interface MTRDeviceEncoderTests : XCTestCase diff --git a/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m b/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m index 6c567443f9afec..94808102b2519d 100644 --- a/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m +++ b/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m @@ -19,6 +19,7 @@ #import #import "MTRDeviceControllerLocalTestStorage.h" +#import "MTRDeviceStorageBehaviorConfiguration.h" #import "MTRDeviceTestDelegate.h" #import "MTRDevice_Internal.h" #import "MTRErrorTestUtils.h" @@ -28,6 +29,7 @@ #import "MTRTestKeys.h" #import "MTRTestPerControllerStorage.h" #import "MTRTestResetCommissioneeHelper.h" +#import "MTRTestServerAppRunner.h" static const uint16_t kPairingTimeoutInSeconds = 10; static const uint16_t kTimeoutInSeconds = 3; @@ -259,10 +261,12 @@ - (nullable MTRDeviceController *)startControllerWithRootKeys:(MTRTestKeys *)roo fabricID:(NSNumber *)fabricID nodeID:(NSNumber *)nodeID storage:(MTRTestPerControllerStorage *)storage - caseAuthenticatedTags:(nullable NSSet *)caseAuthenticatedTags + caseAuthenticatedTags:(NSSet * _Nullable)caseAuthenticatedTags error:(NSError * __autoreleasing *)error certificateIssuer: (MTRPerControllerStorageTestsCertificateIssuer * __autoreleasing *)certificateIssuer + concurrentSubscriptionPoolSize:(NSUInteger)concurrentSubscriptionPoolSize + storageBehaviorConfiguration:(MTRDeviceStorageBehaviorConfiguration * _Nullable)storageBehaviorConfiguration { XCTAssertTrue(error != NULL); @@ -307,9 +311,52 @@ - (nullable MTRDeviceController *)startControllerWithRootKeys:(MTRTestKeys *)roo [params setOperationalCertificateIssuer:ourCertificateIssuer queue:dispatch_get_main_queue()]; + if (concurrentSubscriptionPoolSize > 0) { + params.concurrentSubscriptionEstablishmentsAllowedOnThread = concurrentSubscriptionPoolSize; + } + + if (storageBehaviorConfiguration) { + params.storageBehaviorConfiguration = storageBehaviorConfiguration; + } + return [[MTRDeviceController alloc] initWithParameters:params error:error]; } +- (nullable MTRDeviceController *)startControllerWithRootKeys:(MTRTestKeys *)rootKeys + operationalKeys:(MTRTestKeys *)operationalKeys + fabricID:(NSNumber *)fabricID + nodeID:(NSNumber *)nodeID + storage:(MTRTestPerControllerStorage *)storage + caseAuthenticatedTags:(nullable NSSet *)caseAuthenticatedTags + error:(NSError * __autoreleasing *)error + certificateIssuer: + (MTRPerControllerStorageTestsCertificateIssuer * __autoreleasing *)certificateIssuer +{ + return [self startControllerWithRootKeys:rootKeys operationalKeys:operationalKeys fabricID:fabricID nodeID:nodeID storage:storage caseAuthenticatedTags:caseAuthenticatedTags error:error certificateIssuer:certificateIssuer concurrentSubscriptionPoolSize:0 storageBehaviorConfiguration:nil]; +} + +- (nullable MTRDeviceController *)startControllerWithRootKeys:(MTRTestKeys *)rootKeys + operationalKeys:(MTRTestKeys *)operationalKeys + fabricID:(NSNumber *)fabricID + nodeID:(NSNumber *)nodeID + storage:(MTRTestPerControllerStorage *)storage + error:(NSError * __autoreleasing *)error + certificateIssuer: + (MTRPerControllerStorageTestsCertificateIssuer * __autoreleasing *)certificateIssuer + concurrentSubscriptionPoolSize:(NSUInteger)concurrentSubscriptionPoolSize +{ + return [self startControllerWithRootKeys:rootKeys + operationalKeys:operationalKeys + fabricID:fabricID + nodeID:nodeID + storage:storage + caseAuthenticatedTags:nil + error:error + certificateIssuer:certificateIssuer + concurrentSubscriptionPoolSize:concurrentSubscriptionPoolSize + storageBehaviorConfiguration:nil]; +} + - (nullable MTRDeviceController *)startControllerWithRootKeys:(MTRTestKeys *)rootKeys operationalKeys:(MTRTestKeys *)operationalKeys fabricID:(NSNumber *)fabricID @@ -318,6 +365,7 @@ - (nullable MTRDeviceController *)startControllerWithRootKeys:(MTRTestKeys *)roo error:(NSError * __autoreleasing *)error certificateIssuer: (MTRPerControllerStorageTestsCertificateIssuer * __autoreleasing *)certificateIssuer + storageBehaviorConfiguration:(MTRDeviceStorageBehaviorConfiguration * _Nullable)storageBehaviorConfiguration { return [self startControllerWithRootKeys:rootKeys operationalKeys:operationalKeys @@ -326,7 +374,30 @@ - (nullable MTRDeviceController *)startControllerWithRootKeys:(MTRTestKeys *)roo storage:storage caseAuthenticatedTags:nil error:error - certificateIssuer:certificateIssuer]; + certificateIssuer:certificateIssuer + concurrentSubscriptionPoolSize:0 + storageBehaviorConfiguration:storageBehaviorConfiguration]; +} + +- (nullable MTRDeviceController *)startControllerWithRootKeys:(MTRTestKeys *)rootKeys + operationalKeys:(MTRTestKeys *)operationalKeys + fabricID:(NSNumber *)fabricID + nodeID:(NSNumber *)nodeID + storage:(MTRTestPerControllerStorage *)storage + error:(NSError * __autoreleasing *)error + certificateIssuer: + (MTRPerControllerStorageTestsCertificateIssuer * __autoreleasing *)certificateIssuer +{ + return [self startControllerWithRootKeys:rootKeys + operationalKeys:operationalKeys + fabricID:fabricID + nodeID:nodeID + storage:storage + caseAuthenticatedTags:nil + error:error + certificateIssuer:certificateIssuer + concurrentSubscriptionPoolSize:0 + storageBehaviorConfiguration:nil]; } - (nullable MTRDeviceController *)startControllerWithRootKeys:(MTRTestKeys *)rootKeys @@ -344,7 +415,9 @@ - (nullable MTRDeviceController *)startControllerWithRootKeys:(MTRTestKeys *)roo storage:storage caseAuthenticatedTags:caseAuthenticatedTags error:error - certificateIssuer:nil]; + certificateIssuer:nil + concurrentSubscriptionPoolSize:0 + storageBehaviorConfiguration:nil]; } - (nullable MTRDeviceController *)startControllerWithRootKeys:(MTRTestKeys *)rootKeys @@ -1315,7 +1388,7 @@ - (void)test008_TestDataStoreDirect XCTAssertFalse([controller isRunning]); } -- (void)doDataStoreMTRDeviceTestWithStorageDelegate:(id)storageDelegate +- (void)doDataStoreMTRDeviceTestWithStorageDelegate:(id)storageDelegate disableStorageBehaviorOptimization:(BOOL)disableStorageBehaviorOptimization { __auto_type * factory = [MTRDeviceControllerFactory sharedInstance]; XCTAssertNotNil(factory); @@ -1334,13 +1407,18 @@ - (void)doDataStoreMTRDeviceTestWithStorageDelegate:(id * dataStoreClusterData = [controller.controllerDataStore getStoredClusterDataForNodeID:deviceID]; @@ -1413,32 +1498,44 @@ - (void)doDataStoreMTRDeviceTestWithStorageDelegate:(id *)deviceOnboardingPayloads { __auto_type * factory = [MTRDeviceControllerFactory sharedInstance]; XCTAssertNotNil(factory); @@ -2059,12 +2178,7 @@ - (void)doTestSubscriptionPoolWithSize:(NSInteger)subscriptionPoolSize NSError * error; - NSUserDefaults * defaults = [[NSUserDefaults alloc] initWithSuiteName:kLocalTestUserDefaultDomain]; - NSNumber * subscriptionPoolSizeOverrideOriginalValue = [defaults objectForKey:kLocalTestUserDefaultSubscriptionPoolSizeOverrideKey]; - // Test DeviceController with a Subscription pool - [defaults setInteger:subscriptionPoolSize forKey:kLocalTestUserDefaultSubscriptionPoolSizeOverrideKey]; - MTRPerControllerStorageTestsCertificateIssuer * certificateIssuer; MTRDeviceController * controller = [self startControllerWithRootKeys:rootKeys operationalKeys:operationalKeys @@ -2072,22 +2186,15 @@ - (void)doTestSubscriptionPoolWithSize:(NSInteger)subscriptionPoolSize nodeID:nodeID storage:storageDelegate error:&error - certificateIssuer:&certificateIssuer]; + certificateIssuer:&certificateIssuer + concurrentSubscriptionPoolSize:subscriptionPoolSize]; XCTAssertNil(error); XCTAssertNotNil(controller); XCTAssertTrue([controller isRunning]); XCTAssertEqualObjects(controller.controllerNodeID, nodeID); - // QRCodes generated for discriminators 101~105 and passcodes 1001~1005 NSArray * orderedDeviceIDs = @[ @(101), @(102), @(103), @(104), @(105) ]; - NSDictionary * deviceOnboardingPayloads = @{ - @(101) : @"MT:00000EBQ15IZC900000", - @(102) : @"MT:00000MNY16-AD900000", - @(103) : @"MT:00000UZ427GOD900000", - @(104) : @"MT:00000CQM00Z.D900000", - @(105) : @"MT:00000K0V01FDE900000", - }; // Commission 5 devices for (NSNumber * deviceID in orderedDeviceIDs) { @@ -2164,18 +2271,31 @@ - (void)doTestSubscriptionPoolWithSize:(NSInteger)subscriptionPoolSize [controller shutdown]; XCTAssertFalse([controller isRunning]); - - if (subscriptionPoolSizeOverrideOriginalValue) { - [defaults setInteger:subscriptionPoolSizeOverrideOriginalValue.integerValue forKey:kLocalTestUserDefaultSubscriptionPoolSizeOverrideKey]; - } else { - [defaults removeObjectForKey:kLocalTestUserDefaultSubscriptionPoolSizeOverrideKey]; - } } - (void)testSubscriptionPool { - [self doTestSubscriptionPoolWithSize:1]; - [self doTestSubscriptionPoolWithSize:2]; + // QRCodes generated for discriminators 1111~1115 and passcodes 1001~1005 + NSDictionary * deviceOnboardingPayloads = @{ + @(101) : @"MT:00000UZ427U0D900000", + @(102) : @"MT:00000CQM00BED900000", + @(103) : @"MT:00000K0V01TRD900000", + @(104) : @"MT:00000SC11293E900000", + @(105) : @"MT:00000-O913RGE900000", + }; + + // Start our helper apps. + __auto_type * sortedKeys = [[deviceOnboardingPayloads allKeys] sortedArrayUsingSelector:@selector(compare:)]; + for (NSNumber * deviceID in sortedKeys) { + __auto_type * appRunner = [[MTRTestServerAppRunner alloc] initWithAppName:@"all-clusters" + arguments:@[] + payload:deviceOnboardingPayloads[deviceID] + testcase:self]; + XCTAssertNotNil(appRunner); + } + + [self doTestSubscriptionPoolWithSize:1 deviceOnboardingPayloads:deviceOnboardingPayloads]; + [self doTestSubscriptionPoolWithSize:2 deviceOnboardingPayloads:deviceOnboardingPayloads]; } - (MTRDevice *)getMTRDevice:(NSNumber *)deviceID diff --git a/src/darwin/Framework/CHIPTests/TestHelpers/MTRDeviceTestDelegate.h b/src/darwin/Framework/CHIPTests/TestHelpers/MTRDeviceTestDelegate.h index 2492a184e2c69c..0ca5026a7c5e73 100644 --- a/src/darwin/Framework/CHIPTests/TestHelpers/MTRDeviceTestDelegate.h +++ b/src/darwin/Framework/CHIPTests/TestHelpers/MTRDeviceTestDelegate.h @@ -34,6 +34,11 @@ typedef void (^MTRDeviceTestDelegateDataHandler)(NSArray *> *)attributeReport fromSubscription:(BOOL)isFromSubscription; - (NSUInteger)unitTestAttributesReportedSinceLastCheck; - (void)unitTestClearClusterData; +- (MTRInternalDeviceState)_getInternalState; +- (void)unitTestSetReportToPersistenceDelayTime:(NSTimeInterval)reportToPersistenceDelayTime + reportToPersistenceDelayTimeMax:(NSTimeInterval)reportToPersistenceDelayTimeMax + recentReportTimesMaxCount:(NSUInteger)recentReportTimesMaxCount + timeBetweenReportsTooShortThreshold:(NSTimeInterval)timeBetweenReportsTooShortThreshold + timeBetweenReportsTooShortMinThreshold:(NSTimeInterval)timeBetweenReportsTooShortMinThreshold + reportToPersistenceDelayMaxMultiplier:(double)reportToPersistenceDelayMaxMultiplier + deviceReportingExcessivelyIntervalThreshold:(NSTimeInterval)deviceReportingExcessivelyIntervalThreshold; +- (void)unitTestSetMostRecentReportTimes:(NSMutableArray *)mostRecentReportTimes; @end #endif diff --git a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.m b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.m index 83912e9eb2dc0b..18424ed332a6af 100644 --- a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.m +++ b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.m @@ -90,7 +90,7 @@ - (instancetype)initWithAppName:(NSString *)name arguments:(NSArray [testcase launchTask:_appTask]; - NSLog(@"Started %@ with arguments %@ stdout=%@ and stderr=%@", name, allArguments, outFile, errorFile); + NSLog(@"Started chip-%@-app with arguments %@ stdout=%@ and stderr=%@", name, allArguments, outFile, errorFile); return self; #endif // HAVE_NSTASK diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj index 212c859610172b..df5949868d0012 100644 --- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj @@ -254,6 +254,8 @@ 75139A702B7FE68C00E3A919 /* MTRDeviceControllerLocalTestStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 75139A6D2B7FE5D600E3A919 /* MTRDeviceControllerLocalTestStorage.h */; settings = {ATTRIBUTES = (Private, ); }; }; 7534F12828BFF20300390851 /* MTRDeviceAttestationDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7534F12628BFF20300390851 /* MTRDeviceAttestationDelegate.mm */; }; 7534F12928BFF20300390851 /* MTRDeviceAttestationDelegate_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7534F12728BFF20300390851 /* MTRDeviceAttestationDelegate_Internal.h */; }; + 754784652BFE65CB0089C372 /* MTRDeviceStorageBehaviorConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 754784642BFE65CB0089C372 /* MTRDeviceStorageBehaviorConfiguration.mm */; }; + 754784672BFE93B00089C372 /* MTRDeviceStorageBehaviorConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 754784632BFE65B70089C372 /* MTRDeviceStorageBehaviorConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; 754F3DF427FBB94B00E60580 /* MTREventTLVValueDecoder_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 754F3DF327FBB94B00E60580 /* MTREventTLVValueDecoder_Internal.h */; }; 7560FD1C27FBBD3F005E85B3 /* MTREventTLVValueDecoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7560FD1B27FBBD3F005E85B3 /* MTREventTLVValueDecoder.mm */; }; 7596A83E28751220004DAE0E /* MTRBaseClusters_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7596A83D28751220004DAE0E /* MTRBaseClusters_Internal.h */; }; @@ -676,6 +678,9 @@ 75139A6E2B7FE5E900E3A919 /* MTRDeviceControllerLocalTestStorage.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRDeviceControllerLocalTestStorage.mm; sourceTree = ""; }; 7534F12628BFF20300390851 /* MTRDeviceAttestationDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRDeviceAttestationDelegate.mm; sourceTree = ""; }; 7534F12728BFF20300390851 /* MTRDeviceAttestationDelegate_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRDeviceAttestationDelegate_Internal.h; sourceTree = ""; }; + 754784632BFE65B70089C372 /* MTRDeviceStorageBehaviorConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRDeviceStorageBehaviorConfiguration.h; sourceTree = ""; }; + 754784642BFE65CB0089C372 /* MTRDeviceStorageBehaviorConfiguration.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRDeviceStorageBehaviorConfiguration.mm; sourceTree = ""; }; + 754784662BFE6B890089C372 /* MTRDeviceStorageBehaviorConfiguration_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRDeviceStorageBehaviorConfiguration_Internal.h; sourceTree = ""; }; 754F3DF327FBB94B00E60580 /* MTREventTLVValueDecoder_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTREventTLVValueDecoder_Internal.h; sourceTree = ""; }; 7560FD1B27FBBD3F005E85B3 /* MTREventTLVValueDecoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTREventTLVValueDecoder.mm; sourceTree = ""; }; 7596A83D28751220004DAE0E /* MTRBaseClusters_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRBaseClusters_Internal.h; sourceTree = ""; }; @@ -1330,6 +1335,9 @@ 5A6FEC9527B5983000F25F42 /* MTRDeviceControllerXPCConnection.mm */, 5A6FEC8B27B5609C00F25F42 /* MTRDeviceOverXPC.h */, 5A6FEC9727B5C6AF00F25F42 /* MTRDeviceOverXPC.mm */, + 754784632BFE65B70089C372 /* MTRDeviceStorageBehaviorConfiguration.h */, + 754784662BFE6B890089C372 /* MTRDeviceStorageBehaviorConfiguration_Internal.h */, + 754784642BFE65CB0089C372 /* MTRDeviceStorageBehaviorConfiguration.mm */, 51F522692AE70761000C4050 /* MTRDeviceTypeMetadata.h */, 5129BCFC26A9EE3300122DDF /* MTRError.h */, B2E0D7AB245B0B5C003C5B48 /* MTRError_Internal.h */, @@ -1593,6 +1601,7 @@ 7596A85728788557004DAE0E /* MTRClusters.h in Headers */, 99D466E12798936D0089A18F /* MTRCommissioningParameters.h in Headers */, 75B3269C2BCDB9D600E17C4E /* MTRDeviceConnectivityMonitor.h in Headers */, + 754784672BFE93B00089C372 /* MTRDeviceStorageBehaviorConfiguration.h in Headers */, 5136661528067D550025EDAE /* MTRDeviceControllerFactory_Internal.h in Headers */, 515C1C70284F9FFB00A48F0C /* MTRFramework.h in Headers */, 7534F12928BFF20300390851 /* MTRDeviceAttestationDelegate_Internal.h in Headers */, @@ -1979,6 +1988,7 @@ B289D4222639C0D300D4E314 /* MTROnboardingPayloadParser.mm in Sources */, 3CF134AD289D8E570017A19E /* MTRDeviceAttestationInfo.mm in Sources */, 2C1B027A2641DB4E00780EF1 /* MTROperationalCredentialsDelegate.mm in Sources */, + 754784652BFE65CB0089C372 /* MTRDeviceStorageBehaviorConfiguration.mm in Sources */, 7560FD1C27FBBD3F005E85B3 /* MTREventTLVValueDecoder.mm in Sources */, 5178E67E2AE098210069DF72 /* MTRCommandTimedCheck.mm in Sources */, 7596A84928762783004DAE0E /* MTRAsyncCallbackWorkQueue.mm in Sources */, diff --git a/src/lib/core/CHIPError.h b/src/lib/core/CHIPError.h index fe030670fea57d..aebf7804aefb10 100644 --- a/src/lib/core/CHIPError.h +++ b/src/lib/core/CHIPError.h @@ -132,7 +132,7 @@ class ChipError * The result is valid only if CanEncapsulate() is true. */ constexpr ChipError(Range range, ValueType value) : ChipError(range, value, /*file=*/nullptr, /*line=*/0) {} -#if __cplusplus >= 202002L +#if CHIP_CONFIG_ERROR_SOURCE && __cplusplus >= 202002L constexpr ChipError(Range range, ValueType value, const char * file, unsigned int line, std::source_location location = std::source_location::current()) : mError(MakeInteger(range, (value & MakeMask(0, kValueLength)))) CHIP_INITIALIZE_ERROR_SOURCE(file, line, location) @@ -141,7 +141,7 @@ class ChipError constexpr ChipError(Range range, ValueType value, const char * file, unsigned int line) : mError(MakeInteger(range, (value & MakeMask(0, kValueLength)))) CHIP_INITIALIZE_ERROR_SOURCE(file, line, /*loc=*/nullptr) {} -#endif // __cplusplus >= 202002L +#endif // CHIP_CONFIG_ERROR_SOURCE && __cplusplus >= 202002L /** * Construct a CHIP_ERROR for SdkPart @a part with @a code. @@ -150,7 +150,7 @@ class ChipError * The macro version CHIP_SDK_ERROR checks that the numeric value is constant and well-formed. */ constexpr ChipError(SdkPart part, uint8_t code) : ChipError(part, code, /*file=*/nullptr, /*line=*/0) {} -#if __cplusplus >= 202002L +#if CHIP_CONFIG_ERROR_SOURCE && __cplusplus >= 202002L constexpr ChipError(SdkPart part, uint8_t code, const char * file, unsigned int line, std::source_location location = std::source_location::current()) : mError(MakeInteger(part, code)) CHIP_INITIALIZE_ERROR_SOURCE(file, line, location) @@ -159,7 +159,7 @@ class ChipError constexpr ChipError(SdkPart part, uint8_t code, const char * file, unsigned int line) : mError(MakeInteger(part, code)) CHIP_INITIALIZE_ERROR_SOURCE(file, line, /*loc=*/nullptr) {} -#endif // __cplusplus >= 202002L +#endif // CHIP_CONFIG_ERROR_SOURCE && __cplusplus >= 202002L /** * Construct a CHIP_ERROR constant for SdkPart @a part with @a code at the current source line. @@ -180,7 +180,7 @@ class ChipError * This is intended to be used only in foreign function interfaces. */ explicit constexpr ChipError(StorageType error) : ChipError(error, /*file=*/nullptr, /*line=*/0) {} -#if __cplusplus >= 202002L +#if CHIP_CONFIG_ERROR_SOURCE && __cplusplus >= 202002L explicit constexpr ChipError(StorageType error, const char * file, unsigned int line, std::source_location location = std::source_location::current()) : mError(error) CHIP_INITIALIZE_ERROR_SOURCE(file, line, location) @@ -189,7 +189,7 @@ class ChipError explicit constexpr ChipError(StorageType error, const char * file, unsigned int line) : mError(error) CHIP_INITIALIZE_ERROR_SOURCE(file, line, /*loc=*/nullptr) {} -#endif // __cplusplus >= 202002L +#endif // CHIP_CONFIG_ERROR_SOURCE && __cplusplus >= 202002L #undef CHIP_INITIALIZE_ERROR_SOURCE diff --git a/src/lib/support/static_support_smart_ptr.h b/src/lib/support/static_support_smart_ptr.h index 76035e48d33841..8d5ab3eaae4071 100644 --- a/src/lib/support/static_support_smart_ptr.h +++ b/src/lib/support/static_support_smart_ptr.h @@ -54,7 +54,7 @@ template class CheckedGlobalInstanceReference { public: - CheckedGlobalInstanceReference() = default; + CheckedGlobalInstanceReference() = default; CheckedGlobalInstanceReference(T * e) { VerifyOrDie(e == GlobalInstanceProvider::InstancePointer()); } CheckedGlobalInstanceReference & operator=(T * value) { @@ -89,7 +89,7 @@ template class SimpleInstanceReference { public: - SimpleInstanceReference() = default; + SimpleInstanceReference() = default; SimpleInstanceReference(T * e) : mValue(e) {} SimpleInstanceReference & operator=(T * value) { diff --git a/src/messaging/tests/BUILD.gn b/src/messaging/tests/BUILD.gn index 89f0934486b9c1..3232796aabe7e1 100644 --- a/src/messaging/tests/BUILD.gn +++ b/src/messaging/tests/BUILD.gn @@ -14,7 +14,6 @@ import("//build_overrides/build.gni") import("//build_overrides/chip.gni") -import("//build_overrides/nlunit_test.gni") import("${chip_root}/build/chip/chip_test_suite.gni") import("${chip_root}/src/app/icd/icd.gni") @@ -38,15 +37,9 @@ static_library("helpers") { "${chip_root}/src/transport", "${chip_root}/src/transport/tests:helpers", ] - - # MessagingContext exposes nl-test compatible setup/teardown functions, specifically - # they return nltest specific SUCCESS/FAILURE constants hence this dependency. - # - # Once all tests are moved to pw_unittest/gtest, this dependency should be removed - public_deps = [ "${nlunit_test_root}:nlunit-test" ] } -chip_test_suite_using_nltest("tests") { +chip_test_suite("tests") { output_name = "libMessagingLayerTests" test_sources = [ @@ -73,12 +66,10 @@ chip_test_suite_using_nltest("tests") { "${chip_root}/src/lib/core", "${chip_root}/src/lib/support", "${chip_root}/src/lib/support:test_utils", - "${chip_root}/src/lib/support:testing_nlunit", "${chip_root}/src/messaging", "${chip_root}/src/protocols", "${chip_root}/src/transport", "${chip_root}/src/transport/raw/tests:helpers", - "${nlunit_test_root}:nlunit-test", ] if (chip_enable_icd_server) { diff --git a/src/messaging/tests/MessagingContext.cpp b/src/messaging/tests/MessagingContext.cpp index 61565da1cd4ac0..909bc90f443575 100644 --- a/src/messaging/tests/MessagingContext.cpp +++ b/src/messaging/tests/MessagingContext.cpp @@ -58,7 +58,7 @@ CHIP_ERROR MessagingContext::Init(TransportMgrBase * transport, IOContext * ioCo ReturnErrorOnFailure(mExchangeManager.Init(&mSessionManager)); ReturnErrorOnFailure(mMessageCounterManager.Init(&mExchangeManager)); - if (sInitializeNodes) + if (mInitializeNodes) { ReturnErrorOnFailure(CreateAliceFabric()); ReturnErrorOnFailure(CreateBobFabric()); @@ -112,8 +112,6 @@ using namespace System::Clock::Literals; constexpr chip::System::Clock::Timeout MessagingContext::kResponsiveIdleRetransTimeout; constexpr chip::System::Clock::Timeout MessagingContext::kResponsiveActiveRetransTimeout; -bool MessagingContext::sInitializeNodes = true; - void MessagingContext::SetMRPMode(MRPMode mode) { if (mode == MRPMode::kDefault) diff --git a/src/messaging/tests/MessagingContext.h b/src/messaging/tests/MessagingContext.h index ab9912924e713c..51525f967d1475 100644 --- a/src/messaging/tests/MessagingContext.h +++ b/src/messaging/tests/MessagingContext.h @@ -32,8 +32,6 @@ #include #include -#include - #include namespace chip { @@ -98,10 +96,12 @@ class MessagingContext : public PlatformMemoryUser mInitialized(false), mAliceAddress(Transport::PeerAddress::UDP(GetAddress(), CHIP_PORT + 1)), mBobAddress(Transport::PeerAddress::UDP(GetAddress(), CHIP_PORT)) {} + // TODO Replace VerifyOrDie with Pigweed assert after transition app/tests to Pigweed. + // TODO Currently src/app/icd/server/tests is using MessagingConetext as dependency. ~MessagingContext() { VerifyOrDie(mInitialized == false); } // Whether Alice and Bob are initialized, must be called before Init - static void ConfigInitializeNodes(bool initializeNodes) { sInitializeNodes = initializeNodes; } + void ConfigInitializeNodes(bool initializeNodes) { mInitializeNodes = initializeNodes; } /// Initialize the underlying layers and test suite pointer CHIP_ERROR Init(TransportMgrBase * transport, IOContext * io); @@ -178,7 +178,7 @@ class MessagingContext : public PlatformMemoryUser System::Layer & GetSystemLayer() { return mIOContext->GetSystemLayer(); } private: - static bool sInitializeNodes; + bool mInitializeNodes = true; bool mInitialized; FabricTable mFabricTable; diff --git a/src/messaging/tests/TestAbortExchangesForFabric.cpp b/src/messaging/tests/TestAbortExchangesForFabric.cpp index d5c945c848a9aa..8f8eb825553bf5 100644 --- a/src/messaging/tests/TestAbortExchangesForFabric.cpp +++ b/src/messaging/tests/TestAbortExchangesForFabric.cpp @@ -21,10 +21,9 @@ * one) for a fabric. */ +#include + #include -#include -#include -#include #include #include #include @@ -49,16 +48,23 @@ using namespace chip::System; using namespace chip::System::Clock::Literals; using namespace chip::Protocols; -struct TestContext : Test::LoopbackMessagingContext +struct TestAbortExchangesForFabric : public chip::Test::LoopbackMessagingContext, public ::testing::Test { + static void SetUpTestSuite() { chip::Test::LoopbackMessagingContext::SetUpTestSuite(); } + + static void TearDownTestSuite() { chip::Test::LoopbackMessagingContext::TearDownTestSuite(); } + void SetUp() override { #if CHIP_CRYPTO_PSA - // TODO: use ASSERT_EQ, once transition to pw_unit_test is complete - VerifyOrDie(psa_crypto_init() == PSA_SUCCESS); + ASSERT_EQ(psa_crypto_init(), PSA_SUCCESS); #endif chip::Test::LoopbackMessagingContext::SetUp(); } + + void TearDown() override { chip::Test::LoopbackMessagingContext::TearDown(); } + + void CommonCheckAbortAllButOneExchange(bool dropResponseMessages); }; class MockAppDelegate : public ExchangeDelegate @@ -76,57 +82,57 @@ class MockAppDelegate : public ExchangeDelegate bool mOnMessageReceivedCalled = false; }; -void CommonCheckAbortAllButOneExchange(nlTestSuite * inSuite, TestContext & ctx, bool dropResponseMessages) +void TestAbortExchangesForFabric::CommonCheckAbortAllButOneExchange(bool dropResponseMessages) { // We want to have two sessions using the same fabric id that we use for // creating our exchange contexts. That lets us test exchanges on the same // session as the "special exchange" as well as on other sessions. - auto & sessionManager = ctx.GetSecureSessionManager(); + auto & sessionManager = GetSecureSessionManager(); // Use key ids that are not going to collide with anything else that ctx is // doing. // TODO: These should really be CASE sessions... SessionHolder session1; - CHIP_ERROR err = sessionManager.InjectCaseSessionWithTestKey(session1, 100, 101, ctx.GetAliceFabric()->GetNodeId(), - ctx.GetBobFabric()->GetNodeId(), ctx.GetAliceFabricIndex(), - ctx.GetBobAddress(), CryptoContext::SessionRole::kInitiator, {}); + CHIP_ERROR err = sessionManager.InjectCaseSessionWithTestKey(session1, 100, 101, GetAliceFabric()->GetNodeId(), + GetBobFabric()->GetNodeId(), GetAliceFabricIndex(), + GetBobAddress(), CryptoContext::SessionRole::kInitiator, {}); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); SessionHolder session1Reply; - err = sessionManager.InjectCaseSessionWithTestKey(session1Reply, 101, 100, ctx.GetBobFabric()->GetNodeId(), - ctx.GetAliceFabric()->GetNodeId(), ctx.GetBobFabricIndex(), - ctx.GetAliceAddress(), CryptoContext::SessionRole::kResponder, {}); + err = sessionManager.InjectCaseSessionWithTestKey(session1Reply, 101, 100, GetBobFabric()->GetNodeId(), + GetAliceFabric()->GetNodeId(), GetBobFabricIndex(), GetAliceAddress(), + CryptoContext::SessionRole::kResponder, {}); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); // TODO: Ideally this would go to a different peer, but we don't have that // set up right now: only Alice and Bob have useful node ids and whatnot. SessionHolder session2; - err = sessionManager.InjectCaseSessionWithTestKey(session2, 200, 201, ctx.GetAliceFabric()->GetNodeId(), - ctx.GetBobFabric()->GetNodeId(), ctx.GetAliceFabricIndex(), - ctx.GetBobAddress(), CryptoContext::SessionRole::kInitiator, {}); + err = sessionManager.InjectCaseSessionWithTestKey(session2, 200, 201, GetAliceFabric()->GetNodeId(), + GetBobFabric()->GetNodeId(), GetAliceFabricIndex(), GetBobAddress(), + CryptoContext::SessionRole::kInitiator, {}); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); SessionHolder session2Reply; - err = sessionManager.InjectCaseSessionWithTestKey(session2Reply, 101, 100, ctx.GetBobFabric()->GetNodeId(), - ctx.GetAliceFabric()->GetNodeId(), ctx.GetBobFabricIndex(), - ctx.GetAliceAddress(), CryptoContext::SessionRole::kResponder, {}); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = sessionManager.InjectCaseSessionWithTestKey(session2Reply, 101, 100, GetBobFabric()->GetNodeId(), + GetAliceFabric()->GetNodeId(), GetBobFabricIndex(), GetAliceAddress(), + CryptoContext::SessionRole::kResponder, {}); + EXPECT_EQ(err, CHIP_NO_ERROR); - auto & exchangeMgr = ctx.GetExchangeManager(); + auto & exchangeMgr = GetExchangeManager(); MockAppDelegate delegate; Echo::EchoServer server; err = server.Init(&exchangeMgr); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); auto trySendMessage = [&](ExchangeContext * exchange, SendMessageFlags flags) { PacketBufferHandle buffer = MessagePacketBuffer::New(0); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); return exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), flags); }; @@ -137,79 +143,81 @@ void CommonCheckAbortAllButOneExchange(nlTestSuite * inSuite, TestContext & ctx, loopback.mDroppedMessageCount = 0; err = trySendMessage(exchange, flags); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, !delegate.mOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); + DrainAndServiceIO(); + EXPECT_FALSE(delegate.mOnMessageReceivedCalled); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); }; - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); // We want to test three possible exchange states: // 1) Closed but waiting for ack. // 2) Waiting for a response. // 3) Waiting for a send. auto * waitingForAck1 = exchangeMgr.NewContext(session1.Get().Value(), &delegate); - NL_TEST_ASSERT(inSuite, waitingForAck1 != nullptr); + ASSERT_NE(waitingForAck1, nullptr); sendAndDropMessage(waitingForAck1, SendMessageFlags::kNone); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); auto * waitingForAck2 = exchangeMgr.NewContext(session2.Get().Value(), &delegate); - NL_TEST_ASSERT(inSuite, waitingForAck2 != nullptr); + ASSERT_NE(waitingForAck2, nullptr); sendAndDropMessage(waitingForAck2, SendMessageFlags::kNone); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 2); + EXPECT_EQ(rm->TestGetCountRetransTable(), 2); auto * waitingForIncomingMessage1 = exchangeMgr.NewContext(session1.Get().Value(), &delegate); - NL_TEST_ASSERT(inSuite, waitingForIncomingMessage1 != nullptr); + ASSERT_NE(waitingForIncomingMessage1, nullptr); sendAndDropMessage(waitingForIncomingMessage1, SendMessageFlags::kExpectResponse); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 3); + EXPECT_EQ(rm->TestGetCountRetransTable(), 3); auto * waitingForIncomingMessage2 = exchangeMgr.NewContext(session2.Get().Value(), &delegate); - NL_TEST_ASSERT(inSuite, waitingForIncomingMessage2 != nullptr); + ASSERT_NE(waitingForIncomingMessage2, nullptr); sendAndDropMessage(waitingForIncomingMessage2, SendMessageFlags::kExpectResponse); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 4); + EXPECT_EQ(rm->TestGetCountRetransTable(), 4); auto * waitingForSend1 = exchangeMgr.NewContext(session1.Get().Value(), &delegate); - NL_TEST_ASSERT(inSuite, waitingForSend1 != nullptr); + ASSERT_NE(waitingForSend1, nullptr); waitingForSend1->WillSendMessage(); auto * waitingForSend2 = exchangeMgr.NewContext(session2.Get().Value(), &delegate); - NL_TEST_ASSERT(inSuite, waitingForSend2 != nullptr); + ASSERT_NE(waitingForSend2, nullptr); waitingForSend2->WillSendMessage(); // Grab handles to our sessions now, before we evict things. const auto & sessionHandle1 = session1.Get(); const auto & sessionHandle2 = session2.Get(); - session1->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig( - Test::MessagingContext::kResponsiveIdleRetransTimeout, Test::MessagingContext::kResponsiveActiveRetransTimeout)); + session1->AsSecureSession()->SetRemoteSessionParameters( + ReliableMessageProtocolConfig(chip::Test::MessagingContext::kResponsiveIdleRetransTimeout, + chip::Test::MessagingContext::kResponsiveActiveRetransTimeout)); - session1Reply->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig( - Test::MessagingContext::kResponsiveIdleRetransTimeout, Test::MessagingContext::kResponsiveActiveRetransTimeout)); + session1Reply->AsSecureSession()->SetRemoteSessionParameters( + ReliableMessageProtocolConfig(chip::Test::MessagingContext::kResponsiveIdleRetransTimeout, + chip::Test::MessagingContext::kResponsiveActiveRetransTimeout)); - NL_TEST_ASSERT(inSuite, session1); - NL_TEST_ASSERT(inSuite, session2); + EXPECT_TRUE(session1); + EXPECT_TRUE(session2); auto * specialExhange = exchangeMgr.NewContext(session1.Get().Value(), &delegate); specialExhange->AbortAllOtherCommunicationOnFabric(); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); - NL_TEST_ASSERT(inSuite, !session1); - NL_TEST_ASSERT(inSuite, !session2); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); + EXPECT_FALSE(session1); + EXPECT_FALSE(session2); - NL_TEST_ASSERT(inSuite, exchangeMgr.NewContext(sessionHandle1.Value(), &delegate) == nullptr); - NL_TEST_ASSERT(inSuite, exchangeMgr.NewContext(sessionHandle2.Value(), &delegate) == nullptr); + EXPECT_EQ(exchangeMgr.NewContext(sessionHandle1.Value(), &delegate), nullptr); + EXPECT_EQ(exchangeMgr.NewContext(sessionHandle2.Value(), &delegate), nullptr); // Make sure we can't send messages on any of the other exchanges. - NL_TEST_ASSERT(inSuite, trySendMessage(waitingForSend1, SendMessageFlags::kExpectResponse) != CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, trySendMessage(waitingForSend2, SendMessageFlags::kExpectResponse) != CHIP_NO_ERROR); + EXPECT_NE(trySendMessage(waitingForSend1, SendMessageFlags::kExpectResponse), CHIP_NO_ERROR); + EXPECT_NE(trySendMessage(waitingForSend2, SendMessageFlags::kExpectResponse), CHIP_NO_ERROR); // Make sure we can send a message on the special exchange. - NL_TEST_ASSERT(inSuite, !delegate.mOnMessageReceivedCalled); + EXPECT_FALSE(delegate.mOnMessageReceivedCalled); err = trySendMessage(specialExhange, SendMessageFlags::kNone); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); // Should be waiting for an ack now. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); if (dropResponseMessages) { @@ -217,7 +225,7 @@ void CommonCheckAbortAllButOneExchange(nlTestSuite * inSuite, TestContext & ctx, // This version of the test allows us to validate logic that marks expired sessions as defunct // on encountering an MRP failure. // - loopback.mNumMessagesToDrop = Test::LoopbackTransport::kUnlimitedMessageCount; + loopback.mNumMessagesToDrop = chip::Test::LoopbackTransport::kUnlimitedMessageCount; loopback.mDroppedMessageCount = 0; // @@ -231,18 +239,18 @@ void CommonCheckAbortAllButOneExchange(nlTestSuite * inSuite, TestContext & ctx, waitTimeout += ICDConfigurationData::GetInstance().GetFastPollingInterval(); #endif - ctx.GetIOContext().DriveIOUntil(waitTimeout, [&]() { return false; }); + GetIOContext().DriveIOUntil(waitTimeout, [&]() { return false; }); } else { - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); } // Should not get an app-level response, since we are not expecting one. - NL_TEST_ASSERT(inSuite, !delegate.mOnMessageReceivedCalled); + EXPECT_FALSE(delegate.mOnMessageReceivedCalled); // We should have gotten our ack. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); waitingForSend1->Close(); waitingForSend2->Close(); @@ -251,43 +259,14 @@ void CommonCheckAbortAllButOneExchange(nlTestSuite * inSuite, TestContext & ctx, loopback.mDroppedMessageCount = 0; } -void CheckAbortAllButOneExchange(nlTestSuite * inSuite, void * inContext) +TEST_F(TestAbortExchangesForFabric, CheckAbortAllButOneExchange) { - TestContext & ctx = *reinterpret_cast(inContext); - CommonCheckAbortAllButOneExchange(inSuite, ctx, false); + CommonCheckAbortAllButOneExchange(false); } -void CheckAbortAllButOneExchangeResponseTimeout(nlTestSuite * inSuite, void * inContext) +TEST_F(TestAbortExchangesForFabric, CheckAbortAllButOneExchangeResponseTimeout) { - TestContext & ctx = *reinterpret_cast(inContext); - CommonCheckAbortAllButOneExchange(inSuite, ctx, true); + CommonCheckAbortAllButOneExchange(true); } -const nlTest sTests[] = { - NL_TEST_DEF("Test aborting all but one exchange", CheckAbortAllButOneExchange), - NL_TEST_DEF("Test aborting all but one exchange + response timeout", CheckAbortAllButOneExchangeResponseTimeout), - NL_TEST_SENTINEL(), -}; - -// clang-format off -nlTestSuite sSuite = { - "Test-AbortExchangesForFabric", - &sTests[0], - NL_TEST_WRAP_FUNCTION(TestContext::SetUpTestSuite), - NL_TEST_WRAP_FUNCTION(TestContext::TearDownTestSuite), - NL_TEST_WRAP_METHOD(TestContext, SetUp), - NL_TEST_WRAP_METHOD(TestContext, TearDown), -}; -// clang-format on - } // namespace - -/** - * Main - */ -int TestAbortExchangesForFabric() -{ - return chip::ExecuteTestsWithContext(&sSuite); -} - -CHIP_REGISTER_TEST_SUITE(TestAbortExchangesForFabric); diff --git a/src/messaging/tests/TestExchange.cpp b/src/messaging/tests/TestExchange.cpp index fbc1c9e505204d..7c0257d3093d3f 100644 --- a/src/messaging/tests/TestExchange.cpp +++ b/src/messaging/tests/TestExchange.cpp @@ -14,12 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include +#include + +#include #include #include #include -#include -#include #include #include #include @@ -28,12 +30,6 @@ #include #include -#include -#include - -#include -#include - #if CHIP_CRYPTO_PSA #include "psa/crypto.h" #endif @@ -45,18 +41,30 @@ using namespace chip::Inet; using namespace chip::Transport; using namespace chip::Messaging; -struct TestContext : Test::LoopbackMessagingContext +class MockExchangeDelegate; + +struct TestExchange : public Test::LoopbackMessagingContext, public ::testing::Test { // TODO Add TearDown function when changing test framework to Pigweed to make it more clear how it works. // Currently, the TearDown function is from LoopbackMessagingContext void SetUp() override { #if CHIP_CRYPTO_PSA - // TODO: use ASSERT_EQ, once transition to pw_unit_test is complete - VerifyOrDie(psa_crypto_init() == PSA_SUCCESS); + ASSERT_EQ(psa_crypto_init(), PSA_SUCCESS); #endif chip::Test::LoopbackMessagingContext::SetUp(); } + + void TearDown() override { chip::Test::LoopbackMessagingContext::TearDown(); } + + static void SetUpTestSuite() { chip::Test::LoopbackMessagingContext::SetUpTestSuite(); } + + static void TearDownTestSuite() { chip::Test::LoopbackMessagingContext::TearDownTestSuite(); } + + template + void DoRoundTripTest(MockExchangeDelegate & delegate1, MockExchangeDelegate & delegate2, uint8_t requestMessageType, + uint8_t responseMessageType, AfterRequestChecker && afterRequestChecker, + AfterResponseChecker && afterResponseChecker); }; enum : uint8_t @@ -113,18 +121,16 @@ class MockExchangeDelegate : public UnsolicitedMessageHandler, public ExchangeDe // handler, sends a message of type requestMessageType via an exchange that has // delegate1 as delegate, responds with responseMessageType. template -void DoRoundTripTest(nlTestSuite * inSuite, void * inContext, MockExchangeDelegate & delegate1, MockExchangeDelegate & delegate2, - uint8_t requestMessageType, uint8_t responseMessageType, AfterRequestChecker && afterRequestChecker, - AfterResponseChecker && afterResponseChecker) +void TestExchange::DoRoundTripTest(MockExchangeDelegate & delegate1, MockExchangeDelegate & delegate2, uint8_t requestMessageType, + uint8_t responseMessageType, AfterRequestChecker && afterRequestChecker, + AfterResponseChecker && afterResponseChecker) { - TestContext & ctx = *reinterpret_cast(inContext); - - ExchangeContext * ec1 = ctx.NewExchangeToBob(&delegate1); - NL_TEST_ASSERT(inSuite, ec1 != nullptr); + ExchangeContext * ec1 = NewExchangeToBob(&delegate1); + ASSERT_NE(ec1, nullptr); - CHIP_ERROR err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::Id, - requestMessageType, &delegate2); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + CHIP_ERROR err = + GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::Id, requestMessageType, &delegate2); + EXPECT_EQ(err, CHIP_NO_ERROR); // To simplify things, skip MRP for all our messages, and make sure we are // always expecting responses. @@ -133,45 +139,45 @@ void DoRoundTripTest(nlTestSuite * inSuite, void * inContext, MockExchangeDelega err = ec1->SendMessage(Protocols::SecureChannel::Id, requestMessageType, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize), sendFlags); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); afterRequestChecker(); ExchangeContext * ec2 = delegate2.mExchange; err = ec2->SendMessage(Protocols::SecureChannel::Id, responseMessageType, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize), sendFlags); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); afterResponseChecker(); ec1->Close(); ec2->Close(); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::Id, kMsgType_TEST1); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::Id, kMsgType_TEST1); + EXPECT_EQ(err, CHIP_NO_ERROR); } -void CheckBasicMessageRoundTrip(nlTestSuite * inSuite, void * inContext) +TEST_F(TestExchange, CheckBasicMessageRoundTrip) { MockExchangeDelegate delegate1; MockExchangeDelegate delegate2; DoRoundTripTest( - inSuite, inContext, delegate1, delegate2, kMsgType_TEST1, kMsgType_TEST2, + delegate1, delegate2, kMsgType_TEST1, kMsgType_TEST2, [&] { - NL_TEST_ASSERT(inSuite, delegate1.mReceivedMessageCount == 0); - NL_TEST_ASSERT(inSuite, delegate2.mReceivedMessageCount == 1); + EXPECT_EQ(delegate1.mReceivedMessageCount, 0u); + EXPECT_EQ(delegate2.mReceivedMessageCount, 1u); }, [&] { - NL_TEST_ASSERT(inSuite, delegate1.mReceivedMessageCount == 1); - NL_TEST_ASSERT(inSuite, delegate2.mReceivedMessageCount == 1); + EXPECT_EQ(delegate1.mReceivedMessageCount, 1u); + EXPECT_EQ(delegate2.mReceivedMessageCount, 1u); }); } -void CheckBasicExchangeMessageDispatch(nlTestSuite * inSuite, void * inContext) +TEST_F(TestExchange, CheckBasicExchangeMessageDispatch) { class MockMessageDispatch : public ExchangeMessageDispatch { @@ -191,14 +197,14 @@ void CheckBasicExchangeMessageDispatch(nlTestSuite * inSuite, void * inContext) MockExchangeDelegate delegate2; DoRoundTripTest( - inSuite, inContext, delegate1, delegate2, kMsgType_TEST1, kMsgType_TEST1, + delegate1, delegate2, kMsgType_TEST1, kMsgType_TEST1, [&] { - NL_TEST_ASSERT(inSuite, delegate1.mReceivedMessageCount == 0); - NL_TEST_ASSERT(inSuite, delegate2.mReceivedMessageCount == 1); + EXPECT_EQ(delegate1.mReceivedMessageCount, 0u); + EXPECT_EQ(delegate2.mReceivedMessageCount, 1u); }, [&] { - NL_TEST_ASSERT(inSuite, delegate1.mReceivedMessageCount == 1); - NL_TEST_ASSERT(inSuite, delegate2.mReceivedMessageCount == 1); + EXPECT_EQ(delegate1.mReceivedMessageCount, 1u); + EXPECT_EQ(delegate2.mReceivedMessageCount, 1u); }); } @@ -209,53 +215,15 @@ void CheckBasicExchangeMessageDispatch(nlTestSuite * inSuite, void * inContext) MockExchangeDelegate delegate2; DoRoundTripTest( - inSuite, inContext, delegate1, delegate2, kMsgType_TEST1, kMsgType_TEST2, + delegate1, delegate2, kMsgType_TEST1, kMsgType_TEST2, [&] { - NL_TEST_ASSERT(inSuite, delegate1.mReceivedMessageCount == 0); - NL_TEST_ASSERT(inSuite, delegate2.mReceivedMessageCount == 1); + EXPECT_EQ(delegate1.mReceivedMessageCount, 0u); + EXPECT_EQ(delegate2.mReceivedMessageCount, 1u); }, [&] { - NL_TEST_ASSERT(inSuite, delegate1.mReceivedMessageCount == 0); - NL_TEST_ASSERT(inSuite, delegate2.mReceivedMessageCount == 1); + EXPECT_EQ(delegate1.mReceivedMessageCount, 0u); + EXPECT_EQ(delegate2.mReceivedMessageCount, 1u); }); } } - -// Test Suite - -/** - * Test Suite that lists all the test functions. - */ -// clang-format off -const nlTest sTests[] = -{ - NL_TEST_DEF("Test ExchangeContext::SendMessage", CheckBasicMessageRoundTrip), - NL_TEST_DEF("Test ExchangeMessageDispatch", CheckBasicExchangeMessageDispatch), - - NL_TEST_SENTINEL() -}; -// clang-format on - -// clang-format off -nlTestSuite sSuite = -{ - "Test-Exchange", - &sTests[0], - NL_TEST_WRAP_FUNCTION(TestContext::SetUpTestSuite), - NL_TEST_WRAP_FUNCTION(TestContext::TearDownTestSuite), - NL_TEST_WRAP_METHOD(TestContext, SetUp), - NL_TEST_WRAP_METHOD(TestContext, TearDown), -}; -// clang-format on - } // namespace - -/** - * Main - */ -int TestExchange() -{ - return chip::ExecuteTestsWithContext(&sSuite); -} - -CHIP_REGISTER_TEST_SUITE(TestExchange); diff --git a/src/messaging/tests/TestExchangeHolder.cpp b/src/messaging/tests/TestExchangeHolder.cpp index 849c1856d9b013..35f0856cfede24 100644 --- a/src/messaging/tests/TestExchangeHolder.cpp +++ b/src/messaging/tests/TestExchangeHolder.cpp @@ -21,11 +21,10 @@ * one) for a fabric. */ +#include + #include "messaging/ExchangeDelegate.h" #include "system/SystemClock.h" -#include -#include -#include #include #include #include @@ -69,9 +68,16 @@ using namespace chip::Messaging; using namespace chip::System; using namespace chip::Protocols; -using TestContext = Test::LoopbackMessagingContext; +struct TestExchangeHolder : public chip::Test::LoopbackMessagingContext, public ::testing::Test +{ + static void SetUpTestSuite() { chip::Test::LoopbackMessagingContext::SetUpTestSuite(); } + + static void TearDownTestSuite() { chip::Test::LoopbackMessagingContext::TearDownTestSuite(); } -TestContext * gCtx = nullptr; + void SetUp() override { chip::Test::LoopbackMessagingContext::SetUp(); } + + void TearDown() override { chip::Test::LoopbackMessagingContext::TearDown(); } +}; class MockProtocolResponder : public ExchangeDelegate, public Messaging::UnsolicitedMessageHandler { @@ -87,26 +93,27 @@ class MockProtocolResponder : public ExchangeDelegate, public Messaging::Unsolic }; template - MockProtocolResponder(BehaviorModifier modifier1, Args &&... args) : - mExchangeCtx(*this), mBehaviorModifier(modifier1, std::forward(args)...) + MockProtocolResponder(TestExchangeHolder & ctx, BehaviorModifier modifier1, Args &&... args) : + mExchangeCtx(*this), mBehaviorModifier(modifier1, std::forward(args)...), testExchangeHolder(ctx) { - VerifyOrDie(gCtx != nullptr); - gCtx->GetExchangeManager().RegisterUnsolicitedMessageHandlerForProtocol(chip::Protocols::MockProtocol::Id, this); + testExchangeHolder.GetExchangeManager().RegisterUnsolicitedMessageHandlerForProtocol(chip::Protocols::MockProtocol::Id, + this); ChipLogDetail(ExchangeManager, "[%p] MockProtocolResponder: %p", this, &mExchangeCtx); } - MockProtocolResponder(BehaviorModifier modifier = BehaviorModifier::kNone) : mExchangeCtx(*this) + MockProtocolResponder(TestExchangeHolder & ctx, BehaviorModifier modifier = BehaviorModifier::kNone) : + mExchangeCtx(*this), testExchangeHolder(ctx) { - VerifyOrDie(gCtx != nullptr); mBehaviorModifier.Set(modifier); - gCtx->GetExchangeManager().RegisterUnsolicitedMessageHandlerForProtocol(chip::Protocols::MockProtocol::Id, this); + testExchangeHolder.GetExchangeManager().RegisterUnsolicitedMessageHandlerForProtocol(chip::Protocols::MockProtocol::Id, + this); ChipLogDetail(ExchangeManager, "[%p] MockProtocolResponder: %p", this, &mExchangeCtx); } ~MockProtocolResponder() { ChipLogDetail(ExchangeManager, "[%p] ~MockProtocolResponder", this); - gCtx->GetExchangeManager().UnregisterUnsolicitedMessageHandlerForProtocol(chip::Protocols::MockProtocol::Id); + testExchangeHolder.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForProtocol(chip::Protocols::MockProtocol::Id); } bool DidInteractionSucceed() { return mInteractionSucceeded; } @@ -126,6 +133,7 @@ class MockProtocolResponder : public ExchangeDelegate, public Messaging::Unsolic ExchangeHolder mExchangeCtx; BitFlags mBehaviorModifier = BehaviorModifier::kNone; bool mInteractionSucceeded = false; + TestExchangeHolder & testExchangeHolder; }; class MockProtocolInitiator : public ExchangeDelegate @@ -144,15 +152,16 @@ class MockProtocolInitiator : public ExchangeDelegate kExpireSessionAfterMsg3Send = 0x18, }; - MockProtocolInitiator(BehaviorModifier modifier = BehaviorModifier::kNone) : mExchangeCtx(*this) + MockProtocolInitiator(TestExchangeHolder & ctx, BehaviorModifier modifier = BehaviorModifier::kNone) : + mExchangeCtx(*this), testExchangeHolder(ctx) { mBehaviorModifier.Set(modifier); ChipLogDetail(ExchangeManager, "[%p] MockProtocolInitiator: %p", this, &mExchangeCtx); } template - MockProtocolInitiator(BehaviorModifier modifier1, Args &&... args) : - mExchangeCtx(*this), mBehaviorModifier(modifier1, std::forward(args)...) + MockProtocolInitiator(TestExchangeHolder & ctx, BehaviorModifier modifier1, Args &&... args) : + mExchangeCtx(*this), mBehaviorModifier(modifier1, std::forward(args)...), testExchangeHolder(ctx) { ChipLogDetail(ExchangeManager, "[%p] MockProtocolInitiator: %p", this, &mExchangeCtx); } @@ -172,6 +181,7 @@ class MockProtocolInitiator : public ExchangeDelegate ExchangeHolder mExchangeCtx; BitFlags mBehaviorModifier = BehaviorModifier::kNone; bool mInteractionSucceeded = false; + TestExchangeHolder & testExchangeHolder; }; CHIP_ERROR MockProtocolResponder::OnMessageReceived(ExchangeContext * ec, const PayloadHeader & payloadHeader, @@ -248,7 +258,7 @@ CHIP_ERROR MockProtocolInitiator::StartInteraction(SessionHandle & sessionHandle PacketBufferHandle buffer = MessagePacketBuffer::New(0); VerifyOrReturnError(!buffer.IsNull(), CHIP_ERROR_NO_MEMORY); - auto exchange = gCtx->GetExchangeManager().NewContext(sessionHandle, this); + auto exchange = testExchangeHolder.GetExchangeManager().NewContext(sessionHandle, this); VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_NO_MEMORY); // @@ -344,15 +354,11 @@ CHIP_ERROR MockProtocolInitiator::OnMessageReceived(ExchangeContext * ec, const return err; } -void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) +TEST_F(TestExchangeHolder, TestExchangeHolder) { - TestContext & ctx = *reinterpret_cast(inContext); - - gCtx = &ctx; + auto sessionHandle = GetSessionAliceToBob(); - auto sessionHandle = ctx.GetSessionAliceToBob(); - - ctx.SetMRPMode(chip::Test::MessagingContext::MRPMode::kResponsive); + SetMRPMode(chip::Test::MessagingContext::MRPMode::kResponsive); // // #1: Initiator (AllocExchange) @@ -366,14 +372,14 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) ChipLogProgress(ExchangeManager, "-------- #1: Initiator (AllocExchange) ----------"); { - MockProtocolInitiator initiator(MockProtocolInitiator::BehaviorModifier::kDontSendMsg1); - MockProtocolResponder responder; + MockProtocolInitiator initiator(*this, MockProtocolInitiator::BehaviorModifier::kDontSendMsg1); + MockProtocolResponder responder(*this); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); } - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -389,19 +395,19 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) ChipLogProgress(ExchangeManager, "-------- #2: Initiator --X (SendErr) Msg1 --------- "); { - MockProtocolInitiator initiator(MockProtocolInitiator::BehaviorModifier::kErrMsg1); - MockProtocolResponder responder; + MockProtocolInitiator initiator(*this, MockProtocolInitiator::BehaviorModifier::kErrMsg1); + MockProtocolResponder responder(*this); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); + EXPECT_NE(err, CHIP_NO_ERROR); } // // Service IO AFTER the objects above cease to exist to prevent Msg1 from getting to Responder. This also // flush any pending messages in the queue. // - ctx.DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + DrainAndServiceIO(); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -417,19 +423,19 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) ChipLogProgress(ExchangeManager, "-------- #3: Initiator --X (SessionReleased before) Msg1 --------- "); { - MockProtocolInitiator initiator(MockProtocolInitiator::BehaviorModifier::kExpireSessionBeforeMsg1Send); - MockProtocolResponder responder; + MockProtocolInitiator initiator(*this, MockProtocolInitiator::BehaviorModifier::kExpireSessionBeforeMsg1Send); + MockProtocolResponder responder(*this); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); + EXPECT_NE(err, CHIP_NO_ERROR); } // // Service IO AFTER the objects above cease to exist to prevent Msg1 from getting to Responder. This also // flush any pending messages in the queue. // - ctx.DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + DrainAndServiceIO(); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -445,20 +451,20 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) ChipLogProgress(ExchangeManager, "-------- #4: Initiator --X (SendErr + SessionReleased after) Msg1 --------- "); { - MockProtocolInitiator initiator(MockProtocolInitiator::BehaviorModifier::kExpireSessionAfterMsg1Send, + MockProtocolInitiator initiator(*this, MockProtocolInitiator::BehaviorModifier::kExpireSessionAfterMsg1Send, MockProtocolInitiator::BehaviorModifier::kErrMsg1); - MockProtocolResponder responder; + MockProtocolResponder responder(*this); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); + EXPECT_NE(err, CHIP_NO_ERROR); } // // Service IO AFTER the objects above cease to exist to prevent Msg1 from getting to Responder. This also // flush any pending messages in the queue. // - ctx.DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + DrainAndServiceIO(); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -474,19 +480,19 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) ChipLogProgress(ExchangeManager, "-------- #5: Initiator >-- Msg1 --X Responder ---------"); { - MockProtocolInitiator initiator; - MockProtocolResponder responder; + MockProtocolInitiator initiator(*this); + MockProtocolResponder responder(*this); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); } // // Service IO AFTER the objects above cease to exist to prevent Msg1 from getting to Responder. This also // flush any pending messages in the queue. // - ctx.DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + DrainAndServiceIO(); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -502,16 +508,16 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) { ChipLogProgress(ExchangeManager, "-------- #6: Initiator >-- Msg1 --> Responder (WillSend) ---------"); - MockProtocolInitiator initiator; - MockProtocolResponder responder(MockProtocolResponder::BehaviorModifier::kHoldMsg2); + MockProtocolInitiator initiator(*this); + MockProtocolResponder responder(*this, MockProtocolResponder::BehaviorModifier::kHoldMsg2); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); } - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -529,16 +535,16 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) { ChipLogProgress(ExchangeManager, "-------- #7: Msg2 (SendFailure) X-- Responder ---------"); - MockProtocolInitiator initiator; - MockProtocolResponder responder(MockProtocolResponder::BehaviorModifier::kErrMsg2); + MockProtocolInitiator initiator(*this); + MockProtocolResponder responder(*this, MockProtocolResponder::BehaviorModifier::kErrMsg2); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); } - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -554,16 +560,16 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) { ChipLogProgress(ExchangeManager, "-------- #8: Msg2 (SessionReleased Before) X-- Responder ---------"); - MockProtocolInitiator initiator; - MockProtocolResponder responder(MockProtocolResponder::BehaviorModifier::kExpireSessionBeforeMsg2Send); + MockProtocolInitiator initiator(*this); + MockProtocolResponder responder(*this, MockProtocolResponder::BehaviorModifier::kExpireSessionBeforeMsg2Send); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); } - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -579,17 +585,17 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) { ChipLogProgress(ExchangeManager, "-------- #9: Msg2 (SendErr + SessionReleased after) X-- Responder ---------"); - MockProtocolInitiator initiator; - MockProtocolResponder responder(MockProtocolResponder::BehaviorModifier::kErrMsg2, + MockProtocolInitiator initiator(*this); + MockProtocolResponder responder(*this, MockProtocolResponder::BehaviorModifier::kErrMsg2, MockProtocolResponder::BehaviorModifier::kExpireSessionAfterMsg2Send); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); } - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -605,16 +611,16 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) { ChipLogProgress(ExchangeManager, "-------- #10: (WillSend) Initiator <-- Msg2 <-- Responder ---------"); - MockProtocolInitiator initiator(MockProtocolInitiator::BehaviorModifier::kHoldMsg3); - MockProtocolResponder responder; + MockProtocolInitiator initiator(*this, MockProtocolInitiator::BehaviorModifier::kHoldMsg3); + MockProtocolResponder responder(*this); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); } - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -630,16 +636,16 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) { ChipLogProgress(ExchangeManager, "-------- #11: Initiator --X (SessionReleased before) Msg3 ------------"); - MockProtocolInitiator initiator(MockProtocolInitiator::BehaviorModifier::kExpireSessionBeforeMsg3Send); - MockProtocolResponder responder; + MockProtocolInitiator initiator(*this, MockProtocolInitiator::BehaviorModifier::kExpireSessionBeforeMsg3Send); + MockProtocolResponder responder(*this); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); + EXPECT_NE(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); } - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -657,17 +663,17 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) { ChipLogProgress(ExchangeManager, "-------- #12: Initiator --X (SendErr + SessionReleased after) Msg3 ------------"); - MockProtocolInitiator initiator(MockProtocolInitiator::BehaviorModifier::kErrMsg3, + MockProtocolInitiator initiator(*this, MockProtocolInitiator::BehaviorModifier::kErrMsg3, MockProtocolInitiator::BehaviorModifier::kExpireSessionAfterMsg3Send); - MockProtocolResponder responder; + MockProtocolResponder responder(*this); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); } - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -685,16 +691,16 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) { ChipLogProgress(ExchangeManager, "-------- #13: Initiator >-- Msg3 --> Responder ---------"); - MockProtocolInitiator initiator; - MockProtocolResponder responder; + MockProtocolInitiator initiator(*this); + MockProtocolResponder responder(*this); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); } - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -711,13 +717,13 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) { ChipLogProgress(ExchangeManager, "-------- #14: Initiator >-- Msg3 --> Responder (SessionReleased) ---------"); - MockProtocolInitiator initiator; - MockProtocolResponder responder(MockProtocolResponder::BehaviorModifier::kExpireSessionAfterMsg3Receive); + MockProtocolInitiator initiator(*this); + MockProtocolResponder responder(*this, MockProtocolResponder::BehaviorModifier::kExpireSessionAfterMsg3Receive); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); // // Because of the session expiration right after Msg3 is received, it causes an abort of the underlying EC @@ -730,11 +736,11 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) // entry has been removed. To make this happen, drive the IO forward enough that a single re-transmission happens. This // will result in a duplicate message ACK being delivered by the responder, causing the EC to finally get released. // - ctx.GetIOContext().DriveIOUntil(System::Clock::Seconds16(5), - [&]() { return ctx.GetExchangeManager().GetNumActiveExchanges() == 0; }); + GetIOContext().DriveIOUntil(System::Clock::Seconds16(5), + [&]() { return GetExchangeManager().GetNumActiveExchanges() == 0; }); } - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -750,22 +756,22 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) { ChipLogProgress(ExchangeManager, "-------- #15: Initiator >-- Msg1 --> Responder (WillSend) X2 ---------"); - MockProtocolInitiator initiator; - MockProtocolResponder responder(MockProtocolResponder::BehaviorModifier::kHoldMsg2); + MockProtocolInitiator initiator(*this); + MockProtocolResponder responder(*this, MockProtocolResponder::BehaviorModifier::kHoldMsg2); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); } - ctx.DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + DrainAndServiceIO(); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } // @@ -784,58 +790,21 @@ void TestExchangeHolder(nlTestSuite * inSuite, void * inContext) { ChipLogProgress(ExchangeManager, "-------- #16: Initiator >-- Msg3 --> Responder X2 ---------"); - MockProtocolInitiator initiator; - MockProtocolResponder responder; + MockProtocolInitiator initiator(*this); + MockProtocolResponder responder(*this); auto err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); err = initiator.StartInteraction(sessionHandle); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); } - NL_TEST_ASSERT(inSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); + EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u); } } - -// Test Suite - -/** - * Test Suite that lists all the test functions. - */ -// clang-format off -const nlTest sTests[] = -{ - NL_TEST_DEF("TestExchangeHolder", TestExchangeHolder), - - NL_TEST_SENTINEL() -}; -// clang-format on - -// clang-format off -nlTestSuite sSuite = -{ - "Test-TestExchangeHolder", - &sTests[0], - NL_TEST_WRAP_FUNCTION(TestContext::SetUpTestSuite), - NL_TEST_WRAP_FUNCTION(TestContext::TearDownTestSuite), - NL_TEST_WRAP_METHOD(TestContext, SetUp), - NL_TEST_WRAP_METHOD(TestContext, TearDown), -}; -// clang-format on - } // anonymous namespace - -/** - * Main - */ -int TestExchangeHolder() -{ - return chip::ExecuteTestsWithContext(&sSuite); -} - -CHIP_REGISTER_TEST_SUITE(TestExchangeHolder); diff --git a/src/messaging/tests/TestExchangeMgr.cpp b/src/messaging/tests/TestExchangeMgr.cpp index efca83e6d1a92a..975078ede9bd78 100644 --- a/src/messaging/tests/TestExchangeMgr.cpp +++ b/src/messaging/tests/TestExchangeMgr.cpp @@ -20,12 +20,14 @@ * @file * This file implements unit tests for the ExchangeManager implementation. */ +#include +#include + +#include #include #include #include -#include -#include #include #include #include @@ -34,12 +36,6 @@ #include #include -#include -#include - -#include -#include - #if CHIP_CRYPTO_PSA #include "psa/crypto.h" #endif @@ -51,18 +47,21 @@ using namespace chip::Inet; using namespace chip::Transport; using namespace chip::Messaging; -struct TestContext : Test::LoopbackMessagingContext +struct TestExchangeMgr : public chip::Test::LoopbackMessagingContext, public ::testing::Test { - // TODO Add TearDown function during changing test framework to Pigweed to make it more clear how does it work. - // Currently, the TearDown function is from LoopbackMessagingContext + static void SetUpTestSuite() { chip::Test::LoopbackMessagingContext::SetUpTestSuite(); } + + static void TearDownTestSuite() { chip::Test::LoopbackMessagingContext::TearDownTestSuite(); } + void SetUp() override { #if CHIP_CRYPTO_PSA - // TODO: use ASSERT_EQ, once transition to pw_unit_test is complete - VerifyOrDie(psa_crypto_init() == PSA_SUCCESS); + ASSERT_EQ(psa_crypto_init(), PSA_SUCCESS); #endif chip::Test::LoopbackMessagingContext::SetUp(); } + + void TearDown() override { chip::Test::LoopbackMessagingContext::TearDown(); } }; enum : uint8_t @@ -115,207 +114,155 @@ class ExpireSessionFromTimeoutDelegate : public WaitForTimeoutDelegate } }; -void CheckNewContextTest(nlTestSuite * inSuite, void * inContext) +TEST_F(TestExchangeMgr, CheckNewContextTest) { - TestContext & ctx = *reinterpret_cast(inContext); - MockAppDelegate mockAppDelegate; - ExchangeContext * ec1 = ctx.NewExchangeToBob(&mockAppDelegate); - NL_TEST_EXIT_ON_FAILED_ASSERT(inSuite, ec1 != nullptr); - NL_TEST_ASSERT(inSuite, ec1->IsInitiator() == true); - NL_TEST_ASSERT(inSuite, ec1->GetSessionHandle() == ctx.GetSessionAliceToBob()); - NL_TEST_ASSERT(inSuite, ec1->GetDelegate() == &mockAppDelegate); + ExchangeContext * ec1 = NewExchangeToBob(&mockAppDelegate); + ASSERT_NE(ec1, nullptr); + EXPECT_EQ(ec1->IsInitiator(), true); + EXPECT_EQ(ec1->GetSessionHandle(), GetSessionAliceToBob()); + EXPECT_EQ(ec1->GetDelegate(), &mockAppDelegate); - ExchangeContext * ec2 = ctx.NewExchangeToAlice(&mockAppDelegate); - NL_TEST_EXIT_ON_FAILED_ASSERT(inSuite, ec2 != nullptr); - NL_TEST_ASSERT(inSuite, ec2->GetExchangeId() > ec1->GetExchangeId()); - NL_TEST_ASSERT(inSuite, ec2->GetSessionHandle() == ctx.GetSessionBobToAlice()); + ExchangeContext * ec2 = NewExchangeToAlice(&mockAppDelegate); + ASSERT_NE(ec2, nullptr); + EXPECT_GT(ec2->GetExchangeId(), ec1->GetExchangeId()); + EXPECT_EQ(ec2->GetSessionHandle(), GetSessionBobToAlice()); ec1->Close(); ec2->Close(); } -void CheckSessionExpirationBasics(nlTestSuite * inSuite, void * inContext) +TEST_F(TestExchangeMgr, CheckSessionExpirationBasics) { - TestContext & ctx = *reinterpret_cast(inContext); - MockAppDelegate sendDelegate; - ExchangeContext * ec1 = ctx.NewExchangeToBob(&sendDelegate); + ExchangeContext * ec1 = NewExchangeToBob(&sendDelegate); + ASSERT_NE(ec1, nullptr); // Expire the session this exchange is supposedly on. ec1->GetSessionHandle()->AsSecureSession()->MarkForEviction(); MockAppDelegate receiveDelegate; CHIP_ERROR err = - ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, kMsgType_TEST1, &receiveDelegate); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, kMsgType_TEST1, &receiveDelegate); + EXPECT_EQ(err, CHIP_NO_ERROR); err = ec1->SendMessage(Protocols::BDX::Id, kMsgType_TEST1, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize), SendFlags(Messaging::SendMessageFlags::kNoAutoRequestAck)); - NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_NE(err, CHIP_NO_ERROR); + DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, !receiveDelegate.IsOnMessageReceivedCalled); + EXPECT_FALSE(receiveDelegate.IsOnMessageReceivedCalled); ec1->Close(); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, kMsgType_TEST1); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, kMsgType_TEST1); + EXPECT_EQ(err, CHIP_NO_ERROR); // recreate closed session. - NL_TEST_ASSERT(inSuite, ctx.CreateSessionAliceToBob() == CHIP_NO_ERROR); + EXPECT_EQ(CreateSessionAliceToBob(), CHIP_NO_ERROR); } -void CheckSessionExpirationTimeout(nlTestSuite * inSuite, void * inContext) +TEST_F(TestExchangeMgr, CheckSessionExpirationTimeout) { - TestContext & ctx = *reinterpret_cast(inContext); - WaitForTimeoutDelegate sendDelegate; - ExchangeContext * ec1 = ctx.NewExchangeToBob(&sendDelegate); + ExchangeContext * ec1 = NewExchangeToBob(&sendDelegate); ec1->SendMessage(Protocols::BDX::Id, kMsgType_TEST1, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize), SendFlags(Messaging::SendMessageFlags::kExpectResponse).Set(Messaging::SendMessageFlags::kNoAutoRequestAck)); - ctx.DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, !sendDelegate.IsOnResponseTimeoutCalled); + DrainAndServiceIO(); + EXPECT_FALSE(sendDelegate.IsOnResponseTimeoutCalled); // Expire the session this exchange is supposedly on. This should close the exchange. ec1->GetSessionHandle()->AsSecureSession()->MarkForEviction(); - NL_TEST_ASSERT(inSuite, sendDelegate.IsOnResponseTimeoutCalled); + EXPECT_TRUE(sendDelegate.IsOnResponseTimeoutCalled); // recreate closed session. - NL_TEST_ASSERT(inSuite, ctx.CreateSessionAliceToBob() == CHIP_NO_ERROR); + EXPECT_EQ(CreateSessionAliceToBob(), CHIP_NO_ERROR); } -void CheckSessionExpirationDuringTimeout(nlTestSuite * inSuite, void * inContext) +TEST_F(TestExchangeMgr, CheckSessionExpirationDuringTimeout) { using namespace chip::System::Clock::Literals; - TestContext & ctx = *reinterpret_cast(inContext); - ExpireSessionFromTimeoutDelegate sendDelegate; - ExchangeContext * ec1 = ctx.NewExchangeToBob(&sendDelegate); + ExchangeContext * ec1 = NewExchangeToBob(&sendDelegate); auto timeout = System::Clock::Timeout(100); ec1->SetResponseTimeout(timeout); - NL_TEST_ASSERT(inSuite, !sendDelegate.IsOnResponseTimeoutCalled); + EXPECT_FALSE(sendDelegate.IsOnResponseTimeoutCalled); ec1->SendMessage(Protocols::BDX::Id, kMsgType_TEST1, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize), SendFlags(Messaging::SendMessageFlags::kExpectResponse).Set(Messaging::SendMessageFlags::kNoAutoRequestAck)); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); // Wait for our timeout to elapse. Give it an extra 1000ms of slack, // because if we lose the timeslice for longer than the slack we could end // up breaking out of the loop before the timeout timer has actually fired. - ctx.GetIOContext().DriveIOUntil(timeout + 1000_ms32, [&sendDelegate] { return sendDelegate.IsOnResponseTimeoutCalled; }); + GetIOContext().DriveIOUntil(timeout + 1000_ms32, [&sendDelegate] { return sendDelegate.IsOnResponseTimeoutCalled; }); - NL_TEST_ASSERT(inSuite, sendDelegate.IsOnResponseTimeoutCalled); + EXPECT_TRUE(sendDelegate.IsOnResponseTimeoutCalled); // recreate closed session. - NL_TEST_ASSERT(inSuite, ctx.CreateSessionAliceToBob() == CHIP_NO_ERROR); + EXPECT_EQ(CreateSessionAliceToBob(), CHIP_NO_ERROR); } -void CheckUmhRegistrationTest(nlTestSuite * inSuite, void * inContext) +TEST_F(TestExchangeMgr, CheckUmhRegistrationTest) { - TestContext & ctx = *reinterpret_cast(inContext); - CHIP_ERROR err; MockAppDelegate mockAppDelegate; - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForProtocol(Protocols::BDX::Id, &mockAppDelegate); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForProtocol(Protocols::BDX::Id, &mockAppDelegate); + EXPECT_EQ(err, CHIP_NO_ERROR); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Protocols::Echo::Id, kMsgType_TEST1, &mockAppDelegate); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Protocols::Echo::Id, kMsgType_TEST1, &mockAppDelegate); + EXPECT_EQ(err, CHIP_NO_ERROR); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::BDX::Id); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::BDX::Id); + EXPECT_EQ(err, CHIP_NO_ERROR); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::Echo::Id); - NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::Echo::Id); + EXPECT_NE(err, CHIP_NO_ERROR); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::Echo::Id, kMsgType_TEST1); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::Echo::Id, kMsgType_TEST1); + EXPECT_EQ(err, CHIP_NO_ERROR); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::Echo::Id, kMsgType_TEST2); - NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::Echo::Id, kMsgType_TEST2); + EXPECT_NE(err, CHIP_NO_ERROR); } -void CheckExchangeMessages(nlTestSuite * inSuite, void * inContext) +TEST_F(TestExchangeMgr, CheckExchangeMessages) { - TestContext & ctx = *reinterpret_cast(inContext); - CHIP_ERROR err; // create solicited exchange MockAppDelegate mockSolicitedAppDelegate; - ExchangeContext * ec1 = ctx.NewExchangeToAlice(&mockSolicitedAppDelegate); + ExchangeContext * ec1 = NewExchangeToAlice(&mockSolicitedAppDelegate); // create unsolicited exchange MockAppDelegate mockUnsolicitedAppDelegate; - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, kMsgType_TEST1, - &mockUnsolicitedAppDelegate); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, kMsgType_TEST1, + &mockUnsolicitedAppDelegate); + EXPECT_EQ(err, CHIP_NO_ERROR); // send a malicious packet ec1->SendMessage(Protocols::BDX::Id, kMsgType_TEST2, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize), SendFlags(Messaging::SendMessageFlags::kNoAutoRequestAck)); - ctx.DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, !mockUnsolicitedAppDelegate.IsOnMessageReceivedCalled); + DrainAndServiceIO(); + EXPECT_FALSE(mockUnsolicitedAppDelegate.IsOnMessageReceivedCalled); - ec1 = ctx.NewExchangeToAlice(&mockSolicitedAppDelegate); + ec1 = NewExchangeToAlice(&mockSolicitedAppDelegate); // send a good packet ec1->SendMessage(Protocols::BDX::Id, kMsgType_TEST1, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize), SendFlags(Messaging::SendMessageFlags::kNoAutoRequestAck)); - ctx.DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, mockUnsolicitedAppDelegate.IsOnMessageReceivedCalled); + DrainAndServiceIO(); + EXPECT_TRUE(mockUnsolicitedAppDelegate.IsOnMessageReceivedCalled); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, kMsgType_TEST1); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, kMsgType_TEST1); + EXPECT_EQ(err, CHIP_NO_ERROR); } -// Test Suite - -/** - * Test Suite that lists all the test functions. - */ -// clang-format off -const nlTest sTests[] = -{ - NL_TEST_DEF("Test ExchangeMgr::NewContext", CheckNewContextTest), - NL_TEST_DEF("Test ExchangeMgr::CheckUmhRegistrationTest", CheckUmhRegistrationTest), - NL_TEST_DEF("Test ExchangeMgr::CheckExchangeMessages", CheckExchangeMessages), - NL_TEST_DEF("Test OnConnectionExpired basics", CheckSessionExpirationBasics), - NL_TEST_DEF("Test OnConnectionExpired timeout handling", CheckSessionExpirationTimeout), - NL_TEST_DEF("Test session eviction in timeout handling", CheckSessionExpirationDuringTimeout), - - NL_TEST_SENTINEL() -}; -// clang-format on - -// clang-format off -nlTestSuite sSuite = -{ - "Test-CHIP-ExchangeManager", - &sTests[0], - NL_TEST_WRAP_FUNCTION(TestContext::SetUpTestSuite), - NL_TEST_WRAP_FUNCTION(TestContext::TearDownTestSuite), - NL_TEST_WRAP_METHOD(TestContext, SetUp), - NL_TEST_WRAP_METHOD(TestContext, TearDown), -}; -// clang-format on - } // namespace - -/** - * Main - */ -int TestExchangeMgr() -{ - return chip::ExecuteTestsWithContext(&sSuite); -} - -CHIP_REGISTER_TEST_SUITE(TestExchangeMgr); diff --git a/src/messaging/tests/TestMessagingLayer.cpp b/src/messaging/tests/TestMessagingLayer.cpp index 00dca89f2b6639..4e52cb96972ea6 100644 --- a/src/messaging/tests/TestMessagingLayer.cpp +++ b/src/messaging/tests/TestMessagingLayer.cpp @@ -20,13 +20,15 @@ * @file * This file implements unit tests for the ExchangeManager implementation. */ +#include +#include + +#include #include #include #include #include -#include -#include #include #include #include @@ -36,12 +38,6 @@ #include #include -#include -#include - -#include -#include - namespace { using namespace chip; @@ -53,6 +49,17 @@ using namespace chip::System::Clock::Literals; using TestContext = Test::UDPMessagingContext; +struct TestMessagingLayer : public chip::Test::UDPMessagingContext, public ::testing::Test +{ + static void SetUpTestSuite() { chip::Test::UDPMessagingContext::SetUpTestSuite(); } + static void TearDownTestSuite() { chip::Test::UDPMessagingContext::TearDownTestSuite(); } + + // Performs setup for each individual test in the test suite + void SetUp() override { chip::Test::UDPMessagingContext::SetUp(); } + + void TearDown() override { chip::Test::UDPMessagingContext::TearDown(); } +}; + // The message timeout value in milliseconds. constexpr System::Clock::Timeout kMessageTimeout = System::Clock::Milliseconds32(100); @@ -87,25 +94,23 @@ class MockAppDelegate : public UnsolicitedMessageHandler, public ExchangeDelegat * - Confirm the message is sent successfully * - Observe DUT response timeout with no response */ -void CheckExchangeOutgoingMessagesSuccess(nlTestSuite * inSuite, void * inContext) +TEST_F(TestMessagingLayer, CheckExchangeOutgoingMessagesSuccess) { - TestContext & ctx = *reinterpret_cast(inContext); - // create solicited exchange MockAppDelegate mockSolicitedAppDelegate; - ExchangeContext * ec = ctx.NewExchangeToAlice(&mockSolicitedAppDelegate); + ExchangeContext * ec = NewExchangeToAlice(&mockSolicitedAppDelegate); - NL_TEST_ASSERT(inSuite, ec != nullptr); + ASSERT_NE(ec, nullptr); ec->SetResponseTimeout(kMessageTimeout); CHIP_ERROR err = ec->SendMessage(Echo::MsgType::EchoRequest, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize), SendFlags(SendMessageFlags::kExpectResponse).Set(SendMessageFlags::kNoAutoRequestAck)); // Wait for the initial message to fail (should take 330-413ms) - ctx.GetIOContext().DriveIOUntil(500_ms32, [&] { return mockSolicitedAppDelegate.IsOnMessageReceivedCalled; }); + GetIOContext().DriveIOUntil(500_ms32, [&] { return mockSolicitedAppDelegate.IsOnMessageReceivedCalled; }); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, mockSolicitedAppDelegate.IsOnResponseTimeoutCalled); + EXPECT_EQ(err, CHIP_NO_ERROR); + EXPECT_TRUE(mockSolicitedAppDelegate.IsOnResponseTimeoutCalled); } /** @@ -118,15 +123,13 @@ void CheckExchangeOutgoingMessagesSuccess(nlTestSuite * inSuite, void * inContex * - Confirm the message is sent with failure * - Confirm the DUT response timeout timer is cancelled */ -void CheckExchangeOutgoingMessagesFail(nlTestSuite * inSuite, void * inContext) +TEST_F(TestMessagingLayer, CheckExchangeOutgoingMessagesFail) { - TestContext & ctx = *reinterpret_cast(inContext); - // create solicited exchange MockAppDelegate mockSolicitedAppDelegate; - ExchangeContext * ec = ctx.NewExchangeToAlice(&mockSolicitedAppDelegate); + ExchangeContext * ec = NewExchangeToAlice(&mockSolicitedAppDelegate); - NL_TEST_ASSERT(inSuite, ec != nullptr); + ASSERT_NE(ec, nullptr); ec->SetResponseTimeout(kMessageTimeout); chip::FaultInjection::GetManager().FailAtFault(chip::FaultInjection::kFault_DropOutgoingUDPMsg, 0, 1); @@ -135,48 +138,11 @@ void CheckExchangeOutgoingMessagesFail(nlTestSuite * inSuite, void * inContext) SendFlags(SendMessageFlags::kExpectResponse).Set(SendMessageFlags::kNoAutoRequestAck)); // Wait for the initial message to fail (should take 330-413ms) - ctx.GetIOContext().DriveIOUntil(500_ms32, [&] { return mockSolicitedAppDelegate.IsOnMessageReceivedCalled; }); + GetIOContext().DriveIOUntil(500_ms32, [&] { return mockSolicitedAppDelegate.IsOnMessageReceivedCalled; }); - NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, !mockSolicitedAppDelegate.IsOnResponseTimeoutCalled); + EXPECT_NE(err, CHIP_NO_ERROR); + EXPECT_FALSE(mockSolicitedAppDelegate.IsOnResponseTimeoutCalled); ec->Close(); } -// Test Suite - -/** - * Test Suite that lists all the test functions. - */ -// clang-format off -const nlTest sTests[] = -{ - NL_TEST_DEF("Test MessagingLayer::ExchangeOutgoingMessagesSuccess", CheckExchangeOutgoingMessagesSuccess), - NL_TEST_DEF("Test MessagingLayer::ExchangeOutgoingMessagesFail", CheckExchangeOutgoingMessagesFail), - - NL_TEST_SENTINEL() -}; -// clang-format on - -// clang-format off -nlTestSuite sSuite = -{ - "Test-CHIP-MessagingLayer", - &sTests[0], - NL_TEST_WRAP_FUNCTION(TestContext::SetUpTestSuite), - NL_TEST_WRAP_FUNCTION(TestContext::TearDownTestSuite), - NL_TEST_WRAP_METHOD(TestContext, SetUp), - NL_TEST_WRAP_METHOD(TestContext, TearDown), -}; -// clang-format on - } // namespace - -/** - * Main - */ -int TestMessagingLayer() -{ - return chip::ExecuteTestsWithContext(&sSuite); -} - -CHIP_REGISTER_TEST_SUITE(TestMessagingLayer); diff --git a/src/messaging/tests/TestReliableMessageProtocol.cpp b/src/messaging/tests/TestReliableMessageProtocol.cpp index 68342838a735b1..d965c172a1c010 100644 --- a/src/messaging/tests/TestReliableMessageProtocol.cpp +++ b/src/messaging/tests/TestReliableMessageProtocol.cpp @@ -21,13 +21,13 @@ * This file implements unit tests for the ReliableMessageProtocol * implementation. */ +#include + +#include #include #include #include -#include -#include -#include #include #include #include @@ -36,11 +36,6 @@ #include #include -#include -#include - -#include - #include #include #include @@ -65,26 +60,30 @@ using namespace chip::System::Clock::Literals; const char PAYLOAD[] = "Hello!"; -class TestContext : public chip::Test::LoopbackMessagingContext +class TestReliableMessageProtocol : public chip::Test::LoopbackMessagingContext, public ::testing::Test { public: + static void SetUpTestSuite() { chip::Test::LoopbackMessagingContext::SetUpTestSuite(); } + static void TearDownTestSuite() { chip::Test::LoopbackMessagingContext::TearDownTestSuite(); } + // Performs setup for each individual test in the test suite void SetUp() override { #if CHIP_CRYPTO_PSA - // TODO: use ASSERT_EQ, once transition to pw_unit_test is complete - VerifyOrDie(psa_crypto_init() == PSA_SUCCESS); + ASSERT_EQ(psa_crypto_init(), PSA_SUCCESS); #endif chip::Test::LoopbackMessagingContext::SetUp(); GetSessionAliceToBob()->AsSecureSession()->SetRemoteSessionParameters(GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig())); GetSessionBobToAlice()->AsSecureSession()->SetRemoteSessionParameters(GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig())); } + + void TearDown() override { chip::Test::LoopbackMessagingContext::TearDown(); } }; class MockAppDelegate : public UnsolicitedMessageHandler, public ExchangeDelegate { public: - MockAppDelegate(TestContext & ctx) : mTestContext(ctx) {} + MockAppDelegate(TestReliableMessageProtocol & ctx) : mTestReliableMessageProtocol(ctx) {} CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate) override { @@ -128,11 +127,8 @@ class MockAppDelegate : public UnsolicitedMessageHandler, public ExchangeDelegat } mExchange = ec; - if (mTestSuite != nullptr) - { - NL_TEST_ASSERT(mTestSuite, buffer->TotalLength() == sizeof(PAYLOAD)); - NL_TEST_ASSERT(mTestSuite, memcmp(buffer->Start(), PAYLOAD, buffer->TotalLength()) == 0); - } + EXPECT_EQ(buffer->TotalLength(), sizeof(PAYLOAD)); + EXPECT_EQ(memcmp(buffer->Start(), PAYLOAD, buffer->TotalLength()), 0); return CHIP_NO_ERROR; } @@ -155,7 +151,7 @@ class MockAppDelegate : public UnsolicitedMessageHandler, public ExchangeDelegat // Restart the MRP retransmit timer, now that we are not going to be // dropping acks anymore, so we send out pending retransmits, if // any, as needed. - mTestContext.GetExchangeManager().GetReliableMessageMgr()->StartTimer(); + mTestReliableMessageProtocol.GetExchangeManager().GetReliableMessageMgr()->StartTimer(); } } @@ -164,10 +160,9 @@ class MockAppDelegate : public UnsolicitedMessageHandler, public ExchangeDelegat bool mRetainExchange = false; bool mResponseTimedOut = false; ExchangeContext * mExchange = nullptr; - nlTestSuite * mTestSuite = nullptr; private: - TestContext & mTestContext; + TestReliableMessageProtocol & mTestReliableMessageProtocol; bool mDropAckResponse = false; }; @@ -199,11 +194,8 @@ class MockSessionEstablishmentDelegate : public UnsolicitedMessageHandler, publi System::PacketBufferHandle && buffer) override { IsOnMessageReceivedCalled = true; - if (mTestSuite != nullptr) - { - NL_TEST_ASSERT(mTestSuite, buffer->TotalLength() == sizeof(PAYLOAD)); - NL_TEST_ASSERT(mTestSuite, memcmp(buffer->Start(), PAYLOAD, buffer->TotalLength()) == 0); - } + EXPECT_EQ(buffer->TotalLength(), sizeof(PAYLOAD)); + EXPECT_EQ(memcmp(buffer->Start(), PAYLOAD, buffer->TotalLength()), 0); return CHIP_NO_ERROR; } @@ -213,7 +205,6 @@ class MockSessionEstablishmentDelegate : public UnsolicitedMessageHandler, publi bool IsOnMessageReceivedCalled = false; MockSessionEstablishmentExchangeDispatch mMessageDispatch; - nlTestSuite * mTestSuite = nullptr; }; struct BackoffComplianceTestVector @@ -316,7 +307,7 @@ struct BackoffComplianceTestVector theBackoffComplianceTestVector[] = { { .backoffMax = System::Clock::Timeout(20'286'001), } }; -void CheckGetBackoffImpl(nlTestSuite * inSuite, System::Clock::Timeout additionalMRPBackoffTime) +void CheckGetBackoffImpl(System::Clock::Timeout additionalMRPBackoffTime) { ReliableMessageMgr::SetAdditionalMRPBackoffTime(MakeOptional(additionalMRPBackoffTime)); @@ -336,8 +327,8 @@ void CheckGetBackoffImpl(nlTestSuite * inSuite, System::Clock::Timeout additiona ChipLogProgress(Test, "Backoff base %" PRIu32 " extra %" PRIu32 " # %d: %" PRIu32, test.backoffBase.count(), extraBackoff.count(), test.sendCount, backoff.count()); - NL_TEST_ASSERT(inSuite, backoff >= test.backoffMin + extraBackoff); - NL_TEST_ASSERT(inSuite, backoff <= test.backoffMax + extraBackoff); + EXPECT_GE(backoff, test.backoffMin + extraBackoff); + EXPECT_LE(backoff, test.backoffMax + extraBackoff); } } @@ -346,52 +337,23 @@ void CheckGetBackoffImpl(nlTestSuite * inSuite, System::Clock::Timeout additiona } // namespace -class TestReliableMessageProtocol +TEST_F(TestReliableMessageProtocol, CheckAddClearRetrans) { -public: - static void CheckAddClearRetrans(nlTestSuite * inSuite, void * inContext); - static void CheckResendApplicationMessage(nlTestSuite * inSuite, void * inContext); - static void CheckCloseExchangeAndResendApplicationMessage(nlTestSuite * inSuite, void * inContext); - static void CheckFailedMessageRetainOnSend(nlTestSuite * inSuite, void * inContext); - static void CheckResendApplicationMessageWithPeerExchange(nlTestSuite * inSuite, void * inContext); - static void CheckResendSessionEstablishmentMessageWithPeerExchange(nlTestSuite * inSuite, void * inContext); - static void CheckDuplicateMessage(nlTestSuite * inSuite, void * inContext); - static void CheckDuplicateMessageClosedExchange(nlTestSuite * inSuite, void * inContext); - static void CheckDuplicateOldMessageClosedExchange(nlTestSuite * inSuite, void * inContext); - static void CheckReceiveAfterStandaloneAck(nlTestSuite * inSuite, void * inContext); - static void CheckPiggybackAfterPiggyback(nlTestSuite * inSuite, void * inContext); - static void CheckSendUnsolicitedStandaloneAckMessage(nlTestSuite * inSuite, void * inContext); - static void CheckSendStandaloneAckMessage(nlTestSuite * inSuite, void * inContext); - static void CheckMessageAfterClosed(nlTestSuite * inSuite, void * inContext); - static void CheckUnencryptedMessageReceiveFailure(nlTestSuite * inSuite, void * inContext); - static void CheckLostResponseWithPiggyback(nlTestSuite * inSuite, void * inContext); - static void CheckLostStandaloneAck(nlTestSuite * inSuite, void * inContext); - static void CheckIsPeerActiveNotInitiator(nlTestSuite * inSuite, void * inContext); - static void CheckGetBackoff(nlTestSuite * inSuite, void * inContext); - static void CheckGetBackoffAdditionalTime(nlTestSuite * inSuite, void * inContext); - static void CheckApplicationResponseDelayed(nlTestSuite * inSuite, void * inContext); - static void CheckApplicationResponseNeverComes(nlTestSuite * inSuite, void * inContext); -}; + MockAppDelegate mockAppDelegate(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockAppDelegate); + ASSERT_NE(exchange, nullptr); -void TestReliableMessageProtocol::CheckAddClearRetrans(nlTestSuite * inSuite, void * inContext) -{ - TestContext & ctx = *reinterpret_cast(inContext); - - MockAppDelegate mockAppDelegate(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockAppDelegate); - NL_TEST_ASSERT(inSuite, exchange != nullptr); - - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); ReliableMessageContext * rc = exchange->GetReliableMessageContext(); - NL_TEST_ASSERT(inSuite, rm != nullptr); - NL_TEST_ASSERT(inSuite, rc != nullptr); + ASSERT_NE(rm, nullptr); + ASSERT_NE(rc, nullptr); ReliableMessageMgr::RetransTableEntry * entry; rm->AddToRetransTable(rc, &entry); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); rm->ClearRetransTable(*entry); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); exchange->Close(); } @@ -422,26 +384,25 @@ void TestReliableMessageProtocol::CheckAddClearRetrans(nlTestSuite * inSuite, vo * - PEER to acknowledge message * - Observe DUT signal successful reliable transmission */ -void TestReliableMessageProtocol::CheckResendApplicationMessage(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckResendApplicationMessage) { - TestContext & ctx = *reinterpret_cast(inContext); BackoffComplianceTestVector * expectedBackoff; System::Clock::Timestamp now, startTime; System::Clock::Timeout timeoutTime, margin; margin = System::Clock::Timeout(15); chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockSender(ctx); + MockAppDelegate mockSender(*this); // TODO: temporarily create a SessionHandle from node id, will be fix in PR 3602 - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ System::Clock::Timestamp(300), // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL @@ -449,112 +410,111 @@ void TestReliableMessageProtocol::CheckResendApplicationMessage(nlTestSuite * in })); // Let's drop the initial message - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 4; loopback.mDroppedMessageCount = 0; // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // Ensure the exchange stays open after we send (unlike the CheckCloseExchangeAndResendApplicationMessage case), by claiming to // expect a response. startTime = System::SystemClock().GetMonotonicTimestamp(); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendMessageFlags::kExpectResponse); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the initial message was dropped and was added to retransmit table - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 3); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(loopback.mNumMessagesToDrop, 3u); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // Wait for the initial message to fail (should take 330-413ms) - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); now = System::SystemClock().GetMonotonicTimestamp(); timeoutTime = now - startTime; ChipLogProgress(Test, "Attempt #1 Timeout : %" PRIu32 "ms", timeoutTime.count()); expectedBackoff = &theBackoffComplianceTestVector[0]; - NL_TEST_ASSERT(inSuite, timeoutTime >= expectedBackoff->backoffMin - margin); + EXPECT_GE(timeoutTime, expectedBackoff->backoffMin - margin); startTime = System::SystemClock().GetMonotonicTimestamp(); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); // Ensure the 1st retry was dropped, and is still there in the retransmit table - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 2); - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 2); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 2); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(loopback.mSentMessageCount, 2u); + EXPECT_EQ(loopback.mNumMessagesToDrop, 2u); + EXPECT_EQ(loopback.mDroppedMessageCount, 2u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // Wait for the 1st retry to fail (should take 330-413ms) - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); now = System::SystemClock().GetMonotonicTimestamp(); timeoutTime = now - startTime; ChipLogProgress(Test, "Attempt #2 Timeout : %" PRIu32 "ms", timeoutTime.count()); expectedBackoff = &theBackoffComplianceTestVector[1]; - NL_TEST_ASSERT(inSuite, timeoutTime >= expectedBackoff->backoffMin - margin); + EXPECT_GE(timeoutTime, expectedBackoff->backoffMin - margin); startTime = System::SystemClock().GetMonotonicTimestamp(); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); // Ensure the 2nd retry was dropped, and is still there in the retransmit table - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 3); - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 3); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(loopback.mSentMessageCount, 3u); + EXPECT_EQ(loopback.mNumMessagesToDrop, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 3u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // Wait for the 2nd retry to fail (should take 528-660ms) - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 4; }); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 4; }); now = System::SystemClock().GetMonotonicTimestamp(); timeoutTime = now - startTime; ChipLogProgress(Test, "Attempt #3 Timeout : %" PRIu32 "ms", timeoutTime.count()); expectedBackoff = &theBackoffComplianceTestVector[2]; - NL_TEST_ASSERT(inSuite, timeoutTime >= expectedBackoff->backoffMin - margin); + EXPECT_GE(timeoutTime, expectedBackoff->backoffMin - margin); startTime = System::SystemClock().GetMonotonicTimestamp(); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); // Ensure the 3rd retry was dropped, and is still there in the retransmit table - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 4); - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 0); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 4); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(loopback.mSentMessageCount, 4u); + EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); + EXPECT_EQ(loopback.mDroppedMessageCount, 4u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // Wait for the 3rd retry to fail (should take 845-1056ms) - ctx.GetIOContext().DriveIOUntil(1500_ms32, [&] { return loopback.mSentMessageCount >= 5; }); + GetIOContext().DriveIOUntil(1500_ms32, [&] { return loopback.mSentMessageCount >= 5; }); now = System::SystemClock().GetMonotonicTimestamp(); timeoutTime = now - startTime; ChipLogProgress(Test, "Attempt #4 Timeout : %" PRIu32 "ms", timeoutTime.count()); expectedBackoff = &theBackoffComplianceTestVector[3]; - NL_TEST_ASSERT(inSuite, timeoutTime >= expectedBackoff->backoffMin - margin); + EXPECT_GE(timeoutTime, expectedBackoff->backoffMin - margin); // Trigger final transmission - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); // Ensure the last retransmission was NOT dropped, and the retransmit table is empty, as we should have gotten an ack - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount >= 5); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 4); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_GE(loopback.mSentMessageCount, 5u); + EXPECT_EQ(loopback.mDroppedMessageCount, 4u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); exchange->Close(); } -void TestReliableMessageProtocol::CheckCloseExchangeAndResendApplicationMessage(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckCloseExchangeAndResendApplicationMessage) { - TestContext & ctx = *reinterpret_cast(inContext); chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockSender(ctx); + MockAppDelegate mockSender(*this); // TODO: temporarily create a SessionHandle from node id, will be fixed in PR 3602 - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ 64_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL @@ -562,58 +522,57 @@ void TestReliableMessageProtocol::CheckCloseExchangeAndResendApplicationMessage( })); // Let's drop the initial message - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 2; loopback.mDroppedMessageCount = 0; // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was dropped, and was added to retransmit table - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(loopback.mNumMessagesToDrop, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // Wait for the first re-transmit (should take 64ms) - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); + DrainAndServiceIO(); // Ensure the retransmit message was dropped, and is still there in the retransmit table - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 2); - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 0); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 2); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(loopback.mSentMessageCount, 2u); + EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); + EXPECT_EQ(loopback.mDroppedMessageCount, 2u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // Wait for the second re-transmit (should take 64ms) - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); + DrainAndServiceIO(); // Ensure the retransmit message was NOT dropped, and the retransmit table is empty, as we should have gotten an ack - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount >= 3); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 2); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_GE(loopback.mSentMessageCount, 3u); + EXPECT_EQ(loopback.mDroppedMessageCount, 2u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); } -void TestReliableMessageProtocol::CheckFailedMessageRetainOnSend(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckFailedMessageRetainOnSend) { - TestContext & ctx = *reinterpret_cast(inContext); chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; MockSessionEstablishmentDelegate mockSender; - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ 64_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL @@ -622,88 +581,82 @@ void TestReliableMessageProtocol::CheckFailedMessageRetainOnSend(nlTestSuite * i mockSender.mMessageDispatch.mRetainMessageOnSend = false; // Let's drop the initial message - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 1; loopback.mDroppedMessageCount = 0; // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was dropped - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); // Wait for the first re-transmit (should take 64ms) - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); + DrainAndServiceIO(); // Ensure the retransmit table is empty, as we did not provide a message to retain - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); } -void TestReliableMessageProtocol::CheckUnencryptedMessageReceiveFailure(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckUnencryptedMessageReceiveFailure) { - TestContext & ctx = *reinterpret_cast(inContext); - chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); MockSessionEstablishmentDelegate mockReceiver; - CHIP_ERROR err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + CHIP_ERROR err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); // Expect the received messages to be encrypted mockReceiver.mMessageDispatch.mRequireEncryption = true; MockSessionEstablishmentDelegate mockSender; - ExchangeContext * exchange = ctx.NewUnauthenticatedExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + ExchangeContext * exchange = NewUnauthenticatedExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 0; loopback.mDroppedMessageCount = 0; // We are sending a malicious packet, doesn't expect an ack err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kNoAutoRequestAck)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Test that the message was actually sent (and not dropped) - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // Test that the message was dropped by the receiver - NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); } -void TestReliableMessageProtocol::CheckResendApplicationMessageWithPeerExchange(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckResendApplicationMessageWithPeerExchange) { - TestContext & ctx = *reinterpret_cast(inContext); - chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockReceiver(ctx); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - mockReceiver.mTestSuite = inSuite; + MockAppDelegate mockReceiver(*this); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ 64_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL @@ -711,61 +664,55 @@ void TestReliableMessageProtocol::CheckResendApplicationMessageWithPeerExchange( })); // Let's drop the initial message - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 1; loopback.mDroppedMessageCount = 0; // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was dropped, and was added to retransmit table - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 0); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); - NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); + EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); + EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); // Wait for the first re-transmit (should take 64ms) - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); + DrainAndServiceIO(); // Ensure the retransmit message was not dropped, and is no longer in the retransmit table - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount >= 2); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); - - mockReceiver.mTestSuite = nullptr; + EXPECT_GE(loopback.mSentMessageCount, 2u); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); + EXPECT_EQ(err, CHIP_NO_ERROR); } -void TestReliableMessageProtocol::CheckDuplicateMessageClosedExchange(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckDuplicateMessageClosedExchange) { - TestContext & ctx = *reinterpret_cast(inContext); - chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockReceiver(ctx); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + MockAppDelegate mockReceiver(*this); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); - mockReceiver.mTestSuite = inSuite; + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); - - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ 64_ms32, // CHIP_CONFIG_RMP_DEFAULT_INITIAL_RETRY_INTERVAL @@ -773,7 +720,7 @@ void TestReliableMessageProtocol::CheckDuplicateMessageClosedExchange(nlTestSuit })); // Let's not drop the message. Expectation is that it is received by the peer, but the ack is dropped - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 0; loopback.mDroppedMessageCount = 0; @@ -783,56 +730,52 @@ void TestReliableMessageProtocol::CheckDuplicateMessageClosedExchange(nlTestSuit mockReceiver.mRetainExchange = false; // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was sent // The ack was dropped, and message was added to the retransmit table - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // Let's not drop the duplicate message mockReceiver.SetDropAckResponse(false); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); + EXPECT_EQ(err, CHIP_NO_ERROR); // Wait for the first re-transmit and ack (should take 64ms) - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); + DrainAndServiceIO(); // Ensure the retransmit message was sent and the ack was sent // and retransmit table was cleared - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 3); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(loopback.mSentMessageCount, 3u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); } -void TestReliableMessageProtocol::CheckDuplicateOldMessageClosedExchange(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckDuplicateOldMessageClosedExchange) { - TestContext & ctx = *reinterpret_cast(inContext); - chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockReceiver(ctx); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - mockReceiver.mTestSuite = inSuite; + MockAppDelegate mockReceiver(*this); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ 64_ms32, // CHIP_CONFIG_RMP_DEFAULT_INITIAL_RETRY_INTERVAL @@ -840,7 +783,7 @@ void TestReliableMessageProtocol::CheckDuplicateOldMessageClosedExchange(nlTestS })); // Let's not drop the message. Expectation is that it is received by the peer, but the ack is dropped - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 0; loopback.mDroppedMessageCount = 0; @@ -850,17 +793,17 @@ void TestReliableMessageProtocol::CheckDuplicateOldMessageClosedExchange(nlTestS mockReceiver.mRetainExchange = false; // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was sent // The ack was dropped, and message was added to the retransmit table - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // Now send CHIP_CONFIG_MESSAGE_COUNTER_WINDOW_SIZE + 2 messages to make // sure our original message is out of the message counter window. These @@ -870,70 +813,65 @@ void TestReliableMessageProtocol::CheckDuplicateOldMessageClosedExchange(nlTestS for (size_t i = 0; i < extraMessages; ++i) { buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); - ExchangeContext * newExchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, newExchange != nullptr); + ExchangeContext * newExchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(newExchange, nullptr); mockReceiver.mRetainExchange = false; // Ensure the retransmit table has our one message right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // Send without MRP. err = newExchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendMessageFlags::kNoAutoRequestAck); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was sent, but not added to the retransmit table. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1 + (i + 1)); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(loopback.mSentMessageCount, 1u + (i + 1u)); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); } // Let's not drop the duplicate message's ack. mockReceiver.SetDropAckResponse(false); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); + EXPECT_EQ(err, CHIP_NO_ERROR); // Wait for the first re-transmit and ack (should take 64ms) rm->StartTimer(); - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3 + extraMessages; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3 + extraMessages; }); + DrainAndServiceIO(); // Ensure the retransmit message was sent and the ack was sent // and retransmit table was cleared - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 3 + extraMessages); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(loopback.mSentMessageCount, 3u + extraMessages); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); } -void TestReliableMessageProtocol::CheckResendSessionEstablishmentMessageWithPeerExchange(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckResendSessionEstablishmentMessageWithPeerExchange) { // Making this static to reduce stack usage, as some platforms have limits on stack size. - static Test::MessagingContext ctx; - - TestContext & inctx = *static_cast(inContext); - - CHIP_ERROR err = ctx.InitFromExisting(inctx); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + static chip::Test::MessagingContext ctx; + CHIP_ERROR err = ctx.InitFromExisting(*this); + EXPECT_EQ(err, CHIP_NO_ERROR); chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + ASSERT_FALSE(buffer.IsNull()); MockSessionEstablishmentDelegate mockReceiver; err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - mockReceiver.mTestSuite = inSuite; + EXPECT_EQ(err, CHIP_NO_ERROR); MockSessionEstablishmentDelegate mockSender; ExchangeContext * exchange = ctx.NewUnauthenticatedExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + ASSERT_NE(exchange, nullptr); ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ASSERT_NE(rm, nullptr); exchange->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ 64_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL @@ -941,63 +879,57 @@ void TestReliableMessageProtocol::CheckResendSessionEstablishmentMessageWithPeer })); // Let's drop the initial message - auto & loopback = inctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 1; loopback.mDroppedMessageCount = 0; // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - inctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was dropped, and was added to retransmit table - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 0); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); - NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); + EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); + EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); // Wait for the first re-transmit (should take 64ms) - inctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); - inctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); + DrainAndServiceIO(); // Ensure the retransmit message was not dropped, and is no longer in the retransmit table - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount >= 2); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); - - mockReceiver.mTestSuite = nullptr; + EXPECT_GE(loopback.mSentMessageCount, 2u); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); - ctx.ShutdownAndRestoreExisting(inctx); + ctx.ShutdownAndRestoreExisting(*this); } -void TestReliableMessageProtocol::CheckDuplicateMessage(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckDuplicateMessage) { - TestContext & ctx = *reinterpret_cast(inContext); - chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockReceiver(ctx); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + MockAppDelegate mockReceiver(*this); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); - mockReceiver.mTestSuite = inSuite; + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); - - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ 64_ms32, // CHIP_CONFIG_RMP_DEFAULT_INITIAL_RETRY_INTERVAL @@ -1005,7 +937,7 @@ void TestReliableMessageProtocol::CheckDuplicateMessage(nlTestSuite * inSuite, v })); // Let's not drop the message. Expectation is that it is received by the peer, but the ack is dropped - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 0; loopback.mDroppedMessageCount = 0; @@ -1015,154 +947,142 @@ void TestReliableMessageProtocol::CheckDuplicateMessage(nlTestSuite * inSuite, v mockReceiver.mRetainExchange = true; // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was sent // The ack was dropped, and message was added to the retransmit table - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); + EXPECT_EQ(err, CHIP_NO_ERROR); // Let's not drop the duplicate message mockReceiver.SetDropAckResponse(false); mockReceiver.mRetainExchange = false; // Wait for the first re-transmit and ack (should take 64ms) - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); + DrainAndServiceIO(); // Ensure the retransmit message was sent and the ack was sent // and retransmit table was cleared - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 3); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(loopback.mSentMessageCount, 3u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); mockReceiver.CloseExchangeIfNeeded(); } -void TestReliableMessageProtocol::CheckReceiveAfterStandaloneAck(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckReceiveAfterStandaloneAck) { - TestContext & ctx = *reinterpret_cast(inContext); - chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockReceiver(ctx); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - mockReceiver.mTestSuite = inSuite; + MockAppDelegate mockReceiver(*this); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - mockSender.mTestSuite = inSuite; - - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // We send a message, have it get received by the peer, then an ack is // returned, then a reply is returned. We need to keep the receiver // exchange alive until it does the message send (so we can send the // response from the receiver and so the initial sender exchange can get // it). - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 0; loopback.mDroppedMessageCount = 0; mockReceiver.mRetainExchange = true; err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // And that it was received. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // And that we have not seen an ack yet. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); ReliableMessageContext * receiverRc = mockReceiver.mExchange->GetReliableMessageContext(); - NL_TEST_ASSERT(inSuite, receiverRc->IsAckPending()); + EXPECT_TRUE(receiverRc->IsAckPending()); // Send the standalone ack. receiverRc->SendStandaloneAckMessage(); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); // Ensure the ack was sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 2); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 2u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // Ensure that we have not gotten any app-level responses so far. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // And that we have now gotten our ack. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // Now send a message from the other side. buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the response and its ack was sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 4); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 4u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // Ensure that we have received that response. - NL_TEST_ASSERT(inSuite, mockSender.IsOnMessageReceivedCalled); + EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); + EXPECT_EQ(err, CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); } -void TestReliableMessageProtocol::CheckPiggybackAfterPiggyback(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckPiggybackAfterPiggyback) { - TestContext & ctx = *reinterpret_cast(inContext); - chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockReceiver(ctx); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - mockReceiver.mTestSuite = inSuite; + MockAppDelegate mockReceiver(*this); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - mockSender.mTestSuite = inSuite; - - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // We send a message, have it get received by the peer, have the peer return // a piggybacked ack. Then we send a second message this time _not_ @@ -1170,7 +1090,7 @@ void TestReliableMessageProtocol::CheckPiggybackAfterPiggyback(nlTestSuite * inS // piggybacked. We need to keep both exchanges alive for that (so we can // send the response from the receiver and so the initial sender exchange // can get it). - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 0; loopback.mDroppedMessageCount = 0; @@ -1178,45 +1098,45 @@ void TestReliableMessageProtocol::CheckPiggybackAfterPiggyback(nlTestSuite * inS mockSender.mRetainExchange = true; err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // And that it was received. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // And that we have not seen an ack yet. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); ReliableMessageContext * receiverRc = mockReceiver.mExchange->GetReliableMessageContext(); - NL_TEST_ASSERT(inSuite, receiverRc->IsAckPending()); + EXPECT_TRUE(receiverRc->IsAckPending()); // Ensure that we have not gotten any app-level responses or acks so far. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, !mockSender.mReceivedPiggybackAck); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockSender.mReceivedPiggybackAck); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // Now send a message from the other side. buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse).Set(SendMessageFlags::kNoAutoRequestAck)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the response was sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 2); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 2u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // Ensure that we have received that response and it had a piggyback ack. - NL_TEST_ASSERT(inSuite, mockSender.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, mockSender.mReceivedPiggybackAck); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); + EXPECT_TRUE(mockSender.mReceivedPiggybackAck); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // Reset various state so we can measure things again. mockReceiver.IsOnMessageReceivedCalled = false; @@ -1225,22 +1145,22 @@ void TestReliableMessageProtocol::CheckPiggybackAfterPiggyback(nlTestSuite * inS // Now send a new message to the other side, but don't ask for an ack. buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse).Set(SendMessageFlags::kNoAutoRequestAck)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 3); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 3u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // And that it was received. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // And that we are not expecting an ack. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // Send the final response. At this point we don't need to keep the // exchanges alive anymore. @@ -1248,27 +1168,27 @@ void TestReliableMessageProtocol::CheckPiggybackAfterPiggyback(nlTestSuite * inS mockSender.mRetainExchange = false; buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the response and its ack was sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 5); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 5u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // Ensure that we have received that response and it had a piggyback ack. - NL_TEST_ASSERT(inSuite, mockSender.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, mockSender.mReceivedPiggybackAck); + EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); + EXPECT_TRUE(mockSender.mReceivedPiggybackAck); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); + EXPECT_EQ(err, CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); } -void TestReliableMessageProtocol::CheckSendUnsolicitedStandaloneAckMessage(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckSendUnsolicitedStandaloneAckMessage) { /** * Tests sending a standalone ack message that is: @@ -1278,68 +1198,62 @@ void TestReliableMessageProtocol::CheckSendUnsolicitedStandaloneAckMessage(nlTes * This is not a thing that would normally happen, but a malicious entity * could absolutely do this. */ - TestContext & ctx = *reinterpret_cast(inContext); - chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData("", 0); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - mockSender.mTestSuite = inSuite; - - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // We send a message, have it get received by the peer, expect an ack from // the peer. - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 0; loopback.mDroppedMessageCount = 0; // Purposefully sending a standalone ack that requests an ack! err = exchange->SendMessage(SecureChannel::MsgType::StandaloneAck, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_EQ(err, CHIP_NO_ERROR); // Needs a manual close, because SendMessage does not close for standalone acks. exchange->Close(); - ctx.DrainAndServiceIO(); + DrainAndServiceIO(); // Ensure the message and its ack were sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 2); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 2u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // And that nothing is waiting for acks. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); } -void TestReliableMessageProtocol::CheckSendStandaloneAckMessage(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckSendStandaloneAckMessage) { - TestContext & ctx = *reinterpret_cast(inContext); - - MockAppDelegate mockAppDelegate(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockAppDelegate); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + MockAppDelegate mockAppDelegate(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockAppDelegate); + ASSERT_NE(exchange, nullptr); - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); ReliableMessageContext * rc = exchange->GetReliableMessageContext(); - NL_TEST_ASSERT(inSuite, rm != nullptr); - NL_TEST_ASSERT(inSuite, rc != nullptr); + ASSERT_NE(rm, nullptr); + ASSERT_NE(rc, nullptr); - NL_TEST_ASSERT(inSuite, rc->SendStandaloneAckMessage() == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(rc->SendStandaloneAckMessage(), CHIP_NO_ERROR); + DrainAndServiceIO(); // Need manual close because standalone acks don't close exchanges. exchange->Close(); } -void TestReliableMessageProtocol::CheckMessageAfterClosed(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckMessageAfterClosed) { /** * This test performs the following sequence of actions, where all messages @@ -1355,32 +1269,26 @@ void TestReliableMessageProtocol::CheckMessageAfterClosed(nlTestSuite * inSuite, * responder closing the exchange after it sends the response. */ - TestContext & ctx = *reinterpret_cast(inContext); - chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockReceiver(ctx); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - mockReceiver.mTestSuite = inSuite; + MockAppDelegate mockReceiver(*this); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - mockSender.mTestSuite = inSuite; - - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 0; loopback.mDroppedMessageCount = 0; @@ -1388,49 +1296,49 @@ void TestReliableMessageProtocol::CheckMessageAfterClosed(nlTestSuite * inSuite, mockReceiver.mRetainExchange = true; mockSender.mRetainExchange = true; - NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, !mockReceiver.mReceivedPiggybackAck); + EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockReceiver.mReceivedPiggybackAck); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // And that it was received. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, !mockReceiver.mReceivedPiggybackAck); + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockReceiver.mReceivedPiggybackAck); // And that we have not seen an ack yet. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); ReliableMessageContext * receiverRc = mockReceiver.mExchange->GetReliableMessageContext(); - NL_TEST_ASSERT(inSuite, receiverRc->IsAckPending()); + EXPECT_TRUE(receiverRc->IsAckPending()); // Ensure that we have not gotten any app-level responses or acks so far. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, !mockSender.mReceivedPiggybackAck); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockSender.mReceivedPiggybackAck); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // Now send a message from the other side. buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the response was sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 2); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 2u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // Ensure that we have received that response and it had a piggyback ack. - NL_TEST_ASSERT(inSuite, mockSender.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, mockSender.mReceivedPiggybackAck); + EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); + EXPECT_TRUE(mockSender.mReceivedPiggybackAck); // And that we are now waiting for an ack for the response. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // Reset various state so we can measure things again. mockReceiver.IsOnMessageReceivedCalled = false; @@ -1440,31 +1348,31 @@ void TestReliableMessageProtocol::CheckMessageAfterClosed(nlTestSuite * inSuite, // Now send a second message to the other side. buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was sent (and the ack for it was also sent). - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 4); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 4u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // And that it was not received (because the exchange is closed on the // receiver). - NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); // And that we are not expecting an ack; acks should have been flushed // immediately on the receiver, due to the exchange being closed. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); + EXPECT_EQ(err, CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); } -void TestReliableMessageProtocol::CheckLostResponseWithPiggyback(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckLostResponseWithPiggyback) { /** * This tests the following scenario: @@ -1475,30 +1383,24 @@ void TestReliableMessageProtocol::CheckLostResponseWithPiggyback(nlTestSuite * i * 5) The responder retransmits the application-level response. * 4) The initiator should receive the application-level response. */ - TestContext & ctx = *reinterpret_cast(inContext); - chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockReceiver(ctx); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - mockReceiver.mTestSuite = inSuite; + MockAppDelegate mockReceiver(*this); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - mockSender.mTestSuite = inSuite; - - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // Make sure that we resend our message before the other side does. exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ @@ -1512,30 +1414,30 @@ void TestReliableMessageProtocol::CheckLostResponseWithPiggyback(nlTestSuite * i // we can send the response from the receiver), but don't need anything // special for the sender exchange, because it will be waiting for the // application-level response. - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 0; loopback.mDroppedMessageCount = 0; mockReceiver.mRetainExchange = true; err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // And that it was received. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // And that we have not gotten any app-level responses or acks so far. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); ReliableMessageContext * receiverRc = mockReceiver.mExchange->GetReliableMessageContext(); // Should have pending ack here. - NL_TEST_ASSERT(inSuite, receiverRc->IsAckPending()); + EXPECT_TRUE(receiverRc->IsAckPending()); // Make sure receiver resends after sender does, and there's enough of a gap // that we are very unlikely to actually trigger the resends on the receiver // when we trigger the resends on the sender. @@ -1548,26 +1450,26 @@ void TestReliableMessageProtocol::CheckLostResponseWithPiggyback(nlTestSuite * i loopback.mNumMessagesToDrop = 1; buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); // Stop keeping receiver exchange alive. mockReceiver.mRetainExchange = true; err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the response was sent but dropped. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 2); - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 0); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); + EXPECT_EQ(loopback.mSentMessageCount, 2u); + EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); // Ensure that we have not received that response. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, !mockSender.mReceivedPiggybackAck); + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockSender.mReceivedPiggybackAck); // We now have our un-acked message still waiting to retransmit and the // message that the other side sent is waiting for an ack. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 2); + EXPECT_EQ(rm->TestGetCountRetransTable(), 2); // Reset various state so we can measure things again. mockReceiver.IsOnMessageReceivedCalled = false; @@ -1576,8 +1478,8 @@ void TestReliableMessageProtocol::CheckLostResponseWithPiggyback(nlTestSuite * i mockSender.mReceivedPiggybackAck = false; // Wait for re-transmit from sender and ack (should take 64ms) - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 4; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 4; }); + DrainAndServiceIO(); // We resent our first message, which did not make it to the app-level // listener on the receiver (because it's a duplicate) but did trigger a @@ -1586,39 +1488,39 @@ void TestReliableMessageProtocol::CheckLostResponseWithPiggyback(nlTestSuite * i // Now the annoying part is that depending on how long we _actually_ slept // we might have also triggered the retransmit from the other side, even // though we did not want to. Handle both cases here. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 4 || loopback.mSentMessageCount == 6); + EXPECT_TRUE(loopback.mSentMessageCount == 4 || loopback.mSentMessageCount == 6); if (loopback.mSentMessageCount == 4) { // Just triggered the retransmit from the sender. - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); } else { // Also triggered the retransmit from the receiver. - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, mockSender.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); } // Wait for re-transmit from receiver (should take 256ms) - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 6; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 6; }); + DrainAndServiceIO(); // And now we've definitely resent our response message, which should show // up as an app-level message and trigger a standalone ack. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 6); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, mockSender.IsOnMessageReceivedCalled); + EXPECT_EQ(loopback.mSentMessageCount, 6u); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); // Should be all done now. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); } -void TestReliableMessageProtocol::CheckIsPeerActiveNotInitiator(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckIsPeerActiveNotInitiator) { /** * This tests the following scenario: @@ -1630,37 +1532,31 @@ void TestReliableMessageProtocol::CheckIsPeerActiveNotInitiator(nlTestSuite * in * 6) Initiator receives the response */ - TestContext & ctx = *reinterpret_cast(inContext); - chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockReceiver(ctx); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - mockReceiver.mTestSuite = inSuite; - - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + MockAppDelegate mockReceiver(*this); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); - mockSender.mTestSuite = inSuite; + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ 1000_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL 1000_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL })); - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 1; loopback.mDroppedMessageCount = 0; @@ -1668,40 +1564,40 @@ void TestReliableMessageProtocol::CheckIsPeerActiveNotInitiator(nlTestSuite * in mockReceiver.mRetainExchange = true; mockSender.mRetainExchange = true; - NL_TEST_ASSERT(inSuite, !exchange->HasReceivedAtLeastOneMessage()); + EXPECT_FALSE(exchange->HasReceivedAtLeastOneMessage()); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Verify that the first message is dropped - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 0); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); // Make sure retransmit was not done before the idle restrans interval hits - ctx.GetIOContext().DriveIOUntil(500_ms32, [&] { return loopback.mSentMessageCount >= 1; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(500_ms32, [&] { return loopback.mSentMessageCount >= 1; }); + DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, !exchange->HasReceivedAtLeastOneMessage()); + EXPECT_FALSE(exchange->HasReceivedAtLeastOneMessage()); // // Make sure nothing happened - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); // // Retrasnmit message - ctx.GetIOContext().DriveIOUntil(2000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(2000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); + DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, !exchange->HasReceivedAtLeastOneMessage()); + EXPECT_FALSE(exchange->HasReceivedAtLeastOneMessage()); // // Make sure nothing happened - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 2); - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); + EXPECT_EQ(loopback.mSentMessageCount, 2u); + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // // Verify that the receiver considers the sender is active - NL_TEST_ASSERT(inSuite, !exchange->HasReceivedAtLeastOneMessage()); - NL_TEST_ASSERT(inSuite, mockReceiver.mExchange->HasReceivedAtLeastOneMessage()); + EXPECT_FALSE(exchange->HasReceivedAtLeastOneMessage()); + EXPECT_TRUE(mockReceiver.mExchange->HasReceivedAtLeastOneMessage()); mockReceiver.mExchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ 1000_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL @@ -1713,30 +1609,30 @@ void TestReliableMessageProtocol::CheckIsPeerActiveNotInitiator(nlTestSuite * in // Now send a message from the other side. buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); // Make receiver message fail once loopback.mNumMessagesToDrop = 1; err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Make sure nothing happened - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 2); - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 0); - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 3); - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); + EXPECT_EQ(loopback.mDroppedMessageCount, 2u); + EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); + EXPECT_EQ(loopback.mSentMessageCount, 3u); + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // // Retrasnmit message - ctx.GetIOContext().DriveIOUntil(500_ms32, [&] { return loopback.mSentMessageCount >= 4; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(500_ms32, [&] { return loopback.mSentMessageCount >= 4; }); + DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, mockSender.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 5); + EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); + EXPECT_EQ(loopback.mSentMessageCount, 5u); } -void TestReliableMessageProtocol::CheckLostStandaloneAck(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckLostStandaloneAck) { /** * This tests the following scenario: @@ -1748,37 +1644,31 @@ void TestReliableMessageProtocol::CheckLostStandaloneAck(nlTestSuite * inSuite, * This should succeed, with all application-level messages being delivered * and no crashes. */ - TestContext & ctx = *reinterpret_cast(inContext); - chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); CHIP_ERROR err = CHIP_NO_ERROR; - MockAppDelegate mockReceiver(ctx); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - mockReceiver.mTestSuite = inSuite; + MockAppDelegate mockReceiver(*this); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - mockSender.mTestSuite = inSuite; - - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // We send a message, the other side sends a standalone ack first (which is // lost), then an application response, then we respond to that response. // We need to keep both exchanges alive for that (so we can send the // response from the receiver and so the initial sender exchange can send a // response to that). - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = 0; loopback.mDroppedMessageCount = 0; @@ -1789,48 +1679,48 @@ void TestReliableMessageProtocol::CheckLostStandaloneAck(nlTestSuite * inSuite, mockReceiver.SetDropAckResponse(true); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // And that it was received. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // And that we have not gotten any app-level responses or acks so far. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); ReliableMessageContext * receiverRc = mockReceiver.mExchange->GetReliableMessageContext(); // Ack should have been dropped. - NL_TEST_ASSERT(inSuite, !receiverRc->IsAckPending()); + EXPECT_FALSE(receiverRc->IsAckPending()); // Don't drop any more acks. mockReceiver.SetDropAckResponse(false); // Now send a message from the other side. buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the response was sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 2); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 2u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // Ensure that we have received that response and had a piggyback ack. - NL_TEST_ASSERT(inSuite, mockSender.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, mockSender.mReceivedPiggybackAck); + EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); + EXPECT_TRUE(mockSender.mReceivedPiggybackAck); // We now have just the received message waiting for an ack. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // And receiver still has no ack pending. - NL_TEST_ASSERT(inSuite, !receiverRc->IsAckPending()); + EXPECT_FALSE(receiverRc->IsAckPending()); // Reset various state so we can measure things again. mockReceiver.IsOnMessageReceivedCalled = false; @@ -1843,67 +1733,64 @@ void TestReliableMessageProtocol::CheckLostStandaloneAck(nlTestSuite * inSuite, // Now send a new message to the other side. buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message and the standalone ack to it were sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 4); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 0); + EXPECT_EQ(loopback.mSentMessageCount, 4u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); // And that it was received. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, mockReceiver.mReceivedPiggybackAck); + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); + EXPECT_TRUE(mockReceiver.mReceivedPiggybackAck); // At this point all our exchanges and reliable message contexts should be // dead, so we can't test anything about their state. // And that there are no un-acked messages left. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); } -void TestReliableMessageProtocol::CheckGetBackoff(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckGetBackoff) { - CheckGetBackoffImpl(inSuite, System::Clock::kZero); + CheckGetBackoffImpl(System::Clock::kZero); } -void TestReliableMessageProtocol::CheckGetBackoffAdditionalTime(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckGetBackoffAdditionalTime) { - CheckGetBackoffImpl(inSuite, System::Clock::Seconds32(1)); + CheckGetBackoffImpl(System::Clock::Seconds32(1)); } -void TestReliableMessageProtocol::CheckApplicationResponseDelayed(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckApplicationResponseDelayed) { - TestContext & ctx = *reinterpret_cast(inContext); - CHIP_ERROR err = CHIP_NO_ERROR; // Make sure we are using CASE sessions, because there is no defunct-marking for PASE. - ctx.ExpireSessionBobToAlice(); - ctx.ExpireSessionAliceToBob(); - err = ctx.CreateCASESessionBobToAlice(); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - err = ctx.CreateCASESessionAliceToBob(); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + ExpireSessionBobToAlice(); + ExpireSessionAliceToBob(); + err = CreateCASESessionBobToAlice(); + EXPECT_EQ(err, CHIP_NO_ERROR); + err = CreateCASESessionAliceToBob(); + EXPECT_EQ(err, CHIP_NO_ERROR); chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); - MockAppDelegate mockReceiver(ctx); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + MockAppDelegate mockReceiver(*this); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); - mockReceiver.mTestSuite = inSuite; mockReceiver.mRetainExchange = true; - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ 30_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL @@ -1913,51 +1800,51 @@ void TestReliableMessageProtocol::CheckApplicationResponseDelayed(nlTestSuite * constexpr uint32_t kMaxMRPTransmits = 5; // Counting the initial message. // Let's drop all but the last MRP transmit. - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = kMaxMRPTransmits - 1; loopback.mDroppedMessageCount = 0; // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); exchange->SetResponseTimeout(3000_ms32); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendMessageFlags::kExpectResponse); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was dropped, and was added to retransmit table - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == kMaxMRPTransmits - 2); - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); - NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); + EXPECT_EQ(loopback.mNumMessagesToDrop, kMaxMRPTransmits - 2u); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); + EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // Wait for all but the last retransmit to happen. - ctx.GetIOContext().DriveIOUntil(5000_ms32, [&] { return loopback.mDroppedMessageCount >= kMaxMRPTransmits - 1; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(5000_ms32, [&] { return loopback.mDroppedMessageCount >= kMaxMRPTransmits - 1; }); + DrainAndServiceIO(); // Ensure that nothing has been sent yet. - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 0); - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == kMaxMRPTransmits - 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == kMaxMRPTransmits - 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); - NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); + EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); + EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits - 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); + EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // Now allow through the next message (our last retransmit), but make sure // there is no standalone ack for it. mockReceiver.SetDropAckResponse(true); - ctx.GetIOContext().DriveIOUntil(5000_ms32, [&] { return loopback.mSentMessageCount >= kMaxMRPTransmits; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(5000_ms32, [&] { return loopback.mSentMessageCount >= kMaxMRPTransmits; }); + DrainAndServiceIO(); // Verify that message was sent and received but nothing else has been sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == kMaxMRPTransmits); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == kMaxMRPTransmits - 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); // We have no ack yet. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); // Other side got the message. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); // We did not get a response. + EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits); + EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // We have no ack yet. + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got the message. + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We did not get a response. // Ensure there will be no more weirdness with acks and that our MRP timer is restarted properly. mockReceiver.SetDropAckResponse(false); @@ -1973,99 +1860,93 @@ void TestReliableMessageProtocol::CheckApplicationResponseDelayed(nlTestSuite * })); buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + EXPECT_FALSE(buffer.IsNull()); err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // At this point, we should have two MRP contexts pending. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 2); // We have no ack yet. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); // We did not get a response. + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 2); // We have no ack yet. + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We did not get a response. // Now wait for all but the last retransmit to happen from the other side. - ctx.GetIOContext().DriveIOUntil(5000_ms32, [&] { return loopback.mSentMessageCount >= kMaxMRPTransmits - 1; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(5000_ms32, [&] { return loopback.mSentMessageCount >= kMaxMRPTransmits - 1; }); + DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == kMaxMRPTransmits - 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == kMaxMRPTransmits - 1); + EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits - 1); + EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); // We might have timed our MRP resends out, or not, but the other side is waiting for an ack. - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1 || rm->TestGetCountRetransTable() == 2); - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); // We did not get a response. + EXPECT_TRUE(rm->TestGetCountRetransTable() == 1 || rm->TestGetCountRetransTable() == 2); + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We did not get a response. // Now wait for us to time out our MRP context for sure. - ctx.GetIOContext().DriveIOUntil(5000_ms32, [&] { return rm->TestGetCountRetransTable() == 1; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(5000_ms32, [&] { return rm->TestGetCountRetransTable() == 1; }); + DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == kMaxMRPTransmits - 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == kMaxMRPTransmits - 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); // We timed out our MRP context. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); // We did not get a response. - NL_TEST_ASSERT(inSuite, !mockSender.mResponseTimedOut); // We did not time out yet. + EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits - 1); + EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // We timed out our MRP context. + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We did not get a response. + EXPECT_FALSE(mockSender.mResponseTimedOut); // We did not time out yet. // Now wait for the last retransmit (and our ack) to to happen. - ctx.GetIOContext().DriveIOUntil(5000_ms32, [&] { return loopback.mSentMessageCount >= kMaxMRPTransmits; }); - ctx.DrainAndServiceIO(); - - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == kMaxMRPTransmits + 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == kMaxMRPTransmits - 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); // Everything has been acked. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. - NL_TEST_ASSERT(inSuite, mockSender.IsOnMessageReceivedCalled); // We got the response. - NL_TEST_ASSERT(inSuite, !mockSender.mResponseTimedOut); // We did not time out yet. + GetIOContext().DriveIOUntil(5000_ms32, [&] { return loopback.mSentMessageCount >= kMaxMRPTransmits; }); + DrainAndServiceIO(); - mockReceiver.mTestSuite = nullptr; + EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits + 1); + EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // Everything has been acked. + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. + EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); // We got the response. + EXPECT_FALSE(mockSender.mResponseTimedOut); // We did not time out yet. // Ensure that we did not mark any sessions defunct. - NL_TEST_ASSERT(inSuite, !ctx.GetSessionBobToAlice()->AsSecureSession()->IsDefunct()); - NL_TEST_ASSERT(inSuite, !ctx.GetSessionAliceToBob()->AsSecureSession()->IsDefunct()); + EXPECT_FALSE(GetSessionBobToAlice()->AsSecureSession()->IsDefunct()); + EXPECT_FALSE(GetSessionAliceToBob()->AsSecureSession()->IsDefunct()); // Reset our sessions, so other tests get the usual PASE session - ctx.ExpireSessionBobToAlice(); - ctx.ExpireSessionAliceToBob(); - err = ctx.CreateSessionBobToAlice(); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - err = ctx.CreateSessionAliceToBob(); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + ExpireSessionBobToAlice(); + ExpireSessionAliceToBob(); + err = CreateSessionBobToAlice(); + EXPECT_EQ(err, CHIP_NO_ERROR); + err = CreateSessionAliceToBob(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); + EXPECT_EQ(err, CHIP_NO_ERROR); } -void TestReliableMessageProtocol::CheckApplicationResponseNeverComes(nlTestSuite * inSuite, void * inContext) +TEST_F(TestReliableMessageProtocol, CheckApplicationResponseNeverComes) { - TestContext & ctx = *reinterpret_cast(inContext); - CHIP_ERROR err = CHIP_NO_ERROR; // Make sure we are using CASE sessions, because there is no defunct-marking for PASE. - ctx.ExpireSessionBobToAlice(); - ctx.ExpireSessionAliceToBob(); - err = ctx.CreateCASESessionBobToAlice(); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - err = ctx.CreateCASESessionAliceToBob(); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + ExpireSessionBobToAlice(); + ExpireSessionAliceToBob(); + err = CreateCASESessionBobToAlice(); + EXPECT_EQ(err, CHIP_NO_ERROR); + err = CreateCASESessionAliceToBob(); + EXPECT_EQ(err, CHIP_NO_ERROR); chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); - NL_TEST_ASSERT(inSuite, !buffer.IsNull()); - - MockAppDelegate mockReceiver(ctx); - err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + EXPECT_FALSE(buffer.IsNull()); - mockReceiver.mTestSuite = inSuite; + MockAppDelegate mockReceiver(*this); + err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + EXPECT_EQ(err, CHIP_NO_ERROR); - MockAppDelegate mockSender(ctx); - ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); - NL_TEST_ASSERT(inSuite, exchange != nullptr); + MockAppDelegate mockSender(*this); + ExchangeContext * exchange = NewExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); - ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); - NL_TEST_ASSERT(inSuite, rm != nullptr); + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ 30_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL @@ -2075,94 +1956,92 @@ void TestReliableMessageProtocol::CheckApplicationResponseNeverComes(nlTestSuite constexpr uint32_t kMaxMRPTransmits = 5; // Counting the initial message. // Let's drop all but the last MRP transmit. - auto & loopback = ctx.GetLoopback(); + auto & loopback = GetLoopback(); loopback.mSentMessageCount = 0; loopback.mNumMessagesToDrop = kMaxMRPTransmits - 1; loopback.mDroppedMessageCount = 0; // Ensure the retransmit table is empty right now - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); exchange->SetResponseTimeout(2500_ms32); err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendMessageFlags::kExpectResponse); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + EXPECT_EQ(err, CHIP_NO_ERROR); + DrainAndServiceIO(); // Ensure the message was dropped, and was added to retransmit table - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == kMaxMRPTransmits - 2); - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); - NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); + EXPECT_EQ(loopback.mNumMessagesToDrop, kMaxMRPTransmits - 2u); + EXPECT_EQ(loopback.mSentMessageCount, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 1u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); + EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // Wait for all but the last retransmit to happen. - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mDroppedMessageCount >= kMaxMRPTransmits - 1; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mDroppedMessageCount >= kMaxMRPTransmits - 1; }); + DrainAndServiceIO(); // Ensure that nothing has been sent yet. - NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 0); - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == kMaxMRPTransmits - 1); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == kMaxMRPTransmits - 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); - NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); + EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); + EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits - 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); + EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // Now allow through the next message (our last retransmit), but make sure // there is no standalone ack for it. mockReceiver.SetDropAckResponse(true); - ctx.GetIOContext().DriveIOUntil(500_ms32, [&] { return loopback.mSentMessageCount >= kMaxMRPTransmits; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(500_ms32, [&] { return loopback.mSentMessageCount >= kMaxMRPTransmits; }); + DrainAndServiceIO(); // Verify that message was sent and received but nothing else has been sent. - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == kMaxMRPTransmits); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == kMaxMRPTransmits - 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 1); // We have no ack yet. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); // Other side got the message. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); // We did not get a response. + EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits); + EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // We have no ack yet. + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got the message. + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We did not get a response. // Ensure there will be no more weirdness with acks and that our MRP timer is restarted properly. mockReceiver.SetDropAckResponse(false); // Now wait for us to time out our MRP context. - ctx.GetIOContext().DriveIOUntil(1000_ms32, [&] { return rm->TestGetCountRetransTable() == 0; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(1000_ms32, [&] { return rm->TestGetCountRetransTable() == 0; }); + DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == kMaxMRPTransmits); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == kMaxMRPTransmits - 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); // We timed out our MRP context. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); // We did not get a response. - NL_TEST_ASSERT(inSuite, !mockSender.mResponseTimedOut); // We did not time out yet. + EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits); + EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // We timed out our MRP context. + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We did not get a response. + EXPECT_FALSE(mockSender.mResponseTimedOut); // We did not time out yet. // Now wait for our exchange to time out. - ctx.GetIOContext().DriveIOUntil(3000_ms32, [&] { return mockSender.mResponseTimedOut; }); - ctx.DrainAndServiceIO(); + GetIOContext().DriveIOUntil(3000_ms32, [&] { return mockSender.mResponseTimedOut; }); + DrainAndServiceIO(); - NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == kMaxMRPTransmits); - NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == kMaxMRPTransmits - 1); - NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); // We timed this out long ago. - NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. - NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); // We never got a response. - NL_TEST_ASSERT(inSuite, mockSender.mResponseTimedOut); // We tiemd out - - mockReceiver.mTestSuite = nullptr; + EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits); + EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // We timed this out long ago. + EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. + EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We never got a response. + EXPECT_TRUE(mockSender.mResponseTimedOut); // We tiemd out // We should have marked out session defunct. - NL_TEST_ASSERT(inSuite, ctx.GetSessionBobToAlice()->AsSecureSession()->IsDefunct()); + EXPECT_TRUE(GetSessionBobToAlice()->AsSecureSession()->IsDefunct()); // Other side had no reason to mark its session defunct. - NL_TEST_ASSERT(inSuite, !ctx.GetSessionAliceToBob()->AsSecureSession()->IsDefunct()); + EXPECT_FALSE(GetSessionAliceToBob()->AsSecureSession()->IsDefunct()); // Reset our sessions, so other tests get the usual PASE session - ctx.ExpireSessionBobToAlice(); - ctx.ExpireSessionAliceToBob(); - err = ctx.CreateSessionBobToAlice(); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - err = ctx.CreateSessionAliceToBob(); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + ExpireSessionBobToAlice(); + ExpireSessionAliceToBob(); + err = CreateSessionBobToAlice(); + EXPECT_EQ(err, CHIP_NO_ERROR); + err = CreateSessionAliceToBob(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); + EXPECT_EQ(err, CHIP_NO_ERROR); } /** @@ -2179,66 +2058,3 @@ void TestReliableMessageProtocol::CheckApplicationResponseNeverComes(nlTestSuite * (this is the part that needs testing!) * 8. A sends message 5 to B. */ - -const nlTest sTests[] = { - NL_TEST_DEF("Test ReliableMessageMgr::CheckAddClearRetrans", TestReliableMessageProtocol::CheckAddClearRetrans), - NL_TEST_DEF("Test ReliableMessageMgr::CheckResendApplicationMessage", - TestReliableMessageProtocol::CheckResendApplicationMessage), - NL_TEST_DEF("Test ReliableMessageMgr::CheckCloseExchangeAndResendApplicationMessage", - TestReliableMessageProtocol::CheckCloseExchangeAndResendApplicationMessage), - NL_TEST_DEF("Test ReliableMessageMgr::CheckFailedMessageRetainOnSend", - TestReliableMessageProtocol::CheckFailedMessageRetainOnSend), - NL_TEST_DEF("Test ReliableMessageMgr::CheckResendApplicationMessageWithPeerExchange", - TestReliableMessageProtocol::CheckResendApplicationMessageWithPeerExchange), - NL_TEST_DEF("Test ReliableMessageMgr::CheckResendSessionEstablishmentMessageWithPeerExchange", - TestReliableMessageProtocol::CheckResendSessionEstablishmentMessageWithPeerExchange), - NL_TEST_DEF("Test ReliableMessageMgr::CheckDuplicateMessage", TestReliableMessageProtocol::CheckDuplicateMessage), - NL_TEST_DEF("Test ReliableMessageMgr::CheckDuplicateMessageClosedExchange", - TestReliableMessageProtocol::CheckDuplicateMessageClosedExchange), - NL_TEST_DEF("Test ReliableMessageMgr::CheckDuplicateOldMessageClosedExchange", - TestReliableMessageProtocol::CheckDuplicateOldMessageClosedExchange), - NL_TEST_DEF("Test that a reply after a standalone ack comes through correctly", - TestReliableMessageProtocol::CheckReceiveAfterStandaloneAck), - NL_TEST_DEF("Test that a reply to a non-MRP message piggybacks an ack if there were MRP things happening on the context before", - TestReliableMessageProtocol::CheckPiggybackAfterPiggyback), - NL_TEST_DEF("Test sending an unsolicited ack-soliciting 'standalone ack' message", - TestReliableMessageProtocol::CheckSendUnsolicitedStandaloneAckMessage), - NL_TEST_DEF("Test ReliableMessageMgr::CheckSendStandaloneAckMessage", - TestReliableMessageProtocol::CheckSendStandaloneAckMessage), - NL_TEST_DEF("Test command, response, default response, with receiver closing exchange after sending response", - TestReliableMessageProtocol::CheckMessageAfterClosed), - NL_TEST_DEF("Test that unencrypted message is dropped if exchange requires encryption", - TestReliableMessageProtocol::CheckUnencryptedMessageReceiveFailure), - NL_TEST_DEF("Test that dropping an application-level message with a piggyback ack works ok once both sides retransmit", - TestReliableMessageProtocol::CheckLostResponseWithPiggyback), - NL_TEST_DEF("Test that an application-level response-to-response after a lost standalone ack to the initial message works", - TestReliableMessageProtocol::CheckLostStandaloneAck), - NL_TEST_DEF("Test Is Peer Active Retry logic", TestReliableMessageProtocol::CheckIsPeerActiveNotInitiator), - NL_TEST_DEF("Test MRP backoff algorithm", TestReliableMessageProtocol::CheckGetBackoff), - NL_TEST_DEF("Test MRP backoff algorithm with additional time", TestReliableMessageProtocol::CheckGetBackoffAdditionalTime), - // TODO: Re-enable this test, after changing test to use Mock clock / DriveIO rather than DriveIOUntil. - // Issue: https://github.com/project-chip/connectedhomeip/issues/32440 - // NL_TEST_DEF("Test an application response that comes after MRP retransmits run out", - // TestReliableMessageProtocol::CheckApplicationResponseDelayed), - NL_TEST_DEF("Test an application response that never comes, so MRP retransmits run out and then exchange times out", - TestReliableMessageProtocol::CheckApplicationResponseNeverComes), - NL_TEST_SENTINEL(), -}; - -// clang-format off -nlTestSuite sSuite = { - "Test-CHIP-ReliableMessageProtocol", - &sTests[0], - NL_TEST_WRAP_FUNCTION(TestContext::SetUpTestSuite), - NL_TEST_WRAP_FUNCTION(TestContext::TearDownTestSuite), - NL_TEST_WRAP_METHOD(TestContext, SetUp), - NL_TEST_WRAP_METHOD(TestContext, TearDown), -}; -// clang-format on - -int TestReliableMessageProtocolSuite() -{ - return chip::ExecuteTestsWithContext(&sSuite); -} - -CHIP_REGISTER_TEST_SUITE(TestReliableMessageProtocolSuite) diff --git a/src/platform/Darwin/UserDefaults.mm b/src/platform/Darwin/UserDefaults.mm index d24b0a4ee20f37..e28c1fa90fc0de 100644 --- a/src/platform/Darwin/UserDefaults.mm +++ b/src/platform/Darwin/UserDefaults.mm @@ -21,7 +21,6 @@ #import -static NSString * const kUserDefaultDomain = @"org.csa-iot.matter.darwin"; static NSString * const kSRPTimeoutInMsecsUserDefaultKey = @"SRPTimeoutInMSecsOverride"; namespace chip { @@ -29,7 +28,7 @@ std::optional GetUserDefaultDnssdSRPTimeoutInMSecs() { - NSUserDefaults * defaults = [[NSUserDefaults alloc] initWithSuiteName:kUserDefaultDomain]; + NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; NSInteger srpTimeoutValue = [defaults integerForKey:kSRPTimeoutInMsecsUserDefaultKey]; if (CanCastTo(srpTimeoutValue)) { uint16_t timeoutinMsecs = static_cast(srpTimeoutValue); diff --git a/src/platform/ESP32/CHIPDevicePlatformConfig.h b/src/platform/ESP32/CHIPDevicePlatformConfig.h index e6d7870c35fda6..e347df109d1e3a 100644 --- a/src/platform/ESP32/CHIPDevicePlatformConfig.h +++ b/src/platform/ESP32/CHIPDevicePlatformConfig.h @@ -54,8 +54,17 @@ #define CHIP_DEVICE_CONFIG_ENABLE_THREAD 0 #endif // CONFIG_ENABLE_MATTER_OVER_THREAD +#ifdef CONFIG_OPENTHREAD_SRP_CLIENT #define CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT CONFIG_OPENTHREAD_SRP_CLIENT +#else +#define CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT 0 +#endif // CONFIG_OPENTHREAD_SRP_CLIENT + +#ifdef CONFIG_OPENTHREAD_DNS_CLIENT #define CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT CONFIG_OPENTHREAD_DNS_CLIENT +#else +#define CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT 0 +#endif // CONFIG_OPENTHREAD_DNS_CLIENT #ifdef CONFIG_ENABLE_ETHERNET_TELEMETRY #define CHIP_DEVICE_CONFIG_ENABLE_ETHERNET 1 diff --git a/src/platform/ESP32/DnssdImpl.cpp b/src/platform/ESP32/DnssdImpl.cpp index 1ca07213a866c6..df83ec818ead57 100644 --- a/src/platform/ESP32/DnssdImpl.cpp +++ b/src/platform/ESP32/DnssdImpl.cpp @@ -30,6 +30,7 @@ #endif using namespace ::chip::DeviceLayer; +using chip::DeviceLayer::Internal::ESP32Utils; namespace chip { namespace Dnssd { @@ -39,7 +40,7 @@ CHIP_ERROR ChipDnssdInit(DnssdAsyncReturnCallback initCallback, DnssdAsyncReturn #if CHIP_DEVICE_CONFIG_ENABLE_WIFI || CHIP_DEVICE_CONFIG_ENABLE_ETHERNET ReturnErrorOnFailure(EspDnssdInit(initCallback, errorCallback, context)); #endif -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD && CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT ReturnErrorOnFailure(OpenThreadDnssdInit(initCallback, errorCallback, context)); #endif return CHIP_NO_ERROR; @@ -52,7 +53,7 @@ CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCal #if CHIP_DEVICE_CONFIG_ENABLE_WIFI || CHIP_DEVICE_CONFIG_ENABLE_ETHERNET ReturnErrorOnFailure(EspDnssdPublishService(service, callback, context)); #endif -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD && CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT if (ConnectivityMgr().IsThreadProvisioned()) { ReturnErrorOnFailure(OpenThreadDnssdPublishService(service, callback, context)); @@ -66,7 +67,7 @@ CHIP_ERROR ChipDnssdRemoveServices() #if CHIP_DEVICE_CONFIG_ENABLE_WIFI || CHIP_DEVICE_CONFIG_ENABLE_ETHERNET ReturnErrorOnFailure(EspDnssdRemoveServices()); #endif -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD && CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT if (ConnectivityMgr().IsThreadProvisioned()) { ReturnErrorOnFailure(OpenThreadDnssdRemoveServices()); @@ -77,7 +78,7 @@ CHIP_ERROR ChipDnssdRemoveServices() CHIP_ERROR ChipDnssdFinalizeServiceUpdate() { -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD && CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT if (ConnectivityMgr().IsThreadProvisioned()) { ReturnErrorOnFailure(OpenThreadDnssdFinalizeServiceUpdate()); @@ -91,13 +92,12 @@ CHIP_ERROR ChipDnssdBrowse(const char * type, DnssdServiceProtocol protocol, chi intptr_t * browseIdentifier) { #if CHIP_DEVICE_CONFIG_ENABLE_WIFI || CHIP_DEVICE_CONFIG_ENABLE_ETHERNET - if (ConnectivityMgr().IsWiFiStationProvisioned() || - Internal::ESP32Utils::HasIPv6LinkLocalAddress(Internal::ESP32Utils::kDefaultEthernetNetifKey)) + if (ConnectivityMgr().IsWiFiStationProvisioned() || ESP32Utils::HasIPv6LinkLocalAddress(ESP32Utils::kDefaultEthernetNetifKey)) { ReturnErrorOnFailure(EspDnssdBrowse(type, protocol, addressType, interface, callback, context, browseIdentifier)); } #endif -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD && CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT && CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT if (ConnectivityMgr().IsThreadProvisioned()) { ReturnErrorOnFailure(OpenThreadDnssdBrowse(type, protocol, addressType, interface, callback, context, browseIdentifier)); @@ -115,13 +115,12 @@ CHIP_ERROR ChipDnssdResolve(DnssdService * service, chip::Inet::InterfaceId inte void * context) { #if CHIP_DEVICE_CONFIG_ENABLE_WIFI || CHIP_DEVICE_CONFIG_ENABLE_ETHERNET - if (ConnectivityMgr().IsWiFiStationProvisioned() || - Internal::ESP32Utils::HasIPv6LinkLocalAddress(Internal::ESP32Utils::kDefaultEthernetNetifKey)) + if (ConnectivityMgr().IsWiFiStationProvisioned() || ESP32Utils::HasIPv6LinkLocalAddress(ESP32Utils::kDefaultEthernetNetifKey)) { ReturnErrorOnFailure(EspDnssdResolve(service, interface, callback, context)); } #endif -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD && CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT && CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT if (ConnectivityMgr().IsThreadProvisioned()) { ReturnErrorOnFailure(OpenThreadDnssdResolve(service, interface, callback, context)); diff --git a/src/platform/ESP32/ESP32DnssdImpl.cpp b/src/platform/ESP32/ESP32DnssdImpl.cpp index 2c896a0d5904db..e898a9cadf767d 100644 --- a/src/platform/ESP32/ESP32DnssdImpl.cpp +++ b/src/platform/ESP32/ESP32DnssdImpl.cpp @@ -355,7 +355,7 @@ static CHIP_ERROR OnBrowseDone(BrowseContext * ctx) Inet::IPAddress IPAddr; error = GetIPAddress(IPAddr, currentResult->addr); SuccessOrExit(error); - ctx->mService[servicesIndex].mAddress.SetValue(IPAddr); + ctx->mService[servicesIndex].mAddress.emplace(IPAddr); } currentResult = currentResult->next; servicesIndex++; diff --git a/src/platform/ESP32/ThreadStackManagerImpl.cpp b/src/platform/ESP32/ThreadStackManagerImpl.cpp index 0449607999668b..c05a911a8b4adb 100644 --- a/src/platform/ESP32/ThreadStackManagerImpl.cpp +++ b/src/platform/ESP32/ThreadStackManagerImpl.cpp @@ -52,8 +52,12 @@ ThreadStackManagerImpl ThreadStackManagerImpl::sInstance; CHIP_ERROR ThreadStackManagerImpl::_InitThreadStack() { + CHIP_ERROR err = CHIP_NO_ERROR; openthread_init_stack(); - return GenericThreadStackManagerImpl_OpenThread::DoInit(esp_openthread_get_instance()); + _LockThreadStack(); + err = GenericThreadStackManagerImpl_OpenThread::DoInit(esp_openthread_get_instance()); + _UnlockThreadStack(); + return err; } CHIP_ERROR ThreadStackManagerImpl::_StartThreadTask() diff --git a/src/platform/Infineon/CYW30739/BUILD.gn b/src/platform/Infineon/CYW30739/BUILD.gn index 7613847e0f134c..433b8dcdc49d9d 100644 --- a/src/platform/Infineon/CYW30739/BUILD.gn +++ b/src/platform/Infineon/CYW30739/BUILD.gn @@ -34,8 +34,6 @@ static_library("CYW30739") { "ConfigurationManagerImpl.h", "ConnectivityManagerImpl.cpp", "ConnectivityManagerImpl.h", - "FactoryDataProvider.cpp", - "FactoryDataProvider.h", "InetPlatformConfig.h", "KeyValueStoreManagerImpl.cpp", "KeyValueStoreManagerImpl.h", @@ -57,6 +55,8 @@ static_library("CYW30739") { "EventFlags.h", "FactoryDataProvider.h", "OTAImageProcessorImpl.h", + "OptigaFactoryDataProvider.h", + "UnprovisionedOptigaFactoryDataProvider.h", "cycfg_gatt_db.h", ] diff --git a/src/platform/Infineon/CYW30739/CYW30739Config.h b/src/platform/Infineon/CYW30739/CYW30739Config.h index f5e56f0fee42cc..24b074689d0ecf 100644 --- a/src/platform/Infineon/CYW30739/CYW30739Config.h +++ b/src/platform/Infineon/CYW30739/CYW30739Config.h @@ -72,25 +72,33 @@ class CYW30739Config static constexpr Key kConfigKey_PAICert = CYW30739ConfigKey(kChipFactory_KeyBase, 0x22); static constexpr Key kConfigKey_CertDeclaration = CYW30739ConfigKey(kChipFactory_KeyBase, 0x23); // CHIP Config Keys - static constexpr Key kConfigKey_ServiceConfig = CYW30739ConfigKey(kChipConfig_KeyBase, 0x00); - static constexpr Key kConfigKey_PairedAccountId = CYW30739ConfigKey(kChipConfig_KeyBase, 0x01); - static constexpr Key kConfigKey_ServiceId = CYW30739ConfigKey(kChipConfig_KeyBase, 0x02); - static constexpr Key kConfigKey_LastUsedEpochKeyId = CYW30739ConfigKey(kChipConfig_KeyBase, 0x03); - static constexpr Key kConfigKey_FailSafeArmed = CYW30739ConfigKey(kChipConfig_KeyBase, 0x04); - static constexpr Key kConfigKey_GroupKey = CYW30739ConfigKey(kChipConfig_KeyBase, 0x05); - static constexpr Key kConfigKey_RegulatoryLocation = CYW30739ConfigKey(kChipConfig_KeyBase, 0x07); - static constexpr Key kConfigKey_CountryCode = CYW30739ConfigKey(kChipConfig_KeyBase, 0x08); - static constexpr Key kConfigKey_RebootCount = CYW30739ConfigKey(kChipConfig_KeyBase, 0x09); - static constexpr Key kConfigKey_UniqueId = CYW30739ConfigKey(kChipConfig_KeyBase, 0x0a); - static constexpr Key kConfigKey_LockUser = CYW30739ConfigKey(kChipConfig_KeyBase, 0x0b); - static constexpr Key kConfigKey_Credential = CYW30739ConfigKey(kChipConfig_KeyBase, 0x0c); - static constexpr Key kConfigKey_LockUserName = CYW30739ConfigKey(kChipConfig_KeyBase, 0x0d); - static constexpr Key kConfigKey_CredentialData = CYW30739ConfigKey(kChipConfig_KeyBase, 0x0e); - static constexpr Key kConfigKey_UserCredentials = CYW30739ConfigKey(kChipConfig_KeyBase, 0x0f); - static constexpr Key kConfigKey_WeekDaySchedules = CYW30739ConfigKey(kChipConfig_KeyBase, 0x10); - static constexpr Key kConfigKey_YearDaySchedules = CYW30739ConfigKey(kChipConfig_KeyBase, 0x11); - static constexpr Key kConfigKey_HolidaySchedules = CYW30739ConfigKey(kChipConfig_KeyBase, 0x12); - static constexpr Key kConfigKey_BootReason = CYW30739ConfigKey(kChipConfig_KeyBase, 0x13); + static constexpr Key kConfigKey_ServiceConfig = CYW30739ConfigKey(kChipConfig_KeyBase, 0x00); + static constexpr Key kConfigKey_PairedAccountId = CYW30739ConfigKey(kChipConfig_KeyBase, 0x01); + static constexpr Key kConfigKey_ServiceId = CYW30739ConfigKey(kChipConfig_KeyBase, 0x02); + static constexpr Key kConfigKey_LastUsedEpochKeyId = CYW30739ConfigKey(kChipConfig_KeyBase, 0x03); + static constexpr Key kConfigKey_FailSafeArmed = CYW30739ConfigKey(kChipConfig_KeyBase, 0x04); + static constexpr Key kConfigKey_GroupKey = CYW30739ConfigKey(kChipConfig_KeyBase, 0x05); + static constexpr Key kConfigKey_RegulatoryLocation = CYW30739ConfigKey(kChipConfig_KeyBase, 0x07); + static constexpr Key kConfigKey_CountryCode = CYW30739ConfigKey(kChipConfig_KeyBase, 0x08); + static constexpr Key kConfigKey_RebootCount = CYW30739ConfigKey(kChipConfig_KeyBase, 0x09); + static constexpr Key kConfigKey_UniqueId = CYW30739ConfigKey(kChipConfig_KeyBase, 0x0a); + static constexpr Key kConfigKey_LockUser = CYW30739ConfigKey(kChipConfig_KeyBase, 0x0b); + static constexpr Key kConfigKey_Credential = CYW30739ConfigKey(kChipConfig_KeyBase, 0x0c); + static constexpr Key kConfigKey_LockUserName = CYW30739ConfigKey(kChipConfig_KeyBase, 0x0d); + static constexpr Key kConfigKey_CredentialData = CYW30739ConfigKey(kChipConfig_KeyBase, 0x0e); + static constexpr Key kConfigKey_UserCredentials = CYW30739ConfigKey(kChipConfig_KeyBase, 0x0f); + static constexpr Key kConfigKey_WeekDaySchedules = CYW30739ConfigKey(kChipConfig_KeyBase, 0x10); + static constexpr Key kConfigKey_YearDaySchedules = CYW30739ConfigKey(kChipConfig_KeyBase, 0x11); + static constexpr Key kConfigKey_HolidaySchedules = CYW30739ConfigKey(kChipConfig_KeyBase, 0x12); + static constexpr Key kConfigKey_BootReason = CYW30739ConfigKey(kChipConfig_KeyBase, 0x13); + static constexpr Key kConfigKey_ProvisioningDAC = CYW30739ConfigKey(kChipConfig_KeyBase, 0xe0); + static constexpr Key kConfigKey_ProvisioningPAICert = CYW30739ConfigKey(kChipConfig_KeyBase, 0xe1); + static constexpr Key kConfigKey_ProvisioningSecret = CYW30739ConfigKey(kChipConfig_KeyBase, 0xe2); + static constexpr Key kConfigKey_ProvisioningSecretMetaData = CYW30739ConfigKey(kChipConfig_KeyBase, 0xe3); + static constexpr Key kConfigKey_ProvisioningDACMetaData = CYW30739ConfigKey(kChipConfig_KeyBase, 0xe4); + static constexpr Key kConfigKey_ProvisioningDACKeyMetaData = CYW30739ConfigKey(kChipConfig_KeyBase, 0xe5); + static constexpr Key kConfigKey_ProvisioningManifest = CYW30739ConfigKey(kChipConfig_KeyBase, 0xe6); + static constexpr Key kConfigKey_ProvisioningFragment = CYW30739ConfigKey(kChipConfig_KeyBase, 0xe7); // Set key id limits for each group. static constexpr Key kMinConfigKey_ChipFactory = CYW30739ConfigKey(kChipFactory_KeyBase, 0x00); diff --git a/src/platform/Infineon/CYW30739/FactoryDataProvider.cpp b/src/platform/Infineon/CYW30739/FactoryDataProvider.cpp index 0ece03d3c19c83..412259bda75013 100644 --- a/src/platform/Infineon/CYW30739/FactoryDataProvider.cpp +++ b/src/platform/Infineon/CYW30739/FactoryDataProvider.cpp @@ -25,6 +25,11 @@ namespace DeviceLayer { using namespace chip::DeviceLayer::Internal; +CHIP_ERROR FactoryDataProvider::Init() +{ + return CHIP_NO_ERROR; +} + /* * Members functions that implement the CommissionableDataProvider */ diff --git a/src/platform/Infineon/CYW30739/FactoryDataProvider.h b/src/platform/Infineon/CYW30739/FactoryDataProvider.h index def69734e1dc22..07228520f8ceb6 100644 --- a/src/platform/Infineon/CYW30739/FactoryDataProvider.h +++ b/src/platform/Infineon/CYW30739/FactoryDataProvider.h @@ -30,6 +30,8 @@ class FactoryDataProvider : public CommissionableDataProvider, public DeviceInstanceInfoProvider { public: + CHIP_ERROR Init(); + // ===== Members functions that implement the CommissionableDataProvider CHIP_ERROR GetSetupDiscriminator(uint16_t & setupDiscriminator); CHIP_ERROR SetSetupDiscriminator(uint16_t setupDiscriminator); diff --git a/src/platform/Infineon/CYW30739/OptigaFactoryDataProvider.cpp b/src/platform/Infineon/CYW30739/OptigaFactoryDataProvider.cpp new file mode 100644 index 00000000000000..5febc46f7170c3 --- /dev/null +++ b/src/platform/Infineon/CYW30739/OptigaFactoryDataProvider.cpp @@ -0,0 +1,93 @@ +/* + * + * 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 "OptigaFactoryDataProvider.h" + +#include +#include + +namespace chip { +namespace DeviceLayer { + +using namespace chip::DeviceLayer::Internal; + +/* + * Members functions that implement the DeviceAttestationCredentialsProvider + */ +CHIP_ERROR OptigaFactoryDataProvider::GetDeviceAttestationCert(MutableByteSpan & out_dac_buffer) +{ + uint16_t length = out_dac_buffer.size(); + VerifyOrReturnError(wiced_optiga_read_data(OPTIGA_DAC_OBJECT_ID, OPTIGA_OBJECT_DATA, out_dac_buffer.data(), &length) == + WICED_SUCCESS, + CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); + out_dac_buffer.reduce_size(length); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OptigaFactoryDataProvider::GetProductAttestationIntermediateCert(MutableByteSpan & out_pai_buffer) +{ + uint16_t length = out_pai_buffer.size(); + VerifyOrReturnError(wiced_optiga_read_data(OPTIGA_PAI_CERT_OBJECT_ID, OPTIGA_OBJECT_DATA, out_pai_buffer.data(), &length) == + WICED_SUCCESS, + CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); + out_pai_buffer.reduce_size(length); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OptigaFactoryDataProvider::SignWithDeviceAttestationKey(const ByteSpan & message_to_sign, + MutableByteSpan & out_signature_buffer) +{ + VerifyOrReturnError(!out_signature_buffer.empty(), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(!message_to_sign.empty(), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(out_signature_buffer.size() >= Crypto::kMax_ECDSA_Signature_Length, CHIP_ERROR_BUFFER_TOO_SMALL); + + uint8_t digest[Crypto::kSHA256_Hash_Length]; + ReturnErrorOnFailure(Crypto::Hash_SHA256(message_to_sign.data(), message_to_sign.size(), digest)); + ByteSpan digest_span(digest); + + constexpr uint8_t kAsn1HeaderLength = 2u; + constexpr uint8_t kSeqTag = 0x30u; + + uint8_t asn1_sigature_buffer[Crypto::kMax_ECDSA_Signature_Length_Der]; + MutableByteSpan asn1_signature_span(asn1_sigature_buffer); + + MutableByteSpan optiga_signature_span(asn1_signature_span.SubSpan(kAsn1HeaderLength)); + ReturnErrorOnFailure(SignWithOptigaDeviceAttestationKey(digest_span, optiga_signature_span)); + + asn1_signature_span[0] = kSeqTag; + asn1_signature_span[1] = optiga_signature_span.size(); + asn1_signature_span.reduce_size(kAsn1HeaderLength + optiga_signature_span.size()); + ReturnErrorOnFailure(Crypto::EcdsaAsn1SignatureToRaw(Crypto::kP256_FE_Length, asn1_signature_span, out_signature_buffer)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OptigaFactoryDataProvider::SignWithOptigaDeviceAttestationKey(const ByteSpan & digest, MutableByteSpan & signature) +{ + uint16_t signature_length = signature.size(); + VerifyOrReturnError(wiced_optiga_ecdsa_sign(digest.data(), digest.size(), OPTIGA_DAC_KEY_OBJECT_ID, signature.data(), + &signature_length) == WICED_SUCCESS, + CHIP_ERROR_INTERNAL); + signature.reduce_size(signature_length); + + return CHIP_NO_ERROR; +} + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/Infineon/CYW30739/OptigaFactoryDataProvider.h b/src/platform/Infineon/CYW30739/OptigaFactoryDataProvider.h new file mode 100644 index 00000000000000..a11074632c9ba5 --- /dev/null +++ b/src/platform/Infineon/CYW30739/OptigaFactoryDataProvider.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. + */ + +#pragma once + +#include "FactoryDataProvider.h" + +namespace chip { +namespace DeviceLayer { + +class OptigaFactoryDataProvider : public FactoryDataProvider +{ +public: + // ===== Members functions that implement the DeviceAttestationCredentialsProvider + CHIP_ERROR GetDeviceAttestationCert(MutableByteSpan & out_dac_buffer) override; + CHIP_ERROR GetProductAttestationIntermediateCert(MutableByteSpan & out_pai_buffer) override; + CHIP_ERROR SignWithDeviceAttestationKey(const ByteSpan & message_to_sign, MutableByteSpan & out_signature_buffer) override; + +protected: + CHIP_ERROR SignWithOptigaDeviceAttestationKey(const ByteSpan & digest, MutableByteSpan & signature); +}; + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/Infineon/CYW30739/UnprovisionedOptigaFactoryDataProvider.cpp b/src/platform/Infineon/CYW30739/UnprovisionedOptigaFactoryDataProvider.cpp new file mode 100644 index 00000000000000..9d56181d6bd79b --- /dev/null +++ b/src/platform/Infineon/CYW30739/UnprovisionedOptigaFactoryDataProvider.cpp @@ -0,0 +1,193 @@ +/* + * + * 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 "UnprovisionedOptigaFactoryDataProvider.h" + +#include +#include + +using namespace chip::DeviceLayer::Internal; + +namespace chip { +namespace DeviceLayer { + +CHIP_ERROR UnprovisionedOptigaFactoryDataProvider::Init() +{ + ReturnErrorOnFailure(OptigaFactoryDataProvider::Init()); + + if (CYW30739Config::ConfigValueExists(CYW30739Config::kConfigKey_ProvisioningDAC)) + { + if (!ChipError::IsSuccess(CheckProvisionedDataValidity())) + { + ReturnErrorOnFailure(ProvisionDataOnce()); + } + + CYW30739Config::ClearConfigValue(CYW30739Config::kConfigKey_ProvisioningDAC); + CYW30739Config::ClearConfigValue(CYW30739Config::kConfigKey_ProvisioningPAICert); + CYW30739Config::ClearConfigValue(CYW30739Config::kConfigKey_ProvisioningSecret); + CYW30739Config::ClearConfigValue(CYW30739Config::kConfigKey_ProvisioningSecretMetaData); + CYW30739Config::ClearConfigValue(CYW30739Config::kConfigKey_ProvisioningDACMetaData); + CYW30739Config::ClearConfigValue(CYW30739Config::kConfigKey_ProvisioningDACKeyMetaData); + CYW30739Config::ClearConfigValue(CYW30739Config::kConfigKey_ProvisioningManifest); + CYW30739Config::ClearConfigValue(CYW30739Config::kConfigKey_ProvisioningFragment); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UnprovisionedOptigaFactoryDataProvider::ProvisionDataOnce() +{ + constexpr struct + { + CYW30739Config::Key key; + uint16_t object_id; + uint8_t object_type; + } configs[] = { + { CYW30739Config::kConfigKey_ProvisioningDAC, OPTIGA_DAC_OBJECT_ID, OPTIGA_OBJECT_DATA }, + { CYW30739Config::kConfigKey_ProvisioningPAICert, OPTIGA_PAI_CERT_OBJECT_ID, OPTIGA_OBJECT_DATA }, + { CYW30739Config::kConfigKey_ProvisioningSecret, OPTIGA_SECRET_OBJECT_ID, OPTIGA_OBJECT_DATA }, + { CYW30739Config::kConfigKey_ProvisioningSecretMetaData, OPTIGA_SECRET_OBJECT_ID, OPTIGA_OBJECT_METADATA }, + { CYW30739Config::kConfigKey_ProvisioningDACMetaData, OPTIGA_DAC_OBJECT_ID, OPTIGA_OBJECT_METADATA }, + { CYW30739Config::kConfigKey_ProvisioningDACKeyMetaData, OPTIGA_DAC_KEY_OBJECT_ID, OPTIGA_OBJECT_METADATA }, + }; + + ChipLogProgress(DeviceLayer, "Provisioning Optiga data"); + + for (size_t i = 0; i < ArraySize(configs); i++) + { + ProvisionDataFromConfig(configs[i].key, configs[i].object_id, configs[i].object_type); + } + + ProvisionProtectedData(); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UnprovisionedOptigaFactoryDataProvider::ProvisionDataFromConfig(CYW30739Config::Key key, uint16_t object_id, + uint8_t object_type) +{ + size_t read_size; + uint8_t buf[Credentials::kMaxDERCertLength]; + MutableByteSpan buf_span(buf); + ReturnErrorOnFailure(CYW30739Config::ReadConfigValueBin(key, buf_span.data(), buf_span.size(), read_size)); + buf_span.reduce_size(read_size); + + VerifyOrReturnError(wiced_optiga_write_data(object_id, object_type, buf_span.data(), buf_span.size()) == WICED_SUCCESS, + CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UnprovisionedOptigaFactoryDataProvider::CheckProvisionedDataValidity() +{ + uint8_t digest_buf[Crypto::kSHA256_Hash_Length]; + MutableByteSpan digest_span(digest_buf); + ReturnErrorOnFailure(Crypto::DRBG_get_bytes(digest_span.data(), digest_span.size())); + + uint8_t signature_buffer[Crypto::kMax_ECDSA_Signature_Length_Der]; + MutableByteSpan signature_span(signature_buffer); + ReturnErrorOnFailure(SignWithOptigaDeviceAttestationKey(digest_span, signature_span)); + + ReturnErrorOnFailure(VerifyOptigaSignature(digest_span, signature_span, OPTIGA_DAC_OBJECT_ID)); + + Crypto::P256PublicKey dac_public_key; + ReturnErrorOnFailure(GetDeviceAttestationCertPublicKey(dac_public_key)); + ReturnErrorOnFailure(VerifyOptigaSignature(digest_span, signature_span, dac_public_key)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UnprovisionedOptigaFactoryDataProvider::ProvisionProtectedData() +{ + constexpr size_t kMaxManifestLength = 256; + uint8_t buf[kMaxManifestLength]; + size_t read_size; + + /* Update the manifest */ + MutableByteSpan manifest_span(buf); + ReturnErrorOnFailure(CYW30739Config::ReadConfigValueBin(CYW30739Config::kConfigKey_ProvisioningManifest, manifest_span.data(), + manifest_span.size(), read_size)); + manifest_span.reduce_size(read_size); + + VerifyOrReturnError(wiced_optiga_protected_update_start(1, manifest_span.data(), manifest_span.size()) == WICED_SUCCESS, + CHIP_ERROR_INTERNAL); + + /* Update the fragment */ + MutableByteSpan fragment_span(buf); + ReturnErrorOnFailure(CYW30739Config::ReadConfigValueBin(CYW30739Config::kConfigKey_ProvisioningFragment, fragment_span.data(), + fragment_span.size(), read_size)); + fragment_span.reduce_size(read_size); + + VerifyOrReturnError(wiced_optiga_protected_update_final(fragment_span.data(), fragment_span.size()) == WICED_SUCCESS, + CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UnprovisionedOptigaFactoryDataProvider::VerifyOptigaSignature(MutableByteSpan & digest, MutableByteSpan & signature, + uint16_t public_key) +{ + VerifyOrReturnError(wiced_optiga_ecdsa_verify(digest.data(), digest.size(), signature.data(), signature.size(), + OPTIGA_CRYPT_OID_DATA, &public_key) == WICED_SUCCESS, + CHIP_ERROR_INVALID_SIGNATURE); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UnprovisionedOptigaFactoryDataProvider::VerifyOptigaSignature(MutableByteSpan & digest, MutableByteSpan & signature, + Crypto::P256PublicKey & public_key) +{ + VerifyOrReturnError(public_key.Length() == Crypto::kP256_PublicKey_Length, CHIP_ERROR_INVALID_PUBLIC_KEY); + + constexpr uint8_t kOptigaDerBitstringTag = 0x03; + constexpr uint8_t kOptigaDerAdditionalLength = 0x01; + constexpr uint8_t kOptigaDerNumUnusedBits = 0x00; + constexpr size_t kOptigaPublicKeyHeaderLength = 3; + static uint8_t host_public_key[kOptigaPublicKeyHeaderLength + Crypto::kP256_PublicKey_Length] = { + [0] = kOptigaDerBitstringTag, + [1] = kOptigaDerAdditionalLength + Crypto::kP256_PublicKey_Length, + [2] = kOptigaDerNumUnusedBits, + }; + memcpy(host_public_key + kOptigaPublicKeyHeaderLength, public_key, public_key.Length()); + const public_key_from_host_t public_key_from_host = { + .public_key = host_public_key, + .length = sizeof(host_public_key), + .key_type = OPTIGA_ECC_CURVE_NIST_P_256, + }; + VerifyOrReturnError(wiced_optiga_ecdsa_verify(digest.data(), digest.size(), signature.data(), signature.size(), + OPTIGA_CRYPT_HOST_DATA, &public_key_from_host) == WICED_SUCCESS, + CHIP_ERROR_INVALID_SIGNATURE); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UnprovisionedOptigaFactoryDataProvider::GetDeviceAttestationCertPublicKey(Crypto::P256PublicKey & public_key) +{ + size_t read_size; + uint8_t dac_buf[Credentials::kMaxDERCertLength]; + MutableByteSpan dac_span(dac_buf); + ReturnErrorOnFailure(CYW30739Config::ReadConfigValueBin(CYW30739Config::kConfigKey_ProvisioningDAC, dac_span.data(), + dac_span.size(), read_size)); + dac_span.reduce_size(read_size); + + ReturnErrorOnFailure(ExtractPubkeyFromX509Cert(dac_span, public_key)); + + return CHIP_NO_ERROR; +} + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/Infineon/CYW30739/UnprovisionedOptigaFactoryDataProvider.h b/src/platform/Infineon/CYW30739/UnprovisionedOptigaFactoryDataProvider.h new file mode 100644 index 00000000000000..8c746ee0210aac --- /dev/null +++ b/src/platform/Infineon/CYW30739/UnprovisionedOptigaFactoryDataProvider.h @@ -0,0 +1,43 @@ +/* + * + * 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. + */ + +#pragma once + +#include "OptigaFactoryDataProvider.h" + +#include + +namespace chip { +namespace DeviceLayer { + +class UnprovisionedOptigaFactoryDataProvider : public OptigaFactoryDataProvider +{ +public: + CHIP_ERROR Init(); + +private: + CHIP_ERROR ProvisionDataOnce(); + CHIP_ERROR ProvisionDataFromConfig(Internal::CYW30739Config::Key key, uint16_t object_id, uint8_t object_type); + CHIP_ERROR CheckProvisionedDataValidity(); + CHIP_ERROR ProvisionProtectedData(); + CHIP_ERROR VerifyOptigaSignature(MutableByteSpan & digest, MutableByteSpan & signature, uint16_t public_key); + CHIP_ERROR VerifyOptigaSignature(MutableByteSpan & digest, MutableByteSpan & signature, Crypto::P256PublicKey & public_key); + CHIP_ERROR GetDeviceAttestationCertPublicKey(Crypto::P256PublicKey & public_key); +}; + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/Infineon/CYW30739/args.gni b/src/platform/Infineon/CYW30739/args.gni index 11bfab9379ff68..84a72321b420bd 100644 --- a/src/platform/Infineon/CYW30739/args.gni +++ b/src/platform/Infineon/CYW30739/args.gni @@ -65,3 +65,5 @@ openthread_config_ip6_slaac_enable = true openthread_config_joiner_enable = true openthread_config_log_output = "platform_defined" openthread_config_srp_client_enable = true + +optiga_trust_m_dir = "${chip_root}/third_party/infineon/trustm/optiga-trust-m" diff --git a/src/platform/Infineon/CYW30739/cyw30739-chip-mbedtls-config.h b/src/platform/Infineon/CYW30739/cyw30739-chip-mbedtls-config.h index 1446a2d3d7b3a9..c6e275d4521f69 100644 --- a/src/platform/Infineon/CYW30739/cyw30739-chip-mbedtls-config.h +++ b/src/platform/Infineon/CYW30739/cyw30739-chip-mbedtls-config.h @@ -188,6 +188,39 @@ */ #define MBEDTLS_PKCS5_C +/** + * \def MBEDTLS_X509_USE_C + * + * Enable X.509 core for using certificates. + * + * Module: library/x509.c + * Caller: library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: MBEDTLS_ASN1_PARSE_C, MBEDTLS_BIGNUM_C, MBEDTLS_OID_C, + * MBEDTLS_PK_PARSE_C + * + * This module is required for the X.509 parsing modules. + */ +#define MBEDTLS_X509_USE_C + +/** + * \def MBEDTLS_X509_CRT_PARSE_C + * + * Enable X.509 certificate parsing. + * + * Module: library/x509_crt.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * Requires: MBEDTLS_X509_USE_C + * + * This module is required for X.509 certificate parsing. + */ + +#define MBEDTLS_X509_CRT_PARSE_C /** * \def MBEDTLS_X509_CREATE_C * diff --git a/src/platform/Infineon/CYW30739/cyw30739_platform.gni b/src/platform/Infineon/CYW30739/cyw30739_platform.gni index 367727207e506b..9854a590b99841 100644 --- a/src/platform/Infineon/CYW30739/cyw30739_platform.gni +++ b/src/platform/Infineon/CYW30739/cyw30739_platform.gni @@ -20,7 +20,7 @@ import("${chip_root}/src/platform/Infineon/CYW30739/args.gni") cyw30739_platform_dir = "${chip_root}/src/platform/Infineon/CYW30739" template("cyw30739_platform") { - forward_variables_from(invoker, [ "chip_family" ]) + forward_variables_from(invoker, [ "board" ]) static_library(target_name) { sources = [ @@ -29,6 +29,7 @@ template("cyw30739_platform") { "${cyw30739_platform_dir}/ConfigurationManagerImpl.cpp", "${cyw30739_platform_dir}/DiagnosticDataProviderImpl.cpp", "${cyw30739_platform_dir}/EventFlags.cpp", + "${cyw30739_platform_dir}/FactoryDataProvider.cpp", "${cyw30739_platform_dir}/Logging.cpp", "${cyw30739_platform_dir}/OTAImageProcessorImpl.cpp", "${cyw30739_platform_dir}/PlatformManagerImpl.cpp", @@ -40,8 +41,14 @@ template("cyw30739_platform") { sources += [ "${cyw30739_platform_dir}/ThreadStackManagerImpl.cpp" ] } - configs += - [ "${matter_wpan_sdk_build_root}:wpan_sdk-${chip_family}-config" ] + if (invoker.board_use_optiga) { + sources += [ + "${cyw30739_platform_dir}/OptigaFactoryDataProvider.cpp", + "${cyw30739_platform_dir}/UnprovisionedOptigaFactoryDataProvider.cpp", + ] + } + + configs += [ "${matter_wpan_sdk_build_root}:${board}-config" ] deps = [ "${chip_root}/src/platform:platform", diff --git a/src/platform/Linux/DiagnosticDataProviderImpl.cpp b/src/platform/Linux/DiagnosticDataProviderImpl.cpp index 5ee5558064afeb..bebe5827163fa9 100644 --- a/src/platform/Linux/DiagnosticDataProviderImpl.cpp +++ b/src/platform/Linux/DiagnosticDataProviderImpl.cpp @@ -74,8 +74,10 @@ enum class WiFiStatsCountType kWiFiOverrunCount }; +#if defined(__GLIBC__) // Static variable to store the maximum heap size static size_t maxHeapHighWatermark = 0; +#endif CHIP_ERROR GetEthernetStatsCount(EthernetStatsCountType type, uint64_t & count) { @@ -223,9 +225,7 @@ DiagnosticDataProviderImpl & DiagnosticDataProviderImpl::GetDefaultInstance() CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapFree(uint64_t & currentHeapFree) { -#ifndef __GLIBC__ - return CHIP_ERROR_NOT_IMPLEMENTED; -#else +#if defined(__GLIBC__) struct mallinfo mallocInfo = mallinfo(); // Get the current amount of heap memory, in bytes, that are not being utilized @@ -233,14 +233,14 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapFree(uint64_t & currentHeap currentHeapFree = mallocInfo.fordblks; return CHIP_NO_ERROR; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; #endif } CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapUsed(uint64_t & currentHeapUsed) { -#ifndef __GLIBC__ - return CHIP_ERROR_NOT_IMPLEMENTED; -#else +#if defined(__GLIBC__) struct mallinfo mallocInfo = mallinfo(); // Get the current amount of heap memory, in bytes, that are being used by @@ -253,14 +253,14 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapUsed(uint64_t & currentHeap maxHeapHighWatermark = currentHeapUsed; } return CHIP_NO_ERROR; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; #endif } CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapHighWatermark(uint64_t & currentHeapHighWatermark) { -#ifndef __GLIBC__ - return CHIP_ERROR_NOT_IMPLEMENTED; -#else +#if defined(__GLIBC__) struct mallinfo mallocInfo = mallinfo(); // The usecase of this function is embedded devices,on which we would need to intercept @@ -279,6 +279,8 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapHighWatermark(uint64_t & cu currentHeapHighWatermark = maxHeapHighWatermark; return CHIP_NO_ERROR; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; #endif } @@ -287,10 +289,12 @@ CHIP_ERROR DiagnosticDataProviderImpl::ResetWatermarks() // If implemented, the server SHALL set the value of the CurrentHeapHighWatermark attribute to the // value of the CurrentHeapUsed. +#if defined(__GLIBC__) // Get the current amount of heap memory, in bytes, that are being used by // the current running program and reset the max heap high watermark to current heap amount. struct mallinfo mallocInfo = mallinfo(); maxHeapHighWatermark = mallocInfo.uordblks; +#endif return CHIP_NO_ERROR; } diff --git a/src/platform/Linux/bluez/BluezConnection.cpp b/src/platform/Linux/bluez/BluezConnection.cpp index 722cb2478d1665..eeb1e7fb8f1343 100644 --- a/src/platform/Linux/bluez/BluezConnection.cpp +++ b/src/platform/Linux/bluez/BluezConnection.cpp @@ -335,7 +335,7 @@ void BluezConnection::SubscribeCharacteristicDone(GObject * aObject, GAsyncResul auto * pC2 = reinterpret_cast(aObject); GAutoPtr error; - gboolean success = bluez_gatt_characteristic1_call_write_value_finish(pC2, aResult, &error.GetReceiver()); + gboolean success = bluez_gatt_characteristic1_call_start_notify_finish(pC2, aResult, &error.GetReceiver()); VerifyOrReturn(success == TRUE, ChipLogError(DeviceLayer, "FAIL: SubscribeCharacteristic : %s", error->message)); @@ -367,7 +367,7 @@ void BluezConnection::UnsubscribeCharacteristicDone(GObject * aObject, GAsyncRes auto * pC2 = reinterpret_cast(aObject); GAutoPtr error; - gboolean success = bluez_gatt_characteristic1_call_write_value_finish(pC2, aResult, &error.GetReceiver()); + gboolean success = bluez_gatt_characteristic1_call_stop_notify_finish(pC2, aResult, &error.GetReceiver()); VerifyOrReturn(success == TRUE, ChipLogError(DeviceLayer, "FAIL: UnsubscribeCharacteristic : %s", error->message)); diff --git a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h index 6b9e1f7432612c..317bff7c05403e 100644 --- a/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h +++ b/src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h @@ -230,7 +230,6 @@ class GenericThreadStackManagerImpl_OpenThread DnsBrowseCallback mDnsBrowseCallback; DnsResolveCallback mDnsResolveCallback; - GeneralFaults mNetworkFaults; struct DnsServiceTxtEntries { @@ -264,6 +263,8 @@ class GenericThreadStackManagerImpl_OpenThread #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT + GeneralFaults mNetworkFaults; + inline ImplClass * Impl() { return static_cast(this); } }; diff --git a/src/platform/OpenThread/OpenThreadDnssdImpl.cpp b/src/platform/OpenThread/OpenThreadDnssdImpl.cpp index cb205df95eb72b..f5b0a85f2d0d83 100644 --- a/src/platform/OpenThread/OpenThreadDnssdImpl.cpp +++ b/src/platform/OpenThread/OpenThreadDnssdImpl.cpp @@ -24,6 +24,7 @@ namespace Dnssd { CHIP_ERROR OpenThreadDnssdInit(DnssdAsyncReturnCallback initCallback, DnssdAsyncReturnCallback errorCallback, void * context) { +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT ReturnErrorOnFailure(ThreadStackMgr().SetSrpDnsCallbacks(initCallback, errorCallback, context)); uint8_t macBuffer[ConfigurationManager::kPrimaryMACAddressLength]; @@ -33,6 +34,9 @@ CHIP_ERROR OpenThreadDnssdInit(DnssdAsyncReturnCallback initCallback, DnssdAsync MakeHostName(hostname, sizeof(hostname), mac); return ThreadStackMgr().ClearSrpHost(hostname); +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT } const char * GetProtocolString(DnssdServiceProtocol protocol) diff --git a/src/platform/android/java/chip/platform/NsdManagerServiceBrowser.java b/src/platform/android/java/chip/platform/NsdManagerServiceBrowser.java index 28fc6988a9dbbd..e4ac8a673b5db2 100644 --- a/src/platform/android/java/chip/platform/NsdManagerServiceBrowser.java +++ b/src/platform/android/java/chip/platform/NsdManagerServiceBrowser.java @@ -172,7 +172,11 @@ public void onStopDiscoveryFailed(String serviceType, int errorCode) { @Override public void onDiscoveryStopped(String serviceType) { Log.w(TAG, "Successfully stopped discovery service '" + serviceType); - this.handleServiceBrowse(chipMdnsCallback); + new Handler(Looper.getMainLooper()) + .post( + () -> { + this.handleServiceBrowse(chipMdnsCallback); + }); } public void handleServiceBrowse(ChipMdnsCallback chipMdnsCallback) { diff --git a/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java b/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java index 1f1a9efa4307f0..11b53537bf7d71 100644 --- a/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java +++ b/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java @@ -39,6 +39,7 @@ public class NsdManagerServiceResolver implements ServiceResolver { private static final long RESOLVE_SERVICE_TIMEOUT = 30000; private final NsdManager nsdManager; private MulticastLock multicastLock; + private MulticastLock publishMulticastLock; private List registrationListeners = new ArrayList<>(); private final CopyOnWriteArrayList mMFServiceName = new CopyOnWriteArrayList<>(); @Nullable private final NsdManagerResolverAvailState nsdManagerResolverAvailState; @@ -60,6 +61,12 @@ public NsdManagerServiceResolver( ((WifiManager) context.getSystemService(Context.WIFI_SERVICE)) .createMulticastLock("chipMulticastLock"); this.multicastLock.setReferenceCounted(true); + + this.publishMulticastLock = + ((WifiManager) context.getSystemService(Context.WIFI_SERVICE)) + .createMulticastLock("chipPublishMulticastLock"); + this.publishMulticastLock.setReferenceCounted(true); + this.nsdManagerResolverAvailState = nsdManagerResolverAvailState; this.timeout = timeout; } @@ -204,7 +211,7 @@ public void onServiceUnregistered(NsdServiceInfo serviceInfo) { } }; if (registrationListeners.size() == 0) { - multicastLock.acquire(); + publishMulticastLock.acquire(); } registrationListeners.add(registrationListener); mMFServiceName.add(serviceName); @@ -216,8 +223,8 @@ public void onServiceUnregistered(NsdServiceInfo serviceInfo) { @Override public void removeServices() { Log.d(TAG, "removeServices: "); - if (registrationListeners.size() > 0) { - multicastLock.release(); + if (registrationListeners.size() > 0 && publishMulticastLock.isHeld()) { + publishMulticastLock.release(); } for (NsdManager.RegistrationListener l : registrationListeners) { Log.i(TAG, "Remove " + l); diff --git a/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java b/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java index 70ffdbc0102a62..c1a187680c5c73 100644 --- a/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java +++ b/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java @@ -21,6 +21,8 @@ import android.net.nsd.NsdManager; import android.net.nsd.NsdServiceInfo; import android.net.wifi.WifiManager.MulticastLock; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import androidx.annotation.Nullable; import java.util.concurrent.Executors; @@ -118,20 +120,24 @@ public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { Log.w( TAG, "Failed to resolve service '" + serviceInfo.getServiceName() + "': " + errorCode); - chipMdnsCallback.handleServiceResolve( - serviceInfo.getServiceName(), - // Use the target service info since the resolved service info sometimes appends a - // "." at the front likely because it is trying to strip the service name out of it - // and something is missed. - // The target service info service type should be effectively the same as the - // resolved service info. - NsdServiceFinderAndResolver.this.targetServiceInfo.getServiceType(), - null, - null, - 0, - null, - callbackHandle, - contextHandle); + new Handler(Looper.getMainLooper()) + .post( + () -> { + chipMdnsCallback.handleServiceResolve( + serviceInfo.getServiceName(), + // Use the target service info since the resolved service info sometimes + // appends a "." at the front likely because it is trying to strip the + // service name out of it and something is missed. + // The target service info service type should be effectively the same as + // the resolved service info. + NsdServiceFinderAndResolver.this.targetServiceInfo.getServiceType(), + null, + null, + 0, + null, + callbackHandle, + contextHandle); + }); if (multicastLock.isHeld()) { multicastLock.release(); @@ -153,21 +159,28 @@ public void onServiceResolved(NsdServiceInfo serviceInfo) { + serviceInfo.getHost() + ", type : " + serviceInfo.getServiceType()); - // TODO: Find out if DNS-SD results for Android should contain interface ID - chipMdnsCallback.handleServiceResolve( - serviceInfo.getServiceName(), - // Use the target service info since the resolved service info sometimes appends a - // "." at the front likely because it is trying to strip the service name out of it - // and something is missed. - // The target service info service type should be effectively the same as the - // resolved service info. - NsdServiceFinderAndResolver.this.targetServiceInfo.getServiceType(), - serviceInfo.getHost().getHostName(), - serviceInfo.getHost().getHostAddress(), - serviceInfo.getPort(), - serviceInfo.getAttributes(), - callbackHandle, - contextHandle); + final String hostName = serviceInfo.getHost().getHostName(); + final String address = serviceInfo.getHost().getHostAddress(); + final int port = serviceInfo.getPort(); + new Handler(Looper.getMainLooper()) + .post( + () -> { + // TODO: Find out if DNS-SD results for Android should contain interface ID + chipMdnsCallback.handleServiceResolve( + serviceInfo.getServiceName(), + // Use the target service info since the resolved service info sometimes + // appends a "." at the front likely because it is trying to strip the + // service name out of it and something is missed. + // The target service info service type should be effectively the same as + // the resolved service info. + NsdServiceFinderAndResolver.this.targetServiceInfo.getServiceType(), + hostName, + address, + port, + serviceInfo.getAttributes(), + callbackHandle, + contextHandle); + }); if (multicastLock.isHeld()) { multicastLock.release(); diff --git a/src/platform/qpg/BLEManagerImpl.cpp b/src/platform/qpg/BLEManagerImpl.cpp index 271c9b02e10d9f..45d291e651146d 100644 --- a/src/platform/qpg/BLEManagerImpl.cpp +++ b/src/platform/qpg/BLEManagerImpl.cpp @@ -98,10 +98,10 @@ CHIP_ERROR BLEManagerImpl::_Init() err = BleLayer::Init(this, this, &DeviceLayer::SystemLayer()); SuccessOrExit(err); - appCbacks.stackCback = ExternalCbHandler; - appCbacks.chrReadCback = HandleTXCharRead; - appCbacks.chrWriteCback = HandleRXCharWrite; - appCbacks.cccCback = _handleTXCharCCCDWrite; + appCbacks.stackCallback = ExternalCbHandler; + appCbacks.chrReadCallback = HandleTXCharRead; + appCbacks.chrWriteCallback = HandleRXCharWrite; + appCbacks.cccCallback = _handleTXCharCCCDWrite; #if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING qvCHIP_BleSetUUIDs(chipUUID_CHIPoBLE_Service, chipUUID_CHIPoBLEChar_TX.bytes, chipUUID_CHIPoBLEChar_RX.bytes, @@ -210,7 +210,7 @@ void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) case DeviceEventType::kCHIPoBLESubscribe: { ChipDeviceEvent connEstEvent; - ChipLogProgress(DeviceLayer, "_OnPlatformEvent kCHIPoBLESubscribe"); + ChipLogDetail(DeviceLayer, "_OnPlatformEvent kCHIPoBLESubscribe"); HandleSubscribeReceived(event->CHIPoBLESubscribe.ConId, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_TX); connEstEvent.Type = DeviceEventType::kCHIPoBLEConnectionEstablished; PlatformMgr().PostEventOrDie(&connEstEvent); @@ -220,7 +220,7 @@ void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) case DeviceEventType::kCHIPoBLEUnsubscribe: { ChipDeviceEvent connClosedEvent; - ChipLogProgress(DeviceLayer, "_OnPlatformEvent kCHIPoBLEUnsubscribe"); + ChipLogDetail(DeviceLayer, "_OnPlatformEvent kCHIPoBLEUnsubscribe"); HandleUnsubscribeReceived(event->CHIPoBLEUnsubscribe.ConId, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_TX); connClosedEvent.Type = DeviceEventType::kCHIPoBLEConnectionClosed; PlatformMgr().PostEventOrDie(&connClosedEvent); @@ -228,14 +228,14 @@ void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) break; case DeviceEventType::kCHIPoBLEWriteReceived: { - ChipLogProgress(DeviceLayer, "_OnPlatformEvent kCHIPoBLEWriteReceived"); + ChipLogDetail(DeviceLayer, "_OnPlatformEvent kCHIPoBLEWriteReceived"); HandleWriteReceived(event->CHIPoBLEWriteReceived.ConId, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_RX, PacketBufferHandle::Adopt(event->CHIPoBLEWriteReceived.Data)); } break; case DeviceEventType::kCHIPoBLEConnectionError: { - ChipLogProgress(DeviceLayer, "_OnPlatformEvent kCHIPoBLEConnectionError"); + ChipLogDetail(DeviceLayer, "_OnPlatformEvent kCHIPoBLEConnectionError"); HandleConnectionError(event->CHIPoBLEConnectionError.ConId, event->CHIPoBLEConnectionError.Reason); } break; @@ -250,7 +250,7 @@ void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) break; case DeviceEventType::kCHIPoBLEIndicateConfirm: { - ChipLogProgress(DeviceLayer, "_OnPlatformEvent kCHIPoBLEIndicateConfirm"); + ChipLogDetail(DeviceLayer, "_OnPlatformEvent kCHIPoBLEIndicateConfirm"); HandleIndicationConfirmation(event->CHIPoBLEIndicateConfirm.ConId, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_TX); } break; @@ -610,7 +610,7 @@ void BLEManagerImpl::HandleRXCharWrite(uint16_t connId, uint16_t handle, uint8_t { CHIP_ERROR err = CHIP_NO_ERROR; - ChipLogProgress(DeviceLayer, "Write request received for CHIPoBLE Client RX characteristic (con %u, len %u)", connId, len); + ChipLogDetail(DeviceLayer, "Write request received for CHIPoBLE Client RX characteristic (con %u, len %u)", connId, len); // Copy the data to a packet buffer. PacketBufferHandle buf = System::PacketBufferHandle::NewWithData(pValue, len, 0, 0); @@ -727,11 +727,6 @@ void BLEManagerImpl::HandleDmMsg(qvCHIP_Ble_DmEvt_t * pDmEvt) ChipLogError(DeviceLayer, "QVCHIP_DM_ADV_STOP_IND error: %d", (int) pDmEvt->advSetStop.status); return; } - - if (mFlags.Has(Flags::kRestartAdvertising)) - { - BLEMgr().SetAdvertisingMode(BLEAdvertisingMode::kSlowAdvertising); - } break; } case QVCHIP_DM_CONN_OPEN_IND: { @@ -843,13 +838,13 @@ void BLEManagerImpl::ExternalCbHandler(qvCHIP_Ble_MsgHdr_t * pMsg) /* Process advertising/scanning and connection-related messages */ if (pMsg->event >= QVCHIP_DM_CBACK_START && pMsg->event <= QVCHIP_DM_CBACK_END) { - ChipLogProgress(DeviceLayer, "DM event %d: status %d", pMsg->event, pMsg->status); + ChipLogDetail(DeviceLayer, "DM event %d: status %d", pMsg->event, pMsg->status); sInstance.HandleDmMsg((qvCHIP_Ble_DmEvt_t *) pMsg); } /* Process attribute-related messages */ else if (pMsg->event >= QVCHIP_ATT_CBACK_START && pMsg->event <= QVCHIP_ATT_CBACK_END) { - ChipLogProgress(DeviceLayer, "ATT event %d: status %d", pMsg->event, pMsg->status); + ChipLogDetail(DeviceLayer, "ATT event %d: status %d", pMsg->event, pMsg->status); sInstance.HandleAttMsg((qvCHIP_Ble_AttEvt_t *) pMsg); } else @@ -929,6 +924,7 @@ void BLEManagerImpl::BleAdvTimeoutHandler(TimerHandle_t xTimer) ChipLogDetail(DeviceLayer, "bleAdv Timeout : Start slow advertisement"); sInstance.mFlags.Set(Flags::kRestartAdvertising); sInstance.StopAdvertising(); + BLEMgr().SetAdvertisingMode(BLEAdvertisingMode::kSlowAdvertising); } } diff --git a/src/platform/qpg/ConfigurationManagerImpl.cpp b/src/platform/qpg/ConfigurationManagerImpl.cpp index a7a59bf1438483..6dc81dcf4b7678 100644 --- a/src/platform/qpg/ConfigurationManagerImpl.cpp +++ b/src/platform/qpg/ConfigurationManagerImpl.cpp @@ -71,6 +71,12 @@ CHIP_ERROR ConfigurationManagerImpl::Init() SuccessOrExit(err); } + if (!QPGConfig::ConfigValueExists(QPGConfig::kCounterKey_TotalOperationalHours)) + { + err = StoreTotalOperationalHours(0); + SuccessOrExit(err); + } + qvRebootReason = qvCHIP_GetResetReason(); switch (qvRebootReason) @@ -121,12 +127,6 @@ CHIP_ERROR ConfigurationManagerImpl::StoreRebootCount(uint32_t rebootCount) CHIP_ERROR ConfigurationManagerImpl::GetTotalOperationalHours(uint32_t & totalOperationalHours) { - if (!QPGConfig::ConfigValueExists(QPGConfig::kCounterKey_TotalOperationalHours)) - { - totalOperationalHours = 0; - return CHIP_NO_ERROR; - } - return QPGConfig::ReadConfigValue(QPGConfig::kCounterKey_TotalOperationalHours, totalOperationalHours); } diff --git a/src/platform/qpg/args.gni b/src/platform/qpg/args.gni index 94669645f4cf2d..75e99c6d9a6863 100644 --- a/src/platform/qpg/args.gni +++ b/src/platform/qpg/args.gni @@ -42,13 +42,6 @@ lwip_debug = false chip_build_tests = false openthread_external_mbedtls = mbedtls_target -openthread_project_core_config_file = - "openthread-core-${qpg_target_ic}-config.h" -openthread_core_config_platform_check_file = - "openthread-core-${qpg_target_ic}-config-check.h" -openthread_core_config_deps = [ - "${chip_root}/third_party/openthread/platforms/qpg:libopenthread-qpg-config", -] openthread_external_platform = "${chip_root}/third_party/openthread/platforms/qpg:libopenthread-qpg" diff --git a/src/platform/stm32/BLEManagerImpl.cpp b/src/platform/stm32/BLEManagerImpl.cpp index fc24ebc64ac006..7a040a5fb93ba3 100644 --- a/src/platform/stm32/BLEManagerImpl.cpp +++ b/src/platform/stm32/BLEManagerImpl.cpp @@ -24,7 +24,8 @@ /* this file behaves like a config.h, comes first */ #include -#include +#include +#include #include #include @@ -445,7 +446,15 @@ CHIP_ERROR BLEManagerImpl::StartAdvertising(void) mFlags.Clear(Flags::kRestartAdvertising); - APP_BLE_Adv_Request(APP_BLE_FAST_ADV); + if (mFlags.Has(Flags::kFastAdvertisingEnabled)) + { + APP_BLE_Adv_Request(APP_BLE_FAST_ADV); + } + else + { + APP_BLE_Adv_Request(APP_BLE_LP_ADV); + } + // Flag updated asynchronously by BLE host callback mFlags.Set(Flags::kAdvertising); @@ -700,16 +709,8 @@ void BLEManagerImpl::BleAdvTimeoutHandler(TimerHandle_t xTimer) { if (BLEMgrImpl().mFlags.Has(Flags::kFastAdvertisingEnabled)) { - /* Stop advertising and defer restart for when stop confirmation is received from the stack */ - ChipLogDetail(DeviceLayer, "bleAdv Timeout : Stop advertisement"); - sInstance.StopAdvertising(); - sInstance.mFlags.Set(Flags::kRestartAdvertising); - } - else if (BLEMgrImpl().mFlags.Has(Flags::kAdvertising)) - { - // Advertisement time expired. Stop advertising - ChipLogDetail(DeviceLayer, "bleAdv Timeout : Stop advertisement"); - BLEMgr().SetAdvertisingEnabled(false); + ChipLogDetail(DeviceLayer, "bleAdv Timeout : Start slow advertisement"); + BLEMgr().SetAdvertisingMode(BLEAdvertisingMode::kSlowAdvertising); } } diff --git a/src/platform/stm32/BUILD.gn b/src/platform/stm32/BUILD.gn index 43f656d0eca988..9b2ed9008c858a 100644 --- a/src/platform/stm32/BUILD.gn +++ b/src/platform/stm32/BUILD.gn @@ -10,7 +10,7 @@ # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and -# limitations under the License. +# limitations under the License. import("//build_overrides/chip.gni") import("//build_overrides/pigweed.gni") @@ -32,6 +32,7 @@ if (chip_enable_openthread) { } static_library("stm32") { + friend = [ ":lighting_app" ] deps = [ "${chip_root}/src/setup_payload" ] if (stm32_board == "STM32WB5MM-DK") { @@ -48,6 +49,8 @@ static_library("stm32") { "CHIPPlatformConfig.h", "ConfigurationManagerImpl.cpp", "ConfigurationManagerImpl.h", + "DeviceInfoProviderImpl.cpp", + "DeviceInfoProviderImpl.h", "DiagnosticDataProviderImpl.cpp", "DiagnosticDataProviderImpl.h", "FactoryDataProvider.cpp", @@ -55,6 +58,8 @@ static_library("stm32") { "InetPlatformConfig.h", "KeyValueStoreManagerImpl.cpp", "KeyValueStoreManagerImpl.h", + "OTAImageProcessorImpl.cpp", + "OTAImageProcessorImpl.h", "PlatformManagerImpl.cpp", "PlatformManagerImpl.h", "STM32Config.cpp", @@ -67,7 +72,10 @@ static_library("stm32") { deps += [ "${chip_root}/src/platform/logging:headers" ] } - public = [ "${chip_root}/src/credentials/DeviceAttestationCredsProvider.h" ] + public = [ + "${chip_root}/src/credentials/DeviceAttestationCredsProvider.h", + "${chip_root}/src/credentials/examples/ExampleDACs.h", + ] public_deps = [ "${chip_root}/src/crypto", "${chip_root}/src/platform:platform_base", diff --git a/src/platform/stm32/CHIPDevicePlatformConfig.h b/src/platform/stm32/CHIPDevicePlatformConfig.h index aee9ec36a2b73a..6fe4c14eb26a3c 100644 --- a/src/platform/stm32/CHIPDevicePlatformConfig.h +++ b/src/platform/stm32/CHIPDevicePlatformConfig.h @@ -36,7 +36,6 @@ #define CHIP_CONFIG_PERSISTED_STORAGE_KEY_TYPE uint16_t #define CHIP_CONFIG_PERSISTED_STORAGE_ENC_MSG_CNTR_ID 1 #define CHIP_CONFIG_PERSISTED_STORAGE_MAX_KEY_LENGTH 2 - #define CHIP_CONFIG_LIFETIIME_PERSISTED_COUNTER_KEY 0x01 #if CHIP_ENABLE_OPENTHREAD @@ -56,7 +55,10 @@ // ========== Platform-specific Configuration Overrides ========= -#define CHIP_DEVICE_CONFIG_CHIP_TASK_NAME "STM32WB TASK" +#define CHIP_DEVICE_CONFIG_DEVICE_VENDOR_NAME "STMicroelectronics" +#define CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_NAME "STM32WB Demo App" + +#define CHIP_DEVICE_CONFIG_CHIP_TASK_NAME "STM32 TASK" #define CHIP_DEVICE_CONFIG_CHIP_TASK_STACK_SIZE (8 * 1024) /** diff --git a/src/platform/stm32/CHIPMem-Platform.cpp b/src/platform/stm32/CHIPMem-Platform.cpp index 2e37bafe7cf1b8..b9bf90f429f444 100644 --- a/src/platform/stm32/CHIPMem-Platform.cpp +++ b/src/platform/stm32/CHIPMem-Platform.cpp @@ -162,6 +162,20 @@ static void VerifyInitialized(const char * func) } } +static size_t MemoryBlockSize(void * ptr) +{ + + uint8_t * p = static_cast(ptr); + // Subtract the size of the header from the pointer + p -= sizeof(size_t); + // Read the size of the memory block from the header + size_t size = *reinterpret_cast(p); + // Add the size of the header to the size of the memory block + size += sizeof(size_t); + + return size; +} + CHIP_ERROR MemoryAllocatorInit(void * buf, size_t bufSize) { if (memoryInitialized++ > 0) @@ -211,10 +225,31 @@ void * MemoryCalloc(size_t num, size_t size) void * MemoryRealloc(void * p, size_t size) { - VERIFY_INITIALIZED(); + void * new_ptr; + if (size == 0) + { + MemoryFree(p); + return NULL; + } - p = realloc(p, size); - return p; + new_ptr = MemoryAlloc(size); + if (new_ptr == NULL) + { + return NULL; + } + + if (p != NULL) + { + size_t copy_size = MemoryBlockSize(p); + if (copy_size > size) + { + copy_size = size; + } + memcpy(new_ptr, p, copy_size); + MemoryFree(p); + } + + return new_ptr; } void MemoryFree(void * p) diff --git a/src/platform/stm32/ConfigurationManagerImpl.cpp b/src/platform/stm32/ConfigurationManagerImpl.cpp index da8638460a78f3..cbfa37cf3c3272 100644 --- a/src/platform/stm32/ConfigurationManagerImpl.cpp +++ b/src/platform/stm32/ConfigurationManagerImpl.cpp @@ -164,5 +164,10 @@ ConfigurationManager & ConfigurationMgrImpl() return ConfigurationManagerImpl::GetDefaultInstance(); } +CHIP_ERROR ConfigurationManagerImpl::GetCountryCode(char * buf, size_t bufSize, size_t & codeLen) +{ + return STM32Config::ReadConfigValueStr(STM32Config::kConfigKey_CountryCode, buf, bufSize, codeLen); +} + } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/stm32/ConfigurationManagerImpl.h b/src/platform/stm32/ConfigurationManagerImpl.h index 78b885581ce151..46b5b6a1247de2 100644 --- a/src/platform/stm32/ConfigurationManagerImpl.h +++ b/src/platform/stm32/ConfigurationManagerImpl.h @@ -34,7 +34,7 @@ namespace DeviceLayer { */ // class ConfigurationManagerImpl final : public Internal::GenericConfigurationManagerImpl, -// public Internal::STM32Config +// public Internal::STM32Config class ConfigurationManagerImpl : public Internal::GenericConfigurationManagerImpl { public: @@ -66,6 +66,7 @@ class ConfigurationManagerImpl : public Internal::GenericConfigurationManagerImp CHIP_ERROR WriteConfigValueStr(Key key, const char * str, size_t strLen) override; CHIP_ERROR WriteConfigValueBin(Key key, const uint8_t * data, size_t dataLen) override; void RunConfigUnitTest(void) override; + CHIP_ERROR GetCountryCode(char * buf, size_t bufSize, size_t & codeLen) override; // ===== Private members reserved for use by this class only. diff --git a/src/platform/stm32/DeviceInfoProviderImpl.cpp b/src/platform/stm32/DeviceInfoProviderImpl.cpp new file mode 100644 index 00000000000000..5524b770c0585a --- /dev/null +++ b/src/platform/stm32/DeviceInfoProviderImpl.cpp @@ -0,0 +1,374 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace chip { +namespace DeviceLayer { + +namespace { +constexpr TLV::Tag kLabelNameTag = TLV::ContextTag(0); +constexpr TLV::Tag kLabelValueTag = TLV::ContextTag(1); +} // anonymous namespace + +DeviceInfoProviderImpl & DeviceInfoProviderImpl::GetDefaultInstance() +{ + static DeviceInfoProviderImpl sInstance; + return sInstance; +} + +DeviceInfoProvider::FixedLabelIterator * DeviceInfoProviderImpl::IterateFixedLabel(EndpointId endpoint) +{ + return chip::Platform::New(endpoint); +} + +DeviceInfoProviderImpl::FixedLabelIteratorImpl::FixedLabelIteratorImpl(EndpointId endpoint) : mEndpoint(endpoint) +{ + mIndex = 0; +} + +size_t DeviceInfoProviderImpl::FixedLabelIteratorImpl::Count() +{ + // A hardcoded labelList on all endpoints. + return 4; +} + +bool DeviceInfoProviderImpl::FixedLabelIteratorImpl::Next(FixedLabelType & output) +{ + bool retval = true; + + // A hardcoded list for testing only + CHIP_ERROR err = CHIP_NO_ERROR; + + const char * labelPtr = nullptr; + const char * valuePtr = nullptr; + + VerifyOrReturnError(mIndex < 4, false); + + ChipLogProgress(DeviceLayer, "Get the fixed label with index:%u at endpoint:%d", static_cast(mIndex), mEndpoint); + + switch (mIndex) + { + case 0: + labelPtr = "room"; + valuePtr = "bedroom 2"; + break; + case 1: + labelPtr = "orientation"; + valuePtr = "North"; + break; + case 2: + labelPtr = "floor"; + valuePtr = "2"; + break; + case 3: + labelPtr = "direction"; + valuePtr = "up"; + break; + default: + err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; + break; + } + + if (err == CHIP_NO_ERROR) + { + VerifyOrReturnError(std::strlen(labelPtr) <= kMaxLabelNameLength, false); + VerifyOrReturnError(std::strlen(valuePtr) <= kMaxLabelValueLength, false); + + Platform::CopyString(mFixedLabelNameBuf, kMaxLabelNameLength + 1, labelPtr); + Platform::CopyString(mFixedLabelValueBuf, kMaxLabelValueLength + 1, valuePtr); + + output.label = CharSpan::fromCharString(mFixedLabelNameBuf); + output.value = CharSpan::fromCharString(mFixedLabelValueBuf); + + mIndex++; + + retval = true; + } + else + { + retval = false; + } + + return retval; +} + +CHIP_ERROR DeviceInfoProviderImpl::SetUserLabelLength(EndpointId endpoint, size_t val) +{ + return mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::UserLabelLengthKey(endpoint).KeyName(), &val, + static_cast(sizeof(val))); +} + +CHIP_ERROR DeviceInfoProviderImpl::GetUserLabelLength(EndpointId endpoint, size_t & val) +{ + uint16_t len = static_cast(sizeof(val)); + + return mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::UserLabelLengthKey(endpoint).KeyName(), &val, len); +} + +CHIP_ERROR DeviceInfoProviderImpl::SetUserLabelAt(EndpointId endpoint, size_t index, const UserLabelType & userLabel) +{ + VerifyOrReturnError(CanCastTo(index), CHIP_ERROR_INVALID_ARGUMENT); + + uint8_t buf[UserLabelTLVMaxSize()]; + TLV::TLVWriter writer; + writer.Init(buf); + + TLV::TLVType outerType; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType)); + ReturnErrorOnFailure(writer.PutString(kLabelNameTag, userLabel.label)); + ReturnErrorOnFailure(writer.PutString(kLabelValueTag, userLabel.value)); + ReturnErrorOnFailure(writer.EndContainer(outerType)); + + return mStorage->SyncSetKeyValue( + DefaultStorageKeyAllocator::UserLabelIndexKey(endpoint, static_cast(index)).KeyName(), buf, + static_cast(writer.GetLengthWritten())); +} + +CHIP_ERROR DeviceInfoProviderImpl::DeleteUserLabelAt(EndpointId endpoint, size_t index) +{ + return mStorage->SyncDeleteKeyValue( + DefaultStorageKeyAllocator::UserLabelIndexKey(endpoint, static_cast(index)).KeyName()); +} + +DeviceInfoProvider::UserLabelIterator * DeviceInfoProviderImpl::IterateUserLabel(EndpointId endpoint) +{ + return chip::Platform::New(*this, endpoint); +} + +DeviceInfoProviderImpl::UserLabelIteratorImpl::UserLabelIteratorImpl(DeviceInfoProviderImpl & provider, EndpointId endpoint) : + mProvider(provider), mEndpoint(endpoint) +{ + size_t total = 0; + + ReturnOnFailure(mProvider.GetUserLabelLength(mEndpoint, total)); + mTotal = total; + mIndex = 0; +} + +bool DeviceInfoProviderImpl::UserLabelIteratorImpl::Next(UserLabelType & output) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrReturnError(mIndex < mTotal, false); + VerifyOrReturnError(CanCastTo(mIndex), false); + + uint8_t buf[UserLabelTLVMaxSize()]; + uint16_t len = static_cast(sizeof(buf)); + + err = mProvider.mStorage->SyncGetKeyValue( + DefaultStorageKeyAllocator::UserLabelIndexKey(mEndpoint, static_cast(mIndex)).KeyName(), buf, len); + VerifyOrReturnError(err == CHIP_NO_ERROR, false); + + TLV::ContiguousBufferTLVReader reader; + reader.Init(buf); + err = reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()); + VerifyOrReturnError(err == CHIP_NO_ERROR, false); + + TLV::TLVType containerType; + VerifyOrReturnError(reader.EnterContainer(containerType) == CHIP_NO_ERROR, false); + + chip::CharSpan label; + chip::CharSpan value; + + VerifyOrReturnError(reader.Next(kLabelNameTag) == CHIP_NO_ERROR, false); + VerifyOrReturnError(reader.Get(label) == CHIP_NO_ERROR, false); + + VerifyOrReturnError(reader.Next(kLabelValueTag) == CHIP_NO_ERROR, false); + VerifyOrReturnError(reader.Get(value) == CHIP_NO_ERROR, false); + + VerifyOrReturnError(reader.VerifyEndOfContainer() == CHIP_NO_ERROR, false); + VerifyOrReturnError(reader.ExitContainer(containerType) == CHIP_NO_ERROR, false); + + Platform::CopyString(mUserLabelNameBuf, label); + Platform::CopyString(mUserLabelValueBuf, value); + + output.label = CharSpan::fromCharString(mUserLabelNameBuf); + output.value = CharSpan::fromCharString(mUserLabelValueBuf); + + mIndex++; + + return true; +} + +DeviceInfoProvider::SupportedLocalesIterator * DeviceInfoProviderImpl::IterateSupportedLocales() +{ + return chip::Platform::New(); +} + +size_t DeviceInfoProviderImpl::SupportedLocalesIteratorImpl::Count() +{ + // Hardcoded list of locales + // {("en-US"), ("de-DE"), ("fr-FR"), ("en-GB"), ("es-ES"), ("zh-CN"), ("it-IT"), ("ja-JP")} + + return 8; +} + +bool DeviceInfoProviderImpl::SupportedLocalesIteratorImpl::Next(CharSpan & output) +{ + bool retval = true; + + // Hardcoded list of locales + CHIP_ERROR err = CHIP_NO_ERROR; + + const char * activeLocalePtr = nullptr; + + VerifyOrReturnError(mIndex < 8, false); + + switch (mIndex) + { + case 0: + activeLocalePtr = "en-US"; + break; + case 1: + activeLocalePtr = "de-DE"; + break; + case 2: + activeLocalePtr = "fr-FR"; + break; + case 3: + activeLocalePtr = "en-GB"; + break; + case 4: + activeLocalePtr = "es-ES"; + break; + case 5: + activeLocalePtr = "zh-CN"; + break; + case 6: + activeLocalePtr = "it-IT"; + break; + case 7: + activeLocalePtr = "ja-JP"; + break; + default: + err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; + break; + } + + if (err == CHIP_NO_ERROR) + { + VerifyOrReturnError(std::strlen(activeLocalePtr) <= kMaxActiveLocaleLength, false); + + Platform::CopyString(mActiveLocaleBuf, kMaxActiveLocaleLength + 1, activeLocalePtr); + + output = CharSpan::fromCharString(mActiveLocaleBuf); + + mIndex++; + + retval = true; + } + else + { + retval = false; + } + + return retval; +} + +DeviceInfoProvider::SupportedCalendarTypesIterator * DeviceInfoProviderImpl::IterateSupportedCalendarTypes() +{ + return chip::Platform::New(); +} + +size_t DeviceInfoProviderImpl::SupportedCalendarTypesIteratorImpl::Count() +{ + // Hardcoded list of strings + // {("kBuddhist"), ("kChinese"), ("kCoptic"), ("kEthiopian"), ("kGregorian"), ("kHebrew"), ("kIndian"), ("kJapanese"), + // ("kKorean"), ("kPersian"), ("kTaiwanese"), ("kIslamic")} + + return 12; +} + +bool DeviceInfoProviderImpl::SupportedCalendarTypesIteratorImpl::Next(CalendarType & output) +{ + bool retval = true; + + // Hardcoded list of Strings that are valid values for the Calendar Types. + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrReturnError(mIndex < 12, false); + + switch (mIndex) + { + case 0: + output = app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kBuddhist; + break; + case 1: + output = app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kChinese; + break; + case 2: + output = app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kCoptic; + break; + case 3: + output = app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kEthiopian; + break; + case 4: + output = app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kGregorian; + break; + case 5: + output = app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kHebrew; + break; + case 6: + output = app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kIndian; + break; + case 7: + output = app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kJapanese; + break; + case 8: + output = app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kKorean; + break; + case 9: + output = app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kPersian; + break; + case 10: + output = app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kTaiwanese; + break; + case 11: + output = app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kIslamic; + break; + default: + err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; + break; + } + + if (err == CHIP_NO_ERROR) + { + mIndex++; + retval = true; + } + else + { + retval = false; + } + + return retval; +} + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/stm32/DeviceInfoProviderImpl.h b/src/platform/stm32/DeviceInfoProviderImpl.h new file mode 100644 index 00000000000000..30e153e2815537 --- /dev/null +++ b/src/platform/stm32/DeviceInfoProviderImpl.h @@ -0,0 +1,107 @@ +/* + * + * Copyright (c) 2022 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. + */ +#pragma once + +#include +#include + +namespace chip { +namespace DeviceLayer { + +class DeviceInfoProviderImpl : public DeviceInfoProvider +{ +public: + DeviceInfoProviderImpl() = default; + ~DeviceInfoProviderImpl() override {} + + // Iterators + FixedLabelIterator * IterateFixedLabel(EndpointId endpoint) override; + UserLabelIterator * IterateUserLabel(EndpointId endpoint) override; + SupportedLocalesIterator * IterateSupportedLocales() override; + SupportedCalendarTypesIterator * IterateSupportedCalendarTypes() override; + + static DeviceInfoProviderImpl & GetDefaultInstance(); + +protected: + class FixedLabelIteratorImpl : public FixedLabelIterator + { + public: + FixedLabelIteratorImpl(EndpointId endpoint); + size_t Count() override; + bool Next(FixedLabelType & output) override; + void Release() override { chip::Platform::Delete(this); } + + private: + EndpointId mEndpoint = 0; + size_t mIndex = 0; + char mFixedLabelNameBuf[kMaxLabelNameLength + 1]; + char mFixedLabelValueBuf[kMaxLabelValueLength + 1]; + }; + + class UserLabelIteratorImpl : public UserLabelIterator + { + public: + UserLabelIteratorImpl(DeviceInfoProviderImpl & provider, EndpointId endpoint); + size_t Count() override { return mTotal; } + bool Next(UserLabelType & output) override; + void Release() override { chip::Platform::Delete(this); } + + private: + DeviceInfoProviderImpl & mProvider; + EndpointId mEndpoint = 0; + size_t mIndex = 0; + size_t mTotal = 0; + char mUserLabelNameBuf[kMaxLabelNameLength + 1]; + char mUserLabelValueBuf[kMaxLabelValueLength + 1]; + }; + + class SupportedLocalesIteratorImpl : public SupportedLocalesIterator + { + public: + SupportedLocalesIteratorImpl() = default; + size_t Count() override; + bool Next(CharSpan & output) override; + void Release() override { chip::Platform::Delete(this); } + + private: + size_t mIndex = 0; + char mActiveLocaleBuf[kMaxActiveLocaleLength + 1]; + }; + + class SupportedCalendarTypesIteratorImpl : public SupportedCalendarTypesIterator + { + public: + SupportedCalendarTypesIteratorImpl() = default; + size_t Count() override; + bool Next(CalendarType & output) override; + void Release() override { chip::Platform::Delete(this); } + + private: + size_t mIndex = 0; + }; + + CHIP_ERROR SetUserLabelLength(EndpointId endpoint, size_t val) override; + CHIP_ERROR GetUserLabelLength(EndpointId endpoint, size_t & val) override; + CHIP_ERROR SetUserLabelAt(EndpointId endpoint, size_t index, const UserLabelType & userLabel) override; + CHIP_ERROR DeleteUserLabelAt(EndpointId endpoint, size_t index) override; + +private: + static constexpr size_t UserLabelTLVMaxSize() { return TLV::EstimateStructOverhead(kMaxLabelNameLength, kMaxLabelValueLength); } +}; + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/stm32/DiagnosticDataProviderImpl.h b/src/platform/stm32/DiagnosticDataProviderImpl.h index 85ac3fc206f8ae..eb843ea9a3d140 100644 --- a/src/platform/stm32/DiagnosticDataProviderImpl.h +++ b/src/platform/stm32/DiagnosticDataProviderImpl.h @@ -30,7 +30,7 @@ namespace chip { namespace DeviceLayer { /** - * Concrete implementation of the PlatformManager singleton object for stm32 platforms. + * Concrete implementation of the PlatformManager singleton object for STM32 platforms. */ class DiagnosticDataProviderImpl : public DiagnosticDataProvider diff --git a/src/platform/stm32/FactoryDataProvider.cpp b/src/platform/stm32/FactoryDataProvider.cpp index fc8e072836fd41..8b3d0af05a6ec7 100644 --- a/src/platform/stm32/FactoryDataProvider.cpp +++ b/src/platform/stm32/FactoryDataProvider.cpp @@ -17,12 +17,21 @@ #include "FactoryDataProvider.h" +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) +#include +#endif + #include #include #include #include #include +#ifdef USE_STM32WBXX_DAC_CRYPTO +#include +#include +#endif + namespace chip { namespace {} // namespace @@ -33,6 +42,24 @@ CHIP_ERROR FactoryDataProvider::Init() return CHIP_NO_ERROR; } +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 1) +CHIP_ERROR FactoryDataProvider::MapSTM32WBError(FACTORYDATA_StatusTypeDef err) +{ + switch (err) + { + case DATAFACTORY_OK: + return CHIP_NO_ERROR; + case DATAFACTORY_BUFFER_TOO_SMALL: + return CHIP_ERROR_BUFFER_TOO_SMALL; + case DATAFACTORY_PARAM_ERROR: + return CHIP_ERROR_INVALID_ARGUMENT; + default: + break; + } + return CHIP_ERROR_INTERNAL; +} +#endif + FactoryDataProvider & FactoryDataProvider::GetDefaultInstance() { static FactoryDataProvider sInstance; @@ -61,96 +88,159 @@ CHIP_ERROR LoadKeypairFromRaw(ByteSpan private_key, ByteSpan public_key, Crypto: CHIP_ERROR FactoryDataProvider::SignWithDeviceAttestationKey(const ByteSpan & messageToSign, MutableByteSpan & outSignBuffer) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE Crypto::P256ECDSASignature signature; Crypto::P256Keypair keypair; - const uint8_t kDevelopmentDAC_PublicKey_FFF1_8004[65] = { - 0x04, 0x50, 0x41, 0x38, 0xef, 0x31, 0xc9, 0xdd, 0x16, 0x0e, 0xb4, 0x6c, 0x6c, 0x17, 0x11, 0x4f, 0x9d, - 0x72, 0x88, 0x40, 0x80, 0x1f, 0x73, 0xbb, 0x9b, 0x5a, 0x2c, 0x51, 0x91, 0xc9, 0xb2, 0x06, 0x63, 0x01, - 0x9d, 0x94, 0x76, 0xd1, 0x93, 0x1b, 0x93, 0xff, 0x47, 0xf4, 0x32, 0x56, 0x37, 0x90, 0x35, 0xd2, 0x29, - 0x62, 0x0b, 0x7e, 0x21, 0x0e, 0x59, 0x2f, 0x26, 0x43, 0x7d, 0x2d, 0x57, 0x62, 0x05, - }; - const uint8_t kDevelopmentDAC_PrivateKey_FFF1_8004[32] = { - 0x82, 0x0a, 0x24, 0x2a, 0x03, 0x0e, 0xbc, 0xe1, 0x1f, 0x38, 0x73, 0x5a, 0xcf, 0x1a, 0x6f, 0x37, - 0xc3, 0xad, 0xa6, 0xe4, 0x32, 0xd2, 0x47, 0x0a, 0x8a, 0x41, 0x37, 0x43, 0xf8, 0x95, 0x63, 0xf3, - }; - ByteSpan kDacPrivateKey = ByteSpan(kDevelopmentDAC_PrivateKey_FFF1_8004); - ByteSpan kDacPublicKey = ByteSpan(kDevelopmentDAC_PublicKey_FFF1_8004); +#ifdef USE_STM32WBXX_DAC_CRYPTO + otError otCksDAC_error; + uint8_t signature_bytes[Crypto::kP256_ECDSA_Signature_Length_Raw]; +#endif VerifyOrReturnError(!outSignBuffer.empty(), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(!messageToSign.empty(), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(outSignBuffer.size() >= signature.Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL); +#ifdef USE_STM32WBXX_DAC_CRYPTO + + otCksDAC_error = + otCksDacSignature(messageToSign.data(), messageToSign.size(), DevelopmentCerts::kDacPublicKey.data(), &signature_bytes[0]); + memcpy(signature.Bytes(), &signature_bytes[0], Crypto::kP256_ECDSA_Signature_Length_Raw); + signature.SetLength(Crypto::kP256_ECDSA_Signature_Length_Raw); + + if (otCksDAC_error != OT_ERROR_NONE) + { + ChipLogProgress(Crypto, "DAC signature unexpected failure"); + return CHIP_ERROR_INTERNAL; + } + +#else // In a non-exemplary implementation, the public key is not needed here. It is used here merely because // Crypto::P256Keypair is only (currently) constructable from raw keys if both private/public keys are present. - ReturnErrorOnFailure(LoadKeypairFromRaw(kDacPrivateKey, kDacPublicKey, keypair)); - ReturnErrorOnFailure(keypair.ECDSA_sign_msg(messageToSign.data(), messageToSign.size(), signature)); +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) + ReturnErrorOnFailure(LoadKeypairFromRaw(DevelopmentCerts::kDacPrivateKey, DevelopmentCerts::kDacPublicKey, keypair)); - return CopySpanToMutableSpan(ByteSpan{ signature.ConstBytes(), signature.Length() }, outSignBuffer); #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + uint8_t Privatekeybuffer[PRIVATE_KEY_LEN]; + uint8_t Publickeybuffer[PUBLIC_KEY_LEN]; + uint32_t tlvDataLength; // Dummy value keys values are fix + FACTORYDATA_StatusTypeDef err; + + err = FACTORYDATA_GetValue(TAG_ID_DEVICE_ATTESTATION_PRIVATE_KEY, Privatekeybuffer, PRIVATE_KEY_LEN, &tlvDataLength); + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + err = FACTORYDATA_GetValue(TAG_ID_DEVICE_ATTESTATION_PUBLIC_KEY, Publickeybuffer, PUBLIC_KEY_LEN, &tlvDataLength); + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + ReturnErrorOnFailure(LoadKeypairFromRaw(ByteSpan(Privatekeybuffer), ByteSpan(Publickeybuffer), keypair)); +#endif + + ReturnErrorOnFailure(keypair.ECDSA_sign_msg(messageToSign.data(), messageToSign.size(), signature)); #endif + + return CopySpanToMutableSpan(ByteSpan{ signature.ConstBytes(), signature.Length() }, outSignBuffer); } CHIP_ERROR FactoryDataProvider::GetSetupDiscriminator(uint16_t & setupDiscriminator) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) setupDiscriminator = CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR; return CHIP_NO_ERROR; #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_SETUP_DISCRIMINATOR, (uint8_t *) &setupDiscriminator, sizeof(setupDiscriminator), + &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + return CHIP_NO_ERROR; #endif } CHIP_ERROR FactoryDataProvider::GetSpake2pIterationCount(uint32_t & iterationCount) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) constexpr uint32_t kDefaultTestVerifierIterationCount = 1000; iterationCount = kDefaultTestVerifierIterationCount; return CHIP_NO_ERROR; #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_SPAKE2_ITERATION_COUNT, (uint8_t *) &iterationCount, sizeof(iterationCount), &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + return CHIP_NO_ERROR; #endif } CHIP_ERROR FactoryDataProvider::GetSetupPasscode(uint32_t & setupPasscode) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) setupPasscode = CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE; return CHIP_NO_ERROR; #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_SPAKE2_SETUP_PASSCODE, (uint8_t *) &setupPasscode, sizeof(setupPasscode), &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + return CHIP_NO_ERROR; #endif } CHIP_ERROR FactoryDataProvider::GetVendorId(uint16_t & vendorId) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) vendorId = CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID; return CHIP_NO_ERROR; #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_VENDOR_ID, (uint8_t *) &vendorId, sizeof(vendorId), &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + return CHIP_NO_ERROR; #endif } CHIP_ERROR FactoryDataProvider::GetProductId(uint16_t & productId) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) productId = CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID; return CHIP_NO_ERROR; #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_PRODUCT_ID, (uint8_t *) &productId, sizeof(productId), &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + return CHIP_NO_ERROR; #endif } CHIP_ERROR FactoryDataProvider::GetHardwareVersion(uint16_t & hardwareVersion) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) hardwareVersion = CHIP_DEVICE_CONFIG_DEVICE_HARDWARE_VERSION; return CHIP_NO_ERROR; #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_HARDWARE_VERSION, (uint8_t *) &hardwareVersion, sizeof(hardwareVersion), &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + return CHIP_NO_ERROR; #endif } @@ -176,32 +266,68 @@ CHIP_ERROR FactoryDataProvider::GetProductLabel(char * buf, size_t bufSize) CHIP_ERROR FactoryDataProvider::GetVendorName(char * buf, size_t bufSize) { - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) + ReturnErrorCodeIf(bufSize < sizeof(CHIP_DEVICE_CONFIG_DEVICE_VENDOR_NAME), CHIP_ERROR_BUFFER_TOO_SMALL); + + memcpy(buf, CHIP_DEVICE_CONFIG_DEVICE_VENDOR_NAME, sizeof(CHIP_DEVICE_CONFIG_DEVICE_VENDOR_NAME)); +#else + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_VENDOR_NAME, (uint8_t *) buf, bufSize, &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + +#endif + return CHIP_NO_ERROR; } CHIP_ERROR FactoryDataProvider::GetProductName(char * buf, size_t bufSize) { - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) + ReturnErrorCodeIf(bufSize < sizeof(CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_NAME), CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(buf, CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_NAME, sizeof(CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_NAME)); +#else + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_PRODUCT_NAME, (uint8_t *) buf, bufSize, &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); +#endif + return CHIP_NO_ERROR; } CHIP_ERROR FactoryDataProvider::GetSerialNumber(char * buf, size_t bufSize) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) memcpy(buf, CHIP_DEVICE_CONFIG_TEST_SERIAL_NUMBER, bufSize); return CHIP_NO_ERROR; #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_SERIAL_NUMBER, (uint8_t *) buf, bufSize, &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + return CHIP_NO_ERROR; #endif } CHIP_ERROR FactoryDataProvider::GetHardwareVersionString(char * buf, size_t bufSize) { - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + ReturnErrorCodeIf(bufSize < sizeof(CHIP_DEVICE_CONFIG_DEFAULT_DEVICE_HARDWARE_VERSION_STRING), CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(buf, CHIP_DEVICE_CONFIG_DEFAULT_DEVICE_HARDWARE_VERSION_STRING, + sizeof(CHIP_DEVICE_CONFIG_DEFAULT_DEVICE_HARDWARE_VERSION_STRING)); + return CHIP_NO_ERROR; } CHIP_ERROR FactoryDataProvider::GetSpake2pVerifier(MutableByteSpan & verifierSpan, size_t & verifierLen) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) static const uint8_t kDefaultTestVerifier[97] = { 0xb9, 0x61, 0x70, 0xaa, 0xe8, 0x03, 0x34, 0x68, 0x84, 0x72, 0x4f, 0xe9, 0xa3, 0xb2, 0x87, 0xc3, 0x03, 0x30, 0xc2, 0xa6, 0x60, 0x37, 0x5d, 0x17, 0xbb, 0x20, 0x5a, 0x8c, 0xf1, 0xae, 0xcb, 0x35, 0x04, 0x57, 0xf8, 0xab, 0x79, 0xee, 0x25, 0x3a, @@ -217,16 +343,24 @@ CHIP_ERROR FactoryDataProvider::GetSpake2pVerifier(MutableByteSpan & verifierSpa } memcpy(verifierSpan.data(), &kDefaultTestVerifier[0], verifierLen); verifierSpan.reduce_size(verifierLen); - return CHIP_NO_ERROR; #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_SPAKE2_VERIFIER, verifierSpan.data(), verifierSpan.size(), &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + verifierSpan.reduce_size(tlvDataLength); + verifierLen = tlvDataLength; #endif + return CHIP_NO_ERROR; } CHIP_ERROR FactoryDataProvider::GetCertificationDeclaration(MutableByteSpan & outBufferSpan) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) //-> format_version = 1 //-> vendor_id = 0xFFF1 //-> product_id_array = [ 0x8000, 0x8001, 0x8002, 0x8003, 0x8004, 0x8005, 0x8006, 0x8007, 0x8008, 0x8009, 0x800A, 0x800B, @@ -276,7 +410,15 @@ CHIP_ERROR FactoryDataProvider::GetCertificationDeclaration(MutableByteSpan & ou return CopySpanToMutableSpan(ByteSpan{ kCdForAllExamples }, outBufferSpan); #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_CERTIFICATION_DECLARATION, outBufferSpan.data(), outBufferSpan.size(), &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + outBufferSpan.reduce_size(tlvDataLength); + return CHIP_NO_ERROR; #endif } @@ -287,7 +429,7 @@ CHIP_ERROR FactoryDataProvider::GetFirmwareInformation(MutableByteSpan & firmwar CHIP_ERROR FactoryDataProvider::GetDeviceAttestationCert(MutableByteSpan & attestationCertSpan) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) static const uint8_t kDevelopmentDAC_Cert_FFF1_8004[493] = { 0x30, 0x82, 0x01, 0xe9, 0x30, 0x82, 0x01, 0x8e, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x1e, 0x06, 0x7f, 0x3b, 0xfe, 0xcd, 0xd8, 0x13, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x3d, 0x31, 0x25, 0x30, @@ -318,13 +460,23 @@ CHIP_ERROR FactoryDataProvider::GetDeviceAttestationCert(MutableByteSpan & attes return CopySpanToMutableSpan(ByteSpan(kDevelopmentDAC_Cert_FFF1_8004), attestationCertSpan); #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_DEVICE_ATTESTATION_CERTIFICATE, attestationCertSpan.data(), attestationCertSpan.size(), + &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + attestationCertSpan.reduce_size(tlvDataLength); + + return CHIP_NO_ERROR; #endif } CHIP_ERROR FactoryDataProvider::GetProductAttestationIntermediateCert(MutableByteSpan & intermediateCertSpan) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) static const uint8_t kDevelopmentPAI_Cert_FFF1[463] = { 0x30, 0x82, 0x01, 0xcb, 0x30, 0x82, 0x01, 0x71, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x56, 0xad, 0x82, 0x22, 0xad, 0x94, 0x5b, 0x64, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x30, 0x31, 0x18, 0x30, @@ -354,13 +506,23 @@ CHIP_ERROR FactoryDataProvider::GetProductAttestationIntermediateCert(MutableByt return CopySpanToMutableSpan(ByteSpan(kDevelopmentPAI_Cert_FFF1), intermediateCertSpan); #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_PRODUCT_ATTESTATION_INTERMEDIATE_CERTIFICATE, intermediateCertSpan.data(), + intermediateCertSpan.size(), &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + intermediateCertSpan.reduce_size(tlvDataLength); + + return CHIP_NO_ERROR; #endif } CHIP_ERROR FactoryDataProvider::GetSpake2pSalt(MutableByteSpan & saltSpan) { -#if !CONFIG_STM32_FACTORY_DATA_ENABLE +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 0) static const uint8_t kDefaultTestVerifierSalt[16] = { 0x53, 0x50, 0x41, 0x4b, 0x45, 0x32, 0x50, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x53, 0x61, 0x6c, 0x74, }; @@ -374,7 +536,16 @@ CHIP_ERROR FactoryDataProvider::GetSpake2pSalt(MutableByteSpan & saltSpan) saltSpan.reduce_size(saltLen); return CHIP_NO_ERROR; #else - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + FACTORYDATA_StatusTypeDef err; + uint32_t tlvDataLength; + + err = FACTORYDATA_GetValue(TAG_ID_SPAKE2_SALT, saltSpan.data(), saltSpan.size(), &tlvDataLength); + + VerifyOrReturnError(DATAFACTORY_OK == err, MapSTM32WBError(err)); + + saltSpan.reduce_size(tlvDataLength); + + return CHIP_NO_ERROR; #endif } diff --git a/src/platform/stm32/FactoryDataProvider.h b/src/platform/stm32/FactoryDataProvider.h index 2882b989e8902d..e9522734dca9d2 100644 --- a/src/platform/stm32/FactoryDataProvider.h +++ b/src/platform/stm32/FactoryDataProvider.h @@ -17,6 +17,10 @@ #pragma once +#include "app_conf.h" +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 1) +#include "stm32_factorydata.h" +#endif #include #include #include @@ -63,6 +67,11 @@ class FactoryDataProvider : public chip::Credentials::DeviceAttestationCredentia // ===== Members functions that are platform-specific CHIP_ERROR GetEnableKey(MutableByteSpan & enableKey); + +protected: +#if (CONFIG_STM32_FACTORY_DATA_ENABLE == 1) + static CHIP_ERROR MapSTM32WBError(FACTORYDATA_StatusTypeDef err); +#endif }; } // namespace DeviceLayer diff --git a/src/platform/stm32/KeyValueStoreManagerImpl.cpp b/src/platform/stm32/KeyValueStoreManagerImpl.cpp index b68ea2c1c9adc9..723b97318eba49 100644 --- a/src/platform/stm32/KeyValueStoreManagerImpl.cpp +++ b/src/platform/stm32/KeyValueStoreManagerImpl.cpp @@ -23,7 +23,6 @@ #include #include -#define MATTER_KEY_NAME_MAX_LENGTH (15 * 2) // ADD Max key name string size is 30 "keyType...;KeyName..." namespace chip { namespace DeviceLayer { namespace PersistedStorage { diff --git a/src/platform/stm32/KeyValueStoreManagerImpl.h b/src/platform/stm32/KeyValueStoreManagerImpl.h index 335ffb2b907df9..614bae7da594d2 100644 --- a/src/platform/stm32/KeyValueStoreManagerImpl.h +++ b/src/platform/stm32/KeyValueStoreManagerImpl.h @@ -16,8 +16,8 @@ * limitations under the License. */ -#ifndef MIDDLEWARES_MATTER_PLATFORM_STM32WB_KEYVALUESTOREMANAGERIMPL_H_ -#define MIDDLEWARES_MATTER_PLATFORM_STM32WB_KEYVALUESTOREMANAGERIMPL_H_ +#ifndef MIDDLEWARES_MATTER_PLATFORM_STM32_KEYVALUESTOREMANAGERIMPL_H_ +#define MIDDLEWARES_MATTER_PLATFORM_STM32_KEYVALUESTOREMANAGERIMPL_H_ #pragma once @@ -79,4 +79,4 @@ inline KeyValueStoreManagerImpl & KeyValueStoreMgrImpl(void) } // namespace DeviceLayer } // namespace chip -#endif /* MIDDLEWARES_MATTER_PLATFORM_STM32WB_KEYVALUESTOREMANAGERIMPL_H_ */ +#endif /* MIDDLEWARES_MATTER_PLATFORM_STM32_KEYVALUESTOREMANAGERIMPL_H_ */ diff --git a/src/platform/stm32/OTAImageProcessorImpl.cpp b/src/platform/stm32/OTAImageProcessorImpl.cpp new file mode 100644 index 00000000000000..76363befebb005 --- /dev/null +++ b/src/platform/stm32/OTAImageProcessorImpl.cpp @@ -0,0 +1,439 @@ +/* + * + * Copyright (c) 2021 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 "app_common.h" +#if (OTA_SUPPORT == 1) +#include +#include + +#include "OTAImageProcessorImpl.h" +#include "ota.h" +#include "sfu_fwimg_regions.h" +#include "stm_ext_flash.h" +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) +#include "mapping_fwimg.h" +#include "mapping_sbsfu.h" +#elif defined(__ICCARM__) || defined(__GNUC__) +#include "mapping_export.h" +#endif /* __CC_ARM || __ARMCC_VERSION */ +#include "sfu_standalone_loader.h" + +#define STM_HEADER_SIZE 8 +static uint32_t mCPU1Size; +static uint32_t mCPU2Size; +static uint32_t mDownloadedBytesCPU2; +static uint8_t mSwitchDwlSlot; + +namespace chip { + +bool OTAImageProcessorImpl::IsFirstImageRun() +{ + OTARequestorInterface * requestor = chip::GetRequestorInstance(); + if (requestor == nullptr) + { + return false; + } + + return requestor->GetCurrentUpdateState() == OTARequestorInterface::OTAUpdateStateEnum::kApplying; +} + +CHIP_ERROR OTAImageProcessorImpl::ConfirmCurrentImage() +{ + ChipLogProgress(DeviceLayer, "OTA Confirm current image"); + OTARequestorInterface * requestor = chip::GetRequestorInstance(); + if (requestor == nullptr) + { + return CHIP_ERROR_INTERNAL; + } + + uint32_t currentVersion; + uint32_t targetVersion = requestor->GetTargetVersion(); + ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().GetSoftwareVersion(currentVersion)); + + if (currentVersion != targetVersion) + { + ChipLogError(SoftwareUpdate, "Current software version = %" PRIu32 ", expected software version = %" PRIu32, currentVersion, + targetVersion); + return CHIP_ERROR_INCORRECT_STATE; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::PrepareDownload() +{ + + // Get OTA status - under what circumstances does prepared break? + // what happens if a prepare is pending and another one is invoked + // Should we store the state here and wait until we receive notification + + mHeaderParser.Init(); + + DeviceLayer::PlatformMgr().ScheduleWork(HandlePrepareDownload, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::ProcessHeader(ByteSpan & block) +{ + ChipLogProgress(DeviceLayer, "OTA Process Header"); + if (mHeaderParser.IsInitialized()) + { + OTAImageHeader header; + CHIP_ERROR error = mHeaderParser.AccumulateAndDecode(block, header); + + // Needs more data to decode the header + ReturnErrorCodeIf(error == CHIP_ERROR_BUFFER_TOO_SMALL, CHIP_NO_ERROR); + ReturnErrorOnFailure(error); + + mParams.totalFileBytes = header.mPayloadSize; + mHeaderParser.Clear(); + + // Load Ota_ImageHeader_t header structure and call application callback to validate image header + Ota_ImageHeader_t OtaImgHeader; + this->mSwVer = header.mSoftwareVersion; // Store software version in imageProcessor as well + OtaImgHeader.vendorId = header.mVendorId; + OtaImgHeader.productId = header.mProductId; + OtaImgHeader.softwareVersion = header.mSoftwareVersion; + OtaImgHeader.minApplicableVersion = header.mMinApplicableVersion.ValueOr(0); + OtaImgHeader.maxApplicableVersion = header.mMaxApplicableVersion.ValueOr(0); + + if (true != OtaHeaderValidation(OtaImgHeader)) + { + return CHIP_ERROR_INCORRECT_STATE; + } + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::Finalize() +{ + DeviceLayer::PlatformMgr().ScheduleWork(HandleFinalize, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::Apply() +{ + ChipLogProgress(SoftwareUpdate, "Applying - resetting device"); + + if (mCPU2Size == 0) + { + STANDALONE_LOADER_STATE = STANDALONE_LOADER_INSTALL_REQ; // install only CPU1 + } + else if (mCPU1Size == 0) + { + STANDALONE_LOADER_STATE = STANDALONE_LOADER_BYPASS_REQ; // install only CPU2 + } + else + { + STANDALONE_LOADER_STATE = STANDALONE_LOADER_BYPASS_REQ_AND_INSTALL_REQ; // install CPU1 and CPU2 + } + NVIC_SystemReset(); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::Abort() +{ + DeviceLayer::PlatformMgr().ScheduleWork(HandleAbort, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & block) +{ + if ((block.data() == nullptr) || block.empty()) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + CHIP_ERROR err = ProcessHeader(block); + ChipLogProgress(DeviceLayer, "OTA Process Block"); + if (err != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Matter image header parser error %s", chip::ErrorStr(err)); + this->mDownloader->EndDownload(CHIP_ERROR_INVALID_FILE_IDENTIFIER); + return err; + } + + // Store block data for HandleProcessBlock to access + err = SetBlock(block); + if (err != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Cannot set block data: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + + DeviceLayer::PlatformMgr().ScheduleWork(HandleProcessBlock, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context) +{ + auto * imageProcessor = reinterpret_cast(context); + ChipLogProgress(DeviceLayer, "OTA Prepare DL"); + if (imageProcessor == nullptr) + { + ChipLogError(SoftwareUpdate, "ImageProcessor context is null"); + return; + } + else if (imageProcessor->mDownloader == nullptr) + { + ChipLogError(SoftwareUpdate, "mDownloader is null"); + return; + } + + // running this in a thread so won't block main event loop + ChipLogProgress(SoftwareUpdate, "HandlePrepareDownload"); + + mSwitchDwlSlot = false; // start with dwl_slot1 for CPU1 + + STM_EXT_FLASH_Delete_Image(EXTERNAL_FLASH_ADDRESS + SLOT_DWL_1_START, SLOT_SIZE(SLOT_DWL_1)); + STM_EXT_FLASH_Delete_Image(EXTERNAL_FLASH_ADDRESS + SLOT_DWL_4_START, SLOT_SIZE(SLOT_DWL_4)); + + // Initialize tracking variables + imageProcessor->mParams.downloadedBytes = 0; + mDownloadedBytesCPU2 = 0; + imageProcessor->mDownloader->OnPreparedForDownload(CHIP_NO_ERROR); +} + +void OTAImageProcessorImpl::HandleFinalize(intptr_t context) +{ + auto * imageProcessor = reinterpret_cast(context); + if (imageProcessor == nullptr) + { + return; + } + + ChipLogProgress(SoftwareUpdate, "HandleFinalize"); + + imageProcessor->ReleaseBlock(); + // Start from scratch + imageProcessor->mParams.downloadedBytes = 0; +} + +void OTAImageProcessorImpl::HandleAbort(intptr_t context) +{ + auto * imageProcessor = reinterpret_cast(context); + if (imageProcessor == nullptr) + { + return; + } + + ChipLogProgress(SoftwareUpdate, "HandleAbort"); + + STM_EXT_FLASH_Delete_Image(EXTERNAL_FLASH_ADDRESS + SLOT_DWL_1_START, SLOT_SIZE(SLOT_DWL_1)); + STM_EXT_FLASH_Delete_Image(EXTERNAL_FLASH_ADDRESS + SLOT_DWL_4_START, SLOT_SIZE(SLOT_DWL_4)); + imageProcessor->ReleaseBlock(); + // Start from scratch + imageProcessor->mParams.downloadedBytes = 0; + mDownloadedBytesCPU2 = 0; + mSwitchDwlSlot = false; // start with dwl_slot1 for CPU1 +} + +void OTAImageProcessorImpl::HandleProcessBlock(intptr_t context) +{ + STM_OTA_StatusTypeDef status; + auto * imageProcessor = reinterpret_cast(context); + + if (imageProcessor == nullptr) + { + ChipLogError(SoftwareUpdate, "ImageProcessor context is null"); + return; + } + else if (imageProcessor->mDownloader == nullptr) + { + ChipLogError(SoftwareUpdate, "mDownloader is null"); + return; + } + if (mSwitchDwlSlot == false) + { // CPU1 write in DWL_SLOT 1 + if (imageProcessor->mParams.downloadedBytes == 0) + { // get STM_Header + uint8_t STMHeader[STM_HEADER_SIZE]; + + memcpy(STMHeader, reinterpret_cast(imageProcessor->mBlock.data()), sizeof(STMHeader)); + + // retrieve the cpu1/2 size with STM header + mCPU1Size = STMHeader[0] + ((STMHeader[1]) << 8) + ((STMHeader[2]) << 16) + ((STMHeader[3]) << 24); + mCPU2Size = STMHeader[4] + ((STMHeader[5]) << 8) + ((STMHeader[6]) << 16) + ((STMHeader[7]) << 24); + + // check the header + if ((mCPU1Size > SLOT_SIZE(SLOT_DWL_1)) || (mCPU2Size > SLOT_SIZE(SLOT_DWL_4)) || (mCPU2Size + mCPU1Size == 0)) + { + ChipLogError(SoftwareUpdate, "Flash decode failed"); + imageProcessor->mDownloader->EndDownload(CHIP_ERROR_DECODE_FAILED); + return; + } + + if (mCPU1Size == 0) + { // update only CPU2 + // write in DWL_SLOT1 data without STM header + status = STM_EXT_FLASH_WriteChunk(imageProcessor->mParams.downloadedBytes + SLOT_DWL_4_START, + reinterpret_cast(imageProcessor->mBlock.data()) + STM_HEADER_SIZE, + static_cast(imageProcessor->mBlock.size()) - STM_HEADER_SIZE); + if (status != STM_EXT_FLASH_OK) + { + ChipLogError(SoftwareUpdate, "Flash write failed"); + imageProcessor->mDownloader->EndDownload(CHIP_ERROR_WRITE_FAILED); + return; + } + + imageProcessor->mParams.downloadedBytes += imageProcessor->mBlock.size() - STM_HEADER_SIZE; + mSwitchDwlSlot = true; // switch to DWL_SLOT 4 for all the next data + mDownloadedBytesCPU2 = imageProcessor->mBlock.size() - STM_HEADER_SIZE; // update the bytes write in Dwl_slot4 + } + else + { + + // write in DWL_SLOT1 data without STM header + status = STM_EXT_FLASH_WriteChunk(imageProcessor->mParams.downloadedBytes + SLOT_DWL_1_START, + reinterpret_cast(imageProcessor->mBlock.data()) + STM_HEADER_SIZE, + static_cast(imageProcessor->mBlock.size()) - STM_HEADER_SIZE); + if (status != STM_EXT_FLASH_OK) + { + ChipLogError(SoftwareUpdate, "Flash write failed"); + imageProcessor->mDownloader->EndDownload(CHIP_ERROR_WRITE_FAILED); + return; + } + + imageProcessor->mParams.downloadedBytes += imageProcessor->mBlock.size() - STM_HEADER_SIZE; + } + } + else if ((imageProcessor->mParams.downloadedBytes + static_cast(imageProcessor->mBlock.size())) >= mCPU1Size) + { + + // split the block in 2 for Dwl_slot1 and Dwl_slot4 + uint32_t CPU2BlockStart = + imageProcessor->mParams.downloadedBytes + static_cast(imageProcessor->mBlock.size()) - mCPU1Size; + uint32_t CPU1BlockEnd = static_cast(imageProcessor->mBlock.size()) - CPU2BlockStart; + + if (CPU2BlockStart + CPU1BlockEnd == static_cast(imageProcessor->mBlock.size())) + { + mDownloadedBytesCPU2 = 0; + + // CPU1 write in DWL_SLOT 1 + status = STM_EXT_FLASH_WriteChunk(imageProcessor->mParams.downloadedBytes + SLOT_DWL_1_START, + reinterpret_cast(imageProcessor->mBlock.data()), CPU1BlockEnd); + if (status != STM_EXT_FLASH_OK) + { + ChipLogError(SoftwareUpdate, "Flash write failed"); + imageProcessor->mDownloader->EndDownload(CHIP_ERROR_WRITE_FAILED); + return; + } + + if (mCPU2Size != 0) + { + // CPU2 write in DWL_SLOT 4 + status = STM_EXT_FLASH_WriteChunk( + mDownloadedBytesCPU2 + SLOT_DWL_4_START, + reinterpret_cast(imageProcessor->mBlock.data()) + CPU1BlockEnd, CPU2BlockStart); + if (status != STM_EXT_FLASH_OK) + { + ChipLogError(SoftwareUpdate, "Flash write failed"); + imageProcessor->mDownloader->EndDownload(CHIP_ERROR_WRITE_FAILED); + return; + } + + mSwitchDwlSlot = true; // switch to DWL_SLOT 4 for all the next data + imageProcessor->mParams.downloadedBytes += imageProcessor->mBlock.size(); // keep track of all bytes dwl + mDownloadedBytesCPU2 = CPU2BlockStart; // update the bytes write in Dwl_slot4 + } + } + else + { + ChipLogError(SoftwareUpdate, "Flash decode failed"); + imageProcessor->mDownloader->EndDownload(CHIP_ERROR_DECODE_FAILED); + return; + } + } + else + { // CPU1 write in DWL_SLOT 1 + status = STM_EXT_FLASH_WriteChunk(imageProcessor->mParams.downloadedBytes + SLOT_DWL_1_START, + reinterpret_cast(imageProcessor->mBlock.data()), + static_cast(imageProcessor->mBlock.size())); + if (status != STM_EXT_FLASH_OK) + { + ChipLogError(SoftwareUpdate, "Flash write failed"); + imageProcessor->mDownloader->EndDownload(CHIP_ERROR_WRITE_FAILED); + return; + } + imageProcessor->mParams.downloadedBytes += imageProcessor->mBlock.size(); + } + } + else + { // CPU2 write in DWL_SLOT 4 + status = STM_EXT_FLASH_WriteChunk(mDownloadedBytesCPU2 + SLOT_DWL_4_START, + reinterpret_cast(imageProcessor->mBlock.data()), + static_cast(imageProcessor->mBlock.size())); + + if (status != STM_EXT_FLASH_OK) + { + ChipLogError(SoftwareUpdate, "Flash write failed"); + imageProcessor->mDownloader->EndDownload(CHIP_ERROR_WRITE_FAILED); + return; + } + + imageProcessor->mParams.downloadedBytes += imageProcessor->mBlock.size(); // keep track of all bytes dwl + mDownloadedBytesCPU2 += imageProcessor->mBlock.size(); // update the bytes write in Dwl_slot4 + } + + imageProcessor->mDownloader->FetchNextData(); +} + +CHIP_ERROR OTAImageProcessorImpl::SetBlock(ByteSpan & block) +{ + if (!IsSpanUsable(block)) + { + ReleaseBlock(); + return CHIP_NO_ERROR; + } + if (mBlock.size() < block.size()) + { + if (!mBlock.empty()) + { + ReleaseBlock(); + } + uint8_t * mBlock_ptr = static_cast(chip::Platform::MemoryAlloc(block.size())); + if (mBlock_ptr == nullptr) + { + return CHIP_ERROR_NO_MEMORY; + } + mBlock = MutableByteSpan(mBlock_ptr, block.size()); + } + CHIP_ERROR err = CopySpanToMutableSpan(block, mBlock); + if (err != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Cannot copy block data: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::ReleaseBlock() +{ + if (mBlock.data() != nullptr) + { + chip::Platform::MemoryFree(mBlock.data()); + } + + mBlock = MutableByteSpan(); + return CHIP_NO_ERROR; +} + +} // namespace chip +#endif diff --git a/src/platform/stm32/OTAImageProcessorImpl.h b/src/platform/stm32/OTAImageProcessorImpl.h new file mode 100644 index 00000000000000..493aa23842e816 --- /dev/null +++ b/src/platform/stm32/OTAImageProcessorImpl.h @@ -0,0 +1,68 @@ +/* + * + * Copyright (c) 2021 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 + +namespace chip { + +class OTAImageProcessorImpl : public OTAImageProcessorInterface +{ +public: + //////////// OTAImageProcessorInterface Implementation /////////////// + CHIP_ERROR PrepareDownload() override; + CHIP_ERROR ProcessHeader(ByteSpan & block); + CHIP_ERROR Finalize() override; + CHIP_ERROR Apply() override; + CHIP_ERROR Abort() override; + CHIP_ERROR ProcessBlock(ByteSpan & block) override; + bool IsFirstImageRun() override; + CHIP_ERROR ConfirmCurrentImage() override; + + void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; } + +private: + //////////// Actual handlers for the OTAImageProcessorInterface /////////////// + static void HandlePrepareDownload(intptr_t context); + static void HandleFinalize(intptr_t context); + static void HandleAbort(intptr_t context); + static void HandleProcessBlock(intptr_t context); + + /** + * Called to allocate memory for mBlock if necessary and set it to block + */ + CHIP_ERROR SetBlock(ByteSpan & block); + + /** + * Called to release allocated memory for mBlock + */ + CHIP_ERROR ReleaseBlock(); + + std::uint32_t mSwVer; + std::uint32_t mHwVer; + + MutableByteSpan mBlock; + OTADownloader * mDownloader = nullptr; + OTAImageHeaderParser mHeaderParser; +}; + +} // namespace chip diff --git a/src/platform/stm32/PlatformManagerImpl.cpp b/src/platform/stm32/PlatformManagerImpl.cpp index 60148b07f6e4ca..4c8d25dcd5cb60 100644 --- a/src/platform/stm32/PlatformManagerImpl.cpp +++ b/src/platform/stm32/PlatformManagerImpl.cpp @@ -34,6 +34,7 @@ namespace chip { namespace DeviceLayer { PlatformManagerImpl PlatformManagerImpl::sInstance; + extern "C" int mbedtls_hardware_poll(void * data, unsigned char * output, size_t len, size_t * olen); CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) diff --git a/src/platform/stm32/PlatformManagerImpl.h b/src/platform/stm32/PlatformManagerImpl.h index 8de9bfa4a24f48..0ed24ce93f9d9e 100644 --- a/src/platform/stm32/PlatformManagerImpl.h +++ b/src/platform/stm32/PlatformManagerImpl.h @@ -31,7 +31,7 @@ namespace chip { namespace DeviceLayer { /** - * Concrete implementation of the PlatformManager singleton object for the stm32 platform. + * Concrete implementation of the PlatformManager singleton object for the STM32 platform. */ class PlatformManagerImpl final : public PlatformManager, public Internal::GenericPlatformManagerImpl_FreeRTOS { @@ -49,7 +49,6 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener // ===== Platform-specific members that may be accessed directly by the application. CHIP_ERROR InitLwIPCoreLock(void); - // static void HandleESPSystemEvent(void * arg, esp_event_base_t eventBase, int32_t eventId, void * eventData); private: // ===== Methods that implement the PlatformManager abstract interface. @@ -90,7 +89,7 @@ inline PlatformManager & PlatformMgr(void) * Returns the platform-specific implementation of the PlatformManager singleton object. * * Chip applications can use this to gain access to features of the PlatformManager - * that are specific to the stm32 platform. + * that are specific to the STM32 platform. */ inline PlatformManagerImpl & PlatformMgrImpl(void) { diff --git a/src/platform/stm32/STM32Config.cpp b/src/platform/stm32/STM32Config.cpp index cf1a6bdc652e19..6b1134dcf0fdf2 100644 --- a/src/platform/stm32/STM32Config.cpp +++ b/src/platform/stm32/STM32Config.cpp @@ -29,42 +29,61 @@ CHIP_ERROR STM32Config::Init() return CHIP_NO_ERROR; } -template -CHIP_ERROR STM32Config::ReadConfigValue(Key key, T & val) +CHIP_ERROR STM32Config::ReadConfigValueBin(Key key, uint8_t * buf, size_t bufSize, size_t & outLen) { - uint8_t * buffer_key[35] = { 0 }; - size_t * read_by_size = NULL; + + uint8_t buffer_key[35] = { 0 }; sprintf((char *) buffer_key, "Config%i", key); - NM_GetKeyValue((void *) &val, (char *) buffer_key, sizeof(val), read_by_size, SECTOR_NO_SECURE); + return PrintError(NM_GetKeyValue(buf, (char *) buffer_key, bufSize, &outLen, SECTOR_SECURE)); +} - return CHIP_NO_ERROR; +CHIP_ERROR STM32Config::ReadConfigValue(Key key, bool & val) +{ + uint8_t buffer_key[35] = { 0 }; + size_t Out_Length; + + sprintf((char *) buffer_key, "Config%i", key); + return PrintError( + NM_GetKeyValue(reinterpret_cast(&val), (char *) buffer_key, sizeof(bool), &Out_Length, SECTOR_SECURE)); } -CHIP_ERROR STM32Config::ReadConfigValueStr(Key key, char * buf, size_t bufSize, size_t & outLen) +CHIP_ERROR STM32Config::ReadConfigValue(Key key, uint32_t & val) { + uint8_t buffer_key[35] = { 0 }; + size_t Out_Length; - return ReadConfigValueBin(key, reinterpret_cast(buf), bufSize, outLen); + sprintf((char *) buffer_key, "Config%i", key); + return PrintError( + NM_GetKeyValue(reinterpret_cast(&val), (char *) buffer_key, sizeof(uint32_t), &Out_Length, SECTOR_SECURE)); } -CHIP_ERROR STM32Config::ReadConfigValueBin(Key key, uint8_t * buf, size_t bufSize, size_t & outLen) +CHIP_ERROR STM32Config::ReadConfigValue(Key key, uint64_t & val) { + uint8_t buffer_key[35] = { 0 }; + size_t Out_Length; - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + sprintf((char *) buffer_key, "Config%i", key); + return PrintError( + NM_GetKeyValue(reinterpret_cast(&val), (char *) buffer_key, sizeof(uint64_t), &Out_Length, SECTOR_SECURE)); } -template CHIP_ERROR STM32Config::ReadConfigValue(Key key, bool & val); -template CHIP_ERROR STM32Config::ReadConfigValue(Key key, uint32_t & val); -template CHIP_ERROR STM32Config::ReadConfigValue(Key key, uint64_t & val); +CHIP_ERROR STM32Config::ReadConfigValueStr(Key key, char * buf, size_t bufSize, size_t & outLen) +{ + + return ReadConfigValueBin(key, reinterpret_cast(buf), bufSize, outLen); +} CHIP_ERROR STM32Config::WriteConfigValue(Key key, uint32_t val) { - uint8_t * buffer_key[35] = { 0 }; - size_t * read_by_size = NULL; - + uint8_t buffer_key[35] = { 0 }; + uint8_t buffer_convert[4]; + buffer_convert[0] = val; + buffer_convert[1] = val >> 8; + buffer_convert[2] = val >> 16; + buffer_convert[3] = val >> 24; sprintf((char *) buffer_key, "Config%i", key); - NM_SetKeyValue((char *) &val, (char *) buffer_key, sizeof(val), SECTOR_NO_SECURE); - return CHIP_NO_ERROR; + return PrintError(NM_SetKeyValue((char *) buffer_convert, (char *) buffer_key, sizeof(uint32_t), SECTOR_SECURE)); } CHIP_ERROR STM32Config::WriteConfigValueStr(Key key, const char * str) @@ -79,12 +98,10 @@ CHIP_ERROR STM32Config::WriteConfigValueStr(Key key, const char * str, size_t st CHIP_ERROR STM32Config::WriteConfigValueBin(Key key, const uint8_t * data, size_t dataLen) { - uint8_t * buffer_key[35] = { 0 }; - size_t * read_by_size = NULL; + uint8_t buffer_key[35] = { 0 }; sprintf((char *) buffer_key, "Config%i", key); - NM_SetKeyValue((char *) data, (char *) buffer_key, dataLen, SECTOR_NO_SECURE); - return CHIP_NO_ERROR; + return PrintError(NM_SetKeyValue((char *) data, (char *) buffer_key, dataLen, SECTOR_SECURE)); } bool STM32Config::ConfigValueExists(Key key) @@ -100,6 +117,55 @@ CHIP_ERROR STM32Config::FactoryResetConfig(void) void STM32Config::RunConfigUnitTest(void) {} +CHIP_ERROR STM32Config::PrintError(NVM_StatusTypeDef err) +{ + switch (err) + { + case NVM_OK: + ChipLogDetail(DataManagement, "NVM_OK"); + return CHIP_NO_ERROR; + + case NVM_KEY_NOT_FOUND: + ChipLogDetail(DataManagement, "CHIP_ERROR_PERSISTED_STORAGE_NOT_FOUND"); + return CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; + + case NVM_WRITE_FAILED: + ChipLogDetail(DataManagement, "NVM_WRITE_FAILED"); + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + + case NVM_READ_FAILED: + ChipLogDetail(DataManagement, "NVM_READ_FAILED"); + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + + case NVM_DELETE_FAILED: + ChipLogDetail(DataManagement, "NVM_DELETE_FAILED"); + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + + case NVM_SIZE_FULL: + ChipLogDetail(DataManagement, "NVM_SIZE_FULL"); + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + + case NVM_BLOCK_SIZE_OVERFLOW: + ChipLogDetail(DataManagement, "NVM_BLOCK_SIZE_OVERFLOW"); + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + + case NVM_ERROR_BLOCK_ALIGN: + ChipLogDetail(DataManagement, "NVM_ERROR_BLOCK_ALIGN"); + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + + case NVM_BUFFER_TOO_SMALL: + ChipLogDetail(DataManagement, "NVM_BUFFER_TOO_SMALL"); + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + + case NVM_PARAM_ERROR: + ChipLogDetail(DataManagement, "NVM_BUFFER_TOO_SMALL"); + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + + default: + ChipLogDetail(DataManagement, "NVM_UNKNOWN_ERROR "); + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + } +} } // namespace Internal } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/stm32/STM32Config.h b/src/platform/stm32/STM32Config.h index 98ddb844616342..f1df1dc7638861 100644 --- a/src/platform/stm32/STM32Config.h +++ b/src/platform/stm32/STM32Config.h @@ -23,6 +23,7 @@ #pragma once +#include "flash_wb.h" #include namespace chip { @@ -71,11 +72,11 @@ class STM32Config static constexpr Key kConfigKey_Max = kConfigKey_UniqueId; static CHIP_ERROR Init(); + // Config value accessors. - template - // Config value accessors. - static CHIP_ERROR ReadConfigValue(Key key, T & val); - // Configuration methods used by the GenericConfigurationManagerImpl<> template. + static CHIP_ERROR ReadConfigValue(Key key, bool & val); + static CHIP_ERROR ReadConfigValue(Key key, uint32_t & val); + static CHIP_ERROR ReadConfigValue(Key key, uint64_t & val); static CHIP_ERROR ReadConfigValueStr(Key key, char * buf, size_t bufSize, size_t & outLen); static CHIP_ERROR ReadConfigValueBin(Key key, uint8_t * buf, size_t bufSize, size_t & outLen); static CHIP_ERROR WriteConfigValue(Key key, uint32_t val); @@ -86,6 +87,7 @@ class STM32Config static bool ConfigValueExists(Key key); static CHIP_ERROR FactoryResetConfig(void); static void RunConfigUnitTest(void); + static CHIP_ERROR PrintError(NVM_StatusTypeDef err); }; } // namespace Internal diff --git a/src/platform/stm32/args.gni b/src/platform/stm32/args.gni index 56fd4e52bab0d6..4357e8ea8fa20f 100644 --- a/src/platform/stm32/args.gni +++ b/src/platform/stm32/args.gni @@ -10,7 +10,7 @@ # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and -# limitations under the License. +# limitations under the License. import("//build_overrides/chip.gni") import("//build_overrides/stm32_sdk.gni") diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index ccc2bc2188d176..b0642a966890fd 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -2362,21 +2362,36 @@ CHIP_ERROR CASESession::OnMessageReceived(ExchangeContext * ec, const PayloadHea return err; } -System::Clock::Timeout CASESession::ComputeSigma1ResponseTimeout(const ReliableMessageProtocolConfig & remoteMrpConfig) +namespace { +System::Clock::Timeout ComputeRoundTripTimeout(ExchangeContext::Timeout serverProcessingTime, + const ReliableMessageProtocolConfig & remoteMrpConfig) { + // TODO: This is duplicating logic from Session::ComputeRoundTripTimeout. Unfortunately, it's called by + // consumers who do not have a session. + const auto & maybeLocalMRPConfig = GetLocalMRPConfig(); + const auto & defaultMRRPConfig = GetDefaultMRPConfig(); + const auto & localMRPConfig = maybeLocalMRPConfig.ValueOr(defaultMRRPConfig); return GetRetransmissionTimeout(remoteMrpConfig.mActiveRetransTimeout, remoteMrpConfig.mIdleRetransTimeout, - // Assume peer is idle, since that's what we - // will assume for our initial message. + // Assume peer is idle, as a worst-case assumption (probably true for + // Sigma1, since that will be our initial message on the session, but less + // so for Sigma2). System::Clock::kZero, remoteMrpConfig.mActiveThresholdTime) + - kExpectedSigma1ProcessingTime; + serverProcessingTime + + GetRetransmissionTimeout(localMRPConfig.mActiveRetransTimeout, localMRPConfig.mIdleRetransTimeout, + // Peer will assume we are active, since it's + // responding to our message. + System::SystemClock().GetMonotonicTimestamp(), localMRPConfig.mActiveThresholdTime); +} +} // anonymous namespace + +System::Clock::Timeout CASESession::ComputeSigma1ResponseTimeout(const ReliableMessageProtocolConfig & remoteMrpConfig) +{ + return ComputeRoundTripTimeout(kExpectedSigma1ProcessingTime, remoteMrpConfig); } System::Clock::Timeout CASESession::ComputeSigma2ResponseTimeout(const ReliableMessageProtocolConfig & remoteMrpConfig) { - return GetRetransmissionTimeout(remoteMrpConfig.mActiveRetransTimeout, remoteMrpConfig.mIdleRetransTimeout, - // Assume peer is idle, as a worst-case assumption. - System::Clock::kZero, remoteMrpConfig.mActiveThresholdTime) + - kExpectedHighProcessingTime; + return ComputeRoundTripTimeout(kExpectedHighProcessingTime, remoteMrpConfig); } bool CASESession::InvokeBackgroundWorkWatchdog() diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index 5fc6a37f72f4ab..68d6ddd177eeb8 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -64,6 +64,13 @@ class TestContext : public Test::LoopbackMessagingContext static void SetUpTestSuite(); // Performs shared teardown for all tests in the test suite static void TearDownTestSuite(); + + virtual void SetUp() override + { + ConfigInitializeNodes(false); + chip::Test::LoopbackMessagingContext::SetUp(); + } + virtual void TearDown() override { chip::Test::LoopbackMessagingContext::TearDown(); } }; void ServiceEvents(TestContext & ctx) @@ -329,7 +336,6 @@ CHIP_ERROR InitCredentialSets() void TestContext::SetUpTestSuite() { - ConfigInitializeNodes(false); CHIP_ERROR err = CHIP_NO_ERROR; LoopbackMessagingContext::SetUpTestSuite(); // TODO: use ASSERT_EQ, once transition to pw_unit_test is complete diff --git a/src/protocols/secure_channel/tests/TestPASESession.cpp b/src/protocols/secure_channel/tests/TestPASESession.cpp index d610b304cec481..896f721579334d 100644 --- a/src/protocols/secure_channel/tests/TestPASESession.cpp +++ b/src/protocols/secure_channel/tests/TestPASESession.cpp @@ -89,11 +89,16 @@ class TestContext : public chip::Test::LoopbackMessagingContext { public: // Performs shared setup for all tests in the test suite - static void SetUpTestSuite() + static void SetUpTestSuite() { chip::Test::LoopbackMessagingContext::SetUpTestSuite(); } + static void TearDownTestSuite() { chip::Test::LoopbackMessagingContext::TearDownTestSuite(); } + + void SetUp() override { ConfigInitializeNodes(false); - chip::Test::LoopbackMessagingContext::SetUpTestSuite(); + chip::Test::LoopbackMessagingContext::SetUp(); } + + void TearDown() override { chip::Test::LoopbackMessagingContext::TearDown(); } }; class PASETestLoopbackTransportDelegate : public Test::LoopbackTransportDelegate diff --git a/src/pybindings/pycontroller/build-chip-wheel.py b/src/pybindings/pycontroller/build-chip-wheel.py index a61b591d5c5368..61bdf373e8615c 100644 --- a/src/pybindings/pycontroller/build-chip-wheel.py +++ b/src/pybindings/pycontroller/build-chip-wheel.py @@ -60,7 +60,6 @@ def __init__(self, name): chipPackageVer = args.build_number installScripts = [ - # InstalledScriptInfo('chip-device-ctrl.py'), # InstalledScriptInfo('chip-repl.py'), ] diff --git a/src/python_testing/TC_IDM_1_2.py b/src/python_testing/TC_IDM_1_2.py index 8f329daca51101..87ede456dacb1a 100644 --- a/src/python_testing/TC_IDM_1_2.py +++ b/src/python_testing/TC_IDM_1_2.py @@ -236,8 +236,11 @@ async def test_TC_IDM_1_2(self): # Lucky candidate ArmFailSafe is at it again - command side effect is to set breadcrumb attribute cmd = Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=900, breadcrumb=2) - await self.default_controller.SendCommand(nodeid=self.dut_node_id, endpoint=0, payload=cmd, suppressResponse=True) - # TODO: Once the above issue is resolved, this needs a check to ensure that no response was received. + try: + await self.default_controller.SendCommand(nodeid=self.dut_node_id, endpoint=0, payload=cmd, suppressResponse=True) + # TODO: Once the above issue is resolved, this needs a check to ensure that (always) no response was received. + except ChipStackError: + logging.info("DUT correctly supressed the response") # Verify that the command had the correct side effect even if a response was sent breadcrumb = await self.read_single_attribute_check_success( diff --git a/src/python_testing/TC_IDM_1_4.py b/src/python_testing/TC_IDM_1_4.py index 507b5d74254abf..34d75f7d8f6184 100644 --- a/src/python_testing/TC_IDM_1_4.py +++ b/src/python_testing/TC_IDM_1_4.py @@ -74,7 +74,11 @@ async def test_TC_IDM_1_4(self): cap_for_batch_commands = 100 number_of_commands_to_send = min(max_paths_per_invoke + 1, cap_for_batch_commands) - invalid_command_id = 0xffff_ffff + # Use a valid (according to MEI definition) command-id (in this case belonging to Test Vendor MC with prefix 0xfff4) + # which should never be existing on a prodiction device. We use this decodable id to prevent hitting issues with the + # specification being not clearly defined in respect to decoding vs processing the invoke requests. + invalid_command_id = 0xfff4_00ff + list_of_commands_to_send = [] for endpoint_index in range(number_of_commands_to_send): # Using Toggle command to form the base as it is a command that doesn't take diff --git a/src/python_testing/TC_SC_3_6.py b/src/python_testing/TC_SC_3_6.py index a6994cbf288539..ec09d4bb8e815e 100644 --- a/src/python_testing/TC_SC_3_6.py +++ b/src/python_testing/TC_SC_3_6.py @@ -20,6 +20,7 @@ import queue import time from threading import Event +from typing import List import chip.clusters as Clusters from chip.clusters import ClusterObjects as ClustersObjects @@ -123,6 +124,24 @@ async def test_TC_SC_3_6(self): ) asserts.assert_greater_equal(capability_minima.caseSessionsPerFabric, 3) + logging.info("Pre-condition: Remove all pre-existing fabrics on the device that do not belong to the TH") + commissioned_fabric_count: int = await self.read_single_attribute( + dev_ctrl, node_id=self.dut_node_id, + endpoint=0, attribute=Clusters.OperationalCredentials.Attributes.CommissionedFabrics) + + if commissioned_fabric_count > 1: + fabrics: List[Clusters.OperationalCredentials.Structs.FabricDescriptorStruct] = await self.read_single_attribute( + dev_ctrl, node_id=self.dut_node_id, endpoint=0, + attribute=Clusters.OperationalCredentials.Attributes.Fabrics, fabricFiltered=False) + current_fabric_index = await self.read_single_attribute_check_success(cluster=Clusters.OperationalCredentials, attribute=Clusters.OperationalCredentials.Attributes.CurrentFabricIndex) + for fabric in fabrics: + if fabric.fabricIndex == current_fabric_index: + continue + # This is not the test client's fabric, so remove it. + logging.info(f"Removing extra fabric at {fabric.fabricIndex} from device.") + await dev_ctrl.SendCommand( + self.dut_node_id, 0, Clusters.OperationalCredentials.Commands.RemoveFabric(fabricIndex=fabric.fabricIndex)) + logging.info("Pre-conditions: use existing fabric to configure new fabrics so that total is %d fabrics" % num_fabrics_to_commission) diff --git a/src/python_testing/TestConformanceSupport.py b/src/python_testing/TestConformanceSupport.py index b383f1acb63cc7..f71eb3ed1d531f 100644 --- a/src/python_testing/TestConformanceSupport.py +++ b/src/python_testing/TestConformanceSupport.py @@ -17,7 +17,7 @@ import xml.etree.ElementTree as ElementTree -from conformance_support import ConformanceDecision, ConformanceParseParameters, parse_callable_from_xml +from conformance_support import ConformanceDecision, ConformanceException, ConformanceParseParameters, parse_callable_from_xml from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main from mobly import asserts @@ -616,6 +616,61 @@ def test_conformance_otherwise(self): asserts.assert_equal(xml_callable(f, [], []), ConformanceDecision.PROVISIONAL) asserts.assert_equal(str(xml_callable), 'AB & !CD, P') + def test_conformance_greater(self): + # AB, [CD] + xml = ('' + '' + '' + '' + '' + '') + et = ElementTree.fromstring(xml) + xml_callable = parse_callable_from_xml(et, self.params) + # TODO: switch this to check greater than once the update to the base is done (#33422) + asserts.assert_equal(xml_callable(0x00, [], []), ConformanceDecision.OPTIONAL) + asserts.assert_equal(str(xml_callable), 'attr1 > 1') + + # Ensure that we can only have greater terms with exactly 2 value + xml = ('' + '' + '' + '' + '' + '' + '') + et = ElementTree.fromstring(xml) + try: + xml_callable = parse_callable_from_xml(et, self.params) + asserts.fail("Incorrectly parsed bad greaterTerm XML with > 2 values") + except ConformanceException: + pass + + xml = ('' + '' + '' + '' + '') + et = ElementTree.fromstring(xml) + try: + xml_callable = parse_callable_from_xml(et, self.params) + asserts.fail("Incorrectly parsed bad greaterTerm XML with < 2 values") + except ConformanceException: + pass + + # Only attributes and literals allowed because arithmetic operations require values + xml = ('' + '' + '' + '' + '' + '') + et = ElementTree.fromstring(xml) + try: + xml_callable = parse_callable_from_xml(et, self.params) + asserts.fail("Incorrectly parsed greater term with feature value") + except ConformanceException: + pass + if __name__ == "__main__": default_matter_test_main() diff --git a/src/python_testing/conformance_support.py b/src/python_testing/conformance_support.py index 89157c226351b1..025c1afa678995 100644 --- a/src/python_testing/conformance_support.py +++ b/src/python_testing/conformance_support.py @@ -33,10 +33,12 @@ AND_TERM = 'andTerm' OR_TERM = 'orTerm' NOT_TERM = 'notTerm' +GREATER_TERM = 'greaterTerm' FEATURE_TAG = 'feature' ATTRIBUTE_TAG = 'attribute' COMMAND_TAG = 'command' CONDITION_TAG = 'condition' +LITERAL_TAG = 'literal' class ConformanceException(Exception): @@ -123,6 +125,18 @@ def __str__(self): return 'P' +class literal: + def __init__(self, value: str): + self.value = int(value) + + def __call__(self): + # This should never be called + raise ConformanceException('Literal conformance function should not be called - this is simply a value holder') + + def __str__(self): + return str(self.value) + + class feature: def __init__(self, requiredFeature: uint, code: str): self.requiredFeature = requiredFeature @@ -267,8 +281,25 @@ def __str__(self): op_strs = [str(op) for op in self.op_list] return f'({" | ".join(op_strs)})' -# TODO: add xor operation once it's required -# TODO: how would equal and unequal operations work here? + +class greater_operation: + def _type_ok(self, op: Callable): + return type(op) == attribute or type(op) == literal + + def __init__(self, op1: Callable, op2: Callable): + if not self._type_ok(op1) or not self._type_ok(op2): + raise ConformanceException('Arithmetic operations can only have attribute or literal value children') + self.op1 = op1 + self.op2 = op2 + + def __call__(self, feature_map: uint, attribute_list: list[uint], all_command_list: list[uint]) -> ConformanceDecision: + # For now, this is fully optional, need to implement this properly later, but it requires access to the actual attribute values + # We need to reach into the attribute, but can't use it directly because the attribute callable is an EXISTENCE check and + # the arithmetic functions require a value. + return ConformanceDecision.OPTIONAL + + def __str__(self): + return f'{str(self.op1)} > {str(self.op2)}' class otherwise: @@ -325,6 +356,8 @@ def parse_callable_from_xml(element: ElementTree.Element, params: ConformancePar return command(params.command_map[element.get('name')], element.get('name')) elif element.tag == CONDITION_TAG and element.get('name').lower() == 'zigbee': return zigbee() + elif element.tag == LITERAL_TAG: + return literal(element.get('value')) else: raise ConformanceException( f'Unexpected xml conformance element with no children {str(element.tag)} {str(element.attrib)}') @@ -354,5 +387,9 @@ def parse_callable_from_xml(element: ElementTree.Element, params: ConformancePar return not_operation(ops[0]) elif element.tag == OTHERWISE_CONFORM: return otherwise(ops) + elif element.tag == GREATER_TERM: + if len(ops) != 2: + raise ConformanceException(f'Greater than term found with more than two subelements {list(element)}') + return greater_operation(ops[0], ops[1]) else: raise ConformanceException(f'Unexpected conformance tag with children {element}') diff --git a/src/setup_payload/python/Base38.py b/src/setup_payload/python/Base38.py index 23113f7ed25c8d..834ae9e1d520e7 100644 --- a/src/setup_payload/python/Base38.py +++ b/src/setup_payload/python/Base38.py @@ -24,6 +24,7 @@ RADIX = len(CODES) BASE38_CHARS_NEEDED_IN_CHUNK = [2, 4, 5] MAX_BYTES_IN_CHUNK = 3 +MAX_ENCODED_BYTES_IN_CHUNK = 5 def encode(bytes): @@ -47,3 +48,25 @@ def encode(bytes): base38_chars_needed -= 1 return qrcode + + +def decode(qrcode): + total_chars = len(qrcode) + decoded_bytes = bytearray() + + for i in range(0, total_chars, MAX_ENCODED_BYTES_IN_CHUNK): + if (i + MAX_ENCODED_BYTES_IN_CHUNK) > total_chars: + chars_in_chunk = total_chars - i + else: + chars_in_chunk = MAX_ENCODED_BYTES_IN_CHUNK + + value = 0 + for j in range(i + chars_in_chunk - 1, i - 1, -1): + value = value * RADIX + CODES.index(qrcode[j]) + + bytes_in_chunk = BASE38_CHARS_NEEDED_IN_CHUNK.index(chars_in_chunk) + 1 + for k in range(0, bytes_in_chunk): + decoded_bytes.append(value & 0xFF) + value = value >> 8 + + return decoded_bytes diff --git a/src/setup_payload/python/README.md b/src/setup_payload/python/README.md index 068bf553fb7e6b..a39496104300d1 100644 --- a/src/setup_payload/python/README.md +++ b/src/setup_payload/python/README.md @@ -1,19 +1,22 @@ -## Python tool to generate Matter onboarding codes +## Python tool to generate and parse Matter onboarding codes -Generates Manual Pairing Code and QR Code +Generates and parses Manual Pairing Code and QR Code #### example usage: +- Parse + ``` -./generate_setup_payload.py -h -./generate_setup_payload.py -d 3840 -p 20202021 -cf 0 -dm 2 -vid 65521 -pid 32768 +./SetupPayload.py parse MT:U9VJ0OMV172PX813210 +./SetupPayload.py parse 34970112332 ``` -- Output +- Generate ``` -Manualcode : 34970112332 -QRCode : MT:Y.K9042C00KA0648G00 +./SetupPayload.py generate --help +./SetupPayload.py generate -d 3840 -p 20202021 +./SetupPayload.py generate -d 3840 -p 20202021 --vendor-id 65521 --product-id 32768 -cf 0 -dm 2 ``` For more details please refer Matter Specification diff --git a/src/setup_payload/python/SetupPayload.py b/src/setup_payload/python/SetupPayload.py new file mode 100755 index 00000000000000..82a58d957eff08 --- /dev/null +++ b/src/setup_payload/python/SetupPayload.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +# +# 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 enum + +import Base38 +import click +from bitarray import bitarray +from bitarray.util import int2ba, zeros +from construct import BitsInteger, BitStruct, Enum +from stdnum.verhoeff import calc_check_digit + +# Format for constructing manualcode +manualcode_format = BitStruct( + 'version' / BitsInteger(1), + 'vid_pid_present' / BitsInteger(1), + 'discriminator' / BitsInteger(4), + 'pincode_lsb' / BitsInteger(14), + 'pincode_msb' / BitsInteger(13), + 'vid' / BitsInteger(16), + 'pid' / BitsInteger(16), + 'padding' / BitsInteger(7), # this is intentional as BitStruct only takes 8-bit aligned data +) + +# Format for constructing qrcode +# qrcode bytes are packed as lsb....msb, hence the order is reversed +qrcode_format = BitStruct( + 'padding' / BitsInteger(4), + 'pincode' / BitsInteger(27), + 'discriminator' / BitsInteger(12), + 'discovery' / BitsInteger(8), + 'flow' / Enum(BitsInteger(2), + Standard=0, UserIntent=1, Custom=2), + 'pid' / BitsInteger(16), + 'vid' / BitsInteger(16), + 'version' / BitsInteger(3), +) + + +class CommissioningFlow(enum.IntEnum): + Standard = 0, + UserIntent = 1, + Custom = 2 + + +class SetupPayload: + def __init__(self, discriminator, pincode, rendezvous=4, flow=CommissioningFlow.Standard, vid=0, pid=0): + self.long_discriminator = discriminator + self.short_discriminator = discriminator >> 8 + self.pincode = pincode + self.discovery = rendezvous + self.flow = flow + self.vid = vid + self.pid = pid + + def p_print(self): + print('{:<{}} :{}'.format('Flow', 24, self.flow)) + print('{:<{}} :{}'.format('Pincode', 24, self.pincode)) + print('{:<{}} :{}'.format('Short Discriminator', 24, self.short_discriminator)) + if self.long_discriminator: + print('{:<{}} :{}'.format('Long Discriminator', 24, self.long_discriminator)) + if self.discovery: + print('{:<{}} :{}'.format('Discovery Capabilities', 24, self.discovery)) + if self.vid is not None and self.pid is not None: + print('{:<{}} :{:<{}} (0x{:04x})'.format('Vendor Id', 24, self.vid, 6, self.vid)) + print('{:<{}} :{:<{}} (0x{:04x})'.format('Product Id', 24, self.pid, 6, self.pid)) + + def qrcode_dict(self): + return { + 'version': 0, + 'vid': self.vid, + 'pid': self.pid, + 'flow': int(self.flow), + 'discovery': self.discovery, + 'discriminator': self.long_discriminator, + 'pincode': self.pincode, + 'padding': 0, + } + + def manualcode_dict(self): + return { + 'version': 0, + 'vid_pid_present': 0 if self.flow == CommissioningFlow.Standard else 1, + 'discriminator': self.short_discriminator, + 'pincode_lsb': self.pincode & 0x3FFF, # 14 ls-bits + 'pincode_msb': self.pincode >> 14, # 13 ms-bits + 'vid': 0 if self.flow == CommissioningFlow.Standard else self.vid, + 'pid': 0 if self.flow == CommissioningFlow.Standard else self.pid, + 'padding': 0, + } + + def generate_qrcode(self): + data = qrcode_format.build(self.qrcode_dict()) + b38_encoded = Base38.encode(data[::-1]) # reversing + return 'MT:{}'.format(b38_encoded) + + def generate_manualcode(self): + CHUNK1_START = 0 + CHUNK1_LEN = 4 + CHUNK2_START = CHUNK1_START + CHUNK1_LEN + CHUNK2_LEN = 16 + CHUNK3_START = CHUNK2_START + CHUNK2_LEN + CHUNK3_LEN = 13 + + bytes = manualcode_format.build(self.manualcode_dict()) + bits = bitarray() + bits.frombytes(bytes) + + chunk1 = str(int(bits[CHUNK1_START:CHUNK1_START + CHUNK1_LEN].to01(), 2)).zfill(1) + chunk2 = str(int(bits[CHUNK2_START:CHUNK2_START + CHUNK2_LEN].to01(), 2)).zfill(5) + chunk3 = str(int(bits[CHUNK3_START:CHUNK3_START + CHUNK3_LEN].to01(), 2)).zfill(4) + chunk4 = str(self.vid).zfill(5) if self.flow != CommissioningFlow.Standard else '' + chunk5 = str(self.pid).zfill(5) if self.flow != CommissioningFlow.Standard else '' + payload = '{}{}{}{}{}'.format(chunk1, chunk2, chunk3, chunk4, chunk5) + return '{}{}'.format(payload, calc_check_digit(payload)) + + @staticmethod + def from_container(container, is_qrcode): + payload = None + if is_qrcode: + payload = SetupPayload(container['discriminator'], container['pincode'], + container['discovery'], CommissioningFlow(container['flow'].__int__()), + container['vid'], container['pid']) + else: + payload = SetupPayload(discriminator=container['discriminator'], + pincode=(container['pincode_msb'] << 14) | container['pincode_lsb'], + vid=container['vid'] if container['vid_pid_present'] else None, + pid=container['pid'] if container['vid_pid_present'] else None) + payload.short_discriminator = container['discriminator'] + payload.long_discriminator = None + payload.discovery = None + payload.flow = 2 if container['vid_pid_present'] else 0 + + return payload + + @staticmethod + def parse_qrcode(payload): + payload = payload[3:] # remove 'MT:' + b38_decoded = Base38.decode(payload)[::-1] + container = qrcode_format.parse(b38_decoded) + return SetupPayload.from_container(container, is_qrcode=True) + + @staticmethod + def parse_manualcode(payload): + payload_len = len(payload) + if payload_len != 11 and payload_len != 21: + print('Invalid length') + return None + + # if first digit is greater than 7 the its not v1 + if int(str(payload)[0]) > 7: + print('incorrect first digit') + return None + + if calc_check_digit(payload[:-1]) != str(payload)[-1]: + print('check digit mismatch') + return None + + # vid_pid_present bit position + is_long = int(str(payload)[0]) & (1 << 2) + + bits = int2ba(int(payload[0]), length=4) + bits += int2ba(int(payload[1:6]), length=16) + bits += int2ba(int(payload[6:10]), length=13) + bits += int2ba(int(payload[10:15]), length=16) if is_long else zeros(16) + bits += int2ba(int(payload[15:20]), length=16) if is_long else zeros(16) + bits += zeros(7) # padding + + container = manualcode_format.parse(bits.tobytes()) + return SetupPayload.from_container(container, is_qrcode=False) + + @staticmethod + def parse(payload): + if payload.startswith('MT:'): + return SetupPayload.parse_qrcode(payload) + else: + return SetupPayload.parse_manualcode(payload) + + +@click.group() +def cli(): + pass + + +@cli.command() +@click.argument('payload') +def parse(payload): + click.echo(f'Parsing payload: {payload}') + SetupPayload.parse(payload).p_print() + + +@cli.command() +@click.option('--discriminator', '-d', required=True, type=click.IntRange(0, 0xFFF), help='Discriminator') +@click.option('--passcode', '-p', required=True, type=click.IntRange(1, 0x5F5E0FE), help='setup pincode') +@click.option('--vendor-id', '-vid', type=click.IntRange(0, 0xFFFF), default=0, help='Vendor ID') +@click.option('--product-id', '-pid', type=click.IntRange(0, 0xFFFF), default=0, help='Product ID') +@click.option('--discovery-cap-bitmask', '-dm', type=click.IntRange(0, 7), default=4, help='Commissionable device discovery capability bitmask. 0:SoftAP, 1:BLE, 2:OnNetwork. Default: OnNetwork') +@click.option('--commissioning-flow', '-cf', type=click.IntRange(0, 2), default=0, help='Commissioning flow, 0:Standard, 1:User-Intent, 2:Custom') +def generate(passcode, discriminator, vendor_id, product_id, discovery_cap_bitmask, commissioning_flow): + payload = SetupPayload(discriminator, passcode, discovery_cap_bitmask, commissioning_flow, vendor_id, product_id) + print("Manualcode : {}".format(payload.generate_manualcode())) + print("QRCode : {}".format(payload.generate_qrcode())) + + +if __name__ == '__main__': + cli() diff --git a/src/setup_payload/python/generate_setup_payload.py b/src/setup_payload/python/generate_setup_payload.py deleted file mode 100755 index 28a834651214b1..00000000000000 --- a/src/setup_payload/python/generate_setup_payload.py +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2022 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 argparse -import enum -import sys - -import Base38 -from bitarray import bitarray -from stdnum.verhoeff import calc_check_digit - -# See section 5.1.4.1 Manual Pairing Code in the Matter specification v1.0 -MANUAL_DISCRIMINATOR_LEN = 4 -PINCODE_LEN = 27 - -MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_LEN = 2 -MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_POS = 0 -MANUAL_CHUNK1_VID_PID_PRESENT_BIT_POS = MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_POS + MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_LEN -MANUAL_CHUNK1_LEN = 1 - -MANUAL_CHUNK2_DISCRIMINATOR_LSBITS_LEN = 2 -MANUAL_CHUNK2_PINCODE_LSBITS_LEN = 14 -MANUAL_CHUNK2_PINCODE_LSBITS_POS = 0 -MANUAL_CHUNK2_DISCRIMINATOR_LSBITS_POS = MANUAL_CHUNK2_PINCODE_LSBITS_POS + MANUAL_CHUNK2_PINCODE_LSBITS_LEN -MANUAL_CHUNK2_LEN = 5 - -MANUAL_CHUNK3_PINCODE_MSBITS_LEN = 13 -MANUAL_CHUNK3_PINCODE_MSBITS_POS = 0 -MANUAL_CHUNK3_LEN = 4 - -MANUAL_VID_LEN = 5 -MANUAL_PID_LEN = 5 - -# See section 5.1.3. QR Code in the Matter specification v1.0 -QRCODE_VERSION_LEN = 3 -QRCODE_DISCRIMINATOR_LEN = 12 -QRCODE_VID_LEN = 16 -QRCODE_PID_LEN = 16 -QRCODE_COMMISSIONING_FLOW_LEN = 2 -QRCODE_DISCOVERY_CAP_BITMASK_LEN = 8 -QRCODE_PADDING_LEN = 4 -QRCODE_VERSION = 0 -QRCODE_PADDING = 0 - -INVALID_PASSCODES = [00000000, 11111111, 22222222, 33333333, 44444444, 55555555, - 66666666, 77777777, 88888888, 99999999, 12345678, 87654321] - - -class CommissioningFlow(enum.IntEnum): - Standard = 0, - UserIntent = 1, - Custom = 2 - - -class SetupPayload: - def __init__(self, discriminator, pincode, rendezvous=4, flow=CommissioningFlow.Standard, vid=0, pid=0): - self.long_discriminator = discriminator - self.short_discriminator = discriminator >> 8 - self.pincode = pincode - self.rendezvous = rendezvous - self.flow = flow - self.vid = vid - self.pid = pid - - def manual_chunk1(self): - discriminator_shift = (MANUAL_DISCRIMINATOR_LEN - MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_LEN) - discriminator_mask = (1 << MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_LEN) - 1 - discriminator_chunk = (self.short_discriminator >> discriminator_shift) & discriminator_mask - vid_pid_present_flag = 0 if self.flow == CommissioningFlow.Standard else 1 - return (discriminator_chunk << MANUAL_CHUNK1_DISCRIMINATOR_MSBITS_POS) | (vid_pid_present_flag << MANUAL_CHUNK1_VID_PID_PRESENT_BIT_POS) - - def manual_chunk2(self): - discriminator_mask = (1 << MANUAL_CHUNK2_DISCRIMINATOR_LSBITS_LEN) - 1 - pincode_mask = (1 << MANUAL_CHUNK2_PINCODE_LSBITS_LEN) - 1 - discriminator_chunk = self.short_discriminator & discriminator_mask - return ((self.pincode & pincode_mask) << MANUAL_CHUNK2_PINCODE_LSBITS_POS) | (discriminator_chunk << MANUAL_CHUNK2_DISCRIMINATOR_LSBITS_POS) - - def manual_chunk3(self): - pincode_shift = PINCODE_LEN - MANUAL_CHUNK3_PINCODE_MSBITS_LEN - pincode_mask = (1 << MANUAL_CHUNK3_PINCODE_MSBITS_LEN) - 1 - return ((self.pincode >> pincode_shift) & pincode_mask) << MANUAL_CHUNK3_PINCODE_MSBITS_POS - - def generate_manualcode(self): - payload = str(self.manual_chunk1()).zfill(MANUAL_CHUNK1_LEN) - payload += str(self.manual_chunk2()).zfill(MANUAL_CHUNK2_LEN) - payload += str(self.manual_chunk3()).zfill(MANUAL_CHUNK3_LEN) - - if self.flow != CommissioningFlow.Standard: - payload += str(self.vid).zfill(MANUAL_VID_LEN) - payload += str(self.pid).zfill(MANUAL_PID_LEN) - - payload += calc_check_digit(payload) - return payload - - def generate_qrcode(self): - qrcode_bit_string = '{0:b}'.format(QRCODE_PADDING).zfill(QRCODE_PADDING_LEN) - qrcode_bit_string += '{0:b}'.format(self.pincode).zfill(PINCODE_LEN) - qrcode_bit_string += '{0:b}'.format(self.long_discriminator).zfill(QRCODE_DISCRIMINATOR_LEN) - qrcode_bit_string += '{0:b}'.format(self.rendezvous).zfill(QRCODE_DISCOVERY_CAP_BITMASK_LEN) - qrcode_bit_string += '{0:b}'.format(int(self.flow)).zfill(QRCODE_COMMISSIONING_FLOW_LEN) - qrcode_bit_string += '{0:b}'.format(self.pid).zfill(QRCODE_PID_LEN) - qrcode_bit_string += '{0:b}'.format(self.vid).zfill(QRCODE_VID_LEN) - qrcode_bit_string += '{0:b}'.format(QRCODE_VERSION).zfill(QRCODE_VERSION_LEN) - - qrcode_bits = bitarray(qrcode_bit_string) - bytes = list(qrcode_bits.tobytes()) - bytes.reverse() - return 'MT:{}'.format(Base38.encode(bytes)) - - -def validate_args(args): - def check_int_range(value, min_value, max_value, name): - if value and ((value < min_value) or (value > max_value)): - print('{} is out of range, should be in range from {} to {}'.format(name, min_value, max_value)) - sys.exit(1) - - if args.passcode is not None: - if ((args.passcode < 0x0000001 and args.passcode > 0x5F5E0FE) or (args.passcode in INVALID_PASSCODES)): - print('Invalid passcode:' + str(args.passcode)) - sys.exit(1) - - check_int_range(args.discriminator, 0x0000, 0x0FFF, 'Discriminator') - check_int_range(args.product_id, 0x0000, 0xFFFF, 'Product id') - check_int_range(args.vendor_id, 0x0000, 0xFFFF, 'Vendor id') - check_int_range(args.discovery_cap_bitmask, 0x0001, 0x0007, 'Discovery Capability Mask') - - -def main(): - def any_base_int(s): return int(s, 0) - parser = argparse.ArgumentParser(description='Matter Manual and QRCode Setup Payload Generator Tool') - parser.add_argument('-d', '--discriminator', type=any_base_int, required=True, - help='The discriminator for pairing, range: 0x00-0x0FFF') - parser.add_argument('-p', '--passcode', type=any_base_int, required=True, - help='The setup passcode for pairing, range: 0x01-0x5F5E0FE') - parser.add_argument('-vid', '--vendor-id', type=any_base_int, default=0, help='Vendor id') - parser.add_argument('-pid', '--product-id', type=any_base_int, default=0, help='Product id') - parser.add_argument('-cf', '--commissioning-flow', type=any_base_int, default=0, - help='Device commissioning flow, 0:Standard, 1:User-Intent, 2:Custom. \ - Default is 0.', choices=[0, 1, 2]) - parser.add_argument('-dm', '--discovery-cap-bitmask', type=any_base_int, default=4, - help='Commissionable device discovery capability bitmask. \ - 0:SoftAP, 1:BLE, 2:OnNetwork. Default: OnNetwork') - args = parser.parse_args() - validate_args(args) - - payloads = SetupPayload(args.discriminator, args.passcode, args.discovery_cap_bitmask, - CommissioningFlow(args.commissioning_flow), args.vendor_id, args.product_id) - manualcode = payloads.generate_manualcode() - qrcode = payloads.generate_qrcode() - - print("Manualcode : {}".format(manualcode)) - print("QRCode : {}".format(qrcode)) - - -if __name__ == '__main__': - main() diff --git a/src/setup_payload/python/requirements.txt b/src/setup_payload/python/requirements.txt index 43800d601c7736..1026fa98d061c5 100644 --- a/src/setup_payload/python/requirements.txt +++ b/src/setup_payload/python/requirements.txt @@ -1,2 +1,4 @@ -bitarray==2.6.0 +bitarray>=2.8.1 +click>=8.1.3 +construct>=2.10.68 python_stdnum==1.18 diff --git a/src/setup_payload/tests/run_python_setup_payload_gen_test.py b/src/setup_payload/tests/run_python_setup_payload_gen_test.py deleted file mode 100644 index b52000c1a6fd98..00000000000000 --- a/src/setup_payload/tests/run_python_setup_payload_gen_test.py +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2023 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 os -import re -import subprocess -import sys - -CHIP_TOPDIR = os.path.dirname(os.path.realpath(__file__))[:-len(os.path.join('src', 'setup_payload', 'tests'))] -sys.path.insert(0, os.path.join(CHIP_TOPDIR, 'src', 'setup_payload', 'python')) -from generate_setup_payload import CommissioningFlow, SetupPayload # noqa: E402 - - -def payload_param_dict(): - return { - 'Version': None, - 'VendorID': None, - 'ProductID': None, - 'Custom flow': None, - 'Discovery Bitmask': None, - 'Short discriminator': None, - 'Long discriminator': None, - 'Passcode': None - } - - -def remove_escape_sequence(data): - ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') - result = ansi_escape.sub('', data) - return result - - -def parse_setup_payload(chip_tool, payload): - cmd_args = [chip_tool, 'payload', 'parse-setup-payload', payload] - data = subprocess.check_output(cmd_args).decode('utf-8') - data = remove_escape_sequence(data) - parsed_params = payload_param_dict() - for key in parsed_params: - k_st = data.find(key) - if k_st == -1: - continue - - # 1 is for ":" - k_end = k_st + len(key) + 1 - - k_nl = data.find('\n', k_end) - parsed_params[key] = data[k_end:k_nl].split()[0] - - return parsed_params - - -def generate_payloads(in_params): - payloads = SetupPayload(in_params['Long discriminator'], in_params['Passcode'], - in_params['Discovery Bitmask'], CommissioningFlow(in_params['Custom flow']), - in_params['VendorID'], in_params['ProductID']) - manualcode = payloads.generate_manualcode() - qrcode = payloads.generate_qrcode() - return manualcode, qrcode - - -def verify_payloads(in_params, manualcode_params, qrcode_params): - assert in_params['Version'] == int(manualcode_params['Version'], 0) - assert in_params['Passcode'] == int(manualcode_params['Passcode'], 0) - assert in_params['Short discriminator'] == int(manualcode_params['Short discriminator'], 0) - if in_params['Custom flow'] != 0: - assert in_params['VendorID'] == int(manualcode_params['VendorID'], 0) - assert in_params['ProductID'] == int(manualcode_params['ProductID'], 0) - - assert in_params['Version'] == int(qrcode_params['Version'], 0) - assert in_params['VendorID'] == int(qrcode_params['VendorID'], 0) - assert in_params['ProductID'] == int(qrcode_params['ProductID'], 0) - assert in_params['Custom flow'] == int(qrcode_params['Custom flow'], 0) - assert in_params['Discovery Bitmask'] == int(qrcode_params['Discovery Bitmask'], 0) - assert in_params['Passcode'] == int(qrcode_params['Passcode'], 0) - assert in_params['Long discriminator'] == int(qrcode_params['Long discriminator'], 0) - - -def get_payload_params(discriminator, passcode, discovery=4, flow=0, vid=0, pid=0, version=0): - p = payload_param_dict() - p['Version'] = version - p['VendorID'] = vid - p['ProductID'] = pid - p['Custom flow'] = flow - p['Discovery Bitmask'] = discovery - p['Long discriminator'] = discriminator - p['Short discriminator'] = discriminator >> 8 - p['Passcode'] = passcode - return p - - -def run_tests(chip_tool): - test_data_set = [ - get_payload_params(3840, 20202021), - get_payload_params(3781, 12349876, flow=1, vid=1, pid=1), - get_payload_params(2310, 23005908, flow=2, vid=0xFFF3, pid=0x8098), - get_payload_params(3091, 43338551, discovery=2, flow=2, vid=0x1123, pid=0x0012), - get_payload_params(80, 54757432, discovery=6, flow=2, vid=0x2345, pid=0x1023), - get_payload_params(174, 81235604, discovery=7, flow=1, vid=0x45, pid=0x10), - ] - - for test_params in test_data_set: - manualcode, qrcode = generate_payloads(test_params) - manualcode_params = parse_setup_payload(chip_tool, manualcode) - qrcode_params = parse_setup_payload(chip_tool, qrcode) - - print("Input parameters:", test_params) - print("Manualcode:", manualcode) - print("QRCode:", qrcode) - print("Manualcode parsed by chip-tool:", manualcode_params) - print("QRCode parsed by chip-tool:", qrcode_params) - print("") - - verify_payloads(test_params, manualcode_params, qrcode_params) - - -def main(): - if len(sys.argv) == 2: - chip_tool = sys.argv[1] - run_tests(chip_tool) - - -if __name__ == '__main__': - main() diff --git a/src/setup_payload/tests/run_python_setup_payload_test.py b/src/setup_payload/tests/run_python_setup_payload_test.py new file mode 100644 index 00000000000000..da62a45c05fe97 --- /dev/null +++ b/src/setup_payload/tests/run_python_setup_payload_test.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2023 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 os +import re +import subprocess +import sys + +CHIP_TOPDIR = os.path.dirname(os.path.realpath(__file__))[:-len(os.path.join('src', 'setup_payload', 'tests'))] +sys.path.insert(0, os.path.join(CHIP_TOPDIR, 'src', 'setup_payload', 'python')) +from SetupPayload import CommissioningFlow, SetupPayload # noqa: E402 + + +def payload_param_dict(): + return { + 'Version': None, + 'VendorID': None, + 'ProductID': None, + 'Custom flow': None, + 'Discovery Bitmask': None, + 'Short discriminator': None, + 'Long discriminator': None, + 'Passcode': None + } + + +def remove_escape_sequence(data): + ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') + result = ansi_escape.sub('', data) + return result + + +def chip_tool_parse_setup_payload(chip_tool, payload): + cmd_args = [chip_tool, 'payload', 'parse-setup-payload', payload] + data = subprocess.check_output(cmd_args).decode('utf-8') + data = remove_escape_sequence(data) + parsed_params = payload_param_dict() + for key in parsed_params: + k_st = data.find(key) + if k_st == -1: + continue + + # 1 is for ":" + k_end = k_st + len(key) + 1 + + k_nl = data.find('\n', k_end) + parsed_params[key] = data[k_end:k_nl].split()[0] + + return parsed_params + + +def generate_payloads(in_params): + payloads = SetupPayload(in_params['Long discriminator'], in_params['Passcode'], + in_params['Discovery Bitmask'], CommissioningFlow(in_params['Custom flow']), + in_params['VendorID'], in_params['ProductID']) + manualcode = payloads.generate_manualcode() + qrcode = payloads.generate_qrcode() + return manualcode, qrcode + + +def verify_generated_payloads(in_params, manualcode_params, qrcode_params): + assert in_params['Version'] == int(manualcode_params['Version'], 0) + assert in_params['Passcode'] == int(manualcode_params['Passcode'], 0) + assert in_params['Short discriminator'] == int(manualcode_params['Short discriminator'], 0) + if in_params['Custom flow'] != 0: + assert in_params['VendorID'] == int(manualcode_params['VendorID'], 0) + assert in_params['ProductID'] == int(manualcode_params['ProductID'], 0) + + assert in_params['Version'] == int(qrcode_params['Version'], 0) + assert in_params['VendorID'] == int(qrcode_params['VendorID'], 0) + assert in_params['ProductID'] == int(qrcode_params['ProductID'], 0) + assert in_params['Custom flow'] == int(qrcode_params['Custom flow'], 0) + assert in_params['Discovery Bitmask'] == int(qrcode_params['Discovery Bitmask'], 0) + assert in_params['Passcode'] == int(qrcode_params['Passcode'], 0) + assert in_params['Long discriminator'] == int(qrcode_params['Long discriminator'], 0) + + +def get_payload_params(discriminator, passcode, discovery=4, flow=0, vid=0, pid=0, version=0, short_discriminator=None): + p = payload_param_dict() + p['Version'] = version + p['VendorID'] = vid + p['ProductID'] = pid + p['Custom flow'] = flow + p['Discovery Bitmask'] = discovery + p['Long discriminator'] = discriminator + p['Short discriminator'] = short_discriminator if short_discriminator is not None else (discriminator >> 8) + p['Passcode'] = passcode + return p + + +def test_code_generation(chip_tool): + test_data_set = [ + get_payload_params(3840, 20202021), + get_payload_params(3781, 12349876, flow=1, vid=1, pid=1), + get_payload_params(2310, 23005908, flow=2, vid=0xFFF3, pid=0x8098), + get_payload_params(3091, 43338551, discovery=2, flow=2, vid=0x1123, pid=0x0012), + get_payload_params(80, 54757432, discovery=6, flow=2, vid=0x2345, pid=0x1023), + get_payload_params(174, 81235604, discovery=7, flow=1, vid=0x45, pid=0x10), + ] + + for test_params in test_data_set: + manualcode, qrcode = generate_payloads(test_params) + manualcode_params = chip_tool_parse_setup_payload(chip_tool, manualcode) + qrcode_params = chip_tool_parse_setup_payload(chip_tool, qrcode) + + verify_generated_payloads(test_params, manualcode_params, qrcode_params) + + +def test_onboardingcode_parsing(): + # This test dataset is generated using `chip-tool payload parse-setup-payload ` + + test_data_set = [ + { + 'code': '34970112332', + 'res': get_payload_params(discriminator=None, passcode=20202021, discovery=None, + flow=0, vid=None, pid=None, version=0, short_discriminator=15), + }, + { + 'code': '745492075300001000013', + 'res': get_payload_params(discriminator=None, passcode=12349876, discovery=None, + flow=2, vid=1, pid=1, version=0, short_discriminator=14), + }, + { + 'code': '619156140465523329207', + 'res': get_payload_params(discriminator=None, passcode=23005908, discovery=None, + flow=2, vid=65523, pid=32920, version=0, short_discriminator=9), + }, + { + 'code': '702871264504387000187', + 'res': get_payload_params(discriminator=None, passcode=43338551, discovery=None, + flow=2, vid=4387, pid=18, version=0, short_discriminator=12), + }, + { + 'code': '402104334209029041311', + 'res': get_payload_params(discriminator=None, passcode=54757432, discovery=None, + flow=2, vid=9029, pid=4131, version=0, short_discriminator=0), + }, + { + 'code': '403732495800069000166', + 'res': get_payload_params(discriminator=None, passcode=81235604, discovery=None, + flow=2, vid=69, pid=16, version=0, short_discriminator=0), + }, + { + 'code': 'MT:U9VJ0OMV172PX813210', + 'res': get_payload_params(discriminator=3431, passcode=49910688, discovery=2, + flow=0, vid=4891, pid=2, version=0, short_discriminator=None), + }, + { + 'code': 'MT:00000CQM00KA0648G00', + 'res': get_payload_params(discriminator=3840, passcode=20202021, discovery=4, + flow=0, vid=0, pid=0, version=0, short_discriminator=None), + }, + { + 'code': 'MT:A3L90ARR15G6N57Y900', + 'res': get_payload_params(discriminator=3781, passcode=12349876, discovery=4, + flow=1, vid=1, pid=1, version=0, short_discriminator=None), + }, + { + 'code': 'MT:MZWA6G6026O2XP0II00', + 'res': get_payload_params(discriminator=2310, passcode=23005908, discovery=4, + flow=2, vid=65523, pid=32920, version=0, short_discriminator=None), + }, + { + 'code': 'MT:KSNK4M5113-JPR4UY00', + 'res': get_payload_params(discriminator=3091, passcode=43338551, discovery=2, + flow=2, vid=4387, pid=18, version=0, short_discriminator=12), + }, + { + 'code': 'MT:0A.T0P--00Y0OJ0.510', + 'res': get_payload_params(discriminator=80, passcode=54757432, discovery=6, + flow=2, vid=9029, pid=4131, version=0, short_discriminator=None), + }, + { + 'code': 'MT:EPX0482F26DAVY09R10', + 'res': get_payload_params(discriminator=174, passcode=81235604, discovery=7, + flow=1, vid=69, pid=16, version=0, short_discriminator=None), + }, + ] + + for test_payload in test_data_set: + payload = SetupPayload.parse(test_payload['code']) + + assert payload.long_discriminator == test_payload['res']['Long discriminator'] + assert payload.short_discriminator == test_payload['res']['Short discriminator'] + assert payload.pincode == test_payload['res']['Passcode'] + assert payload.discovery == test_payload['res']['Discovery Bitmask'] + assert payload.flow == test_payload['res']['Custom flow'] + assert payload.vid == test_payload['res']['VendorID'] + assert payload.pid == test_payload['res']['ProductID'] + + +def main(): + if len(sys.argv) == 2: + chip_tool = sys.argv[1] + test_code_generation(chip_tool) + test_onboardingcode_parsing() + + +if __name__ == '__main__': + main() diff --git a/src/system/SystemPacketBuffer.cpp b/src/system/SystemPacketBuffer.cpp index 59e1e822901db0..f8dda8b01ab84e 100644 --- a/src/system/SystemPacketBuffer.cpp +++ b/src/system/SystemPacketBuffer.cpp @@ -508,37 +508,64 @@ void PacketBuffer::AddRef() PacketBufferHandle PacketBufferHandle::New(size_t aAvailableSize, uint16_t aReservedSize) { - // Adding three 16-bit-int sized numbers together will never overflow - // assuming int is at least 32 bits. - static_assert(INT_MAX >= INT32_MAX, "int is not big enough"); + // Sanity check for kStructureSize to ensure that it matches the PacketBuffer size. static_assert(PacketBuffer::kStructureSize == sizeof(PacketBuffer), "PacketBuffer size mismatch"); - static_assert(PacketBuffer::kStructureSize < UINT16_MAX, "Check for overflow more carefully"); - static_assert(SIZE_MAX >= INT_MAX, "Our additions might not fit in size_t"); - static_assert(PacketBuffer::kMaxSizeWithoutReserve <= UINT32_MAX, "PacketBuffer may have size not fitting uint32_t"); + // Setting a static upper bound on kStructureSize to ensure the summation of all the sizes does not overflow. + static_assert(PacketBuffer::kStructureSize <= UINT16_MAX, "kStructureSize should not exceed UINT16_MAX."); + // Setting a static upper bound on the maximum buffer size allocation for regular sized messages (not large). + static_assert(PacketBuffer::kMaxSizeWithoutReserve <= UINT16_MAX, "kMaxSizeWithoutReserve should not exceed UINT16_MAX."); + + // Ensure that aAvailableSize is bound within a max and is not big enough to cause overflow during + // subsequent addition of all the sizes. + if (aAvailableSize > UINT32_MAX) + { + ChipLogError(chipSystemLayer, + "PacketBuffer: AvailableSize of a buffer cannot exceed UINT32_MAX. aAvailableSize = 0x" ChipLogFormatX64, + ChipLogValueX64(static_cast(aAvailableSize))); + return PacketBufferHandle(); + } + + // Cast all to uint64_t and add. This cannot overflow because we have + // ensured that the maximal value of the summation is + // UINT32_MAX + UINT16_MAX + UINT16_MAX, which should always fit in + // a uint64_t variable. + uint64_t sumOfSizes = static_cast(aAvailableSize) + static_cast(aReservedSize) + + static_cast(PacketBuffer::kStructureSize); + uint64_t sumOfAvailAndReserved = static_cast(aAvailableSize) + static_cast(aReservedSize); + + // Ensure that the sum fits in a size_t so that casting into size_t variables, + // viz., lBlockSize and lAllocSize, is safe. + if (!CanCastTo(sumOfSizes)) + { + ChipLogError(chipSystemLayer, + "PacketBuffer: Sizes of allocation request are invalid. (aAvailableSize = " ChipLogFormatX64 + ", aReservedSize = " ChipLogFormatX64 ")", + ChipLogValueX64(static_cast(aAvailableSize)), ChipLogValueX64(static_cast(aReservedSize))); + return PacketBufferHandle(); + } + #if CHIP_SYSTEM_CONFIG_USE_LWIP // LwIP based APIs have a maximum buffer size of UINT16_MAX. Ensure that // limit is met during allocation. - VerifyOrDieWithMsg(aAvailableSize + aReservedSize < UINT16_MAX, chipSystemLayer, - "LwIP based systems can handle only up to UINT16_MAX!"); + VerifyOrDieWithMsg(sumOfAvailAndReserved < UINT16_MAX, chipSystemLayer, "LwIP based systems can handle only up to UINT16_MAX!"); #endif // CHIP_SYSTEM_CONFIG_USE_LWIP - // When `aAvailableSize` fits in uint16_t (as tested below) and size_t is at least 32 bits (as asserted above), - // these additions will not overflow. - const size_t lAllocSize = aReservedSize + aAvailableSize; - const size_t lBlockSize = PacketBuffer::kStructureSize + lAllocSize; + // sumOfAvailAndReserved is no larger than sumOfSizes, which we checked can be cast to + // size_t. + const size_t lAllocSize = static_cast(sumOfAvailAndReserved); PacketBuffer * lPacket; CHIP_SYSTEM_FAULT_INJECT(FaultInjection::kFault_PacketBufferNew, return PacketBufferHandle()); - // TODO: Change the max to a lower value - if (aAvailableSize > UINT32_MAX || lAllocSize > PacketBuffer::kMaxSizeWithoutReserve || lBlockSize > UINT32_MAX) + if (lAllocSize > PacketBuffer::kMaxSizeWithoutReserve) { - ChipLogError(chipSystemLayer, "PacketBuffer: allocation too large."); + ChipLogError(chipSystemLayer, "PacketBuffer: allocation exceeding buffer capacity limits."); return PacketBufferHandle(); } #if CHIP_SYSTEM_CONFIG_USE_LWIP - + // This cast is safe because lAllocSize is no larger than + // kMaxSizeWithoutReserve, which fits in uint16_t. lPacket = static_cast( pbuf_alloc(PBUF_RAW, static_cast(lAllocSize), CHIP_SYSTEM_PACKETBUFFER_LWIP_PBUF_TYPE)); @@ -546,7 +573,6 @@ PacketBufferHandle PacketBufferHandle::New(size_t aAvailableSize, uint16_t aRese #elif CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_POOL - static_cast(lBlockSize); #if !CHIP_SYSTEM_CONFIG_NO_LOCKING && CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING if (!sBufferPoolMutex.isInitialized()) { @@ -565,8 +591,10 @@ PacketBufferHandle PacketBufferHandle::New(size_t aAvailableSize, uint16_t aRese UNLOCK_BUF_POOL(); #elif CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_HEAP - - lPacket = reinterpret_cast(chip::Platform::MemoryAlloc(lBlockSize)); + // sumOfSizes is essentially (kStructureSize + lAllocSize) which we already + // checked to fit in a size_t. + const size_t lBlockSize = static_cast(sumOfSizes); + lPacket = reinterpret_cast(chip::Platform::MemoryAlloc(lBlockSize)); SYSTEM_STATS_INCREMENT(chip::System::Stats::kSystemLayer_NumPacketBufs); #else diff --git a/src/test_driver/nrfconnect/main/runner.cpp b/src/test_driver/nrfconnect/main/runner.cpp index 1cd57d0bbbd406..d779f1d5300d09 100644 --- a/src/test_driver/nrfconnect/main/runner.cpp +++ b/src/test_driver/nrfconnect/main/runner.cpp @@ -35,8 +35,9 @@ extern "C" int main(void) VerifyOrDie(settings_subsys_init() == 0); LOG_INF("Starting CHIP tests!"); - int status = RunRegisteredUnitTests(); + int status = 0; status += chip::test::RunAllTests(); + status += RunRegisteredUnitTests(); LOG_INF("CHIP test status: %d", status); _exit(status); diff --git a/src/test_driver/openiotsdk/unit-tests/test_components.txt b/src/test_driver/openiotsdk/unit-tests/test_components.txt index 8c59caffcf8de4..35ba62bc492d6d 100644 --- a/src/test_driver/openiotsdk/unit-tests/test_components.txt +++ b/src/test_driver/openiotsdk/unit-tests/test_components.txt @@ -20,3 +20,4 @@ SecureChannelTests ICDServerTests DataModelTests InetLayerTests +MessagingLayerTests diff --git a/src/test_driver/openiotsdk/unit-tests/test_components_nl.txt b/src/test_driver/openiotsdk/unit-tests/test_components_nl.txt index 55ebc4e3cd7e7e..d57fda14efe12f 100644 --- a/src/test_driver/openiotsdk/unit-tests/test_components_nl.txt +++ b/src/test_driver/openiotsdk/unit-tests/test_components_nl.txt @@ -1,3 +1,2 @@ AppTests -MessagingLayerTests SecureChannelTestsNL diff --git a/src/transport/GroupSession.h b/src/transport/GroupSession.h index f20ff069ab029f..1c5ffecd2be3c9 100644 --- a/src/transport/GroupSession.h +++ b/src/transport/GroupSession.h @@ -75,6 +75,13 @@ class IncomingGroupSession : public Session, public ReferenceCounted