diff --git a/.github/workflows/examples-efr32.yaml b/.github/workflows/examples-efr32.yaml index ed6f5b45fa0d1b..c236b4ca3db4c3 100644 --- a/.github/workflows/examples-efr32.yaml +++ b/.github/workflows/examples-efr32.yaml @@ -40,7 +40,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-efr32:73 + image: ghcr.io/project-chip/chip-build-efr32:74 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" steps: diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index dbbda0aa36580f..d60ffb185de022 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -514,6 +514,7 @@ jobs: scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestIdChecks.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestSpecParsingDeviceType.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestConformanceSupport.py' + scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestConformanceTest.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestChoiceConformanceSupport.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_IDM_10_4.py' scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_TC_SC_7_1.py' diff --git a/.gitmodules b/.gitmodules index 0b015f8d14cd16..78a6cabb940d05 100644 --- a/.gitmodules +++ b/.gitmodules @@ -213,17 +213,17 @@ [submodule "third_party/silabs/simplicity_sdk"] path = third_party/silabs/simplicity_sdk url = https://github.com/SiliconLabs/simplicity_sdk.git - branch = v2024.6.0 + branch = v2024.6.1-0 platforms = silabs [submodule "third_party/silabs/wiseconnect-wifi-bt-sdk"] path = third_party/silabs/wiseconnect-wifi-bt-sdk url = https://github.com/SiliconLabs/wiseconnect-wifi-bt-sdk.git - branch = 2.8.2 + branch = 2.10.0 platforms = silabs [submodule "third_party/silabs/wifi_sdk"] path = third_party/silabs/wifi_sdk url = https://github.com/SiliconLabs/wiseconnect.git - branch = v3.3.0 + branch = v3.3.1 platforms = silabs [submodule "editline"] path = third_party/editline/repo diff --git a/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn b/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn index 7f2fbcbbfe0556..157032f8f93c19 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn +++ b/examples/fabric-bridge-app/fabric-bridge-common/BUILD.gn @@ -41,10 +41,12 @@ source_set("fabric-bridge-lib") { public_configs = [ ":config" ] sources = [ + "include/BridgedAdministratorCommissioning.h", "include/BridgedDevice.h", "include/BridgedDeviceBasicInformationImpl.h", "include/BridgedDeviceManager.h", "include/CHIPProjectAppConfig.h", + "src/BridgedAdministratorCommissioning.cpp", "src/BridgedDevice.cpp", "src/BridgedDeviceBasicInformationImpl.cpp", "src/BridgedDeviceManager.cpp", diff --git a/examples/fabric-bridge-app/fabric-bridge-common/include/BridgedAdministratorCommissioning.h b/examples/fabric-bridge-app/fabric-bridge-common/include/BridgedAdministratorCommissioning.h new file mode 100644 index 00000000000000..06fd9026d44225 --- /dev/null +++ b/examples/fabric-bridge-app/fabric-bridge-common/include/BridgedAdministratorCommissioning.h @@ -0,0 +1,58 @@ +/* + * 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 + +/** + * @brief CADMIN cluster implementation for handling attribute interactions of bridged device endpoints. + * + * The current Administrator Commissioning Cluster server's zap generated code will automatically + * register an Attribute Access Interface for the root node endpoint implementation. In order to + * properly respond to a read attribute for bridged devices we are representing, we override the + * currently registered Attribute Interface such that we are first to receive any read attribute + * request on Administrator Commissioning Cluster, and if it is not an endpoint for a device we + * are a bridge for we redirect to the default cluster server implementation of Administrator + * Commissioning Cluster. + */ +class BridgedAdministratorCommissioning : public chip::app::AttributeAccessInterface +{ +public: + // Register for the AdministratorCommissioning cluster on all endpoints. + BridgedAdministratorCommissioning() : + AttributeAccessInterface(chip::NullOptional, chip::app::Clusters::AdministratorCommissioning::Id) + {} + + CHIP_ERROR Init(); + + CHIP_ERROR Read(const chip::app::ConcreteReadAttributePath & aPath, chip::app::AttributeValueEncoder & aEncoder) override; + + // We do not allow writing to CADMIN attributes of a bridged device endpoint. We simply redirect + // write requests to the original attribute interface. + CHIP_ERROR Write(const chip::app::ConcreteDataAttributePath & aPath, chip::app::AttributeValueDecoder & aDecoder) override + { + VerifyOrDie(mOriginalAttributeInterface); + return mOriginalAttributeInterface->Write(aPath, aDecoder); + } + +private: + // If mOriginalAttributeInterface is removed from here, the class description needs to be updated + // to reflect this change. + chip::app::AttributeAccessInterface * mOriginalAttributeInterface = nullptr; +}; diff --git a/examples/fabric-bridge-app/fabric-bridge-common/include/BridgedDevice.h b/examples/fabric-bridge-app/fabric-bridge-common/include/BridgedDevice.h index 3dab8d3b16b2d6..7081278f4dc0f3 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/include/BridgedDevice.h +++ b/examples/fabric-bridge-app/fabric-bridge-common/include/BridgedDevice.h @@ -18,6 +18,7 @@ #pragma once +#include #include #include @@ -40,6 +41,14 @@ class BridgedDevice std::string softwareVersionString; }; + struct AdminCommissioningAttributes + { + chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum commissioningWindowStatus = + chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen; + std::optional openerFabricIndex = std::nullopt; + std::optional openerVendorId = std::nullopt; + }; + BridgedDevice(chip::NodeId nodeId); virtual ~BridgedDevice() = default; @@ -59,6 +68,8 @@ class BridgedDevice [[nodiscard]] const BridgedAttributes & GetBridgedAttributes() const { return mAttributes; } void SetBridgedAttributes(const BridgedAttributes & value) { mAttributes = value; } + // TODO(#35077): Need to allow mAdminCommissioningAttributes to be set from fabric-admin. + const AdminCommissioningAttributes & GetAdminCommissioningAttributes() const { return mAdminCommissioningAttributes; } /// Convenience method to set just the unique id of a bridged device as it /// is one of the few attributes that is not always bulk-set @@ -73,4 +84,5 @@ class BridgedDevice chip::EndpointId mParentEndpointId = 0; BridgedAttributes mAttributes; + AdminCommissioningAttributes mAdminCommissioningAttributes; }; diff --git a/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedAdministratorCommissioning.cpp b/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedAdministratorCommissioning.cpp new file mode 100644 index 00000000000000..a0d87cbb3b81ef --- /dev/null +++ b/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedAdministratorCommissioning.cpp @@ -0,0 +1,81 @@ +/* + * 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 "BridgedAdministratorCommissioning.h" + +#include "BridgedDevice.h" +#include "BridgedDeviceManager.h" +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::AdministratorCommissioning; + +CHIP_ERROR BridgedAdministratorCommissioning::Init() +{ + // We expect initialization after emberAfInit(). This allows us to unregister the existing + // AccessAttributeInterface for AdministratorCommissioning and register ourselves, ensuring we + // get the callback for reading attribute. If the read is not intended for a bridged device we will + // forward it to the original attribute interface that we are unregistering. + mOriginalAttributeInterface = AttributeAccessInterfaceRegistry::Instance().Get(kRootEndpointId, AdministratorCommissioning::Id); + VerifyOrReturnError(mOriginalAttributeInterface, CHIP_ERROR_INTERNAL); + AttributeAccessInterfaceRegistry::Instance().Unregister(mOriginalAttributeInterface); + VerifyOrDie(AttributeAccessInterfaceRegistry::Instance().Register(this)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR BridgedAdministratorCommissioning::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + VerifyOrDie(aPath.mClusterId == Clusters::AdministratorCommissioning::Id); + EndpointId endpointId = aPath.mEndpointId; + BridgedDevice * device = BridgeDeviceMgr().GetDevice(endpointId); + + if (!device) + { + VerifyOrDie(mOriginalAttributeInterface); + return mOriginalAttributeInterface->Read(aPath, aEncoder); + } + auto attr = device->GetAdminCommissioningAttributes(); + + switch (aPath.mAttributeId) + { + case Attributes::WindowStatus::Id: { + return aEncoder.Encode(attr.commissioningWindowStatus); + } + case Attributes::AdminFabricIndex::Id: { + DataModel::Nullable encodeableFabricIndex = DataModel::NullNullable; + if (attr.openerFabricIndex.has_value()) + { + encodeableFabricIndex.SetNonNull(attr.openerFabricIndex.value()); + } + return aEncoder.Encode(encodeableFabricIndex); + } + case Attributes::AdminVendorId::Id: { + DataModel::Nullable encodeableVendorId = DataModel::NullNullable; + if (attr.openerVendorId.has_value()) + { + encodeableVendorId.SetNonNull(attr.openerVendorId.value()); + } + return aEncoder.Encode(encodeableVendorId); + } + default: + break; + } + + return CHIP_NO_ERROR; +} diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp index 3b6502421045f8..eeb290210caa6b 100644 --- a/examples/fabric-bridge-app/linux/main.cpp +++ b/examples/fabric-bridge-app/linux/main.cpp @@ -18,6 +18,7 @@ #include +#include "BridgedAdministratorCommissioning.h" #include "BridgedDevice.h" #include "BridgedDeviceBasicInformationImpl.h" #include "BridgedDeviceManager.h" @@ -234,6 +235,7 @@ void BridgedDeviceInformationCommandHandler::InvokeCommand(HandlerContext & hand handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, status); } +BridgedAdministratorCommissioning gBridgedAdministratorCommissioning; AdministratorCommissioningCommandHandler gAdministratorCommissioningCommandHandler; BridgedDeviceInformationCommandHandler gBridgedDeviceInformationCommandHandler; @@ -258,6 +260,7 @@ void ApplicationInit() pollingThread.detach(); BridgeDeviceMgr().Init(); + VerifyOrDie(gBridgedAdministratorCommissioning.Init() == CHIP_NO_ERROR); VerifyOrDieWithMsg(CommissionerControlInit() == CHIP_NO_ERROR, NotSpecified, "Failed to initialize Commissioner Control Server"); diff --git a/examples/platform/silabs/FreeRTOSConfig.h b/examples/platform/silabs/FreeRTOSConfig.h index d2805c40654632..b27c775d9b7a54 100644 --- a/examples/platform/silabs/FreeRTOSConfig.h +++ b/examples/platform/silabs/FreeRTOSConfig.h @@ -150,7 +150,7 @@ extern uint32_t SystemCoreClock; #define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 70 #define configPRE_SLEEP_PROCESSING(x) #define configPOST_SLEEP_PROCESSING(x) -#define configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING(x) sl_si91x_pre_supress_ticks_and_sleep(&x) +#define configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING(x) #else #define configUSE_TICKLESS_IDLE 0 #endif // SL_CATALOG_POWER_MANAGER_PRESENT diff --git a/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp b/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp index bfdedbfe82bb13..03b51540cf4b6d 100644 --- a/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp +++ b/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp @@ -15,6 +15,10 @@ * limitations under the License. */ +/* + * This file implements the interface to the wifi sdk + */ + #include #include #include @@ -23,38 +27,44 @@ #include "sl_matter_wifi_config.h" #endif // SL_MATTER_GN_BUILD +#include "FreeRTOS.h" +#include "ble_config.h" +#include "dhcp_client.h" +#include "event_groups.h" +#include "sl_board_configuration.h" #include "sl_status.h" +#include "task.h" +#include "wfx_host_events.h" +#include "wfx_rsi.h" #include #include #include -#include "FreeRTOS.h" -#include "event_groups.h" -#include "sl_board_configuration.h" extern "C" { +#include "sl_net.h" +#include "sl_si91x_driver.h" +#include "sl_si91x_host_interface.h" #include "sl_si91x_types.h" +#include "sl_wifi.h" +#include "sl_wifi_callback_framework.h" #include "sl_wifi_constants.h" #include "sl_wifi_types.h" #include "sl_wlan_config.h" +#include "wfx_host_events.h" +#if SL_MBEDTLS_USE_TINYCRYPT +#include "sl_si91x_constants.h" +#include "sl_si91x_trng.h" +#endif // SL_MBEDTLS_USE_TINYCRYPT } -#include "task.h" #if (EXP_BOARD) #include "rsi_bt_common_apis.h" #endif -#include "ble_config.h" - #if CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI91X_MCU_INTERFACE #include "rsi_rom_power_save.h" #include "sl_si91x_button_pin_config.h" -#if DISPLAY_ENABLED -#include "sl_memlcd.h" -#endif // DISPLAY_ENABLED -extern "C" { -#include "sl_si91x_driver.h" -#include "sl_si91x_m4_ps.h" -} +#include "sl_si91x_power_manager.h" namespace { // TODO: should be removed once we are getting the press interrupt for button 0 with sleep @@ -67,9 +77,6 @@ bool ps_requirement_added = false; } // namespace #endif // CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI91X_MCU_INTERFACE -#include "dhcp_client.h" -#include "wfx_host_events.h" -#include "wfx_rsi.h" #define ADV_SCAN_THRESHOLD -40 #define ADV_RSSI_TOLERANCE_THRESHOLD 5 #define ADV_ACTIVE_SCAN_DURATION 15 @@ -79,17 +86,9 @@ bool ps_requirement_added = false; // TODO: Confirm that this value works for size and timing #define WFX_QUEUE_SIZE 10 -extern "C" { -#include "sl_net.h" -#include "sl_si91x_host_interface.h" -#include "sl_wifi.h" -#include "sl_wifi_callback_framework.h" -#include "wfx_host_events.h" -#if SL_MBEDTLS_USE_TINYCRYPT -#include "sl_si91x_constants.h" -#include "sl_si91x_trng.h" -#endif // SL_MBEDTLS_USE_TINYCRYPT -} + +// TODO: Figure out why we actually need this, we are already handling failure and retries somewhere else. +#define WIFI_SCAN_TIMEOUT_TICK 10000 WfxRsi_t wfx_rsi; @@ -109,20 +108,9 @@ bool is_wifi_disconnection_event = false; uint32_t retryInterval = WLAN_MIN_RETRY_TIMER_MS; volatile bool scan_results_complete = false; volatile bool bg_scan_results_complete = false; - -// TODO: Figure out why we actually need this, we are already handling failure and retries somewhere else. -#define WIFI_SCAN_TIMEOUT_TICK 10000 - extern osSemaphoreId_t sl_rs_ble_init_sem; - -/* - * This file implements the interface to the wifi sdk - */ - static wfx_wifi_scan_ext_t temp_reset; - volatile sl_status_t callback_status = SL_STATUS_OK; - // Scan semaphore static osSemaphoreId_t sScanSemaphore; // DHCP Poll timer @@ -319,28 +307,6 @@ void sl_si91x_invoke_btn_press_event() } #endif // ENABLE_CHIP_SHELL } - -/****************************************************************** - * @fn sl_app_sleep_ready() - * @brief - * Called from the supress ticks from tickless to check if it - * is ok to go to sleep - * @param[in] None - * @return - * None - *********************************************************************/ -uint32_t sl_app_sleep_ready() -{ - if (wfx_rsi.dev_state & WFX_RSI_ST_SLEEP_READY) - { -#if DISPLAY_ENABLED - // Powering down the LCD - sl_memlcd_power_on(NULL, false); -#endif /* DISPLAY_ENABLED */ - return true; - } - return false; -} #endif // SLI_SI91X_MCU_INTERFACE /****************************************************************** @@ -370,14 +336,7 @@ int32_t wfx_rsi_power_save(rsi_power_save_profile_mode_t sl_si91x_ble_state, sl_ ChipLogError(DeviceLayer, "sl_wifi_set_performance_profile failed: 0x%lx", static_cast(status)); return status; } - if (sl_si91x_wifi_state == HIGH_PERFORMANCE) - { - wfx_rsi.dev_state &= ~(WFX_RSI_ST_SLEEP_READY); - } - else - { - wfx_rsi.dev_state |= WFX_RSI_ST_SLEEP_READY; - } + return status; } #endif /* CHIP_CONFIG_ENABLE_ICD_SERVER */ diff --git a/examples/platform/silabs/display/demo-ui.c b/examples/platform/silabs/display/demo-ui.c index 479af92fd5bb90..e0fe37ec9cb09b 100644 --- a/examples/platform/silabs/display/demo-ui.c +++ b/examples/platform/silabs/display/demo-ui.c @@ -25,6 +25,7 @@ #include "glib.h" #include "sl_component_catalog.h" #include "sl_memlcd.h" +#include #if SL_WIFI && !SLI_SI91X_MCU_INTERFACE #include "spi_multiplex.h" #endif // SL_WIFI && !SLI_SI91X_MCU_INTERFACE @@ -106,9 +107,12 @@ void demoUIInit(GLIB_Context_t * context) sl_status_t updateDisplay(void) { sl_status_t status = SL_STATUS_OK; -#if SLI_SI91X_MCU_INTERFACE && SL_ICD_ENABLED && DISPLAY_ENABLED + +#if SLI_SI91X_MCU_INTERFACE && CHIP_CONFIG_ENABLE_ICD_SERVER + // In sleep, memlcd will not be retained so re-initialize MEMLCD interface after sleep wakeup sl_memlcd_post_wakeup_init(); -#endif // SLI_SI91X_MCU_INTERFACE && SL_ICD_ENABLED && DISPLAY_ENABLED +#endif // SLI_SI91X_MCU_INTERFACE && SL_ICD_ENABLED && CHIP_CONFIG_ENABLE_ICD_SERVER + #if SL_LCDCTRL_MUX status = sl_wfx_host_pre_lcd_spi_transfer(); if (status != SL_STATUS_OK) @@ -123,6 +127,12 @@ sl_status_t updateDisplay(void) return status; #endif // SL_LCDCTRL_MUX +#if SLI_SI91X_MCU_INTERFACE && CHIP_CONFIG_ENABLE_ICD_SERVER + // MEMLCD is not a UULP component and not available in sleep so powering down before sleep and need to be re-initialized after + // sleep-wakeup + sl_memlcd_power_on(NULL, false); +#endif // SLI_SI91X_MCU_INTERFACE && CHIP_CONFIG_ENABLE_ICD_SERVER + return SL_STATUS_OK; } diff --git a/examples/platform/silabs/efr32/rs911x/rsi_if.c b/examples/platform/silabs/efr32/rs911x/rsi_if.c index a08e2a2f292de1..e4d6f51bf0ded9 100644 --- a/examples/platform/silabs/efr32/rs911x/rsi_if.c +++ b/examples/platform/silabs/efr32/rs911x/rsi_if.c @@ -511,9 +511,9 @@ static void wfx_rsi_save_ap_info() // translation case SME_WEP: wfx_rsi.sec.security = WFX_SEC_WEP; break; - case SME_WPA3_TRANSITION: + case SME_WPA3_PERSONAL_TRANSITION: #if WIFI_ENABLE_SECURITY_WPA3_TRANSITION - case SME_WPA3: + case SME_WPA3_PERSONAL: wfx_rsi.sec.security = WFX_SEC_WPA3; #else wfx_rsi.sec.security = WFX_SEC_WPA2; @@ -557,7 +557,7 @@ static void wfx_rsi_do_join(void) break; #if WIFI_ENABLE_SECURITY_WPA3_TRANSITION case WFX_SEC_WPA3: - connect_security_mode = RSI_WPA3_TRANSITION; + connect_security_mode = RSI_WPA3_PERSONAL_TRANSITION; break; #endif // WIFI_ENABLE_SECURITY_WPA3_TRANSITION case WFX_SEC_NONE: diff --git a/examples/platform/silabs/provision/ProvisionStorageDefault.cpp b/examples/platform/silabs/provision/ProvisionStorageDefault.cpp index 1162323cda3d51..801eb9e0b70c2e 100644 --- a/examples/platform/silabs/provision/ProvisionStorageDefault.cpp +++ b/examples/platform/silabs/provision/ProvisionStorageDefault.cpp @@ -90,7 +90,7 @@ size_t RoundNearest(size_t n, size_t multiple) CHIP_ERROR WriteFile(Storage & store, SilabsConfig::Key offset_key, SilabsConfig::Key size_key, const ByteSpan & value) { uint32_t base_addr = 0; - ReturnErrorOnFailure(store.GetBaseAddress(base_addr)); + ReturnErrorOnFailure(store.GetCredentialsBaseAddress(base_addr)); if (0 == sCredentialsOffset) { ReturnErrorOnFailure(ErasePage(base_addr)); @@ -120,7 +120,7 @@ CHIP_ERROR WriteFile(Storage & store, SilabsConfig::Key offset_key, SilabsConfig CHIP_ERROR ReadFileByOffset(Storage & store, const char * description, uint32_t offset, uint32_t size, MutableByteSpan & value) { uint32_t base_addr = 0; - ReturnErrorOnFailure(store.GetBaseAddress(base_addr)); + ReturnErrorOnFailure(store.GetCredentialsBaseAddress(base_addr)); uint8_t * address = (uint8_t *) (base_addr + offset); ByteSpan span(address, size); @@ -167,12 +167,7 @@ CHIP_ERROR Storage::Initialize(uint32_t flash_addr, uint32_t flash_size) setNvm3End(base_addr); #endif } - return SilabsConfig::WriteConfigValue(SilabsConfig::kConfigKey_Creds_Base_Addr, base_addr); -} - -CHIP_ERROR Storage::GetBaseAddress(uint32_t & value) -{ - return SilabsConfig::ReadConfigValue(SilabsConfig::kConfigKey_Creds_Base_Addr, value); + return SetCredentialsBaseAddress(base_addr); } CHIP_ERROR Storage::Commit() @@ -625,6 +620,16 @@ CHIP_ERROR Storage::SignWithDeviceAttestationKey(const ByteSpan & message, Mutab // Other // +CHIP_ERROR Storage::SetCredentialsBaseAddress(uint32_t addr) +{ + return SilabsConfig::WriteConfigValue(SilabsConfig::kConfigKey_Creds_Base_Addr, addr); +} + +CHIP_ERROR Storage::GetCredentialsBaseAddress(uint32_t & addr) +{ + return SilabsConfig::ReadConfigValue(SilabsConfig::kConfigKey_Creds_Base_Addr, addr); +} + CHIP_ERROR Storage::SetProvisionVersion(const char * value, size_t size) { return SilabsConfig::WriteConfigValueStr(SilabsConfig::kConfigKey_Provision_Version, value, size); diff --git a/examples/platform/silabs/provision/ProvisionStorageFlash.cpp b/examples/platform/silabs/provision/ProvisionStorageFlash.cpp index 35a32d6af8f5ec..af59d6560cd0fc 100644 --- a/examples/platform/silabs/provision/ProvisionStorageFlash.cpp +++ b/examples/platform/silabs/provision/ProvisionStorageFlash.cpp @@ -296,12 +296,6 @@ CHIP_ERROR Storage::Commit() return CHIP_NO_ERROR; } -CHIP_ERROR Storage::GetBaseAddress(uint32_t & value) -{ - value = (uint32_t) Flash::sReadOnlyPage; - return CHIP_NO_ERROR; -} - // // DeviceInstanceInfoProvider // @@ -675,6 +669,18 @@ CHIP_ERROR Storage::SignWithDeviceAttestationKey(const ByteSpan & message, Mutab // Other // +CHIP_ERROR Storage::SetCredentialsBaseAddress(uint32_t addr) +{ + Flash::sReadOnlyPage = (uint8_t *) addr; + return CHIP_NO_ERROR; +} + +CHIP_ERROR Storage::GetCredentialsBaseAddress(uint32_t & addr) +{ + addr = (uint32_t) Flash::sReadOnlyPage; + return CHIP_NO_ERROR; +} + CHIP_ERROR Storage::SetProvisionVersion(const char * value, size_t size) { return Flash::Set(Parameters::ID::kVersion, value, size); diff --git a/examples/platform/silabs/wfx_rsi.h b/examples/platform/silabs/wfx_rsi.h index c47a4ee0af251e..c559e1e7610880 100644 --- a/examples/platform/silabs/wfx_rsi.h +++ b/examples/platform/silabs/wfx_rsi.h @@ -62,7 +62,6 @@ typedef enum WFX_RSI_ST_STA_READY = (WFX_RSI_ST_STA_CONNECTED | WFX_RSI_ST_STA_DHCP_DONE), WFX_RSI_ST_STARTED = (1 << 9), /* RSI task started */ WFX_RSI_ST_SCANSTARTED = (1 << 10), /* Scan Started */ - WFX_RSI_ST_SLEEP_READY = (1 << 11) /* Notify the M4 to go to sleep*/ } WfxStateType_e; typedef struct WfxEvent_s diff --git a/src/app/clusters/service-area-server/service-area-cluster-objects.h b/src/app/clusters/service-area-server/service-area-cluster-objects.h index 1b040c53cdc1bf..66f20720701587 100644 --- a/src/app/clusters/service-area-server/service-area-cluster-objects.h +++ b/src/app/clusters/service-area-server/service-area-cluster-objects.h @@ -110,9 +110,9 @@ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs:: { areaDesc.locationInfo.SetNonNull(); // Copy the name - auto sizeToCopy = std::min(sizeof(mAreaNameBuffer), locationName.size()); - memcpy(mAreaNameBuffer, locationName.data(), sizeToCopy); - areaDesc.locationInfo.Value().locationName = CharSpan(mAreaNameBuffer, sizeToCopy); + auto areaNameSpan = MutableCharSpan(mAreaNameBuffer, kAreaNameMaxSize); + CopyCharSpanToMutableCharSpan(locationName, areaNameSpan); + areaDesc.locationInfo.Value().locationName = CharSpan(areaNameSpan.data(), areaNameSpan.size()); areaDesc.locationInfo.Value().floorNumber = floorNumber; areaDesc.locationInfo.Value().areaType = areaType; @@ -320,24 +320,10 @@ struct MapStructureWrapper : public chip::app::Clusters::ServiceArea::Structs::M */ void Set(uint32_t aMapId, const CharSpan & aMapName) { - mapID = aMapId; - - if (aMapName.empty()) - { - name = CharSpan(mMapNameBuffer, 0); - } - else if (aMapName.size() > sizeof(mMapNameBuffer)) - { - // Save the truncated name that fits into available size. - memcpy(mMapNameBuffer, aMapName.data(), sizeof(mMapNameBuffer)); - name = CharSpan(mMapNameBuffer, sizeof(mMapNameBuffer)); - } - else - { - // Save full name. - memcpy(mMapNameBuffer, aMapName.data(), aMapName.size()); - name = CharSpan(mMapNameBuffer, aMapName.size()); - } + mapID = aMapId; + auto mapNameSpan = MutableCharSpan(mMapNameBuffer, kMapNameMaxSize); + CopyCharSpanToMutableCharSpan(aMapName, mapNameSpan); + name = CharSpan(mapNameSpan.data(), mapNameSpan.size()); } /** diff --git a/src/app/clusters/service-area-server/service-area-server.cpp b/src/app/clusters/service-area-server/service-area-server.cpp index a4c132df8a6e4d..baf56cd4fc84be 100644 --- a/src/app/clusters/service-area-server/service-area-server.cpp +++ b/src/app/clusters/service-area-server/service-area-server.cpp @@ -378,15 +378,6 @@ void Instance::HandleSkipAreaCmd(HandlerContext & ctx, const Commands::SkipArea: return; } - // If the CurrentArea attribute is null, the status should be set to InvalidInMode. - // If the Status field is not set to Success, or InvalidAreaList, the StatusText field SHALL include a vendor defined error - // description. - if (mCurrentArea.IsNull()) - { - exitResponse(SkipAreaStatus::kInvalidInMode, "Current Area attribute is null"_span); - return; - } - // have the device attempt to skip // If the Status field is not set to Success, or InvalidAreaList, the StatusText field SHALL include a vendor defined error // description. InvalidInMode | The received request cannot be handled due to the current mode of the device. (skipStatusText to diff --git a/src/darwin/Darwin.xcworkspace/xcshareddata/IDETemplateMacros.plist b/src/darwin/Darwin.xcworkspace/xcshareddata/IDETemplateMacros.plist index 861a74eaf635b0..03de09ad225a13 100644 --- a/src/darwin/Darwin.xcworkspace/xcshareddata/IDETemplateMacros.plist +++ b/src/darwin/Darwin.xcworkspace/xcshareddata/IDETemplateMacros.plist @@ -3,9 +3,8 @@ FILEHEADER - -/** - * Copyright (c) 2023 Project CHIP Authors + /** + * 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. diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index d8fc52b1a2fc6e..4fc30b90b3e433 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -64,43 +64,6 @@ // Disabling pending crashes #define ENABLE_CONNECTIVITY_MONITORING 0 -// Consider moving utility classes to their own file -#pragma mark - Utility Classes - -// container of MTRDevice delegate weak reference, its queue, and its interested paths for attribute reports -MTR_DIRECT_MEMBERS -@interface MTRDeviceDelegateInfo : NSObject { -@private - void * _delegatePointerValue; - __weak id _delegate; - dispatch_queue_t _queue; - NSArray * _Nullable _interestedPathsForAttributes; - NSArray * _Nullable _interestedPathsForEvents; -} - -// Array of interested cluster paths, attribute paths, or endpointID, for attribute report filtering. -@property (readonly, nullable) NSArray * interestedPathsForAttributes; - -// Array of interested cluster paths, attribute paths, or endpointID, for event report filtering. -@property (readonly, nullable) NSArray * interestedPathsForEvents; - -// Expose delegate -@property (readonly) id delegate; - -// Pointer value for logging purpose only -@property (readonly) void * delegatePointerValue; - -- (instancetype)initWithDelegate:(id)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents; - -// Returns YES if delegate and queue are both non-null, and the block is scheduled to run. -- (BOOL)callDelegateWithBlock:(void (^)(id))block; - -#ifdef DEBUG -// Only used for unit test purposes - normal delegate should not expect or handle being called back synchronously. -- (BOOL)callDelegateSynchronouslyWithBlock:(void (^)(id))block; -#endif -@end - @implementation MTRDeviceDelegateInfo - (instancetype)initWithDelegate:(id)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents { @@ -371,7 +334,6 @@ - (BOOL)isEqual:(id)object #define MTRDEVICE_SUBSCRIPTION_LATENCY_NEW_VALUE_WEIGHT (1.0 / 3.0) @interface MTRDevice () -@property (nonatomic, readonly) os_unfair_lock lock; // protects the caches and device state // protects against concurrent time updates by guarding timeUpdateScheduled flag which manages time updates scheduling, // and protects device calls to setUTCTime and setDSTOffset. This can't just be replaced with "lock", because the time // update code calls public APIs like readAttributeWithEndpointID:.. (which attempt to take "lock") while holding @@ -502,8 +464,6 @@ @implementation MTRDevice { // System time change observer reference id _systemTimeChangeObserverToken; - NSMutableSet * _delegates; - // Protects mutable state used by our description getter. This is a separate lock from "lock" // so that we don't need to worry about getting our description while holding "lock" (e.g due to // logging self). This lock _must_ be held narrowly, with no other lock acquisitions allowed @@ -526,10 +486,13 @@ @implementation MTRDevice { NSDate * _Nullable _lastSubscriptionFailureTimeForDescription; } -- (instancetype)initForSubclasses +- (instancetype)initForSubclassesWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller { if (self = [super init]) { - // nothing, as superclass of MTRDevice is NSObject + _lock = OS_UNFAIR_LOCK_INIT; + _delegates = [NSMutableSet set]; + _deviceController = controller; + _nodeID = nodeID; } return self; @@ -875,6 +838,8 @@ - (BOOL)_subscriptionsAllowed { os_unfair_lock_assert_owner(&self->_lock); + // TODO: XPC: This function and all its callsites should go away from this class. + // We should not allow a subscription for device controllers over XPC. return ![_deviceController isKindOfClass:MTRDeviceControllerOverXPC.class]; } @@ -923,34 +888,14 @@ - (void)_addDelegate:(id)delegate queue:(dispatch_queue_t)que [_delegates addObject:newDelegateInfo]; MTR_LOG("%@ added delegate info %@", self, newDelegateInfo); - __block BOOL shouldSetUpSubscription = [self _subscriptionsAllowed]; - - // 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 - [self _callFirstDelegateSynchronouslyWithBlock:^(id testDelegate) { - if ([testDelegate respondsToSelector:@selector(unitTestShouldSetUpSubscriptionForDevice:)]) { - shouldSetUpSubscription = [testDelegate unitTestShouldSetUpSubscriptionForDevice:self]; - } - }]; -#endif + // Call hook to allow subclasses to act on delegate addition. + [self _delegateAdded]; +} - if (shouldSetUpSubscription) { - MTR_LOG("%@ - starting subscription setup", self); - // Record the time of first addDelegate call that triggers initial subscribe, and do not reset this value on subsequent addDelegate calls - if (!_initialSubscribeStart) { - _initialSubscribeStart = [NSDate now]; - } - if ([self _deviceUsesThread]) { - MTR_LOG(" => %@ - device is a thread device, scheduling in pool", self); - [self _scheduleSubscriptionPoolWork:^{ - std::lock_guard lock(self->_lock); - [self _setupSubscriptionWithReason:@"delegate is set and scheduled subscription is happening"]; - } inNanoseconds:0 description:@"MTRDevice setDelegate first subscription"]; - } else { - [self _setupSubscriptionWithReason:@"delegate is set and subscription is needed"]; - } - } +- (void)_delegateAdded +{ + // Nothing to do; this is a hook for subclasses. If that ever changes for + // some reason, subclasses need to start calling this hook on their super. } - (void)removeDelegate:(id)delegate @@ -962,7 +907,10 @@ - (void)removeDelegate:(id)delegate NSMutableSet * delegatesToRemove = [NSMutableSet set]; [self _iterateDelegatesWithBlock:^(MTRDeviceDelegateInfo * delegateInfo) { id strongDelegate = delegateInfo.delegate; - if (strongDelegate == delegate) { + if (!strongDelegate) { + [delegatesToRemove addObject:delegateInfo]; + MTR_LOG("%@ removing delegate info for nil delegate %p", self, delegateInfo.delegatePointerValue); + } else if (strongDelegate == delegate) { [delegatesToRemove addObject:delegateInfo]; MTR_LOG("%@ removing delegate info %@ for %p", self, delegateInfo, delegate); } @@ -976,45 +924,9 @@ - (void)removeDelegate:(id)delegate - (void)invalidate { - MTR_LOG("%@ invalidate", self); - - [_asyncWorkQueue invalidate]; - - os_unfair_lock_lock(&self->_timeSyncLock); - _timeUpdateScheduled = NO; - os_unfair_lock_unlock(&self->_timeSyncLock); - - os_unfair_lock_lock(&self->_lock); - - _state = MTRDeviceStateUnknown; + std::lock_guard lock(_lock); [_delegates removeAllObjects]; - - // Make sure we don't try to resubscribe if we have a pending resubscribe - // attempt, since we now have no delegate. - _reattemptingSubscription = NO; - - [_deviceController asyncDispatchToMatterQueue:^{ - MTR_LOG("%@ invalidate disconnecting ReadClient and SubscriptionCallback", self); - - // Destroy the read client and callback (has to happen on the Matter - // queue, to avoid deleting objects that are being referenced), to - // tear down the subscription. We will get no more callbacks from - // the subscription after this point. - std::lock_guard lock(self->_lock); - self->_currentReadClient = nullptr; - if (self->_currentSubscriptionCallback) { - delete self->_currentSubscriptionCallback; - } - self->_currentSubscriptionCallback = nullptr; - - [self _changeInternalState:MTRInternalDeviceStateUnsubscribed]; - } - errorHandler:nil]; - - [self _stopConnectivityMonitoring]; - - os_unfair_lock_unlock(&self->_lock); } - (void)nodeMayBeAdvertisingOperational @@ -1145,8 +1057,7 @@ - (BOOL)_delegateExists return [self _iterateDelegatesWithBlock:nil]; } -// Returns YES if any non-null delegates were found -- (BOOL)_iterateDelegatesWithBlock:(void(NS_NOESCAPE ^)(MTRDeviceDelegateInfo * delegateInfo)_Nullable)block +- (BOOL)_iterateDelegatesWithBlock:(void(NS_NOESCAPE ^ _Nullable)(MTRDeviceDelegateInfo * delegateInfo))block { os_unfair_lock_assert_owner(&self->_lock); @@ -1196,9 +1107,14 @@ - (BOOL)_callDelegatesWithBlock:(void (^)(id delegate))block return (delegatesCalled > 0); } +- (BOOL)_lockAndCallDelegatesWithBlock:(void (^)(id delegate))block +{ + std::lock_guard lock(self->_lock); + return [self _callDelegatesWithBlock:block]; +} + #ifdef DEBUG // Only used for unit test purposes - normal delegate should not expect or handle being called back synchronously -// Returns YES if a delegate is called - (void)_callFirstDelegateSynchronouslyWithBlock:(void (^)(id delegate))block { os_unfair_lock_assert_owner(&self->_lock); @@ -2496,6 +2412,8 @@ - (void)unitTestResetSubscription // assume lock is held - (void)_setupSubscriptionWithReason:(NSString *)reason { + // TODO: XPC: This is not really called anymore in this class. Should + // remove this function and anything only reachable from it. os_unfair_lock_assert_owner(&self->_lock); if (![self _subscriptionsAllowed]) { diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index 322593312ccea4..11fd481b48b2fc 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -109,9 +109,6 @@ using namespace chip::Tracing::DarwinFramework; @implementation MTRDeviceController { - // queue used to serialize all work performed by the MTRDeviceController - dispatch_queue_t _chipWorkQueue; - chip::Controller::DeviceCommissioner * _cppCommissioner; chip::Credentials::PartialDACVerifier * _partialDACVerifier; chip::Credentials::DefaultDACVerifier * _defaultDACVerifier; diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h index 85d2c2e069ee2c..54d5cfd8d340fa 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h @@ -68,6 +68,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readwrite, nullable) NSMapTable * nodeIDToDeviceMap; @property (readonly, assign) os_unfair_lock_t deviceMapLock; +// queue used to serialize all work performed by the MTRDeviceController +// (moved here so subclasses can initialize differently) +@property (readwrite, retain) dispatch_queue_t chipWorkQueue; + - (instancetype)initForSubclasses; #pragma mark - MTRDeviceControllerFactory methods diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm b/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm index 157a38199f025e..31acadcce576d3 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm @@ -43,6 +43,26 @@ @implementation MTRDeviceController_XPC @synthesize uniqueIdentifier = _uniqueIdentifier; +- (NSXPCInterface *)_interfaceForClientProtocol +{ + NSXPCInterface * interface = [NSXPCInterface interfaceWithProtocol:@protocol(MTRXPCClientProtocol)]; + NSSet * allowedClasses = [NSSet setWithArray:@[ + [NSString class], [NSNumber class], [NSData class], [NSArray class], [NSDictionary class], [NSError class], [MTRAttributePath class] + ]]; + [interface setClasses:allowedClasses + forSelector:@selector(device:receivedAttributeReport:) + argumentIndex:1 + ofReply:NO]; + allowedClasses = [NSSet setWithArray:@[ + [NSString class], [NSNumber class], [NSData class], [NSArray class], [NSDictionary class], [NSError class], [MTREventPath class] + ]]; + [interface setClasses:allowedClasses + forSelector:@selector(device:receivedEventReport:) + argumentIndex:1 + ofReply:NO]; + return interface; +} + - (id)initWithUniqueIdentifier:(NSUUID *)UUID xpConnectionBlock:(NSXPCConnection * (^)(void) )connectionBlock { if (self = [super initForSubclasses]) { @@ -59,12 +79,14 @@ - (id)initWithUniqueIdentifier:(NSUUID *)UUID xpConnectionBlock:(NSXPCConnection self.xpcConnection = connectionBlock(); self.uniqueIdentifier = UUID; + self.chipWorkQueue = dispatch_queue_create("MTRDeviceController_XPC_queue", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); + self.nodeIDToDeviceMap = [NSMapTable strongToWeakObjectsMapTable]; MTR_LOG("Set up XPC Connection: %@", self.xpcConnection); if (self.xpcConnection) { self.xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MTRXPCServerProtocol)]; - self.xpcConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MTRXPCClientProtocol)]; + self.xpcConnection.exportedInterface = [self _interfaceForClientProtocol]; self.xpcConnection.exportedObject = self; self.xpcConnection.interruptionHandler = ^{ diff --git a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm index e22b0b8edff243..c9f4b7f07d295e 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm +++ b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm @@ -74,84 +74,6 @@ @interface MTRDevice_Concrete () // Disabling pending crashes #define ENABLE_CONNECTIVITY_MONITORING 0 -// Consider moving utility classes to their own file -#pragma mark - Utility Classes - -// container of MTRDevice delegate weak reference, its queue, and its interested paths for attribute reports -MTR_DIRECT_MEMBERS -@interface MTRDeviceDelegateInfo_ConcreteCopy : NSObject { -@private - void * _delegatePointerValue; - __weak id _delegate; - dispatch_queue_t _queue; - NSArray * _Nullable _interestedPathsForAttributes; - NSArray * _Nullable _interestedPathsForEvents; -} - -// Array of interested cluster paths, attribute paths, or endpointID, for attribute report filtering. -@property (readonly, nullable) NSArray * interestedPathsForAttributes; - -// Array of interested cluster paths, attribute paths, or endpointID, for event report filtering. -@property (readonly, nullable) NSArray * interestedPathsForEvents; - -// Expose delegate -@property (readonly) id delegate; - -// Pointer value for logging purpose only -@property (readonly) void * delegatePointerValue; - -- (instancetype)initWithDelegate:(id)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents; - -// Returns YES if delegate and queue are both non-null, and the block is scheduled to run. -- (BOOL)callDelegateWithBlock:(void (^)(id))block; - -#ifdef DEBUG -// Only used for unit test purposes - normal delegate should not expect or handle being called back synchronously. -- (BOOL)callDelegateSynchronouslyWithBlock:(void (^)(id))block; -#endif -@end - -@implementation MTRDeviceDelegateInfo_ConcreteCopy -- (instancetype)initWithDelegate:(id)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents -{ - if (self = [super init]) { - _delegate = delegate; - _delegatePointerValue = (__bridge void *) delegate; - _queue = queue; - _interestedPathsForAttributes = [interestedPathsForAttributes copy]; - _interestedPathsForEvents = [interestedPathsForEvents copy]; - } - return self; -} - -- (NSString *)description -{ - return [NSString stringWithFormat:@"", self, _delegatePointerValue, static_cast(_interestedPathsForAttributes.count), static_cast(_interestedPathsForEvents.count)]; -} - -- (BOOL)callDelegateWithBlock:(void (^)(id))block -{ - id strongDelegate = _delegate; - VerifyOrReturnValue(strongDelegate, NO); - dispatch_async(_queue, ^{ - block(strongDelegate); - }); - return YES; -} - -#ifdef DEBUG -- (BOOL)callDelegateSynchronouslyWithBlock:(void (^)(id))block -{ - id strongDelegate = _delegate; - VerifyOrReturnValue(strongDelegate, NO); - - block(strongDelegate); - - return YES; -} -#endif -@end - /* 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) @@ -281,7 +203,6 @@ typedef NS_ENUM(NSUInteger, MTRDeviceWorkItemDuplicateTypeID) { #define MTRDEVICE_SUBSCRIPTION_LATENCY_NEW_VALUE_WEIGHT (1.0 / 3.0) @interface MTRDevice_Concrete () -@property (nonatomic, readonly) os_unfair_lock lock; // protects the caches and device state // protects against concurrent time updates by guarding timeUpdateScheduled flag which manages time updates scheduling, // and protects device calls to setUTCTime and setDSTOffset. This can't just be replaced with "lock", because the time // update code calls public APIs like readAttributeWithEndpointID:.. (which attempt to take "lock") while holding @@ -412,8 +333,6 @@ @implementation MTRDevice_Concrete { // System time change observer reference id _systemTimeChangeObserverToken; - NSMutableSet * _delegates; - // Protects mutable state used by our description getter. This is a separate lock from "lock" // so that we don't need to worry about getting our description while holding "lock" (e.g due to // logging self). This lock _must_ be held narrowly, with no other lock acquisitions allowed @@ -450,8 +369,7 @@ @implementation MTRDevice_Concrete { - (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller { // `super` was NSObject, is now MTRDevice. MTRDevice hides its `init` - if (self = [super initForSubclasses]) { - _lock = OS_UNFAIR_LOCK_INIT; + if (self = [super initForSubclassesWithNodeID:nodeID controller:controller]) { _timeSyncLock = OS_UNFAIR_LOCK_INIT; _descriptionLock = OS_UNFAIR_LOCK_INIT; _nodeID = [nodeID copy]; @@ -483,8 +401,6 @@ - (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceControlle }]; } - _delegates = [NSMutableSet set]; - MTR_LOG_DEBUG("%@ init with hex nodeID 0x%016llX", self, _nodeID.unsignedLongLongValue); } return self; @@ -792,49 +708,9 @@ - (BOOL)_subscriptionsAllowed return ![_deviceController isKindOfClass:MTRDeviceControllerOverXPC.class]; } -- (void)setDelegate:(id)delegate queue:(dispatch_queue_t)queue -{ - MTR_LOG("%@ setDelegate %@", self, delegate); - [self _addDelegate:delegate queue:queue interestedPathsForAttributes:nil interestedPathsForEvents:nil]; -} - -- (void)addDelegate:(id)delegate queue:(dispatch_queue_t)queue -{ - MTR_LOG("%@ addDelegate %@", self, delegate); - [self _addDelegate:delegate queue:queue interestedPathsForAttributes:nil interestedPathsForEvents:nil]; -} - -- (void)addDelegate:(id)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents +- (void)_delegateAdded { - MTR_LOG("%@ addDelegate %@ with interested attribute paths %@ event paths %@", self, delegate, interestedPathsForAttributes, interestedPathsForEvents); - [self _addDelegate:delegate queue:queue interestedPathsForAttributes:interestedPathsForAttributes interestedPathsForEvents:interestedPathsForEvents]; -} - -- (void)_addDelegate:(id)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents -{ - std::lock_guard lock(_lock); - - // Replace delegate info with the same delegate object, and opportunistically remove defunct delegate references - NSMutableSet * delegatesToRemove = [NSMutableSet set]; - for (MTRDeviceDelegateInfo_ConcreteCopy * delegateInfo in _delegates) { - id strongDelegate = delegateInfo.delegate; - if (!strongDelegate) { - [delegatesToRemove addObject:delegateInfo]; - MTR_LOG("%@ removing delegate info for nil delegate %p", self, delegateInfo.delegatePointerValue); - } else if (strongDelegate == delegate) { - [delegatesToRemove addObject:delegateInfo]; - MTR_LOG("%@ replacing delegate info for %p", self, delegate); - } - } - if (delegatesToRemove.count) { - NSUInteger oldDelegatesCount = _delegates.count; - [_delegates minusSet:delegatesToRemove]; - MTR_LOG("%@ addDelegate: removed %lu", self, static_cast(_delegates.count - oldDelegatesCount)); - } - - MTRDeviceDelegateInfo_ConcreteCopy * newDelegateInfo = [[MTRDeviceDelegateInfo_ConcreteCopy alloc] initWithDelegate:delegate queue:queue interestedPathsForAttributes:interestedPathsForAttributes interestedPathsForEvents:interestedPathsForEvents]; - [_delegates addObject:newDelegateInfo]; - MTR_LOG("%@ added delegate info %@", self, newDelegateInfo); + os_unfair_lock_assert_owner(&self->_lock); __block BOOL shouldSetUpSubscription = [self _subscriptionsAllowed]; @@ -866,27 +742,6 @@ - (void)_addDelegate:(id)delegate queue:(dispatch_queue_t)que } } -- (void)removeDelegate:(id)delegate -{ - MTR_LOG("%@ removeDelegate %@", self, delegate); - - std::lock_guard lock(_lock); - - NSMutableSet * delegatesToRemove = [NSMutableSet set]; - [self _iterateDelegatesWithBlock:^(MTRDeviceDelegateInfo_ConcreteCopy * delegateInfo) { - id strongDelegate = delegateInfo.delegate; - if (strongDelegate == delegate) { - [delegatesToRemove addObject:delegateInfo]; - MTR_LOG("%@ removing delegate info %@ for %p", self, delegateInfo, delegate); - } - }]; - if (delegatesToRemove.count) { - NSUInteger oldDelegatesCount = _delegates.count; - [_delegates minusSet:delegatesToRemove]; - MTR_LOG("%@ removeDelegate: removed %lu", self, static_cast(_delegates.count - oldDelegatesCount)); - } -} - - (void)invalidate { MTR_LOG("%@ invalidate", self); @@ -901,8 +756,6 @@ - (void)invalidate _state = MTRDeviceStateUnknown; - [_delegates removeAllObjects]; - // Make sure we don't try to resubscribe if we have a pending resubscribe // attempt, since we now have no delegate. _reattemptingSubscription = NO; @@ -928,6 +781,8 @@ - (void)invalidate [self _stopConnectivityMonitoring]; os_unfair_lock_unlock(&self->_lock); + + [super invalidate]; } - (void)nodeMayBeAdvertisingOperational @@ -1052,79 +907,6 @@ - (void)_readThroughSkipped errorHandler:nil]; } -- (BOOL)_delegateExists -{ - os_unfair_lock_assert_owner(&self->_lock); - return [self _iterateDelegatesWithBlock:nil]; -} - -// Returns YES if any non-null delegates were found -- (BOOL)_iterateDelegatesWithBlock:(void(NS_NOESCAPE ^)(MTRDeviceDelegateInfo_ConcreteCopy * delegateInfo)_Nullable)block -{ - os_unfair_lock_assert_owner(&self->_lock); - - if (!_delegates.count) { - MTR_LOG_DEBUG("%@ no delegates to iterate", self); - return NO; - } - - // Opportunistically remove defunct delegate references on every iteration - NSMutableSet * delegatesToRemove = nil; - for (MTRDeviceDelegateInfo_ConcreteCopy * delegateInfo in _delegates) { - id strongDelegate = delegateInfo.delegate; - if (strongDelegate) { - if (block) { - @autoreleasepool { - block(delegateInfo); - } - } - (void) strongDelegate; // ensure it stays alive - } else { - if (!delegatesToRemove) { - delegatesToRemove = [NSMutableSet set]; - } - [delegatesToRemove addObject:delegateInfo]; - } - } - - if (delegatesToRemove.count) { - [_delegates minusSet:delegatesToRemove]; - MTR_LOG("%@ _iterateDelegatesWithBlock: removed %lu remaining %lu", self, static_cast(delegatesToRemove.count), (unsigned long) static_cast(_delegates.count)); - } - - return (_delegates.count > 0); -} - -- (BOOL)_callDelegatesWithBlock:(void (^)(id delegate))block -{ - os_unfair_lock_assert_owner(&self->_lock); - - __block NSUInteger delegatesCalled = 0; - [self _iterateDelegatesWithBlock:^(MTRDeviceDelegateInfo_ConcreteCopy * delegateInfo) { - if ([delegateInfo callDelegateWithBlock:block]) { - delegatesCalled++; - } - }]; - - return (delegatesCalled > 0); -} - -#ifdef DEBUG -// Only used for unit test purposes - normal delegate should not expect or handle being called back synchronously -// Returns YES if a delegate is called -- (void)_callFirstDelegateSynchronouslyWithBlock:(void (^)(id delegate))block -{ - os_unfair_lock_assert_owner(&self->_lock); - - for (MTRDeviceDelegateInfo_ConcreteCopy * delegateInfo in _delegates) { - if ([delegateInfo callDelegateSynchronouslyWithBlock:block]) { - MTR_LOG("%@ _callFirstDelegateSynchronouslyWithBlock: successfully called %@", self, delegateInfo); - return; - } - } -} -#endif - - (void)_callDelegateDeviceCachePrimed { os_unfair_lock_assert_owner(&self->_lock); @@ -1944,7 +1726,7 @@ - (void)_reportAttributes:(NSArray *> *)attributes { os_unfair_lock_assert_owner(&self->_lock); if (attributes.count) { - [self _iterateDelegatesWithBlock:^(MTRDeviceDelegateInfo_ConcreteCopy * delegateInfo) { + [self _iterateDelegatesWithBlock:^(MTRDeviceDelegateInfo * delegateInfo) { // _iterateDelegatesWithBlock calls this with an autorelease pool, and so temporary filtered attributes reports don't bloat memory NSArray *> * filteredAttributes = [self _filteredAttributes:attributes forInterestedPaths:delegateInfo.interestedPathsForAttributes]; if (filteredAttributes.count) { @@ -2123,7 +1905,7 @@ - (void)_handleEventReport:(NSArray *> *)eventRepor } __block BOOL delegatesCalled = NO; - [self _iterateDelegatesWithBlock:^(MTRDeviceDelegateInfo_ConcreteCopy * delegateInfo) { + [self _iterateDelegatesWithBlock:^(MTRDeviceDelegateInfo * delegateInfo) { // _iterateDelegatesWithBlock calls this with an autorelease pool, and so temporary filtered event reports don't bloat memory NSArray *> * filteredEvents = [self _filteredEvents:reportToReturn forInterestedPaths:delegateInfo.interestedPathsForEvents]; if (filteredEvents.count) { @@ -2658,20 +2440,6 @@ - (NSUInteger)unitTestAttributesReportedSinceLastCheck _unitTestAttributesReportedSinceLastCheck = 0; return attributesReportedSinceLastCheck; } - -- (NSUInteger)unitTestNonnullDelegateCount -{ - std::lock_guard lock(self->_lock); - - NSUInteger nonnullDelegateCount = 0; - for (MTRDeviceDelegateInfo_ConcreteCopy * delegateInfo in _delegates) { - if (delegateInfo.delegate) { - nonnullDelegateCount++; - } - } - - return nonnullDelegateCount; -} #endif #pragma mark Device Interactions diff --git a/src/darwin/Framework/CHIP/MTRDevice_Internal.h b/src/darwin/Framework/CHIP/MTRDevice_Internal.h index 4c1b51f51c0a48..bce5abfdfed75d 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDevice_Internal.h @@ -18,6 +18,7 @@ #import #import #import +#import #import "MTRAsyncWorkQueue.h" #import "MTRDefines_Internal.h" @@ -65,8 +66,55 @@ MTR_TESTABLE - (nullable instancetype)initWithDataVersion:(NSNumber * _Nullable)dataVersion attributes:(NSDictionary * _Nullable)attributes; @end -@interface MTRDevice () -- (instancetype)initForSubclasses; +// Consider moving utility classes to their own file +#pragma mark - Utility Classes + +/** + * container of MTRDevice delegate weak reference, its queue, and its interested + * paths for attribute reports. + */ +MTR_DIRECT_MEMBERS +@interface MTRDeviceDelegateInfo : NSObject { +@private + void * _delegatePointerValue; + __weak id _delegate; + dispatch_queue_t _queue; +} + +// Array of interested cluster paths, attribute paths, or endpointID, for attribute report filtering. +@property (readonly, nullable) NSArray * interestedPathsForAttributes; + +// Array of interested cluster paths, attribute paths, or endpointID, for event report filtering. +@property (readonly, nullable) NSArray * interestedPathsForEvents; + +// Expose delegate +@property (readonly) id delegate; + +// Pointer value for logging purpose only +@property (readonly) void * delegatePointerValue; + +- (instancetype)initWithDelegate:(id)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents; + +// Returns YES if delegate and queue are both non-null, and the block is scheduled to run. +- (BOOL)callDelegateWithBlock:(void (^)(id))block; + +#ifdef DEBUG +// Only used for unit test purposes - normal delegate should not expect or handle being called back synchronously. +- (BOOL)callDelegateSynchronouslyWithBlock:(void (^)(id))block; +#endif +@end + +#pragma mark - MTRDevice internal extensions + +@interface MTRDevice () { + // Ivars needed to implement shared MTRDevice functionality. +@protected + // Lock that protects overall device state, including delegate storage. + os_unfair_lock _lock; + NSMutableSet * _delegates; +} + +- (instancetype)initForSubclassesWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller; - (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller; // Called from MTRClusters for writes and commands @@ -83,6 +131,9 @@ MTR_TESTABLE - (BOOL)_callDelegatesWithBlock:(void (^)(id delegate))block; +// Called by MTRDevice_XPC to forward delegate callbacks +- (BOOL)_lockAndCallDelegatesWithBlock:(void (^)(id delegate))block; + /** * Like the public invokeCommandWithEndpointID but: * @@ -123,6 +174,19 @@ MTR_TESTABLE // Returns whether this MTRDevice uses Thread for communication - (BOOL)deviceUsesThread; +#pragma mark - MTRDevice functionality to deal with delegates. + +// Returns YES if any non-null delegates were found +- (BOOL)_iterateDelegatesWithBlock:(void(NS_NOESCAPE ^ _Nullable)(MTRDeviceDelegateInfo * delegateInfo))block; + +- (BOOL)_delegateExists; + +#ifdef DEBUG +// Only used for unit test purposes - normal delegate should not expect or handle being called back synchronously +// Returns YES if a delegate is called +- (void)_callFirstDelegateSynchronouslyWithBlock:(void (^)(id delegate))block; +#endif + @end #pragma mark - Constants diff --git a/src/darwin/Framework/CHIP/MTRDevice_XPC.mm b/src/darwin/Framework/CHIP/MTRDevice_XPC.mm index 69eba5bc6f5ea4..7dfb8da370823e 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_XPC.mm +++ b/src/darwin/Framework/CHIP/MTRDevice_XPC.mm @@ -82,13 +82,24 @@ @implementation MTRDevice_XPC +- (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller +{ + // TODO: Verify that this is a valid MTRDeviceController_XPC? + + if (self = [super initForSubclassesWithNodeID:nodeID controller:controller]) { + // Nothing else to do, all set. + } + + return self; +} + #pragma mark - Client Callbacks (MTRDeviceDelegate) // required methods for MTRDeviceDelegates - (oneway void)device:(NSNumber *)nodeID stateChanged:(MTRDeviceState)state { MTR_LOG("%s", __PRETTY_FUNCTION__); - [self _callDelegatesWithBlock:^(id delegate) { + [self _lockAndCallDelegatesWithBlock:^(id delegate) { [delegate device:self stateChanged:state]; }]; } @@ -96,7 +107,7 @@ - (oneway void)device:(NSNumber *)nodeID stateChanged:(MTRDeviceState)state - (oneway void)device:(NSNumber *)nodeID receivedAttributeReport:(NSArray *> *)attributeReport { MTR_LOG("%s", __PRETTY_FUNCTION__); - [self _callDelegatesWithBlock:^(id delegate) { + [self _lockAndCallDelegatesWithBlock:^(id delegate) { [delegate device:self receivedAttributeReport:attributeReport]; }]; } @@ -104,7 +115,7 @@ - (oneway void)device:(NSNumber *)nodeID receivedAttributeReport:(NSArray *> *)eventReport { MTR_LOG("%s", __PRETTY_FUNCTION__); - [self _callDelegatesWithBlock:^(id delegate) { + [self _lockAndCallDelegatesWithBlock:^(id delegate) { [delegate device:self receivedEventReport:eventReport]; }]; } @@ -113,7 +124,7 @@ - (oneway void)device:(NSNumber *)nodeID receivedEventReport:(NSArray delegate) { + [self _lockAndCallDelegatesWithBlock:^(id delegate) { if ([delegate respondsToSelector:@selector(deviceBecameActive:)]) { [delegate deviceBecameActive:self]; } @@ -122,7 +133,7 @@ - (oneway void)deviceBecameActive:(NSNumber *)nodeID - (oneway void)deviceCachePrimed:(NSNumber *)nodeID { - [self _callDelegatesWithBlock:^(id delegate) { + [self _lockAndCallDelegatesWithBlock:^(id delegate) { if ([delegate respondsToSelector:@selector(deviceCachePrimed:)]) { [delegate deviceCachePrimed:self]; } @@ -131,7 +142,7 @@ - (oneway void)deviceCachePrimed:(NSNumber *)nodeID - (oneway void)deviceConfigurationChanged:(NSNumber *)nodeID { - [self _callDelegatesWithBlock:^(id delegate) { + [self _lockAndCallDelegatesWithBlock:^(id delegate) { if ([delegate respondsToSelector:@selector(deviceConfigurationChanged:)]) { [delegate deviceConfigurationChanged:self]; } diff --git a/src/darwin/Framework/CHIP/XPC Protocol/MTRXPCClientProtocol.h b/src/darwin/Framework/CHIP/XPC Protocol/MTRXPCClientProtocol.h index a571ebc07a9257..3e276eb25f760d 100644 --- a/src/darwin/Framework/CHIP/XPC Protocol/MTRXPCClientProtocol.h +++ b/src/darwin/Framework/CHIP/XPC Protocol/MTRXPCClientProtocol.h @@ -1,6 +1,5 @@ -// /** - * Copyright (c) 2023 Project CHIP Authors + * 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. @@ -15,10 +14,12 @@ * limitations under the License. */ +#import #import // for MTRDeviceState NS_ASSUME_NONNULL_BEGIN +MTR_NEWLY_AVAILABLE @protocol MTRXPCClientProtocol_MTRDevice - (oneway void)device:(NSNumber *)nodeID stateChanged:(MTRDeviceState)state; - (oneway void)device:(NSNumber *)nodeID receivedAttributeReport:(NSArray *> *)attributeReport; @@ -28,6 +29,7 @@ NS_ASSUME_NONNULL_BEGIN - (oneway void)deviceConfigurationChanged:(NSNumber *)nodeID; @end +MTR_NEWLY_AVAILABLE @protocol MTRXPCClientProtocol_MTRDeviceController // Not Supported via XPC //- (oneway void)controller:(NSUUID *)controller statusUpdate:(MTRCommissioningStatus)status; @@ -36,6 +38,7 @@ NS_ASSUME_NONNULL_BEGIN //- (oneway void)controller:(NSUUID *)controller readCommissioningInfo:(MTRProductIdentity *)info; @end +MTR_NEWLY_AVAILABLE @protocol MTRXPCClientProtocol @end diff --git a/src/darwin/Framework/CHIP/XPC Protocol/MTRXPCServerProtocol.h b/src/darwin/Framework/CHIP/XPC Protocol/MTRXPCServerProtocol.h index 5d619453b5fd93..1eebb0396570ac 100644 --- a/src/darwin/Framework/CHIP/XPC Protocol/MTRXPCServerProtocol.h +++ b/src/darwin/Framework/CHIP/XPC Protocol/MTRXPCServerProtocol.h @@ -1,6 +1,5 @@ -// /** - * Copyright (c) 2023 Project CHIP Authors + * 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. @@ -15,8 +14,11 @@ * limitations under the License. */ +#import + NS_ASSUME_NONNULL_BEGIN +MTR_NEWLY_AVAILABLE @protocol MTRXPCServerProtocol_MTRDevice - (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID getStateWithReply:(void (^)(MTRDeviceState state))reply; @@ -45,6 +47,7 @@ NS_ASSUME_NONNULL_BEGIN // - (oneway void)downloadLogOfType:(MTRDiagnosticLogType)type nodeID:(NSNumber *)nodeID timeout:(NSTimeInterval)timeout completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion; @end +MTR_NEWLY_AVAILABLE @protocol MTRXPCServerProtocol_MTRDeviceController - (oneway void)deviceController:(NSUUID *)controller getIsRunningWithReply:(void (^)(BOOL response))reply; @@ -69,6 +72,7 @@ NS_ASSUME_NONNULL_BEGIN @end +MTR_NEWLY_AVAILABLE @protocol MTRXPCServerProtocol - (oneway void)deviceController:(NSUUID *)controller checkInWithContext:(NSDictionary *)context; @end diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj index 3a5968072d5a8d..efafd27d28dc1e 100644 --- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj @@ -2565,6 +2565,7 @@ "-Wl,-unexported_symbol,\"___*\"", "-Wl,-unexported_symbol,\"__Unwind_*\"", "-Wl,-unexported_symbol,\"_unw_*\"", + "-Wl,-unexported_symbol,\"_OBJC_IVAR_*\"", "-Wl,-hidden-lCHIP", ); "OTHER_LDFLAGS[sdk=macosx*]" = ( @@ -2583,6 +2584,7 @@ "-Wl,-unexported_symbol,\"___*\"", "-Wl,-unexported_symbol,\"__Unwind_*\"", "-Wl,-unexported_symbol,\"_unw_*\"", + "-Wl,-unexported_symbol,\"_OBJC_IVAR_*\"", "-Wl,-hidden-lCHIP", ); PRODUCT_BUNDLE_IDENTIFIER = com.csa.matter; diff --git a/src/platform/silabs/provision/ProvisionStorage.h b/src/platform/silabs/provision/ProvisionStorage.h index 3dbc78076442a7..ff012491ae3d7c 100644 --- a/src/platform/silabs/provision/ProvisionStorage.h +++ b/src/platform/silabs/provision/ProvisionStorage.h @@ -42,7 +42,7 @@ enum ID : uint16_t kFlashAddress = 0x0101, kFlashSize = 0x0102, kFlashPageSize = 0x0103, - kBaseAddress = 0x0104, + kCredsAddress = 0x0104, kCsrFile = 0x0105, // Options, kVersion = 0x0111, @@ -175,7 +175,6 @@ struct Storage : public GenericStorage, CHIP_ERROR Initialize(uint32_t flash_addr = 0, uint32_t flash_size = 0); CHIP_ERROR Commit(); - CHIP_ERROR GetBaseAddress(uint32_t & value); // // Generic Interface @@ -233,6 +232,8 @@ struct Storage : public GenericStorage, // Other // + CHIP_ERROR SetCredentialsBaseAddress(uint32_t addr); + CHIP_ERROR GetCredentialsBaseAddress(uint32_t & addr); CHIP_ERROR GetSetupPayload(chip::MutableCharSpan & value); CHIP_ERROR SetProvisionRequest(bool value); CHIP_ERROR GetProvisionRequest(bool & value); diff --git a/src/platform/silabs/rs911x/ble_config.h b/src/platform/silabs/rs911x/ble_config.h deleted file mode 120000 index 48a308dc911649..00000000000000 --- a/src/platform/silabs/rs911x/ble_config.h +++ /dev/null @@ -1 +0,0 @@ -rsi_ble_config.h \ No newline at end of file diff --git a/src/platform/silabs/rs911x/ble_config.h b/src/platform/silabs/rs911x/ble_config.h new file mode 100644 index 00000000000000..0b8c951b4f63b1 --- /dev/null +++ b/src/platform/silabs/rs911x/ble_config.h @@ -0,0 +1,18 @@ +/******************************************************************************* + * @file ble_config.h + * @brief + ******************************************************************************* + * # License + * Copyright 2020 Silicon Laboratories Inc. www.silabs.com + ******************************************************************************* + * + * The licensor of this software is Silicon Laboratories Inc. Your use of this + * software is governed by the terms of Silicon Labs Master Software License + * Agreement (MSLA) available at + * www.silabs.com/about-us/legal/master-software-license-agreement. This + * software is distributed to you in Source Code format and is governed by the + * sections of the MSLA applicable to Source Code. + * + ******************************************************************************/ + +#include diff --git a/src/platform/silabs/rs911x/rsi_ble_config.h b/src/platform/silabs/rs911x/rsi_ble_config.h index f5442578e5d013..781ad80a29e7d7 100644 --- a/src/platform/silabs/rs911x/rsi_ble_config.h +++ b/src/platform/silabs/rs911x/rsi_ble_config.h @@ -24,6 +24,10 @@ #include #endif +#if SL_MATTER_GN_BUILD == 0 +#include "sl_matter_wifi_config.h" +#endif // SL_MATTER_GN_BUILD + /****************************************************** * * Macros * ******************************************************/ @@ -107,7 +111,7 @@ #else #define RSI_BLE_MAX_NBR_ATT_REC (80) -#if (SLI_SI91X_MCU_INTERFACE | EXP_BOARD) +#if (EXP_BOARD) #define RSI_BLE_MAX_NBR_PERIPHERALS (3) #else #define RSI_BLE_MAX_NBR_SLAVES (3) diff --git a/src/python_testing/TC_DeviceBasicComposition.py b/src/python_testing/TC_DeviceBasicComposition.py index 7af7b865542e42..72e6e3e2418c8c 100644 --- a/src/python_testing/TC_DeviceBasicComposition.py +++ b/src/python_testing/TC_DeviceBasicComposition.py @@ -775,7 +775,18 @@ def test_TC_IDM_12_1(self): software_version = self.endpoints[0][Clusters.BasicInformation][Clusters.BasicInformation.Attributes.SoftwareVersion] filename = f'device_dump_0x{vid:04X}_0x{pid:04X}_{software_version}.json' dump_device_composition_path = self.user_params.get("dump_device_composition_path", filename) - self.dump_wildcard(dump_device_composition_path) + json_str, txt_str = self.dump_wildcard(dump_device_composition_path) + + # Structured dump so we can pull these back out of the logs + def log_structured_data(start_tag: str, dump_string): + lines = dump_string.splitlines() + logging.info(f'{start_tag}BEGIN ({len(lines)} lines)====') + for line in lines: + logging.info(f'{start_tag}{line}') + logging.info(f'{start_tag}END ====') + + log_structured_data('==== json: ', json_str) + log_structured_data('==== txt: ', txt_str) if __name__ == "__main__": diff --git a/src/python_testing/TC_DeviceConformance.py b/src/python_testing/TC_DeviceConformance.py index 3cc57e2b018ab6..611d7f54a82b62 100644 --- a/src/python_testing/TC_DeviceConformance.py +++ b/src/python_testing/TC_DeviceConformance.py @@ -50,6 +50,22 @@ async def setup_class_helper(self): self.xml_device_types, problems = build_xml_device_types() self.problems.extend(problems) + def _get_device_type_id(self, device_type_name: str) -> int: + id = [id for id, dt in self.xml_device_types.items() if dt.name.lower() == device_type_name.lower()] + if len(id) != 1: + self.fail_current_test(f"Unable to find {device_type_name} device type") + return id[0] + + def _has_nim(self): + nim_id = self._get_device_type_id('network infrastructure manager') + for endpoint in self.endpoints_tlv.values(): + desc = Clusters.Descriptor + device_types = [dt.deviceType for dt in endpoint[desc.id][desc.Attributes.DeviceTypeList.attribute_id]] + if nim_id in device_types: + # TODO: it's unclear if this needs to be present on every endpoint. Right now, this assumes one is sufficient. + return True + return False + def check_conformance(self, ignore_in_progress: bool, is_ci: bool): problems = [] success = True @@ -125,6 +141,13 @@ def record_warning(location, problem): for f in feature_masks: location = AttributePathLocation(endpoint_id=endpoint_id, cluster_id=cluster_id, attribute_id=GlobalAttributeIds.FEATURE_MAP_ID) + if cluster_id == Clusters.AccessControl.id and f == Clusters.AccessControl.Bitmaps.Feature.kManagedDevice: + # Managed ACL is treated as a special case because it is only allowed if other endpoints support NIM and disallowed otherwise. + if not self._has_nim(): + record_error( + location=location, problem="MACL feature is disallowed if the Network Infrastructure Manager device type is not present") + continue + if f not in self.xml_clusters[cluster_id].features.keys(): record_error(location=location, problem=f'Unknown feature with mask 0x{f:02x}') continue diff --git a/src/python_testing/TC_ICDM_3_4.py b/src/python_testing/TC_ICDM_3_4.py index 61230532178a08..b062da0845e4ea 100644 --- a/src/python_testing/TC_ICDM_3_4.py +++ b/src/python_testing/TC_ICDM_3_4.py @@ -32,7 +32,8 @@ import time import chip.clusters as Clusters -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from matter_testing_support import (MatterBaseTest, MatterStackState, MatterTestConfig, TestStep, async_test_body, + default_matter_test_main) from mobly import asserts logger = logging.getLogger(__name__) @@ -110,6 +111,16 @@ async def test_TC_ICDM_3_4(self): time.sleep(wait_time_reboot) self.step(3) + if not is_ci: + # since device has rebooted, force establishing a new CASE session by closing it + self.config = MatterTestConfig() + self.stack = MatterStackState(self.config) + devCtrl = self.stack.certificate_authorities[0].adminList[0].NewController( + nodeId=self.config.controller_node_id, + paaTrustStorePath=str(self.config.paa_trust_store_path), + catTags=self.config.controller_cat_tags + ) + devCtrl.CloseSession(self.dut_node_id) icdCounter2 = await self._read_icdm_attribute_expect_success(attribute=attributes.ICDCounter) asserts.assert_greater_equal(icdCounter2, icdCounter1, "ICDCounter have reboot is not greater or equal to the ICDCounter read before the reboot.") diff --git a/src/python_testing/TestConformanceTest.py b/src/python_testing/TestConformanceTest.py new file mode 100644 index 00000000000000..a656e228bf716f --- /dev/null +++ b/src/python_testing/TestConformanceTest.py @@ -0,0 +1,131 @@ +# +# 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. +# + +from typing import Any + +import chip.clusters as Clusters +from conformance_support import ConformanceDecision +from global_attribute_ids import GlobalAttributeIds +from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main +from mobly import asserts +from spec_parsing_support import build_xml_clusters, build_xml_device_types +from TC_DeviceConformance import DeviceConformanceTests + + +def is_mandatory(conformance): + return conformance(0, [], []).decision == ConformanceDecision.MANDATORY + + +class TestConformanceSupport(MatterBaseTest, DeviceConformanceTests): + def setup_class(self): + self.xml_clusters, self.problems = build_xml_clusters() + self.xml_device_types, problems = build_xml_device_types() + self.problems.extend(problems) + + def _create_minimal_cluster(self, cluster_id: int) -> dict[int, Any]: + attrs = {} + attrs[GlobalAttributeIds.FEATURE_MAP_ID] = 0 + + mandatory_attributes = [id for id, a in self.xml_clusters[cluster_id].attributes.items() if is_mandatory(a.conformance)] + for m in mandatory_attributes: + # dummy versions - we're not using the values in this test + attrs[m] = 0 + attrs[GlobalAttributeIds.ATTRIBUTE_LIST_ID] = mandatory_attributes + mandatory_accepted_commands = [id for id, a in self.xml_clusters[cluster_id].accepted_commands.items() + if is_mandatory(a.conformance)] + attrs[GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID] = mandatory_accepted_commands + mandatory_generated_commands = [id for id, a in self.xml_clusters[cluster_id].generated_commands.items() + if is_mandatory(a.conformance)] + attrs[GlobalAttributeIds.GENERATED_COMMAND_LIST_ID] = mandatory_generated_commands + attrs[GlobalAttributeIds.CLUSTER_REVISION_ID] = self.xml_clusters[cluster_id].revision + return attrs + + def _create_minimal_dt(self, device_type_id: int) -> dict[int, dict[int, Any]]: + ''' Creates the internals of an endpoint_tlv with the minimal set of clusters, with the minimal set of attributes and commands. Global attributes only. + Does NOT take into account overrides yet. + ''' + endpoint_tlv = {} + required_servers = [id for id, c in self.xml_device_types[device_type_id].server_clusters.items() + if is_mandatory(c.conformance)] + required_clients = [id for id, c in self.xml_device_types[device_type_id].client_clusters.items() + if is_mandatory(c.conformance)] + device_type_revision = self.xml_device_types[device_type_id].revision + + for s in required_servers: + endpoint_tlv[s] = self._create_minimal_cluster(s) + + # Descriptor + attr = Clusters.Descriptor.Attributes + attrs = {} + attrs[attr.FeatureMap.attribute_id] = 0 + attrs[attr.AcceptedCommandList.attribute_id] = [] + attrs[attr.GeneratedCommandList.attribute_id] = [] + attrs[attr.ClusterRevision.attribute_id] = self.xml_clusters[Clusters.Descriptor.id].revision + attrs[attr.DeviceTypeList.attribute_id] = [ + Clusters.Descriptor.Structs.DeviceTypeStruct(deviceType=device_type_id, revision=device_type_revision)] + attrs[attr.ServerList.attribute_id] = required_servers + attrs[attr.ClientList.attribute_id] = required_clients + attrs[attr.PartsList.attribute_id] = [] + attrs[attr.AttributeList.attribute_id] = [] + attrs[attr.AttributeList.attribute_id] = list(attrs.keys()) + + endpoint_tlv[Clusters.Descriptor.id] = attrs + return endpoint_tlv + + def add_macl(self, root_endpoint: dict[int, dict[int, Any]]): + ac = Clusters.AccessControl + root_endpoint[ac.id][ac.Attributes.FeatureMap.attribute_id] = ac.Bitmaps.Feature.kManagedDevice + root_endpoint[ac.id][ac.Attributes.Arl.attribute_id] = [] + root_endpoint[ac.id][ac.Attributes.CommissioningARL.attribute_id] = [] + root_endpoint[ac.id][ac.Attributes.AttributeList.attribute_id].extend([ + ac.Attributes.Arl.attribute_id, ac.Attributes.CommissioningARL.attribute_id]) + root_endpoint[ac.id][ac.Attributes.AcceptedCommandList.attribute_id].append(ac.Commands.ReviewFabricRestrictions.command_id) + root_endpoint[ac.id][ac.Attributes.GeneratedCommandList.attribute_id].append( + ac.Commands.ReviewFabricRestrictionsResponse.command_id) + + @async_test_body + async def test_macl_handling(self): + nim_id = self._get_device_type_id('network infrastructure manager') + root_node_id = self._get_device_type_id('root node') + on_off_id = self._get_device_type_id('On/Off Light') + + root = self._create_minimal_dt(device_type_id=root_node_id) + nim = self._create_minimal_dt(device_type_id=nim_id) + self.endpoints_tlv = {0: root, 1: nim} + asserts.assert_true(self._has_nim(), "Did not find NIM in generated device") + + success, problems = self.check_conformance(ignore_in_progress=False, is_ci=False) + self.problems.extend(problems) + asserts.assert_true(success, "Unexpected failure parsing minimal dt") + + self.add_macl(root) + # A MACL is allowed when there is a NIM, so this should succeed as well + success, problems = self.check_conformance(ignore_in_progress=False, is_ci=False) + self.problems.extend(problems) + asserts.assert_true(success, "Unexpected failure with NIM and MACL") + + # A MACL is not allowed when there is no NIM + self.endpoints_tlv[1] = self._create_minimal_dt(device_type_id=on_off_id) + success, problems = self.check_conformance(ignore_in_progress=False, is_ci=False) + self.problems.extend(problems) + asserts.assert_false(success, "Unexpected success with On/Off and MACL") + + # TODO: what happens if there is a NIM and a non-NIM endpoint? + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/basic_composition_support.py b/src/python_testing/basic_composition_support.py index 678c249d0abf5d..e25de55c0441a9 100644 --- a/src/python_testing/basic_composition_support.py +++ b/src/python_testing/basic_composition_support.py @@ -23,7 +23,7 @@ import pathlib import sys import typing -from pprint import pprint +from pprint import pformat, pprint from typing import Any, Optional import chip.clusters.ClusterObjects @@ -105,15 +105,20 @@ async def connect_over_pase(self, dev_ctrl): asserts.assert_equal(len(setupCode), 1, "Require one of either --qr-code or --manual-code.") await dev_ctrl.FindOrEstablishPASESession(setupCode[0], self.dut_node_id) - def dump_wildcard(self, dump_device_composition_path: typing.Optional[str]): + def dump_wildcard(self, dump_device_composition_path: typing.Optional[str]) -> tuple[str, str]: + """ Dumps a json and a txt file of the attribute wildcard for this device if the dump_device_composition_path is supplied. + Returns the json and txt as strings. + """ node_dump_dict = {endpoint_id: MatterTlvToJson(self.endpoints_tlv[endpoint_id]) for endpoint_id in self.endpoints_tlv} - logging.debug(f"Raw TLV contents of Node: {json.dumps(node_dump_dict, indent=2)}") + json_dump_string = json.dumps(node_dump_dict, indent=2) + logging.debug(f"Raw TLV contents of Node: {json_dump_string}") if dump_device_composition_path is not None: with open(pathlib.Path(dump_device_composition_path).with_suffix(".json"), "wt+") as outfile: json.dump(node_dump_dict, outfile, indent=2) with open(pathlib.Path(dump_device_composition_path).with_suffix(".txt"), "wt+") as outfile: pprint(self.endpoints, outfile, indent=1, width=200, compact=True) + return (json_dump_string, pformat(self.endpoints, indent=1, width=200, compact=True)) async def setup_class_helper(self, default_to_pase: bool = True): dev_ctrl = self.default_controller diff --git a/src/python_testing/execute_python_tests.py b/src/python_testing/execute_python_tests.py index ad3fc83c910bf8..86d88e5aa1c7a5 100644 --- a/src/python_testing/execute_python_tests.py +++ b/src/python_testing/execute_python_tests.py @@ -87,6 +87,7 @@ def main(search_directory, env_file): "TestChoiceConformanceSupport.py", "TC_DEMTestBase.py", "choice_conformance_support.py", + "TestConformanceTest.py", # Unit test of the conformance test (TC_DeviceConformance) - does not run against an app. "TestIdChecks.py", "TestSpecParsingDeviceType.py", "TestMatterTestingSupport.py", diff --git a/third_party/silabs/SiWx917_sdk.gni b/third_party/silabs/SiWx917_sdk.gni index 8c7635ad9235d3..1163cd28c99de3 100644 --- a/third_party/silabs/SiWx917_sdk.gni +++ b/third_party/silabs/SiWx917_sdk.gni @@ -27,17 +27,8 @@ import("silabs_board.gni") examples_plat_dir = "${chip_root}/examples/platform/silabs/SiWx917" declare_args() { - # Enable the Alarm Based Wakeup for 917 SoC when sleep is enabled - si91x_alarm_based_periodic_wakeup = false - - # Periodic time at which the 917 SoC should wakeup - si91x_alarm_periodic_time = 30 - # option to select the crypto library sl_si91x_crypto_flavor = "tinycrypt" - - # enable 917 SoC M4 sleep wakeup - si917_m4_sleep_enabled = false } # Defines an siwx917 SDK build target. @@ -325,17 +316,6 @@ template("siwx917_sdk") { "SL_SI91X_POWER_MANAGER_UC_AVAILABLE=1", "SL_SI91X_TICKLESS_MODE=1", ] - - if (si91x_alarm_based_periodic_wakeup) { - defines += [ "ALARM_PERIODIC_TIME=${si91x_alarm_periodic_time}" ] - } - - if (si917_m4_sleep_enabled) { - defines += [ - "SI917_M4_SLEEP_ENABLED=1", - "XTAL_OFF", - ] - } } if (sl_uart_log_output) { diff --git a/third_party/silabs/efr32_sdk.gni b/third_party/silabs/efr32_sdk.gni index c9126d02e43a9f..617dfd10facb95 100644 --- a/third_party/silabs/efr32_sdk.gni +++ b/third_party/silabs/efr32_sdk.gni @@ -485,6 +485,8 @@ template("efr32_sdk") { "RSI_LITTLE_ENDIAN=1", "SLI_SI91X_ENABLE_BLE=1", "SL_SI91X_ENABLE_LITTLE_ENDIAN=1", + "RSI_BLE_MAX_NBR_PERIPHERALS=1", + "RSI_BLE_MAX_NBR_CENTRALS=1", ] } diff --git a/third_party/silabs/matter_support b/third_party/silabs/matter_support index 8f476b30f9c604..e313f2dc9663da 160000 --- a/third_party/silabs/matter_support +++ b/third_party/silabs/matter_support @@ -1 +1 @@ -Subproject commit 8f476b30f9c6041de334abadcdb6852ade77790e +Subproject commit e313f2dc9663da1ee4488eceba17dbff038f6a63 diff --git a/third_party/silabs/simplicity_sdk b/third_party/silabs/simplicity_sdk index a1a37fa7dda1f7..aa5ce2e835dfdc 160000 --- a/third_party/silabs/simplicity_sdk +++ b/third_party/silabs/simplicity_sdk @@ -1 +1 @@ -Subproject commit a1a37fa7dda1f76ea70229aa4ffe2e47cb066edc +Subproject commit aa5ce2e835dfdce8c20fb828f27d3a261946f946 diff --git a/third_party/silabs/wifi_sdk b/third_party/silabs/wifi_sdk index e97a0ed00ddda3..841ea3f7e0e8ce 160000 --- a/third_party/silabs/wifi_sdk +++ b/third_party/silabs/wifi_sdk @@ -1 +1 @@ -Subproject commit e97a0ed00ddda347a8a39e8276f470e1c5fea469 +Subproject commit 841ea3f7e0e8ce64a160ae2354e747260a61b1e9 diff --git a/third_party/silabs/wiseconnect-wifi-bt-sdk b/third_party/silabs/wiseconnect-wifi-bt-sdk index c3843e95e63f84..f94b83d050fe62 160000 --- a/third_party/silabs/wiseconnect-wifi-bt-sdk +++ b/third_party/silabs/wiseconnect-wifi-bt-sdk @@ -1 +1 @@ -Subproject commit c3843e95e63f84e301a2727f7b3c26125818b53a +Subproject commit f94b83d050fe6200c5ec2dacfafa2edc92ad5ef3