From c17fd97dad04a154ce6d26b0a8ffc4e868fdfa23 Mon Sep 17 00:00:00 2001 From: Hasty Granbery Date: Fri, 23 Aug 2024 06:57:11 -0700 Subject: [PATCH 01/12] [HVAC] Sync atomic write error order with spec (#34936) * Add support for Presets attributes and commands to the Thermostat cluster Clean up the Thermostat cluster and remove the TemperatureSetpointHoldPolicy attribute and SetTemperatureSetpointHoldPolicy command * Restyled by whitespace * Restyled by clang-format * Restyled by gn. * Fix build error for Linux configure build of all-clusters-app * Fix Darwin CI issues Editorial fixes * Restyled by clang-format * More fixes * Restyled by clang-format * BUILD.gn fixes for CI * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Address review comments. * Restyled by clang-format * Regenerate Thermostat XML from spec * Move atomic enum to global-enums.xml, actually # Conflicts: # src/app/zap-templates/zcl/data-model/chip/global-structs.xml * Regenerate XML and convert thermostat-server to atomic writes * Pull in ACCapacityFormat typo un-fix * Update Test_TC_TSTAT_1_1 to know about AtomicResponse command. * Restyled patch * Fix weird merge with upstream * Fix emberAfIsTypeSigned not understanding temperature type * Merge fixes from atomic write branch * Relocate thermostat-manager sample code to all-clusters-common * Fix g++ build error on linux * Fix C formatter for long int, cast whole expression * Sync cast fix with master * Add thermostat-common dependency to thermostat app under linux * Remove MatterPostAttributeChangeCallback from thermostat-manager, as it conflicts with other implementations * Convert Atomic enums and structs to global * Restyled patch * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Regen with alchemy 0.6.1 * Updates based on comments * Add TC_MCORE_FS_1_3.py test implementation (#34650) * Fix most TC-SWTCH-2.4 remaining issues (#34677) - Move 2.4 in a better place in the file - Add test steps properly - Allow default button press position override Issue #34656 Testing done: - Test still passes on DUT with automation * Initial test script for Fabric Sync TC_MCORE_FS_1_2 (#34675) * Initial test script for Fabric Sync TC_MCORE_FS_1_2 * Apply suggestions from code review Co-authored-by: C Freeman * Address Review Comments * Address review comments * Fix default timeout after other timeouts changed * Restyled by autopep8 * Fix linter error --------- Co-authored-by: C Freeman Co-authored-by: Restyled.io * Test automation for FabricSync ICD BridgedDeviceBasicInfoCluster (#34628) * WIP Bridged ICD, commissioning to both fabrics * wip testing sending KeepActive * wip most steps implemented * using SIGSTOP and SIGCONT to control ICD server pausing * Update src/python_testing/TC_BRBINFO_4_1.py Co-authored-by: Terence Hampson * comments addressed * more comments addressed * lint pass * Update src/python_testing/TC_BRBINFO_4_1.py Co-authored-by: C Freeman * comments addressed, incl TH_SERVER configurable * added setupQRCode and setupManualCode as options for DUT commissioning * Restyled by autopep8 * Restyled by isort * Update src/python_testing/TC_BRBINFO_4_1.py Co-authored-by: Terence Hampson * Update src/python_testing/TC_BRBINFO_4_1.py Co-authored-by: Terence Hampson * Update src/python_testing/TC_BRBINFO_4_1.py Co-authored-by: Terence Hampson * comments addressed * Restyled by autopep8 --------- Co-authored-by: Terence Hampson Co-authored-by: C Freeman Co-authored-by: Restyled.io * ServiceArea test scripts (#34548) * initial commit * fix bugs * fix issues reported by the linter * fix bug in checking for unique areaDesc * add TC 1.5 * Update src/python_testing/TC_SEAR_1_2.py Co-authored-by: William * Update src/python_testing/TC_SEAR_1_2.py Co-authored-by: William * address code review comments * fix issue introduced by the previous commit * address code review feedback * Update src/python_testing/TC_SEAR_1_2.py Co-authored-by: Kiel Oleson * address code review feedback * remove PICS checked by the TC_SEAR_1.6 * more code review updates * Restyled by autopep8 --------- Co-authored-by: William Co-authored-by: Kiel Oleson Co-authored-by: Restyled.io * Remove manual tests for Thermostat presets (#34679) * Dump details about leaked ExchangeContexts before aborting (#34617) * Dump details about leaked ExchangeContexts before aborting This is implemented via a VerifyOrDieWithObject() variant of the existing VerifyOrDie() macro that calls a DumpToLog() method on the provided object if it exists (otherwise this is simply a no-op). If CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE is not enabled, VerifyOrDieWithObject() simply behaves like a plain VerifyOrDie(). DumpToLog() implementations can use ChipLogFormatRtti to log type information about an object (usually a delegate); if RTTI is disabled this simply outputs whether the object was null or not. * Address review comments * Make gcc happy and improve documentation * Remove unused include * Fix compile error without CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE * Avoid unused parameter warning * [TI] CC13x4_26x4 build fixes (#34682) * lwip pbuf, map file, and hex creation when OTA is disabled * added cc13x4 family define around the non OTA hex creation * whitespace fix * reversed custom factoy data flash with cc13x4 check * more whitespace fixes * [ICD] Add missing polling function to NoWifi connectivity manager (#34684) * Add missing polling function to NoWifi connectivity manager * Update GenericConnectivityManagerImpl_NoWiFi.h Co-authored-by: Boris Zbarsky --------- Co-authored-by: Boris Zbarsky * [OPSTATE] Add Q test script for CountdownTime (#34632) * Add Q test * Added test to test set * Remove unused var * Restyled by autopep8 * Restyled by isort * Fix name * Use pics over other method * Removed unused stuff * Added pipe commands * Fix reset * Get example to report appropriate changes. * WiP * Added some comments * Changes to make things work * Removed dev msgs * Missed some * Removed dev msgs * Straggler * Restyled by clang-format * Restyled by autopep8 * Restyled by isort * Commented unused var * Update examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp * Fix bug --------- Co-authored-by: Restyled.io * YAML update to BRBINFO, ProductId (#34513) * Bridged Device Information Cluster, Attribute ProductID test reflects marking as O, not X * Update src/app/tests/suites/certification/Test_TC_BRBINFO_2_1.yaml Co-authored-by: Terence Hampson * corrected pics * corrected pics * WIP Bridged ICD, commissioning to both fabrics * wip testing sending KeepActive * update to bridged-device-basic-information.xml and zap generated files * removed unrelated file --------- Co-authored-by: Terence Hampson Co-authored-by: Andrei Litvin * Fix simplified Linux tv-casting-app gn build error. (#34692) * adding parallel execution to restyle-diff (#34663) * adding parallel execution to restyle-diff * using xargs to call restyle-paths * fixing Copyright year * restyle the restyler * Add some bits to exercise global structs/enums to Unit Testing cluster. (#34540) * Adds things to the Unit Testing cluster XML. * This requires those things to be enabled in all-clusters-app, all-clusters-minimal-app, and one of the chef contact sensors to pass CI. * That requires an implementation in test-cluster-server * At which point might as well add a YAML test to exercise it all. * [Silabs] Port platform specific Multi-Chip OTA work (#34440) * Pull request #1836: Cherry multi ota Merge in WMN_TOOLS/matter from cherry-multi-ota to silabs_slc_1.3 Squashed commit of the following: commit 4320bb46571658bc44fb82345348265def394991 Author: Michael Rupp Date: Fri May 10 14:26:07 2024 -0400 remove some unwanted diffs in provision files commit be160931dc600de7e7ead378b70d6a43c3945e46 Author: Michael Rupp Date: Fri May 10 14:24:25 2024 -0400 revert changes to generator.project.mak commit 14b6605887166e6d5284a61feb2bf407d850bdcf Author: Michael Rupp Date: Fri May 10 13:06:12 2024 -0400 revert NVM key changes and script changes ... and 8 more commits * Restyled by whitespace * Restyled by clang-format * Restyled by gn * Restyled by autopep8 * remove unused libs caught by linter * update doctree with new readmes * rerun CI, cirque failing for unknown reasons * fix include guards in provision examples * Restyled by clang-format --------- Co-authored-by: Restyled.io * Add python tests for Thermostat presets feature (#34693) * Add python tests for Thermostat presets feature * Restyled by autopep8 * Restyled by isort * Update the PICS code for presets attribute --------- Co-authored-by: Restyled.io * removing unneccessary git fetch (#34698) * Restyle patch * Regen to fix ordering of global structs * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Return correct AtomicResponse when committing or rolling back * Patch tests for atomic write of presets * Fix tests to work with the new setup. Specific changes: * Enable SetActivePresetRequest command in all-clusters-app. * Fix assignment of a PresetStructWithOwnedMembers to another PresetStructWithOwnedMembers to actually work correctly. * Move constraint checks that happen on write from commit to write. * Fix sending of atomic responses to not have use-stack-after-return. * Fix PICS for the tests involved. * Fix PICS values for atomic requests * Remove PresetsSchedulesEditable and QueuedPreset from various places * Restyled patch * Restyled patch, again * Remove PICS value for PresetsSchedulesEditable * clang-tidy fixes * clang-tidy fixes * Clear associated atomic writes when fabric is removed * Add tests for fabric removal and lockout of clients outside of atomic write * Python linter * Restyled patch * Clear timer when fabric is removed * Check for open atomic write before resetting * Revert auto delegate declaration on lines where there's no collision * Allow Thermostat delegate to provide timeout for atomic requests * Relocate thermostat example code to thermostat-common * Remove thermostat-manager code, replace with thermostat delegate * Sync atomic write error order with spec * Restyle patch * Drop memset of atomic write sessions * Add PreCommit stage to allow rollback of multiple attributes when only one fails * Separate OnTimerExpired method, vs ResetWrite * Method documentation * Apply suggestions from code review Co-authored-by: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com> * Remove unused InWrite check * Drop imcode alias * Switch AtomicWriteState to enum class * DRY up atomic write manager * Apply suggestions from code review Co-authored-by: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com> * Drop duplicate doc comments * Rename GetAtomicWriteScopedNodeId to GetAtomicWriteOriginatorScopedNodeId * Updates based on comments * Add MatterReportingAttributeChangeCallback calls for updated attributes * Relocate thermostat example code to thermostat-common, and remove thermostat-manager * Merge atomic write code back into thermostat-server * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Fix build after suggestions * Actually track attribute IDs associated with atomic write * Only commit presets if all attribute precommits were successful * Fix scope on err * Add documentation to methods * Remove duplicate preset check. * Move various functions into anonymous namespaces, or Thermostat namespace * Drop impossible non-atomic attribute status after rollback * Namespace workaround for compilers on other platforms * Apply suggestions from code review --------- Co-authored-by: Nivedita Sarkar Co-authored-by: Restyled.io Co-authored-by: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com> Co-authored-by: Boris Zbarsky Co-authored-by: Terence Hampson Co-authored-by: Tennessee Carmel-Veilleux Co-authored-by: Chris Letnick Co-authored-by: C Freeman Co-authored-by: Douglas Rocha Ferraz Co-authored-by: Petru Lauric <81822411+plauric@users.noreply.github.com> Co-authored-by: William Co-authored-by: Kiel Oleson Co-authored-by: Karsten Sperling <113487422+ksperling-apple@users.noreply.github.com> Co-authored-by: Anu Biradar <104591549+abiradarti@users.noreply.github.com> Co-authored-by: mkardous-silabs <84793247+mkardous-silabs@users.noreply.github.com> Co-authored-by: Rob Bultman Co-authored-by: Andrei Litvin Co-authored-by: Shao Ling Tan <161761051+shaoltan-amazon@users.noreply.github.com> Co-authored-by: Amine Alami <43780877+Alami-Amine@users.noreply.github.com> Co-authored-by: Michael Rupp <95718139+mykrupp@users.noreply.github.com> --- examples/all-clusters-app/linux/BUILD.gn | 4 +- examples/thermostat/linux/BUILD.gn | 8 +- .../linux/include/thermostat-manager.h | 73 -- examples/thermostat/linux/main.cpp | 16 +- .../thermostat/linux/thermostat-manager.cpp | 497 -------- .../thermostat/thermostat-common/BUILD.gn | 4 + .../include/thermostat-delegate-impl.h | 6 +- .../src}/thermostat-delegate-impl.cpp | 88 +- src/app/chip_data_model.gni | 2 + .../thermostat-server/thermostat-delegate.h | 13 +- .../thermostat-server-atomic.cpp | 644 ++++++++++ .../thermostat-server-presets.cpp | 546 ++++++++ .../thermostat-server/thermostat-server.cpp | 1113 ++--------------- .../thermostat-server/thermostat-server.h | 163 ++- src/python_testing/TC_TSTAT_4_2.py | 50 +- 15 files changed, 1542 insertions(+), 1685 deletions(-) delete mode 100644 examples/thermostat/linux/include/thermostat-manager.h delete mode 100644 examples/thermostat/linux/thermostat-manager.cpp rename examples/thermostat/{linux => thermostat-common}/include/thermostat-delegate-impl.h (92%) rename examples/thermostat/{linux => thermostat-common/src}/thermostat-delegate-impl.cpp (75%) create mode 100644 src/app/clusters/thermostat-server/thermostat-server-atomic.cpp create mode 100644 src/app/clusters/thermostat-server/thermostat-server-presets.cpp diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index ed228b51b2cb32..baac52014d3c3b 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -75,7 +75,7 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/energy-management-app/energy-management-common/energy-evse/src/EnergyEvseTargetsStore.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/energy-evse/src/energy-evse-mode.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/energy-reporting/src/ElectricalPowerMeasurementDelegate.cpp", - "${chip_root}/examples/thermostat/linux/thermostat-delegate-impl.cpp", + "${chip_root}/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp", "AllClustersCommandDelegate.cpp", "AllClustersCommandDelegate.h", "AppOptions.cpp", @@ -102,7 +102,7 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/energy-management-app/energy-management-common/device-energy-management/include", "${chip_root}/examples/energy-management-app/energy-management-common/energy-evse/include", "${chip_root}/examples/energy-management-app/energy-management-common/energy-reporting/include", - "${chip_root}/examples/thermostat/linux/include", + "${chip_root}/examples/thermostat/thermostat-common/include", ] if (chip_enable_pw_rpc) { diff --git a/examples/thermostat/linux/BUILD.gn b/examples/thermostat/linux/BUILD.gn index 71c0eccfcfae50..0683b39abb4cc6 100644 --- a/examples/thermostat/linux/BUILD.gn +++ b/examples/thermostat/linux/BUILD.gn @@ -17,11 +17,10 @@ import("//build_overrides/chip.gni") executable("thermostat-app") { sources = [ + "${chip_root}/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp", "include/low-power/LowPowerManager.cpp", "include/low-power/LowPowerManager.h", "main.cpp", - "thermostat-delegate-impl.cpp", - "thermostat-manager.cpp", ] deps = [ @@ -30,7 +29,10 @@ executable("thermostat-app") { "${chip_root}/src/lib", ] - include_dirs = [ "include" ] + include_dirs = [ + "include", + "${chip_root}/examples/thermostat/thermostat-common/include", + ] cflags = [ "-Wconversion" ] diff --git a/examples/thermostat/linux/include/thermostat-manager.h b/examples/thermostat/linux/include/thermostat-manager.h deleted file mode 100644 index 274f66c66917cf..00000000000000 --- a/examples/thermostat/linux/include/thermostat-manager.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * 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 - -class ThermostatManager -{ -public: - CHIP_ERROR Init(); - - /// @brief Callback called when any attribute changed on the device - void AttributeChangeHandler(chip::EndpointId endpointId, chip::ClusterId clusterId, chip::AttributeId attributeId, - uint8_t * value, uint16_t size); - - chip::app::Clusters::Thermostat::SystemModeEnum GetSystemMode(); - chip::app::Clusters::Thermostat::ThermostatRunningModeEnum GetRunningMode(); - int16_t GetCurrentTemperature(); - int16_t GetCurrentHeatingSetPoint(); - int16_t GetCurrentCoolingSetPoint(); - uint8_t GetNumberOfPresets(); - CHIP_ERROR SetSystemMode(chip::app::Clusters::Thermostat::SystemModeEnum systemMode); - CHIP_ERROR SetRunningMode(chip::app::Clusters::Thermostat::ThermostatRunningModeEnum runningMode); - CHIP_ERROR SetCurrentTemperature(int16_t temperature); - CHIP_ERROR SetCurrentHeatingSetPoint(int16_t heatingSetpoint); - CHIP_ERROR SetCurrentCoolingSetPoint(int16_t coolingSetpoint); - -private: - friend ThermostatManager & ThermostatMgr(); - - chip::app::Clusters::Thermostat::SystemModeEnum mSystemMode; - chip::app::Clusters::Thermostat::ThermostatRunningModeEnum mRunningMode; - int16_t mLocalTemperature; - int16_t mOccupiedCoolingSetpoint; - int16_t mOccupiedHeatingSetpoint; - uint8_t mOccupiedSetback; - - static ThermostatManager sThermostatMgr; - - /// @brief attribute handler for the thermostat endpoint - void ThermostatEndpointAttributeChangeHandler(chip::ClusterId clusterId, chip::AttributeId attributeId, uint8_t * value, - uint16_t size); - void ThermostatClusterAttributeChangeHandler(chip::AttributeId attributeId, uint8_t * value, uint16_t size); - void LocalTemperatureMeasurementEndpointAttributeChangeHandler(chip::ClusterId clusterId, chip::AttributeId attributeId, - uint8_t * value, uint16_t size); - void LocalTemperatureMeasurementClusterAttributeChangeHandler(chip::AttributeId attributeId, uint8_t * value, uint16_t size); - - /// @brief Main method that evaluates the current thermostat state and updates attributes - void EvalThermostatState(); - void UpdateRunningModeForHeating(); - void UpdateRunningModeForCooling(); -}; - -inline ThermostatManager & ThermostatMgr() -{ - return ThermostatManager::sThermostatMgr; -} diff --git a/examples/thermostat/linux/main.cpp b/examples/thermostat/linux/main.cpp index 2279f02bef3963..b9f82696e8ce79 100644 --- a/examples/thermostat/linux/main.cpp +++ b/examples/thermostat/linux/main.cpp @@ -22,8 +22,6 @@ #include #include -#include "thermostat-manager.h" - using namespace chip; using namespace chip::app; // using namespace chip::app::Clusters; @@ -76,19 +74,7 @@ void ApplicationShutdown() {} int main(int argc, char * argv[]) { - if (ChipLinuxAppInit(argc, argv) != 0) - { - return -1; - } - ChipLogProgress(Zcl, "Starting Thermostat Manager"); - CHIP_ERROR err = ThermostatManager().Init(); - - if (err != CHIP_NO_ERROR) - { - ChipLogError(AppServer, "Failed to initialize thermostat manager: %" CHIP_ERROR_FORMAT, err.Format()); - chip::DeviceLayer::PlatformMgr().Shutdown(); - return -1; - } + VerifyOrDie(ChipLinuxAppInit(argc, argv) == 0); ChipLinuxAppMainLoop(); return 0; } diff --git a/examples/thermostat/linux/thermostat-manager.cpp b/examples/thermostat/linux/thermostat-manager.cpp deleted file mode 100644 index ea1f4375c1649d..00000000000000 --- a/examples/thermostat/linux/thermostat-manager.cpp +++ /dev/null @@ -1,497 +0,0 @@ -/* - * - * 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. - */ - -/********************************************************** - * Includes - *********************************************************/ - -#include -#include - -#include -#include -#include -#include - -/********************************************************** - * Defines and Constants - *********************************************************/ - -using namespace chip; -using namespace chip::app; -using namespace chip::app::DataModel; -using namespace chip::Controller; -using namespace chip::app::Clusters; -using namespace chip::app::Clusters::Thermostat; -using namespace chip::app::Clusters::Thermostat::Structs; -using namespace chip::app::Clusters::Thermostat::Attributes; -using namespace chip::app::Clusters::TemperatureMeasurement; -using namespace chip::app::Clusters::TemperatureMeasurement::Attributes; -using namespace Protocols::InteractionModel; - -using namespace chip::DeviceLayer; - -static constexpr EndpointId kThermostatEndpoint = 1; - -static constexpr uint16_t kMaxIntervalCeilingSeconds = 3600; - -static const char * SystemModeString(SystemModeEnum systemMode); -static const char * RunningModeString(ThermostatRunningModeEnum runningMode); - -/********************************************************** - * Variable declarations - *********************************************************/ - -ThermostatManager ThermostatManager::sThermostatMgr; - -namespace { - -template -static void OnAttributeChangeReported(const ConcreteDataAttributePath & path, const DecodableAttributeType & value); - -template <> -void OnAttributeChangeReported(const ConcreteDataAttributePath & path, - const MeasuredValue::TypeInfo::DecodableType & value) -{ - ClusterId clusterId = path.mClusterId; - if (clusterId != TemperatureMeasurement::Id) - { - ChipLogError(AppServer, - "Attribute change reported for TemperatureMeasurement cluster on incorrect cluster id " ChipLogFormatMEI, - ChipLogValueMEI(clusterId)); - return; - } - - AttributeId attributeId = path.mAttributeId; - if (attributeId != MeasuredValue::Id) - { - ChipLogError(AppServer, - "Attribute change reported for TemperatureMeasurement cluster for incorrect attribute" ChipLogFormatMEI, - ChipLogValueMEI(attributeId)); - return; - } - - if (!value.IsNull()) - { - ChipLogDetail(AppServer, "Attribute change reported for TemperatureMeasurement cluster - MeasuredValue is %d", - value.Value()); - } -} - -static void OnError(const ConcreteDataAttributePath * path, ChipError err) -{ - ChipLogError(AppServer, - "Subscribing to cluster Id " ChipLogFormatMEI " and attribute Id " ChipLogFormatMEI - " failed with error %" CHIP_ERROR_FORMAT, - ChipLogValueMEI(path->mClusterId), ChipLogValueMEI(path->mAttributeId), err.Format()); -} - -static void OnSubscriptionEstablished(const ReadClient & client, unsigned int value) -{ - ChipLogDetail(AppServer, "OnSubscriptionEstablished with subscription Id: %d", value); -} - -template -void SubscribeToAttribute(ClusterId clusterId, AttributeId attributeId, const EmberBindingTableEntry & binding, - OperationalDeviceProxy * peer_device) -{ - VerifyOrReturn(peer_device->GetSecureSession().HasValue(), - ChipLogError(AppServer, "SubscribeToAttribute failed. Secure session is null")); - - SubscribeAttribute( - peer_device->GetExchangeManager(), peer_device->GetSecureSession().Value(), binding.remote, clusterId, attributeId, - &OnAttributeChangeReported, &OnError, 0, kMaxIntervalCeilingSeconds, &OnSubscriptionEstablished, - nullptr, true /* fabricFiltered */, false /* keepExistingSubscription */); -} - -static void ThermostatBoundDeviceChangedHandler(const EmberBindingTableEntry & binding, OperationalDeviceProxy * peer_device, - void * context) -{ - VerifyOrReturn(binding.clusterId.has_value(), ChipLogError(AppServer, "Cluster Id is null")); - ClusterId clusterId = binding.clusterId.value(); - - switch (clusterId) - { - case TemperatureMeasurement::Id: - - // Subscribe to the MeasuredValue attribute - SubscribeToAttribute(clusterId, MeasuredValue::Id, binding, peer_device); - break; - default: - ChipLogError(AppServer, "Unsupported Cluster Id"); - break; - } -} - -void NotifyBoundClusterChangedForAllClusters() -{ - BindingManager::GetInstance().NotifyBoundClusterChanged(kThermostatEndpoint, TemperatureMeasurement::Id, nullptr); -} - -static void OnPlatformChipDeviceEvent(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg) -{ - if (event->Type == DeviceLayer::DeviceEventType::kBindingsChangedViaCluster) - { - NotifyBoundClusterChangedForAllClusters(); - } -} - -void InitBindingManager(intptr_t context) -{ - auto & server = Server::GetInstance(); - CHIP_ERROR error = BindingManager::GetInstance().Init( - { &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() }); - - if (error != CHIP_NO_ERROR) - { - ChipLogError(AppServer, "Failed to init binding manager"); - } - - BindingManager::GetInstance().RegisterBoundDeviceChangedHandler(ThermostatBoundDeviceChangedHandler); - NotifyBoundClusterChangedForAllClusters(); -} - -} // anonymous namespace - -CHIP_ERROR ThermostatManager::Init() -{ - // Init binding manager - - DeviceLayer::PlatformMgr().AddEventHandler(OnPlatformChipDeviceEvent, reinterpret_cast(this)); - DeviceLayer::PlatformMgr().ScheduleWork(InitBindingManager); - - mLocalTemperature = GetCurrentTemperature(); - mSystemMode = GetSystemMode(); - mRunningMode = GetRunningMode(); - mOccupiedCoolingSetpoint = GetCurrentCoolingSetPoint(); - mOccupiedHeatingSetpoint = GetCurrentHeatingSetPoint(); - // TODO: Gotta expose this properly on attribute - mOccupiedSetback = 5; // 0.5 C - - ChipLogError(AppServer, - "Initialized a thermostat with \n " - "mSystemMode: %u (%s) \n mRunningMode: %u (%s) \n mLocalTemperature: %d \n mOccupiedHeatingSetpoint: %d \n " - "mOccupiedCoolingSetpoint: %d" - "NumberOfPresets: %d", - to_underlying(mSystemMode), SystemModeString(mSystemMode), to_underlying(mRunningMode), - RunningModeString(mRunningMode), mLocalTemperature, mOccupiedHeatingSetpoint, mOccupiedCoolingSetpoint, - GetNumberOfPresets()); - - // TODO: Should this be called later? - EvalThermostatState(); - - return CHIP_NO_ERROR; -} - -void ThermostatManager::AttributeChangeHandler(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, uint8_t * value, - uint16_t size) -{ - switch (endpointId) - { - case kThermostatEndpoint: - ThermostatEndpointAttributeChangeHandler(clusterId, attributeId, value, size); - break; - - default: - ChipLogError(AppServer, "Attribute change reported for Thermostat on incorrect endpoint. Ignoring."); - break; - } -} - -void ThermostatManager::ThermostatEndpointAttributeChangeHandler(ClusterId clusterId, AttributeId attributeId, uint8_t * value, - uint16_t size) -{ - switch (clusterId) - { - case Thermostat::Id: - ThermostatClusterAttributeChangeHandler(attributeId, value, size); - break; - - default: - ChipLogError(AppServer, - "Attribute change reported for Thermostat on incorrect cluster for the thermostat endpoint. Ignoring."); - break; - } -} - -void ThermostatManager::ThermostatClusterAttributeChangeHandler(AttributeId attributeId, uint8_t * value, uint16_t size) -{ - switch (attributeId) - { - case LocalTemperature::Id: { - memcpy(&mLocalTemperature, value, size); - ChipLogError(AppServer, "Local temperature changed to %d", mLocalTemperature); - EvalThermostatState(); - } - break; - - case OccupiedCoolingSetpoint::Id: { - memcpy(&mOccupiedCoolingSetpoint, value, size); - ChipLogError(AppServer, "Cooling temperature changed to %d", mOccupiedCoolingSetpoint); - EvalThermostatState(); - } - break; - - case OccupiedHeatingSetpoint::Id: { - memcpy(&mOccupiedHeatingSetpoint, value, size); - ChipLogError(AppServer, "Heating temperature changed to %d", mOccupiedHeatingSetpoint); - EvalThermostatState(); - } - break; - - case SystemMode::Id: { - mSystemMode = static_cast(*value); - ChipLogError(AppServer, "System mode changed to %u (%s)", *value, SystemModeString(mSystemMode)); - EvalThermostatState(); - } - break; - - case ThermostatRunningMode::Id: { - mRunningMode = static_cast(*value); - ChipLogError(AppServer, "Running mode changed to %u (%s)", *value, RunningModeString(mRunningMode)); - } - break; - - default: { - ChipLogError(AppServer, "Unhandled thermostat attribute %u", static_cast(attributeId)); - return; - } - break; - } -} - -SystemModeEnum ThermostatManager::GetSystemMode() -{ - SystemModeEnum systemMode; - SystemMode::Get(kThermostatEndpoint, &systemMode); - return systemMode; -} - -ThermostatRunningModeEnum ThermostatManager::GetRunningMode() -{ - ThermostatRunningModeEnum runningMode; - ThermostatRunningMode::Get(kThermostatEndpoint, &runningMode); - return runningMode; -} - -int16_t ThermostatManager::GetCurrentTemperature() -{ - DataModel::Nullable currentTemperature; - currentTemperature.SetNull(); - LocalTemperature::Get(kThermostatEndpoint, currentTemperature); - return currentTemperature.ValueOr(0); -} - -int16_t ThermostatManager::GetCurrentHeatingSetPoint() -{ - int16_t heatingSetpoint; - OccupiedHeatingSetpoint::Get(kThermostatEndpoint, &heatingSetpoint); - return heatingSetpoint; -} - -int16_t ThermostatManager::GetCurrentCoolingSetPoint() -{ - int16_t coolingSetpoint; - OccupiedCoolingSetpoint::Get(kThermostatEndpoint, &coolingSetpoint); - return coolingSetpoint; -} - -uint8_t ThermostatManager::GetNumberOfPresets() -{ - return ThermostatDelegate::GetInstance().GetNumberOfPresets(); -} - -CHIP_ERROR ThermostatManager::SetSystemMode(SystemModeEnum systemMode) -{ - uint8_t systemModeValue = to_underlying(systemMode); - if (mSystemMode == systemMode) - { - ChipLogDetail(AppServer, "Already in system mode: %u (%s)", systemModeValue, SystemModeString(systemMode)); - return CHIP_NO_ERROR; - } - - ChipLogError(AppServer, "Setting system mode: %u (%s)", systemModeValue, SystemModeString(systemMode)); - return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(SystemMode::Set(kThermostatEndpoint, systemMode)); -} - -CHIP_ERROR ThermostatManager::SetRunningMode(ThermostatRunningModeEnum runningMode) -{ - uint8_t runningModeValue = to_underlying(runningMode); - if (mRunningMode == runningMode) - { - ChipLogDetail(AppServer, "Already in running mode: %u (%s)", runningModeValue, RunningModeString(runningMode)); - return CHIP_NO_ERROR; - } - - ChipLogError(AppServer, "Setting running mode: %u (%s)", runningModeValue, RunningModeString(runningMode)); - return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(ThermostatRunningMode::Set(kThermostatEndpoint, runningMode)); -} - -CHIP_ERROR ThermostatManager::SetCurrentTemperature(int16_t temperature) -{ - return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(LocalTemperature::Set(kThermostatEndpoint, temperature)); -} - -CHIP_ERROR ThermostatManager::SetCurrentHeatingSetPoint(int16_t heatingSetpoint) -{ - return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(OccupiedHeatingSetpoint::Set(kThermostatEndpoint, heatingSetpoint)); -} - -CHIP_ERROR ThermostatManager::SetCurrentCoolingSetPoint(int16_t coolingSetpoint) -{ - return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(OccupiedCoolingSetpoint::Set(kThermostatEndpoint, coolingSetpoint)); -} - -void ThermostatManager::EvalThermostatState() -{ - ChipLogError(AppServer, - "Eval Thermostat Running Mode \n " - "mSystemMode: %u (%s) \n mRunningMode: %u (%s) \n mLocalTemperature: %d \n mOccupiedHeatingSetpoint: %d \n " - "mOccupiedCoolingSetpoint: %d", - to_underlying(mSystemMode), SystemModeString(mSystemMode), to_underlying(mRunningMode), - RunningModeString(mRunningMode), mLocalTemperature, mOccupiedHeatingSetpoint, mOccupiedCoolingSetpoint); - - switch (mSystemMode) - { - case SystemModeEnum::kOff: { - SetRunningMode(ThermostatRunningModeEnum::kOff); - break; - } - case SystemModeEnum::kHeat: { - UpdateRunningModeForHeating(); - break; - } - case SystemModeEnum::kCool: { - UpdateRunningModeForCooling(); - break; - } - case SystemModeEnum::kAuto: { - UpdateRunningModeForHeating(); - UpdateRunningModeForCooling(); - break; - } - default: - break; - } -} - -void ThermostatManager::UpdateRunningModeForHeating() -{ - const int16_t heatingOnThreshold = mOccupiedHeatingSetpoint - static_cast(mOccupiedSetback * 10); - const int16_t heatingOffThreshold = mOccupiedHeatingSetpoint + static_cast(mOccupiedSetback * 10); - - if (mRunningMode == ThermostatRunningModeEnum::kHeat) - { - if (mLocalTemperature >= heatingOffThreshold) - { - ChipLogDetail(AppServer, "Eval Heat - Turning off"); - SetRunningMode(ThermostatRunningModeEnum::kOff); - } - else - { - ChipLogDetail(AppServer, "Eval Heat - Keep Heating"); - } - } - else - { - if (mLocalTemperature <= heatingOnThreshold) - { - ChipLogDetail(AppServer, "Eval Heat - Turn on"); - SetRunningMode(ThermostatRunningModeEnum::kHeat); - } - else - { - ChipLogDetail(AppServer, "Eval Heat - Nothing to do"); - } - } -} - -void ThermostatManager::UpdateRunningModeForCooling() -{ - const int16_t coolingOffThreshold = mOccupiedCoolingSetpoint - static_cast(mOccupiedSetback * 10); - const int16_t coolingOnThreshold = mOccupiedCoolingSetpoint + static_cast(mOccupiedSetback * 10); - - if (mRunningMode == ThermostatRunningModeEnum::kCool) - { - if (mLocalTemperature <= coolingOffThreshold) - { - ChipLogDetail(AppServer, "Eval Cool - Turning off"); - SetRunningMode(ThermostatRunningModeEnum::kOff); - } - else - { - ChipLogDetail(AppServer, "Eval Cool - Keep Cooling"); - } - } - else - { - if (mLocalTemperature >= coolingOnThreshold) - { - ChipLogDetail(AppServer, "Eval Cool - Turn on"); - SetRunningMode(ThermostatRunningModeEnum::kCool); - } - else - { - ChipLogDetail(AppServer, "Eval Cool - Nothing to do"); - } - } -} - -static const char * SystemModeString(SystemModeEnum systemMode) -{ - switch (systemMode) - { - case SystemModeEnum::kOff: - return "Off"; - case SystemModeEnum::kAuto: - return "Auto"; - case SystemModeEnum::kCool: - return "Cool"; - case SystemModeEnum::kHeat: - return "Heat"; - default: - return "Unknown"; - } -} - -static const char * RunningModeString(ThermostatRunningModeEnum runningMode) -{ - switch (runningMode) - { - case ThermostatRunningModeEnum::kOff: - return "Off"; - case ThermostatRunningModeEnum::kCool: - return "Cool"; - case ThermostatRunningModeEnum::kHeat: - return "Heat"; - default: - return "Unknown"; - } -} - -void emberAfThermostatClusterInitCallback(EndpointId endpoint) -{ - ChipLogProgress(Zcl, "Starting Thermostat Manager"); - ThermostatManager().Init(); - - // Register the delegate for the Thermostat - auto & delegate = ThermostatDelegate::GetInstance(); - // Set the default delegate for endpoint kThermostatEndpoint. - VerifyOrDie(endpoint == kThermostatEndpoint); - SetDefaultDelegate(endpoint, &delegate); -} diff --git a/examples/thermostat/thermostat-common/BUILD.gn b/examples/thermostat/thermostat-common/BUILD.gn index 93a0c7540fb93a..1f8f839b4a4f3a 100644 --- a/examples/thermostat/thermostat-common/BUILD.gn +++ b/examples/thermostat/thermostat-common/BUILD.gn @@ -16,6 +16,10 @@ import("//build_overrides/chip.gni") import("${chip_root}/src/app/chip_data_model.gni") +config("config") { + include_dirs = [ "include" ] +} + chip_data_model("thermostat-common") { zap_file = "thermostat.zap" is_server = true diff --git a/examples/thermostat/linux/include/thermostat-delegate-impl.h b/examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h similarity index 92% rename from examples/thermostat/linux/include/thermostat-delegate-impl.h rename to examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h index 6bf9d02cea462f..9edf13f839df44 100644 --- a/examples/thermostat/linux/include/thermostat-delegate-impl.h +++ b/examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h @@ -44,9 +44,7 @@ class ThermostatDelegate : public Delegate public: static inline ThermostatDelegate & GetInstance() { return sInstance; } - std::optional - GetAtomicWriteTimeout(DataModel::DecodableList attributeRequests, - System::Clock::Milliseconds16 timeoutRequest) override; + std::optional GetMaxAtomicWriteTimeout(chip::AttributeId attributeId) override; CHIP_ERROR GetPresetTypeAtIndex(size_t index, Structs::PresetTypeStruct::Type & presetType) override; @@ -64,7 +62,7 @@ class ThermostatDelegate : public Delegate CHIP_ERROR GetPendingPresetAtIndex(size_t index, PresetStructWithOwnedMembers & preset) override; - CHIP_ERROR ApplyPendingPresets() override; + CHIP_ERROR CommitPendingPresets() override; void ClearPendingPresetList() override; diff --git a/examples/thermostat/linux/thermostat-delegate-impl.cpp b/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp similarity index 75% rename from examples/thermostat/linux/thermostat-delegate-impl.cpp rename to examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp index b931db20b7c4f7..8c411cd5a9176e 100644 --- a/examples/thermostat/linux/thermostat-delegate-impl.cpp +++ b/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp @@ -17,7 +17,6 @@ */ #include -#include #include #include @@ -36,34 +35,12 @@ ThermostatDelegate::ThermostatDelegate() mNextFreeIndexInPresetsList = 0; mNextFreeIndexInPendingPresetsList = 0; - InitializePresetTypes(); InitializePresets(); memset(mActivePresetHandleData, 0, sizeof(mActivePresetHandleData)); mActivePresetHandleDataSize = 0; } -void ThermostatDelegate::InitializePresetTypes() -{ - PresetScenarioEnum presetScenarioEnumArray[kMaxNumberOfPresetTypes] = { - PresetScenarioEnum::kOccupied, PresetScenarioEnum::kUnoccupied, PresetScenarioEnum::kSleep, - PresetScenarioEnum::kWake, PresetScenarioEnum::kVacation, PresetScenarioEnum::kGoingToSleep - }; - static_assert(ArraySize(presetScenarioEnumArray) <= ArraySize(mPresetTypes)); - - uint8_t index = 0; - for (PresetScenarioEnum presetScenario : presetScenarioEnumArray) - { - mPresetTypes[index].presetScenario = presetScenario; - mPresetTypes[index].numberOfPresets = kMaxNumberOfPresetsOfEachType; - mPresetTypes[index].presetTypeFeatures = - (presetScenario == PresetScenarioEnum::kOccupied || presetScenario == PresetScenarioEnum::kUnoccupied) - ? PresetTypeFeaturesBitmap::kAutomatic - : PresetTypeFeaturesBitmap::kSupportsNames; - index++; - } -} - void ThermostatDelegate::InitializePresets() { // Initialize the presets with 2 built in presets - occupied and unoccupied. @@ -94,9 +71,26 @@ void ThermostatDelegate::InitializePresets() CHIP_ERROR ThermostatDelegate::GetPresetTypeAtIndex(size_t index, PresetTypeStruct::Type & presetType) { - if (index < ArraySize(mPresetTypes)) + static PresetTypeStruct::Type presetTypes[] = { + { .presetScenario = PresetScenarioEnum::kOccupied, + .numberOfPresets = kMaxNumberOfPresetsOfEachType, + .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kAutomatic) }, + { .presetScenario = PresetScenarioEnum::kUnoccupied, + .numberOfPresets = kMaxNumberOfPresetsOfEachType, + .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kAutomatic) }, + { .presetScenario = PresetScenarioEnum::kSleep, + .numberOfPresets = kMaxNumberOfPresetsOfEachType, + .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) }, + { .presetScenario = PresetScenarioEnum::kWake, + .numberOfPresets = kMaxNumberOfPresetsOfEachType, + .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) }, + { .presetScenario = PresetScenarioEnum::kVacation, + .numberOfPresets = kMaxNumberOfPresetsOfEachType, + .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) }, + }; + if (index < ArraySize(presetTypes)) { - presetType = mPresetTypes[index]; + presetType = presetTypes[index]; return CHIP_NO_ERROR; } return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; @@ -158,45 +152,19 @@ CHIP_ERROR ThermostatDelegate::SetActivePresetHandle(const DataModel::Nullable -ThermostatDelegate::GetAtomicWriteTimeout(DataModel::DecodableList attributeRequests, - System::Clock::Milliseconds16 timeoutRequest) +std::optional ThermostatDelegate::GetMaxAtomicWriteTimeout(chip::AttributeId attributeId) { - auto attributeIdsIter = attributeRequests.begin(); - bool requestedPresets = false, requestedSchedules = false; - while (attributeIdsIter.Next()) - { - auto & attributeId = attributeIdsIter.GetValue(); - - switch (attributeId) - { - case Attributes::Presets::Id: - requestedPresets = true; - break; - case Attributes::Schedules::Id: - requestedSchedules = true; - break; - default: - return System::Clock::Milliseconds16(0); - } - } - if (attributeIdsIter.GetStatus() != CHIP_NO_ERROR) - { - return System::Clock::Milliseconds16(0); - } - auto timeout = System::Clock::Milliseconds16(0); - if (requestedPresets) + switch (attributeId) { + case Attributes::Presets::Id: // If the client expects to edit the presets, then we'll give it 3 seconds to do so - timeout += std::chrono::milliseconds(3000); - } - if (requestedSchedules) - { + return std::chrono::milliseconds(3000); + case Attributes::Schedules::Id: // If the client expects to edit the schedules, then we'll give it 9 seconds to do so - timeout += std::chrono::milliseconds(9000); + return std::chrono::milliseconds(9000); + default: + return std::nullopt; } - // If the client requested an even smaller timeout, then use that one - return std::min(timeoutRequest, timeout); } void ThermostatDelegate::InitializePendingPresets() @@ -238,7 +206,7 @@ CHIP_ERROR ThermostatDelegate::GetPendingPresetAtIndex(size_t index, PresetStruc return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; } -CHIP_ERROR ThermostatDelegate::ApplyPendingPresets() +CHIP_ERROR ThermostatDelegate::CommitPendingPresets() { mNextFreeIndexInPresetsList = 0; for (uint8_t indexInPendingPresets = 0; indexInPendingPresets < mNextFreeIndexInPendingPresetsList; indexInPendingPresets++) diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 3e4448a3bee467..01d47a47cb7184 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -428,6 +428,8 @@ template("chip_data_model") { ] } else if (cluster == "thermostat-server") { sources += [ + "${_app_root}/clusters/${cluster}/${cluster}-atomic.cpp", + "${_app_root}/clusters/${cluster}/${cluster}-presets.cpp", "${_app_root}/clusters/${cluster}/${cluster}.cpp", "${_app_root}/clusters/${cluster}/${cluster}.h", "${_app_root}/clusters/${cluster}/PresetStructWithOwnedMembers.cpp", diff --git a/src/app/clusters/thermostat-server/thermostat-delegate.h b/src/app/clusters/thermostat-server/thermostat-delegate.h index 0f89f69468099b..ccb690a34fba60 100644 --- a/src/app/clusters/thermostat-server/thermostat-delegate.h +++ b/src/app/clusters/thermostat-server/thermostat-delegate.h @@ -39,15 +39,12 @@ class Delegate virtual ~Delegate() = default; /** - * @brief Get the maximum timeout for atomically writing to a set of attributes + * @brief Get the maximum timeout for atomically writing to an attribute * - * @param[in] attributeRequests The list of attributes to write to. - * @param[out] timeoutRequest The timeout proposed by the client. - * @return The maximum allowed timeout; zero if the request is invalid. + * @param[in] attributeId The attribute to write to. + * @return The maximum allowed timeout; nullopt if the request is invalid. */ - virtual std::optional - GetAtomicWriteTimeout(DataModel::DecodableList attributeRequests, - System::Clock::Milliseconds16 timeoutRequest) = 0; + virtual std::optional GetMaxAtomicWriteTimeout(chip::AttributeId attributeId) = 0; /** * @brief Get the preset type at a given index in the PresetTypes attribute @@ -129,7 +126,7 @@ class Delegate * @return CHIP_ERROR if the updates to the presets attribute failed to commit for some reason. * */ - virtual CHIP_ERROR ApplyPendingPresets() = 0; + virtual CHIP_ERROR CommitPendingPresets() = 0; /** * @brief Clears the pending presets list. diff --git a/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp b/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp new file mode 100644 index 00000000000000..2a6e52e504887e --- /dev/null +++ b/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp @@ -0,0 +1,644 @@ +/** + * + * 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 "thermostat-server.h" + +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::Thermostat; +using namespace chip::app::Clusters::Thermostat::Attributes; +using namespace chip::app::Clusters::Thermostat::Structs; +using namespace chip::app::Clusters::Globals::Structs; +using namespace chip::Protocols::InteractionModel; + +namespace chip { +namespace app { +namespace Clusters { +namespace Thermostat { + +extern ThermostatAttrAccess gThermostatAttrAccess; + +/** + * @brief Callback that is called when the timeout for editing the presets expires. + * + * @param[in] systemLayer The system layer. + * @param[in] callbackContext The context passed to the timer callback. + */ +void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext) +{ + EndpointId endpoint = static_cast(reinterpret_cast(callbackContext)); + gThermostatAttrAccess.ResetAtomicWrite(endpoint); +} + +/** + * @brief Schedules a timer for the given timeout in milliseconds. + * + * @param[in] endpoint The endpoint to use. + * @param[in] timeoutMilliseconds The timeout in milliseconds. + */ +void ScheduleTimer(EndpointId endpoint, System::Clock::Milliseconds16 timeout) +{ + DeviceLayer::SystemLayer().StartTimer(timeout, TimerExpiredCallback, + reinterpret_cast(static_cast(endpoint))); +} + +/** + * @brief Clears the currently scheduled timer. + * + * @param[in] endpoint The endpoint to use. + */ +void ClearTimer(EndpointId endpoint) +{ + DeviceLayer::SystemLayer().CancelTimer(TimerExpiredCallback, reinterpret_cast(static_cast(endpoint))); +} + +/** + * @brief Get the source scoped node id. + * + * @param[in] commandObj The command handler object. + * + * @return The scoped node id of the source node. If the scoped node id is not retreived, return ScopedNodeId(). + */ +ScopedNodeId GetSourceScopedNodeId(CommandHandler * commandObj) +{ + ScopedNodeId sourceNodeId = ScopedNodeId(); + auto sessionHandle = commandObj->GetExchangeContext()->GetSessionHandle(); + + if (sessionHandle->IsSecureSession()) + { + sourceNodeId = sessionHandle->AsSecureSession()->GetPeer(); + } + else if (sessionHandle->IsGroupSession()) + { + sourceNodeId = sessionHandle->AsIncomingGroupSession()->GetPeer(); + } + return sourceNodeId; +} + +/** + * @brief Counts the number of attribute requests + * + * @param attributeRequests The decodable list of attribute IDs + * @param attributeRequestCount The total number of attribute requests + * @param requestedPresets Whether the Presets attribute was requested + * @param requestedSchedules Whether the Schedules attribute was requested + * @return true if the attribute list was counted + * @return false if there was an error reading the list + */ +bool CountAttributeRequests(const DataModel::DecodableList attributeRequests, size_t & attributeRequestCount, + bool & requestedPresets, bool & requestedSchedules) +{ + attributeRequestCount = 0; + requestedPresets = false; + requestedSchedules = false; + auto attributeIdsIter = attributeRequests.begin(); + while (attributeIdsIter.Next()) + { + auto & attributeId = attributeIdsIter.GetValue(); + switch (attributeId) + { + case Presets::Id: + requestedPresets = true; + break; + case Schedules::Id: + requestedSchedules = true; + break; + default: + break; + } + attributeRequestCount++; + } + return attributeIdsIter.GetStatus() == CHIP_NO_ERROR; +} + +/// @brief Builds the list of attribute statuses to return from an AtomicRequest invocation +/// @param endpoint The associated endpoint for the AtomicRequest invocation +/// @param attributeRequests The list of requested attributes +/// @param attributeStatusCount The number of attribute statuses in attributeStatuses +/// @param attributeStatuses The status of each requested attribute, plus additional attributes if needed +/// @return Status::Success if the request is valid, an error status if it is not +Status BuildAttributeStatuses(const EndpointId endpoint, const DataModel::DecodableList attributeRequests, + Platform::ScopedMemoryBufferWithSize & attributeStatuses) +{ + + bool requestedPresets = false, requestedSchedules = false; + size_t attributeStatusCount = 0; + if (!CountAttributeRequests(attributeRequests, attributeStatusCount, requestedPresets, requestedSchedules)) + { + // We errored reading the list + return Status::InvalidCommand; + } + if (attributeStatusCount == 0) + { + // List can't be empty + return Status::InvalidCommand; + } + attributeStatuses.Alloc(attributeStatusCount); + for (size_t i = 0; i < attributeStatusCount; ++i) + { + attributeStatuses[i].attributeID = kInvalidAttributeId; + attributeStatuses[i].statusCode = 0; + } + auto attributeIdsIter = attributeRequests.begin(); + size_t index = 0; + while (attributeIdsIter.Next()) + { + auto & attributeId = attributeIdsIter.GetValue(); + + for (size_t i = 0; i < index; ++i) + { + auto & attributeStatus = attributeStatuses[i]; + if (attributeStatus.attributeID == attributeId) + { + // Double-requesting an attribute is invalid + return Status::InvalidCommand; + } + } + attributeStatuses[index].attributeID = attributeId; + attributeStatuses[index].statusCode = to_underlying(Status::Success); + index++; + } + if (attributeIdsIter.GetStatus() != CHIP_NO_ERROR) + { + return Status::InvalidCommand; + } + for (size_t i = 0; i < index; ++i) + { + auto & attributeStatus = attributeStatuses[i]; + const EmberAfAttributeMetadata * metadata = + emberAfLocateAttributeMetadata(endpoint, Thermostat::Id, attributeStatus.attributeID); + + if (metadata == nullptr) + { + // This is not a valid attribute on the Thermostat cluster on the supplied endpoint + return Status::InvalidCommand; + } + } + return Status::Success; +} + +bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, Optional attributeId) +{ + + uint16_t ep = + emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); + + if (ep >= ArraySize(mAtomicWriteSessions)) + { + return false; + } + auto & atomicWriteSession = mAtomicWriteSessions[ep]; + if (atomicWriteSession.state != AtomicWriteState::Open) + { + return false; + } + if (!attributeId.HasValue()) + { + return true; + } + for (size_t i = 0; i < atomicWriteSession.attributeIds.AllocatedSize(); ++i) + { + if (atomicWriteSession.attributeIds[i] == attributeId.Value()) + { + return true; + } + } + return false; +} + +bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, const Access::SubjectDescriptor & subjectDescriptor, + Optional attributeId) +{ + if (!InAtomicWrite(endpoint, attributeId)) + { + return false; + } + return subjectDescriptor.authMode == Access::AuthMode::kCase && + GetAtomicWriteOriginatorScopedNodeId(endpoint) == ScopedNodeId(subjectDescriptor.subject, subjectDescriptor.fabricIndex); +} + +bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, CommandHandler * commandObj, Optional attributeId) +{ + if (!InAtomicWrite(endpoint, attributeId)) + { + return false; + } + ScopedNodeId sourceNodeId = GetSourceScopedNodeId(commandObj); + return GetAtomicWriteOriginatorScopedNodeId(endpoint) == sourceNodeId; +} + +bool ThermostatAttrAccess::InAtomicWrite( + EndpointId endpoint, CommandHandler * commandObj, + Platform::ScopedMemoryBufferWithSize & attributeStatuses) +{ + uint16_t ep = + emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); + + if (ep >= ArraySize(mAtomicWriteSessions)) + { + return false; + } + auto & atomicWriteSession = mAtomicWriteSessions[ep]; + if (atomicWriteSession.state != AtomicWriteState::Open) + { + return false; + } + if (atomicWriteSession.attributeIds.AllocatedSize() == 0 || + atomicWriteSession.attributeIds.AllocatedSize() != attributeStatuses.AllocatedSize()) + { + return false; + } + for (size_t i = 0; i < atomicWriteSession.attributeIds.AllocatedSize(); ++i) + { + bool hasAttribute = false; + auto attributeId = atomicWriteSession.attributeIds[i]; + for (size_t j = 0; j < attributeStatuses.AllocatedSize(); ++j) + { + auto & attributeStatus = attributeStatuses[j]; + if (attributeStatus.attributeID == attributeId) + { + hasAttribute = true; + break; + } + } + if (!hasAttribute) + { + return false; + } + } + return true; +} + +bool ThermostatAttrAccess::SetAtomicWrite( + EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state, + Platform::ScopedMemoryBufferWithSize & attributeStatuses) +{ + uint16_t ep = + emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); + + if (ep >= ArraySize(mAtomicWriteSessions)) + { + return false; + } + + auto & atomicWriteSession = mAtomicWriteSessions[ep]; + atomicWriteSession.endpointId = endpoint; + if (!atomicWriteSession.attributeIds.Alloc(attributeStatuses.AllocatedSize())) + { + atomicWriteSession.state = AtomicWriteState::Closed; + atomicWriteSession.nodeId = ScopedNodeId(); + return false; + } + + atomicWriteSession.state = state; + atomicWriteSession.nodeId = originatorNodeId; + + for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) + { + atomicWriteSession.attributeIds[i] = attributeStatuses[i].attributeID; + } + return true; +} + +void ThermostatAttrAccess::ResetAtomicWrite(EndpointId endpoint) +{ + auto delegate = GetDelegate(endpoint); + if (delegate != nullptr) + { + delegate->ClearPendingPresetList(); + } + ClearTimer(endpoint); + uint16_t ep = + emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); + + if (ep >= ArraySize(mAtomicWriteSessions)) + { + return; + } + auto & atomicWriteSession = mAtomicWriteSessions[ep]; + atomicWriteSession.state = AtomicWriteState::Closed; + atomicWriteSession.endpointId = endpoint; + atomicWriteSession.nodeId = ScopedNodeId(); + atomicWriteSession.attributeIds.Free(); +} + +ScopedNodeId ThermostatAttrAccess::GetAtomicWriteOriginatorScopedNodeId(const EndpointId endpoint) +{ + ScopedNodeId originatorNodeId = ScopedNodeId(); + uint16_t ep = + emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); + + if (ep < ArraySize(mAtomicWriteSessions)) + { + originatorNodeId = mAtomicWriteSessions[ep].nodeId; + } + return originatorNodeId; +} + +void SendAtomicResponse(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, Status status, + const Platform::ScopedMemoryBufferWithSize & attributeStatuses, + Optional timeout = NullOptional) +{ + Commands::AtomicResponse::Type response; + response.statusCode = to_underlying(status); + response.attributeStatus = + DataModel::List(attributeStatuses.Get(), attributeStatuses.AllocatedSize()); + response.timeout = timeout; + commandObj->AddResponse(commandPath, response); +} + +void ThermostatAttrAccess::BeginAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::AtomicRequest::DecodableType & commandData) +{ + EndpointId endpoint = commandPath.mEndpointId; + + auto delegate = GetDelegate(endpoint); + + if (delegate == nullptr) + { + ChipLogError(Zcl, "Delegate is null"); + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + Platform::ScopedMemoryBufferWithSize attributeStatuses; + auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatuses); + if (status != Status::Success) + { + commandObj->AddStatus(commandPath, status); + return; + } + + if (InAtomicWrite(endpoint, commandObj)) + { + // This client already has an open atomic write + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + if (!commandData.timeout.HasValue()) + { + commandObj->AddStatus(commandPath, Status::InvalidCommand); + return; + } + + auto maximumTimeout = System::Clock::Milliseconds16(0); + auto attributeIdsIter = commandData.attributeRequests.begin(); + while (attributeIdsIter.Next()) + { + auto & attributeId = attributeIdsIter.GetValue(); + switch (attributeId) + { + case Presets::Id: + case Schedules::Id: + auto attributeTimeout = delegate->GetMaxAtomicWriteTimeout(attributeId); + + if (attributeTimeout.has_value()) + { + // Add to the maximum timeout + maximumTimeout += attributeTimeout.value(); + } + break; + } + } + + status = Status::Success; + for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) + { + auto & attributeStatus = attributeStatuses[i]; + auto statusCode = Status::Success; + switch (attributeStatus.attributeID) + { + case Presets::Id: + case Schedules::Id: + statusCode = InAtomicWrite(endpoint, MakeOptional(attributeStatus.attributeID)) ? Status::Busy : Status::Success; + break; + default: + statusCode = Status::InvalidCommand; + break; + } + if (statusCode != Status::Success) + { + status = Status::Failure; + } + attributeStatus.statusCode = to_underlying(statusCode); + } + + auto timeout = std::min(System::Clock::Milliseconds16(commandData.timeout.Value()), maximumTimeout); + if (timeout.count() == 0) + { + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + if (status == Status::Success) + { + if (!SetAtomicWrite(endpoint, GetSourceScopedNodeId(commandObj), AtomicWriteState::Open, attributeStatuses)) + { + for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) + { + attributeStatuses[i].statusCode = to_underlying(Status::ResourceExhausted); + } + status = Status::Failure; + } + else + { + // This is a valid request to open an atomic write. Tell the delegate it + // needs to keep track of a pending preset list now. + delegate->InitializePendingPresets(); + ScheduleTimer(endpoint, timeout); + } + } + + SendAtomicResponse(commandObj, commandPath, status, attributeStatuses, MakeOptional(timeout.count())); +} + +void ThermostatAttrAccess::CommitAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::AtomicRequest::DecodableType & commandData) +{ + EndpointId endpoint = commandPath.mEndpointId; + auto delegate = GetDelegate(endpoint); + + if (delegate == nullptr) + { + ChipLogError(Zcl, "Delegate is null"); + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + Platform::ScopedMemoryBufferWithSize attributeStatuses; + auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatuses); + if (status != Status::Success) + { + commandObj->AddStatus(commandPath, status); + return; + } + + if (!InAtomicWrite(endpoint, commandObj, attributeStatuses)) + { + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + status = Status::Success; + for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) + { + auto & attributeStatus = attributeStatuses[i]; + auto statusCode = Status::Success; + switch (attributeStatus.attributeID) + { + case Presets::Id: + statusCode = PrecommitPresets(endpoint); + break; + case Schedules::Id: + statusCode = Status::Success; + break; + default: + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + attributeStatus.statusCode = to_underlying(statusCode); + if (statusCode != Status::Success) + { + status = Status::Failure; + } + } + + if (status == Status::Success) + { + for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) + { + auto & attributeStatus = attributeStatuses[i]; + auto statusCode = Status::Success; + CHIP_ERROR err; + switch (attributeStatus.attributeID) + { + case Presets::Id: + err = delegate->CommitPendingPresets(); + if (err != CHIP_NO_ERROR) + { + statusCode = Status::InvalidInState; + } + break; + case Schedules::Id: + break; + default: + // Not reachable, since we returned in this situation above. + break; + } + attributeStatus.statusCode = to_underlying(statusCode); + if (statusCode != Status::Success) + { + status = Status::Failure; + } + } + } + + ResetAtomicWrite(endpoint); + SendAtomicResponse(commandObj, commandPath, status, attributeStatuses); +} + +void ThermostatAttrAccess::RollbackAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::AtomicRequest::DecodableType & commandData) +{ + + EndpointId endpoint = commandPath.mEndpointId; + auto delegate = GetDelegate(endpoint); + + if (delegate == nullptr) + { + ChipLogError(Zcl, "Delegate is null"); + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + Platform::ScopedMemoryBufferWithSize attributeStatuses; + auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatuses); + if (status != Status::Success) + { + commandObj->AddStatus(commandPath, status); + return; + } + + if (!InAtomicWrite(endpoint, commandObj, attributeStatuses)) + { + // There's no open atomic write + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + ResetAtomicWrite(endpoint); + + for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) + { + attributeStatuses[i].statusCode = to_underlying(Status::Success); + } + + SendAtomicResponse(commandObj, commandPath, status, attributeStatuses); +} + +void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint) +{ + ChipLogProgress(Zcl, "Shutting down thermostat server cluster on endpoint %d", endpoint); + gThermostatAttrAccess.ResetAtomicWrite(endpoint); +} + +bool emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Clusters::Thermostat::Commands::AtomicRequest::DecodableType & commandData) +{ + auto & requestType = commandData.requestType; + + // If we've gotten this far, then the client has manage permission to call AtomicRequest, which is also the + // privilege necessary to write to the atomic attributes, so no need to check + + switch (requestType) + { + case Globals::AtomicRequestTypeEnum::kBeginWrite: + gThermostatAttrAccess.BeginAtomicWrite(commandObj, commandPath, commandData); + return true; + case Globals::AtomicRequestTypeEnum::kCommitWrite: + gThermostatAttrAccess.CommitAtomicWrite(commandObj, commandPath, commandData); + return true; + case Globals::AtomicRequestTypeEnum::kRollbackWrite: + gThermostatAttrAccess.RollbackAtomicWrite(commandObj, commandPath, commandData); + return true; + case Globals::AtomicRequestTypeEnum::kUnknownEnumValue: + commandObj->AddStatus(commandPath, Status::InvalidCommand); + return true; + } + + return false; +} + +} // namespace Thermostat +} // namespace Clusters +} // namespace app +} // namespace chip + +bool emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Clusters::Thermostat::Commands::AtomicRequest::DecodableType & commandData) +{ + return Thermostat::emberAfThermostatClusterAtomicRequestCallback(commandObj, commandPath, commandData); +} + +void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint) +{ + Thermostat::MatterThermostatClusterServerShutdownCallback(endpoint); +} diff --git a/src/app/clusters/thermostat-server/thermostat-server-presets.cpp b/src/app/clusters/thermostat-server/thermostat-server-presets.cpp new file mode 100644 index 00000000000000..e57c2f9c95f8fd --- /dev/null +++ b/src/app/clusters/thermostat-server/thermostat-server-presets.cpp @@ -0,0 +1,546 @@ +/** + * + * 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 "thermostat-server.h" + +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::Thermostat; +using namespace chip::app::Clusters::Thermostat::Attributes; +using namespace chip::app::Clusters::Thermostat::Structs; +using namespace chip::app::Clusters::Globals::Structs; +using namespace chip::Protocols::InteractionModel; + +namespace { + +/** + * @brief Check if a preset is valid. + * + * @param[in] preset The preset to check. + * + * @return true If the preset is valid i.e the PresetHandle (if not null) fits within size constraints and the presetScenario enum + * value is valid. Otherwise, return false. + */ +bool IsValidPresetEntry(const PresetStruct::Type & preset) +{ + // Check that the preset handle is not too long. + if (!preset.presetHandle.IsNull() && preset.presetHandle.Value().size() > kPresetHandleSize) + { + return false; + } + + // Ensure we have a valid PresetScenario. + return (preset.presetScenario != PresetScenarioEnum::kUnknownEnumValue); +} + +/** + * @brief Checks if the preset is built-in + * + * @param[in] preset The preset to check. + * + * @return true If the preset is built-in, false otherwise. + */ +bool IsBuiltIn(const PresetStructWithOwnedMembers & preset) +{ + return preset.GetBuiltIn().ValueOr(false); +} + +/** + * @brief Checks if the presets are matching i.e the presetHandles are the same. + * + * @param[in] preset The preset to check. + * @param[in] presetToMatch The preset to match with. + * + * @return true If the presets match, false otherwise. If both preset handles are null, returns false + */ +bool PresetHandlesExistAndMatch(const PresetStructWithOwnedMembers & preset, const PresetStructWithOwnedMembers & presetToMatch) +{ + return !preset.GetPresetHandle().IsNull() && !presetToMatch.GetPresetHandle().IsNull() && + preset.GetPresetHandle().Value().data_equal(presetToMatch.GetPresetHandle().Value()); +} + +/** + * @brief Finds an entry in the pending presets list that matches a preset. + * The presetHandle of the two presets must match. + * + * @param[in] delegate The delegate to use. + * @param[in] presetToMatch The preset to match with. + * + * @return true if a matching entry was found in the pending presets list, false otherwise. + */ +bool MatchingPendingPresetExists(Delegate * delegate, const PresetStructWithOwnedMembers & presetToMatch) +{ + VerifyOrReturnValue(delegate != nullptr, false); + + for (uint8_t i = 0; true; i++) + { + PresetStructWithOwnedMembers preset; + CHIP_ERROR err = delegate->GetPendingPresetAtIndex(i, preset); + + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + break; + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "MatchingPendingPresetExists: GetPendingPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, + err.Format()); + return false; + } + + if (PresetHandlesExistAndMatch(preset, presetToMatch)) + { + return true; + } + } + return false; +} + +/** + * @brief Finds and returns an entry in the Presets attribute list that matches + * a preset, if such an entry exists. The presetToMatch must have a preset handle. + * + * @param[in] delegate The delegate to use. + * @param[in] presetToMatch The preset to match with. + * @param[out] matchingPreset The preset in the Presets attribute list that has the same PresetHandle as the presetToMatch. + * + * @return true if a matching entry was found in the presets attribute list, false otherwise. + */ +bool GetMatchingPresetInPresets(Delegate * delegate, const PresetStruct::Type & presetToMatch, + PresetStructWithOwnedMembers & matchingPreset) +{ + VerifyOrReturnValue(delegate != nullptr, false); + + for (uint8_t i = 0; true; i++) + { + CHIP_ERROR err = delegate->GetPresetAtIndex(i, matchingPreset); + + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + break; + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "GetMatchingPresetInPresets: GetPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, err.Format()); + return false; + } + + // Note: presets coming from our delegate always have a handle. + if (presetToMatch.presetHandle.Value().data_equal(matchingPreset.GetPresetHandle().Value())) + { + return true; + } + } + return false; +} + +/** + * @brief Returns the length of the list of presets if the pending presets were to be applied. The size of the pending presets list + * calculated, after all the constraint checks are done, is the new size of the updated Presets attribute since the pending + * preset list is expected to have all existing presets with or without edits plus new presets. + * This is called before changes are actually applied. + * + * @param[in] delegate The delegate to use. + * + * @return count of the updated Presets attribute if the pending presets were applied to it. Return 0 for error cases. + */ +uint8_t CountNumberOfPendingPresets(Delegate * delegate) +{ + uint8_t numberOfPendingPresets = 0; + + VerifyOrReturnValue(delegate != nullptr, 0); + + for (uint8_t i = 0; true; i++) + { + PresetStructWithOwnedMembers pendingPreset; + CHIP_ERROR err = delegate->GetPendingPresetAtIndex(i, pendingPreset); + + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + break; + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "CountNumberOfPendingPresets: GetPendingPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, + err.Format()); + return 0; + } + numberOfPendingPresets++; + } + + return numberOfPendingPresets; +} + +/** + * @brief Checks if the presetScenario is present in the PresetTypes attribute. + * + * @param[in] delegate The delegate to use. + * @param[in] presetScenario The presetScenario to match with. + * + * @return true if the presetScenario is found, false otherwise. + */ +bool PresetScenarioExistsInPresetTypes(Delegate * delegate, PresetScenarioEnum presetScenario) +{ + VerifyOrReturnValue(delegate != nullptr, false); + + for (uint8_t i = 0; true; i++) + { + PresetTypeStruct::Type presetType; + auto err = delegate->GetPresetTypeAtIndex(i, presetType); + if (err != CHIP_NO_ERROR) + { + return false; + } + + if (presetType.presetScenario == presetScenario) + { + return true; + } + } + return false; +} + +/** + * @brief Returns the count of preset entries in the pending presets list that have the matching presetHandle. + * @param[in] delegate The delegate to use. + * @param[in] presetHandleToMatch The preset handle to match. + * + * @return count of the number of presets found with the matching presetHandle. Returns 0 if no matching presets were found. + */ +uint8_t CountPresetsInPendingListWithPresetHandle(Delegate * delegate, const ByteSpan & presetHandleToMatch) +{ + uint8_t count = 0; + VerifyOrReturnValue(delegate != nullptr, count); + + for (uint8_t i = 0; true; i++) + { + PresetStructWithOwnedMembers preset; + auto err = delegate->GetPendingPresetAtIndex(i, preset); + if (err != CHIP_NO_ERROR) + { + return count; + } + + DataModel::Nullable presetHandle = preset.GetPresetHandle(); + if (!presetHandle.IsNull() && presetHandle.Value().data_equal(presetHandleToMatch)) + { + count++; + } + } + return count; +} + +/** + * @brief Checks if the presetType for the given preset scenario supports name in the presetTypeFeatures bitmap. + * + * @param[in] delegate The delegate to use. + * @param[in] presetScenario The presetScenario to match with. + * + * @return true if the presetType for the given preset scenario supports name, false otherwise. + */ +bool PresetTypeSupportsNames(Delegate * delegate, PresetScenarioEnum scenario) +{ + VerifyOrReturnValue(delegate != nullptr, false); + + for (uint8_t i = 0; true; i++) + { + PresetTypeStruct::Type presetType; + auto err = delegate->GetPresetTypeAtIndex(i, presetType); + if (err != CHIP_NO_ERROR) + { + return false; + } + + if (presetType.presetScenario == scenario) + { + return (presetType.presetTypeFeatures.Has(PresetTypeFeaturesBitmap::kSupportsNames)); + } + } + return false; +} + +/** + * @brief Checks if the given preset handle is present in the presets attribute + * @param[in] delegate The delegate to use. + * @param[in] presetHandleToMatch The preset handle to match with. + * + * @return true if the given preset handle is present in the presets attribute list, false otherwise. + */ +bool IsPresetHandlePresentInPresets(Delegate * delegate, const ByteSpan & presetHandleToMatch) +{ + VerifyOrReturnValue(delegate != nullptr, false); + + PresetStructWithOwnedMembers matchingPreset; + for (uint8_t i = 0; true; i++) + { + CHIP_ERROR err = delegate->GetPresetAtIndex(i, matchingPreset); + + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + return false; + } + + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "IsPresetHandlePresentInPresets: GetPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, + err.Format()); + return false; + } + + if (!matchingPreset.GetPresetHandle().IsNull() && matchingPreset.GetPresetHandle().Value().data_equal(presetHandleToMatch)) + { + return true; + } + } + return false; +} + +} // namespace + +namespace chip { +namespace app { +namespace Clusters { +namespace Thermostat { + +extern ThermostatAttrAccess gThermostatAttrAccess; +extern int16_t EnforceHeatingSetpointLimits(int16_t HeatingSetpoint, EndpointId endpoint); +extern int16_t EnforceCoolingSetpointLimits(int16_t CoolingSetpoint, EndpointId endpoint); + +Status ThermostatAttrAccess::SetActivePreset(EndpointId endpoint, DataModel::Nullable presetHandle) +{ + + auto delegate = GetDelegate(endpoint); + + if (delegate == nullptr) + { + ChipLogError(Zcl, "Delegate is null"); + return Status::InvalidInState; + } + + // If the preset handle passed in the command is not present in the Presets attribute, return INVALID_COMMAND. + if (!presetHandle.IsNull() && !IsPresetHandlePresentInPresets(delegate, presetHandle.Value())) + { + return Status::InvalidCommand; + } + + CHIP_ERROR err = delegate->SetActivePresetHandle(presetHandle); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Failed to set ActivePresetHandle with error %" CHIP_ERROR_FORMAT, err.Format()); + return StatusIB(err).mStatus; + } + + return Status::Success; +} + +CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Thermostat::Delegate * delegate, const PresetStruct::Type & preset) +{ + if (!IsValidPresetEntry(preset)) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + + if (preset.presetHandle.IsNull()) + { + if (IsBuiltIn(preset)) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + } + else + { + auto & presetHandle = preset.presetHandle.Value(); + + // Per spec we need to check that: + // (a) There is an existing non-pending preset with this handle. + PresetStructWithOwnedMembers matchingPreset; + if (!GetMatchingPresetInPresets(delegate, preset, matchingPreset)) + { + return CHIP_IM_GLOBAL_STATUS(NotFound); + } + + // (b) There is no existing pending preset with this handle. + if (CountPresetsInPendingListWithPresetHandle(delegate, presetHandle) > 0) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + + // (c)/(d) The built-in fields do not have a mismatch. + // TODO: What's the story with nullability on the BuiltIn field? + if (!preset.builtIn.IsNull() && !matchingPreset.GetBuiltIn().IsNull() && + preset.builtIn.Value() != matchingPreset.GetBuiltIn().Value()) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + } + + if (!PresetScenarioExistsInPresetTypes(delegate, preset.presetScenario)) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + + if (preset.name.HasValue() && !PresetTypeSupportsNames(delegate, preset.presetScenario)) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + + return delegate->AppendToPendingPresetList(preset); +} + +Status ThermostatAttrAccess::PrecommitPresets(EndpointId endpoint) +{ + auto delegate = GetDelegate(endpoint); + + if (delegate == nullptr) + { + ChipLogError(Zcl, "Delegate is null"); + return Status::InvalidInState; + } + + CHIP_ERROR err = CHIP_NO_ERROR; + + // For each preset in the presets attribute, check that the matching preset in the pending presets list does not + // violate any spec constraints. + for (uint8_t i = 0; true; i++) + { + PresetStructWithOwnedMembers preset; + err = delegate->GetPresetAtIndex(i, preset); + + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + break; + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, + "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: GetPresetAtIndex failed with error " + "%" CHIP_ERROR_FORMAT, + err.Format()); + return Status::InvalidInState; + } + + bool found = MatchingPendingPresetExists(delegate, preset); + + // If a built in preset in the Presets attribute list is removed and not found in the pending presets list, return + // CONSTRAINT_ERROR. + if (IsBuiltIn(preset) && !found) + { + return Status::ConstraintError; + } + } + + // If there is an ActivePresetHandle set, find the preset in the pending presets list that matches the ActivePresetHandle + // attribute. If a preset is not found with the same presetHandle, return INVALID_IN_STATE. If there is no ActivePresetHandle + // attribute set, continue with other checks. + uint8_t buffer[kPresetHandleSize]; + MutableByteSpan activePresetHandleSpan(buffer); + auto activePresetHandle = DataModel::MakeNullable(activePresetHandleSpan); + + err = delegate->GetActivePresetHandle(activePresetHandle); + + if (err != CHIP_NO_ERROR) + { + return Status::InvalidInState; + } + + if (!activePresetHandle.IsNull()) + { + uint8_t count = CountPresetsInPendingListWithPresetHandle(delegate, activePresetHandle.Value()); + if (count == 0) + { + return Status::InvalidInState; + } + } + + // For each preset in the pending presets list, check that the preset does not violate any spec constraints. + for (uint8_t i = 0; true; i++) + { + PresetStructWithOwnedMembers pendingPreset; + err = delegate->GetPendingPresetAtIndex(i, pendingPreset); + + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + break; + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, + "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: GetPendingPresetAtIndex failed with error " + "%" CHIP_ERROR_FORMAT, + err.Format()); + return Status::InvalidInState; + } + + // Enforce the Setpoint Limits for both the cooling and heating setpoints in the pending preset. + // TODO: This code does not work, because it's modifying our temporary copy. + Optional coolingSetpointValue = pendingPreset.GetCoolingSetpoint(); + if (coolingSetpointValue.HasValue()) + { + pendingPreset.SetCoolingSetpoint(MakeOptional(EnforceCoolingSetpointLimits(coolingSetpointValue.Value(), endpoint))); + } + + Optional heatingSetpointValue = pendingPreset.GetHeatingSetpoint(); + if (heatingSetpointValue.HasValue()) + { + pendingPreset.SetHeatingSetpoint(MakeOptional(EnforceHeatingSetpointLimits(heatingSetpointValue.Value(), endpoint))); + } + } + + uint8_t totalCount = CountNumberOfPendingPresets(delegate); + + uint8_t numberOfPresetsSupported = delegate->GetNumberOfPresets(); + + if (numberOfPresetsSupported == 0) + { + ChipLogError(Zcl, "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: Failed to get NumberOfPresets"); + return Status::InvalidInState; + } + + // If the expected length of the presets attribute with the applied changes exceeds the total number of presets supported, + // return RESOURCE_EXHAUSTED. Note that the changes are not yet applied. + if (numberOfPresetsSupported > 0 && totalCount > numberOfPresetsSupported) + { + return Status::ResourceExhausted; + } + + // TODO: Check if the number of presets for each presetScenario exceeds the max number of presets supported for that + // scenario. We plan to support only one preset for each presetScenario for our use cases so defer this for re-evaluation. + return Status::Success; +} + +bool emberAfThermostatClusterSetActivePresetRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::SetActivePresetRequest::DecodableType & commandData) +{ + auto status = gThermostatAttrAccess.SetActivePreset(commandPath.mEndpointId, commandData.presetHandle); + commandObj->AddStatus(commandPath, status); + return true; +} + +} // namespace Thermostat +} // namespace Clusters +} // namespace app +} // namespace chip + +bool emberAfThermostatClusterSetActivePresetRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::SetActivePresetRequest::DecodableType & commandData) +{ + return Thermostat::emberAfThermostatClusterSetActivePresetRequestCallback(commandObj, commandPath, commandData); +} diff --git a/src/app/clusters/thermostat-server/thermostat-server.cpp b/src/app/clusters/thermostat-server/thermostat-server.cpp index 8fe70211eeda1a..fe8ccf8aab3cf0 100644 --- a/src/app/clusters/thermostat-server/thermostat-server.cpp +++ b/src/app/clusters/thermostat-server/thermostat-server.cpp @@ -30,7 +30,6 @@ #include #include #include -#include using namespace chip; using namespace chip::app; @@ -38,8 +37,7 @@ using namespace chip::app::Clusters; using namespace chip::app::Clusters::Thermostat; using namespace chip::app::Clusters::Thermostat::Structs; using namespace chip::app::Clusters::Thermostat::Attributes; - -using imcode = Protocols::InteractionModel::Status; +using namespace Protocols::InteractionModel; constexpr int16_t kDefaultAbsMinHeatSetpointLimit = 700; // 7C (44.5 F) is the default constexpr int16_t kDefaultAbsMaxHeatSetpointLimit = 3000; // 30C (86 F) is the default @@ -69,381 +67,16 @@ constexpr int8_t kDefaultDeadBand = 25; // 2.5C is the default #define FEATURE_MAP_DEFAULT FEATURE_MAP_HEAT | FEATURE_MAP_COOL | FEATURE_MAP_AUTO -namespace { - -ThermostatAttrAccess gThermostatAttrAccess; - static_assert(kThermostatEndpointCount <= kEmberInvalidEndpointIndex, "Thermostat Delegate table size error"); Delegate * gDelegateTable[kThermostatEndpointCount] = { nullptr }; -Delegate * GetDelegate(EndpointId endpoint) -{ - uint16_t ep = - emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - return (ep >= ArraySize(gDelegateTable) ? nullptr : gDelegateTable[ep]); -} - -/** - * @brief Check if a preset is valid. - * - * @param[in] preset The preset to check. - * - * @return true If the preset is valid i.e the PresetHandle (if not null) fits within size constraints and the presetScenario enum - * value is valid. Otherwise, return false. - */ -bool IsValidPresetEntry(const PresetStruct::Type & preset) -{ - // Check that the preset handle is not too long. - if (!preset.presetHandle.IsNull() && preset.presetHandle.Value().size() > kPresetHandleSize) - { - return false; - } - - // Ensure we have a valid PresetScenario. - return (preset.presetScenario != PresetScenarioEnum::kUnknownEnumValue); -} - -/** - * @brief Callback that is called when the timeout for editing the presets expires. - * - * @param[in] systemLayer The system layer. - * @param[in] callbackContext The context passed to the timer callback. - */ -void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext) -{ - EndpointId endpoint = static_cast(reinterpret_cast(callbackContext)); - - Delegate * delegate = GetDelegate(endpoint); - VerifyOrReturn(delegate != nullptr, ChipLogError(Zcl, "Delegate is null. Unable to handle timer expired")); - - delegate->ClearPendingPresetList(); - gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed); -} - -/** - * @brief Schedules a timer for the given timeout in milliseconds. - * - * @param[in] endpoint The endpoint to use. - * @param[in] timeoutMilliseconds The timeout in milliseconds. - */ -void ScheduleTimer(EndpointId endpoint, System::Clock::Milliseconds16 timeout) -{ - DeviceLayer::SystemLayer().StartTimer(timeout, TimerExpiredCallback, - reinterpret_cast(static_cast(endpoint))); -} - -/** - * @brief Clears the currently scheduled timer. - * - * @param[in] endpoint The endpoint to use. - */ -void ClearTimer(EndpointId endpoint) -{ - DeviceLayer::SystemLayer().CancelTimer(TimerExpiredCallback, reinterpret_cast(static_cast(endpoint))); -} - -/** - * @brief Checks if the preset is built-in - * - * @param[in] preset The preset to check. - * - * @return true If the preset is built-in, false otherwise. - */ -bool IsBuiltIn(const PresetStructWithOwnedMembers & preset) -{ - return preset.GetBuiltIn().ValueOr(false); -} - -/** - * @brief Checks if the presets are matching i.e the presetHandles are the same. - * - * @param[in] preset The preset to check. - * @param[in] presetToMatch The preset to match with. - * - * @return true If the presets match, false otherwise. If both preset handles are null, returns false - */ -bool PresetHandlesExistAndMatch(const PresetStructWithOwnedMembers & preset, const PresetStructWithOwnedMembers & presetToMatch) -{ - return !preset.GetPresetHandle().IsNull() && !presetToMatch.GetPresetHandle().IsNull() && - preset.GetPresetHandle().Value().data_equal(presetToMatch.GetPresetHandle().Value()); -} - -/** - * @brief Get the source scoped node id. - * - * @param[in] commandObj The command handler object. - * - * @return The scoped node id of the source node. If the scoped node id is not retreived, return ScopedNodeId(). - */ -ScopedNodeId GetSourceScopedNodeId(CommandHandler * commandObj) -{ - ScopedNodeId sourceNodeId = ScopedNodeId(); - auto sessionHandle = commandObj->GetExchangeContext()->GetSessionHandle(); - - if (sessionHandle->IsSecureSession()) - { - sourceNodeId = sessionHandle->AsSecureSession()->GetPeer(); - } - else if (sessionHandle->IsGroupSession()) - { - sourceNodeId = sessionHandle->AsIncomingGroupSession()->GetPeer(); - } - return sourceNodeId; -} - -/** - * @brief Discards pending atomic writes and atomic state. - * - * @param[in] delegate The delegate to use. - * @param[in] endpoint The endpoint to use. - * - */ -void resetAtomicWrite(Delegate * delegate, EndpointId endpoint) -{ - if (delegate != nullptr) - { - delegate->ClearPendingPresetList(); - } - ClearTimer(endpoint); - gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed); -} - -/** - * @brief Finds an entry in the pending presets list that matches a preset. - * The presetHandle of the two presets must match. - * - * @param[in] delegate The delegate to use. - * @param[in] presetToMatch The preset to match with. - * - * @return true if a matching entry was found in the pending presets list, false otherwise. - */ -bool MatchingPendingPresetExists(Delegate * delegate, const PresetStructWithOwnedMembers & presetToMatch) -{ - VerifyOrReturnValue(delegate != nullptr, false); - - for (uint8_t i = 0; true; i++) - { - PresetStructWithOwnedMembers preset; - CHIP_ERROR err = delegate->GetPendingPresetAtIndex(i, preset); - - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - break; - } - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "MatchingPendingPresetExists: GetPendingPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, - err.Format()); - return false; - } - - if (PresetHandlesExistAndMatch(preset, presetToMatch)) - { - return true; - } - } - return false; -} - -/** - * @brief Finds and returns an entry in the Presets attribute list that matches - * a preset, if such an entry exists. The presetToMatch must have a preset handle. - * - * @param[in] delegate The delegate to use. - * @param[in] presetToMatch The preset to match with. - * @param[out] matchingPreset The preset in the Presets attribute list that has the same PresetHandle as the presetToMatch. - * - * @return true if a matching entry was found in the presets attribute list, false otherwise. - */ -bool GetMatchingPresetInPresets(Delegate * delegate, const PresetStruct::Type & presetToMatch, - PresetStructWithOwnedMembers & matchingPreset) -{ - VerifyOrReturnValue(delegate != nullptr, false); - - for (uint8_t i = 0; true; i++) - { - CHIP_ERROR err = delegate->GetPresetAtIndex(i, matchingPreset); - - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - break; - } - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "GetMatchingPresetInPresets: GetPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, err.Format()); - return false; - } - - // Note: presets coming from our delegate always have a handle. - if (presetToMatch.presetHandle.Value().data_equal(matchingPreset.GetPresetHandle().Value())) - { - return true; - } - } - return false; -} - -/** - * @brief Checks if the given preset handle is present in the presets attribute - * @param[in] delegate The delegate to use. - * @param[in] presetHandleToMatch The preset handle to match with. - * - * @return true if the given preset handle is present in the presets attribute list, false otherwise. - */ -bool IsPresetHandlePresentInPresets(Delegate * delegate, const ByteSpan & presetHandleToMatch) -{ - VerifyOrReturnValue(delegate != nullptr, false); - - PresetStructWithOwnedMembers matchingPreset; - for (uint8_t i = 0; true; i++) - { - CHIP_ERROR err = delegate->GetPresetAtIndex(i, matchingPreset); - - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - return false; - } - - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "IsPresetHandlePresentInPresets: GetPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, - err.Format()); - return false; - } - - if (!matchingPreset.GetPresetHandle().IsNull() && matchingPreset.GetPresetHandle().Value().data_equal(presetHandleToMatch)) - { - return true; - } - } - return false; -} - -/** - * @brief Returns the length of the list of presets if the pending presets were to be applied. The size of the pending presets list - * calculated, after all the constraint checks are done, is the new size of the updated Presets attribute since the pending - * preset list is expected to have all existing presets with or without edits plus new presets. - * This is called before changes are actually applied. - * - * @param[in] delegate The delegate to use. - * - * @return count of the updated Presets attribute if the pending presets were applied to it. Return 0 for error cases. - */ -uint8_t CountNumberOfPendingPresets(Delegate * delegate) -{ - uint8_t numberOfPendingPresets = 0; - - VerifyOrReturnValue(delegate != nullptr, 0); - - for (uint8_t i = 0; true; i++) - { - PresetStructWithOwnedMembers pendingPreset; - CHIP_ERROR err = delegate->GetPendingPresetAtIndex(i, pendingPreset); - - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - break; - } - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "CountNumberOfPendingPresets: GetPendingPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, - err.Format()); - return 0; - } - numberOfPendingPresets++; - } - - return numberOfPendingPresets; -} - -/** - * @brief Checks if the presetScenario is present in the PresetTypes attribute. - * - * @param[in] delegate The delegate to use. - * @param[in] presetScenario The presetScenario to match with. - * - * @return true if the presetScenario is found, false otherwise. - */ -bool PresetScenarioExistsInPresetTypes(Delegate * delegate, PresetScenarioEnum presetScenario) -{ - VerifyOrReturnValue(delegate != nullptr, false); - - for (uint8_t i = 0; true; i++) - { - PresetTypeStruct::Type presetType; - auto err = delegate->GetPresetTypeAtIndex(i, presetType); - if (err != CHIP_NO_ERROR) - { - return false; - } - - if (presetType.presetScenario == presetScenario) - { - return true; - } - } - return false; -} - -/** - * @brief Returns the count of preset entries in the pending presets list that have the matching presetHandle. - * @param[in] delegate The delegate to use. - * @param[in] presetHandleToMatch The preset handle to match. - * - * @return count of the number of presets found with the matching presetHandle. Returns 0 if no matching presets were found. - */ -uint8_t CountPresetsInPendingListWithPresetHandle(Delegate * delegate, const ByteSpan & presetHandleToMatch) -{ - uint8_t count = 0; - VerifyOrReturnValue(delegate != nullptr, count); - - for (uint8_t i = 0; true; i++) - { - PresetStructWithOwnedMembers preset; - auto err = delegate->GetPendingPresetAtIndex(i, preset); - if (err != CHIP_NO_ERROR) - { - return count; - } - - DataModel::Nullable presetHandle = preset.GetPresetHandle(); - if (!presetHandle.IsNull() && presetHandle.Value().data_equal(presetHandleToMatch)) - { - count++; - } - } - return count; -} - -/** - * @brief Checks if the presetType for the given preset scenario supports name in the presetTypeFeatures bitmap. - * - * @param[in] delegate The delegate to use. - * @param[in] presetScenario The presetScenario to match with. - * - * @return true if the presetType for the given preset scenario supports name, false otherwise. - */ -bool PresetTypeSupportsNames(Delegate * delegate, PresetScenarioEnum scenario) -{ - VerifyOrReturnValue(delegate != nullptr, false); - - for (uint8_t i = 0; true; i++) - { - PresetTypeStruct::Type presetType; - auto err = delegate->GetPresetTypeAtIndex(i, presetType); - if (err != CHIP_NO_ERROR) - { - return false; - } +namespace chip { +namespace app { +namespace Clusters { +namespace Thermostat { - if (presetType.presetScenario == scenario) - { - return (presetType.presetTypeFeatures.Has(PresetTypeFeaturesBitmap::kSupportsNames)); - } - } - return false; -} +ThermostatAttrAccess gThermostatAttrAccess; int16_t EnforceHeatingSetpointLimits(int16_t HeatingSetpoint, EndpointId endpoint) { @@ -461,7 +94,7 @@ int16_t EnforceHeatingSetpointLimits(int16_t HeatingSetpoint, EndpointId endpoin // Note that the limits are initialized above per the spec limits // if they are not present Get() will not update the value so the defaults are used - imcode status; + Status status; // https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/3724 // behavior is not specified when Abs * values are not present and user values are present @@ -471,24 +104,24 @@ int16_t EnforceHeatingSetpointLimits(int16_t HeatingSetpoint, EndpointId endpoin // if a attribute is not present then it's default shall be used. status = AbsMinHeatSetpointLimit::Get(endpoint, &AbsMinHeatSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { ChipLogError(Zcl, "Warning: AbsMinHeatSetpointLimit missing using default"); } status = AbsMaxHeatSetpointLimit::Get(endpoint, &AbsMaxHeatSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { ChipLogError(Zcl, "Warning: AbsMaxHeatSetpointLimit missing using default"); } status = MinHeatSetpointLimit::Get(endpoint, &MinHeatSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { MinHeatSetpointLimit = AbsMinHeatSetpointLimit; } status = MaxHeatSetpointLimit::Get(endpoint, &MaxHeatSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { MaxHeatSetpointLimit = AbsMaxHeatSetpointLimit; } @@ -532,7 +165,7 @@ int16_t EnforceCoolingSetpointLimits(int16_t CoolingSetpoint, EndpointId endpoin // Note that the limits are initialized above per the spec limits // if they are not present Get() will not update the value so the defaults are used - imcode status; + Status status; // https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/3724 // behavior is not specified when Abs * values are not present and user values are present @@ -542,25 +175,25 @@ int16_t EnforceCoolingSetpointLimits(int16_t CoolingSetpoint, EndpointId endpoin // if a attribute is not present then it's default shall be used. status = AbsMinCoolSetpointLimit::Get(endpoint, &AbsMinCoolSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { ChipLogError(Zcl, "Warning: AbsMinCoolSetpointLimit missing using default"); } status = AbsMaxCoolSetpointLimit::Get(endpoint, &AbsMaxCoolSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { ChipLogError(Zcl, "Warning: AbsMaxCoolSetpointLimit missing using default"); } status = MinCoolSetpointLimit::Get(endpoint, &MinCoolSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { MinCoolSetpointLimit = AbsMinCoolSetpointLimit; } status = MaxCoolSetpointLimit::Get(endpoint, &MaxCoolSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { MaxCoolSetpointLimit = AbsMaxCoolSetpointLimit; } @@ -587,81 +220,22 @@ int16_t EnforceCoolingSetpointLimits(int16_t CoolingSetpoint, EndpointId endpoin return CoolingSetpoint; } -} // anonymous namespace - -namespace chip { -namespace app { -namespace Clusters { -namespace Thermostat { - -void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate) -{ - uint16_t ep = - emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - // if endpoint is found, add the delegate in the delegate table - if (ep < ArraySize(gDelegateTable)) - { - gDelegateTable[ep] = delegate; - } -} - -void ThermostatAttrAccess::SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state) -{ - uint16_t ep = - emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - - if (ep < ArraySize(mAtomicWriteSessions)) - { - mAtomicWriteSessions[ep].state = state; - mAtomicWriteSessions[ep].endpointId = endpoint; - mAtomicWriteSessions[ep].nodeId = originatorNodeId; - } -} - -bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint) +Delegate * GetDelegate(EndpointId endpoint) { - bool inAtomicWrite = false; uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - - if (ep < ArraySize(mAtomicWriteSessions)) - { - inAtomicWrite = (mAtomicWriteSessions[ep].state == kAtomicWriteState_Open); - } - return inAtomicWrite; -} - -bool ThermostatAttrAccess::InAtomicWrite(const Access::SubjectDescriptor & subjectDescriptor, EndpointId endpoint) -{ - if (!InAtomicWrite(endpoint)) - { - return false; - } - return subjectDescriptor.authMode == Access::AuthMode::kCase && - GetAtomicWriteScopedNodeId(endpoint) == ScopedNodeId(subjectDescriptor.subject, subjectDescriptor.fabricIndex); -} - -bool ThermostatAttrAccess::InAtomicWrite(CommandHandler * commandObj, EndpointId endpoint) -{ - if (!InAtomicWrite(endpoint)) - { - return false; - } - ScopedNodeId sourceNodeId = GetSourceScopedNodeId(commandObj); - return GetAtomicWriteScopedNodeId(endpoint) == sourceNodeId; + return (ep >= ArraySize(gDelegateTable) ? nullptr : gDelegateTable[ep]); } -ScopedNodeId ThermostatAttrAccess::GetAtomicWriteScopedNodeId(EndpointId endpoint) +void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate) { - ScopedNodeId originatorNodeId = ScopedNodeId(); uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - - if (ep < ArraySize(mAtomicWriteSessions)) + // if endpoint is found, add the delegate in the delegate table + if (ep < ArraySize(gDelegateTable)) { - originatorNodeId = mAtomicWriteSessions[ep].nodeId; + gDelegateTable[ep] = delegate; } - return originatorNodeId; } CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) @@ -669,7 +243,7 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A VerifyOrDie(aPath.mClusterId == Thermostat::Id); uint32_t ourFeatureMap; - bool localTemperatureNotExposedSupported = (FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == imcode::Success) && + bool localTemperatureNotExposedSupported = (FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == Status::Success) && ((ourFeatureMap & to_underlying(Feature::kLocalTemperatureNotExposed)) != 0); switch (aPath.mAttributeId) @@ -684,8 +258,8 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A if (localTemperatureNotExposedSupported) { BitMask valueRemoteSensing; - imcode status = RemoteSensing::Get(aPath.mEndpointId, &valueRemoteSensing); - if (status != imcode::Success) + Status status = RemoteSensing::Get(aPath.mEndpointId, &valueRemoteSensing); + if (status != Status::Success) { StatusIB statusIB(status); return statusIB.ToChipError(); @@ -725,7 +299,7 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); auto & subjectDescriptor = aEncoder.GetSubjectDescriptor(); - if (InAtomicWrite(subjectDescriptor, aPath.mEndpointId)) + if (InAtomicWrite(aPath.mEndpointId, subjectDescriptor, MakeOptional(aPath.mAttributeId))) { return aEncoder.EncodeList([delegate](const auto & encoder) -> CHIP_ERROR { for (uint8_t i = 0; true; i++) @@ -801,12 +375,12 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath, VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); // Presets are not editable, return INVALID_IN_STATE. - VerifyOrReturnError(InAtomicWrite(endpoint), CHIP_IM_GLOBAL_STATUS(InvalidInState), + VerifyOrReturnError(InAtomicWrite(endpoint, MakeOptional(aPath.mAttributeId)), CHIP_IM_GLOBAL_STATUS(InvalidInState), ChipLogError(Zcl, "Presets are not editable")); // OK, we're in an atomic write, make sure the requesting node is the same one that started the atomic write, // otherwise return BUSY. - if (!InAtomicWrite(subjectDescriptor, endpoint)) + if (!InAtomicWrite(endpoint, subjectDescriptor, MakeOptional(aPath.mAttributeId))) { ChipLogError(Zcl, "Another node is editing presets. Server is busy. Try again later"); return CHIP_IM_GLOBAL_STATUS(Busy); @@ -848,14 +422,14 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath, } // This is not an atomic attribute, so check to make sure we don't have an atomic write going for this client - if (InAtomicWrite(subjectDescriptor, endpoint)) + if (InAtomicWrite(endpoint, subjectDescriptor)) { ChipLogError(Zcl, "Can not write to non-atomic attributes during atomic write"); return CHIP_IM_GLOBAL_STATUS(InvalidInState); } uint32_t ourFeatureMap; - bool localTemperatureNotExposedSupported = (FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == imcode::Success) && + bool localTemperatureNotExposedSupported = (FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == Status::Success) && ((ourFeatureMap & to_underlying(Feature::kLocalTemperatureNotExposed)) != 0); switch (aPath.mAttributeId) @@ -869,7 +443,7 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath, { return CHIP_IM_GLOBAL_STATUS(ConstraintError); } - imcode status = RemoteSensing::Set(aPath.mEndpointId, valueRemoteSensing); + Status status = RemoteSensing::Set(aPath.mEndpointId, valueRemoteSensing); StatusIB statusIB(status); return statusIB.ToChipError(); } @@ -882,73 +456,14 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath, return CHIP_NO_ERROR; } -CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Thermostat::Delegate * delegate, const PresetStruct::Type & preset) -{ - if (!IsValidPresetEntry(preset)) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - - if (preset.presetHandle.IsNull()) - { - if (IsBuiltIn(preset)) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - } - else - { - auto & presetHandle = preset.presetHandle.Value(); - - // Per spec we need to check that: - // (a) There is an existing non-pending preset with this handle. - PresetStructWithOwnedMembers matchingPreset; - if (!GetMatchingPresetInPresets(delegate, preset, matchingPreset)) - { - return CHIP_IM_GLOBAL_STATUS(NotFound); - } - - // (b) There is no existing pending preset with this handle. - if (CountPresetsInPendingListWithPresetHandle(delegate, presetHandle) > 0) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - - // (c)/(d) The built-in fields do not have a mismatch. - // TODO: What's the story with nullability on the BuiltIn field? - if (!preset.builtIn.IsNull() && !matchingPreset.GetBuiltIn().IsNull() && - preset.builtIn.Value() != matchingPreset.GetBuiltIn().Value()) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - } - - if (!PresetScenarioExistsInPresetTypes(delegate, preset.presetScenario)) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - - if (preset.name.HasValue() && !PresetTypeSupportsNames(delegate, preset.presetScenario)) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - - return delegate->AppendToPendingPresetList(preset); -} - void ThermostatAttrAccess::OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) { for (size_t i = 0; i < ArraySize(mAtomicWriteSessions); ++i) { - auto atomicWriteState = mAtomicWriteSessions[i]; - if (atomicWriteState.state == kAtomicWriteState_Open && atomicWriteState.nodeId.GetFabricIndex() == fabricIndex) + auto & atomicWriteState = mAtomicWriteSessions[i]; + if (atomicWriteState.state == AtomicWriteState::Open && atomicWriteState.nodeId.GetFabricIndex() == fabricIndex) { - auto delegate = GetDelegate(atomicWriteState.endpointId); - if (delegate == nullptr) - { - continue; - } - resetAtomicWrite(delegate, atomicWriteState.endpointId); + ResetAtomicWrite(atomicWriteState.endpointId); } } } @@ -1003,7 +518,7 @@ MatterThermostatClusterServerPreAttributeChangedCallback(const app::ConcreteAttr bool CoolSupported = false; bool OccupancySupported = false; - if (FeatureMap::Get(endpoint, &OurFeatureMap) != imcode::Success) + if (FeatureMap::Get(endpoint, &OurFeatureMap) != Status::Success) OurFeatureMap = FEATURE_MAP_DEFAULT; if (OurFeatureMap & 1 << 5) // Bit 5 is Auto Mode supported @@ -1020,63 +535,63 @@ MatterThermostatClusterServerPreAttributeChangedCallback(const app::ConcreteAttr if (AutoSupported) { - if (MinSetpointDeadBand::Get(endpoint, &DeadBand) != imcode::Success) + if (MinSetpointDeadBand::Get(endpoint, &DeadBand) != Status::Success) { DeadBand = kDefaultDeadBand; } DeadBandTemp = static_cast(DeadBand * 10); } - if (AbsMinCoolSetpointLimit::Get(endpoint, &AbsMinCoolSetpointLimit) != imcode::Success) + if (AbsMinCoolSetpointLimit::Get(endpoint, &AbsMinCoolSetpointLimit) != Status::Success) AbsMinCoolSetpointLimit = kDefaultAbsMinCoolSetpointLimit; - if (AbsMaxCoolSetpointLimit::Get(endpoint, &AbsMaxCoolSetpointLimit) != imcode::Success) + if (AbsMaxCoolSetpointLimit::Get(endpoint, &AbsMaxCoolSetpointLimit) != Status::Success) AbsMaxCoolSetpointLimit = kDefaultAbsMaxCoolSetpointLimit; - if (MinCoolSetpointLimit::Get(endpoint, &MinCoolSetpointLimit) != imcode::Success) + if (MinCoolSetpointLimit::Get(endpoint, &MinCoolSetpointLimit) != Status::Success) MinCoolSetpointLimit = AbsMinCoolSetpointLimit; - if (MaxCoolSetpointLimit::Get(endpoint, &MaxCoolSetpointLimit) != imcode::Success) + if (MaxCoolSetpointLimit::Get(endpoint, &MaxCoolSetpointLimit) != Status::Success) MaxCoolSetpointLimit = AbsMaxCoolSetpointLimit; - if (AbsMinHeatSetpointLimit::Get(endpoint, &AbsMinHeatSetpointLimit) != imcode::Success) + if (AbsMinHeatSetpointLimit::Get(endpoint, &AbsMinHeatSetpointLimit) != Status::Success) AbsMinHeatSetpointLimit = kDefaultAbsMinHeatSetpointLimit; - if (AbsMaxHeatSetpointLimit::Get(endpoint, &AbsMaxHeatSetpointLimit) != imcode::Success) + if (AbsMaxHeatSetpointLimit::Get(endpoint, &AbsMaxHeatSetpointLimit) != Status::Success) AbsMaxHeatSetpointLimit = kDefaultAbsMaxHeatSetpointLimit; - if (MinHeatSetpointLimit::Get(endpoint, &MinHeatSetpointLimit) != imcode::Success) + if (MinHeatSetpointLimit::Get(endpoint, &MinHeatSetpointLimit) != Status::Success) MinHeatSetpointLimit = AbsMinHeatSetpointLimit; - if (MaxHeatSetpointLimit::Get(endpoint, &MaxHeatSetpointLimit) != imcode::Success) + if (MaxHeatSetpointLimit::Get(endpoint, &MaxHeatSetpointLimit) != Status::Success) MaxHeatSetpointLimit = AbsMaxHeatSetpointLimit; if (CoolSupported) - if (OccupiedCoolingSetpoint::Get(endpoint, &OccupiedCoolingSetpoint) != imcode::Success) + if (OccupiedCoolingSetpoint::Get(endpoint, &OccupiedCoolingSetpoint) != Status::Success) { ChipLogError(Zcl, "Error: Can not read Occupied Cooling Setpoint"); - return imcode::Failure; + return Status::Failure; } if (HeatSupported) - if (OccupiedHeatingSetpoint::Get(endpoint, &OccupiedHeatingSetpoint) != imcode::Success) + if (OccupiedHeatingSetpoint::Get(endpoint, &OccupiedHeatingSetpoint) != Status::Success) { ChipLogError(Zcl, "Error: Can not read Occupied Heating Setpoint"); - return imcode::Failure; + return Status::Failure; } if (CoolSupported && OccupancySupported) - if (UnoccupiedCoolingSetpoint::Get(endpoint, &UnoccupiedCoolingSetpoint) != imcode::Success) + if (UnoccupiedCoolingSetpoint::Get(endpoint, &UnoccupiedCoolingSetpoint) != Status::Success) { ChipLogError(Zcl, "Error: Can not read Unoccupied Cooling Setpoint"); - return imcode::Failure; + return Status::Failure; } if (HeatSupported && OccupancySupported) - if (UnoccupiedHeatingSetpoint::Get(endpoint, &UnoccupiedHeatingSetpoint) != imcode::Success) + if (UnoccupiedHeatingSetpoint::Get(endpoint, &UnoccupiedHeatingSetpoint) != Status::Success) { ChipLogError(Zcl, "Error: Can not read Unoccupied Heating Setpoint"); - return imcode::Failure; + return Status::Failure; } switch (attributePath.mAttributeId) @@ -1084,143 +599,143 @@ MatterThermostatClusterServerPreAttributeChangedCallback(const app::ConcreteAttr case OccupiedHeatingSetpoint::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!HeatSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinHeatSetpointLimit || requested < MinHeatSetpointLimit || requested > AbsMaxHeatSetpointLimit || requested > MaxHeatSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested > OccupiedCoolingSetpoint - DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case OccupiedCoolingSetpoint::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!CoolSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinCoolSetpointLimit || requested < MinCoolSetpointLimit || requested > AbsMaxCoolSetpointLimit || requested > MaxCoolSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested < OccupiedHeatingSetpoint + DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case UnoccupiedHeatingSetpoint::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!(HeatSupported && OccupancySupported)) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinHeatSetpointLimit || requested < MinHeatSetpointLimit || requested > AbsMaxHeatSetpointLimit || requested > MaxHeatSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested > UnoccupiedCoolingSetpoint - DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case UnoccupiedCoolingSetpoint::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!(CoolSupported && OccupancySupported)) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinCoolSetpointLimit || requested < MinCoolSetpointLimit || requested > AbsMaxCoolSetpointLimit || requested > MaxCoolSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested < UnoccupiedHeatingSetpoint + DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case MinHeatSetpointLimit::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!HeatSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinHeatSetpointLimit || requested > MaxHeatSetpointLimit || requested > AbsMaxHeatSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested > MinCoolSetpointLimit - DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case MaxHeatSetpointLimit::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!HeatSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinHeatSetpointLimit || requested < MinHeatSetpointLimit || requested > AbsMaxHeatSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested > MaxCoolSetpointLimit - DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case MinCoolSetpointLimit::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!CoolSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinCoolSetpointLimit || requested > MaxCoolSetpointLimit || requested > AbsMaxCoolSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested < MinHeatSetpointLimit + DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case MaxCoolSetpointLimit::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!CoolSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinCoolSetpointLimit || requested < MinCoolSetpointLimit || requested > AbsMaxCoolSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested < MaxHeatSetpointLimit + DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case MinSetpointDeadBand::Id: { requested = *value; if (!AutoSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < 0 || requested > 25) - return imcode::InvalidValue; - return imcode::Success; + return Status::InvalidValue; + return Status::Success; } case ControlSequenceOfOperation::Id: { uint8_t requestedCSO; requestedCSO = *value; if (requestedCSO > to_underlying(ControlSequenceOfOperationEnum::kCoolingAndHeatingWithReheat)) - return imcode::InvalidValue; - return imcode::Success; + return Status::InvalidValue; + return Status::Success; } case SystemMode::Id: { ControlSequenceOfOperationEnum ControlSequenceOfOperation; - imcode status = ControlSequenceOfOperation::Get(endpoint, &ControlSequenceOfOperation); - if (status != imcode::Success) + Status status = ControlSequenceOfOperation::Get(endpoint, &ControlSequenceOfOperation); + if (status != Status::Success) { - return imcode::InvalidValue; + return Status::InvalidValue; } auto RequestedSystemMode = static_cast(*value); if (ControlSequenceOfOperation > ControlSequenceOfOperationEnum::kCoolingAndHeatingWithReheat || RequestedSystemMode > SystemModeEnum::kFanOnly) { - return imcode::InvalidValue; + return Status::InvalidValue; } switch (ControlSequenceOfOperation) @@ -1228,22 +743,22 @@ MatterThermostatClusterServerPreAttributeChangedCallback(const app::ConcreteAttr case ControlSequenceOfOperationEnum::kCoolingOnly: case ControlSequenceOfOperationEnum::kCoolingWithReheat: if (RequestedSystemMode == SystemModeEnum::kHeat || RequestedSystemMode == SystemModeEnum::kEmergencyHeat) - return imcode::InvalidValue; + return Status::InvalidValue; else - return imcode::Success; + return Status::Success; case ControlSequenceOfOperationEnum::kHeatingOnly: case ControlSequenceOfOperationEnum::kHeatingWithReheat: if (RequestedSystemMode == SystemModeEnum::kCool || RequestedSystemMode == SystemModeEnum::kPrecooling) - return imcode::InvalidValue; + return Status::InvalidValue; else - return imcode::Success; + return Status::Success; default: - return imcode::Success; + return Status::Success; } } default: - return imcode::Success; + return Status::Success; } } @@ -1279,363 +794,6 @@ bool emberAfThermostatClusterSetActiveScheduleRequestCallback( return false; } -bool emberAfThermostatClusterSetActivePresetRequestCallback( - CommandHandler * commandObj, const ConcreteCommandPath & commandPath, - const Clusters::Thermostat::Commands::SetActivePresetRequest::DecodableType & commandData) -{ - EndpointId endpoint = commandPath.mEndpointId; - Delegate * delegate = GetDelegate(endpoint); - - if (delegate == nullptr) - { - ChipLogError(Zcl, "Delegate is null"); - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return true; - } - - DataModel::Nullable newPresetHandle = commandData.presetHandle; - - // If the preset handle passed in the command is not present in the Presets attribute, return INVALID_COMMAND. - if (!newPresetHandle.IsNull() && !IsPresetHandlePresentInPresets(delegate, newPresetHandle.Value())) - { - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return true; - } - - CHIP_ERROR err = delegate->SetActivePresetHandle(newPresetHandle); - - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "Failed to set ActivePresetHandle with error %" CHIP_ERROR_FORMAT, err.Format()); - commandObj->AddStatus(commandPath, StatusIB(err).mStatus); - return true; - } - - commandObj->AddStatus(commandPath, imcode::Success); - return true; -} - -bool validAtomicAttributes(const Commands::AtomicRequest::DecodableType & commandData, bool requireBoth) -{ - auto attributeIdsIter = commandData.attributeRequests.begin(); - bool requestedPresets = false, requestedSchedules = false; - while (attributeIdsIter.Next()) - { - auto & attributeId = attributeIdsIter.GetValue(); - - switch (attributeId) - { - case Presets::Id: - if (requestedPresets) // Double-requesting an attribute is invalid - { - return false; - } - requestedPresets = true; - break; - case Schedules::Id: - if (requestedSchedules) // Double-requesting an attribute is invalid - { - return false; - } - requestedSchedules = true; - break; - default: - return false; - } - } - if (attributeIdsIter.GetStatus() != CHIP_NO_ERROR) - { - return false; - } - if (requireBoth) - { - return (requestedPresets && requestedSchedules); - } - // If the atomic request doesn't contain at least one of these attributes, it's invalid - return (requestedPresets || requestedSchedules); -} - -void sendAtomicResponse(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, imcode status, imcode presetsStatus, - imcode schedulesStatus, Optional timeout = NullOptional) -{ - Commands::AtomicResponse::Type response; - Globals::Structs::AtomicAttributeStatusStruct::Type attributeStatus[] = { - { .attributeID = Presets::Id, .statusCode = to_underlying(presetsStatus) }, - { .attributeID = Schedules::Id, .statusCode = to_underlying(schedulesStatus) } - }; - response.statusCode = to_underlying(status); - response.attributeStatus = attributeStatus; - response.timeout = timeout; - commandObj->AddResponse(commandPath, response); -} - -void handleAtomicBegin(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, - const Commands::AtomicRequest::DecodableType & commandData) -{ - EndpointId endpoint = commandPath.mEndpointId; - - Delegate * delegate = GetDelegate(endpoint); - - if (delegate == nullptr) - { - ChipLogError(Zcl, "Delegate is null"); - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return; - } - - if (gThermostatAttrAccess.InAtomicWrite(commandObj, endpoint)) - { - // This client already has an open atomic write - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return; - } - - if (!commandData.timeout.HasValue()) - { - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return; - } - - if (!validAtomicAttributes(commandData, false)) - { - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return; - } - - if (gThermostatAttrAccess.InAtomicWrite(endpoint)) - { - sendAtomicResponse(commandObj, commandPath, imcode::Failure, imcode::Busy, imcode::Busy); - return; - } - - // This is a valid request to open an atomic write. Tell the delegate it - // needs to keep track of a pending preset list now. - delegate->InitializePendingPresets(); - - auto timeout = - delegate->GetAtomicWriteTimeout(commandData.attributeRequests, System::Clock::Milliseconds16(commandData.timeout.Value())); - - if (!timeout.has_value()) - { - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return; - } - ScheduleTimer(endpoint, timeout.value()); - gThermostatAttrAccess.SetAtomicWrite(endpoint, GetSourceScopedNodeId(commandObj), kAtomicWriteState_Open); - sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success, - MakeOptional(timeout.value().count())); -} - -imcode commitPresets(Delegate * delegate, EndpointId endpoint) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - - // For each preset in the presets attribute, check that the matching preset in the pending presets list does not - // violate any spec constraints. - for (uint8_t i = 0; true; i++) - { - PresetStructWithOwnedMembers preset; - err = delegate->GetPresetAtIndex(i, preset); - - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - break; - } - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, - "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: GetPresetAtIndex failed with error " - "%" CHIP_ERROR_FORMAT, - err.Format()); - return imcode::InvalidInState; - } - - bool found = MatchingPendingPresetExists(delegate, preset); - - // If a built in preset in the Presets attribute list is removed and not found in the pending presets list, return - // CONSTRAINT_ERROR. - if (IsBuiltIn(preset) && !found) - { - return imcode::ConstraintError; - } - } - - // If there is an ActivePresetHandle set, find the preset in the pending presets list that matches the ActivePresetHandle - // attribute. If a preset is not found with the same presetHandle, return INVALID_IN_STATE. If there is no ActivePresetHandle - // attribute set, continue with other checks. - uint8_t buffer[kPresetHandleSize]; - MutableByteSpan activePresetHandleSpan(buffer); - auto activePresetHandle = DataModel::MakeNullable(activePresetHandleSpan); - - err = delegate->GetActivePresetHandle(activePresetHandle); - - if (err != CHIP_NO_ERROR) - { - return imcode::InvalidInState; - } - - if (!activePresetHandle.IsNull()) - { - uint8_t count = CountPresetsInPendingListWithPresetHandle(delegate, activePresetHandle.Value()); - if (count == 0) - { - return imcode::InvalidInState; - } - } - - // For each preset in the pending presets list, check that the preset does not violate any spec constraints. - for (uint8_t i = 0; true; i++) - { - PresetStructWithOwnedMembers pendingPreset; - err = delegate->GetPendingPresetAtIndex(i, pendingPreset); - - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - break; - } - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, - "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: GetPendingPresetAtIndex failed with error " - "%" CHIP_ERROR_FORMAT, - err.Format()); - return imcode::InvalidInState; - } - - // Enforce the Setpoint Limits for both the cooling and heating setpoints in the pending preset. - // TODO: This code does not work, because it's modifying our temporary copy. - Optional coolingSetpointValue = pendingPreset.GetCoolingSetpoint(); - if (coolingSetpointValue.HasValue()) - { - pendingPreset.SetCoolingSetpoint(MakeOptional(EnforceCoolingSetpointLimits(coolingSetpointValue.Value(), endpoint))); - } - - Optional heatingSetpointValue = pendingPreset.GetHeatingSetpoint(); - if (heatingSetpointValue.HasValue()) - { - pendingPreset.SetHeatingSetpoint(MakeOptional(EnforceHeatingSetpointLimits(heatingSetpointValue.Value(), endpoint))); - } - } - - uint8_t totalCount = CountNumberOfPendingPresets(delegate); - - uint8_t numberOfPresetsSupported = delegate->GetNumberOfPresets(); - - if (numberOfPresetsSupported == 0) - { - ChipLogError(Zcl, "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: Failed to get NumberOfPresets"); - return imcode::InvalidInState; - } - - // If the expected length of the presets attribute with the applied changes exceeds the total number of presets supported, - // return RESOURCE_EXHAUSTED. Note that the changes are not yet applied. - if (numberOfPresetsSupported > 0 && totalCount > numberOfPresetsSupported) - { - return imcode::ResourceExhausted; - } - - // TODO: Check if the number of presets for each presetScenario exceeds the max number of presets supported for that - // scenario. We plan to support only one preset for each presetScenario for our use cases so defer this for re-evaluation. - - // Call the delegate API to apply the pending presets to the presets attribute and update it. - err = delegate->ApplyPendingPresets(); - - if (err != CHIP_NO_ERROR) - { - return imcode::InvalidInState; - } - - return imcode::Success; -} - -void handleAtomicCommit(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, - const Commands::AtomicRequest::DecodableType & commandData) -{ - if (!validAtomicAttributes(commandData, true)) - { - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return; - } - EndpointId endpoint = commandPath.mEndpointId; - bool inAtomicWrite = gThermostatAttrAccess.InAtomicWrite(commandObj, endpoint); - if (!inAtomicWrite) - { - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return; - } - - Delegate * delegate = GetDelegate(endpoint); - - if (delegate == nullptr) - { - ChipLogError(Zcl, "Delegate is null"); - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return; - } - - auto presetsStatus = commitPresets(delegate, endpoint); - // TODO: copy over schedules code - auto schedulesStatus = imcode::Success; - resetAtomicWrite(delegate, endpoint); - imcode status = (presetsStatus == imcode::Success && schedulesStatus == imcode::Success) ? imcode::Success : imcode::Failure; - sendAtomicResponse(commandObj, commandPath, status, presetsStatus, schedulesStatus); -} - -void handleAtomicRollback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, - const Commands::AtomicRequest::DecodableType & commandData) -{ - if (!validAtomicAttributes(commandData, true)) - { - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return; - } - EndpointId endpoint = commandPath.mEndpointId; - bool inAtomicWrite = gThermostatAttrAccess.InAtomicWrite(commandObj, endpoint); - if (!inAtomicWrite) - { - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return; - } - - Delegate * delegate = GetDelegate(endpoint); - - if (delegate == nullptr) - { - ChipLogError(Zcl, "Delegate is null"); - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return; - } - resetAtomicWrite(delegate, endpoint); - sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success); -} - -bool emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, - const Clusters::Thermostat::Commands::AtomicRequest::DecodableType & commandData) -{ - auto & requestType = commandData.requestType; - - // If we've gotten this far, then the client has manage permission to call AtomicRequest, which is also the - // privilege necessary to write to the atomic attributes, so no need to check - - switch (requestType) - { - case Globals::AtomicRequestTypeEnum::kBeginWrite: - handleAtomicBegin(commandObj, commandPath, commandData); - return true; - case Globals::AtomicRequestTypeEnum::kCommitWrite: - handleAtomicCommit(commandObj, commandPath, commandData); - return true; - case Globals::AtomicRequestTypeEnum::kRollbackWrite: - handleAtomicRollback(commandObj, commandPath, commandData); - return true; - case Globals::AtomicRequestTypeEnum::kUnknownEnumValue: - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return true; - } - - return false; -} - bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, const Commands::SetpointRaiseLower::DecodableType & commandData) @@ -1646,9 +804,9 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co EndpointId aEndpointId = commandPath.mEndpointId; int16_t HeatingSetpoint = kDefaultHeatingSetpoint, CoolingSetpoint = kDefaultCoolingSetpoint; // Set to defaults to be safe - imcode status = imcode::Failure; - imcode WriteCoolingSetpointStatus = imcode::Failure; - imcode WriteHeatingSetpointStatus = imcode::Failure; + Status status = Status::Failure; + Status WriteCoolingSetpointStatus = Status::Failure; + Status WriteHeatingSetpointStatus = Status::Failure; int16_t DeadBandTemp = 0; int8_t DeadBand = 0; uint32_t OurFeatureMap; @@ -1656,7 +814,7 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co bool HeatSupported = false; bool CoolSupported = false; - if (FeatureMap::Get(aEndpointId, &OurFeatureMap) != imcode::Success) + if (FeatureMap::Get(aEndpointId, &OurFeatureMap) != Status::Success) OurFeatureMap = FEATURE_MAP_DEFAULT; if (OurFeatureMap & 1 << 5) // Bit 5 is Auto Mode supported @@ -1670,7 +828,7 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co if (AutoSupported) { - if (MinSetpointDeadBand::Get(aEndpointId, &DeadBand) != imcode::Success) + if (MinSetpointDeadBand::Get(aEndpointId, &DeadBand) != Status::Success) DeadBand = kDefaultDeadBand; DeadBandTemp = static_cast(DeadBand * 10); } @@ -1681,13 +839,13 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co if (HeatSupported && CoolSupported) { int16_t DesiredCoolingSetpoint, CoolLimit, DesiredHeatingSetpoint, HeatLimit; - if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == imcode::Success) + if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == Status::Success) { DesiredCoolingSetpoint = static_cast(CoolingSetpoint + amount * 10); CoolLimit = static_cast(DesiredCoolingSetpoint - EnforceCoolingSetpointLimits(DesiredCoolingSetpoint, aEndpointId)); { - if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == imcode::Success) + if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == Status::Success) { DesiredHeatingSetpoint = static_cast(HeatingSetpoint + amount * 10); HeatLimit = static_cast(DesiredHeatingSetpoint - @@ -1709,12 +867,12 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co } } WriteCoolingSetpointStatus = OccupiedCoolingSetpoint::Set(aEndpointId, DesiredCoolingSetpoint); - if (WriteCoolingSetpointStatus != imcode::Success) + if (WriteCoolingSetpointStatus != Status::Success) { ChipLogError(Zcl, "Error: SetOccupiedCoolingSetpoint failed!"); } WriteHeatingSetpointStatus = OccupiedHeatingSetpoint::Set(aEndpointId, DesiredHeatingSetpoint); - if (WriteHeatingSetpointStatus != imcode::Success) + if (WriteHeatingSetpointStatus != Status::Success) { ChipLogError(Zcl, "Error: SetOccupiedHeatingSetpoint failed!"); } @@ -1726,12 +884,12 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co if (CoolSupported && !HeatSupported) { - if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == imcode::Success) + if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == Status::Success) { CoolingSetpoint = static_cast(CoolingSetpoint + amount * 10); CoolingSetpoint = EnforceCoolingSetpointLimits(CoolingSetpoint, aEndpointId); WriteCoolingSetpointStatus = OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint); - if (WriteCoolingSetpointStatus != imcode::Success) + if (WriteCoolingSetpointStatus != Status::Success) { ChipLogError(Zcl, "Error: SetOccupiedCoolingSetpoint failed!"); } @@ -1740,34 +898,34 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co if (HeatSupported && !CoolSupported) { - if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == imcode::Success) + if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == Status::Success) { HeatingSetpoint = static_cast(HeatingSetpoint + amount * 10); HeatingSetpoint = EnforceHeatingSetpointLimits(HeatingSetpoint, aEndpointId); WriteHeatingSetpointStatus = OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint); - if (WriteHeatingSetpointStatus != imcode::Success) + if (WriteHeatingSetpointStatus != Status::Success) { ChipLogError(Zcl, "Error: SetOccupiedHeatingSetpoint failed!"); } } } - if ((!HeatSupported || WriteHeatingSetpointStatus == imcode::Success) && - (!CoolSupported || WriteCoolingSetpointStatus == imcode::Success)) - status = imcode::Success; + if ((!HeatSupported || WriteHeatingSetpointStatus == Status::Success) && + (!CoolSupported || WriteCoolingSetpointStatus == Status::Success)) + status = Status::Success; break; case SetpointRaiseLowerModeEnum::kCool: if (CoolSupported) { - if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == imcode::Success) + if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == Status::Success) { CoolingSetpoint = static_cast(CoolingSetpoint + amount * 10); CoolingSetpoint = EnforceCoolingSetpointLimits(CoolingSetpoint, aEndpointId); if (AutoSupported) { // Need to check if we can move the cooling setpoint while maintaining the dead band - if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == imcode::Success) + if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == Status::Success) { if (CoolingSetpoint - HeatingSetpoint < DeadBandTemp) { @@ -1778,10 +936,10 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co { // Desired cooling setpoint is enforcable // Set the new cooling and heating setpoints - if (OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint) == imcode::Success) + if (OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint) == Status::Success) { - if (OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint) == imcode::Success) - status = imcode::Success; + if (OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint) == Status::Success) + status = Status::Success; } else ChipLogError(Zcl, "Error: SetOccupiedHeatingSetpoint failed!"); @@ -1789,7 +947,7 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co else { ChipLogError(Zcl, "Error: Could Not adjust heating setpoint to maintain dead band!"); - status = imcode::InvalidCommand; + status = Status::InvalidCommand; } } else @@ -1807,20 +965,20 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co ChipLogError(Zcl, "Error: GetOccupiedCoolingSetpoint failed!"); } else - status = imcode::InvalidCommand; + status = Status::InvalidCommand; break; case SetpointRaiseLowerModeEnum::kHeat: if (HeatSupported) { - if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == imcode::Success) + if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == Status::Success) { HeatingSetpoint = static_cast(HeatingSetpoint + amount * 10); HeatingSetpoint = EnforceHeatingSetpointLimits(HeatingSetpoint, aEndpointId); if (AutoSupported) { // Need to check if we can move the cooling setpoint while maintaining the dead band - if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == imcode::Success) + if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == Status::Success) { if (CoolingSetpoint - HeatingSetpoint < DeadBandTemp) { @@ -1831,10 +989,10 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co { // Desired cooling setpoint is enforcable // Set the new cooling and heating setpoints - if (OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint) == imcode::Success) + if (OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint) == Status::Success) { - if (OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint) == imcode::Success) - status = imcode::Success; + if (OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint) == Status::Success) + status = Status::Success; } else ChipLogError(Zcl, "Error: SetOccupiedCoolingSetpoint failed!"); @@ -1842,7 +1000,7 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co else { ChipLogError(Zcl, "Error: Could Not adjust cooling setpoint to maintain dead band!"); - status = imcode::InvalidCommand; + status = Status::InvalidCommand; } } else @@ -1860,11 +1018,11 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co ChipLogError(Zcl, "Error: GetOccupiedHeatingSetpoint failed!"); } else - status = imcode::InvalidCommand; + status = Status::InvalidCommand; break; default: - status = imcode::InvalidCommand; + status = Status::InvalidCommand; break; } @@ -1877,14 +1035,3 @@ void MatterThermostatPluginServerInitCallback() Server::GetInstance().GetFabricTable().AddFabricDelegate(&gThermostatAttrAccess); AttributeAccessInterfaceRegistry::Instance().Register(&gThermostatAttrAccess); } - -void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint) -{ - ChipLogProgress(Zcl, "Shutting down thermostat server cluster on endpoint %d", endpoint); - Delegate * delegate = GetDelegate(endpoint); - - if (delegate != nullptr) - { - resetAtomicWrite(delegate, endpoint); - } -} diff --git a/src/app/clusters/thermostat-server/thermostat-server.h b/src/app/clusters/thermostat-server/thermostat-server.h index ddede8a9bb13f9..cc941cfa766d92 100644 --- a/src/app/clusters/thermostat-server/thermostat-server.h +++ b/src/app/clusters/thermostat-server/thermostat-server.h @@ -26,6 +26,7 @@ #include "thermostat-delegate.h" +#include #include #include @@ -34,25 +35,62 @@ namespace app { namespace Clusters { namespace Thermostat { +enum class AtomicWriteState +{ + Closed = 0, + Open, +}; + static constexpr size_t kThermostatEndpointCount = MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; -enum AtomicWriteState -{ - kAtomicWriteState_Closed = 0, - kAtomicWriteState_Open, -}; /** * @brief Thermostat Attribute Access Interface. */ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public chip::FabricTable::Delegate { + public: ThermostatAttrAccess() : AttributeAccessInterface(Optional::Missing(), Thermostat::Id) {} CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, chip::app::AttributeValueDecoder & aDecoder) override; +private: + /** + * @brief Set the Active Preset to a given preset handle, or null + * + * @param endpoint The endpoint + * @param presetHandle The handle of the preset to set active, or null to clear the active preset + * @return Success if the active preset was updated, an error code if not + */ + Protocols::InteractionModel::Status SetActivePreset(EndpointId endpoint, DataModel::Nullable presetHandle); + + /** + * @brief Apply a preset to the pending lists of presets during an atomic write + * + * @param delegate The current ThermostatDelegate + * @param preset The preset to append + * @return CHIP_NO_ERROR if successful, an error code if not + */ + CHIP_ERROR AppendPendingPreset(Thermostat::Delegate * delegate, const Structs::PresetStruct::Type & preset); + + /** + * @brief Verifies if the pending presets for a given endpoint are valid + * + * @param endpoint The endpoint + * @return Success if the list of pending presets is valid, an error code if not + */ + Protocols::InteractionModel::Status PrecommitPresets(EndpointId endpoint); + + /** + * @brief Callback for when the server is removed from a given fabric; all associated atomic writes are reset + * + * @param fabricTable The fabric table + * @param fabricIndex The fabric index + */ + void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override; + /** * @brief Gets the scoped node id of the originator that sent the last successful * AtomicRequest of type BeginWrite for the given endpoint. @@ -61,7 +99,7 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public * * @return the scoped node id for the given endpoint if set. Otherwise returns ScopedNodeId(). */ - ScopedNodeId GetAtomicWriteScopedNodeId(EndpointId endpoint); + ScopedNodeId GetAtomicWriteOriginatorScopedNodeId(EndpointId endpoint); /** * @brief Sets the atomic write state for the given endpoint and originatorNodeId @@ -69,46 +107,119 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public * @param[in] endpoint The endpoint. * @param[in] originatorNodeId The originator scoped node id. * @param[in] state Whether or not an atomic write is open or closed. + * @param attributeStatuses The set of attribute status structs the atomic write should be associated with + * @return true if it was able to update the atomic write state + * @return false if it was unable to update the atomic write state */ - void SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state); + bool + SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state, + Platform::ScopedMemoryBufferWithSize & attributeStatuses); /** - * @brief Gets whether an atomic write is in progress for the given endpoint + * @brief Sets the atomic write state for the given endpoint and originatorNodeId * - * @param[in] endpoint The endpoint. + */ + /** + * @brief Resets the atomic write for a given endpoint * - * @return Whether an atomic write is in progress for the given endpoint + * @param endpoint The endpoint */ - bool InAtomicWrite(EndpointId endpoint); + void ResetAtomicWrite(EndpointId endpoint); /** - * @brief Gets whether an atomic write is in progress for the given endpoint + * @brief Checks if a given endpoint has an atomic write open, optionally filtered by an attribute ID * - * @param[in] subjectDescriptor The subject descriptor. - * @param[in] endpoint The endpoint. + * @param endpoint The endpoint + * @param attributeId The optional attribute ID to filter on + * @return true if the endpoint has an open atomic write + * @return false if the endpoint does not have an open atomic write + */ + bool InAtomicWrite(EndpointId endpoint, Optional attributeId = NullOptional); + + /** + * @brief Checks if a given endpoint has an atomic write open for a given subject descriptor, optionally filtered by an + * attribute ID * - * @return Whether an atomic write is in progress for the given endpoint + * @param endpoint The endpoint + * @param subjectDescriptor The subject descriptor for the client making a read or write request + * @param attributeId The optional attribute ID to filter on + * @return true if the endpoint has an open atomic write + * @return false if the endpoint does not have an open atomic write */ - bool InAtomicWrite(const Access::SubjectDescriptor & subjectDescriptor, EndpointId endpoint); + bool InAtomicWrite(EndpointId endpoint, const Access::SubjectDescriptor & subjectDescriptor, + Optional attributeId = NullOptional); /** - * @brief Gets whether an atomic write is in progress for the given endpoint + * @brief Checks if a given endpoint has an atomic write open for a given command invocation, optionally filtered by an + * attribute ID * - * @param[in] commandObj The command handler. - * @param[in] endpoint The endpoint. + * @param endpoint The endpoint + * @param commandObj The CommandHandler for the invoked command + * @param attributeId The optional attribute ID to filter on + * @return true if the endpoint has an open atomic write + * @return false if the endpoint does not have an open atomic write + */ + bool InAtomicWrite(EndpointId endpoint, CommandHandler * commandObj, Optional attributeId = NullOptional); + + /** + * @brief Checks if a given endpoint has an atomic write open for a given command invocation and a list of attributes * - * @return Whether an atomic write is in progress for the given endpoint + * @param endpoint The endpoint + * @param commandObj The CommandHandler for the invoked command + * @param attributeStatuses The list of attribute statuses whose attributeIds must match the open atomic write + * @return true if the endpoint has an open atomic write + * @return false if the endpoint does not have an open atomic write */ - bool InAtomicWrite(CommandHandler * commandObj, EndpointId endpoint); + bool + InAtomicWrite(EndpointId endpoint, CommandHandler * commandObj, + Platform::ScopedMemoryBufferWithSize & attributeStatuses); -private: - CHIP_ERROR AppendPendingPreset(Thermostat::Delegate * delegate, const Structs::PresetStruct::Type & preset); + /** + * @brief Handles an AtomicRequest of type BeginWrite + * + * @param commandObj The AtomicRequest command handler + * @param commandPath The path for the Atomic Request command + * @param commandData The payload data for the Atomic Request + */ + void BeginAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::AtomicRequest::DecodableType & commandData); - void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override; + /** + * @brief Handles an AtomicRequest of type CommitWrite + * + * @param commandObj The AtomicRequest command handler + * @param commandPath The path for the Atomic Request command + * @param commandData The payload data for the Atomic Request + */ + void CommitAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::AtomicRequest::DecodableType & commandData); + + /** + * @brief Handles an AtomicRequest of type RollbackWrite + * + * @param commandObj The AtomicRequest command handler + * @param commandPath The path for the Atomic Request command + * @param commandData The payload data for the Atomic Request + */ + void RollbackAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::AtomicRequest::DecodableType & commandData); + + friend void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext); + + friend void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint); + + friend bool emberAfThermostatClusterSetActivePresetRequestCallback( + CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Clusters::Thermostat::Commands::SetActivePresetRequest::DecodableType & commandData); + + friend bool + emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Clusters::Thermostat::Commands::AtomicRequest::DecodableType & commandData); struct AtomicWriteSession { - AtomicWriteState state = kAtomicWriteState_Closed; + AtomicWriteState state = AtomicWriteState::Closed; + Platform::ScopedMemoryBufferWithSize attributeIds; ScopedNodeId nodeId; EndpointId endpointId = kInvalidEndpointId; }; @@ -124,6 +235,8 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public */ void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate); +Delegate * GetDelegate(EndpointId endpoint); + } // namespace Thermostat } // namespace Clusters } // namespace app diff --git a/src/python_testing/TC_TSTAT_4_2.py b/src/python_testing/TC_TSTAT_4_2.py index 5c289ced50604e..563d6f3f2eddfc 100644 --- a/src/python_testing/TC_TSTAT_4_2.py +++ b/src/python_testing/TC_TSTAT_4_2.py @@ -61,15 +61,30 @@ class TC_TSTAT_4_2(MatterBaseTest): def check_atomic_response(self, response: object, expected_status: Status = Status.Success, expected_overall_status: Status = Status.Success, - expected_preset_status: Status = Status.Success): + expected_preset_status: Status = Status.Success, + expected_schedules_status: Status = None, + expected_timeout: int = None): asserts.assert_equal(expected_status, Status.Success, "We expected we had a valid response") asserts.assert_equal(response.statusCode, expected_overall_status, "Response should have the right overall status") found_preset_status = False + found_schedules_status = False for attrStatus in response.attributeStatus: if attrStatus.attributeID == cluster.Attributes.Presets.attribute_id: asserts.assert_equal(attrStatus.statusCode, expected_preset_status, "Preset attribute should have the right status") found_preset_status = True + if attrStatus.attributeID == cluster.Attributes.Schedules.attribute_id: + asserts.assert_equal(attrStatus.statusCode, expected_schedules_status, + "Schedules attribute should have the right status") + found_schedules_status = True + if expected_timeout is not None: + asserts.assert_equal(response.timeout, expected_timeout, + "Timeout should have the right value") + asserts.assert_true(found_preset_status, "Preset attribute should have a status") + if expected_schedules_status is not None: + asserts.assert_true(found_schedules_status, "Schedules attribute should have a status") + asserts.assert_equal(attrStatus.statusCode, expected_schedules_status, + "Schedules attribute should have the right status") asserts.assert_true(found_preset_status, "Preset attribute should have a status") async def write_presets(self, @@ -87,17 +102,21 @@ async def write_presets(self, async def send_atomic_request_begin_command(self, dev_ctrl: ChipDeviceCtrl = None, endpoint: int = None, + timeout: int = 1800, expected_status: Status = Status.Success, expected_overall_status: Status = Status.Success, - expected_preset_status: Status = Status.Success): + expected_preset_status: Status = Status.Success, + expected_schedules_status: Status = None, + expected_timeout: int = None): try: response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kBeginWrite, attributeRequests=[ cluster.Attributes.Presets.attribute_id], - timeout=1800), + timeout=timeout), dev_ctrl=dev_ctrl, endpoint=endpoint) - self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status) + self.check_atomic_response(response, expected_status, expected_overall_status, + expected_preset_status, expected_schedules_status, expected_timeout) except InteractionModelError as e: asserts.assert_equal(e.status, expected_status, "Unexpected error returned") @@ -107,13 +126,15 @@ async def send_atomic_request_commit_command(self, endpoint: int = None, expected_status: Status = Status.Success, expected_overall_status: Status = Status.Success, - expected_preset_status: Status = Status.Success): + expected_preset_status: Status = Status.Success, + expected_schedules_status: Status = None): try: response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kCommitWrite, - attributeRequests=[cluster.Attributes.Presets.attribute_id, cluster.Attributes.Schedules.attribute_id]), + attributeRequests=[cluster.Attributes.Presets.attribute_id]), dev_ctrl=dev_ctrl, endpoint=endpoint) - self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status) + self.check_atomic_response(response, expected_status, expected_overall_status, + expected_preset_status, expected_schedules_status) except InteractionModelError as e: asserts.assert_equal(e.status, expected_status, "Unexpected error returned") @@ -122,13 +143,16 @@ async def send_atomic_request_rollback_command(self, endpoint: int = None, expected_status: Status = Status.Success, expected_overall_status: Status = Status.Success, - expected_preset_status: Status = Status.Success): + expected_preset_status: Status = Status.Success, + expected_schedules_status: Status = None): try: response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kRollbackWrite, - attributeRequests=[cluster.Attributes.Presets.attribute_id, cluster.Attributes.Schedules.attribute_id]), + attributeRequests=[cluster.Attributes.Presets.attribute_id]), dev_ctrl=dev_ctrl, endpoint=endpoint) - self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status) + self.check_atomic_response(response, expected_status, expected_overall_status, + expected_preset_status, expected_schedules_status) + except InteractionModelError as e: asserts.assert_equal(e.status, expected_status, "Unexpected error returned") @@ -219,7 +243,6 @@ async def test_TC_TSTAT_4_2(self): await self.write_presets(endpoint=endpoint, presets=new_presets, expected_status=Status.InvalidInState) self.step("3") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): await self.send_atomic_request_begin_command() @@ -260,7 +283,7 @@ async def test_TC_TSTAT_4_2(self): if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_atomic_request_begin_command() + await self.send_atomic_request_begin_command(timeout=5000, expected_timeout=3000) # Write to the presets attribute after removing a built in preset from the list. Remove the first entry. test_presets = new_presets_with_handle.copy() @@ -406,10 +429,7 @@ async def test_TC_TSTAT_4_2(self): self.step("14") if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): - - # Send the AtomicRequest begin command await self.send_atomic_request_begin_command() - # Send the AtomicRequest begin command from separate controller, which should receive busy status = await self.send_atomic_request_begin_command(dev_ctrl=secondary_controller, expected_overall_status=Status.Failure, expected_preset_status=Status.Busy) From fe34e8198d8b27058b7d446516f5737e190d7326 Mon Sep 17 00:00:00 2001 From: Wang Qixiang <43193572+wqx6@users.noreply.github.com> Date: Fri, 23 Aug 2024 23:17:10 +0800 Subject: [PATCH 02/12] esp32: fix compiling when enabling ble commissioner (#35085) * esp32: fix compiling when enabling ble commissioner * Restyled by clang-format --------- Co-authored-by: Restyled.io --- src/platform/ESP32/nimble/BLEManagerImpl.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/platform/ESP32/nimble/BLEManagerImpl.cpp b/src/platform/ESP32/nimble/BLEManagerImpl.cpp index 4f6ab4f73f7200..d4d90f45362dbe 100644 --- a/src/platform/ESP32/nimble/BLEManagerImpl.cpp +++ b/src/platform/ESP32/nimble/BLEManagerImpl.cpp @@ -659,7 +659,7 @@ CHIP_ERROR BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const C if (pBuf->DataLength() > UINT16_MAX) { ChipLogError(Ble, "Buffer data Length is too long"); - return false; + return CHIP_ERROR_INVALID_ARGUMENT; } rc = ble_gattc_write_flat(conId, chr->chr.val_handle, pBuf->Start(), static_cast(pBuf->DataLength()), OnWriteComplete, this); @@ -1746,12 +1746,12 @@ void BLEManagerImpl::DriveBLEState(intptr_t arg) #ifdef CONFIG_ENABLE_ESP32_BLE_CONTROLLER CHIP_ERROR BLEManagerImpl::HandleRXNotify(struct ble_gap_event * ble_event) { - size_t dataLen = OS_MBUF_PKTLEN(ble_event->notify_rx.om); + uint16_t dataLen = OS_MBUF_PKTLEN(ble_event->notify_rx.om); System::PacketBufferHandle buf = System::PacketBufferHandle::New(dataLen, 0); VerifyOrReturnError(!buf.IsNull(), CHIP_ERROR_NO_MEMORY); - VerifyOrExit(buf->AvailableDataLength() >= data_len, err = CHIP_ERROR_BUFFER_TOO_SMALL); - ble_hs_mbuf_to_flat(ble_event->notify_rx.om, buf->Start(), data_len, NULL); - buf->SetDataLength(data_len); + VerifyOrReturnError(buf->AvailableDataLength() >= dataLen, CHIP_ERROR_BUFFER_TOO_SMALL); + ble_hs_mbuf_to_flat(ble_event->notify_rx.om, buf->Start(), dataLen, NULL); + buf->SetDataLength(dataLen); ChipLogDetail(DeviceLayer, "Indication received, conn = %d", ble_event->notify_rx.conn_handle); From 9520bef5d88e4ec9f208390182dce8d1b3f8a664 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 23 Aug 2024 12:06:22 -0400 Subject: [PATCH 03/12] Remove writeAttributeWithEndpointID implementation from MTRDevice. (#35170) This is implemented (differently) by the different subclasses. Once this implementation is removed, removeExpectedValueForAttributePath becomes unused and can be removed. Also removes the unused setExpectedValues declaration in MTRDevice_Internal.h and the implementations of it. --- src/darwin/Framework/CHIP/MTRDevice.mm | 136 ++---------------- .../Framework/CHIP/MTRDevice_Concrete.mm | 5 - .../Framework/CHIP/MTRDevice_Internal.h | 4 - 3 files changed, 9 insertions(+), 136 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index 3f2acce5f5587e..f913f29388f5fb 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -1831,11 +1831,12 @@ static BOOL AttributeHasChangesOmittedQuality(MTRAttributePath * attributePath) attributeID:(NSNumber *)attributeID params:(MTRReadParams * _Nullable)params { -#define ErrorStr "MTRDevice readAttributeWithEndpointID:clusterID:attributeID:params: must be handled by subclasses" - MTR_LOG_ERROR(ErrorStr); +#define MTRDeviceErrorStr "MTRDevice readAttributeWithEndpointID:clusterID:attributeID:params: must be handled by subclasses" + MTR_LOG_ERROR(MTRDeviceErrorStr); #ifdef DEBUG - NSAssert(NO, @ErrorStr); + NSAssert(NO, @MTRDeviceErrorStr); #endif // DEBUG +#undef MTRDeviceErrorStr return nil; } @@ -1846,120 +1847,12 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID expectedValueInterval:(NSNumber *)expectedValueInterval timedWriteTimeout:(NSNumber * _Nullable)timeout { - if (timeout) { - timeout = MTRClampedNumber(timeout, @(1), @(UINT16_MAX)); - } - expectedValueInterval = MTRClampedNumber(expectedValueInterval, @(1), @(UINT32_MAX)); - MTRAttributePath * attributePath = [MTRAttributePath attributePathWithEndpointID:endpointID - clusterID:clusterID - - attributeID:attributeID]; - - __block BOOL useValueAsExpectedValue = YES; +#define MTRDeviceErrorStr "MTRDevice writeAttributeWithEndpointID:clusterID:attributeID:value:expectedValueInterval:timedWriteTimeout: must be handled by subclasses" + MTR_LOG_ERROR(MTRDeviceErrorStr); #ifdef DEBUG - os_unfair_lock_lock(&self->_lock); - [self _callFirstDelegateSynchronouslyWithBlock:^(id delegate) { - if ([delegate respondsToSelector:@selector(unitTestShouldSkipExpectedValuesForWrite:)]) { - useValueAsExpectedValue = ![delegate unitTestShouldSkipExpectedValuesForWrite:self]; - } - }]; - os_unfair_lock_unlock(&self->_lock); -#endif - - uint64_t expectedValueID = 0; - if (useValueAsExpectedValue) { - // Commit change into expected value cache - NSDictionary * newExpectedValueDictionary = @{ MTRAttributePathKey : attributePath, MTRDataKey : value }; - [self setExpectedValues:@[ newExpectedValueDictionary ] - expectedValueInterval:expectedValueInterval - expectedValueID:&expectedValueID]; - } - - MTRAsyncWorkItem * workItem = [[MTRAsyncWorkItem alloc] initWithQueue:self.queue]; - uint64_t workItemID = workItem.uniqueID; // capture only the ID, not the work item - NSNumber * nodeID = _nodeID; - - // Write request data is an array of items (for now always length 1). Each - // item is an array containing: - // - // [ attribute path, value, timedWriteTimeout, expectedValueID ] - // - // where expectedValueID is stored as NSNumber and NSNull represents nil timeouts - auto * writeData = @[ attributePath, [value copy], timeout ?: [NSNull null], @(expectedValueID) ]; - - NSMutableArray * writeRequests = [NSMutableArray arrayWithObject:writeData]; - - [workItem setBatchingID:MTRDeviceWorkItemBatchingWriteID data:writeRequests handler:^(id opaqueDataCurrent, id opaqueDataNext) { - mtr_hide(self); // don't capture self accidentally - NSMutableArray * writeRequestsCurrent = opaqueDataCurrent; - NSMutableArray * writeRequestsNext = opaqueDataNext; - - if (writeRequestsCurrent.count != 1) { - // Very unexpected! - MTR_LOG_ERROR("Batching write attribute work item [%llu]: Unexpected write request count %lu", workItemID, static_cast(writeRequestsCurrent.count)); - return MTRNotBatched; - } - - MTRBatchingOutcome outcome = MTRNotBatched; - while (writeRequestsNext.count) { - // If paths don't match, we cannot replace the earlier write - // with the later one. - if (![writeRequestsNext[0][MTRDeviceWriteRequestFieldPathIndex] - isEqual:writeRequestsCurrent[0][MTRDeviceWriteRequestFieldPathIndex]]) { - MTR_LOG("Batching write attribute work item [%llu]: cannot replace with next work item due to path mismatch", workItemID); - return outcome; - } - - // Replace our one request with the first one from the next item. - auto writeItem = writeRequestsNext.firstObject; - [writeRequestsNext removeObjectAtIndex:0]; - [writeRequestsCurrent replaceObjectAtIndex:0 withObject:writeItem]; - MTR_LOG("Batching write attribute work item [%llu]: replaced with new write value %@ [0x%016llX]", - workItemID, writeItem, nodeID.unsignedLongLongValue); - outcome = MTRBatchedPartially; - } - NSCAssert(writeRequestsNext.count == 0, @"should have batched everything or returned early"); - return MTRBatchedFully; - }]; - // The write operation will install a duplicate check handler, to return NO for "isDuplicate". Since a write operation may - // change values, only read requests after this should be considered for duplicate requests. - [workItem setDuplicateTypeID:MTRDeviceWorkItemDuplicateReadTypeID handler:^(id opaqueItemData, BOOL * isDuplicate, BOOL * stop) { - *isDuplicate = NO; - *stop = YES; - }]; - [workItem setReadyHandler:^(MTRDevice * self, NSInteger retryCount, MTRAsyncWorkCompletionBlock completion) { - 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: %lu", static_cast(writeRequests.count)); - - auto * request = writeRequests[0]; - MTRAttributePath * path = request[MTRDeviceWriteRequestFieldPathIndex]; - - id timedWriteTimeout = request[MTRDeviceWriteRequestFieldTimeoutIndex]; - if (timedWriteTimeout == [NSNull null]) { - timedWriteTimeout = nil; - } - - [baseDevice - writeAttributeWithEndpointID:path.endpoint - clusterID:path.cluster - attributeID:path.attribute - value:request[MTRDeviceWriteRequestFieldValueIndex] - timedWriteTimeout:timedWriteTimeout - queue:self.queue - completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - if (error) { - MTR_LOG_ERROR("Write attribute work item [%llu] failed: %@", workItemID, error); - if (useValueAsExpectedValue) { - NSNumber * expectedValueID = request[MTRDeviceWriteRequestFieldExpectedValueIDIndex]; - [self removeExpectedValueForAttributePath:attributePath expectedValueID:expectedValueID.unsignedLongLongValue]; - } - } - completion(MTRAsyncWorkComplete); - }]; - }]; - [_asyncWorkQueue enqueueWorkItem:workItem descriptionWithFormat:@"write %@ 0x%llx 0x%llx", endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue]; + NSAssert(NO, @MTRDeviceErrorStr); +#endif // DEBUG +#undef MTRDeviceErrorStr } - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID @@ -2796,11 +2689,6 @@ - (NSArray *)_getAttributesToReportWithNewExpectedValues:(NSArray *> *)values expectedValueInterval:(NSNumber *)expectedValueInterval -{ - [self setExpectedValues:values expectedValueInterval:expectedValueInterval expectedValueID:nil]; -} - // expectedValueID is an out-argument that returns an identifier to be used when removing expected values - (void)setExpectedValues:(NSArray *> *)values expectedValueInterval:(NSNumber *)expectedValueInterval @@ -2833,12 +2721,6 @@ - (void)removeExpectedValuesForAttributePaths:(NSArray *)att } } -- (void)removeExpectedValueForAttributePath:(MTRAttributePath *)attributePath expectedValueID:(uint64_t)expectedValueID -{ - std::lock_guard lock(_lock); - [self _removeExpectedValueForAttributePath:attributePath expectedValueID:expectedValueID]; -} - - (void)_removeExpectedValueForAttributePath:(MTRAttributePath *)attributePath expectedValueID:(uint64_t)expectedValueID { os_unfair_lock_assert_owner(&self->_lock); diff --git a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm index 12614ea4091b0c..b7e7bf78027b4f 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm +++ b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm @@ -3644,11 +3644,6 @@ - (NSArray *)_getAttributesToReportWithNewExpectedValues:(NSArray *> *)values expectedValueInterval:(NSNumber *)expectedValueInterval -{ - [self setExpectedValues:values expectedValueInterval:expectedValueInterval expectedValueID:nil]; -} - // expectedValueID is an out-argument that returns an identifier to be used when removing expected values - (void)setExpectedValues:(NSArray *> *)values expectedValueInterval:(NSNumber *)expectedValueInterval diff --git a/src/darwin/Framework/CHIP/MTRDevice_Internal.h b/src/darwin/Framework/CHIP/MTRDevice_Internal.h index b6a59ac9321d42..dae183eec217f6 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDevice_Internal.h @@ -125,10 +125,6 @@ MTR_DIRECT_MEMBERS - (instancetype)initForSubclassesWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller; - (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller; -// Called from MTRClusters for writes and commands -- (void)setExpectedValues:(NSArray *> *)values - expectedValueInterval:(NSNumber *)expectedValueIntervalMs; - // called by controller to clean up and shutdown - (void)invalidate; From c1b3304fa69f356f21b8803c572c670c0ad1be17 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 23 Aug 2024 13:16:06 -0400 Subject: [PATCH 04/12] Address followup issues for preset/atomic-write implementation. (#35175) * Puts some file-local functions in an anonymous namespace. * Fixes the "is this attribute supported?" check to correctly check for global attributes that are not in Ember metadata. * Moves the comment explaining why it's OK to skip the spec-required ACL check to the place where that check is being skipped. * Removes a non-spec-compliant "error if the timeout is 0" bit. Fixes https://github.com/project-chip/connectedhomeip/issues/35168 --- src/app/GlobalAttributes.h | 13 ++++++++ .../thermostat-server-atomic.cpp | 31 ++++++++++++------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/app/GlobalAttributes.h b/src/app/GlobalAttributes.h index 5096792309a880..461294267f38c0 100644 --- a/src/app/GlobalAttributes.h +++ b/src/app/GlobalAttributes.h @@ -40,5 +40,18 @@ constexpr AttributeId GlobalAttributesNotInMetadata[] = { static_assert(ArrayIsSorted(GlobalAttributesNotInMetadata), "Array of global attribute ids must be sorted"); +inline bool IsSupportedGlobalAttributeNotInMetadata(AttributeId attributeId) +{ + for (auto & attr : GlobalAttributesNotInMetadata) + { + if (attr == attributeId) + { + return true; + } + } + + return false; +} + } // namespace app } // namespace chip diff --git a/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp b/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp index 2a6e52e504887e..19a6ffa3387e12 100644 --- a/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp +++ b/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp @@ -17,6 +17,7 @@ #include "thermostat-server.h" +#include #include using namespace chip; @@ -47,6 +48,8 @@ void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext) gThermostatAttrAccess.ResetAtomicWrite(endpoint); } +namespace { + /** * @brief Schedules a timer for the given timeout in milliseconds. * @@ -185,15 +188,25 @@ Status BuildAttributeStatuses(const EndpointId endpoint, const DataModel::Decoda const EmberAfAttributeMetadata * metadata = emberAfLocateAttributeMetadata(endpoint, Thermostat::Id, attributeStatus.attributeID); - if (metadata == nullptr) + if (metadata != nullptr) + { + // This is definitely an attribute we know about. + continue; + } + + if (IsSupportedGlobalAttributeNotInMetadata(attributeStatus.attributeID)) { - // This is not a valid attribute on the Thermostat cluster on the supplied endpoint - return Status::InvalidCommand; + continue; } + + // This is not a valid attribute on the Thermostat cluster on the supplied endpoint + return Status::InvalidCommand; } return Status::Success; } +} // anonymous namespace + bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, Optional attributeId) { @@ -422,6 +435,10 @@ void ThermostatAttrAccess::BeginAtomicWrite(CommandHandler * commandObj, const C status = Status::Success; for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) { + // If we've gotten this far, then the client has manage permission to call AtomicRequest, + // which is also the privilege necessary to write to the atomic attributes, so no need to do + // the "If the client does not have sufficient privilege to write to the attribute" check + // from the spec. auto & attributeStatus = attributeStatuses[i]; auto statusCode = Status::Success; switch (attributeStatus.attributeID) @@ -442,11 +459,6 @@ void ThermostatAttrAccess::BeginAtomicWrite(CommandHandler * commandObj, const C } auto timeout = std::min(System::Clock::Milliseconds16(commandData.timeout.Value()), maximumTimeout); - if (timeout.count() == 0) - { - commandObj->AddStatus(commandPath, Status::InvalidInState); - return; - } if (status == Status::Success) { @@ -605,9 +617,6 @@ bool emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, { auto & requestType = commandData.requestType; - // If we've gotten this far, then the client has manage permission to call AtomicRequest, which is also the - // privilege necessary to write to the atomic attributes, so no need to check - switch (requestType) { case Globals::AtomicRequestTypeEnum::kBeginWrite: From b3507cebe7defed94c3394b8bf3d574115279531 Mon Sep 17 00:00:00 2001 From: Anush Nadathur Date: Fri, 23 Aug 2024 12:28:24 -0700 Subject: [PATCH 05/12] [Darwin] Enable taking assertion on MTRDeviceController (#35148) * [Darwin] Enable taking assertion on MTRDeviceController - Added ability to take assertion on MTRDeviceController to keep it running until all assertions are removed - A request to shutdown will take place only if there are no assertions are present * Fixed format string * Account for existing controller that is asserted in factory when creating a new one * Simplified to use lock for assertion tracking * Fixed build error * Removed unneeded includes * Fixed bugs with wrong match logic * resytle fixes * Restyle fixes * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Fixed build failure from review suggestions and added comment per review feedback --------- Co-authored-by: Boris Zbarsky --- .../Framework/CHIP/MTRDeviceController.mm | 94 +++++++++++++++++++ .../CHIP/MTRDeviceControllerFactory.mm | 19 ++++ .../CHIP/MTRDeviceControllerStartupParams.mm | 23 +++++ ...TRDeviceControllerStartupParams_Internal.h | 4 + .../CHIP/MTRDeviceController_Internal.h | 40 ++++++++ 5 files changed, 180 insertions(+) diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index 11fd481b48b2fc..4630ff7fe00b4f 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -46,6 +46,7 @@ #import "MTRSetupPayload.h" #import "MTRTimeUtils.h" #import "MTRUnfairLock.h" +#import "MTRUtilities.h" #import "NSDataSpanConversion.h" #import "NSStringSpanConversion.h" #import @@ -129,6 +130,11 @@ @implementation MTRDeviceController { std::atomic> _storedCompressedFabricID; MTRP256KeypairBridge _signingKeypairBridge; MTRP256KeypairBridge _operationalKeypairBridge; + + // Counters to track assertion status and access controlled by the _assertionLock + NSUInteger _keepRunningAssertionCounter; + BOOL _shutdownPending; + os_unfair_lock _assertionLock; } - (os_unfair_lock_t)deviceMapLock @@ -142,6 +148,11 @@ - (instancetype)initForSubclasses // nothing, as superclass of MTRDeviceController is NSObject } _underlyingDeviceMapLock = OS_UNFAIR_LOCK_INIT; + + // Setup assertion variables + _keepRunningAssertionCounter = 0; + _shutdownPending = NO; + _assertionLock = OS_UNFAIR_LOCK_INIT; return self; } @@ -178,6 +189,12 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory // Make sure our storage is all set up to work as early as possible, // before we start doing anything else with the controller. _uniqueIdentifier = uniqueIdentifier; + + // Setup assertion variables + _keepRunningAssertionCounter = 0; + _shutdownPending = NO; + _assertionLock = OS_UNFAIR_LOCK_INIT; + if (storageDelegate != nil) { if (storageDelegateQueue == nil) { MTR_LOG_ERROR("storageDelegate provided without storageDelegateQueue"); @@ -295,6 +312,9 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory _storedFabricIndex = chip::kUndefinedFabricIndex; _storedCompressedFabricID = std::nullopt; + self.nodeID = nil; + self.fabricID = nil; + self.rootPublicKey = nil; _storageBehaviorConfiguration = storageBehaviorConfiguration; } @@ -311,8 +331,69 @@ - (BOOL)isRunning return _cppCommissioner != nullptr; } +- (BOOL)matchesPendingShutdownWithParams:(MTRDeviceControllerParameters *)parameters +{ + if (!parameters.operationalCertificate || !parameters.rootCertificate) { + return FALSE; + } + NSNumber * nodeID = [MTRDeviceControllerParameters nodeIDFromNOC:parameters.operationalCertificate]; + NSNumber * fabricID = [MTRDeviceControllerParameters fabricIDFromNOC:parameters.operationalCertificate]; + NSData * publicKey = [MTRDeviceControllerParameters publicKeyFromCertificate:parameters.rootCertificate]; + + std::lock_guard lock(_assertionLock); + + // If any of the local above are nil, the return will be false since MTREqualObjects handles them correctly + return _keepRunningAssertionCounter > 0 && _shutdownPending && MTREqualObjects(nodeID, self.nodeID) && MTREqualObjects(fabricID, self.fabricID) && MTREqualObjects(publicKey, self.rootPublicKey); +} + +- (void)addRunAssertion +{ + std::lock_guard lock(_assertionLock); + + // Only take an assertion if running + if ([self isRunning]) { + ++_keepRunningAssertionCounter; + MTR_LOG("%@ Adding keep running assertion, total %lu", self, static_cast(_keepRunningAssertionCounter)); + } +} + +- (void)removeRunAssertion; +{ + std::lock_guard lock(_assertionLock); + + if (_keepRunningAssertionCounter > 0) { + --_keepRunningAssertionCounter; + MTR_LOG("%@ Removing keep running assertion, total %lu", self, static_cast(_keepRunningAssertionCounter)); + + if ([self isRunning] && _keepRunningAssertionCounter == 0 && _shutdownPending) { + MTR_LOG("%@ All assertions removed and shutdown is pending, shutting down", self); + [self finalShutdown]; + } + } +} + +- (void)clearPendingShutdown +{ + std::lock_guard lock(_assertionLock); + _shutdownPending = NO; +} + - (void)shutdown { + std::lock_guard lock(_assertionLock); + + if (_keepRunningAssertionCounter > 0) { + MTR_LOG("%@ Pending shutdown since %lu assertions are present", self, static_cast(_keepRunningAssertionCounter)); + _shutdownPending = YES; + return; + } + [self finalShutdown]; +} + +- (void)finalShutdown +{ + os_unfair_lock_assert_owner(&_assertionLock); + MTR_LOG("%@ shutdown called", self); if (_cppCommissioner == nullptr) { // Already shut down. @@ -369,11 +450,16 @@ - (void)shutDownCppController // shuts down. _storedFabricIndex = chip::kUndefinedFabricIndex; _storedCompressedFabricID = std::nullopt; + self.nodeID = nil; + self.fabricID = nil; + self.rootPublicKey = nil; + delete commissionerToShutDown; if (_operationalCredentialsDelegate != nil) { _operationalCredentialsDelegate->SetDeviceCommissioner(nullptr); } } + _shutdownPending = NO; } - (void)deinitFromFactory @@ -609,6 +695,14 @@ - (BOOL)startup:(MTRDeviceControllerStartupParamsInternal *)startupParams self->_storedFabricIndex = fabricIdx; self->_storedCompressedFabricID = _cppCommissioner->GetCompressedFabricId(); + + chip::Crypto::P256PublicKey rootPublicKey; + if (_cppCommissioner->GetRootPublicKey(rootPublicKey) == CHIP_NO_ERROR) { + self.rootPublicKey = [NSData dataWithBytes:rootPublicKey.Bytes() length:rootPublicKey.Length()]; + self.nodeID = @(_cppCommissioner->GetNodeId()); + self.fabricID = @(_cppCommissioner->GetFabricId()); + } + commissionerInitialized = YES; MTR_LOG("%@ startup succeeded for nodeID 0x%016llX", self, self->_cppCommissioner->GetNodeId()); diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm index 5b089b392074e6..bd0b90450b9ac4 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm @@ -1133,12 +1133,31 @@ - (void)operationalInstanceAdded:(chip::PeerId &)operationalID } } +- (nullable MTRDeviceController *)_findControllerWithPendingShutdownMatchingParams:(MTRDeviceControllerParameters *)parameters +{ + std::lock_guard lock(_controllersLock); + for (MTRDeviceController * controller in _controllers) { + if ([controller matchesPendingShutdownWithParams:parameters]) { + MTR_LOG("%@ Found existing controller %@ that is pending shutdown and matching parameters, re-using it", self, controller); + [controller clearPendingShutdown]; + return controller; + } + } + return nil; +} + - (nullable MTRDeviceController *)initializeController:(MTRDeviceController *)controller withParameters:(MTRDeviceControllerParameters *)parameters error:(NSError * __autoreleasing *)error { [self _assertCurrentQueueIsNotMatterQueue]; + // If there is a controller already running with matching parameters that is conceptually shut down from the API consumer's viewpoint, re-use it. + MTRDeviceController * existingController = [self _findControllerWithPendingShutdownMatchingParams:parameters]; + if (existingController) { + return existingController; + } + return [self _startDeviceController:controller startupParams:parameters fabricChecker:^MTRDeviceControllerStartupParamsInternal *( diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm index c5923ad4e54619..5850a8c4efe384 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm @@ -306,6 +306,29 @@ - (void)setOTAProviderDelegate:(id)otaProviderDelegate q _otaProviderDelegateQueue = queue; } ++ (nullable NSNumber *)nodeIDFromNOC:(MTRCertificateDERBytes)noc +{ + NSNumber * nodeID = nil; + ExtractNodeIDFromNOC(noc, &nodeID); + return nodeID; +} + ++ (nullable NSNumber *)fabricIDFromNOC:(MTRCertificateDERBytes)noc +{ + NSNumber * fabricID = nil; + ExtractFabricIDFromNOC(noc, &fabricID); + return fabricID; +} + ++ (nullable NSData *)publicKeyFromCertificate:(MTRCertificateDERBytes)certificate +{ + Crypto::P256PublicKey pubKey; + if (ExtractPubkeyFromX509Cert(AsByteSpan(certificate), pubKey) != CHIP_NO_ERROR) { + return nil; + } + return [NSData dataWithBytes:pubKey.Bytes() length:pubKey.Length()]; +} + @end @implementation MTRDeviceControllerExternalCertificateParameters diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams_Internal.h index 6b8c762633578a..0b4065f491873b 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams_Internal.h @@ -85,6 +85,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, readonly, nullable) id otaProviderDelegate; @property (nonatomic, strong, readonly, nullable) dispatch_queue_t otaProviderDelegateQueue; ++ (nullable NSNumber *)nodeIDFromNOC:(MTRCertificateDERBytes)noc; ++ (nullable NSNumber *)fabricIDFromNOC:(MTRCertificateDERBytes)noc; ++ (nullable NSData *)publicKeyFromCertificate:(MTRCertificateDERBytes)certificate; + @end @interface MTRDeviceControllerStartupParamsInternal : MTRDeviceControllerStartupParams diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h index 54d5cfd8d340fa..e625d13a80e77e 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h @@ -45,6 +45,7 @@ #import #import +@class MTRDeviceControllerParameters; @class MTRDeviceControllerStartupParamsInternal; @class MTRDeviceControllerFactory; @class MTRDevice; @@ -117,6 +118,21 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, readonly) MTRAsyncWorkQueue * concurrentSubscriptionPool; +/** + * Fabric ID tied to controller + */ +@property (nonatomic, retain, nullable) NSNumber * fabricID; + +/** + * Node ID tied to controller + */ +@property (nonatomic, retain, nullable) NSNumber * nodeID; + +/** + * Root Public Key tied to controller + */ +@property (nonatomic, retain, nullable) NSData * rootPublicKey; + /** * Init a newly created controller. * @@ -289,6 +305,30 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)directlyGetSessionForNode:(chip::NodeId)nodeID completion:(MTRInternalDeviceConnectionCallback)completion; +/** + * Takes an assertion to keep the controller running. If `-[MTRDeviceController shutdown]` is called while an assertion + * is held, the shutdown will be honored only after all assertions are released. Invoking this method multiple times increases + * the number of assertions and needs to be matched with equal amount of '-[MTRDeviceController removeRunAssertion]` to release + * the assertion. + */ +- (void)addRunAssertion; + +/** + * Removes an assertion to allow the controller to shutdown once all assertions have been released. + * Invoking this method once all assertions have been released in a noop. + */ +- (void)removeRunAssertion; + +/** + * This method returns TRUE if this controller matches the fabric reference and node ID as listed in the parameters. + */ +- (BOOL)matchesPendingShutdownWithParams:(MTRDeviceControllerParameters *)parameters; + +/** + * Clear any pending shutdown request. + */ +- (void)clearPendingShutdown; + @end /** From 0b5af2a0bcdbc1c0f96b239b631a8eb3148fce12 Mon Sep 17 00:00:00 2001 From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:31:26 -0400 Subject: [PATCH 06/12] Cleanup/updates tied with latest sdk update (#35140) --- examples/platform/silabs/FreeRTOSConfig.h | 2 +- src/platform/silabs/ConfigurationManagerImpl.cpp | 2 +- .../silabs/efr32/Efr32PsaOpaqueKeypair.cpp | 15 +++++---------- .../silabs/efr32/OTAImageProcessorImpl.cpp | 2 +- .../silabs/multi-ota/OTACustomProcessor.cpp | 2 +- .../silabs/multi-ota/OTAFirmwareProcessor.cpp | 2 +- .../multi-ota/OTAMultiImageProcessorImpl.cpp | 2 +- .../silabs/platformAbstraction/GsdkSpam.cpp | 16 +++++++++++++++- src/test_driver/efr32/include/FreeRTOSConfig.h | 2 +- 9 files changed, 27 insertions(+), 18 deletions(-) diff --git a/examples/platform/silabs/FreeRTOSConfig.h b/examples/platform/silabs/FreeRTOSConfig.h index b27c775d9b7a54..1c2db9621f61ee 100644 --- a/examples/platform/silabs/FreeRTOSConfig.h +++ b/examples/platform/silabs/FreeRTOSConfig.h @@ -118,8 +118,8 @@ extern uint32_t SystemCoreClock; #include "RTE_Components.h" #include CMSIS_device_header -#include "em_assert.h" #include "em_device.h" +#include "sl_assert.h" #endif #if defined(SL_COMPONENT_CATALOG_PRESENT) diff --git a/src/platform/silabs/ConfigurationManagerImpl.cpp b/src/platform/silabs/ConfigurationManagerImpl.cpp index 5a511038c799ed..122799952acab5 100644 --- a/src/platform/silabs/ConfigurationManagerImpl.cpp +++ b/src/platform/silabs/ConfigurationManagerImpl.cpp @@ -121,7 +121,7 @@ CHIP_ERROR ConfigurationManagerImpl::GetBootReason(uint32_t & bootReason) matterBootCause = BootReasonType::kPowerOnReboot; } else if (rebootCause & EMU_RSTCAUSE_AVDDBOD || rebootCause & EMU_RSTCAUSE_DVDDBOD || rebootCause & EMU_RSTCAUSE_DECBOD || - rebootCause & EMU_RSTCAUSE_VREGIN || rebootCause & EMU_RSTCAUSE_IOVDD0BOD || rebootCause & EMU_RSTCAUSE_DVDDLEBOD) + rebootCause & EMU_RSTCAUSE_IOVDD0BOD || rebootCause & EMU_RSTCAUSE_DVDDLEBOD) { matterBootCause = BootReasonType::kBrownOutReset; } diff --git a/src/platform/silabs/efr32/Efr32PsaOpaqueKeypair.cpp b/src/platform/silabs/efr32/Efr32PsaOpaqueKeypair.cpp index 7a74c137596070..542142cd1a1d5c 100644 --- a/src/platform/silabs/efr32/Efr32PsaOpaqueKeypair.cpp +++ b/src/platform/silabs/efr32/Efr32PsaOpaqueKeypair.cpp @@ -18,6 +18,7 @@ #include "Efr32OpaqueKeypair.h" #include "em_device.h" #include +#include #include #include @@ -47,14 +48,6 @@ namespace Internal { static_assert((kEFR32OpaqueKeyIdPersistentMax - kEFR32OpaqueKeyIdPersistentMin) < PSA_KEY_ID_FOR_MATTER_SIZE, "Not enough PSA range to store all allowed opaque key IDs"); -#if defined(SEMAILBOX_PRESENT) && (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT) -#define PSA_CRYPTO_LOCATION_FOR_DEVICE PSA_KEY_LOCATION_SL_SE_OPAQUE -#elif defined(CRYPTOACC_PRESENT) && defined(SEPUF_PRESENT) && defined(SL_TRUSTZONE_NONSECURE) -#define PSA_CRYPTO_LOCATION_FOR_DEVICE PSA_KEY_LOCATION_SL_CRYPTOACC_OPAQUE -#else -#define PSA_CRYPTO_LOCATION_FOR_DEVICE PSA_KEY_LOCATION_LOCAL_STORAGE -#endif - static void _log_PSA_error(psa_status_t status) { if (status != PSA_SUCCESS) @@ -190,7 +183,8 @@ CHIP_ERROR EFR32OpaqueKeypair::Create(EFR32OpaqueKeyId opaque_id, EFR32OpaqueKey if (opaque_id == kEFR32OpaqueKeyIdVolatile) { psa_set_key_lifetime( - &attr, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_VOLATILE, PSA_CRYPTO_LOCATION_FOR_DEVICE)); + &attr, + PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_VOLATILE, sl_psa_get_most_secure_key_location())); } else { @@ -210,7 +204,8 @@ CHIP_ERROR EFR32OpaqueKeypair::Create(EFR32OpaqueKeyId opaque_id, EFR32OpaqueKey psa_set_key_id(&attr, key_id); psa_set_key_lifetime( - &attr, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_PERSISTENT, PSA_CRYPTO_LOCATION_FOR_DEVICE)); + &attr, + PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_PERSISTENT, sl_psa_get_most_secure_key_location())); } switch (usage) diff --git a/src/platform/silabs/efr32/OTAImageProcessorImpl.cpp b/src/platform/silabs/efr32/OTAImageProcessorImpl.cpp index 598ee5a2093605..846d4f720ac930 100644 --- a/src/platform/silabs/efr32/OTAImageProcessorImpl.cpp +++ b/src/platform/silabs/efr32/OTAImageProcessorImpl.cpp @@ -22,7 +22,7 @@ extern "C" { #include "btl_interface.h" -#include "em_bus.h" // For CORE_CRITICAL_SECTION +#include "sl_core.h" #if SL_WIFI #include "spi_multiplex.h" #endif // SL_WIFI diff --git a/src/platform/silabs/multi-ota/OTACustomProcessor.cpp b/src/platform/silabs/multi-ota/OTACustomProcessor.cpp index 85ceb7d44d4eaf..ef761d003e0499 100644 --- a/src/platform/silabs/multi-ota/OTACustomProcessor.cpp +++ b/src/platform/silabs/multi-ota/OTACustomProcessor.cpp @@ -24,7 +24,7 @@ extern "C" { #include "btl_interface.h" -#include "em_bus.h" // For CORE_CRITICAL_SECTION +#include "sl_core.h" #if SL_WIFI #include "spi_multiplex.h" #endif // SL_WIFI diff --git a/src/platform/silabs/multi-ota/OTAFirmwareProcessor.cpp b/src/platform/silabs/multi-ota/OTAFirmwareProcessor.cpp index 3fc66e53913b87..d8545d01f40996 100644 --- a/src/platform/silabs/multi-ota/OTAFirmwareProcessor.cpp +++ b/src/platform/silabs/multi-ota/OTAFirmwareProcessor.cpp @@ -24,7 +24,7 @@ extern "C" { #include "btl_interface.h" -#include "em_bus.h" // For CORE_CRITICAL_SECTION +#include "sl_core.h" #if SL_WIFI #include "spi_multiplex.h" #endif // SL_WIFI diff --git a/src/platform/silabs/multi-ota/OTAMultiImageProcessorImpl.cpp b/src/platform/silabs/multi-ota/OTAMultiImageProcessorImpl.cpp index 9dfb42fc879c4b..a95cb4e1ada4b4 100644 --- a/src/platform/silabs/multi-ota/OTAMultiImageProcessorImpl.cpp +++ b/src/platform/silabs/multi-ota/OTAMultiImageProcessorImpl.cpp @@ -32,7 +32,7 @@ static chip::OTAMultiImageProcessorImpl gImageProcessor; extern "C" { #include "btl_interface.h" -#include "em_bus.h" // For CORE_CRITICAL_SECTION +#include "sl_core.h" #if SL_WIFI #include "spi_multiplex.h" #endif // SL_WIFI diff --git a/src/platform/silabs/platformAbstraction/GsdkSpam.cpp b/src/platform/silabs/platformAbstraction/GsdkSpam.cpp index 514003305868cf..eb8704ecfc1116 100644 --- a/src/platform/silabs/platformAbstraction/GsdkSpam.cpp +++ b/src/platform/silabs/platformAbstraction/GsdkSpam.cpp @@ -17,7 +17,11 @@ #include +#if defined(_SILICON_LABS_32B_SERIES_2) #include "em_rmu.h" +#else +#include "sl_hal_emu.h" +#endif #include "sl_system_kernel.h" #ifdef ENABLE_WSTK_LEDS @@ -71,9 +75,19 @@ SilabsPlatform::SilabsButtonCb SilabsPlatform::mButtonCallback = nullptr; CHIP_ERROR SilabsPlatform::Init(void) { +#ifdef _SILICON_LABS_32B_SERIES_2 + // Read the cause of last reset. mRebootCause = RMU_ResetCauseGet(); - // Clear register so it does accumualate the causes of each reset + + // Clear the register, as the causes cumulate over resets. RMU_ResetCauseClear(); +#else + // Read the cause of last reset. + mRebootCause = sl_hal_emu_get_reset_cause(); + + // Clear the register, as the causes cumulate over resets. + sl_hal_emu_clear_reset_cause(); +#endif // _SILICON_LABS_32B_SERIES_2 #if SILABS_LOG_OUT_UART && defined(SL_CATALOG_CLI_PRESENT) sl_iostream_set_default(sl_iostream_stdio_handle); diff --git a/src/test_driver/efr32/include/FreeRTOSConfig.h b/src/test_driver/efr32/include/FreeRTOSConfig.h index 7a02673c27b5b0..5cd17b4a7257e0 100644 --- a/src/test_driver/efr32/include/FreeRTOSConfig.h +++ b/src/test_driver/efr32/include/FreeRTOSConfig.h @@ -108,8 +108,8 @@ extern "C" { #include "RTE_Components.h" #include CMSIS_device_header -#include "em_assert.h" #include "em_device.h" +#include "sl_assert.h" #if defined(SL_COMPONENT_CATALOG_PRESENT) #include "sl_component_catalog.h" From f01ec93099f1d3f9c542ff50d2ff1c3cbdb52271 Mon Sep 17 00:00:00 2001 From: chirag-silabs <100861685+chirag-silabs@users.noreply.github.com> Date: Sat, 24 Aug 2024 02:02:50 +0530 Subject: [PATCH 07/12] [Silabs] Adding the gn build header for the lwip IPv4 and RS9116 (#35142) * adding the gn build header for the lwip and rs9116 * Restyled by clang-format --------- Co-authored-by: Restyled.io --- .../platform/silabs/efr32/rs911x/rsi_wlan_config.h | 4 ++++ src/lwip/silabs/lwipopts-rs911x.h | 10 ++++++++++ src/lwip/silabs/lwipopts-wf200.h | 10 ++++++++++ 3 files changed, 24 insertions(+) diff --git a/examples/platform/silabs/efr32/rs911x/rsi_wlan_config.h b/examples/platform/silabs/efr32/rs911x/rsi_wlan_config.h index 96bd71bb80539a..b851cd79580a22 100644 --- a/examples/platform/silabs/efr32/rs911x/rsi_wlan_config.h +++ b/examples/platform/silabs/efr32/rs911x/rsi_wlan_config.h @@ -20,6 +20,10 @@ #include "rsi_wlan_defines.h" +#if (SL_MATTER_GN_BUILD == 0) +#include "sl_matter_wifi_config.h" +#endif // SL_MATTER_GN_BUILD + //! Enable feature #define RSI_ENABLE 1 //! Disable feature diff --git a/src/lwip/silabs/lwipopts-rs911x.h b/src/lwip/silabs/lwipopts-rs911x.h index 4f8075461b0c6d..c563a706ac9737 100644 --- a/src/lwip/silabs/lwipopts-rs911x.h +++ b/src/lwip/silabs/lwipopts-rs911x.h @@ -32,6 +32,10 @@ #include +#if (SL_MATTER_GN_BUILD == 0) +#include "sl_matter_wifi_config.h" +#endif // SL_MATTER_GN_BUILD + #define NO_SYS 0 #define MEM_ALIGNMENT (4) #define MEMP_NUM_TCP_SEG (TCP_SND_QUEUELEN + 1) @@ -90,6 +94,12 @@ #define MEMP_NUM_NETCONN (0) +#if CHIP_DEVICE_CONFIG_ENABLE_IPV4 +#define LWIP_IPV4 1 +#else +#define LWIP_IPV4 0 +#endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 + #ifndef LWIP_ARP #define LWIP_ARP (LWIP_IPV4) #endif /* LWIP_ARP */ diff --git a/src/lwip/silabs/lwipopts-wf200.h b/src/lwip/silabs/lwipopts-wf200.h index cc9b45b144278a..9ea7e4f6bbf07c 100644 --- a/src/lwip/silabs/lwipopts-wf200.h +++ b/src/lwip/silabs/lwipopts-wf200.h @@ -32,6 +32,10 @@ #include +#if (SL_MATTER_GN_BUILD == 0) +#include "sl_matter_wifi_config.h" +#endif // SL_MATTER_GN_BUILD + #define NO_SYS 0 #define MEM_ALIGNMENT (4) #define MEMP_NUM_TCP_SEG (TCP_SND_QUEUELEN + 1) @@ -91,6 +95,12 @@ #define MEMP_NUM_NETCONN (0) +#if CHIP_DEVICE_CONFIG_ENABLE_IPV4 +#define LWIP_IPV4 1 +#else +#define LWIP_IPV4 0 +#endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 + #ifndef LWIP_ARP #define LWIP_ARP (LWIP_IPV4) #endif /* LWIP_ARP */ From 26e59b68b795ff36f1e8a14dd602c46d1e442c57 Mon Sep 17 00:00:00 2001 From: chirag-silabs <100861685+chirag-silabs@users.noreply.github.com> Date: Sat, 24 Aug 2024 02:17:24 +0530 Subject: [PATCH 08/12] [Silabs] [WiFi] Fixing the retry logic for the WiFi Devices (#34225) * Modifying the retry logic for the wifi devices * Baseapplication.cpp modification of the delegate fix * Restyled by clang-format * removing the soc and sleepy condition * Restyled by clang-format * Restyled by gn * moving the variable to efr32_sdk.gni to make it available everywhere * correcting the path of the 917 ncp file * fixing the build for the 917 ncp * addressing the review comments * fixing the build and replacing the define * removing the unused else case * Update third_party/silabs/efr32_sdk.gni Co-authored-by: mkardous-silabs <84793247+mkardous-silabs@users.noreply.github.com> * addressing the review comments * Restyled by whitespace * Restyled by clang-format * fixing the build * fixing the efr32 build --------- Co-authored-by: Restyled.io Co-authored-by: mkardous-silabs <84793247+mkardous-silabs@users.noreply.github.com> --- examples/platform/silabs/BaseApplication.cpp | 3 + examples/platform/silabs/BaseApplication.h | 5 +- examples/platform/silabs/SiWx917/BUILD.gn | 4 +- .../silabs/SiWx917/SiWx917/sl_wifi_if.cpp | 22 +- examples/platform/silabs/efr32/BUILD.gn | 2 - .../platform/silabs/efr32/rs911x/rs9117.gni | 2 +- .../platform/silabs/efr32/rs911x/rs911x.gni | 2 +- .../platform/silabs/efr32/rs911x/rsi_if.c | 103 +++----- .../platform/silabs/efr32/wf200/host_if.cpp | 28 +-- .../platform/silabs/efr32/wf200/wf200.gni | 2 +- examples/platform/silabs/wfx_rsi.h | 1 - .../platform/silabs}/wifi/wfx_notify.cpp | 130 +++++------ .../silabs/SiWx917/wifi/wfx_host_events.h | 8 +- .../silabs/efr32/wifi/wfx_host_events.h | 13 +- src/platform/silabs/efr32/wifi/wfx_notify.cpp | 220 ------------------ third_party/silabs/efr32_sdk.gni | 2 + 16 files changed, 131 insertions(+), 416 deletions(-) rename {src/platform/silabs/SiWx917 => examples/platform/silabs}/wifi/wfx_notify.cpp (64%) delete mode 100644 src/platform/silabs/efr32/wifi/wfx_notify.cpp diff --git a/examples/platform/silabs/BaseApplication.cpp b/examples/platform/silabs/BaseApplication.cpp index 99123c5bf764d1..8cf5b3891dfe8e 100644 --- a/examples/platform/silabs/BaseApplication.cpp +++ b/examples/platform/silabs/BaseApplication.cpp @@ -190,6 +190,9 @@ void BaseApplicationDelegate::OnCommissioningWindowClosed() #endif // CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI917 if (BaseApplication::GetProvisionStatus()) { + // After the device is provisioned and the commissioning passed + // resetting the isCommissioningStarted to false + isComissioningStarted = false; #ifdef DISPLAY_ENABLED #ifdef QR_CODE_ENABLED SilabsLCD::Screen_e screen; diff --git a/examples/platform/silabs/BaseApplication.h b/examples/platform/silabs/BaseApplication.h index 9052e9355aab90..5d757b8ca4672d 100644 --- a/examples/platform/silabs/BaseApplication.h +++ b/examples/platform/silabs/BaseApplication.h @@ -65,9 +65,12 @@ class BaseApplicationDelegate : public AppDelegate, public chip::FabricTable::Delegate { +public: + bool isCommissioningInProgress() { return isComissioningStarted; } + private: // AppDelegate - bool isComissioningStarted; + bool isComissioningStarted = false; void OnCommissioningSessionStarted() override; void OnCommissioningSessionStopped() override; void OnCommissioningWindowClosed() override; diff --git a/examples/platform/silabs/SiWx917/BUILD.gn b/examples/platform/silabs/SiWx917/BUILD.gn index 80d247b56c4df4..2780e2ecbcc16e 100644 --- a/examples/platform/silabs/SiWx917/BUILD.gn +++ b/examples/platform/silabs/SiWx917/BUILD.gn @@ -50,8 +50,6 @@ declare_args() { # Sanity check assert(chip_enable_wifi) - -silabs_common_plat_dir = "${chip_root}/examples/platform/silabs" silabs_plat_si91x_wifi_dir = "${chip_root}/src/platform/silabs/SiWx917/wifi" import("${silabs_common_plat_dir}/args.gni") @@ -193,10 +191,10 @@ source_set("siwx917-common") { "${silabs_common_plat_dir}/SoftwareFaultReports.cpp", "${silabs_common_plat_dir}/silabs_utils.cpp", "${silabs_common_plat_dir}/syscalls_stubs.cpp", + "${silabs_common_plat_dir}/wifi/wfx_notify.cpp", "${silabs_plat_si91x_wifi_dir}/dhcp_client.cpp", "${silabs_plat_si91x_wifi_dir}/ethernetif.cpp", "${silabs_plat_si91x_wifi_dir}/lwip_netif.cpp", - "${silabs_plat_si91x_wifi_dir}/wfx_notify.cpp", "SiWx917/sl_wifi_if.cpp", "SiWx917/wfx_rsi_host.cpp", ] diff --git a/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp b/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp index 03b51540cf4b6d..92d5ca4808195c 100644 --- a/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp +++ b/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp @@ -101,11 +101,6 @@ bool hasNotifiedIPV4 = false; #endif /* CHIP_DEVICE_CONFIG_ENABLE_IPV4 */ bool hasNotifiedWifiConnectivity = false; -/* Declare a flag to differentiate between after boot-up first IP connection or reconnection */ -bool is_wifi_disconnection_event = false; - -/* Declare a variable to hold connection time intervals */ -uint32_t retryInterval = WLAN_MIN_RETRY_TIMER_MS; volatile bool scan_results_complete = false; volatile bool bg_scan_results_complete = false; extern osSemaphoreId_t sl_rs_ble_init_sem; @@ -247,12 +242,7 @@ sl_status_t join_callback_handler(sl_wifi_event_t event, char * result, uint32_t callback_status = *(sl_status_t *) result; ChipLogError(DeviceLayer, "join_callback_handler: failed: 0x%lx", static_cast(callback_status)); wfx_rsi.dev_state &= ~(WFX_RSI_ST_STA_CONNECTED); - wfx_retry_interval_handler(is_wifi_disconnection_event, wfx_rsi.join_retries++); - if (is_wifi_disconnection_event || wfx_rsi.join_retries <= WFX_RSI_CONFIG_MAX_JOIN) - { - WfxEvent.eventType = WFX_EVT_STA_START_JOIN; - WfxPostEvent(&WfxEvent); - } + wfx_retry_connection(++wfx_rsi.join_retries); return SL_STATUS_FAIL; } /* @@ -264,10 +254,7 @@ sl_status_t join_callback_handler(sl_wifi_event_t event, char * result, uint32_t WfxEvent.eventType = WFX_EVT_STA_CONN; WfxPostEvent(&WfxEvent); wfx_rsi.join_retries = 0; - retryInterval = WLAN_MIN_RETRY_TIMER_MS; - // Once the join passes setting the disconnection event to true to differentiate between the first connection and reconnection - is_wifi_disconnection_event = true; - callback_status = SL_STATUS_OK; + callback_status = SL_STATUS_OK; return SL_STATUS_OK; } @@ -693,12 +680,11 @@ static sl_status_t wfx_rsi_do_join(void) // failure only happens when the firmware returns an error ChipLogError(DeviceLayer, "wfx_rsi_do_join: sl_wifi_connect failed: 0x%lx", static_cast(status)); - VerifyOrReturnError((is_wifi_disconnection_event || wfx_rsi.join_retries <= MAX_JOIN_RETRIES_COUNT), status); + VerifyOrReturnError((wfx_rsi.join_retries <= MAX_JOIN_RETRIES_COUNT), status); wfx_rsi.dev_state &= ~(WFX_RSI_ST_STA_CONNECTING | WFX_RSI_ST_STA_CONNECTED); ChipLogProgress(DeviceLayer, "wfx_rsi_do_join: retry attempt %d", wfx_rsi.join_retries); - wfx_retry_interval_handler(is_wifi_disconnection_event, wfx_rsi.join_retries); - wfx_rsi.join_retries++; + wfx_retry_connection(++wfx_rsi.join_retries); event.eventType = WFX_EVT_STA_START_JOIN; WfxPostEvent(&event); return status; diff --git a/examples/platform/silabs/efr32/BUILD.gn b/examples/platform/silabs/efr32/BUILD.gn index 22b7458588d97c..3691091a0bc3c7 100644 --- a/examples/platform/silabs/efr32/BUILD.gn +++ b/examples/platform/silabs/efr32/BUILD.gn @@ -48,8 +48,6 @@ declare_args() { sl_test_event_trigger_enable_key = "00112233445566778899AABBCCDDEEFF" } -silabs_common_plat_dir = "${chip_root}/examples/platform/silabs" - import("${silabs_common_plat_dir}/args.gni") # Sanity check diff --git a/examples/platform/silabs/efr32/rs911x/rs9117.gni b/examples/platform/silabs/efr32/rs911x/rs9117.gni index c068e7aa3efaff..356b72f55f75d4 100644 --- a/examples/platform/silabs/efr32/rs911x/rs9117.gni +++ b/examples/platform/silabs/efr32/rs911x/rs9117.gni @@ -8,7 +8,7 @@ rs911x_src_plat = [ "${examples_plat_dir}/rs911x/hal/rsi_hal_mcu_interrupt.c", "${examples_plat_dir}/rs911x/hal/sl_si91x_ncp_utility.c", "${examples_plat_dir}/rs911x/hal/efx32_ncp_host.c", - "${silabs_plat_efr32_wifi_dir}/wfx_notify.cpp", + "${silabs_common_plat_dir}/wifi/wfx_notify.cpp", ] rs9117_inc_plat = [ diff --git a/examples/platform/silabs/efr32/rs911x/rs911x.gni b/examples/platform/silabs/efr32/rs911x/rs911x.gni index ebf7c546f6a068..54507de66e0ced 100644 --- a/examples/platform/silabs/efr32/rs911x/rs911x.gni +++ b/examples/platform/silabs/efr32/rs911x/rs911x.gni @@ -9,7 +9,7 @@ rs911x_src_plat = [ "${examples_plat_dir}/rs911x/hal/rsi_hal_mcu_ioports.c", "${examples_plat_dir}/rs911x/hal/rsi_hal_mcu_timer.c", "${examples_plat_dir}/rs911x/hal/efx_spi.c", - "${silabs_plat_efr32_wifi_dir}/wfx_notify.cpp", + "${silabs_common_plat_dir}/wifi/wfx_notify.cpp", ] # diff --git a/examples/platform/silabs/efr32/rs911x/rsi_if.c b/examples/platform/silabs/efr32/rs911x/rsi_if.c index e4d6f51bf0ded9..f0121285383d00 100644 --- a/examples/platform/silabs/efr32/rs911x/rsi_if.c +++ b/examples/platform/silabs/efr32/rs911x/rsi_if.c @@ -70,12 +70,6 @@ bool hasNotifiedIPV4 = false; #endif /* CHIP_DEVICE_CONFIG_ENABLE_IPV4 */ bool hasNotifiedWifiConnectivity = false; -/* Declare a flag to differentiate between after boot-up first IP connection or reconnection */ -bool is_wifi_disconnection_event = false; - -/* Declare a variable to hold connection time intervals */ -uint32_t retryInterval = WLAN_MIN_RETRY_TIMER_MS; - #if (RSI_BLE_ENABLE) extern rsi_semaphore_handle_t sl_rs_ble_init_sem; #endif @@ -279,13 +273,8 @@ static void wfx_rsi_join_cb(uint16_t status, const uint8_t * buf, const uint16_t /* * We should enable retry.. (Need config variable for this) */ - SILABS_LOG("%s: failed. retry: %d", __func__, wfx_rsi.join_retries); - wfx_retry_interval_handler(is_wifi_disconnection_event, wfx_rsi.join_retries++); - if (is_wifi_disconnection_event || wfx_rsi.join_retries <= WFX_RSI_CONFIG_MAX_JOIN) - { - WfxEvent.eventType = WFX_EVT_STA_START_JOIN; - WfxPostEvent(&WfxEvent); - } + SILABS_LOG("wfx_rsi_join_cb: failed. retry: %d", wfx_rsi.join_retries); + wfx_retry_connection(++wfx_rsi.join_retries); } else { @@ -293,11 +282,10 @@ static void wfx_rsi_join_cb(uint16_t status, const uint8_t * buf, const uint16_t * Join was complete - Do the DHCP */ memset(&temp_reset, 0, sizeof(wfx_wifi_scan_ext_t)); - SILABS_LOG("%s: join completed.", __func__); + SILABS_LOG("wfx_rsi_join_cb: join completed."); WfxEvent.eventType = WFX_EVT_STA_CONN; WfxPostEvent(&WfxEvent); wfx_rsi.join_retries = 0; - retryInterval = WLAN_MIN_RETRY_TIMER_MS; } } @@ -313,12 +301,11 @@ static void wfx_rsi_join_cb(uint16_t status, const uint8_t * buf, const uint16_t *********************************************************************/ static void wfx_rsi_join_fail_cb(uint16_t status, uint8_t * buf, uint32_t len) { - SILABS_LOG("%s: error: failed status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_join_fail_cb: error: failed status: %02x", status); WfxEvent_t WfxEvent; wfx_rsi.join_retries += 1; wfx_rsi.dev_state &= ~(WFX_RSI_ST_STA_CONNECTING | WFX_RSI_ST_STA_CONNECTED); - is_wifi_disconnection_event = true; - WfxEvent.eventType = WFX_EVT_STA_START_JOIN; + WfxEvent.eventType = WFX_EVT_STA_START_JOIN; WfxPostEvent(&WfxEvent); } /************************************************************************************* @@ -354,23 +341,23 @@ static int32_t wfx_rsi_init(void) uint8_t buf[RSI_RESPONSE_HOLD_BUFF_SIZE]; extern void rsi_hal_board_init(void); - SILABS_LOG("%s: starting(HEAP_SZ = %d)", __func__, SL_HEAP_SIZE); + SILABS_LOG("wfx_rsi_init: starting(HEAP_SZ = %d)", SL_HEAP_SIZE); //! Driver initialization status = rsi_driver_init(wfx_rsi_drv_buf, WFX_RSI_BUF_SZ); if ((status < RSI_DRIVER_STATUS) || (status > WFX_RSI_BUF_SZ)) { - SILABS_LOG("%s: error: RSI Driver initialization failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: error: RSI Driver initialization failed with status: %02x", status); return status; } - SILABS_LOG("%s: rsi_device_init", __func__); + SILABS_LOG("wfx_rsi_init: rsi_device_init", __func__); /* ! Redpine module intialisation */ if ((status = rsi_device_init(LOAD_NWP_FW)) != RSI_SUCCESS) { - SILABS_LOG("%s: error: rsi_device_init failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: error: rsi_device_init failed with status: %02x", status); return status; } - SILABS_LOG("%s: start wireless drv task", __func__); + SILABS_LOG("wfx_rsi_init: start wireless drv task", __func__); /* * Create the driver task */ @@ -378,7 +365,7 @@ static int32_t wfx_rsi_init(void) WLAN_TASK_PRIORITY, driverRsiTaskStack, &driverRsiTaskBuffer); if (NULL == wfx_rsi.drv_task) { - SILABS_LOG("%s: error: rsi_wireless_driver_task failed", __func__); + SILABS_LOG("wfx_rsi_init: error: rsi_wireless_driver_task failed", __func__); return RSI_ERROR_INVALID_PARAM; } @@ -389,40 +376,40 @@ static int32_t wfx_rsi_init(void) if ((status = rsi_wireless_init(OPER_MODE_0, COEX_MODE_0)) != RSI_SUCCESS) { #endif - SILABS_LOG("%s: error: Initialize WiSeConnect failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: error: Initialize WiSeConnect failed with status: %02x", status); return status; } - SILABS_LOG("%s: get FW version..", __func__); + SILABS_LOG("wfx_rsi_init: get FW version..", __func__); /* * Get the MAC and other info to let the user know about it. */ if (rsi_wlan_get(RSI_FW_VERSION, buf, sizeof(buf)) != RSI_SUCCESS) { - SILABS_LOG("%s: error: rsi_wlan_get(RSI_FW_VERSION) failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: error: rsi_wlan_get(RSI_FW_VERSION) failed with status: %02x", status); return status; } buf[sizeof(buf) - 1] = 0; - SILABS_LOG("%s: RSI firmware version: %s", __func__, buf); + SILABS_LOG("wfx_rsi_init: RSI firmware version: %s", buf); //! Send feature frame if ((status = rsi_send_feature_frame()) != RSI_SUCCESS) { - SILABS_LOG("%s: error: rsi_send_feature_frame failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: error: rsi_send_feature_frame failed with status: %02x", status); return status; } - SILABS_LOG("%s: sent rsi_send_feature_frame", __func__); + SILABS_LOG("wfx_rsi_init: sent rsi_send_feature_frame", __func__); /* initializes wlan radio parameters and WLAN supplicant parameters. */ (void) rsi_wlan_radio_init(); /* Required so we can get MAC address */ if ((status = rsi_wlan_get(RSI_MAC_ADDRESS, &wfx_rsi.sta_mac.octet[0], RESP_BUFF_SIZE)) != RSI_SUCCESS) { - SILABS_LOG("%s: error: rsi_wlan_get failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: error: rsi_wlan_get failed with status: %02x", status); return status; } - SILABS_LOG("%s: WLAN: MAC %02x:%02x:%02x %02x:%02x:%02x", __func__, wfx_rsi.sta_mac.octet[0], wfx_rsi.sta_mac.octet[1], + SILABS_LOG("wfx_rsi_init: WLAN: MAC %02x:%02x:%02x %02x:%02x:%02x", wfx_rsi.sta_mac.octet[0], wfx_rsi.sta_mac.octet[1], wfx_rsi.sta_mac.octet[2], wfx_rsi.sta_mac.octet[3], wfx_rsi.sta_mac.octet[4], wfx_rsi.sta_mac.octet[5]); // Create the message queue @@ -445,12 +432,12 @@ static int32_t wfx_rsi_init(void) */ if ((status = rsi_wlan_register_callbacks(RSI_JOIN_FAIL_CB, wfx_rsi_join_fail_cb)) != RSI_SUCCESS) { - SILABS_LOG("%s: RSI callback register join failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: RSI callback register join failed with status: %02x", status); return status; } if ((status = rsi_wlan_register_callbacks(RSI_WLAN_DATA_RECEIVE_NOTIFY_CB, wfx_rsi_wlan_pkt_cb)) != RSI_SUCCESS) { - SILABS_LOG("%s: RSI callback register data-notify failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: RSI callback register data-notify failed with status: %02x", status); return status; } @@ -459,7 +446,7 @@ static int32_t wfx_rsi_init(void) #endif wfx_rsi.dev_state |= WFX_RSI_ST_DEV_READY; - SILABS_LOG("%s: RSI: OK", __func__); + SILABS_LOG("wfx_rsi_init: RSI: OK", __func__); return RSI_SUCCESS; } @@ -488,7 +475,7 @@ static void wfx_rsi_save_ap_info() // translation #else /* !WIFI_ENABLE_SECURITY_WPA3_TRANSITION */ wfx_rsi.sec.security = WFX_SEC_WPA2; #endif /* WIFI_ENABLE_SECURITY_WPA3_TRANSITION */ - SILABS_LOG("%s: warn: failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_save_ap_info: warn: failed with status: %02x", status); return; } wfx_rsi.sec.security = WFX_SEC_UNSPECIFIED; @@ -524,7 +511,8 @@ static void wfx_rsi_save_ap_info() // translation break; } - SILABS_LOG("%s: WLAN: connecting to %s, sec=%d, status=%02x", __func__, &wfx_rsi.sec.ssid[0], wfx_rsi.sec.security, status); + SILABS_LOG("wfx_rsi_save_ap_info: WLAN: connecting to %s, sec=%d, status=%02x", &wfx_rsi.sec.ssid[0], wfx_rsi.sec.security, + status); } /******************************************************************************************** @@ -541,7 +529,7 @@ static void wfx_rsi_do_join(void) if (wfx_rsi.dev_state & (WFX_RSI_ST_STA_CONNECTING | WFX_RSI_ST_STA_CONNECTED)) { - SILABS_LOG("%s: not joining - already in progress", __func__); + SILABS_LOG("wfx_rsi_do_join: not joining - already in progress"); } else { @@ -564,11 +552,11 @@ static void wfx_rsi_do_join(void) connect_security_mode = RSI_OPEN; break; default: - SILABS_LOG("%s: error: unknown security type.", __func__); + SILABS_LOG("wfx_rsi_do_join: error: unknown security type."); return; } - SILABS_LOG("%s: WLAN: connecting to %s, sec=%d", __func__, &wfx_rsi.sec.ssid[0], wfx_rsi.sec.security); + SILABS_LOG("wfx_rsi_do_join: WLAN: connecting to %s, sec=%d", &wfx_rsi.sec.ssid[0], wfx_rsi.sec.security); /* * Join the network @@ -580,33 +568,18 @@ static void wfx_rsi_do_join(void) if ((status = rsi_wlan_register_callbacks(RSI_JOIN_FAIL_CB, wfx_rsi_join_fail_cb)) != RSI_SUCCESS) { - SILABS_LOG("%s: RSI callback register join failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_do_join: RSI callback register join failed with status: %02x", status); } /* Try to connect Wifi with given Credentials * untill there is a success or maximum number of tries allowed */ - while (is_wifi_disconnection_event || wfx_rsi.join_retries <= WFX_RSI_CONFIG_MAX_JOIN) + if ((status = rsi_wlan_connect_async((int8_t *) &wfx_rsi.sec.ssid[0], connect_security_mode, &wfx_rsi.sec.passkey[0], + wfx_rsi_join_cb)) != RSI_SUCCESS) { - /* Call rsi connect call with given ssid and password - * And check there is a success - */ - if ((status = rsi_wlan_connect_async((int8_t *) &wfx_rsi.sec.ssid[0], connect_security_mode, &wfx_rsi.sec.passkey[0], - wfx_rsi_join_cb)) != RSI_SUCCESS) - { - - wfx_rsi.dev_state &= ~WFX_RSI_ST_STA_CONNECTING; - SILABS_LOG("%s: rsi_wlan_connect_async failed with status: %02x on try %d", __func__, status, wfx_rsi.join_retries); - - wfx_retry_interval_handler(is_wifi_disconnection_event, wfx_rsi.join_retries); - wfx_rsi.join_retries++; - } - else - { - SILABS_LOG("%s: starting JOIN to %s after %d tries\n", __func__, (char *) &wfx_rsi.sec.ssid[0], - wfx_rsi.join_retries); - break; // exit while loop - } + wfx_rsi.dev_state &= ~WFX_RSI_ST_STA_CONNECTING; + SILABS_LOG("wfx_rsi_do_join: rsi_wlan_connect_async failed with status: %02x on try %d", status, wfx_rsi.join_retries); + wfx_retry_connection(++wfx_rsi.join_retries); } } } @@ -701,7 +674,7 @@ void ProcessEvent(WfxEvent_t inEvent) switch (inEvent.eventType) { case WFX_EVT_STA_CONN: - SILABS_LOG("%s: starting LwIP STA", __func__); + SILABS_LOG("Starting LwIP STA"); wfx_rsi.dev_state |= WFX_RSI_ST_STA_CONNECTED; ResetDHCPNotificationFlags(); wfx_lwip_set_sta_link_up(); @@ -715,7 +688,7 @@ void ProcessEvent(WfxEvent_t inEvent) // TODO: This event is not being posted anywhere, seems to be a dead code or we are missing something wfx_rsi.dev_state &= ~(WFX_RSI_ST_STA_READY | WFX_RSI_ST_STA_CONNECTING | WFX_RSI_ST_STA_CONNECTED | WFX_RSI_ST_STA_DHCP_DONE); - SILABS_LOG("%s: disconnect notify", __func__); + SILABS_LOG("Disconnect notify"); /* TODO: Implement disconnect notify */ ResetDHCPNotificationFlags(); wfx_lwip_set_sta_link_down(); // Internally dhcpclient_poll(netif) -> @@ -813,7 +786,7 @@ void wfx_rsi_task(void * arg) uint32_t rsi_status = wfx_rsi_init(); if (rsi_status != RSI_SUCCESS) { - SILABS_LOG("%s: error: wfx_rsi_init with status: %02x", __func__, rsi_status); + SILABS_LOG("wfx_rsi_task: error: wfx_rsi_init with status: %02x", rsi_status); return; } WfxEvent_t wfxEvent; @@ -853,7 +826,7 @@ void wfx_dhcp_got_ipv4(uint32_t ip) wfx_rsi.ip4_addr[1] = (ip >> 8) & HEX_VALUE_FF; wfx_rsi.ip4_addr[2] = (ip >> 16) & HEX_VALUE_FF; wfx_rsi.ip4_addr[3] = (ip >> 24) & HEX_VALUE_FF; - SILABS_LOG("%s: DHCP OK: IP=%d.%d.%d.%d", __func__, wfx_rsi.ip4_addr[0], wfx_rsi.ip4_addr[1], wfx_rsi.ip4_addr[2], + SILABS_LOG("wfx_dhcp_got_ipv4: DHCP OK: IP=%d.%d.%d.%d", wfx_rsi.ip4_addr[0], wfx_rsi.ip4_addr[1], wfx_rsi.ip4_addr[2], wfx_rsi.ip4_addr[3]); /* Notify the Connectivity Manager - via the app */ wfx_rsi.dev_state |= WFX_RSI_ST_STA_DHCP_DONE; diff --git a/examples/platform/silabs/efr32/wf200/host_if.cpp b/examples/platform/silabs/efr32/wf200/host_if.cpp index ac3ad07773d9d3..44e65e2728fa4b 100644 --- a/examples/platform/silabs/efr32/wf200/host_if.cpp +++ b/examples/platform/silabs/efr32/wf200/host_if.cpp @@ -99,12 +99,6 @@ bool hasNotifiedWifiConnectivity = false; static uint8_t retryJoin = 0; bool retryInProgress = false; -/* Declare a flag to differentiate between after boot-up first IP connection or reconnection */ -bool is_wifi_disconnection_event = false; - -/* Declare a variable to hold connection time intervals */ -uint32_t retryInterval = WLAN_MIN_RETRY_TIMER_MS; - #ifdef SL_WFX_CONFIG_SCAN static struct scan_result_holder { @@ -401,14 +395,14 @@ static void sl_wfx_connect_callback(sl_wfx_connect_ind_body_t connect_indication } } - if ((status != WFM_STATUS_SUCCESS) && (!is_wifi_disconnection_event ? (retryJoin < MAX_JOIN_RETRIES_COUNT) : true)) + if (status != WFM_STATUS_SUCCESS) { retryJoin += 1; retryInProgress = false; SILABS_LOG("WFX Retry to connect to network count: %d", retryJoin); sl_wfx_context->state = static_cast(static_cast(sl_wfx_context->state) & ~static_cast(SL_WFX_STARTED)); - xEventGroupSetBits(sl_wfx_event_group, SL_WFX_RETRY_CONNECT); + wfx_retry_connection(retryJoin); } } @@ -424,9 +418,8 @@ static void sl_wfx_disconnect_callback(uint8_t * mac, uint16_t reason) SILABS_LOG("WFX Disconnected %d\r\n", reason); sl_wfx_context->state = static_cast(static_cast(sl_wfx_context->state) & ~static_cast(SL_WFX_STA_INTERFACE_CONNECTED)); - retryInProgress = false; - is_wifi_disconnection_event = true; - xEventGroupSetBits(sl_wfx_event_group, SL_WFX_RETRY_CONNECT); + retryInProgress = false; + wfx_retry_connection(retryJoin); } #ifdef SL_WFX_CONFIG_SOFTAP @@ -541,13 +534,8 @@ static void wfx_events_task(void * p_arg) pdTRUE, pdFALSE, pdMS_TO_TICKS(250)); /* 250 msec delay converted to ticks */ if (flags & SL_WFX_RETRY_CONNECT) { - if (!retryInProgress) - { - retryInProgress = true; - wfx_retry_interval_handler(is_wifi_disconnection_event, retryJoin); - SILABS_LOG("WFX sending the connect command"); - wfx_connect_to_ap(); - } + SILABS_LOG("WFX sending the connect command"); + wfx_connect_to_ap(); } if (wifi_extra & WE_ST_STA_CONN) @@ -599,8 +587,7 @@ static void wfx_events_task(void * p_arg) hasNotifiedWifiConnectivity = false; SILABS_LOG("WIFI: Connected to AP"); wifi_extra |= WE_ST_STA_CONN; - retryJoin = 0; - retryInterval = WLAN_MIN_RETRY_TIMER_MS; + retryJoin = 0; wfx_lwip_set_sta_link_up(); #if CHIP_CONFIG_ENABLE_ICD_SERVER if (!(wfx_get_wifi_state() & SL_WFX_AP_INTERFACE_UP)) @@ -750,6 +737,7 @@ static void wfx_wifi_hw_start(void) /* Initialize the LwIP stack */ SILABS_LOG("WF200:Start LWIP"); wfx_lwip_start(); + wfx_started_notify(); wifiContext.state = SL_WFX_STARTED; /* Really this is a bit mask */ SILABS_LOG("WF200:ready.."); } diff --git a/examples/platform/silabs/efr32/wf200/wf200.gni b/examples/platform/silabs/efr32/wf200/wf200.gni index 7e3b4ae6f18d75..307b6815374c38 100644 --- a/examples/platform/silabs/efr32/wf200/wf200.gni +++ b/examples/platform/silabs/efr32/wf200/wf200.gni @@ -4,7 +4,7 @@ import("${efr32_sdk_build_root}/efr32_sdk.gni") wf200_plat_incs = [ "${examples_plat_dir}/wf200" ] wf200_plat_src = [ - "${silabs_plat_efr32_wifi_dir}/wfx_notify.cpp", + "${silabs_common_plat_dir}/wifi/wfx_notify.cpp", "${examples_plat_dir}/wf200/sl_wfx_task.c", "${examples_plat_dir}/wf200/wf200_init.c", "${examples_plat_dir}/wf200/efr_spi.c", diff --git a/examples/platform/silabs/wfx_rsi.h b/examples/platform/silabs/wfx_rsi.h index c559e1e7610880..502dd1a96e772d 100644 --- a/examples/platform/silabs/wfx_rsi.h +++ b/examples/platform/silabs/wfx_rsi.h @@ -31,7 +31,6 @@ #define WFX_RSI_WLAN_TASK_SZ (1024 + 512 + 256) /* Stack for the WLAN task */ #define WFX_RSI_TASK_SZ (1024 + 1024) /* Stack for the WFX/RSI task */ #define WFX_RSI_BUF_SZ (1024 * 10) /* May need tweak */ -#define WFX_RSI_CONFIG_MAX_JOIN (5) /* Max join retries */ // TODO: Default values are usually in minutes, but this is in ms. Confirm if this is correct #define WFX_RSI_DHCP_POLL_INTERVAL (250) /* Poll interval in ms for DHCP */ #define WFX_RSI_NUM_TIMERS (2) /* Number of RSI timers to alloc */ diff --git a/src/platform/silabs/SiWx917/wifi/wfx_notify.cpp b/examples/platform/silabs/wifi/wfx_notify.cpp similarity index 64% rename from src/platform/silabs/SiWx917/wifi/wfx_notify.cpp rename to examples/platform/silabs/wifi/wfx_notify.cpp index ded8e8389d256d..a37b102c334804 100644 --- a/src/platform/silabs/SiWx917/wifi/wfx_notify.cpp +++ b/examples/platform/silabs/wifi/wfx_notify.cpp @@ -15,6 +15,9 @@ * limitations under the License. */ +#include "AppConfig.h" +#include "BaseApplication.h" +#include #include #include #include @@ -29,32 +32,33 @@ #include "wfx_rsi.h" #endif -#if SL_ICD_ENABLED -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif -#endif // SL_ICD_ENABLED - #include -// #include -#include -#include using namespace ::chip; using namespace ::chip::DeviceLayer; -#include - -extern uint32_t retryInterval; +namespace { +constexpr uint8_t kWlanMinRetryIntervalsInSec = 1; +constexpr uint8_t kWlanMaxRetryIntervalsInSec = 60; +constexpr uint8_t kWlanRetryIntervalInSec = 5; +uint8_t retryInterval = kWlanMinRetryIntervalsInSec; +osTimerId_t sRetryTimer; +} // namespace /* * Notifications to the upper-layer * All done in the context of the RSI/WiFi task (rsi_if.c) */ +static void RetryConnectionTimerHandler(void * arg) +{ +#if CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI91X_MCU_INTERFACE + wfx_rsi_power_save(RSI_ACTIVE, HIGH_PERFORMANCE); +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI91X_MCU_INTERFACE + if (wfx_connect_to_ap() != SL_STATUS_OK) + { + ChipLogError(DeviceLayer, "wfx_connect_to_ap() failed."); + } +} /*********************************************************************************** * @fn wfx_started_notify() * @brief @@ -67,6 +71,10 @@ void wfx_started_notify() sl_wfx_startup_ind_t evt; sl_wfx_mac_address_t mac; + // Creating a timer which will be used to retry connection with AP + sRetryTimer = osTimerNew(RetryConnectionTimerHandler, osTimerOnce, NULL, NULL); + VerifyOrReturn(sRetryTimer != NULL); + memset(&evt, 0, sizeof(evt)); evt.header.id = SL_WFX_STARTUP_IND_ID; evt.header.length = sizeof evt; @@ -90,13 +98,7 @@ void wfx_connected_notify(int32_t status, sl_wfx_mac_address_t * ap) { sl_wfx_connect_ind_t evt; - if (status != SUCCESS_STATUS) - { - ChipLogProgress(DeviceLayer, "%s: error: failed status: %ld.", __func__, status); - return; - } - - ChipLogProgress(DeviceLayer, "%s: connected.", __func__); + VerifyOrReturn(status != SUCCESS_STATUS); memset(&evt, 0, sizeof(evt)); evt.header.id = SL_WFX_CONNECT_IND_ID; @@ -143,16 +145,6 @@ void wfx_ipv6_notify(int got_ip) eventData.header.id = got_ip ? IP_EVENT_GOT_IP6 : IP_EVENT_STA_LOST_IP; eventData.header.length = sizeof(eventData.header); PlatformMgrImpl().HandleWFXSystemEvent(IP_EVENT, &eventData); - - /* So the other threads can run and have the connectivity OK */ - if (got_ip) - { - /* Should remember this */ - vTaskDelay(1); - chip::DeviceLayer::PlatformMgr().LockChipStack(); - chip::app::DnssdServer::Instance().StartServer(/*Dnssd::CommissioningMode::kEnabledBasic*/); - chip::DeviceLayer::PlatformMgr().UnlockChipStack(); - } } /************************************************************************************** @@ -170,67 +162,67 @@ void wfx_ip_changed_notify(int got_ip) eventData.header.id = got_ip ? IP_EVENT_STA_GOT_IP : IP_EVENT_STA_LOST_IP; eventData.header.length = sizeof(eventData.header); PlatformMgrImpl().HandleWFXSystemEvent(IP_EVENT, &eventData); - - /* So the other threads can run and have the connectivity OK */ - if (got_ip) - { - /* Should remember this */ - vTaskDelay(1); - chip::DeviceLayer::PlatformMgr().LockChipStack(); - chip::app::DnssdServer::Instance().StartServer(/*Dnssd::CommissioningMode::kEnabledBasic*/); - chip::DeviceLayer::PlatformMgr().UnlockChipStack(); - } } /************************************************************************************** - * @fn void wfx_retry_interval_handler(bool is_wifi_disconnection_event, uint16_t retryJoin) + * @fn void wfx_retry_connection(uint16_t retryAttempt) * @brief - * Based on condition will delay for a certain period of time. - * @param[in] is_wifi_disconnection_event, retryJoin + * During commissioning, we retry to join the network MAX_JOIN_RETRIES_COUNT times. + * If DUT is disconnected from the AP or device is power cycled, then retry connection + * with AP continously after a certain time interval. + * @param[in] retryAttempt * @return None ********************************************************************************************/ -void wfx_retry_interval_handler(bool is_wifi_disconnection_event, uint16_t retryJoin) +void wfx_retry_connection(uint16_t retryAttempt) { - if (!is_wifi_disconnection_event) + // During commissioning, we retry to join the network MAX_JOIN_RETRIES_COUNT + if (BaseApplication::sAppDelegate.isCommissioningInProgress()) { - /* After the reboot or a commissioning time device failed to connect with AP. - * Device will retry to connect with AP upto WFX_RSI_CONFIG_MAX_JOIN retries. - */ - if (retryJoin < MAX_JOIN_RETRIES_COUNT) + if (retryAttempt < MAX_JOIN_RETRIES_COUNT) { - ChipLogProgress(DeviceLayer, "wfx_retry_interval_handler : Next attempt after %d Seconds", - CONVERT_MS_TO_SEC(WLAN_RETRY_TIMER_MS)); -#if SL_ICD_ENABLED - // TODO: cleanup the retry logic MATTER-1921 - if (!chip::Server::GetInstance().GetCommissioningWindowManager().IsCommissioningWindowOpen()) + ChipLogProgress(DeviceLayer, "wfx_retry_connection : Next attempt after %d Seconds", kWlanRetryIntervalInSec); + if (osTimerStart(sRetryTimer, pdMS_TO_TICKS(CONVERT_SEC_TO_MS(kWlanRetryIntervalInSec))) != osOK) { - wfx_rsi_power_save(RSI_SLEEP_MODE_8, STANDBY_POWER_SAVE_WITH_RAM_RETENTION); + ChipLogProgress(DeviceLayer, "Failed to start retry timer"); + // Sending the join command if retry timer failed to start + if (wfx_connect_to_ap() != SL_STATUS_OK) + { + ChipLogError(DeviceLayer, "wfx_connect_to_ap() failed."); + } + return; } -#endif // SL_ICD_ENABLED - vTaskDelay(pdMS_TO_TICKS(WLAN_RETRY_TIMER_MS)); } else { - ChipLogProgress(DeviceLayer, "Connect failed after max %d tries", retryJoin); + ChipLogProgress(DeviceLayer, "Connect failed after max %d tries", retryAttempt); } } else { - /* After disconnection + /* After disconnection or power cycle the DUT * At the telescopic time interval device try to reconnect with AP, upto WLAN_MAX_RETRY_TIMER_MS intervals * are telescopic. If interval exceed WLAN_MAX_RETRY_TIMER_MS then it will try to reconnect at * WLAN_MAX_RETRY_TIMER_MS intervals. */ - if (retryInterval > WLAN_MAX_RETRY_TIMER_MS) + if (retryInterval > kWlanMaxRetryIntervalsInSec) + { + retryInterval = kWlanMaxRetryIntervalsInSec; + } + if (osTimerStart(sRetryTimer, pdMS_TO_TICKS(CONVERT_SEC_TO_MS(retryInterval))) != osOK) { - retryInterval = WLAN_MAX_RETRY_TIMER_MS; + ChipLogProgress(DeviceLayer, "Failed to start retry timer"); + // Sending the join command if retry timer failed to start + if (wfx_connect_to_ap() != SL_STATUS_OK) + { + ChipLogError(DeviceLayer, "wfx_connect_to_ap() failed."); + } + return; } - ChipLogProgress(DeviceLayer, "wfx_retry_interval_handler : Next attempt after %ld Seconds", - CONVERT_MS_TO_SEC(retryInterval)); -#if SL_ICD_ENABLED +#if CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI91X_MCU_INTERFACE wfx_rsi_power_save(RSI_SLEEP_MODE_8, STANDBY_POWER_SAVE_WITH_RAM_RETENTION); -#endif // SL_ICD_ENABLED - vTaskDelay(pdMS_TO_TICKS(retryInterval)); +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI91X_MCU_INTERFACE + ChipLogProgress(DeviceLayer, "wfx_retry_connection : Next attempt after %d Seconds", retryInterval); retryInterval += retryInterval; + return; } } diff --git a/src/platform/silabs/SiWx917/wifi/wfx_host_events.h b/src/platform/silabs/SiWx917/wifi/wfx_host_events.h index beee667f58f261..319a1c508e658d 100644 --- a/src/platform/silabs/SiWx917/wifi/wfx_host_events.h +++ b/src/platform/silabs/SiWx917/wifi/wfx_host_events.h @@ -60,11 +60,7 @@ #define BLE_DRIVER_TASK_PRIORITY (2) #define MAX_JOIN_RETRIES_COUNT (5) -// WLAN retry time intervals in milli seconds -#define WLAN_MAX_RETRY_TIMER_MS 30000 -#define WLAN_MIN_RETRY_TIMER_MS 1000 -#define WLAN_RETRY_TIMER_MS 5000 -#define CONVERT_MS_TO_SEC(TimeInMS) (TimeInMS / 1000) +#define CONVERT_SEC_TO_MS(TimeInS) (TimeInS * 1000) // WLAN related Macros #define ETH_FRAME (0) @@ -255,7 +251,7 @@ void sl_button_on_change(uint8_t btn, uint8_t btnAction); #endif /* SL_ICD_ENABLED */ void wfx_ipv6_notify(int got_ip); -void wfx_retry_interval_handler(bool is_wifi_disconnection_event, uint16_t retryJoin); +void wfx_retry_connection(uint16_t retryAttempt); #ifdef __cplusplus } diff --git a/src/platform/silabs/efr32/wifi/wfx_host_events.h b/src/platform/silabs/efr32/wifi/wfx_host_events.h index 24e5b6ea707a7a..51f96d7b9dbc73 100644 --- a/src/platform/silabs/efr32/wifi/wfx_host_events.h +++ b/src/platform/silabs/efr32/wifi/wfx_host_events.h @@ -139,12 +139,10 @@ typedef struct __attribute__((__packed__)) sl_wfx_mib_req_s #define WLAN_TASK_PRIORITY 1 #define WLAN_DRIVER_TASK_PRIORITY 1 #define BLE_DRIVER_TASK_PRIORITY 1 -#define MAX_JOIN_RETRIES_COUNT 5 #else /* WF200 */ #define WLAN_TASK_STACK_SIZE 1024 #define WLAN_TASK_PRIORITY 1 -#define MAX_JOIN_RETRIES_COUNT 5 #endif // RS911X_WIFI // MAX SSID LENGTH excluding NULL character @@ -152,11 +150,10 @@ typedef struct __attribute__((__packed__)) sl_wfx_mib_req_s // MAX PASSKEY LENGTH including NULL character #define WFX_MAX_PASSKEY_LENGTH (64) -// WLAN retry time intervals in milli seconds -#define WLAN_MAX_RETRY_TIMER_MS 30000 -#define WLAN_MIN_RETRY_TIMER_MS 1000 -#define WLAN_RETRY_TIMER_MS 5000 -#define CONVERT_MS_TO_SEC(TimeInMS) (TimeInMS / 1000) +#define CONVERT_SEC_TO_MS(TimeInS) (TimeInS * 1000) + +// WLAN MAX retry +#define MAX_JOIN_RETRIES_COUNT 5 // WLAN related Macros #define ETH_FRAME 0 @@ -388,7 +385,7 @@ void sl_wfx_host_gpio_init(void); sl_status_t sl_wfx_host_process_event(sl_wfx_generic_message_t * event_payload); #endif -void wfx_retry_interval_handler(bool is_wifi_disconnection_event, uint16_t retryJoin); +void wfx_retry_connection(uint16_t retryAttempt); #ifdef __cplusplus } diff --git a/src/platform/silabs/efr32/wifi/wfx_notify.cpp b/src/platform/silabs/efr32/wifi/wfx_notify.cpp deleted file mode 100644 index fd183100e7b913..00000000000000 --- a/src/platform/silabs/efr32/wifi/wfx_notify.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* - * - * 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 "em_bus.h" -#include "em_cmu.h" -#include "em_gpio.h" -#include "em_ldma.h" -#include "em_usart.h" -#include "gpiointerrupt.h" - -#include "FreeRTOS.h" -#include "event_groups.h" -#include "task.h" - -#include "wfx_host_events.h" - -#ifdef RS911X_WIFI -#include "wfx_rsi.h" -#endif - -#include -#include -#include -#include - -using namespace ::chip; -using namespace ::chip::DeviceLayer; - -extern uint32_t retryInterval; - -/* - * Notifications to the upper-layer - * All done in the context of the RSI/WiFi task (rsi_if.c) - */ - -/*********************************************************************************** - * @fn wfx_started_notify() - * @brief - * Wifi device started notification - * @param[in]: None - * @return None - *************************************************************************************/ -void wfx_started_notify() -{ - sl_wfx_startup_ind_t evt; - sl_wfx_mac_address_t mac; - - memset(&evt, 0, sizeof(evt)); - evt.header.id = SL_WFX_STARTUP_IND_ID; - evt.header.length = sizeof evt; - evt.body.status = 0; - wfx_get_wifi_mac_addr(SL_WFX_STA_INTERFACE, &mac); - memcpy(&evt.body.mac_addr[0], &mac.octet[0], MAC_ADDRESS_FIRST_OCTET); - - PlatformMgrImpl().HandleWFXSystemEvent(WIFI_EVENT, (sl_wfx_generic_message_t *) &evt); -} - -/*********************************************************************************** - * @fn void wfx_connected_notify(int32_t status, sl_wfx_mac_address_t *ap) - * @brief - * For now we are not notifying anything other than AP Mac - - * Other stuff such as DTIM etc. may be required for later - * @param[in] status: - * @param[in] ap: access point - * @return None - *************************************************************************************/ -void wfx_connected_notify(int32_t status, sl_wfx_mac_address_t * ap) -{ - sl_wfx_connect_ind_t evt; - - if (status != SUCCESS_STATUS) - { - ChipLogProgress(DeviceLayer, "%s: error: failed status: %ld.", __func__, status); - return; - } - - ChipLogProgress(DeviceLayer, "%s: connected.", __func__); - - memset(&evt, 0, sizeof(evt)); - evt.header.id = SL_WFX_CONNECT_IND_ID; - evt.header.length = sizeof evt; - -#ifdef RS911X_WIFI - evt.body.channel = wfx_rsi.ap_chan; -#endif - memcpy(&evt.body.mac[0], &ap->octet[0], MAC_ADDRESS_FIRST_OCTET); - - PlatformMgrImpl().HandleWFXSystemEvent(WIFI_EVENT, (sl_wfx_generic_message_t *) &evt); -} - -/************************************************************************************** - * @fn void wfx_disconnected_notify(int32_t status) - * @brief - * notification of disconnection - * @param[in] status: - * @return None - ********************************************************************************************/ -void wfx_disconnected_notify(int32_t status) -{ - sl_wfx_disconnect_ind_t evt; - - memset(&evt, 0, sizeof(evt)); - evt.header.id = SL_WFX_DISCONNECT_IND_ID; - evt.header.length = sizeof evt; - evt.body.reason = status; - PlatformMgrImpl().HandleWFXSystemEvent(WIFI_EVENT, (sl_wfx_generic_message_t *) &evt); -} - -/************************************************************************************** - * @fn void wfx_ipv6_notify(int got_ip) - * @brief - * notification of ipv6 - * @param[in] got_ip: - * @return None - ********************************************************************************************/ -void wfx_ipv6_notify(int got_ip) -{ - sl_wfx_generic_message_t eventData; - - memset(&eventData, 0, sizeof(eventData)); - eventData.header.id = got_ip ? IP_EVENT_GOT_IP6 : IP_EVENT_STA_LOST_IP; - eventData.header.length = sizeof(eventData.header); - PlatformMgrImpl().HandleWFXSystemEvent(IP_EVENT, &eventData); - - /* So the other threads can run and have the connectivity OK */ - if (got_ip) - { - /* Should remember this */ - vTaskDelay(1); - chip::DeviceLayer::PlatformMgr().LockChipStack(); - chip::app::DnssdServer::Instance().StartServer(/*Dnssd::CommissioningMode::kEnabledBasic*/); - chip::DeviceLayer::PlatformMgr().UnlockChipStack(); - } -} - -/************************************************************************************** - * @fn void wfx_ip_changed_notify(int got_ip) - * @brief - * notification of ip change - * @param[in] got_ip: - * @return None - ********************************************************************************************/ -void wfx_ip_changed_notify(int got_ip) -{ - sl_wfx_generic_message_t eventData; - - memset(&eventData, 0, sizeof(eventData)); - eventData.header.id = got_ip ? IP_EVENT_STA_GOT_IP : IP_EVENT_STA_LOST_IP; - eventData.header.length = sizeof(eventData.header); - PlatformMgrImpl().HandleWFXSystemEvent(IP_EVENT, &eventData); - - /* So the other threads can run and have the connectivity OK */ - if (got_ip) - { - /* Should remember this */ - vTaskDelay(1); - chip::DeviceLayer::PlatformMgr().LockChipStack(); - chip::app::DnssdServer::Instance().StartServer(/*Dnssd::CommissioningMode::kEnabledBasic*/); - chip::DeviceLayer::PlatformMgr().UnlockChipStack(); - } -} - -/************************************************************************************** - * @fn void wfx_retry_interval_handler(bool is_wifi_disconnection_event, uint16_t retryJoin) - * @brief - * Based on condition will delay for a certain period of time. - * @param[in] is_wifi_disconnection_event, retryJoin - * @return None - ********************************************************************************************/ -void wfx_retry_interval_handler(bool is_wifi_disconnection_event, uint16_t retryJoin) -{ - if (!is_wifi_disconnection_event) - { - /* After the reboot or a commissioning time device failed to connect with AP. - * Device will retry to connect with AP upto WFX_RSI_CONFIG_MAX_JOIN retries. - */ - if (retryJoin < MAX_JOIN_RETRIES_COUNT) - { - ChipLogProgress(DeviceLayer, "%s: Next attempt after %d Seconds", __func__, CONVERT_MS_TO_SEC(WLAN_RETRY_TIMER_MS)); - vTaskDelay(pdMS_TO_TICKS(WLAN_RETRY_TIMER_MS)); - } - else - { - ChipLogProgress(DeviceLayer, "Connect failed after max %d tries", retryJoin); - } - } - else - { - /* After disconnection - * At the telescopic time interval device try to reconnect with AP, upto WLAN_MAX_RETRY_TIMER_MS intervals - * are telescopic. If interval exceed WLAN_MAX_RETRY_TIMER_MS then it will try to reconnect at - * WLAN_MAX_RETRY_TIMER_MS intervals. - */ - if (retryInterval > WLAN_MAX_RETRY_TIMER_MS) - { - retryInterval = WLAN_MAX_RETRY_TIMER_MS; - } - ChipLogProgress(DeviceLayer, "%s: Next attempt after %ld Seconds", __func__, CONVERT_MS_TO_SEC(retryInterval)); - vTaskDelay(pdMS_TO_TICKS(retryInterval)); - retryInterval += retryInterval; - } -} diff --git a/third_party/silabs/efr32_sdk.gni b/third_party/silabs/efr32_sdk.gni index 617dfd10facb95..ce7b09e3e012ac 100644 --- a/third_party/silabs/efr32_sdk.gni +++ b/third_party/silabs/efr32_sdk.gni @@ -89,6 +89,8 @@ declare_args() { examples_plat_dir = "${chip_root}/examples/platform/silabs/efr32" silabs_plat_efr32_wifi_dir = "${chip_root}/src/platform/silabs/efr32/wifi" +silabs_common_plat_dir = "${chip_root}/examples/platform/silabs" + is_series_2 = silabs_family == "mgm24" || silabs_family == "efr32mg24" || silabs_family == "efr32mg26" From be0009e75879cb61e57ad09e7d2e67677d52f679 Mon Sep 17 00:00:00 2001 From: waqar rashid <6962179+wickywaka@users.noreply.github.com> Date: Sat, 24 Aug 2024 01:33:21 +0200 Subject: [PATCH 09/12] Fixed missing trailing zero in example documentation (#35149) Co-authored-by: Boris Zbarsky --- docs/getting_started/first_example.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting_started/first_example.md b/docs/getting_started/first_example.md index 4aba0d4e480204..fefa1916c2ef83 100644 --- a/docs/getting_started/first_example.md +++ b/docs/getting_started/first_example.md @@ -92,7 +92,7 @@ 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` +`./out/linux-x64-chip-tool/chip-tool pairing code 0x12344321 MT:-24J0AFN00KA0648G00` 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. From a93f4b6a98535cd03905a06032ad362f20c48faf Mon Sep 17 00:00:00 2001 From: chirag-silabs <100861685+chirag-silabs@users.noreply.github.com> Date: Sat, 24 Aug 2024 06:45:25 +0530 Subject: [PATCH 10/12] [Silabs] Fixing the build for the custom boards on NCP (#35055) * fixing the build for the custom boards on ncp * Restyled by clang-format * applying the restyler * addressing the review comments and adding sl_custom_board.h file * adding the warning message for 917 NCP * Restyled by whitespace * Restyled by clang-format * Apply suggestions from code review Co-authored-by: Yufeng Wang * Restyled by clang-format --------- Co-authored-by: Restyled.io Co-authored-by: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com> Co-authored-by: Yufeng Wang --- .../silabs/efr32/rs911x/hal/efx32_ncp_host.c | 3 + .../silabs/efr32/rs911x/hal/efx_spi.c | 19 ++--- .../rs911x/hal/rsi_board_configuration.h | 3 +- .../efr32/rs911x/hal/sl_board_configuration.h | 3 +- .../efr32/rs911x/hal/sl_si91x_ncp_utility.c | 4 +- .../platform/silabs/efr32/sl_custom_board.h | 71 +++++++++++++++++++ .../platform/silabs/efr32/wf200/efr_spi.c | 5 -- .../silabs/efr32/wf200/sl_wfx_board.h | 3 +- 8 files changed, 88 insertions(+), 23 deletions(-) create mode 100644 examples/platform/silabs/efr32/sl_custom_board.h diff --git a/examples/platform/silabs/efr32/rs911x/hal/efx32_ncp_host.c b/examples/platform/silabs/efr32/rs911x/hal/efx32_ncp_host.c index fded8defa30dce..0a2fad7d0deb6c 100644 --- a/examples/platform/silabs/efr32/rs911x/hal/efx32_ncp_host.c +++ b/examples/platform/silabs/efr32/rs911x/hal/efx32_ncp_host.c @@ -36,7 +36,10 @@ #include "sl_power_manager.h" #endif +#ifdef SL_BOARD_NAME #include "sl_board_control.h" +#endif // SL_BOARD_NAME + #include "sl_si91x_ncp_utility.h" #include "spi_multiplex.h" diff --git a/examples/platform/silabs/efr32/rs911x/hal/efx_spi.c b/examples/platform/silabs/efr32/rs911x/hal/efx_spi.c index 6c03b6f1f4835b..1f10640e04bad8 100644 --- a/examples/platform/silabs/efr32/rs911x/hal/efx_spi.c +++ b/examples/platform/silabs/efr32/rs911x/hal/efx_spi.c @@ -35,7 +35,6 @@ #include "em_gpio.h" #include "em_ldma.h" #include "gpiointerrupt.h" -#include "sl_board_control.h" #include "sl_device_init_clocks.h" #include "sl_device_init_hfxo.h" #include "sl_spidrv_instances.h" @@ -47,6 +46,10 @@ #include "wfx_host_events.h" #include "wfx_rsi.h" +#ifdef SL_BOARD_NAME +#include "sl_board_control.h" +#endif // SL_BOARD_NAME + #if defined(SL_CATALOG_POWER_MANAGER_PRESENT) #include "sl_power_manager.h" #endif @@ -65,18 +68,12 @@ #include "sl_mx25_flash_shutdown_usart_config.h" #endif // SL_MX25CTRL_MUX -#if defined(EFR32MG24) #include "em_eusart.h" #include "sl_spidrv_eusart_exp_config.h" #include "spi_multiplex.h" -#else -#error "Unknown platform" -#endif -#if defined(EFR32MG24) #define SL_SPIDRV_HANDLE sl_spidrv_eusart_exp_handle #define SL_SPIDRV_EXP_BITRATE_MULTIPLEXED SL_SPIDRV_EUSART_EXP_BITRATE -#endif #define CONCAT(A, B) (A##B) #define SPI_CLOCK(N) CONCAT(cmuClock_USART, N) @@ -113,10 +110,8 @@ void sl_wfx_host_gpio_init(void) // Enable GPIO clock. CMU_ClockEnable(cmuClock_GPIO, true); -#if defined(EFR32MG24) // Set CS pin to high/inactive GPIO_PinModeSet(SL_SPIDRV_EUSART_EXP_CS_PORT, SL_SPIDRV_EUSART_EXP_CS_PIN, gpioModePushPull, PINOUT_SET); -#endif // EFR32MG24 GPIO_PinModeSet(WFX_RESET_PIN.port, WFX_RESET_PIN.pin, gpioModePushPull, PINOUT_SET); GPIO_PinModeSet(WFX_SLEEP_CONFIRM_PIN.port, WFX_SLEEP_CONFIRM_PIN.pin, gpioModePushPull, PINOUT_CLEAR); @@ -195,9 +190,7 @@ sl_status_t sl_wfx_host_spi_cs_assert(void) if (!spi_enabled) // Reduce sl_spidrv_init_instances { sl_spidrv_init_instances(); -#if defined(EFR32MG24) GPIO_PinOutClear(SL_SPIDRV_EUSART_EXP_CS_PORT, SL_SPIDRV_EUSART_EXP_CS_PIN); -#endif // EFR32MG24 spi_enabled = true; } return SL_STATUS_OK; @@ -211,11 +204,9 @@ sl_status_t sl_wfx_host_spi_cs_deassert(void) status = SPIDRV_DeInit(SL_SPIDRV_HANDLE); if (SL_STATUS_OK == status) { -#if defined(EFR32MG24) GPIO_PinOutSet(SL_SPIDRV_EUSART_EXP_CS_PORT, SL_SPIDRV_EUSART_EXP_CS_PIN); GPIO->EUSARTROUTE[SL_SPIDRV_EUSART_EXP_PERIPHERAL_NO].ROUTEEN = PINOUT_CLEAR; -#endif // EFR32MG24 - spi_enabled = false; + spi_enabled = false; } } #if SL_SPICTRL_MUX diff --git a/examples/platform/silabs/efr32/rs911x/hal/rsi_board_configuration.h b/examples/platform/silabs/efr32/rs911x/hal/rsi_board_configuration.h index 4d8d9da1dd36a9..ed04acf5b3fa40 100644 --- a/examples/platform/silabs/efr32/rs911x/hal/rsi_board_configuration.h +++ b/examples/platform/silabs/efr32/rs911x/hal/rsi_board_configuration.h @@ -35,7 +35,8 @@ typedef struct #elif defined(EFR32MG24_BRD4187C) || defined(BRD4187C) #include "brd4187c.h" #else -#error "Need SPI Pins" +#include "sl_custom_board.h" +#warning "Modify sl_custom_board.h configuration file to match your hardware SPIDRV USART peripheral" #endif /* EFR32MG24_BRD4186C */ #endif /* _RSI_BOARD_CONFIGURATION_H_ */ diff --git a/examples/platform/silabs/efr32/rs911x/hal/sl_board_configuration.h b/examples/platform/silabs/efr32/rs911x/hal/sl_board_configuration.h index c1dceab06d8228..317bdc5bd22aa4 100644 --- a/examples/platform/silabs/efr32/rs911x/hal/sl_board_configuration.h +++ b/examples/platform/silabs/efr32/rs911x/hal/sl_board_configuration.h @@ -36,7 +36,8 @@ typedef struct #elif defined(EFR32MG24_BRD4187C) || defined(BRD4187C) #include "brd4187c.h" #else -#error "Need SPI Pins" +#include "sl_custom_board.h" +#warning "Modify sl_custom_board.h configuration file to match your hardware SPIDRV USART peripheral" #endif #if EXP_BOARD #define RESET_PIN PIN(A, 6) diff --git a/examples/platform/silabs/efr32/rs911x/hal/sl_si91x_ncp_utility.c b/examples/platform/silabs/efr32/rs911x/hal/sl_si91x_ncp_utility.c index 4b876883c12b0f..b16bbdc47579e0 100644 --- a/examples/platform/silabs/efr32/rs911x/hal/sl_si91x_ncp_utility.c +++ b/examples/platform/silabs/efr32/rs911x/hal/sl_si91x_ncp_utility.c @@ -36,7 +36,9 @@ #include "spidrv.h" #include "task.h" +#ifdef SL_BOARD_NAME #include "sl_board_control.h" +#endif // SL_BOARD_NAME #include "sl_device_init_clocks.h" #include "sl_device_init_hfxo.h" @@ -242,4 +244,4 @@ sl_status_t sl_wfx_host_spiflash_cs_deassert(void) GPIO_PinOutSet(SL_MX25_FLASH_SHUTDOWN_CS_PORT, SL_MX25_FLASH_SHUTDOWN_CS_PIN); return SL_STATUS_OK; } -#endif // SL_MX25CTRL_MUX \ No newline at end of file +#endif // SL_MX25CTRL_MUX diff --git a/examples/platform/silabs/efr32/sl_custom_board.h b/examples/platform/silabs/efr32/sl_custom_board.h new file mode 100644 index 00000000000000..420b715de9ba76 --- /dev/null +++ b/examples/platform/silabs/efr32/sl_custom_board.h @@ -0,0 +1,71 @@ +/* + * This file is used to set the pins for the SPI for Custom boards + * The SPI pins are defined in the file + * + * !!!! MODIFY THIS FILE TO THE CORRECT PINS !!!! + */ + +#ifndef _CUSTOM_BOARD_H_ +#define _CUSTOM_BOARD_H_ + +#define WAKE_INDICATOR_PIN PIN(D, 2) +#ifdef RS911X_WIFI +// SPI ports and pins +#define EUS1MOSI_PORT gpioPortC +#define EUS1MOSI_PIN 1 +#define EUS1MISO_PORT gpioPortC +#define EUS1MISO_PIN 2 +#define EUS1SCLK_PORT gpioPortC +#define EUS1SCLK_PIN 3 +#define EUS1CS_PORT gpioPortC +#define EUS1CS_PIN 0 + +#define MY_USART EUSART1 +#define MY_USART_CLOCK cmuClock_EUSART1 +#define MY_USART_TX_SIGNAL dmadrvPeripheralSignal_EUSART1_TXBL +#define MY_USART_RX_SIGNAL dmadrvPeripheralSignal_EUSART1_RXDATAV + +#define WFX_RESET_PIN PIN(A, 6) +#define WFX_INTERRUPT_PIN PIN(A, 7) +#ifdef EXP_BOARD +#define WFX_SLEEP_CONFIRM_PIN PIN(D, 2) /* Expansion header pin7 */ +#else +#define WFX_SLEEP_CONFIRM_PIN PIN(A, 5) +#endif /* EXP_BOARD */ +#define SL_WFX_HOST_PINOUT_SPI_IRQ 5 + +#else /* WF200 */ + +#define PIN_OUT_SET 1 +#define PIN_OUT_CLEAR 0 + +#define MY_USART USART0 +#define MY_USART_CLOCK cmuClock_USART0 +#define MY_USART_TX_SIGNAL dmadrvPeripheralSignal_USART0_TXBL +#define MY_USART_RX_SIGNAL dmadrvPeripheralSignal_USART0_RXDATAV + +#define SL_WFX_HOST_PINOUT_RESET_PORT gpioPortA +#define SL_WFX_HOST_PINOUT_RESET_PIN 5 +#define SL_WFX_HOST_PINOUT_SPI_WIRQ_PORT gpioPortA /* SPI IRQ port */ +#define SL_WFX_HOST_PINOUT_SPI_WIRQ_PIN 8 /* SPI IRQ pin */ +#define SL_WFX_HOST_PINOUT_WUP_PORT gpioPortB +#define SL_WFX_HOST_PINOUT_WUP_PIN 5 + +#define SL_WFX_HOST_PINOUT_SPI_TX_PORT gpioPortC +#define SL_WFX_HOST_PINOUT_SPI_TX_PIN 1 +#define SL_WFX_HOST_PINOUT_SPI_TX_LOC 1 + +#define SL_WFX_HOST_PINOUT_SPI_RX_PORT gpioPortC +#define SL_WFX_HOST_PINOUT_SPI_RX_PIN 2 +#define SL_WFX_HOST_PINOUT_SPI_RX_LOC 1 + +#define SL_WFX_HOST_PINOUT_SPI_CLK_PORT gpioPortC +#define SL_WFX_HOST_PINOUT_SPI_CLK_PIN 3 +#define SL_WFX_HOST_PINOUT_SPI_CLK_LOC 1 + +#define SL_WFX_HOST_PINOUT_SPI_CS_PORT gpioPortC +#define SL_WFX_HOST_PINOUT_SPI_CS_PIN 0 +#define SL_WFX_HOST_PINOUT_SPI_CS_LOC 1 + +#endif /* WF200/9116 */ +#endif /* _CUSTOM_BOARD_H_ */ diff --git a/examples/platform/silabs/efr32/wf200/efr_spi.c b/examples/platform/silabs/efr32/wf200/efr_spi.c index 122652e96da39d..3ef34c928e0363 100644 --- a/examples/platform/silabs/efr32/wf200/efr_spi.c +++ b/examples/platform/silabs/efr32/wf200/efr_spi.c @@ -98,11 +98,9 @@ sl_status_t sl_wfx_host_init_bus(void) * EUSARTROUTE register to do this. */ -#if defined(EFR32MG24) GPIO->USARTROUTE[0].ROUTEEN = GPIO_USART_ROUTEEN_RXPEN | // MISO GPIO_USART_ROUTEEN_TXPEN | // MOSI GPIO_USART_ROUTEEN_CLKPEN; -#endif spi_sem = xSemaphoreCreateBinaryStatic(&xEfrSpiSemaBuffer); xSemaphoreGive(spi_sem); @@ -338,11 +336,8 @@ void sl_wfx_host_gpio_init(void) { // Enable GPIO clock. CMU_ClockEnable(cmuClock_GPIO, true); - -#if defined(EFR32MG24) // configure WF200 CS pin. GPIO_PinModeSet(SL_SPIDRV_EXP_CS_PORT, SL_SPIDRV_EXP_CS_PIN, gpioModePushPull, 1); -#endif // Configure WF200 reset pin. GPIO_PinModeSet(SL_WFX_HOST_PINOUT_RESET_PORT, SL_WFX_HOST_PINOUT_RESET_PIN, gpioModePushPull, 0); // Configure WF200 WUP pin. diff --git a/examples/platform/silabs/efr32/wf200/sl_wfx_board.h b/examples/platform/silabs/efr32/wf200/sl_wfx_board.h index 3cbed666d68eab..35ce2f633d0c59 100644 --- a/examples/platform/silabs/efr32/wf200/sl_wfx_board.h +++ b/examples/platform/silabs/efr32/wf200/sl_wfx_board.h @@ -25,6 +25,7 @@ #elif defined(EFR32MG24_BRD4187C) || defined(BRD4187C) || defined(EFR32MG24_BRD4187A) || defined(BRD4187A) #include "brd4187c.h" #else -#error "Need SPI Pins" +#include "sl_custom_board.h" +#warning "Modify sl_custom_board.h configuration file to match your hardware SPIDRV USART peripheral" #endif #endif /* _SL_WFX_BOARD_H_ */ From 05b60aa86a6d71aa3f8d011652f8b06cbea8ba3f Mon Sep 17 00:00:00 2001 From: Anush Nadathur Date: Sat, 24 Aug 2024 13:54:25 -0700 Subject: [PATCH 11/12] [Darwin] Adding missing property on MTRDeviceController_XPC (#35188) - Added controllerID to match the OverXPC interface --- src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm b/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm index 67049b280d81f3..d72dc211b70b4f 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm @@ -78,6 +78,11 @@ - (NSXPCInterface *)_interfaceForClientProtocol return interface; } +- (id)controllerXPCID +{ + return [self.uniqueIdentifier UUIDString]; +} + - (id)initWithUniqueIdentifier:(NSUUID *)UUID xpConnectionBlock:(NSXPCConnection * (^)(void) )connectionBlock { if (self = [super initForSubclasses]) { From a296b6609ec064b201cca9880c577f0197217d43 Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Sun, 25 Aug 2024 19:16:00 -0700 Subject: [PATCH 12/12] [Fabric-Bridge] Add state machine to CommissionerControlDelegate to guard various unexpected commands (#35186) * Add state machine to CommissionerControlDelegate to guard various unexpected commands * Restyled by whitespace * Restyled by clang-format * Do not reset state when commissionNode fail to allow retry --------- Co-authored-by: Restyled.io --- .../linux/CommissionerControl.cpp | 67 ++++++++++++++++--- .../linux/include/CommissionerControl.h | 13 ++++ 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/examples/fabric-bridge-app/linux/CommissionerControl.cpp b/examples/fabric-bridge-app/linux/CommissionerControl.cpp index 3613aeec9305e4..0466ab8b711b69 100644 --- a/examples/fabric-bridge-app/linux/CommissionerControl.cpp +++ b/examples/fabric-bridge-app/linux/CommissionerControl.cpp @@ -46,8 +46,33 @@ namespace app { namespace Clusters { namespace CommissionerControl { +void CommissionerControlDelegate::ResetDelegateState() +{ + // Reset the step to the initial state + mNextStep = Step::kIdle; + + // Reset identifiers and product information + mRequestId = 0; + mClientNodeId = kUndefinedNodeId; + mVendorId = VendorId::Unspecified; + mProductId = 0; + + // Clear the label buffer and optional label + memset(mLabelBuffer, 0, sizeof(mLabelBuffer)); + mLabel.ClearValue(); + + // Reset PBKDF salt and PAKE passcode verifier buffers + mPBKDFSalt = ByteSpan(); + memset(mPBKDFSaltBuffer, 0, sizeof(mPBKDFSaltBuffer)); + + mPAKEPasscodeVerifier = ByteSpan(); + memset(mPAKEPasscodeVerifierBuffer, 0, sizeof(mPAKEPasscodeVerifierBuffer)); +} + CHIP_ERROR CommissionerControlDelegate::HandleCommissioningApprovalRequest(const CommissioningApprovalRequest & request) { + VerifyOrReturnError(mNextStep == Step::kIdle, CHIP_ERROR_INCORRECT_STATE); + CommissionerControl::Events::CommissioningRequestResult::Type result; result.requestId = request.requestId; result.clientNodeId = request.clientNodeId; @@ -86,19 +111,34 @@ CHIP_ERROR CommissionerControlDelegate::HandleCommissioningApprovalRequest(const mLabel.ClearValue(); } - return CommissionerControlServer::Instance().GenerateCommissioningRequestResultEvent(result); + CHIP_ERROR err = CommissionerControlServer::Instance().GenerateCommissioningRequestResultEvent(result); + + if (err == CHIP_NO_ERROR) + { + mNextStep = Step::kWaitCommissionNodeRequest; + } + else + { + ResetDelegateState(); + } + + return err; } CHIP_ERROR CommissionerControlDelegate::ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId) { CHIP_ERROR err = CHIP_NO_ERROR; + VerifyOrReturnError(mNextStep == Step::kWaitCommissionNodeRequest, CHIP_ERROR_INCORRECT_STATE); + // Verify if the CommissionNode command is sent from the same NodeId as the RequestCommissioningApproval. VerifyOrExit(mClientNodeId == clientNodeId, err = CHIP_ERROR_WRONG_NODE_ID); // Verify if the provided RequestId matches the value provided to the RequestCommissioningApproval. VerifyOrExit(mRequestId == requestId, err = CHIP_ERROR_INCORRECT_STATE); + mNextStep = Step::kStartCommissionNode; + exit: return err; } @@ -129,20 +169,29 @@ CHIP_ERROR CommissionerControlDelegate::GetCommissioningWindowParams(Commissioni CHIP_ERROR CommissionerControlDelegate::HandleCommissionNode(const CommissioningWindowParams & params, const Optional & ipAddress, const Optional & port) { + CHIP_ERROR err = CHIP_NO_ERROR; + ChipLogProgress(NotSpecified, "CommissionerControlDelegate::HandleCommissionNode"); + VerifyOrReturnError(mNextStep == Step::kStartCommissionNode, CHIP_ERROR_INCORRECT_STATE); + #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE - return CommissionNode(Controller::CommissioningWindowPasscodeParams() - .SetSetupPIN(kSetupPinCode) - .SetTimeout(params.commissioningTimeout) - .SetDiscriminator(params.discriminator) - .SetIteration(params.iterations) - .SetSalt(params.salt), - mVendorId, mProductId); + err = CommissionNode(Controller::CommissioningWindowPasscodeParams() + .SetSetupPIN(kSetupPinCode) + .SetTimeout(params.commissioningTimeout) + .SetDiscriminator(params.discriminator) + .SetIteration(params.iterations) + .SetSalt(params.salt), + mVendorId, mProductId); #else ChipLogProgress(NotSpecified, "Failed to reverse commission bridge: PW_RPC_FABRIC_BRIDGE_SERVICE not defined"); - return CHIP_ERROR_NOT_IMPLEMENTED; + err = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE + + // Reset the delegate's state to prepare for a new commissioning sequence. + ResetDelegateState(); + + return err; } } // namespace CommissionerControl diff --git a/examples/fabric-bridge-app/linux/include/CommissionerControl.h b/examples/fabric-bridge-app/linux/include/CommissionerControl.h index 633be761ba1004..486668ffa212f7 100644 --- a/examples/fabric-bridge-app/linux/include/CommissionerControl.h +++ b/examples/fabric-bridge-app/linux/include/CommissionerControl.h @@ -38,8 +38,21 @@ class CommissionerControlDelegate : public Delegate ~CommissionerControlDelegate() = default; private: + enum class Step : uint8_t + { + // Ready to start reverse commissioning. + kIdle, + // Wait for the commission node command. + kWaitCommissionNodeRequest, + // Need to commission node. + kStartCommissionNode, + }; + + void ResetDelegateState(); + static constexpr size_t kLabelBufferSize = 64; + Step mNextStep = Step::kIdle; uint64_t mRequestId = 0; NodeId mClientNodeId = kUndefinedNodeId; VendorId mVendorId = VendorId::Unspecified;