diff --git a/docs/testing/python.md b/docs/testing/python.md index 354490c896c445..3b5c8ffc74abf7 100644 --- a/docs/testing/python.md +++ b/docs/testing/python.md @@ -635,9 +635,19 @@ markers must be present. - `test-runner-run//script-args`: Specifies the arguments to be passed to the test script. + - Example: `--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto` +- `test-runner-run//script-start-delay`: Specifies the number + of seconds to wait before starting the test script. This parameter can be + used to allow the application to initialize itself properly before the test + script will try to commission it (e.g. in case if the application needs to + be commissioned to some other controller first). By default, the delay is 0 + seconds. + + - Example: `10` + This structured format ensures that all necessary configurations are clearly defined and easily understood, allowing for consistent and reliable test execution. diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index 6926da18d78a61..1f8bc5463d8a9d 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -3235,7 +3235,7 @@ cluster LaundryWasherControls = 83 { /** Attributes and commands for selecting a mode from a list of supported options. */ cluster RvcRunMode = 84 { - revision 2; + revision 3; enum ModeTag : enum16 { kIdle = 16384; @@ -3255,7 +3255,7 @@ cluster RvcRunMode = 84 { } bitmap Feature : bitmap32 { - kNoFeatures = 0x0; + kDirectModeChange = 0x10000; } struct ModeTagStruct { @@ -3294,7 +3294,7 @@ cluster RvcRunMode = 84 { /** Attributes and commands for selecting a mode from a list of supported options. */ cluster RvcCleanMode = 85 { - revision 2; + revision 3; enum ModeTag : enum16 { kDeepClean = 16384; @@ -3307,7 +3307,7 @@ cluster RvcCleanMode = 85 { } bitmap Feature : bitmap32 { - kNoFeatures = 0x0; + kDirectModeChange = 0x10000; } struct ModeTagStruct { @@ -8489,7 +8489,7 @@ endpoint 1 { callback attribute eventList; callback attribute attributeList; callback attribute featureMap; - ram attribute clusterRevision default = 2; + ram attribute clusterRevision default = 3; handle command ChangeToMode; handle command ChangeToModeResponse; @@ -8503,7 +8503,7 @@ endpoint 1 { callback attribute eventList; callback attribute attributeList; callback attribute featureMap; - ram attribute clusterRevision default = 2; + ram attribute clusterRevision default = 3; handle command ChangeToMode; handle command ChangeToModeResponse; diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap index 51ea8b7188a96b..00f525cf1710ae 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap @@ -9648,7 +9648,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "2", + "defaultValue": "3", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -9804,7 +9804,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "2", + "defaultValue": "3", "reportable": 1, "minInterval": 1, "maxInterval": 65534, diff --git a/examples/all-clusters-app/all-clusters-common/src/rvc-modes.cpp b/examples/all-clusters-app/all-clusters-common/src/rvc-modes.cpp index 627709a8c30921..5571834c375646 100644 --- a/examples/all-clusters-app/all-clusters-common/src/rvc-modes.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/rvc-modes.cpp @@ -16,6 +16,7 @@ * limitations under the License. */ #include +#include #include #include @@ -41,12 +42,15 @@ void RvcRunModeDelegate::HandleChangeToMode(uint8_t NewMode, ModeBase::Commands: { uint8_t currentMode = mInstance->GetCurrentMode(); - // Our business logic states that we can only switch into a running mode from the idle state. - if (NewMode != RvcRunMode::ModeIdle && currentMode != RvcRunMode::ModeIdle) + if (!gRvcRunModeInstance->HasFeature(static_cast(RvcRunMode::Feature::kDirectModeChange))) { - response.status = to_underlying(ModeBase::StatusCode::kInvalidInMode); - response.statusText.SetValue(chip::CharSpan::fromCharString("Change to a running mode is only allowed from idle")); - return; + // Our business logic states that we can only switch into a running mode from the idle state. + if (NewMode != RvcRunMode::ModeIdle && currentMode != RvcRunMode::ModeIdle) + { + response.status = to_underlying(ModeBase::StatusCode::kInvalidInMode); + response.statusText.SetValue(chip::CharSpan::fromCharString("Change to a running mode is only allowed from idle")); + return; + } } auto rvcOpStateInstance = RvcOperationalState::GetRvcOperationalStateInstance(); @@ -123,8 +127,8 @@ void emberAfRvcRunModeClusterInitCallback(chip::EndpointId endpointId) VerifyOrDie(endpointId == 1); // this cluster is only enabled for endpoint 1. VerifyOrDie(gRvcRunModeDelegate == nullptr && gRvcRunModeInstance == nullptr); gRvcRunModeDelegate = new RvcRunMode::RvcRunModeDelegate; - gRvcRunModeInstance = - new ModeBase::Instance(gRvcRunModeDelegate, 0x1, RvcRunMode::Id, chip::to_underlying(RvcRunMode::Feature::kNoFeatures)); + gRvcRunModeInstance = new ModeBase::Instance(gRvcRunModeDelegate, 0x1, RvcRunMode::Id, + chip::to_underlying(RvcRunMode::Feature::kDirectModeChange)); gRvcRunModeInstance->Init(); } @@ -139,14 +143,17 @@ CHIP_ERROR RvcCleanModeDelegate::Init() void RvcCleanModeDelegate::HandleChangeToMode(uint8_t NewMode, ModeBase::Commands::ChangeToModeResponse::Type & response) { - uint8_t rvcRunCurrentMode = gRvcRunModeInstance->GetCurrentMode(); - - if (rvcRunCurrentMode != RvcRunMode::ModeIdle) + if (!gRvcCleanModeInstance->HasFeature(static_cast(RvcCleanMode::Feature::kDirectModeChange))) { - response.status = to_underlying(ModeBase::StatusCode::kInvalidInMode); - response.statusText.SetValue( - chip::CharSpan::fromCharString("Cannot change the cleaning mode when the device is not in idle")); - return; + uint8_t rvcRunCurrentMode = gRvcRunModeInstance->GetCurrentMode(); + + if (rvcRunCurrentMode != RvcRunMode::ModeIdle) + { + response.status = to_underlying(ModeBase::StatusCode::kInvalidInMode); + response.statusText.SetValue( + chip::CharSpan::fromCharString("Cannot change the cleaning mode when the device is not in idle")); + return; + } } response.status = to_underlying(ModeBase::StatusCode::kSuccess); @@ -213,7 +220,7 @@ void emberAfRvcCleanModeClusterInitCallback(chip::EndpointId endpointId) VerifyOrDie(endpointId == 1); // this cluster is only enabled for endpoint 1. VerifyOrDie(gRvcCleanModeDelegate == nullptr && gRvcCleanModeInstance == nullptr); gRvcCleanModeDelegate = new RvcCleanMode::RvcCleanModeDelegate; - gRvcCleanModeInstance = - new ModeBase::Instance(gRvcCleanModeDelegate, 0x1, RvcCleanMode::Id, chip::to_underlying(RvcRunMode::Feature::kNoFeatures)); + gRvcCleanModeInstance = new ModeBase::Instance(gRvcCleanModeDelegate, 0x1, RvcCleanMode::Id, + chip::to_underlying(RvcCleanMode::Feature::kDirectModeChange)); gRvcCleanModeInstance->Init(); } diff --git a/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp index 53a08672fdbc07..0122eba4336340 100644 --- a/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp +++ b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp @@ -235,6 +235,7 @@ void ButtonEventsSimulator::Next() break; } case ButtonEventsSimulator::State::kEmitStartOfMultiPress: { + SetButtonPosition(mEndpointId, mPressedButtonId); EmitInitialPress(mEndpointId, mPressedButtonId); if (mFeatureMap & static_cast(Clusters::Switch::Feature::kActionSwitch)) { @@ -268,6 +269,7 @@ void ButtonEventsSimulator::Next() { EmitShortRelease(mEndpointId, mPressedButtonId); } + SetButtonPosition(mEndpointId, mIdleButtonId); StartTimer(mMultiPressReleasedTimeMillis); break; } diff --git a/examples/chef/common/chef-rvc-mode-delegate.cpp b/examples/chef/common/chef-rvc-mode-delegate.cpp index 32b4cad0236899..9caf2ab94fd835 100644 --- a/examples/chef/common/chef-rvc-mode-delegate.cpp +++ b/examples/chef/common/chef-rvc-mode-delegate.cpp @@ -154,8 +154,8 @@ void emberAfRvcRunModeClusterInitCallback(chip::EndpointId endpointId) VerifyOrDie(!gRvcRunModeDelegate && !gRvcRunModeInstance); gRvcRunModeDelegate = std::make_unique(); - gRvcRunModeInstance = std::make_unique(gRvcRunModeDelegate.get(), endpointId, RvcRunMode::Id, - chip::to_underlying(RvcRunMode::Feature::kNoFeatures)); + gRvcRunModeInstance = + std::make_unique(gRvcRunModeDelegate.get(), endpointId, RvcRunMode::Id, 0 /* No feature bits */); gRvcRunModeInstance->Init(); } @@ -290,8 +290,8 @@ void emberAfRvcCleanModeClusterInitCallback(chip::EndpointId endpointId) VerifyOrDie(!gRvcCleanModeDelegate && !gRvcCleanModeInstance); gRvcCleanModeDelegate = std::make_unique(); - gRvcCleanModeInstance = std::make_unique(gRvcCleanModeDelegate.get(), endpointId, RvcCleanMode::Id, - chip::to_underlying(RvcCleanMode::Feature::kNoFeatures)); + gRvcCleanModeInstance = + std::make_unique(gRvcCleanModeDelegate.get(), endpointId, RvcCleanMode::Id, 0 /* No feature bits */); gRvcCleanModeInstance->Init(); } #endif // MATTER_DM_PLUGIN_RVC_CLEAN_MODE_SERVER diff --git a/examples/chef/devices/rootnode_colortemperaturelight_hbUnzYVeyn.zap b/examples/chef/devices/rootnode_colortemperaturelight_hbUnzYVeyn.zap index c38b543d1385e9..9121f5bb32a4f0 100644 --- a/examples/chef/devices/rootnode_colortemperaturelight_hbUnzYVeyn.zap +++ b/examples/chef/devices/rootnode_colortemperaturelight_hbUnzYVeyn.zap @@ -17,13 +17,6 @@ } ], "package": [ - { - "pathRelativity": "relativeToZap", - "path": "../../../src/app/zap-templates/app-templates.json", - "type": "gen-templates-json", - "category": "matter", - "version": "chip-v1" - }, { "pathRelativity": "relativeToZap", "path": "../../../src/app/zap-templates/zcl/zcl.json", @@ -31,6 +24,13 @@ "category": "matter", "version": 1, "description": "Matter SDK ZCL data" + }, + { + "pathRelativity": "relativeToZap", + "path": "../../../src/app/zap-templates/app-templates.json", + "type": "gen-templates-json", + "category": "matter", + "version": "chip-v1" } ], "endpointTypes": [ diff --git a/examples/chef/devices/rootnode_roboticvacuumcleaner_1807ff0c49.matter b/examples/chef/devices/rootnode_roboticvacuumcleaner_1807ff0c49.matter index d53a594c514919..ae1989b2777122 100644 --- a/examples/chef/devices/rootnode_roboticvacuumcleaner_1807ff0c49.matter +++ b/examples/chef/devices/rootnode_roboticvacuumcleaner_1807ff0c49.matter @@ -1584,7 +1584,7 @@ cluster GroupKeyManagement = 63 { /** Attributes and commands for selecting a mode from a list of supported options. */ cluster RvcRunMode = 84 { - revision 2; + revision 3; enum ModeTag : enum16 { kIdle = 16384; @@ -1604,7 +1604,7 @@ cluster RvcRunMode = 84 { } bitmap Feature : bitmap32 { - kNoFeatures = 0x0; + kDirectModeChange = 0x10000; } struct ModeTagStruct { @@ -1643,7 +1643,7 @@ cluster RvcRunMode = 84 { /** Attributes and commands for selecting a mode from a list of supported options. */ cluster RvcCleanMode = 85 { - revision 2; + revision 3; enum ModeTag : enum16 { kDeepClean = 16384; @@ -1656,7 +1656,7 @@ cluster RvcCleanMode = 85 { } bitmap Feature : bitmap32 { - kNoFeatures = 0x0; + kDirectModeChange = 0x10000; } struct ModeTagStruct { @@ -2010,7 +2010,7 @@ endpoint 1 { callback attribute eventList; callback attribute attributeList; callback attribute featureMap; - ram attribute clusterRevision default = 2; + ram attribute clusterRevision default = 3; handle command ChangeToMode; handle command ChangeToModeResponse; @@ -2024,7 +2024,7 @@ endpoint 1 { callback attribute eventList; callback attribute attributeList; callback attribute featureMap; - ram attribute clusterRevision default = 2; + ram attribute clusterRevision default = 3; handle command ChangeToMode; handle command ChangeToModeResponse; diff --git a/examples/chef/devices/rootnode_roboticvacuumcleaner_1807ff0c49.zap b/examples/chef/devices/rootnode_roboticvacuumcleaner_1807ff0c49.zap index 81144c4b3b437f..0463c7d0fb801c 100644 --- a/examples/chef/devices/rootnode_roboticvacuumcleaner_1807ff0c49.zap +++ b/examples/chef/devices/rootnode_roboticvacuumcleaner_1807ff0c49.zap @@ -2852,7 +2852,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "2", + "defaultValue": "3", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -3008,7 +3008,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "2", + "defaultValue": "3", "reportable": 1, "minInterval": 1, "maxInterval": 65534, diff --git a/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp b/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp index 22b634145ab93f..aabcb09f2314b2 100644 --- a/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp +++ b/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp @@ -31,6 +31,7 @@ #if defined(PW_RPC_ENABLED) #include +#include #endif using namespace chip; @@ -116,7 +117,7 @@ void ENFORCE_FORMAT(3, 0) LoggingCallback(const char * module, uint8_t category, #if defined(PW_RPC_ENABLED) void AttemptRpcClientConnect(System::Layer * systemLayer, void * appState) { - if (InitRpcClient(kFabricBridgeServerPort) == CHIP_NO_ERROR) + if (StartRpcClient() == CHIP_NO_ERROR) { ChipLogProgress(NotSpecified, "Connected to Fabric-Bridge"); } @@ -196,6 +197,9 @@ CHIP_ERROR InteractiveStartCommand::RunCommand() } #if defined(PW_RPC_ENABLED) + SetRpcRemoteServerPort(mFabricBridgeServerPort.Value()); + InitRpcServer(mLocalServerPort.Value()); + ChipLogProgress(NotSpecified, "PW_RPC initialized."); DeviceLayer::PlatformMgr().ScheduleWork(ExecuteDeferredConnect, 0); #endif diff --git a/examples/fabric-admin/commands/interactive/InteractiveCommands.h b/examples/fabric-admin/commands/interactive/InteractiveCommands.h index e3d28080657e26..648984d1ce94a5 100644 --- a/examples/fabric-admin/commands/interactive/InteractiveCommands.h +++ b/examples/fabric-admin/commands/interactive/InteractiveCommands.h @@ -24,6 +24,9 @@ #include +constexpr uint16_t kFabricBridgeServerPort = 33002; +constexpr uint16_t kFabricLocalServerPort = 33001; + class Commands; class InteractiveCommand : public CHIPCommand @@ -55,7 +58,13 @@ class InteractiveStartCommand : public InteractiveCommand InteractiveStartCommand(Commands * commandsHandler, CredentialIssuerCommands * credsIssuerConfig) : InteractiveCommand("start", commandsHandler, "Start an interactive shell that can then run other commands.", credsIssuerConfig) - {} + { +#if defined(PW_RPC_ENABLED) + AddArgument("fabric-bridge-server-port", 0, UINT16_MAX, &mFabricBridgeServerPort, + "The fabric-bridge RPC port number to connect to."); + AddArgument("local-server-port", 0, UINT16_MAX, &mLocalServerPort, "The port number for local RPC server."); +#endif + } /////////// CHIPCommand Interface ///////// CHIP_ERROR RunCommand() override; @@ -63,6 +72,11 @@ class InteractiveStartCommand : public InteractiveCommand private: char * GetCommand(char * command); std::string GetHistoryFilePath() const; + +#if defined(PW_RPC_ENABLED) + chip::Optional mFabricBridgeServerPort{ kFabricBridgeServerPort }; + chip::Optional mLocalServerPort{ kFabricLocalServerPort }; +#endif }; void PushCommand(const std::string & command); diff --git a/examples/fabric-admin/main.cpp b/examples/fabric-admin/main.cpp index c7f9089c120c8d..5768abf496fefc 100644 --- a/examples/fabric-admin/main.cpp +++ b/examples/fabric-admin/main.cpp @@ -28,19 +28,10 @@ #include #include -#if defined(PW_RPC_ENABLED) -#include -#endif - using namespace chip; void ApplicationInit() { -#if defined(PW_RPC_ENABLED) - InitRpcServer(kFabricAdminServerPort); - ChipLogProgress(NotSpecified, "PW_RPC initialized."); -#endif - DeviceMgr().Init(); } diff --git a/examples/fabric-admin/rpc/RpcClient.cpp b/examples/fabric-admin/rpc/RpcClient.cpp index 29fc2b1ea9d73c..df7a475071c4ba 100644 --- a/examples/fabric-admin/rpc/RpcClient.cpp +++ b/examples/fabric-admin/rpc/RpcClient.cpp @@ -117,9 +117,13 @@ void RpcCompletedWithEmptyResponse(const pw_protobuf_Empty & response, pw::Statu } // namespace -CHIP_ERROR InitRpcClient(uint16_t rpcServerPort) +void SetRpcRemoteServerPort(uint16_t port) +{ + rpc::client::SetRpcServerPort(port); +} + +CHIP_ERROR StartRpcClient() { - rpc::client::SetRpcServerPort(rpcServerPort); return rpc::client::StartPacketProcessing(); } diff --git a/examples/fabric-admin/rpc/RpcClient.h b/examples/fabric-admin/rpc/RpcClient.h index 6dd2b5b20bbd90..41d37cf85191b8 100644 --- a/examples/fabric-admin/rpc/RpcClient.h +++ b/examples/fabric-admin/rpc/RpcClient.h @@ -22,17 +22,19 @@ #include "fabric_bridge_service/fabric_bridge_service.rpc.pb.h" -constexpr uint16_t kFabricBridgeServerPort = 33002; - /** - * @brief Initializes the RPC client with the specified server port. + * @brief Sets the RPC server port to which the RPC client will connect. * - * This function sets the RPC server port and starts packet processing for the RPC client. + * @param port The port number. + */ +void SetRpcRemoteServerPort(uint16_t port); + +/** + * @brief Starts packet processing for the RPC client. * - * @param rpcServerPort The port number on which the RPC server is running. - * @return CHIP_NO_ERROR on successful initialization, or an appropriate CHIP_ERROR on failure. + * @return CHIP_NO_ERROR on successful start, or an appropriate CHIP_ERROR on failure. */ -CHIP_ERROR InitRpcClient(uint16_t rpcServerPort); +CHIP_ERROR StartRpcClient(); /** * @brief Adds a synchronized device to the RPC client. diff --git a/examples/fabric-admin/rpc/RpcServer.h b/examples/fabric-admin/rpc/RpcServer.h index bc03bc0ac4abd3..d283c0db5a0248 100644 --- a/examples/fabric-admin/rpc/RpcServer.h +++ b/examples/fabric-admin/rpc/RpcServer.h @@ -18,6 +18,4 @@ #pragma once -constexpr uint16_t kFabricAdminServerPort = 33001; - void InitRpcServer(uint16_t rpcServerPort); diff --git a/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter b/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter index 3fb7436adbfe5a..22f5ae6ccda3be 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter +++ b/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter @@ -1730,8 +1730,6 @@ provisional cluster CommissionerControl = 1873 { request struct CommissionNodeRequest { int64u requestId = 0; int16u responseTimeoutSeconds = 1; - optional octet_string ipAddress = 2; - optional int16u port = 3; } response struct ReverseOpenCommissioningWindow = 2 { diff --git a/examples/fabric-bridge-app/linux/CommissionerControl.cpp b/examples/fabric-bridge-app/linux/CommissionerControl.cpp index 0466ab8b711b69..1e67dc0f30c1b4 100644 --- a/examples/fabric-bridge-app/linux/CommissionerControl.cpp +++ b/examples/fabric-bridge-app/linux/CommissionerControl.cpp @@ -166,8 +166,7 @@ CHIP_ERROR CommissionerControlDelegate::GetCommissioningWindowParams(Commissioni return CHIP_NO_ERROR; } -CHIP_ERROR CommissionerControlDelegate::HandleCommissionNode(const CommissioningWindowParams & params, - const Optional & ipAddress, const Optional & port) +CHIP_ERROR CommissionerControlDelegate::HandleCommissionNode(const CommissioningWindowParams & params) { CHIP_ERROR err = CHIP_NO_ERROR; diff --git a/examples/fabric-bridge-app/linux/RpcClient.cpp b/examples/fabric-bridge-app/linux/RpcClient.cpp index 633b652b74180b..3ef88e8ed7fec5 100644 --- a/examples/fabric-bridge-app/linux/RpcClient.cpp +++ b/examples/fabric-bridge-app/linux/RpcClient.cpp @@ -107,9 +107,13 @@ void RpcCompletedWithEmptyResponse(const pw_protobuf_Empty & response, pw::Statu } // namespace -CHIP_ERROR InitRpcClient(uint16_t rpcServerPort) +void SetRpcRemoteServerPort(uint16_t port) +{ + rpc::client::SetRpcServerPort(port); +} + +CHIP_ERROR StartRpcClient() { - rpc::client::SetRpcServerPort(rpcServerPort); return rpc::client::StartPacketProcessing(); } diff --git a/examples/fabric-bridge-app/linux/include/CommissionerControl.h b/examples/fabric-bridge-app/linux/include/CommissionerControl.h index 486668ffa212f7..6e1d9c69d54bf9 100644 --- a/examples/fabric-bridge-app/linux/include/CommissionerControl.h +++ b/examples/fabric-bridge-app/linux/include/CommissionerControl.h @@ -32,8 +32,7 @@ class CommissionerControlDelegate : public Delegate CHIP_ERROR HandleCommissioningApprovalRequest(const CommissioningApprovalRequest & request) override; CHIP_ERROR ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId) override; CHIP_ERROR GetCommissioningWindowParams(CommissioningWindowParams & outParams) override; - CHIP_ERROR HandleCommissionNode(const CommissioningWindowParams & params, const Optional & ipAddress, - const Optional & port) override; + CHIP_ERROR HandleCommissionNode(const CommissioningWindowParams & params) override; ~CommissionerControlDelegate() = default; diff --git a/examples/fabric-bridge-app/linux/include/RpcClient.h b/examples/fabric-bridge-app/linux/include/RpcClient.h index f183b2ed38aa2b..2455bc56c8400f 100644 --- a/examples/fabric-bridge-app/linux/include/RpcClient.h +++ b/examples/fabric-bridge-app/linux/include/RpcClient.h @@ -21,17 +21,21 @@ #include #include -constexpr uint16_t kFabricAdminServerPort = 33001; +/** + * Sets the RPC server port to which the RPC client will connect. + * + * @param port The port number. + */ +void SetRpcRemoteServerPort(uint16_t port); /** - * Initializes the RPC client by setting the server port and starting packet processing. + * Starts packet processing for the RPC client. * - * @param rpcServerPort The port number of the RPC server. * @return CHIP_ERROR An error code indicating the success or failure of the initialization process. * - CHIP_NO_ERROR: Initialization was successful. * - Other error codes indicating specific failure reasons. */ -CHIP_ERROR InitRpcClient(uint16_t rpcServerPort); +CHIP_ERROR StartRpcClient(); /** * Opens a commissioning window for a specified node using setup PIN (passcode). diff --git a/examples/fabric-bridge-app/linux/include/RpcServer.h b/examples/fabric-bridge-app/linux/include/RpcServer.h index f86858b19bdfe3..d283c0db5a0248 100644 --- a/examples/fabric-bridge-app/linux/include/RpcServer.h +++ b/examples/fabric-bridge-app/linux/include/RpcServer.h @@ -18,6 +18,4 @@ #pragma once -constexpr uint16_t kFabricBridgeServerPort = 33002; - void InitRpcServer(uint16_t rpcServerPort); diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp index 08c70782de16ab..d08673ad12a7a6 100644 --- a/examples/fabric-bridge-app/linux/main.cpp +++ b/examples/fabric-bridge-app/linux/main.cpp @@ -16,6 +16,10 @@ * limitations under the License. */ +#include +#include +#include + #include #include "BridgedAdministratorCommissioning.h" @@ -28,15 +32,13 @@ #include #include #include +#include #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE #include "RpcClient.h" #include "RpcServer.h" #endif -#include -#include - // This is declared here and not in a header because zap/embr assumes all clusters // are defined in a static endpoint in the .zap file. From there, the codegen will // automatically use PluginApplicationCallbacksHeader.jinja to declare and call @@ -59,8 +61,48 @@ constexpr uint16_t kPollIntervalMs = 100; constexpr uint16_t kRetryIntervalS = 3; #endif +uint16_t gFabricAdminServerPort = 33001; +uint16_t gLocalServerPort = 33002; + BridgedDeviceBasicInformationImpl gBridgedDeviceBasicInformationAttributes; +constexpr uint16_t kOptionFabricAdminServerPortNumber = 0xFF01; +constexpr uint16_t kOptionLocalServerPortNumber = 0xFF02; + +ArgParser::OptionDef sProgramCustomOptionDefs[] = { + { "fabric-admin-server-port", ArgParser::kArgumentRequired, kOptionFabricAdminServerPortNumber }, + { "local-server-port", ArgParser::kArgumentRequired, kOptionLocalServerPortNumber }, + {}, +}; + +const char sProgramCustomOptionHelp[] = " --fabric-admin-server-port \n" + " The fabric-admin RPC port number to connect to (default: 33001).\n" + " --local-server-port \n" + " The port number for local RPC server (default: 33002).\n" + "\n"; + +bool HandleCustomOption(const char * aProgram, ArgParser::OptionSet * aOptions, int aIdentifier, const char * aName, + const char * aValue) +{ + switch (aIdentifier) + { + case kOptionFabricAdminServerPortNumber: + gFabricAdminServerPort = atoi(aValue); + break; + case kOptionLocalServerPortNumber: + gLocalServerPort = atoi(aValue); + break; + default: + ArgParser::PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", aProgram, aName); + return false; + } + + return true; +} + +ArgParser::OptionSet sProgramCustomOptions = { HandleCustomOption, sProgramCustomOptionDefs, "GENERAL OPTIONS", + sProgramCustomOptionHelp }; + bool KeyboardHit() { int bytesWaiting; @@ -105,7 +147,7 @@ void BridgePollingThread() #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE void AttemptRpcClientConnect(System::Layer * systemLayer, void * appState) { - if (InitRpcClient(kFabricAdminServerPort) == CHIP_NO_ERROR) + if (StartRpcClient() == CHIP_NO_ERROR) { ChipLogProgress(NotSpecified, "Connected to Fabric-Admin"); } @@ -258,7 +300,8 @@ void ApplicationInit() AttributeAccessInterfaceRegistry::Instance().Register(&gBridgedDeviceBasicInformationAttributes); #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE - InitRpcServer(kFabricBridgeServerPort); + SetRpcRemoteServerPort(gFabricAdminServerPort); + InitRpcServer(gLocalServerPort); AttemptRpcClientConnect(&DeviceLayer::SystemLayer(), nullptr); #endif @@ -285,7 +328,7 @@ void ApplicationShutdown() int main(int argc, char * argv[]) { - if (ChipLinuxAppInit(argc, argv) != 0) + if (ChipLinuxAppInit(argc, argv, &sProgramCustomOptions) != 0) { return -1; } diff --git a/examples/rvc-app/rvc-common/rvc-app.matter b/examples/rvc-app/rvc-common/rvc-app.matter index ee4d751919be22..4c0ebdf1acc682 100644 --- a/examples/rvc-app/rvc-common/rvc-app.matter +++ b/examples/rvc-app/rvc-common/rvc-app.matter @@ -1248,7 +1248,7 @@ cluster GroupKeyManagement = 63 { /** Attributes and commands for selecting a mode from a list of supported options. */ cluster RvcRunMode = 84 { - revision 2; + revision 3; enum ModeTag : enum16 { kIdle = 16384; @@ -1268,7 +1268,7 @@ cluster RvcRunMode = 84 { } bitmap Feature : bitmap32 { - kNoFeatures = 0x0; + kDirectModeChange = 0x10000; } struct ModeTagStruct { @@ -1307,7 +1307,7 @@ cluster RvcRunMode = 84 { /** Attributes and commands for selecting a mode from a list of supported options. */ cluster RvcCleanMode = 85 { - revision 2; + revision 3; enum ModeTag : enum16 { kDeepClean = 16384; @@ -1320,7 +1320,7 @@ cluster RvcCleanMode = 85 { } bitmap Feature : bitmap32 { - kNoFeatures = 0x0; + kDirectModeChange = 0x10000; } struct ModeTagStruct { @@ -1735,7 +1735,7 @@ endpoint 1 { callback attribute eventList; callback attribute attributeList; callback attribute featureMap; - ram attribute clusterRevision default = 2; + ram attribute clusterRevision default = 3; handle command ChangeToMode; handle command ChangeToModeResponse; @@ -1749,7 +1749,7 @@ endpoint 1 { callback attribute eventList; callback attribute attributeList; callback attribute featureMap; - ram attribute clusterRevision default = 2; + ram attribute clusterRevision default = 3; handle command ChangeToMode; handle command ChangeToModeResponse; diff --git a/examples/rvc-app/rvc-common/rvc-app.zap b/examples/rvc-app/rvc-common/rvc-app.zap index 89fe82c64f5c21..770686677a8630 100644 --- a/examples/rvc-app/rvc-common/rvc-app.zap +++ b/examples/rvc-app/rvc-common/rvc-app.zap @@ -2478,7 +2478,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "2", + "defaultValue": "3", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2634,7 +2634,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "2", + "defaultValue": "3", "reportable": 1, "minInterval": 1, "maxInterval": 65534, diff --git a/examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h b/examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h index b57ee2492122e7..7726fc337866bb 100644 --- a/examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h +++ b/examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h @@ -39,6 +39,10 @@ static constexpr uint8_t kMaxNumberOfPresetTypes = 6; // We will support only one preset of each preset type. static constexpr uint8_t kMaxNumberOfPresetsOfEachType = 1; +// For testing the use case where number of presets added exceeds the number of presets supported, we will have the value of +// kMaxNumberOfPresetsSupported < kMaxNumberOfPresetTypes * kMaxNumberOfPresetsOfEachType +static constexpr uint8_t kMaxNumberOfPresetsSupported = kMaxNumberOfPresetTypes * kMaxNumberOfPresetsOfEachType - 1; + class ThermostatDelegate : public Delegate { public: diff --git a/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp b/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp index 7991e48323a62f..da58c840049275 100644 --- a/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp +++ b/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp @@ -31,7 +31,7 @@ ThermostatDelegate ThermostatDelegate::sInstance; ThermostatDelegate::ThermostatDelegate() { - mNumberOfPresets = kMaxNumberOfPresetTypes * kMaxNumberOfPresetsOfEachType; + mNumberOfPresets = kMaxNumberOfPresetsSupported; mNextFreeIndexInPresetsList = 0; mNextFreeIndexInPendingPresetsList = 0; @@ -87,6 +87,9 @@ CHIP_ERROR ThermostatDelegate::GetPresetTypeAtIndex(size_t index, PresetTypeStru { .presetScenario = PresetScenarioEnum::kVacation, .numberOfPresets = kMaxNumberOfPresetsOfEachType, .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) }, + { .presetScenario = PresetScenarioEnum::kUserDefined, + .numberOfPresets = kMaxNumberOfPresetsOfEachType, + .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) }, }; if (index < ArraySize(presetTypes)) { diff --git a/scripts/py_matter_yamltests/matter_yamltests/hooks.py b/scripts/py_matter_yamltests/matter_yamltests/hooks.py index 78905826f55757..ca739b8ea27633 100644 --- a/scripts/py_matter_yamltests/matter_yamltests/hooks.py +++ b/scripts/py_matter_yamltests/matter_yamltests/hooks.py @@ -221,7 +221,9 @@ async def step_manual(self): def show_prompt(self, msg: str, placeholder: Optional[str] = None, - default_value: Optional[str] = None) -> None: + default_value: Optional[str] = None, + endpoint_id: Optional[int] = None, + ) -> None: """ This method is called when the step needs to ask the user to perform some action or provide some value. """ diff --git a/scripts/tests/run_python_test.py b/scripts/tests/run_python_test.py index 61ec274bfc1c64..eb8f0633c0a91a 100755 --- a/scripts/tests/run_python_test.py +++ b/scripts/tests/run_python_test.py @@ -87,11 +87,14 @@ def DumpProgramOutputToQueue(thread_list: typing.List[threading.Thread], tag: st 'mobile-device-test.py'), help='Test script to use.') @click.option("--script-args", type=str, default='', help='Script arguments, can use placeholders like {SCRIPT_BASE_NAME}.') +@click.option("--script-start-delay", type=int, default=0, + help='Delay in seconds before starting the script.') @click.option("--script-gdb", is_flag=True, help='Run script through gdb') @click.option("--quiet", is_flag=True, help="Do not print output from passing tests. Use this flag in CI to keep github log sizes manageable.") @click.option("--load-from-env", default=None, help="YAML file that contains values for environment variables.") -def main(app: str, factoryreset: bool, factoryreset_app_only: bool, app_args: str, script: str, script_args: str, script_gdb: bool, quiet: bool, load_from_env): +def main(app: str, factoryreset: bool, factoryreset_app_only: bool, app_args: str, + script: str, script_args: str, script_start_delay: int, script_gdb: bool, quiet: bool, load_from_env): if load_from_env: reader = MetadataReader(load_from_env) runs = reader.parse_script(script) @@ -103,6 +106,7 @@ def main(app: str, factoryreset: bool, factoryreset_app_only: bool, app_args: st app=app, app_args=app_args, script_args=script_args, + script_start_delay=script_start_delay, factoryreset=factoryreset, factoryreset_app_only=factoryreset_app_only, script_gdb=script_gdb, @@ -117,10 +121,11 @@ def main(app: str, factoryreset: bool, factoryreset_app_only: bool, app_args: st for run in runs: print(f"Executing {run.py_script_path.split('/')[-1]} {run.run}") main_impl(run.app, run.factoryreset, run.factoryreset_app_only, run.app_args, - run.py_script_path, run.script_args, run.script_gdb, run.quiet) + run.py_script_path, run.script_args, run.script_start_delay, run.script_gdb, run.quiet) -def main_impl(app: str, factoryreset: bool, factoryreset_app_only: bool, app_args: str, script: str, script_args: str, script_gdb: bool, quiet: bool): +def main_impl(app: str, factoryreset: bool, factoryreset_app_only: bool, app_args: str, + script: str, script_args: str, script_start_delay: int, script_gdb: bool, quiet: bool): app_args = app_args.replace('{SCRIPT_BASE_NAME}', os.path.splitext(os.path.basename(script))[0]) script_args = script_args.replace('{SCRIPT_BASE_NAME}', os.path.splitext(os.path.basename(script))[0]) @@ -175,6 +180,8 @@ def main_impl(app: str, factoryreset: bool, factoryreset_app_only: bool, app_arg DumpProgramOutputToQueue( log_cooking_threads, Fore.GREEN + "APP " + Style.RESET_ALL, app_process, stream_output, log_queue) + time.sleep(script_start_delay) + script_command = [script, "--paa-trust-store-path", os.path.join(DEFAULT_CHIP_ROOT, MATTER_DEVELOPMENT_PAA_ROOT_CERTS), '--log-format', '%(message)s', "--app-pid", str(app_pid)] + shlex.split(script_args) diff --git a/scripts/tools/zap/tests/inputs/all-clusters-app.zap b/scripts/tools/zap/tests/inputs/all-clusters-app.zap index 763b4f66c42140..0ec92a5c61294d 100644 --- a/scripts/tools/zap/tests/inputs/all-clusters-app.zap +++ b/scripts/tools/zap/tests/inputs/all-clusters-app.zap @@ -9648,7 +9648,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "2", + "defaultValue": "3", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -9804,7 +9804,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "2", + "defaultValue": "3", "reportable": 1, "minInterval": 1, "maxInterval": 65534, diff --git a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h index 9eff91f3832e75..a28eb56c6eb299 100644 --- a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h +++ b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h @@ -977,13 +977,13 @@ { ZAP_EMPTY_DEFAULT(), 0x00000000, 0, ZAP_TYPE(ARRAY), ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE) }, /* SupportedModes */ \ { ZAP_EMPTY_DEFAULT(), 0x00000001, 1, ZAP_TYPE(INT8U), ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE) }, /* CurrentMode */ \ { ZAP_EMPTY_DEFAULT(), 0x0000FFFC, 4, ZAP_TYPE(BITMAP32), ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE) }, /* FeatureMap */ \ - { ZAP_SIMPLE_DEFAULT(2), 0x0000FFFD, 2, ZAP_TYPE(INT16U), 0 }, /* ClusterRevision */ \ + { ZAP_SIMPLE_DEFAULT(3), 0x0000FFFD, 2, ZAP_TYPE(INT16U), 0 }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: RVC Clean Mode (server) */ \ { ZAP_EMPTY_DEFAULT(), 0x00000000, 0, ZAP_TYPE(ARRAY), ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE) }, /* SupportedModes */ \ { ZAP_EMPTY_DEFAULT(), 0x00000001, 1, ZAP_TYPE(INT8U), ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE) }, /* CurrentMode */ \ { ZAP_EMPTY_DEFAULT(), 0x0000FFFC, 4, ZAP_TYPE(BITMAP32), ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE) }, /* FeatureMap */ \ - { ZAP_SIMPLE_DEFAULT(2), 0x0000FFFD, 2, ZAP_TYPE(INT16U), 0 }, /* ClusterRevision */ \ + { ZAP_SIMPLE_DEFAULT(3), 0x0000FFFD, 2, ZAP_TYPE(INT16U), 0 }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Temperature Control (server) */ \ { ZAP_SIMPLE_DEFAULT(0), 0x00000004, 1, ZAP_TYPE(INT8U), 0 }, /* SelectedTemperatureLevel */ \ diff --git a/scripts/tools/zap/zap_download.py b/scripts/tools/zap/zap_download.py index 72dea27048a0c6..9d1a553feac0e8 100755 --- a/scripts/tools/zap/zap_download.py +++ b/scripts/tools/zap/zap_download.py @@ -23,6 +23,7 @@ import shutil import subprocess import sys +import tempfile import zipfile from typing import Optional @@ -123,13 +124,22 @@ def _SetupReleaseZap(install_directory: str, zap_version: str): logging.info("Fetching: %s", url) r = requests.get(url, stream=True) - z = zipfile.ZipFile(io.BytesIO(r.content)) - - logging.info("Data downloaded, extracting ...") - # extractall() does not preserve permissions (https://github.com/python/cpython/issues/59999) - for entry in z.filelist: - path = z.extract(entry, install_directory) - os.chmod(path, (entry.external_attr >> 16) & 0o777) + if zap_platform == 'mac': + # zipfile does not support symlinks (https://github.com/python/cpython/issues/82102), + # making a zap.app extracted with it unusable due to embedded frameworks. + with tempfile.NamedTemporaryFile(suffix='.zip') as tf: + for chunk in r.iter_content(chunk_size=4096): + tf.write(chunk) + tf.flush() + os.makedirs(install_directory, exist_ok=True) + _ExecuteProcess(['/usr/bin/unzip', '-oq', tf.name], install_directory) + else: + z = zipfile.ZipFile(io.BytesIO(r.content)) + logging.info("Data downloaded, extracting ...") + # extractall() does not preserve permissions (https://github.com/python/cpython/issues/59999) + for entry in z.filelist: + path = z.extract(entry, install_directory) + os.chmod(path, (entry.external_attr >> 16) & 0o777) logging.info("Done extracting.") diff --git a/src/access/tests/TestAccessControl.cpp b/src/access/tests/TestAccessControl.cpp index 400ea04b76e535..00b95baf39164b 100644 --- a/src/access/tests/TestAccessControl.cpp +++ b/src/access/tests/TestAccessControl.cpp @@ -1752,7 +1752,11 @@ TEST_F(TestAccessControl, TestCheck) for (const auto & checkData : checkData1) { CHIP_ERROR expectedResult = checkData.allow ? CHIP_NO_ERROR : CHIP_ERROR_ACCESS_DENIED; - EXPECT_EQ(accessControl.Check(checkData.subjectDescriptor, checkData.requestPath, checkData.privilege), expectedResult); + auto requestPath = checkData.requestPath; +#if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS + requestPath.requestType = Access::RequestType::kAttributeReadRequest; +#endif + EXPECT_EQ(accessControl.Check(checkData.subjectDescriptor, requestPath, checkData.privilege), expectedResult); } } diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index 4df397326894ed..80d65b2c4d16fc 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -64,14 +64,14 @@ void AddReverseOpenCommissioningWindowResponse(CommandHandler * commandObj, cons void RunDeferredCommissionNode(intptr_t commandArg) { - auto * info = reinterpret_cast(commandArg); + auto * params = reinterpret_cast(commandArg); Clusters::CommissionerControl::Delegate * delegate = Clusters::CommissionerControl::CommissionerControlServer::Instance().GetDelegate(); if (delegate != nullptr) { - CHIP_ERROR err = delegate->HandleCommissionNode(info->params, info->ipAddress.GetIPAddress(), info->port); + CHIP_ERROR err = delegate->HandleCommissionNode(*params); if (err != CHIP_NO_ERROR) { ChipLogError(Zcl, "HandleCommissionNode error: %" CHIP_ERROR_FORMAT, err.Format()); @@ -82,7 +82,7 @@ void RunDeferredCommissionNode(intptr_t commandArg) ChipLogError(Zcl, "No delegate available for HandleCommissionNode"); } - delete info; + delete params; } } // namespace @@ -234,31 +234,27 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( auto requestId = commandData.requestId; - auto commissionNodeInfo = std::make_unique(); + auto commissioningWindowParams = std::make_unique(); Clusters::CommissionerControl::Delegate * delegate = Clusters::CommissionerControl::CommissionerControlServer::Instance().GetDelegate(); VerifyOrExit(delegate != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - // Set IP address and port in the CommissionNodeInfo struct - commissionNodeInfo->port = commandData.port; - err = commissionNodeInfo->ipAddress.SetIPAddress(commandData.ipAddress); - SuccessOrExit(err); - // Validate the commission node command. err = delegate->ValidateCommissionNodeCommand(sourceNodeId, requestId); SuccessOrExit(err); // Populate the parameters for the commissioning window - err = delegate->GetCommissioningWindowParams(commissionNodeInfo->params); + err = delegate->GetCommissioningWindowParams(*commissioningWindowParams); SuccessOrExit(err); // Add the response for the commissioning window. - AddReverseOpenCommissioningWindowResponse(commandObj, commandPath, commissionNodeInfo->params); + AddReverseOpenCommissioningWindowResponse(commandObj, commandPath, *commissioningWindowParams); // Schedule the deferred reverse commission node task - DeviceLayer::PlatformMgr().ScheduleWork(RunDeferredCommissionNode, reinterpret_cast(commissionNodeInfo.release())); + DeviceLayer::PlatformMgr().ScheduleWork(RunDeferredCommissionNode, + reinterpret_cast(commissioningWindowParams.release())); exit: if (err != CHIP_NO_ERROR) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.h b/src/app/clusters/commissioner-control-server/commissioner-control-server.h index 60a4e81a93c3af..e63769de1d0a3e 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.h +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.h @@ -46,43 +46,6 @@ struct CommissioningWindowParams ByteSpan salt; }; -class ProtectedIPAddress -{ -public: - const Optional GetIPAddress() { return ipAddress; } - - CHIP_ERROR SetIPAddress(const Optional & address) - { - if (!address.HasValue()) - { - ipAddress.ClearValue(); - return CHIP_NO_ERROR; - } - - const ByteSpan & addressSpan = address.Value(); - size_t addressLength = addressSpan.size(); - if (addressLength != 4 && addressLength != 16) - { - return CHIP_ERROR_INVALID_ARGUMENT; - } - - memcpy(ipAddressBuffer, addressSpan.data(), addressLength); - ipAddress.SetValue(ByteSpan(ipAddressBuffer, addressLength)); - return CHIP_NO_ERROR; - } - -private: - Optional ipAddress; - uint8_t ipAddressBuffer[kIpAddressBufferSize]; -}; - -struct CommissionNodeInfo -{ - CommissioningWindowParams params; - ProtectedIPAddress ipAddress; - Optional port; -}; - class Delegate { public: @@ -130,12 +93,9 @@ class Delegate * Commission a node specified by the previously approved request. * * @param params The parameters for the commissioning window. - * @param ipAddress Optional IP address for the commissioning window. - * @param port Optional port for the commissioning window. * @return CHIP_ERROR indicating the success or failure of the operation. */ - virtual CHIP_ERROR HandleCommissionNode(const CommissioningWindowParams & params, const Optional & ipAddress, - const Optional & port) = 0; + virtual CHIP_ERROR HandleCommissionNode(const CommissioningWindowParams & params) = 0; virtual ~Delegate() = default; }; diff --git a/src/app/tests/suites/certification/Test_TC_TBRM_2_2.yaml b/src/app/tests/suites/certification/Test_TC_TBRM_2_2.yaml index 852e18a73adc8a..a6b54026729c8f 100644 --- a/src/app/tests/suites/certification/Test_TC_TBRM_2_2.yaml +++ b/src/app/tests/suites/certification/Test_TC_TBRM_2_2.yaml @@ -41,19 +41,21 @@ tests: values: - name: nodeId value: nodeId - + # Step 1 - label: "TH reads the ActiveDatasetTimestamp attribute from the DUT" command: readAttribute attribute: ActiveDatasetTimestamp response: value: null + # Step 2 - label: "TH reads the PendingDatasetTimestamp attribute from the DUT" command: readAttribute attribute: PendingDatasetTimestamp response: value: null + # Step 3 - label: "TH sends a valid ActiveDatasetRequest command to the DUT without having armed the fail-safe" @@ -65,6 +67,7 @@ tests: response: error: FAILSAFE_REQUIRED + # Step 4 - label: "TH sends ArmFailSafe command to the DUT" cluster: General Commissioning command: ArmFailSafe @@ -75,7 +78,12 @@ tests: value: 60 - name: Breadcrumb value: 1 + response: + values: + - name: ErrorCode + value: CommissioningErrorEnum.OK + # Step 5 - label: "TH sends an invalid ActiveDatasetRequest command to the DUT" command: SetActiveDatasetRequest arguments: @@ -85,6 +93,7 @@ tests: response: error: INVALID_COMMAND + # Step 6 - label: "TH sends a valid ActiveDatasetRequest command to the DUT" command: SetActiveDatasetRequest arguments: @@ -92,12 +101,20 @@ tests: - name: ActiveDataset value: PIXIT.TBRM.THREAD_ACTIVE_DATASET + # Step 7 + - label: "TH sends CommissioningComplete command to the DUT" + cluster: General Commissioning + command: CommissioningComplete + endpoint: 0 + + # Step 8 - label: "TH reads the InterfaceEnabled attribute from the DUT" command: readAttribute attribute: InterfaceEnabled response: value: true + # Step 9 - label: "TH reads the ActiveDatasetTimestamp attribute from the DUT" command: readAttribute attribute: ActiveDatasetTimestamp @@ -106,7 +123,8 @@ tests: constraints: type: int64u - - label: "TH sends a valid GetActiveDatasetRequest command to the DUT" + # Step 10 + - label: "TH send a GetActiveDatasetRequest command to the DUT" command: GetActiveDatasetRequest response: values: diff --git a/src/app/tests/suites/certification/Test_TC_TBRM_2_3.yaml b/src/app/tests/suites/certification/Test_TC_TBRM_2_3.yaml index 7a55191f471f0b..a0ecd11693ef8d 100644 --- a/src/app/tests/suites/certification/Test_TC_TBRM_2_3.yaml +++ b/src/app/tests/suites/certification/Test_TC_TBRM_2_3.yaml @@ -49,7 +49,7 @@ tests: constraints: type: int64u - - label: "If the ActiveDatasetTimestamp attribute not null, go to step 4" + - label: "If the ActiveDatasetTimestamp attribute not null, go to step 5" cluster: EqualityCommands command: UnsignedNumberEquals arguments: @@ -75,6 +75,10 @@ tests: value: 60 - name: Breadcrumb value: 1 + response: + values: + - name: ErrorCode + value: CommissioningErrorEnum.OK # Step 3 - label: "TH sends a valid ActiveDatasetRequest command to the DUT" @@ -86,6 +90,13 @@ tests: value: PIXIT.TBRM.THREAD_ACTIVE_DATASET # Step 4 + - label: "TH sends CommissioningComplete command to the DUT" + runIf: noActiveDataset + cluster: General Commissioning + command: CommissioningComplete + endpoint: 0 + + # Step 5 - label: "TH reads the PendingDatasetTimestamp attribute from the DUT" command: readAttribute attribute: PendingDatasetTimestamp @@ -94,7 +105,7 @@ tests: constraints: type: int64u - # Step 5 + # Step 6 - label: "TH sends a SetPendingDatasetRequest command to the DUT" command: SetPendingDatasetRequest arguments: @@ -102,7 +113,7 @@ tests: - name: PendingDataset value: PIXIT.TBRM.THREAD_PENDING_DATASET - # Step 6 + # Step 7 - label: "TH sends a GetPendingDatasetRequest command to the DUT" command: GetPendingDatasetRequest response: @@ -112,7 +123,7 @@ tests: type: octet_string # TODO: This should be PIXIT.TBRM.THREAD_PENDING_DATASET but ignoring the Delay Timer element if present - # Step 7 + # Step 8 - label: "TH reads the PendingDatasetTimestamp attribute from the DUT" command: readAttribute attribute: PendingDatasetTimestamp @@ -121,7 +132,7 @@ tests: type: int64u notValue: initialPendingTimestamp - # Step 8 + # Step 9 - label: "TH subscribes to the ActiveDatasetTimestamp attribute from the DUT" command: subscribeAttribute @@ -141,15 +152,15 @@ tests: constraints: hasValue: true - # Step 9 + # Step 10 - label: "TH reads the PendingDatasetTimestamp attribute from the DUT" command: readAttribute attribute: PendingDatasetTimestamp response: value: null - # Step 10 - - label: "TH sends a valid GetActiveDatasetRequest command to the DUT" + # Step 11 + - label: "TH sends a GetActiveDatasetRequest command to the DUT" command: GetActiveDatasetRequest response: values: @@ -158,7 +169,7 @@ tests: constraints: type: octet_string - # Step 11 + # Step 12 - label: "TH reads the InterfaceEnabled attribute from the DUT" command: readAttribute attribute: InterfaceEnabled diff --git a/src/app/tests/suites/certification/Test_TC_TBRM_2_4.yaml b/src/app/tests/suites/certification/Test_TC_TBRM_2_4.yaml new file mode 100644 index 00000000000000..d1483c1c5a930a --- /dev/null +++ b/src/app/tests/suites/certification/Test_TC_TBRM_2_4.yaml @@ -0,0 +1,86 @@ +# 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. + +name: + "[TC-TBRM-2.4] Verify that getting Active or Pending Dataset in the PASE + session results in unsupported access" + +PICS: + - TBRM.S + +config: + nodeId: 0x12344321 + + payload: "MT:-24J0AFN00KA0648G00" + discriminator: 3840 + PakeVerifier: + type: octet_string + defaultValue: "hex:b96170aae803346884724fe9a3b287c30330c2a660375d17bb205a8cf1aecb350457f8ab79ee253ab6a8e46bb09e543ae422736de501e3db37d441fe344920d09548e4c18240630c4ff4913c53513839b7c07fcc0627a1b8573a149fcd1fa466cf" + +tests: + - label: "Wait for the commissioned device to be retrieved" + cluster: DelayCommands + command: WaitForCommissionee + arguments: + values: + - name: nodeId + value: nodeId + + - label: "Open Commissioning Window" + endpoint: 0 + cluster: Administrator Commissioning + command: OpenCommissioningWindow + timedInteractionTimeoutMs: 2000 + arguments: + values: + - name: CommissioningTimeout + value: 180 + - name: PAKEPasscodeVerifier + value: PakeVerifier + - name: Discriminator + value: discriminator + - name: Iterations + value: 1000 + - name: Salt + value: "SPAKE2P Key Salt" + + - label: "TH2 establishes a PASE session with the DUT" + identity: beta + cluster: CommissionerCommands + endpoint: 0 + command: EstablishPASESession + arguments: + values: + - name: nodeId + value: nodeId + - name: payload + value: payload + + - label: + "TH2 send GetActiveDatasetRequest command to the DUT in PASE session" + identity: beta + cluster: Thread Border Router Management + endpoint: 1 + command: GetActiveDatasetRequest + response: + error: UNSUPPORTED_ACCESS + + - label: + "TH2 send GetPendingDatasetRequest command to the DUT in PASE session" + identity: beta + cluster: Thread Border Router Management + endpoint: 1 + command: GetPendingDatasetRequest + response: + error: UNSUPPORTED_ACCESS diff --git a/src/app/tests/suites/certification/Test_TC_TBRM_3_1_Simulated.yaml b/src/app/tests/suites/certification/Test_TC_TBRM_3_1_Simulated.yaml new file mode 100644 index 00000000000000..095711a0fcee95 --- /dev/null +++ b/src/app/tests/suites/certification/Test_TC_TBRM_3_1_Simulated.yaml @@ -0,0 +1,44 @@ +# 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. + +name: "[TC-TBRM-3.1] Functionality with client as DUT" + +PICS: + - TBRM.C + +config: + nodeId: 0x12344321 + cluster: Thread Border Router Management + endpoint: 1 + +tests: + # Step 1 + - label: "DUT send SetActiveDatasetRequest to TH" + PICS: TBRM.C.C03.Tx + wait: "SetActiveDatasetRequest" + + # Step 2 + - label: "DUT send SetPendingDatasetRequest to TH" + PICS: TBRM.C.C04.Tx + wait: "SetPendingDatasetRequest" + + # Step 3 + - label: "DUT send GetActiveDatasetRequest to TH" + PICS: TBRM.C.C00.Tx + wait: "GetActiveDatasetRequest" + + # Step 4 + - label: "DUT send GetPendingDatasetRequest to TH" + PICS: TBRM.C.C01.Tx + wait: "GetPendingDatasetRequest" diff --git a/src/app/zap-templates/zcl/data-model/chip/commissioner-control-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/commissioner-control-cluster.xml index 7228b8f47a20ab..af9a734d6e7176 100644 --- a/src/app/zap-templates/zcl/data-model/chip/commissioner-control-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/commissioner-control-cluster.xml @@ -49,10 +49,7 @@ limitations under the License. This command is sent by a client to request that the server begins commissioning a previously approved request. - - - - + diff --git a/src/app/zap-templates/zcl/data-model/chip/rvc-clean-mode-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/rvc-clean-mode-cluster.xml index c528759d2a8cb7..afd87fd18f3d54 100644 --- a/src/app/zap-templates/zcl/data-model/chip/rvc-clean-mode-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/rvc-clean-mode-cluster.xml @@ -37,8 +37,19 @@ limitations under the License. true true Attributes and commands for selecting a mode from a list of supported options. - + + + + + + + + + SupportedModes CurrentMode @@ -62,10 +73,4 @@ limitations under the License. - - - - - - diff --git a/src/app/zap-templates/zcl/data-model/chip/rvc-run-mode-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/rvc-run-mode-cluster.xml index f13f86ac2dc97e..df27b2c15e0b83 100644 --- a/src/app/zap-templates/zcl/data-model/chip/rvc-run-mode-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/rvc-run-mode-cluster.xml @@ -44,7 +44,19 @@ limitations under the License. true true Attributes and commands for selecting a mode from a list of supported options. - + + + + + + + + + + SupportedModes CurrentMode @@ -67,10 +79,4 @@ limitations under the License. - - - - - - diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index e21a82213b661a..42086a2caa93c5 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -3453,7 +3453,7 @@ cluster LaundryWasherControls = 83 { /** Attributes and commands for selecting a mode from a list of supported options. */ cluster RvcRunMode = 84 { - revision 2; + revision 3; enum ModeTag : enum16 { kIdle = 16384; @@ -3473,7 +3473,7 @@ cluster RvcRunMode = 84 { } bitmap Feature : bitmap32 { - kNoFeatures = 0x0; + kDirectModeChange = 0x10000; } struct ModeTagStruct { @@ -3512,7 +3512,7 @@ cluster RvcRunMode = 84 { /** Attributes and commands for selecting a mode from a list of supported options. */ cluster RvcCleanMode = 85 { - revision 2; + revision 3; enum ModeTag : enum16 { kDeepClean = 16384; @@ -3525,7 +3525,7 @@ cluster RvcCleanMode = 85 { } bitmap Feature : bitmap32 { - kNoFeatures = 0x0; + kDirectModeChange = 0x10000; } struct ModeTagStruct { @@ -9450,8 +9450,6 @@ provisional cluster CommissionerControl = 1873 { request struct CommissionNodeRequest { int64u requestId = 0; int16u responseTimeoutSeconds = 1; - optional octet_string ipAddress = 2; - optional int16u port = 3; } response struct ReverseOpenCommissioningWindow = 2 { diff --git a/src/controller/data_model/controller-clusters.zap b/src/controller/data_model/controller-clusters.zap index 0c4c6a7a1a842e..32b6ea18fc8fb8 100644 --- a/src/controller/data_model/controller-clusters.zap +++ b/src/controller/data_model/controller-clusters.zap @@ -2332,7 +2332,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "2", + "defaultValue": "3", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2392,7 +2392,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "2", + "defaultValue": "3", "reportable": 1, "minInterval": 1, "maxInterval": 65534, diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java index 8dd9332beb6885..fd792a14bb2e4f 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java @@ -61087,11 +61087,11 @@ public void onResponse(StructType invokeStructValue) { }}, commandId, commandArgs, timedInvokeTimeoutMs); } - public void commissionNode(ReverseOpenCommissioningWindowCallback callback, Long requestId, Integer responseTimeoutSeconds, Optional ipAddress, Optional port) { - commissionNode(callback, requestId, responseTimeoutSeconds, ipAddress, port, 0); + public void commissionNode(ReverseOpenCommissioningWindowCallback callback, Long requestId, Integer responseTimeoutSeconds) { + commissionNode(callback, requestId, responseTimeoutSeconds, 0); } - public void commissionNode(ReverseOpenCommissioningWindowCallback callback, Long requestId, Integer responseTimeoutSeconds, Optional ipAddress, Optional port, int timedInvokeTimeoutMs) { + public void commissionNode(ReverseOpenCommissioningWindowCallback callback, Long requestId, Integer responseTimeoutSeconds, int timedInvokeTimeoutMs) { final long commandId = 1L; ArrayList elements = new ArrayList<>(); @@ -61103,14 +61103,6 @@ public void commissionNode(ReverseOpenCommissioningWindowCallback callback, Long BaseTLVType responseTimeoutSecondstlvValue = new UIntType(responseTimeoutSeconds); elements.add(new StructElement(responseTimeoutSecondsFieldID, responseTimeoutSecondstlvValue)); - final long ipAddressFieldID = 2L; - BaseTLVType ipAddresstlvValue = ipAddress.map((nonOptionalipAddress) -> new ByteArrayType(nonOptionalipAddress)).orElse(new EmptyType()); - elements.add(new StructElement(ipAddressFieldID, ipAddresstlvValue)); - - final long portFieldID = 3L; - BaseTLVType porttlvValue = port.map((nonOptionalport) -> new UIntType(nonOptionalport)).orElse(new EmptyType()); - elements.add(new StructElement(portFieldID, porttlvValue)); - StructType commandArgs = new StructType(elements); invoke(new InvokeCallbackImpl(callback) { @Override diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java index 829cf623e75ba0..b48f4029b62237 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java @@ -17424,7 +17424,7 @@ public static RequestCommissioningApprovalCommandField value(int id) throws NoSu } throw new NoSuchFieldError(); } - }public enum CommissionNodeCommandField {RequestId(0),ResponseTimeoutSeconds(1),IpAddress(2),Port(3),; + }public enum CommissionNodeCommandField {RequestId(0),ResponseTimeoutSeconds(1),; private final int id; CommissionNodeCommandField(int id) { this.id = id; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java index fe948f9429f516..55659d8463581d 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java @@ -29046,12 +29046,6 @@ public Map> getCommandMap() { CommandParameterInfo commissionerControlcommissionNoderesponseTimeoutSecondsCommandParameterInfo = new CommandParameterInfo("responseTimeoutSeconds", Integer.class, Integer.class); commissionerControlcommissionNodeCommandParams.put("responseTimeoutSeconds",commissionerControlcommissionNoderesponseTimeoutSecondsCommandParameterInfo); - - CommandParameterInfo commissionerControlcommissionNodeipAddressCommandParameterInfo = new CommandParameterInfo("ipAddress", Optional.class, byte[].class); - commissionerControlcommissionNodeCommandParams.put("ipAddress",commissionerControlcommissionNodeipAddressCommandParameterInfo); - - CommandParameterInfo commissionerControlcommissionNodeportCommandParameterInfo = new CommandParameterInfo("port", Optional.class, Integer.class); - commissionerControlcommissionNodeCommandParams.put("port",commissionerControlcommissionNodeportCommandParameterInfo); InteractionInfo commissionerControlcommissionNodeInteractionInfo = new InteractionInfo( (cluster, callback, commandArguments) -> { ((ChipClusters.CommissionerControlCluster) cluster) @@ -29062,12 +29056,6 @@ public Map> getCommandMap() { , (Integer) commandArguments.get("responseTimeoutSeconds") - , (Optional) - commandArguments.get("ipAddress") - - , (Optional) - commandArguments.get("port") - ); }, () -> new DelegatedCommissionerControlClusterReverseOpenCommissioningWindowCallback(), diff --git a/src/controller/java/generated/java/matter/controller/cluster/clusters/CommissionerControlCluster.kt b/src/controller/java/generated/java/matter/controller/cluster/clusters/CommissionerControlCluster.kt index 2daf2b464e948f..3e9d07f3f04354 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/clusters/CommissionerControlCluster.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/clusters/CommissionerControlCluster.kt @@ -130,8 +130,6 @@ class CommissionerControlCluster( suspend fun commissionNode( requestId: ULong, responseTimeoutSeconds: UShort, - ipAddress: ByteArray?, - port: UShort?, timedInvokeTimeout: Duration? = null, ): ReverseOpenCommissioningWindow { val commandId: UInt = 1u @@ -144,12 +142,6 @@ class CommissionerControlCluster( val TAG_RESPONSE_TIMEOUT_SECONDS_REQ: Int = 1 tlvWriter.put(ContextSpecificTag(TAG_RESPONSE_TIMEOUT_SECONDS_REQ), responseTimeoutSeconds) - - val TAG_IP_ADDRESS_REQ: Int = 2 - ipAddress?.let { tlvWriter.put(ContextSpecificTag(TAG_IP_ADDRESS_REQ), ipAddress) } - - val TAG_PORT_REQ: Int = 3 - port?.let { tlvWriter.put(ContextSpecificTag(TAG_PORT_REQ), port) } tlvWriter.endStructure() val request: InvokeRequest = diff --git a/src/controller/python/chip/clusters/CHIPClusters.py b/src/controller/python/chip/clusters/CHIPClusters.py index 925ce90cfcf3a8..356d3b43569ea2 100644 --- a/src/controller/python/chip/clusters/CHIPClusters.py +++ b/src/controller/python/chip/clusters/CHIPClusters.py @@ -13389,8 +13389,6 @@ class ChipClusters: "args": { "requestId": "int", "responseTimeoutSeconds": "int", - "ipAddress": "bytes", - "port": "int", }, }, }, diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 82f257f764052c..a5988bbf54e71a 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -17551,7 +17551,7 @@ class StatusCode(MatterIntEnum): class Bitmaps: class Feature(IntFlag): - kNoFeatures = 0x0 + kDirectModeChange = 0x10000 class Structs: @dataclass @@ -17795,7 +17795,7 @@ class StatusCode(MatterIntEnum): class Bitmaps: class Feature(IntFlag): - kNoFeatures = 0x0 + kDirectModeChange = 0x10000 class Structs: @dataclass @@ -47477,14 +47477,10 @@ def descriptor(cls) -> ClusterObjectDescriptor: Fields=[ ClusterObjectFieldDescriptor(Label="requestId", Tag=0, Type=uint), ClusterObjectFieldDescriptor(Label="responseTimeoutSeconds", Tag=1, Type=uint), - ClusterObjectFieldDescriptor(Label="ipAddress", Tag=2, Type=typing.Optional[bytes]), - ClusterObjectFieldDescriptor(Label="port", Tag=3, Type=typing.Optional[uint]), ]) requestId: 'uint' = 0 responseTimeoutSeconds: 'uint' = 0 - ipAddress: 'typing.Optional[bytes]' = None - port: 'typing.Optional[uint]' = None @dataclass class ReverseOpenCommissioningWindow(ClusterCommand): diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h index cf8380030b57cb..bedf8161036583 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h @@ -18583,7 +18583,7 @@ typedef NS_ENUM(uint8_t, MTRRVCRunModeStatusCode) { } MTR_AVAILABLE(ios(17.4), macos(14.4), watchos(10.4), tvos(17.4)); typedef NS_OPTIONS(uint32_t, MTRRVCRunModeFeature) { - MTRRVCRunModeFeatureNoFeatures MTR_PROVISIONALLY_AVAILABLE = 0x0, + MTRRVCRunModeFeatureDirectModeChange MTR_PROVISIONALLY_AVAILABLE = 0x10000, } MTR_AVAILABLE(ios(17.4), macos(14.4), watchos(10.4), tvos(17.4)); typedef NS_ENUM(uint16_t, MTRRVCCleanModeModeTag) { @@ -18597,7 +18597,7 @@ typedef NS_ENUM(uint8_t, MTRRVCCleanModeStatusCode) { } MTR_AVAILABLE(ios(17.4), macos(14.4), watchos(10.4), tvos(17.4)); typedef NS_OPTIONS(uint32_t, MTRRVCCleanModeFeature) { - MTRRVCCleanModeFeatureNoFeatures MTR_PROVISIONALLY_AVAILABLE = 0x0, + MTRRVCCleanModeFeatureDirectModeChange MTR_PROVISIONALLY_AVAILABLE = 0x10000, } MTR_AVAILABLE(ios(17.4), macos(14.4), watchos(10.4), tvos(17.4)); typedef NS_OPTIONS(uint32_t, MTRTemperatureControlFeature) { diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h index 95b3675d94e0ae..044b0116ff4778 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h @@ -10933,10 +10933,6 @@ MTR_PROVISIONALLY_AVAILABLE @property (nonatomic, copy) NSNumber * _Nonnull requestId MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nonnull responseTimeoutSeconds MTR_PROVISIONALLY_AVAILABLE; - -@property (nonatomic, copy) NSData * _Nullable ipAddress MTR_PROVISIONALLY_AVAILABLE; - -@property (nonatomic, copy) NSNumber * _Nullable port MTR_PROVISIONALLY_AVAILABLE; /** * Controls whether the command is a timed command (using Timed Invoke). * diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm index 00dc34a9a37ee6..d24ce54fc84028 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm @@ -31583,10 +31583,6 @@ - (instancetype)init _requestId = @(0); _responseTimeoutSeconds = @(0); - - _ipAddress = nil; - - _port = nil; _timedInvokeTimeoutMs = nil; _serverSideProcessingTimeout = nil; } @@ -31599,8 +31595,6 @@ - (id)copyWithZone:(NSZone * _Nullable)zone; other.requestId = self.requestId; other.responseTimeoutSeconds = self.responseTimeoutSeconds; - other.ipAddress = self.ipAddress; - other.port = self.port; other.timedInvokeTimeoutMs = self.timedInvokeTimeoutMs; other.serverSideProcessingTimeout = self.serverSideProcessingTimeout; @@ -31609,7 +31603,7 @@ - (id)copyWithZone:(NSZone * _Nullable)zone; - (NSString *)description { - NSString * descriptionString = [NSString stringWithFormat:@"<%@: requestId:%@; responseTimeoutSeconds:%@; ipAddress:%@; port:%@; >", NSStringFromClass([self class]), _requestId, _responseTimeoutSeconds, [_ipAddress base64EncodedStringWithOptions:0], _port]; + NSString * descriptionString = [NSString stringWithFormat:@"<%@: requestId:%@; responseTimeoutSeconds:%@; >", NSStringFromClass([self class]), _requestId, _responseTimeoutSeconds]; return descriptionString; } @@ -31627,18 +31621,6 @@ - (CHIP_ERROR)_encodeToTLVReader:(chip::System::PacketBufferTLVReader &)reader { encodableStruct.responseTimeoutSeconds = self.responseTimeoutSeconds.unsignedShortValue; } - { - if (self.ipAddress != nil) { - auto & definedValue_0 = encodableStruct.ipAddress.Emplace(); - definedValue_0 = AsByteSpan(self.ipAddress); - } - } - { - if (self.port != nil) { - auto & definedValue_0 = encodableStruct.port.Emplace(); - definedValue_0 = self.port.unsignedShortValue; - } - } auto buffer = chip::System::PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSizeWithoutReserve, 0); if (buffer.IsNull()) { diff --git a/src/platform/Zephyr/wifi/ConnectivityManagerImplWiFi.cpp b/src/platform/Zephyr/wifi/ConnectivityManagerImplWiFi.cpp index 5bef79bb22fdc5..9b605c31560142 100644 --- a/src/platform/Zephyr/wifi/ConnectivityManagerImplWiFi.cpp +++ b/src/platform/Zephyr/wifi/ConnectivityManagerImplWiFi.cpp @@ -42,7 +42,7 @@ ConnectivityManager::WiFiStationMode ConnectivityManagerImplWiFi::_GetWiFiStatio { if (mStationMode != ConnectivityManager::WiFiStationMode::kWiFiStationMode_ApplicationControlled) { - mStationMode = (WiFiManager::StationStatus::DISABLED == WiFiManager().Instance().GetStationStatus()) + mStationMode = (WiFiManager::StationStatus::DISABLED == WiFiManager::Instance().GetStationStatus()) ? ConnectivityManager::WiFiStationMode::kWiFiStationMode_Disabled : ConnectivityManager::WiFiStationMode::kWiFiStationMode_Enabled; } @@ -60,7 +60,7 @@ CHIP_ERROR ConnectivityManagerImplWiFi::_SetWiFiStationMode(ConnectivityManager: bool ConnectivityManagerImplWiFi::_IsWiFiStationEnabled(void) { - return (WiFiManager::StationStatus::DISABLED <= WiFiManager().Instance().GetStationStatus()); + return (WiFiManager::StationStatus::DISABLED <= WiFiManager::Instance().GetStationStatus()); } bool ConnectivityManagerImplWiFi::_IsWiFiStationApplicationControlled(void) @@ -70,7 +70,7 @@ bool ConnectivityManagerImplWiFi::_IsWiFiStationApplicationControlled(void) bool ConnectivityManagerImplWiFi::_IsWiFiStationConnected(void) { - return (WiFiManager::StationStatus::CONNECTED == WiFiManager().Instance().GetStationStatus()); + return (WiFiManager::StationStatus::CONNECTED == WiFiManager::Instance().GetStationStatus()); } System::Clock::Timeout ConnectivityManagerImplWiFi::_GetWiFiStationReconnectInterval(void) @@ -88,14 +88,14 @@ bool ConnectivityManagerImplWiFi::_IsWiFiStationProvisioned(void) { // from Matter perspective `provisioned` means that the supplicant has been provided // with SSID and password (doesn't matter if valid or not) - return (WiFiManager::StationStatus::CONNECTING <= WiFiManager().Instance().GetStationStatus()); + return (WiFiManager::StationStatus::CONNECTING <= WiFiManager::Instance().GetStationStatus()); } void ConnectivityManagerImplWiFi::_ClearWiFiStationProvision(void) { if (_IsWiFiStationProvisioned()) { - if (CHIP_NO_ERROR != WiFiManager().Instance().ClearStationProvisioningData()) + if (CHIP_NO_ERROR != WiFiManager::Instance().ClearStationProvisioningData()) { ChipLogError(DeviceLayer, "Cannot clear WiFi station provisioning data"); } @@ -104,9 +104,9 @@ void ConnectivityManagerImplWiFi::_ClearWiFiStationProvision(void) bool ConnectivityManagerImplWiFi::_CanStartWiFiScan() { - return (WiFiManager::StationStatus::DISABLED != WiFiManager().Instance().GetStationStatus() && - WiFiManager::StationStatus::SCANNING != WiFiManager().Instance().GetStationStatus() && - WiFiManager::StationStatus::CONNECTING != WiFiManager().Instance().GetStationStatus()); + return (WiFiManager::StationStatus::DISABLED != WiFiManager::Instance().GetStationStatus() && + WiFiManager::StationStatus::SCANNING != WiFiManager::Instance().GetStationStatus() && + WiFiManager::StationStatus::CONNECTING != WiFiManager::Instance().GetStationStatus()); } void ConnectivityManagerImplWiFi::_OnWiFiStationProvisionChange() diff --git a/src/platform/Zephyr/wifi/WiFiManager.cpp b/src/platform/Zephyr/wifi/WiFiManager.cpp index 01d0d937b19b3a..6dbc4930314fb1 100644 --- a/src/platform/Zephyr/wifi/WiFiManager.cpp +++ b/src/platform/Zephyr/wifi/WiFiManager.cpp @@ -23,10 +23,9 @@ #include "WiFiManager.h" #include -#include -#include #include #include +#include #include #include @@ -50,6 +49,21 @@ namespace DeviceLayer { namespace { +app::Clusters::NetworkCommissioning::WiFiBandEnum ConvertBandEnum(uint8_t band) +{ + switch (band) + { + case WIFI_FREQ_BAND_2_4_GHZ: + return app::Clusters::NetworkCommissioning::WiFiBandEnum::k2g4; + case WIFI_FREQ_BAND_5_GHZ: + return app::Clusters::NetworkCommissioning::WiFiBandEnum::k5g; + case WIFI_FREQ_BAND_6_GHZ: + return app::Clusters::NetworkCommissioning::WiFiBandEnum::k6g; + default: + return app::Clusters::NetworkCommissioning::WiFiBandEnum::kUnknownEnumValue; + } +} + NetworkCommissioning::WiFiScanResponse ToScanResponse(const wifi_scan_result * result) { NetworkCommissioning::WiFiScanResponse response = {}; @@ -62,9 +76,10 @@ NetworkCommissioning::WiFiScanResponse ToScanResponse(const wifi_scan_result * r // TODO: Distinguish WPA versions response.security.Set(result->security == WIFI_SECURITY_TYPE_PSK ? NetworkCommissioning::WiFiSecurity::kWpaPersonal : NetworkCommissioning::WiFiSecurity::kUnencrypted); - response.channel = result->channel; - response.rssi = result->rssi; - response.ssidLen = result->ssid_length; + response.channel = result->channel; + response.rssi = result->rssi; + response.ssidLen = result->ssid_length; + response.wiFiBand = ConvertBandEnum(result->band); memcpy(response.ssid, result->ssid, result->ssid_length); // TODO: MAC/BSSID is not filled by the Wi-Fi driver memcpy(response.bssid, result->mac, result->mac_length); @@ -138,57 +153,43 @@ const Map { WIFI_STATE_GROUP_HANDSHAKE, WiFiManager::StationStatus::PROVISIONING }, { WIFI_STATE_COMPLETED, WiFiManager::StationStatus::FULLY_PROVISIONED } }); -const Map - WiFiManager::sEventHandlerMap({ { NET_EVENT_WIFI_SCAN_RESULT, WiFiManager::ScanResultHandler }, - { NET_EVENT_WIFI_SCAN_DONE, WiFiManager::ScanDoneHandler }, - { NET_EVENT_WIFI_CONNECT_RESULT, WiFiManager::ConnectHandler }, - { NET_EVENT_WIFI_DISCONNECT_RESULT, WiFiManager::DisconnectHandler }, - { NET_EVENT_WIFI_DISCONNECT_COMPLETE, WiFiManager::DisconnectHandler } }); +const Map WiFiManager::sEventHandlerMap({ + { NET_EVENT_WIFI_SCAN_RESULT, WiFiManager::ScanResultHandler }, + { NET_EVENT_WIFI_SCAN_DONE, WiFiManager::ScanDoneHandler }, + { NET_EVENT_WIFI_CONNECT_RESULT, WiFiManager::ConnectHandler }, + { NET_EVENT_WIFI_DISCONNECT_RESULT, WiFiManager::DisconnectHandler }, + { NET_EVENT_WIFI_DISCONNECT_COMPLETE, WiFiManager::DisconnectHandler }, +}); void WiFiManager::WifiMgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface) { - if (0 == strcmp(iface->if_dev->dev->name, InetUtils::GetInterface()->if_dev->dev->name)) + if (iface == Instance().mNetIf) { Platform::UniquePtr eventData(new uint8_t[cb->info_length]); VerifyOrReturn(eventData); memcpy(eventData.get(), cb->info, cb->info_length); - sEventHandlerMap[mgmtEvent](std::move(eventData)); + sEventHandlerMap[mgmtEvent](std::move(eventData), cb->info_length); } } -CHIP_ERROR WiFiManager::Init() +void WiFiManager::IPv6MgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface) { - // TODO: consider moving these to ConnectivityManagerImpl to be prepared for handling multiple interfaces on a single device. - Inet::UDPEndPointImplSockets::SetMulticastGroupHandler([](Inet::InterfaceId interfaceId, const Inet::IPAddress & address, - Inet::UDPEndPointImplSockets::MulticastOperation operation) { - const in6_addr addr = InetUtils::ToZephyrAddr(address); - net_if * iface = InetUtils::GetInterface(interfaceId); - VerifyOrReturnError(iface != nullptr, INET_ERROR_UNKNOWN_INTERFACE); - - if (operation == Inet::UDPEndPointImplSockets::MulticastOperation::kJoin) - { - net_if_mcast_addr * maddr = net_if_ipv6_maddr_add(iface, &addr); - - if (maddr && !net_if_ipv6_maddr_is_joined(maddr) && !net_ipv6_is_addr_mcast_link_all_nodes(&addr)) - { - net_if_ipv6_maddr_join(iface, maddr); - } - } - else if (operation == Inet::UDPEndPointImplSockets::MulticastOperation::kLeave) - { - VerifyOrReturnError(net_ipv6_is_addr_mcast_link_all_nodes(&addr) || net_if_ipv6_maddr_rm(iface, &addr), - CHIP_ERROR_INVALID_ADDRESS); - } - else - { - return CHIP_ERROR_INCORRECT_STATE; - } + if (((mgmtEvent == NET_EVENT_IPV6_ADDR_ADD) || (mgmtEvent == NET_EVENT_IPV6_ADDR_DEL)) && cb->info) + { + IPv6AddressChangeHandler(cb->info); + } +} - return CHIP_NO_ERROR; - }); +CHIP_ERROR WiFiManager::Init() +{ + mNetIf = InetUtils::GetWiFiInterface(); + VerifyOrReturnError(mNetIf != nullptr, INET_ERROR_UNKNOWN_INTERFACE); net_mgmt_init_event_callback(&mWiFiMgmtClbk, WifiMgmtEventHandler, kWifiManagementEvents); + net_mgmt_init_event_callback(&mIPv6MgmtClbk, IPv6MgmtEventHandler, kIPv6ManagementEvents); + net_mgmt_add_event_callback(&mWiFiMgmtClbk); + net_mgmt_add_event_callback(&mIPv6MgmtClbk); ChipLogDetail(DeviceLayer, "WiFiManager has been initialized"); @@ -197,9 +198,6 @@ CHIP_ERROR WiFiManager::Init() CHIP_ERROR WiFiManager::Scan(const ByteSpan & ssid, ScanResultCallback resultCallback, ScanDoneCallback doneCallback, bool internalScan) { - net_if * iface = InetUtils::GetInterface(); - VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL); - mInternalScan = internalScan; mScanResultCallback = resultCallback; mScanDoneCallback = doneCallback; @@ -207,17 +205,22 @@ CHIP_ERROR WiFiManager::Scan(const ByteSpan & ssid, ScanResultCallback resultCal mWiFiState = WIFI_STATE_SCANNING; mSsidFound = false; - /* If the ssid is not null, it means the scan must target a specific SSID, and only include this one in the scan - * result. To do so, we save the requested ssid and we will filter the scan results accordingly in the scan done - * handler. */ - if ((ssid.size() > 0) && (!mInternalScan)) + wifi_scan_params * scanParams{ nullptr }; + size_t scanParamsSize{ 0 }; + + if (!ssid.empty()) { - mNetworkToScan.Erase(); - memcpy(mNetworkToScan.ssid, ssid.data(), ssid.size()); - mNetworkToScan.ssidLen = ssid.size(); + /* We must assume that the ssid is handled as a NULL-terminated string. + Note that the mScanSsidBuffer is initialized with zeros. */ + VerifyOrReturnError(ssid.size() < sizeof(mScanSsidBuffer), CHIP_ERROR_INVALID_ARGUMENT); + memcpy(mScanSsidBuffer, ssid.data(), ssid.size()); + mScanSsidBuffer[ssid.size()] = 0; // indicate the end of ssid string + mScanParams.ssids[0] = mScanSsidBuffer; + mScanParams.ssids[1] = nullptr; // indicate the end of ssids list + scanParams = &mScanParams; + scanParamsSize = sizeof(*scanParams); } - - if (0 != net_mgmt(NET_REQUEST_WIFI_SCAN, iface, NULL, 0)) + if (0 != net_mgmt(NET_REQUEST_WIFI_SCAN, mNetIf, scanParams, scanParamsSize)) { ChipLogError(DeviceLayer, "Scan request failed"); return CHIP_ERROR_INTERNAL; @@ -239,9 +242,7 @@ CHIP_ERROR WiFiManager::Connect(const ByteSpan & ssid, const ByteSpan & credenti { ChipLogDetail(DeviceLayer, "Connecting to WiFi network: %.*s", ssid.size(), ssid.data()); - mHandling.mOnConnectionSuccess = handling.mOnConnectionSuccess; - mHandling.mOnConnectionFailed = handling.mOnConnectionFailed; - mHandling.mConnectionTimeout = handling.mConnectionTimeout; + mHandling = handling; mWiFiState = WIFI_STATE_ASSOCIATING; @@ -258,11 +259,8 @@ CHIP_ERROR WiFiManager::Connect(const ByteSpan & ssid, const ByteSpan & credenti CHIP_ERROR WiFiManager::Disconnect() { - net_if * iface = InetUtils::GetInterface(); - VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL); - mApplicationDisconnectRequested = true; - int status = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, iface, NULL, 0); + int status = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, mNetIf, NULL, 0); if (status) { @@ -287,11 +285,9 @@ CHIP_ERROR WiFiManager::Disconnect() CHIP_ERROR WiFiManager::GetWiFiInfo(WiFiInfo & info) const { - net_if * iface = InetUtils::GetInterface(); - VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL); - struct wifi_iface_status status = { 0 }; + wifi_iface_status status = { 0 }; - if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, iface, &status, sizeof(struct wifi_iface_status))) + if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, mNetIf, &status, sizeof(wifi_iface_status))) { ChipLogError(DeviceLayer, "Status request failed"); return CHIP_ERROR_INTERNAL; @@ -316,22 +312,31 @@ CHIP_ERROR WiFiManager::GetWiFiInfo(WiFiInfo & info) const CHIP_ERROR WiFiManager::GetNetworkStatistics(NetworkStatistics & stats) const { net_stats_wifi data{}; - net_mgmt(NET_REQUEST_STATS_GET_WIFI, InetUtils::GetInterface(), &data, sizeof(data)); + net_mgmt(NET_REQUEST_STATS_GET_WIFI, mNetIf, &data, sizeof(data)); stats.mPacketMulticastRxCount = data.multicast.rx; stats.mPacketMulticastTxCount = data.multicast.tx; - stats.mPacketUnicastRxCount = data.pkts.rx - data.multicast.rx - data.broadcast.rx; - stats.mPacketUnicastTxCount = data.pkts.tx - data.multicast.tx - data.broadcast.tx; - stats.mBeaconsSuccessCount = data.sta_mgmt.beacons_rx; - stats.mBeaconsLostCount = data.sta_mgmt.beacons_miss; +#if CONFIG_CHIP_NXP_PLATFORM + /* Zephyr 3.6 doesn't support the unicast stat in net_stats_wifi struct */ + stats.mPacketUnicastRxCount = data.pkts.rx - data.multicast.rx - data.broadcast.rx; + stats.mPacketUnicastRxCount = data.pkts.tx - data.multicast.tx - data.broadcast.tx; +#else + stats.mPacketUnicastRxCount = data.unicast.rx; + stats.mPacketUnicastTxCount = data.unicast.tx; +#endif + stats.mBeaconsSuccessCount = data.sta_mgmt.beacons_rx; + stats.mBeaconsLostCount = data.sta_mgmt.beacons_miss; return CHIP_NO_ERROR; } -void WiFiManager::ScanResultHandler(Platform::UniquePtr data) +void WiFiManager::ScanResultHandler(Platform::UniquePtr data, size_t length) { + // Validate that input data size matches the expected one. + VerifyOrReturn(length == sizeof(wifi_scan_result)); + // Contrary to other handlers, offload accumulating of the scan results from the CHIP thread to the caller's thread - const struct wifi_scan_result * scanResult = reinterpret_cast(data.get()); + const wifi_scan_result * scanResult = reinterpret_cast(data.get()); if (Instance().mInternalScan && Instance().mWantedNetwork.GetSsidSpan().data_equal(ByteSpan(scanResult->ssid, scanResult->ssid_length))) @@ -369,45 +374,33 @@ void WiFiManager::ScanResultHandler(Platform::UniquePtr data) if (Instance().mScanResultCallback && !Instance().mInternalScan) { - /* Here we need to check if the scan is targeting a specific network and filter the scan result accordingly, - * to make sure only the targeted SSID is reported */ - if (Instance().mNetworkToScan.GetSsidSpan().size() == 0) - { - Instance().mScanResultCallback(ToScanResponse(scanResult)); - } - else if (Instance().mNetworkToScan.GetSsidSpan().data_equal(ByteSpan(scanResult->ssid, scanResult->ssid_length))) - { - Instance().mScanResultCallback(ToScanResponse(scanResult)); - } + Instance().mScanResultCallback(ToScanResponse(scanResult)); } } -void WiFiManager::ScanDoneHandler(Platform::UniquePtr data) +void WiFiManager::ScanDoneHandler(Platform::UniquePtr data, size_t length) { + // Validate that input data size matches the expected one. + VerifyOrReturn(length == sizeof(wifi_status)); + CHIP_ERROR err = SystemLayer().ScheduleLambda([capturedData = data.get()] { Platform::UniquePtr safePtr(capturedData); - uint8_t * rawData = safePtr.get(); - const wifi_status * status = reinterpret_cast(rawData); - WiFiRequestStatus requestStatus = static_cast(status->status); - - /* Reset the specific network to scan */ - if (Instance().mNetworkToScan.GetSsidSpan().size() > 0) - { - Instance().mNetworkToScan.Erase(); - } + uint8_t * rawData = safePtr.get(); + const wifi_status * status = reinterpret_cast(rawData); + ScanDoneStatus scanDoneStatus = status->status; - if (requestStatus == WiFiRequestStatus::FAILURE) + if (scanDoneStatus) { - ChipLogError(DeviceLayer, "Wi-Fi scan finalization failure (%d)", status->status); + ChipLogError(DeviceLayer, "Wi-Fi scan finalization failure (%d)", scanDoneStatus); } else { - ChipLogProgress(DeviceLayer, "Wi-Fi scan done (%d)", status->status); + ChipLogProgress(DeviceLayer, "Wi-Fi scan done"); } if (Instance().mScanDoneCallback && !Instance().mInternalScan) { - Instance().mScanDoneCallback(requestStatus); + Instance().mScanDoneCallback(scanDoneStatus); // restore the connection state from before the scan request was issued Instance().mWiFiState = Instance().mCachedWiFiState; return; @@ -416,6 +409,7 @@ void WiFiManager::ScanDoneHandler(Platform::UniquePtr data) // Internal scan is supposed to be followed by a connection request if the SSID has been found if (Instance().mInternalScan) { + if (!Instance().mSsidFound) { ChipLogProgress(DeviceLayer, "No requested SSID found"); @@ -430,12 +424,13 @@ void WiFiManager::ScanDoneHandler(Platform::UniquePtr data) net_if * iface = InetUtils::GetInterface(); VerifyOrReturn(nullptr != iface, CHIP_ERROR_INTERNAL); - if (net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, &(Instance().mWiFiParams.mParams), sizeof(wifi_connect_req_params))) + if (net_mgmt(NET_REQUEST_WIFI_CONNECT, Instance().mNetIf, &(Instance().mWiFiParams.mParams), + sizeof(wifi_connect_req_params))) { ChipLogError(DeviceLayer, "Connection request failed"); - if (Instance().mHandling.mOnConnectionFailed) + if (Instance().mHandling.mOnConnectionDone) { - Instance().mHandling.mOnConnectionFailed(); + Instance().mHandling.mOnConnectionDone(WIFI_STATUS_CONN_FAIL); } Instance().mWiFiState = WIFI_STATE_DISCONNECTED; return; @@ -455,38 +450,65 @@ void WiFiManager::ScanDoneHandler(Platform::UniquePtr data) void WiFiManager::SendRouterSolicitation(System::Layer * layer, void * param) { - net_if * iface = InetUtils::GetInterface(); - if (iface && iface->if_dev->link_addr.type == NET_LINK_ETHERNET) + net_if_start_rs(Instance().mNetIf); + Instance().mRouterSolicitationCounter++; + if (Instance().mRouterSolicitationCounter < kRouterSolicitationMaxCount) { - net_if_start_rs(iface); - Instance().mRouterSolicitationCounter++; - if (Instance().mRouterSolicitationCounter < kRouterSolicitationMaxCount) - { - DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(kRouterSolicitationIntervalMs), - SendRouterSolicitation, nullptr); - } - else - { - Instance().mRouterSolicitationCounter = 0; - } + DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(kRouterSolicitationIntervalMs), SendRouterSolicitation, + nullptr); + } + else + { + Instance().mRouterSolicitationCounter = 0; } } -void WiFiManager::ConnectHandler(Platform::UniquePtr data) +void WiFiManager::ConnectHandler(Platform::UniquePtr data, size_t length) { + using app::Clusters::WiFiNetworkDiagnostics::AssociationFailureCauseEnum; + + // Validate that input data size matches the expected one. + VerifyOrReturn(length == sizeof(wifi_status)); + CHIP_ERROR err = SystemLayer().ScheduleLambda([capturedData = data.get()] { Platform::UniquePtr safePtr(capturedData); - uint8_t * rawData = safePtr.get(); - const wifi_status * status = reinterpret_cast(rawData); - WiFiRequestStatus requestStatus = static_cast(status->status); + uint8_t * rawData = safePtr.get(); + const wifi_status * status = reinterpret_cast(rawData); + wifi_conn_status connStatus = status->conn_status; - if (requestStatus == WiFiRequestStatus::FAILURE || requestStatus == WiFiRequestStatus::TERMINATED) + if (connStatus) { ChipLogProgress(DeviceLayer, "Connection to WiFi network failed or was terminated by another request"); Instance().mWiFiState = WIFI_STATE_DISCONNECTED; - if (Instance().mHandling.mOnConnectionFailed) + if (Instance().mHandling.mOnConnectionDone) + { + Instance().mHandling.mOnConnectionDone(connStatus); + } + + WiFiDiagnosticsDelegate * delegate = GetDiagnosticDataProvider().GetWiFiDiagnosticsDelegate(); + if (delegate) { - Instance().mHandling.mOnConnectionFailed(); + uint16_t reason = Instance().GetLastDisconnectReason(); + uint8_t associationFailureCause; + + switch (connStatus) + { + case WIFI_STATUS_CONN_WRONG_PASSWORD: + associationFailureCause = to_underlying(AssociationFailureCauseEnum::kAuthenticationFailed); + break; + case WIFI_STATUS_CONN_FAIL: + case WIFI_STATUS_CONN_TIMEOUT: + associationFailureCause = to_underlying(AssociationFailureCauseEnum::kAssociationFailed); + break; + case WIFI_STATUS_CONN_AP_NOT_FOUND: + associationFailureCause = to_underlying(AssociationFailureCauseEnum::kSsidNotFound); + break; + default: + associationFailureCause = to_underlying(AssociationFailureCauseEnum::kUnknown); + break; + } + + delegate->OnAssociationFailureDetected(associationFailureCause, reason); } } else // The connection has been established successfully. @@ -498,9 +520,9 @@ void WiFiManager::ConnectHandler(Platform::UniquePtr data) ChipLogProgress(DeviceLayer, "Connected to WiFi network"); Instance().mWiFiState = WIFI_STATE_COMPLETED; - if (Instance().mHandling.mOnConnectionSuccess) + if (Instance().mHandling.mOnConnectionDone) { - Instance().mHandling.mOnConnectionSuccess(); + Instance().mHandling.mOnConnectionDone(connStatus); } Instance().PostConnectivityStatusChange(kConnectivity_Established); @@ -513,6 +535,13 @@ void WiFiManager::ConnectHandler(Platform::UniquePtr data) { ChipLogError(DeviceLayer, "Cannot post event [error: %s]", ErrorStr(error)); } + + WiFiDiagnosticsDelegate * delegate = GetDiagnosticDataProvider().GetWiFiDiagnosticsDelegate(); + if (delegate) + { + delegate->OnConnectionStatusChanged( + to_underlying(app::Clusters::WiFiNetworkDiagnostics::ConnectionStatusEnum::kConnected)); + } } // cleanup the provisioning data as it is configured per each connect request Instance().ClearStationProvisioningData(); @@ -525,13 +554,74 @@ void WiFiManager::ConnectHandler(Platform::UniquePtr data) } } -void WiFiManager::DisconnectHandler(Platform::UniquePtr) +void WiFiManager::DisconnectHandler(Platform::UniquePtr data, size_t length) { - SystemLayer().ScheduleLambda([] { + // Validate that input data size matches the expected one. + VerifyOrReturn(length == sizeof(wifi_status)); + + CHIP_ERROR err = SystemLayer().ScheduleLambda([capturedData = data.get()] { + Platform::UniquePtr safePtr(capturedData); + uint8_t * rawData = safePtr.get(); + const wifi_status * status = reinterpret_cast(rawData); + uint16_t reason; + + switch (status->disconn_reason) + { + case WIFI_REASON_DISCONN_UNSPECIFIED: + reason = WLAN_REASON_UNSPECIFIED; + break; + case WIFI_REASON_DISCONN_USER_REQUEST: + reason = WLAN_REASON_DEAUTH_LEAVING; + break; + case WIFI_REASON_DISCONN_AP_LEAVING: + reason = WLAN_REASON_DEAUTH_LEAVING; + break; + case WIFI_REASON_DISCONN_INACTIVITY: + reason = WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY; + break; + default: + reason = WLAN_REASON_UNSPECIFIED; + break; + } + Instance().SetLastDisconnectReason(reason); + ChipLogProgress(DeviceLayer, "WiFi station disconnected"); Instance().mWiFiState = WIFI_STATE_DISCONNECTED; Instance().PostConnectivityStatusChange(kConnectivity_Lost); + + WiFiDiagnosticsDelegate * delegate = GetDiagnosticDataProvider().GetWiFiDiagnosticsDelegate(); + if (delegate) + { + delegate->OnConnectionStatusChanged( + to_underlying(app::Clusters::WiFiNetworkDiagnostics::ConnectionStatusEnum::kNotConnected)); + delegate->OnDisconnectionDetected(reason); + } }); + + if (CHIP_NO_ERROR == err) + { + // the ownership has been transferred to the worker thread - release the buffer + data.release(); + } +} + +void WiFiManager::IPv6AddressChangeHandler(const void * data) +{ + const in6_addr * addr = reinterpret_cast(data); + + // Filter out link-local addresses that are not routable outside of a local network. + if (!net_ipv6_is_ll_addr(addr)) + { + // This is needed to send mDNS queries containing updated IPv6 addresses. + ChipDeviceEvent event; + event.Type = DeviceEventType::kDnssdRestartNeeded; + + CHIP_ERROR error = PlatformMgr().PostEvent(&event); + if (error != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Cannot post event: %" CHIP_ERROR_FORMAT, error.Format()); + } + } } WiFiManager::StationStatus WiFiManager::GetStationStatus() const @@ -597,11 +687,10 @@ System::Clock::Milliseconds32 WiFiManager::CalculateNextRecoveryTime() CHIP_ERROR WiFiManager::SetLowPowerMode(bool onoff) { - net_if * iface = InetUtils::GetInterface(); - VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL); + VerifyOrReturnError(nullptr != mNetIf, CHIP_ERROR_INTERNAL); wifi_ps_config currentConfig{}; - if (net_mgmt(NET_REQUEST_WIFI_PS_CONFIG, iface, ¤tConfig, sizeof(currentConfig))) + if (net_mgmt(NET_REQUEST_WIFI_PS_CONFIG, mNetIf, ¤tConfig, sizeof(currentConfig))) { ChipLogError(DeviceLayer, "Get current low power mode config request failed"); return CHIP_ERROR_INTERNAL; @@ -611,7 +700,7 @@ CHIP_ERROR WiFiManager::SetLowPowerMode(bool onoff) (currentConfig.ps_params.enabled == WIFI_PS_DISABLED && onoff == true)) { wifi_ps_params params{ .enabled = onoff ? WIFI_PS_ENABLED : WIFI_PS_DISABLED }; - if (net_mgmt(NET_REQUEST_WIFI_PS, iface, ¶ms, sizeof(params))) + if (net_mgmt(NET_REQUEST_WIFI_PS, mNetIf, ¶ms, sizeof(params))) { ChipLogError(DeviceLayer, "Set low power mode request failed"); return CHIP_ERROR_INTERNAL; @@ -624,5 +713,15 @@ CHIP_ERROR WiFiManager::SetLowPowerMode(bool onoff) return CHIP_NO_ERROR; } +void WiFiManager::SetLastDisconnectReason(uint16_t reason) +{ + mLastDisconnectedReason = reason; +} + +uint16_t WiFiManager::GetLastDisconnectReason() +{ + return mLastDisconnectedReason; +} + } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/Zephyr/wifi/WiFiManager.h b/src/platform/Zephyr/wifi/WiFiManager.h index d48536fb3d0da3..99bf777547a800 100644 --- a/src/platform/Zephyr/wifi/WiFiManager.h +++ b/src/platform/Zephyr/wifi/WiFiManager.h @@ -88,16 +88,16 @@ class Map class WiFiManager { public: - enum WiFiRequestStatus : int - { - SUCCESS = 0, - FAILURE = 1, - TERMINATED = 2 - }; + /* No copy, nor move. */ + WiFiManager(const WiFiManager &) = delete; + WiFiManager & operator=(const WiFiManager &) = delete; + WiFiManager(WiFiManager &&) = delete; + WiFiManager & operator=(WiFiManager &&) = delete; + using ScanDoneStatus = decltype(wifi_status::status); using ScanResultCallback = void (*)(const NetworkCommissioning::WiFiScanResponse &); - using ScanDoneCallback = void (*)(WiFiRequestStatus); - using ConnectionCallback = void (*)(); + using ScanDoneCallback = void (*)(const ScanDoneStatus &); + using ConnectionCallback = void (*)(const wifi_conn_status &); enum class StationStatus : uint8_t { @@ -120,8 +120,7 @@ class WiFiManager struct ConnectionHandling { - ConnectionCallback mOnConnectionSuccess{}; - ConnectionCallback mOnConnectionFailed{}; + ConnectionCallback mOnConnectionDone{}; System::Clock::Seconds32 mConnectionTimeout{}; }; @@ -182,12 +181,18 @@ class WiFiManager CHIP_ERROR ClearStationProvisioningData(); CHIP_ERROR Disconnect(); CHIP_ERROR GetWiFiInfo(WiFiInfo & info) const; + const WiFiNetwork & GetWantedNetwork() const { return mWantedNetwork; } CHIP_ERROR GetNetworkStatistics(NetworkStatistics & stats) const; void AbortConnectionRecovery(); CHIP_ERROR SetLowPowerMode(bool onoff); + void SetLastDisconnectReason(uint16_t reason); + uint16_t GetLastDisconnectReason(); private: - using NetEventHandler = void (*)(Platform::UniquePtr); + using NetEventHandler = void (*)(Platform::UniquePtr, size_t); + + WiFiManager() = default; + ~WiFiManager() = default; struct ConnectionParams { @@ -198,14 +203,18 @@ class WiFiManager constexpr static uint32_t kWifiManagementEvents = NET_EVENT_WIFI_SCAN_RESULT | NET_EVENT_WIFI_SCAN_DONE | NET_EVENT_WIFI_CONNECT_RESULT | NET_EVENT_WIFI_DISCONNECT_RESULT | NET_EVENT_WIFI_IFACE_STATUS; + constexpr static uint32_t kIPv6ManagementEvents = NET_EVENT_IPV6_ADDR_ADD | NET_EVENT_IPV6_ADDR_DEL; + // Event handling static void WifiMgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface); - static void ScanResultHandler(Platform::UniquePtr data); - static void ScanDoneHandler(Platform::UniquePtr data); - static void ConnectHandler(Platform::UniquePtr data); - static void DisconnectHandler(Platform::UniquePtr data); + static void IPv6MgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface); + static void ScanResultHandler(Platform::UniquePtr data, size_t length); + static void ScanDoneHandler(Platform::UniquePtr data, size_t length); + static void ConnectHandler(Platform::UniquePtr data, size_t length); + static void DisconnectHandler(Platform::UniquePtr data, size_t length); static void PostConnectivityStatusChange(ConnectivityChange changeType); static void SendRouterSolicitation(System::Layer * layer, void * param); + static void IPv6AddressChangeHandler(const void * data); // Connection Recovery feature // This feature allows re-scanning and re-connecting the connection to the known network after @@ -220,21 +229,25 @@ class WiFiManager void ResetRecoveryTime(); System::Clock::Milliseconds32 CalculateNextRecoveryTime(); + net_if * mNetIf{ nullptr }; ConnectionParams mWiFiParams{}; - ConnectionHandling mHandling; + ConnectionHandling mHandling{}; + wifi_scan_params mScanParams{}; + char mScanSsidBuffer[DeviceLayer::Internal::kMaxWiFiSSIDLength + 1] = { 0 }; wifi_iface_state mWiFiState; wifi_iface_state mCachedWiFiState; net_mgmt_event_callback mWiFiMgmtClbk{}; + net_mgmt_event_callback mIPv6MgmtClbk{}; ScanResultCallback mScanResultCallback{ nullptr }; ScanDoneCallback mScanDoneCallback{ nullptr }; WiFiNetwork mWantedNetwork{}; - WiFiNetwork mNetworkToScan{}; bool mInternalScan{ false }; uint8_t mRouterSolicitationCounter = 0; bool mSsidFound{ false }; uint32_t mConnectionRecoveryCounter{ 0 }; uint32_t mConnectionRecoveryTimeMs{ kConnectionRecoveryMinIntervalMs }; bool mApplicationDisconnectRequested{ false }; + uint16_t mLastDisconnectedReason = WLAN_REASON_UNSPECIFIED; static const Map sStatusMap; static const Map sEventHandlerMap; diff --git a/src/platform/Zephyr/wifi/ZephyrWifiDriver.cpp b/src/platform/Zephyr/wifi/ZephyrWifiDriver.cpp index 53b7fa684a2213..7404a0272297be 100644 --- a/src/platform/Zephyr/wifi/ZephyrWifiDriver.cpp +++ b/src/platform/Zephyr/wifi/ZephyrWifiDriver.cpp @@ -17,10 +17,13 @@ #include "ZephyrWifiDriver.h" +#include + #include #include #include +#include #include using namespace ::chip; @@ -103,8 +106,9 @@ CHIP_ERROR ZephyrWifiDriver::Init(NetworkStatusChangeCallback * networkStatusCha if (mStagingNetwork.IsConfigured()) { - WiFiManager::ConnectionHandling handling{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); }, - [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); }, + WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) { + Instance().OnNetworkConnStatusChanged(connStatus); + }, System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } }; ReturnErrorOnFailure( WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling)); @@ -113,8 +117,11 @@ CHIP_ERROR ZephyrWifiDriver::Init(NetworkStatusChangeCallback * networkStatusCha return CHIP_NO_ERROR; } -void ZephyrWifiDriver::OnNetworkStatusChanged(Status status) +void ZephyrWifiDriver::OnNetworkConnStatusChanged(const wifi_conn_status & connStatus) { + // TODO: check if we can report more accurate errors + Status status = connStatus ? Status::kUnknownError : Status::kSuccess; + if (status == Status::kSuccess) { ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Enabled); @@ -122,7 +129,23 @@ void ZephyrWifiDriver::OnNetworkStatusChanged(Status status) if (mpNetworkStatusChangeCallback) { - mpNetworkStatusChangeCallback->OnNetworkingStatusChange(status, NullOptional, NullOptional); + const uint8_t * ssid{}; + size_t ssidLen{}; + WiFiManager::WiFiInfo wifiInfo; + + if (CHIP_NO_ERROR == WiFiManager::Instance().GetWiFiInfo(wifiInfo)) + { + ssid = wifiInfo.mSsid; + ssidLen = wifiInfo.mSsidLen; + } + else + { + ssid = WiFiManager::Instance().GetWantedNetwork().ssid; + ssidLen = WiFiManager::Instance().GetWantedNetwork().ssidLen; + } + mpNetworkStatusChangeCallback->OnNetworkingStatusChange(status, MakeOptional(ByteSpan(ssid, ssidLen)), + connStatus ? MakeOptional(static_cast(connStatus)) + : NullOptional); } if (mpConnectCallback) @@ -158,12 +181,15 @@ CHIP_ERROR ZephyrWifiDriver::RevertConfiguration() // we are already connected to this network, so return prematurely return CHIP_NO_ERROR; } + + WiFiManager::Instance().Disconnect(); } if (mStagingNetwork.IsConfigured()) { - WiFiManager::ConnectionHandling handling{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); }, - [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); }, + WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) { + Instance().OnNetworkConnStatusChanged(connStatus); + }, System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } }; ReturnErrorOnFailure( WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling)); @@ -217,8 +243,9 @@ void ZephyrWifiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * call { Status status = Status::kSuccess; WiFiManager::StationStatus stationStatus; - WiFiManager::ConnectionHandling handling{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); }, - [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); }, + WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) { + Instance().OnNetworkConnStatusChanged(connStatus); + }, System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } }; VerifyOrExit(mpConnectCallback == nullptr, status = Status::kUnknownError); @@ -262,11 +289,10 @@ void ZephyrWifiDriver::LoadFromStorage() mStagingNetwork = network; } -void ZephyrWifiDriver::OnScanWiFiNetworkDone(WiFiManager::WiFiRequestStatus status) +void ZephyrWifiDriver::OnScanWiFiNetworkDone(const WiFiManager::ScanDoneStatus & status) { VerifyOrReturn(mScanCallback != nullptr); - mScanCallback->OnFinished(status == WiFiManager::WiFiRequestStatus::SUCCESS ? Status::kSuccess : Status::kUnknownError, - CharSpan(), &mScanResponseIterator); + mScanCallback->OnFinished(status ? Status::kUnknownError : Status::kSuccess, CharSpan(), &mScanResponseIterator); mScanCallback = nullptr; } @@ -280,7 +306,7 @@ void ZephyrWifiDriver::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * ca mScanCallback = callback; CHIP_ERROR error = WiFiManager::Instance().Scan( ssid, [](const WiFiScanResponse & response) { Instance().OnScanWiFiNetworkResult(response); }, - [](WiFiManager::WiFiRequestStatus status) { Instance().OnScanWiFiNetworkDone(status); }); + [](const WiFiManager::ScanDoneStatus & status) { Instance().OnScanWiFiNetworkDone(status); }); if (error != CHIP_NO_ERROR) { @@ -289,6 +315,13 @@ void ZephyrWifiDriver::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * ca } } +uint32_t ZephyrWifiDriver::GetSupportedWiFiBandsMask() const +{ + uint32_t bands = static_cast(1UL << chip::to_underlying(WiFiBandEnum::k2g4)); + bands |= static_cast(1UL << chip::to_underlying(WiFiBandEnum::k5g)); + return bands; +} + } // namespace NetworkCommissioning } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/Zephyr/wifi/ZephyrWifiDriver.h b/src/platform/Zephyr/wifi/ZephyrWifiDriver.h index 9096aa408b69bd..414311a07ec4bf 100644 --- a/src/platform/Zephyr/wifi/ZephyrWifiDriver.h +++ b/src/platform/Zephyr/wifi/ZephyrWifiDriver.h @@ -25,9 +25,9 @@ namespace chip { namespace DeviceLayer { namespace NetworkCommissioning { -constexpr uint8_t kMaxWiFiNetworks = 1; -constexpr uint8_t kWiFiScanNetworksTimeOutSeconds = 10; -constexpr uint8_t kWiFiConnectNetworkTimeoutSeconds = 35; +inline constexpr uint8_t kMaxWiFiNetworks = 1; +inline constexpr uint8_t kWiFiScanNetworksTimeOutSeconds = 10; +inline constexpr uint8_t kWiFiConnectNetworkTimeoutSeconds = 35; class ZephyrWifiScanResponseIterator : public Iterator { @@ -48,8 +48,8 @@ class ZephyrWifiDriver final : public WiFiDriver public: // Define non-volatile storage keys for SSID and password. // The naming convention is aligned with DefaultStorageKeyAllocator class. - static constexpr const char * kSsidKey = "g/wi/s"; - static constexpr const char * kPassKey = "g/wi/p"; + static constexpr char kSsidKey[] = "g/wi/s"; + static constexpr char kPassKey[] = "g/wi/p"; class WiFiNetworkIterator final : public NetworkIterator { @@ -86,6 +86,7 @@ class ZephyrWifiDriver final : public WiFiDriver Status AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) override; void ScanNetworks(ByteSpan ssid, ScanCallback * callback) override; + uint32_t GetSupportedWiFiBandsMask() const override; static ZephyrWifiDriver & Instance() { @@ -93,9 +94,9 @@ class ZephyrWifiDriver final : public WiFiDriver return sInstance; } - void OnNetworkStatusChanged(Status status); + void OnNetworkConnStatusChanged(const wifi_conn_status & connStatus); void OnScanWiFiNetworkResult(const WiFiScanResponse & result); - void OnScanWiFiNetworkDone(WiFiManager::WiFiRequestStatus status); + void OnScanWiFiNetworkDone(const WiFiManager::ScanDoneStatus & status); private: void LoadFromStorage(); diff --git a/src/platform/nxp/zephyr/ConnectivityManagerImpl.cpp b/src/platform/nxp/zephyr/ConnectivityManagerImpl.cpp index f927df2ac17480..4d183558c18953 100644 --- a/src/platform/nxp/zephyr/ConnectivityManagerImpl.cpp +++ b/src/platform/nxp/zephyr/ConnectivityManagerImpl.cpp @@ -17,11 +17,16 @@ #include +#include +#include +#include #include +#include #include -#include -#include +#ifndef CONFIG_ARCH_POSIX +#include +#endif #include @@ -34,15 +39,64 @@ #endif #if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#include #include #endif -using namespace ::chip; +using namespace ::chip::Inet; using namespace ::chip::DeviceLayer::Internal; namespace chip { namespace DeviceLayer { +namespace { +CHIP_ERROR JoinLeaveMulticastGroup(net_if * iface, const Inet::IPAddress & address, + UDPEndPointImplSockets::MulticastOperation operation) +{ +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD + if (net_if_l2(iface) == &NET_L2_GET_NAME(OPENTHREAD)) + { + const otIp6Address otAddress = ToOpenThreadIP6Address(address); + const auto handler = operation == UDPEndPointImplSockets::MulticastOperation::kJoin ? otIp6SubscribeMulticastAddress + : otIp6UnsubscribeMulticastAddress; + otError error; + + ThreadStackMgr().LockThreadStack(); + error = handler(openthread_get_default_instance(), &otAddress); + ThreadStackMgr().UnlockThreadStack(); + + return MapOpenThreadError(error); + } +#endif + +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI + // The following code should also be valid for other interface types, such as Ethernet, + // but they are not officially supported, so for now enable it for Wi-Fi only. + const in6_addr in6Addr = InetUtils::ToZephyrAddr(address); + + if (operation == UDPEndPointImplSockets::MulticastOperation::kJoin) + { + net_if_mcast_addr * maddr = net_if_ipv6_maddr_add(iface, &in6Addr); + + if (maddr && !net_if_ipv6_maddr_is_joined(maddr)) + { + net_if_ipv6_maddr_join(iface, maddr); + } + } + else if (operation == UDPEndPointImplSockets::MulticastOperation::kLeave) + { + VerifyOrReturnError(net_if_ipv6_maddr_rm(iface, &in6Addr), CHIP_ERROR_INVALID_ADDRESS); + } + else + { + return CHIP_ERROR_INCORRECT_STATE; + } +#endif + + return CHIP_NO_ERROR; +} +} // namespace + ConnectivityManagerImpl ConnectivityManagerImpl::sInstance; CHIP_ERROR ConnectivityManagerImpl::_Init() @@ -53,6 +107,28 @@ CHIP_ERROR ConnectivityManagerImpl::_Init() #if CHIP_DEVICE_CONFIG_ENABLE_WIFI ReturnErrorOnFailure(InitWiFi()); #endif + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD || CHIP_DEVICE_CONFIG_ENABLE_WIFI + UDPEndPointImplSockets::SetMulticastGroupHandler( + [](InterfaceId interfaceId, const IPAddress & address, UDPEndPointImplSockets::MulticastOperation operation) { + if (interfaceId.IsPresent()) + { + net_if * iface = InetUtils::GetInterface(interfaceId); + VerifyOrReturnError(iface != nullptr, INET_ERROR_UNKNOWN_INTERFACE); + + return JoinLeaveMulticastGroup(iface, address, operation); + } + + // If the interface is not specified, join or leave the multicast group on all interfaces. + for (int i = 1; net_if * iface = net_if_get_by_index(i); i++) + { + ReturnErrorOnFailure(JoinLeaveMulticastGroup(iface, address, operation)); + } + + return CHIP_NO_ERROR; + }); +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD || CHIP_DEVICE_CONFIG_ENABLE_WIFI + return CHIP_NO_ERROR; } diff --git a/src/python_testing/TC_ECOINFO_2_1.py b/src/python_testing/TC_ECOINFO_2_1.py index 8397402983d163..5b952647ab4113 100644 --- a/src/python_testing/TC_ECOINFO_2_1.py +++ b/src/python_testing/TC_ECOINFO_2_1.py @@ -115,6 +115,11 @@ async def test_TC_ECOINFO_2_1(self): self.print_step(0, "Commissioning, already done") + pause_for_pre_condition = self.user_params.get("pause_for_pre_condition", False) + if pause_for_pre_condition: + self.wait_for_user_input( + "Paused test to allow for manufacturer to satisfy precondition where one or more bridged devices of a supported type is connected to DUT") + self.step(1) endpoint_wild_card_read = await dev_ctrl.ReadAttribute(dut_node_id, [(Clusters.EcosystemInformation.Attributes.ClusterRevision)]) list_of_endpoints = list(endpoint_wild_card_read.keys()) diff --git a/src/python_testing/TC_SWTCH.py b/src/python_testing/TC_SWTCH.py index d6c4694560b16d..8f9a694db7c496 100644 --- a/src/python_testing/TC_SWTCH.py +++ b/src/python_testing/TC_SWTCH.py @@ -44,6 +44,8 @@ logger = logging.getLogger(__name__) +SIMULATED_LONG_PRESS_LENGTH_SECONDS = 2.0 + def bump_substep(step: str) -> str: """Given a string like "5a", bump it to "5b", or "6c" to "6d" """ @@ -94,7 +96,8 @@ def _send_multi_press_named_pipe_command(self, endpoint_id: int, number_of_press def _send_long_press_named_pipe_command(self, endpoint_id: int, pressed_position: int, feature_map: int): command_dict = {"Name": "SimulateLongPress", "EndpointId": endpoint_id, - "ButtonId": pressed_position, "LongPressDelayMillis": 5000, "LongPressDurationMillis": 5500, "FeatureMap": feature_map} + "ButtonId": pressed_position, "LongPressDelayMillis": int(1000 * (SIMULATED_LONG_PRESS_LENGTH_SECONDS - 0.5)), + "LongPressDurationMillis": int(1000 * SIMULATED_LONG_PRESS_LENGTH_SECONDS), "FeatureMap": feature_map} self._send_named_pipe_command(command_dict) def _send_latching_switch_named_pipe_command(self, endpoint_id: int, new_position: int): @@ -168,7 +171,9 @@ def _ask_for_release(self): prompt_msg="Release the button." ) else: - time.sleep(self.keep_pressed_delay/1000) + # This will await for the events to be generated properly. Note that there is a bit of a + # race here for the button simulator, but this race is extremely unlikely to be lost. + time.sleep(SIMULATED_LONG_PRESS_LENGTH_SECONDS + 0.5) def _await_sequence_of_events(self, event_queue: queue.Queue, endpoint_id: int, sequence: list[ClusterObjects.ClusterEvent], timeout_sec: float): start_time = time.time() @@ -373,7 +378,6 @@ async def test_TC_SWTCH_2_3(self): self.step(5) # We're using a long press here with a very long duration (in computer-land). This will let us check the intermediate values. # This is 1s larger than the subscription ceiling - self.keep_pressed_delay = 6000 self.pressed_position = 1 self._ask_for_keep_pressed(endpoint_id, self.pressed_position, feature_map) event_listener.wait_for_event_report(cluster.Events.InitialPress) @@ -595,7 +599,7 @@ def steps_TC_SWTCH_2_5(self): @staticmethod def should_run_SWTCH_2_5(wildcard, endpoint): msm = has_feature(Clusters.Switch, Clusters.Switch.Bitmaps.Feature.kMomentarySwitchMultiPress) - asf = has_feature(Clusters.Switch, 0x20) + asf = has_feature(Clusters.Switch, Clusters.Switch.Bitmaps.Feature.kActionSwitch) return msm(wildcard, endpoint) and not asf(wildcard, endpoint) @per_endpoint_test(should_run_SWTCH_2_5) diff --git a/src/python_testing/TC_TSTAT_4_2.py b/src/python_testing/TC_TSTAT_4_2.py index b133b1be2c8f22..641c827b388882 100644 --- a/src/python_testing/TC_TSTAT_4_2.py +++ b/src/python_testing/TC_TSTAT_4_2.py @@ -210,6 +210,10 @@ def steps_TC_TSTAT_4_2(self) -> list[TestStep]: "Verify that the write request is rejected"), TestStep("16", "TH starts an atomic write, and before it's complete, TH2 removes TH's fabric; TH2 then opens an atomic write", "Verify that the atomic request is successful"), + TestStep("17", "TH writes to the Presets attribute with a preset that has a presetScenario not present in PresetTypes attribute", + "Verify that the write request returned CONSTRAINT_ERROR (0x87)."), + TestStep("18", "TH writes to the Presets attribute such that the total number of presets is greater than the number of presets supported", + "Verify that the write request returned RESOURCE_EXHAUSTED (0x89)."), ] return steps @@ -469,7 +473,86 @@ async def test_TC_TSTAT_4_2(self): # Roll back await self.send_atomic_request_rollback_command() - # TODO: Add tests for the total number of Presets exceeds the NumberOfPresets supported. Also Add tests for adding presets with preset scenario not present in PresetTypes. + self.step("17") + 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")): + + # Read the PresetTypes to get the preset scenarios supported by the Thermostat. + presetTypes = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.PresetTypes) + + scenarioNotPresent = None + + # Find a preset scenario not present in PresetTypes to run this test. + for scenario in cluster.Enums.PresetScenarioEnum: + foundMatchingScenario = False + for presetType in presetTypes: + if presetType.presetScenario == scenario: + foundMatchingScenario = True + break + if not foundMatchingScenario: + scenarioNotPresent = scenario + break + + if scenarioNotPresent is None: + logger.info( + "Couldn't run test step 17 since all preset types in PresetScenarioEnum are supported by this Thermostat") + else: + test_presets = new_presets_with_handle.copy() + test_presets.append(cluster.Structs.PresetStruct(presetHandle=NullValue, presetScenario=scenarioNotPresent, + name="Preset", coolingSetpoint=2500, heatingSetpoint=1700, builtIn=False)) + + # Send the AtomicRequest begin command + await self.send_atomic_request_begin_command() + + await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.ConstraintError) + + # Clear state for next test. + await self.send_atomic_request_rollback_command() + + self.step("18") + 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")): + + # Read the numberOfPresets supported. + numberOfPresetsSupported = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.NumberOfPresets) + + # Read the PresetTypes to get the preset scenarios to build the Presets list. + presetTypes = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.PresetTypes) + + # Read the Presets to copy the existing presets into our testPresets list below. + presets = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.Presets) + + # Calculate the length of the Presets list that could be created using the preset scenarios in PresetTypes and numberOfPresets supported for each scenario. + totalExpectedPresetsLength = 0 + for presetType in presetTypes: + totalExpectedPresetsLength += presetType.numberOfPresets + + if totalExpectedPresetsLength > numberOfPresetsSupported: + testPresets = [] + for presetType in presetTypes: + scenario = presetType.presetScenario + + # For each supported scenario, copy all the existing presets that match it, then add more presets + # until we hit the cap on the number of presets for that scenario. + presetsAddedForScenario = 0 + for preset in presets: + if scenario == preset.presetScenario: + testPresets.append(preset) + presetsAddedForScenario = presetsAddedForScenario + 1 + + while presetsAddedForScenario < presetType.numberOfPresets: + testPresets.append(cluster.Structs.PresetStruct(presetHandle=NullValue, presetScenario=scenario, + name="Preset", coolingSetpoint=2500, heatingSetpoint=1700, builtIn=False)) + presetsAddedForScenario = presetsAddedForScenario + 1 + + # Send the AtomicRequest begin command + await self.send_atomic_request_begin_command() + + await self.write_presets(endpoint=endpoint, presets=testPresets, expected_status=Status.ResourceExhausted) + + # Clear state for next test. + await self.send_atomic_request_rollback_command() + else: + logger.info( + "Couldn't run test step 18 since there are not enough preset types to build a Presets list that exceeds the number of presets supported") if __name__ == "__main__": diff --git a/src/python_testing/matter_testing_infrastructure/metadata_parser/metadata.py b/src/python_testing/matter_testing_infrastructure/metadata_parser/metadata.py index 050f20f9920d6b..f16d3e9803e825 100644 --- a/src/python_testing/matter_testing_infrastructure/metadata_parser/metadata.py +++ b/src/python_testing/matter_testing_infrastructure/metadata_parser/metadata.py @@ -33,6 +33,7 @@ class Metadata: app: str app_args: str script_args: str + script_start_delay: int = 0 factoryreset: bool = False factoryreset_app_only: bool = False script_gdb: bool = False @@ -60,6 +61,9 @@ def copy_from_dict(self, attr_dict: Dict[str, Any]) -> None: if "script-args" in attr_dict: self.script_args = attr_dict["script-args"] + if "script-start-delay" in attr_dict: + self.script_start_delay = int(attr_dict["script-start-delay"]) + if "py_script_path" in attr_dict: self.py_script_path = attr_dict["py_script_path"] @@ -187,6 +191,7 @@ def parse_script(self, py_script_path: str) -> List[Metadata]: app=attr.get("app", ""), app_args=attr.get("app_args", ""), script_args=attr.get("script_args", ""), + script_start_delay=int(attr.get("script_start_delay", 0)), factoryreset=bool(attr.get("factoryreset", False)), factoryreset_app_only=bool(attr.get("factoryreset_app_only", False)), script_gdb=bool(attr.get("script_gdb", False)), diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index c096144d09902e..bd1b32e671ad79 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -1438,11 +1438,21 @@ def wait_for_user_input(self, Returns: str: User input or none if input is closed. """ + + # TODO(#31928): Remove any assumptions of test params for endpoint ID. + + # Get the endpoint user param instead of `--endpoint-id` result, if available, temporarily. + endpoint_id = self.user_params.get("endpoint", None) + if endpoint_id is None or not isinstance(endpoint_id, int): + endpoint_id = self.matter_test_config.endpoint + if self.runner_hook: + # TODO(#31928): Add endpoint support to hooks. self.runner_hook.show_prompt(msg=prompt_msg, placeholder=prompt_msg_placeholder, default_value=default_value) - logging.info("========= USER PROMPT =========") + + logging.info(f"========= USER PROMPT for Endpoint {endpoint_id} =========") logging.info(f">>> {prompt_msg.rstrip()} (press enter to confirm)") try: return input() @@ -1939,6 +1949,9 @@ def _has_attribute(wildcard, endpoint, attribute: ClusterObjects.ClusterAttribut cluster = get_cluster_from_attribute(attribute) try: attr_list = wildcard.attributes[endpoint][cluster][cluster.Attributes.AttributeList] + if not isinstance(attr_list, list): + asserts.fail( + f"Failed to read mandatory AttributeList attribute value for cluster {cluster} on endpoint {endpoint}: {attr_list}.") return attribute.attribute_id in attr_list except KeyError: return False @@ -1968,9 +1981,13 @@ def has_attribute(attribute: ClusterObjects.ClusterAttributeDescriptor) -> Endpo return partial(_has_attribute, attribute=attribute) -def _has_feature(wildcard, endpoint, cluster: ClusterObjects.ClusterObjectDescriptor, feature: IntFlag) -> bool: +def _has_feature(wildcard, endpoint: int, cluster: ClusterObjects.ClusterObjectDescriptor, feature: IntFlag) -> bool: try: feature_map = wildcard.attributes[endpoint][cluster][cluster.Attributes.FeatureMap] + if not isinstance(feature_map, int): + asserts.fail( + f"Failed to read mandatory FeatureMap attribute value for cluster {cluster} on endpoint {endpoint}: {feature_map}.") + return (feature & feature_map) != 0 except KeyError: return False @@ -1984,8 +2001,8 @@ def has_feature(cluster: ClusterObjects.ClusterObjectDescriptor, feature: IntFla EP0: cluster A, B, C EP1: cluster D with feature F0 - EP2, cluster D with feature F0 - EP3, cluster D without feature F0 + EP2: cluster D with feature F0 + EP3: cluster D without feature F0 And the following test specification: @per_endpoint_test(has_feature(Clusters.D.Bitmaps.Feature.F0)) diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h index 998a3c931b42b1..b1a50bcd44a722 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h @@ -2155,7 +2155,7 @@ enum class StatusCode : uint8_t // Bitmap for Feature enum class Feature : uint32_t { - kNoFeatures = 0x0, + kDirectModeChange = 0x10000, }; } // namespace RvcRunMode @@ -2188,7 +2188,7 @@ enum class StatusCode : uint8_t // Bitmap for Feature enum class Feature : uint32_t { - kNoFeatures = 0x0, + kDirectModeChange = 0x10000, }; } // namespace RvcCleanMode diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp index c3947539864e2c..4be7782087f3d6 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp @@ -28973,8 +28973,6 @@ CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; encoder.Encode(to_underlying(Fields::kRequestId), requestId); encoder.Encode(to_underlying(Fields::kResponseTimeoutSeconds), responseTimeoutSeconds); - encoder.Encode(to_underlying(Fields::kIpAddress), ipAddress); - encoder.Encode(to_underlying(Fields::kPort), port); return encoder.Finalize(); } @@ -29000,14 +28998,6 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) { err = DataModel::Decode(reader, responseTimeoutSeconds); } - else if (__context_tag == to_underlying(Fields::kIpAddress)) - { - err = DataModel::Decode(reader, ipAddress); - } - else if (__context_tag == to_underlying(Fields::kPort)) - { - err = DataModel::Decode(reader, port); - } else { } diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h index 3309b8ee41878b..78022f3ac6ce11 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h @@ -42016,8 +42016,6 @@ enum class Fields : uint8_t { kRequestId = 0, kResponseTimeoutSeconds = 1, - kIpAddress = 2, - kPort = 3, }; struct Type @@ -42029,8 +42027,6 @@ struct Type uint64_t requestId = static_cast(0); uint16_t responseTimeoutSeconds = static_cast(0); - Optional ipAddress; - Optional port; CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; @@ -42047,8 +42043,6 @@ struct DecodableType uint64_t requestId = static_cast(0); uint16_t responseTimeoutSeconds = static_cast(0); - Optional ipAddress; - Optional port; CHIP_ERROR Decode(TLV::TLVReader & reader); }; }; // namespace CommissionNode diff --git a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h index e145de431ed5b1..f50b5721bce5d5 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h @@ -13886,8 +13886,6 @@ class CommissionerControlCommissionNode : public ClusterCommand { AddArgument("RequestId", 0, UINT64_MAX, &mRequest.requestId); AddArgument("ResponseTimeoutSeconds", 0, UINT16_MAX, &mRequest.responseTimeoutSeconds); - AddArgument("IpAddress", &mRequest.ipAddress); - AddArgument("Port", 0, UINT16_MAX, &mRequest.port); ClusterCommand::AddArguments(); } diff --git a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h index e8aa8848588f3c..a77ea1f918b90b 100644 --- a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h @@ -164752,12 +164752,6 @@ class CommissionerControlCommissionNode : public ClusterCommand { #endif // MTR_ENABLE_PROVISIONAL #if MTR_ENABLE_PROVISIONAL AddArgument("ResponseTimeoutSeconds", 0, UINT16_MAX, &mRequest.responseTimeoutSeconds); -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - AddArgument("IpAddress", &mRequest.ipAddress); -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - AddArgument("Port", 0, UINT16_MAX, &mRequest.port); #endif // MTR_ENABLE_PROVISIONAL ClusterCommand::AddArguments(); } @@ -164778,20 +164772,6 @@ class CommissionerControlCommissionNode : public ClusterCommand { #endif // MTR_ENABLE_PROVISIONAL #if MTR_ENABLE_PROVISIONAL params.responseTimeoutSeconds = [NSNumber numberWithUnsignedShort:mRequest.responseTimeoutSeconds]; -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - if (mRequest.ipAddress.HasValue()) { - params.ipAddress = [NSData dataWithBytes:mRequest.ipAddress.Value().data() length:mRequest.ipAddress.Value().size()]; - } else { - params.ipAddress = nil; - } -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - if (mRequest.port.HasValue()) { - params.port = [NSNumber numberWithUnsignedShort:mRequest.port.Value()]; - } else { - params.port = nil; - } #endif // MTR_ENABLE_PROVISIONAL uint16_t repeatCount = mRepeatCount.ValueOr(1); uint16_t __block responsesNeeded = repeatCount;