diff --git a/.vscode/settings.json b/.vscode/settings.json index ac8bf404d4ec08..a8e2b99d4e8a70 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -117,6 +117,13 @@ "random": "cpp", "thread": "cpp" }, + // Configure paths or glob patterns to exclude from file watching. + "files.watcherExclude": { + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "out/": true, + "**/third_party/**": true + }, "files.eol": "\n", "editor.formatOnSave": true, "better-comments.tags": [ diff --git a/examples/all-clusters-app/nxp/mw320/README.md b/examples/all-clusters-app/nxp/mw320/README.md index 14699b15a29a48..af1c09621398d8 100755 --- a/examples/all-clusters-app/nxp/mw320/README.md +++ b/examples/all-clusters-app/nxp/mw320/README.md @@ -49,6 +49,16 @@ Note: 2. "source third_party/connectedhomeip/scripts/activate.sh" can be omitted if your environment is already setup without issues. +Tinycrypt ECC operations: + +Note: This solution is temporary. + +In order to use the tinycrypt ecc operations, use the following build arguments: + +``` +$ gn gen out/debug --args='treat_warnings_as_errors=false mbedtls_repo="//third_party/connectedhomeip/third_party/nxp/libs/mbedtls" mbedtls_use_tinycrypt=true' +``` + ## Flashing diff --git a/examples/all-clusters-app/nxp/mw320/main.cpp b/examples/all-clusters-app/nxp/mw320/main.cpp index bf7f1fc454fd4a..52fbb40964aeff 100644 --- a/examples/all-clusters-app/nxp/mw320/main.cpp +++ b/examples/all-clusters-app/nxp/mw320/main.cpp @@ -1411,8 +1411,7 @@ static void OnSwitchAttributeChangeCallback(EndpointId endpointId, AttributeId a /* Callback to receive the cluster modification event */ -void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & path, uint8_t mask, uint8_t type, uint16_t size, - uint8_t * value) +void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & path, uint8_t type, uint16_t size, uint8_t * value) { PRINTF("==> MatterPostAttributeChangeCallback, cluster: %x, attr: %x, size: %d \r\n", path.mClusterId, path.mAttributeId, size); // path.mEndpointId, path.mClusterId, path.mAttributeId, mask, type, size, value diff --git a/examples/lighting-app/bouffalolab/bl602/README.md b/examples/lighting-app/bouffalolab/bl602/README.md index 2bc2e05a371abd..22873801a823c9 100644 --- a/examples/lighting-app/bouffalolab/bl602/README.md +++ b/examples/lighting-app/bouffalolab/bl602/README.md @@ -30,28 +30,7 @@ The steps in this document were validated on Ubuntu 18.04 and 20.04. source ./scripts/activate.sh ``` -## Build the image - -- Build the example application: - - ``` - connectedhomeip$ ./scripts/build/build_examples.py --target bl602-light build - ``` - - Generated files - - ``` - connectedhomeip/out/bl602-light/chip-bl602-lighting-example.bin - ``` - - To delete generated executable, libraries and object files use: - - ``` - cd ~/connectedhomeip/ - rm -rf out/ - ``` - -## Flash the board +## Build the image and flash the board - Build the [lighting-app](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/bouffalolab/bl602) @@ -94,7 +73,7 @@ The steps in this document were validated on Ubuntu 18.04 and 20.04. `/dev/ttyACM0`: ``` -picocom -b 2000000 /dev/ttyACM0 +picocom -b 115200 /dev/ttyACM0 ``` 2.To reset the board, press the RESET button, and you will see the log in the @@ -113,16 +92,16 @@ remote device, as well as the network credentials to use. The command below uses the default values hard-coded into the debug versions of the BL602 lighting-app to commission it onto a Wi-Fi network: -``` -$ sudo ./chip-tool pairing ble-wifi 1 ${SSID} ${PASSWORD} 20202021 3840 - - Parameters: - 1. Discriminator: 3840 - 2. Setup-pin-code: 20202021 - 3. Node ID: 1 - 4. SSID : Wi-Fi SSID - 5. PASSWORD : Wi-Fi Password -``` + ``` + $ sudo ./chip-tool pairing ble-wifi 1 ${SSID} ${PASSWORD} 20202021 3840 + + Parameters: + 1. Discriminator: 3840 + 2. Setup-pin-code: 20202021 + 3. Node ID: 1 + 4. SSID : Wi-Fi SSID + 5. PASSWORD : Wi-Fi Password + ``` ### Cluster control diff --git a/examples/lock-app/nrfconnect/Kconfig b/examples/lock-app/nrfconnect/Kconfig index ddfeff16127be7..e9dfd7de6eff89 100644 --- a/examples/lock-app/nrfconnect/Kconfig +++ b/examples/lock-app/nrfconnect/Kconfig @@ -15,6 +15,18 @@ # mainmenu "Matter nRF Connect Lock Example Application" +config LOCK_NUM_USERS + int "Maximum number of users supported by lock" + default 10 + +config LOCK_NUM_CREDENTIALS + int "Maximum number of credentials supported by lock" + default 20 + +config LOCK_NUM_CREDENTIALS_PER_USER + int "Maximum number of credentials per user supported by lock" + default 3 + config STATE_LEDS bool "Use LEDs to indicate the device state" default y diff --git a/examples/lock-app/nrfconnect/main/BoltLockManager.cpp b/examples/lock-app/nrfconnect/main/BoltLockManager.cpp index a220dae31ece86..0a860818b0e5ce 100644 --- a/examples/lock-app/nrfconnect/main/BoltLockManager.cpp +++ b/examples/lock-app/nrfconnect/main/BoltLockManager.cpp @@ -23,6 +23,8 @@ #include "AppEvent.h" #include "AppTask.h" +using namespace chip; + BoltLockManager BoltLockManager::sLock; void BoltLockManager::Init(StateChangeCallback callback) @@ -33,6 +35,118 @@ void BoltLockManager::Init(StateChangeCallback callback) k_timer_user_data_set(&mActuatorTimer, this); } +bool BoltLockManager::GetUser(uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) const +{ + // userIndex is guaranteed by the caller to be between 1 and CONFIG_LOCK_NUM_USERS + user = mUsers[userIndex - 1]; + + ChipLogProgress(Zcl, "Getting lock user %u: %s", static_cast(userIndex), + user.userStatus == DlUserStatus::kAvailable ? "available" : "occupied"); + + return true; +} + +bool BoltLockManager::SetUser(uint16_t userIndex, FabricIndex creator, FabricIndex modifier, const CharSpan & userName, + uint32_t uniqueId, DlUserStatus userStatus, DlUserType userType, DlCredentialRule credentialRule, + const DlCredential * credentials, size_t totalCredentials) +{ + // userIndex is guaranteed by the caller to be between 1 and CONFIG_LOCK_NUM_USERS + UserData & userData = mUserData[userIndex - 1]; + auto & user = mUsers[userIndex - 1]; + + VerifyOrReturnError(userName.size() <= DOOR_LOCK_MAX_USER_NAME_SIZE, false); + VerifyOrReturnError(totalCredentials <= CONFIG_LOCK_NUM_CREDENTIALS_PER_USER, false); + + Platform::CopyString(userData.mName, userName); + memcpy(userData.mCredentials, credentials, totalCredentials * sizeof(DlCredential)); + + user.userName = CharSpan(userData.mName, userName.size()); + user.credentials = Span(userData.mCredentials, totalCredentials); + user.userUniqueId = uniqueId; + user.userStatus = userStatus; + user.userType = userType; + user.credentialRule = credentialRule; + user.creationSource = DlAssetSource::kMatterIM; + user.createdBy = creator; + user.modificationSource = DlAssetSource::kMatterIM; + user.lastModifiedBy = modifier; + + ChipLogProgress(Zcl, "Setting lock user %u: %s", static_cast(userIndex), + userStatus == DlUserStatus::kAvailable ? "available" : "occupied"); + + return true; +} + +bool BoltLockManager::GetCredential(uint16_t credentialIndex, DlCredentialType credentialType, + EmberAfPluginDoorLockCredentialInfo & credential) const +{ + VerifyOrReturnError(credentialIndex > 0 && credentialIndex <= CONFIG_LOCK_NUM_CREDENTIALS, false); + + credential = mCredentials[credentialIndex - 1]; + + ChipLogProgress(Zcl, "Getting lock credential %u: %s", static_cast(credentialIndex), + credential.status == DlCredentialStatus::kAvailable ? "available" : "occupied"); + + return true; +} + +bool BoltLockManager::SetCredential(uint16_t credentialIndex, FabricIndex creator, FabricIndex modifier, + DlCredentialStatus credentialStatus, DlCredentialType credentialType, const ByteSpan & secret) +{ + VerifyOrReturnError(credentialIndex > 0 && credentialIndex <= CONFIG_LOCK_NUM_CREDENTIALS, false); + VerifyOrReturnError(secret.size() <= kMaxCredentialLength, false); + + CredentialData & credentialData = mCredentialData[credentialIndex - 1]; + auto & credential = mCredentials[credentialIndex - 1]; + + if (!secret.empty()) + { + memcpy(credentialData.mSecret.Alloc(secret.size()).Get(), secret.data(), secret.size()); + } + + credential.status = credentialStatus; + credential.credentialType = credentialType; + credential.credentialData = ByteSpan(credentialData.mSecret.Get(), secret.size()); + credential.creationSource = DlAssetSource::kMatterIM; + credential.createdBy = creator; + credential.modificationSource = DlAssetSource::kMatterIM; + credential.lastModifiedBy = modifier; + + ChipLogProgress(Zcl, "Setting lock credential %u: %s", static_cast(credentialIndex), + credential.status == DlCredentialStatus::kAvailable ? "available" : "occupied"); + + return true; +} + +bool BoltLockManager::ValidatePIN(const Optional & pinCode, DlOperationError & err) const +{ + // Optionality of the PIN code is validated by the caller, so assume it is OK not to provide the PIN code. + if (!pinCode.HasValue()) + { + return true; + } + + // Check the PIN code + for (const auto & credential : mCredentials) + { + if (credential.status == DlCredentialStatus::kAvailable || credential.credentialType != DlCredentialType::kPin) + { + continue; + } + + if (credential.credentialData.data_equal(pinCode.Value())) + { + ChipLogDetail(Zcl, "Valid lock PIN code provided"); + return true; + } + } + + ChipLogDetail(Zcl, "Invalid lock PIN code provided"); + err = DlOperationError::kInvalidCredential; + + return false; +} + void BoltLockManager::Lock(OperationSource source) { VerifyOrReturn(mState != State::kLockingCompleted); diff --git a/examples/lock-app/nrfconnect/main/ZclCallbacks.cpp b/examples/lock-app/nrfconnect/main/ZclCallbacks.cpp index b56d6aa8ee8888..ccb5526344f05c 100644 --- a/examples/lock-app/nrfconnect/main/ZclCallbacks.cpp +++ b/examples/lock-app/nrfconnect/main/ZclCallbacks.cpp @@ -49,34 +49,63 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & } } -bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Optional & pinCode, DlOperationError & err) +bool emberAfPluginDoorLockGetUser(EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) { - return true; + return BoltLockMgr().GetUser(userIndex, user); } -bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Optional & pinCode, - DlOperationError & err) +bool emberAfPluginDoorLockSetUser(EndpointId endpointId, uint16_t userIndex, FabricIndex creator, FabricIndex modifier, + const CharSpan & userName, uint32_t uniqueId, DlUserStatus userStatus, DlUserType userType, + DlCredentialRule credentialRule, const DlCredential * credentials, size_t totalCredentials) { - return true; + return BoltLockMgr().SetUser(userIndex, creator, modifier, userName, uniqueId, userStatus, userType, credentialRule, + credentials, totalCredentials); +} + +bool emberAfPluginDoorLockGetCredential(EndpointId endpointId, uint16_t credentialIndex, DlCredentialType credentialType, + EmberAfPluginDoorLockCredentialInfo & credential) +{ + return BoltLockMgr().GetCredential(credentialIndex, credentialType, credential); +} + +bool emberAfPluginDoorLockSetCredential(EndpointId endpointId, uint16_t credentialIndex, FabricIndex creator, FabricIndex modifier, + DlCredentialStatus credentialStatus, DlCredentialType credentialType, + const ByteSpan & secret) +{ + return BoltLockMgr().SetCredential(credentialIndex, creator, modifier, credentialStatus, credentialType, secret); +} + +bool emberAfPluginDoorLockOnDoorLockCommand(EndpointId endpointId, const Optional & pinCode, DlOperationError & err) +{ + return BoltLockMgr().ValidatePIN(pinCode, err); +} + +bool emberAfPluginDoorLockOnDoorUnlockCommand(EndpointId endpointId, const Optional & pinCode, DlOperationError & err) +{ + return BoltLockMgr().ValidatePIN(pinCode, err); } void emberAfDoorLockClusterInitCallback(EndpointId endpoint) { DoorLockServer::Instance().InitServer(endpoint); - EmberAfStatus status = DoorLock::Attributes::LockType::Set(endpoint, DlLockType::kDeadBolt); - if (status != EMBER_ZCL_STATUS_SUCCESS) - { - LOG_ERR("Updating type %x", status); - } + const auto logOnFailure = [](EmberAfStatus status, const char * attributeName) { + if (status != EMBER_ZCL_STATUS_SUCCESS) + { + ChipLogError(Zcl, "Failed to set DoorLock %s: %x", attributeName, status); + } + }; + + logOnFailure(DoorLock::Attributes::LockType::Set(endpoint, DlLockType::kDeadBolt), "type"); + logOnFailure(DoorLock::Attributes::NumberOfTotalUsersSupported::Set(endpoint, CONFIG_LOCK_NUM_USERS), "number of users"); + logOnFailure(DoorLock::Attributes::NumberOfPINUsersSupported::Set(endpoint, CONFIG_LOCK_NUM_USERS), "number of PIN users"); + logOnFailure(DoorLock::Attributes::NumberOfRFIDUsersSupported::Set(endpoint, 0), "number of RFID users"); + logOnFailure(DoorLock::Attributes::NumberOfCredentialsSupportedPerUser::Set(endpoint, CONFIG_LOCK_NUM_CREDENTIALS_PER_USER), + "number of credentials per user"); - // Set FeatureMap to 0, default is: + // Set FeatureMap to (kUsersManagement|kPINCredentials), default is: // (kUsersManagement|kAccessSchedules|kRFIDCredentials|kPINCredentials) 0x113 - status = DoorLock::Attributes::FeatureMap::Set(endpoint, 0); - if (status != EMBER_ZCL_STATUS_SUCCESS) - { - LOG_ERR("Updating feature map %x", status); - } + logOnFailure(DoorLock::Attributes::FeatureMap::Set(endpoint, 0x101), "feature map"); GetAppTask().UpdateClusterState(BoltLockMgr().GetState(), BoltLockManager::OperationSource::kUnspecified); } diff --git a/examples/lock-app/nrfconnect/main/include/BoltLockManager.h b/examples/lock-app/nrfconnect/main/include/BoltLockManager.h index deeff6024de5a3..940346e176fa2f 100644 --- a/examples/lock-app/nrfconnect/main/include/BoltLockManager.h +++ b/examples/lock-app/nrfconnect/main/include/BoltLockManager.h @@ -19,7 +19,9 @@ #pragma once +#include #include +#include #include @@ -30,6 +32,8 @@ class AppEvent; class BoltLockManager { public: + static constexpr size_t kMaxCredentialLength = 128; + enum class State : uint8_t { kLockingInitiated = 0, @@ -38,6 +42,17 @@ class BoltLockManager kUnlockingCompleted, }; + struct UserData + { + char mName[DOOR_LOCK_USER_NAME_BUFFER_SIZE]; + DlCredential mCredentials[CONFIG_LOCK_NUM_CREDENTIALS_PER_USER]; + }; + + struct CredentialData + { + chip::Platform::ScopedMemoryBuffer mSecret; + }; + using OperationSource = chip::app::Clusters::DoorLock::DlOperationSource; using StateChangeCallback = void (*)(State, OperationSource); @@ -48,6 +63,18 @@ class BoltLockManager State GetState() const { return mState; } bool IsLocked() const { return mState == State::kLockingCompleted; } + bool GetUser(uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) const; + bool SetUser(uint16_t userIndex, chip::FabricIndex creator, chip::FabricIndex modifier, const chip::CharSpan & userName, + uint32_t uniqueId, DlUserStatus userStatus, DlUserType userType, DlCredentialRule credentialRule, + const DlCredential * credentials, size_t totalCredentials); + + bool GetCredential(uint16_t credentialIndex, DlCredentialType credentialType, + EmberAfPluginDoorLockCredentialInfo & credential) const; + bool SetCredential(uint16_t credentialIndex, chip::FabricIndex creator, chip::FabricIndex modifier, + DlCredentialStatus credentialStatus, DlCredentialType credentialType, const chip::ByteSpan & secret); + + bool ValidatePIN(const Optional & pinCode, DlOperationError & err) const; + void Lock(OperationSource source); void Unlock(OperationSource source); @@ -63,6 +90,12 @@ class BoltLockManager OperationSource mActuatorOperationSource = OperationSource::kButton; k_timer mActuatorTimer = {}; + UserData mUserData[CONFIG_LOCK_NUM_USERS]; + EmberAfPluginDoorLockUserInfo mUsers[CONFIG_LOCK_NUM_USERS] = {}; + + CredentialData mCredentialData[CONFIG_LOCK_NUM_CREDENTIALS]; + EmberAfPluginDoorLockCredentialInfo mCredentials[CONFIG_LOCK_NUM_CREDENTIALS] = {}; + static BoltLockManager sLock; }; diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh index 40f1620ac8f5d1..be3a2d721abd57 100644 --- a/scripts/bootstrap.sh +++ b/scripts/bootstrap.sh @@ -26,9 +26,8 @@ _bootstrap_or_activate() { local _CONFIG_FILE="scripts/environment.json" - if [ "$_BOOTSTRAP_NAME" = "no_cipd_bootstrap.sh" ]; then - _CONFIG_FILE="scripts/environment_no_cipd.json" - _BOOTSTRAP_NAME="bootstrap.sh" + if [ ! -z "$PW_CONFIG_FILE" ]; then + _CONFIG_FILE="$PW_CONFIG_FILE" fi if [ "$_BOOTSTRAP_NAME" = "bootstrap.sh" ] || diff --git a/scripts/environment_no_cipd.json b/scripts/environment_no_cipd.json index cc9fde6220e1c3..dbb3106f0f50b2 100644 --- a/scripts/environment_no_cipd.json +++ b/scripts/environment_no_cipd.json @@ -4,5 +4,6 @@ "gn_targets": [":python_packages.install"] }, "required_submodules": ["third_party/pigweed/repo"], + "rosetta": "never", "gni_file": "build_overrides/pigweed_environment.gni" } diff --git a/scripts/no_cipd_bootstrap.sh b/scripts/no_cipd_bootstrap.sh deleted file mode 120000 index eba538975a899f..00000000000000 --- a/scripts/no_cipd_bootstrap.sh +++ /dev/null @@ -1 +0,0 @@ -bootstrap.sh \ No newline at end of file diff --git a/src/app/CommandHandler.cpp b/src/app/CommandHandler.cpp index 206cb44718bc6d..95c77f04949466 100644 --- a/src/app/CommandHandler.cpp +++ b/src/app/CommandHandler.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -244,6 +245,18 @@ CHIP_ERROR CommandHandler::SendCommandResponse() return CHIP_NO_ERROR; } +namespace { +// We use this when the sender did not actually provide a CommandFields struct, +// to avoid downstream consumers having to worry about cases when there is or is +// not a struct available. We use an empty struct with anonymous tag, since we +// can't use a context tag at top level, and consumers should not care about the +// tag here). +constexpr uint8_t sNoFields[] = { + CHIP_TLV_STRUCTURE(CHIP_TLV_TAG_ANONYMOUS), + CHIP_TLV_END_OF_CONTAINER, +}; +} // anonymous namespace + CHIP_ERROR CommandHandler::ProcessCommandDataIB(CommandDataIB::Parser & aCommandElement) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -308,7 +321,8 @@ CHIP_ERROR CommandHandler::ProcessCommandDataIB(CommandDataIB::Parser & aCommand ChipLogDetail(DataManagement, "Received command without data for Endpoint=%u Cluster=" ChipLogFormatMEI " Command=" ChipLogFormatMEI, concretePath.mEndpointId, ChipLogValueMEI(concretePath.mClusterId), ChipLogValueMEI(concretePath.mCommandId)); - err = CHIP_NO_ERROR; + commandDataReader.Init(sNoFields); + err = commandDataReader.Next(); } if (CHIP_NO_ERROR == err) { @@ -365,7 +379,8 @@ CHIP_ERROR CommandHandler::ProcessGroupCommandDataIB(CommandDataIB::Parser & aCo ChipLogDetail(DataManagement, "Received command without data for Group=%u Cluster=" ChipLogFormatMEI " Command=" ChipLogFormatMEI, groupId, ChipLogValueMEI(clusterId), ChipLogValueMEI(commandId)); - err = CHIP_NO_ERROR; + commandDataReader.Init(sNoFields); + err = commandDataReader.Next(); } SuccessOrExit(err); diff --git a/src/app/clusters/door-lock-server/door-lock-server.cpp b/src/app/clusters/door-lock-server/door-lock-server.cpp index f95ab24231d39c..9b2482adf6e1cd 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.cpp +++ b/src/app/clusters/door-lock-server/door-lock-server.cpp @@ -1654,7 +1654,7 @@ EmberAfStatus DoorLockServer::createUser(chip::EndpointId endpointId, chip::Fabr return EMBER_ZCL_STATUS_FAILURE; } - const auto & newUserName = !userName.IsNull() ? userName.Value() : chip::CharSpan(""); + const auto & newUserName = !userName.IsNull() ? userName.Value() : chip::CharSpan::fromCharString(""); auto newUserUniqueId = userUniqueId.IsNull() ? 0xFFFFFFFF : userUniqueId.Value(); auto newUserStatus = userStatus.IsNull() ? DlUserStatus::kOccupiedEnabled : userStatus.Value(); auto newUserType = userType.IsNull() ? DlUserType::kUnrestrictedUser : userType.Value(); @@ -3094,6 +3094,14 @@ void DoorLockServer::clearHolidaySchedule(chip::app::CommandHandler * commandObj emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); } +bool DoorLockServer::RemoteOperationEnabled(chip::EndpointId endpointId) const +{ + DlOperatingMode mode; + + return GetAttribute(endpointId, Attributes::OperatingMode::Id, Attributes::OperatingMode::Get, mode) && + mode != DlOperatingMode::kPrivacy && mode != DlOperatingMode::kNoRemoteLockUnlock; +} + bool DoorLockServer::HandleRemoteLockOperation(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, DlLockOperationType opType, RemoteLockOpHandler opHandler, const Optional & pinCode) @@ -3107,19 +3115,21 @@ bool DoorLockServer::HandleRemoteLockOperation(chip::app::CommandHandler * comma uint16_t pinUserIdx = 0; uint16_t pinCredIdx = 0; bool credentialsOk = false; - bool operationOk = false; + bool success = false; + + VerifyOrExit(RemoteOperationEnabled(endpoint), reason = DlOperationError::kUnspecified); // appclusters.pdf 5.3.4.1: // When the PINCode field is provided an invalid PIN will count towards the WrongCodeEntryLimit and the - // UserCodeTemporaryDisableTime will be triggered if the WrongCodeEntryLimit is exceeded. The lock SHALL ignore any attempts to - // lock/unlock the door until the UserCodeTemporaryDisableTime expires. + // UserCodeTemporaryDisableTime will be triggered if the WrongCodeEntryLimit is exceeded. The lock SHALL ignore any attempts + // to lock/unlock the door until the UserCodeTemporaryDisableTime expires. // TODO: check whether UserCodeTemporaryDisableTime expired or not. if (pinCode.HasValue()) { // appclusters.pdf 5.3.4.1: - // If the PINCode field is provided, the door lock SHALL verify PINCode before granting access regardless of the value of - // RequirePINForRemoteOperation attribute. + // If the PINCode field is provided, the door lock SHALL verify PINCode before granting access regardless of the value + // of RequirePINForRemoteOperation attribute. VerifyOrExit(SupportsPIN(endpoint) && SupportsUSR(endpoint), emberAfDoorLockClusterPrintln( "PIN code is supplied while USR/PIN features are disabled. Exiting [endpoint=%d, lock_op=%d]", endpoint, @@ -3140,9 +3150,9 @@ bool DoorLockServer::HandleRemoteLockOperation(chip::app::CommandHandler * comma bool requirePin = false; // appclusters.pdf 5.3.4.1: - // If the RequirePINForRemoteOperation attribute is True then PINCode field SHALL be provided and the door lock SHALL NOT - // grant access if it is not provided. This attribute exists when COTA and PIN features are both enabled. Otherwise we - // assume PIN to be OK. + // If the RequirePINForRemoteOperation attribute is True then PINCode field SHALL be provided and the door lock SHALL + // NOT grant access if it is not provided. This attribute exists when COTA and PIN features are both enabled. Otherwise + // we assume PIN to be OK. if (SupportsCredentialsOTA(endpoint) && SupportsPIN(endpoint)) { auto status = Attributes::RequirePINforRemoteOperation::Get(endpoint, &requirePin); @@ -3161,15 +3171,13 @@ bool DoorLockServer::HandleRemoteLockOperation(chip::app::CommandHandler * comma }); // credentials check succeeded, try to lock/unlock door - operationOk = opHandler(endpoint, pinCode, reason); - VerifyOrExit(operationOk, /* reason is set by the above call */); + success = opHandler(endpoint, pinCode, reason); + VerifyOrExit(success, /* reason is set by the above call */); // door locked, set cluster attribute VerifyOrDie(SetLockState(endpoint, newLockState, DlOperationSource::kRemote)); exit: - bool success = credentialsOk && operationOk; - // Send command response emberAfSendImmediateDefaultResponse(success ? EMBER_ZCL_STATUS_SUCCESS : EMBER_ZCL_STATUS_FAILURE); @@ -3256,7 +3264,7 @@ void DoorLockServer::SendEvent(chip::EndpointId endpointId, T & event) template bool DoorLockServer::GetAttribute(chip::EndpointId endpointId, chip::AttributeId attributeId, - EmberAfStatus (*getFn)(chip::EndpointId endpointId, T * value), T & value) + EmberAfStatus (*getFn)(chip::EndpointId endpointId, T * value), T & value) const { EmberAfStatus status = getFn(endpointId, &value); bool success = (EMBER_ZCL_STATUS_SUCCESS == status); diff --git a/src/app/clusters/door-lock-server/door-lock-server.h b/src/app/clusters/door-lock-server/door-lock-server.h index efe8a0ace57ae3..ac0cce099e6a56 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.h +++ b/src/app/clusters/door-lock-server/door-lock-server.h @@ -313,6 +313,8 @@ class DoorLockServer void clearHolidaySchedule(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, uint8_t holidayIndex); + bool RemoteOperationEnabled(chip::EndpointId endpointId) const; + /** * @brief Common handler for LockDoor, UnlockDoor, UnlockWithTimeout commands * @@ -378,7 +380,7 @@ class DoorLockServer */ template bool GetAttribute(chip::EndpointId endpointId, chip::AttributeId attributeId, - EmberAfStatus (*getFn)(chip::EndpointId endpointId, T * value), T & value); + EmberAfStatus (*getFn)(chip::EndpointId endpointId, T * value), T & value) const; /** * @brief Set generic attribute value diff --git a/src/app/tests/TestCommandInteraction.cpp b/src/app/tests/TestCommandInteraction.cpp index 50c219b58e61a7..6b7a089104229c 100644 --- a/src/app/tests/TestCommandInteraction.cpp +++ b/src/app/tests/TestCommandInteraction.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -57,10 +58,14 @@ bool isCommandDispatched = false; bool sendResponse = true; bool asyncCommand = false; +// Allow us to do test asserts from arbitrary places. +nlTestSuite * gSuite = nullptr; + constexpr EndpointId kTestEndpointId = 1; constexpr ClusterId kTestClusterId = 3; -constexpr CommandId kTestCommandId = 4; -constexpr CommandId kTestCommandIdCommandSpecificResponse = 5; +constexpr CommandId kTestCommandIdWithData = 4; +constexpr CommandId kTestCommandIdNoData = 5; +constexpr CommandId kTestCommandIdCommandSpecificResponse = 6; constexpr CommandId kTestNonExistCommandId = 0; } // namespace @@ -97,6 +102,36 @@ void DispatchSingleClusterCommand(const ConcreteCommandPath & aCommandPath, chip ChipLogDetail(Controller, "Received Cluster Command: Endpoint=%x Cluster=" ChipLogFormatMEI " Command=" ChipLogFormatMEI, aCommandPath.mEndpointId, ChipLogValueMEI(aCommandPath.mClusterId), ChipLogValueMEI(aCommandPath.mCommandId)); + // Duplicate what our normal command-field-decode code does, in terms of + // checking for a struct and then entering it before getting the fields. + if (aReader.GetType() != TLV::kTLVType_Structure) + { + apCommandObj->AddStatus(aCommandPath, Protocols::InteractionModel::Status::InvalidAction); + return; + } + + TLV::TLVType outerContainerType; + CHIP_ERROR err = aReader.EnterContainer(outerContainerType); + NL_TEST_ASSERT(gSuite, err == CHIP_NO_ERROR); + + err = aReader.Next(); + if (aCommandPath.mCommandId == kTestCommandIdNoData) + { + NL_TEST_ASSERT(gSuite, err == CHIP_ERROR_END_OF_TLV); + } + else + { + NL_TEST_ASSERT(gSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(gSuite, aReader.GetTag() == TLV::ContextTag(1)); + bool val; + err = aReader.Get(val); + NL_TEST_ASSERT(gSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(gSuite, val); + } + + err = aReader.ExitContainer(outerContainerType); + NL_TEST_ASSERT(gSuite, err == CHIP_NO_ERROR); + if (asyncCommand) { asyncCommandHandle = apCommandObj; @@ -105,7 +140,7 @@ void DispatchSingleClusterCommand(const ConcreteCommandPath & aCommandPath, chip if (sendResponse) { - if (aCommandPath.mCommandId == kTestCommandId) + if (aCommandPath.mCommandId == kTestCommandIdNoData || aCommandPath.mCommandId == kTestCommandIdWithData) { apCommandObj->AddStatus(aCommandPath, Protocols::InteractionModel::Status::Success); } @@ -200,16 +235,20 @@ class TestCommandInteraction } private: + // Generate an invoke request. If aCommandId is kTestCommandIdWithData, a + // payload will be included. Otherwise no payload will be included. static void GenerateInvokeRequest(nlTestSuite * apSuite, void * apContext, System::PacketBufferHandle & aPayload, - bool aNeedCommandData, bool aIsTimedRequest, EndpointId aEndpointId = kTestEndpointId, - ClusterId aClusterId = kTestClusterId, CommandId aCommandId = kTestCommandId); + bool aIsTimedRequest, CommandId aCommandId, ClusterId aClusterId = kTestClusterId, + EndpointId aEndpointId = kTestEndpointId); + // Generate an invoke response. If aCommandId is kTestCommandIdWithData, a + // payload will be included. Otherwise no payload will be included. static void GenerateInvokeResponse(nlTestSuite * apSuite, void * apContext, System::PacketBufferHandle & aPayload, - bool aNeedCommandData, EndpointId aEndpointId = kTestEndpointId, - ClusterId aClusterId = kTestClusterId, CommandId aCommandId = kTestCommandId); + CommandId aCommandId, ClusterId aClusterId = kTestClusterId, + EndpointId aEndpointId = kTestEndpointId); static void AddInvokeRequestData(nlTestSuite * apSuite, void * apContext, CommandSender * apCommandSender, - CommandId aCommandId = kTestCommandId); + CommandId aCommandId = kTestCommandIdWithData); static void AddInvokeResponseData(nlTestSuite * apSuite, void * apContext, CommandHandler * apCommandHandler, - bool aNeedStatusCode, CommandId aCommandId = kTestCommandId); + bool aNeedStatusCode, CommandId aCommandId = kTestCommandIdWithData); static void ValidateCommandHandlerWithSendCommand(nlTestSuite * apSuite, void * apContext, bool aNeedStatusCode); }; @@ -224,14 +263,14 @@ class TestExchangeDelegate : public Messaging::ExchangeDelegate void OnResponseTimeout(Messaging::ExchangeContext * ec) override {} }; -CommandPathParams MakeTestCommandPath(CommandId aCommandId = kTestCommandId) +CommandPathParams MakeTestCommandPath(CommandId aCommandId = kTestCommandIdWithData) { return CommandPathParams(kTestEndpointId, 0, kTestClusterId, aCommandId, (chip::app::CommandPathFlags::kEndpointIdValid)); } void TestCommandInteraction::GenerateInvokeRequest(nlTestSuite * apSuite, void * apContext, System::PacketBufferHandle & aPayload, - bool aNeedCommandData, bool aIsTimedRequest, EndpointId aEndpointId, - ClusterId aClusterId, CommandId aCommandId) + bool aIsTimedRequest, CommandId aCommandId, ClusterId aClusterId, + EndpointId aEndpointId) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -255,7 +294,7 @@ void TestCommandInteraction::GenerateInvokeRequest(nlTestSuite * apSuite, void * commandPathBuilder.EndpointId(aEndpointId).ClusterId(aClusterId).CommandId(aCommandId).EndOfCommandPathIB(); NL_TEST_ASSERT(apSuite, commandPathBuilder.GetError() == CHIP_NO_ERROR); - if (aNeedCommandData) + if (aCommandId == kTestCommandIdWithData) { chip::TLV::TLVWriter * pWriter = commandDataIBBuilder.GetWriter(); chip::TLV::TLVType dummyType = chip::TLV::kTLVType_NotSpecified; @@ -284,8 +323,7 @@ void TestCommandInteraction::GenerateInvokeRequest(nlTestSuite * apSuite, void * } void TestCommandInteraction::GenerateInvokeResponse(nlTestSuite * apSuite, void * apContext, System::PacketBufferHandle & aPayload, - bool aNeedCommandData, EndpointId aEndpointId, ClusterId aClusterId, - CommandId aCommandId) + CommandId aCommandId, ClusterId aClusterId, EndpointId aEndpointId) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -312,7 +350,7 @@ void TestCommandInteraction::GenerateInvokeResponse(nlTestSuite * apSuite, void commandPathBuilder.EndpointId(aEndpointId).ClusterId(aClusterId).CommandId(aCommandId).EndOfCommandPathIB(); NL_TEST_ASSERT(apSuite, commandPathBuilder.GetError() == CHIP_NO_ERROR); - if (aNeedCommandData) + if (aCommandId == kTestCommandIdWithData) { chip::TLV::TLVWriter * pWriter = commandDataIBBuilder.GetWriter(); chip::TLV::TLVType dummyType = chip::TLV::kTLVType_NotSpecified; @@ -406,7 +444,7 @@ void TestCommandInteraction::TestCommandHandlerWithWrongState(nlTestSuite * apSu { TestContext & ctx = *static_cast(apContext); CHIP_ERROR err = CHIP_NO_ERROR; - ConcreteCommandPath path = { kTestEndpointId, kTestClusterId, kTestCommandId }; + ConcreteCommandPath path = { kTestEndpointId, kTestClusterId, kTestCommandIdNoData }; app::CommandHandler commandHandler(&mockCommandHandlerDelegate); @@ -435,7 +473,7 @@ void TestCommandInteraction::TestCommandSenderWithSendCommand(nlTestSuite * apSu ctx.DrainAndServiceIO(); - GenerateInvokeResponse(apSuite, apContext, buf, true /*aNeedCommandData*/); + GenerateInvokeResponse(apSuite, apContext, buf, kTestCommandIdWithData); err = commandSender.ProcessInvokeResponse(std::move(buf)); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); } @@ -444,7 +482,7 @@ void TestCommandInteraction::TestCommandHandlerWithSendEmptyCommand(nlTestSuite { TestContext & ctx = *static_cast(apContext); CHIP_ERROR err = CHIP_NO_ERROR; - ConcreteCommandPath path = { kTestEndpointId, kTestClusterId, kTestCommandId }; + ConcreteCommandPath path = { kTestEndpointId, kTestClusterId, kTestCommandIdNoData }; app::CommandHandler commandHandler(&mockCommandHandlerDelegate); System::PacketBufferHandle commandDatabuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); @@ -470,7 +508,7 @@ void TestCommandInteraction::TestCommandSenderWithProcessReceivedMsg(nlTestSuite System::PacketBufferHandle buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); - GenerateInvokeResponse(apSuite, apContext, buf, true /*aNeedCommandData*/); + GenerateInvokeResponse(apSuite, apContext, buf, kTestCommandIdWithData); err = commandSender.ProcessInvokeResponse(std::move(buf)); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); } @@ -636,7 +674,7 @@ void TestCommandInteraction::TestCommandHandlerWithProcessReceivedMsg(nlTestSuit TestExchangeDelegate delegate; commandHandler.mpExchangeCtx = ctx.NewExchangeToAlice(&delegate); - GenerateInvokeRequest(apSuite, apContext, commandDatabuf, true /*aNeedCommandData*/, /* aIsTimedRequest = */ false); + GenerateInvokeRequest(apSuite, apContext, commandDatabuf, /* aIsTimedRequest = */ false, kTestCommandIdWithData); err = commandHandler.ProcessInvokeRequest(std::move(commandDatabuf), false); ChipLogDetail(DataManagement, "###################################### %s", err.AsString()); @@ -650,8 +688,8 @@ void TestCommandInteraction::TestCommandHandlerWithProcessReceivedNotExistComman System::PacketBufferHandle commandDatabuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); // Use some invalid endpoint / cluster / command. - GenerateInvokeRequest(apSuite, apContext, commandDatabuf, false /*aNeedCommandData*/, /* aIsTimedRequest = */ false, - 0xDE /* endpoint */, 0xADBE /* cluster */, 0xEF /* command */); + GenerateInvokeRequest(apSuite, apContext, commandDatabuf, /* aIsTimedRequest = */ false, 0xEF /* command */, + 0xADBE /* cluster */, 0xDE /* endpoint */); // TODO: Need to find a way to get the response instead of only check if a function on key path is called. // We should not reach CommandDispatch if requested command does not exist. @@ -676,7 +714,7 @@ void TestCommandInteraction::TestCommandHandlerWithProcessReceivedEmptyDataMsg(n commandHandler.mpExchangeCtx = ctx.NewExchangeToAlice(&delegate); chip::isCommandDispatched = false; - GenerateInvokeRequest(apSuite, apContext, commandDatabuf, false /*aNeedCommandData*/, messageIsTimed); + GenerateInvokeRequest(apSuite, apContext, commandDatabuf, messageIsTimed, kTestCommandIdNoData); err = commandHandler.ProcessInvokeRequest(std::move(commandDatabuf), transactionIsTimed); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, chip::isCommandDispatched == (messageIsTimed == transactionIsTimed)); @@ -934,6 +972,7 @@ nlTestSuite sSuite = int TestCommandInteraction() { + chip::gSuite = &sSuite; return chip::ExecuteTestsWithContext(&sSuite); } diff --git a/src/app/tests/suites/DL_LockUnlock.yaml b/src/app/tests/suites/DL_LockUnlock.yaml index 4ddaee939b4254..08ff372ee6b45e 100644 --- a/src/app/tests/suites/DL_LockUnlock.yaml +++ b/src/app/tests/suites/DL_LockUnlock.yaml @@ -120,7 +120,7 @@ tests: response: value: 2 - - label: "Try to unlock the door with valid PIN" + - label: "Try to lock the door with valid PIN" command: "LockDoor" timedInteractionTimeoutMs: 10000 arguments: @@ -134,7 +134,25 @@ tests: response: value: 1 + - label: "Set OperatingMode to NoRemoteLockUnlock" + command: "writeAttribute" + attribute: "OperatingMode" + arguments: + value: 3 + + - label: "Try to unlock the door when OperatingMode is NoRemoteLockUnlock" + command: "LockDoor" + timedInteractionTimeoutMs: 10000 + response: + error: FAILURE + # Clean-up + - label: "Set OperatingMode to Normal" + command: "writeAttribute" + attribute: "OperatingMode" + arguments: + value: 0 + - label: "Clean the created credential" command: "ClearCredential" timedInteractionTimeoutMs: 10000 diff --git a/src/darwin/Framework/CHIP/CHIPDeviceController.mm b/src/darwin/Framework/CHIP/CHIPDeviceController.mm index ba2e4309fb35ce..1521d7a4035f11 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceController.mm +++ b/src/darwin/Framework/CHIP/CHIPDeviceController.mm @@ -125,14 +125,21 @@ - (void)cleanupAfterStartup [self cleanup]; } +// Part of cleanupAfterStartup that has to interact with the Matter work queue +// in a very specific way that only MTRControllerFactory knows about. +- (void)shutDownCppController +{ + if (_cppCommissioner) { + _cppCommissioner->Shutdown(); + delete _cppCommissioner; + _cppCommissioner = nullptr; + } +} + // Clean up any members we might have allocated. - (void)cleanup { - if (self->_cppCommissioner) { - self->_cppCommissioner->Shutdown(); - delete self->_cppCommissioner; - self->_cppCommissioner = nullptr; - } + VerifyOrDie(_cppCommissioner == nullptr); [self clearDeviceAttestationDelegateBridge]; @@ -675,11 +682,6 @@ - (BOOL)checkForError:(CHIP_ERROR)errorCode logMsg:(NSString *)logMsg error:(NSE return YES; } -- (void)dealloc -{ - [self cleanup]; -} - - (BOOL)deviceBeingCommissionedOverBLE:(uint64_t)deviceId { CHIP_ERROR errorCode = CHIP_ERROR_INCORRECT_STATE; diff --git a/src/darwin/Framework/CHIP/CHIPDeviceController_Internal.h b/src/darwin/Framework/CHIP/CHIPDeviceController_Internal.h index 8419de759b82f6..3c4cd4f0627f4e 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceController_Internal.h +++ b/src/darwin/Framework/CHIP/CHIPDeviceController_Internal.h @@ -80,6 +80,14 @@ NS_ASSUME_NONNULL_BEGIN fabricIndex:(chip::FabricIndex)fabricIndex isRunning:(BOOL *)isRunning; +/** + * Shut down the underlying C++ controller. Must be called on the Matter work + * queue or after the Matter work queue has been shut down. + * + * Only MTRControllerFactory should be calling this. + */ +- (void)shutDownCppController; + @end NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MatterControllerFactory.mm b/src/darwin/Framework/CHIP/MatterControllerFactory.mm index ba8618302b8b57..12db00bf231f9c 100644 --- a/src/darwin/Framework/CHIP/MatterControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MatterControllerFactory.mm @@ -525,6 +525,13 @@ - (void)controllerShuttingDown:(CHIPDeviceController *)controller // shuts down, because shutdown of the last controller will tear // down most of the world. DeviceLayer::PlatformMgrImpl().StopEventLoopTask(); + + [controller shutDownCppController]; + } else { + // Do the controller shutdown on the Matter work queue. + dispatch_sync(_chipWorkQueue, ^{ + [controller shutDownCppController]; + }); } } diff --git a/src/lib/core/CHIPTLV.h b/src/lib/core/CHIPTLV.h index 8a96f45ce1069f..3ab437560273d5 100644 --- a/src/lib/core/CHIPTLV.h +++ b/src/lib/core/CHIPTLV.h @@ -56,12 +56,12 @@ namespace chip { namespace TLV { -inline uint8_t operator|(TLVElementType lhs, TLVTagControl rhs) +constexpr inline uint8_t operator|(TLVElementType lhs, TLVTagControl rhs) { return static_cast(lhs) | static_cast(rhs); } -inline uint8_t operator|(TLVTagControl lhs, TLVElementType rhs) +constexpr inline uint8_t operator|(TLVTagControl lhs, TLVElementType rhs) { return static_cast(lhs) | static_cast(rhs); } diff --git a/src/platform/BUILD.gn b/src/platform/BUILD.gn index fdcc45d0424ac7..580bd138ae312b 100644 --- a/src/platform/BUILD.gn +++ b/src/platform/BUILD.gn @@ -73,7 +73,7 @@ if (chip_device_platform != "none" && chip_device_platform != "external") { if (chip_stack_lock_tracking == "auto") { if (chip_device_platform == "linux" || chip_device_platform == "tizen" || chip_device_platform == "android" || current_os == "freertos" || - chip_device_platform == "webos") { + chip_device_platform == "webos" || chip_device_platform == "darwin") { # TODO: should be fatal for development. Change once bugs are fixed chip_stack_lock_tracking = "fatal" } else { diff --git a/src/platform/Darwin/PlatformManagerImpl.cpp b/src/platform/Darwin/PlatformManagerImpl.cpp index 3c85bee76cbd62..1a80645a0192c1 100644 --- a/src/platform/Darwin/PlatformManagerImpl.cpp +++ b/src/platform/Darwin/PlatformManagerImpl.cpp @@ -69,9 +69,9 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack() CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask() { - if (mIsWorkQueueRunning == false) + if (mIsWorkQueueSuspended) { - mIsWorkQueueRunning = true; + mIsWorkQueueSuspended = false; dispatch_resume(mWorkQueue); } @@ -80,9 +80,9 @@ CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask() CHIP_ERROR PlatformManagerImpl::_StopEventLoopTask() { - if (mIsWorkQueueRunning == true) + if (!mIsWorkQueueSuspended && !mIsWorkQueueSuspensionPending) { - mIsWorkQueueRunning = false; + mIsWorkQueueSuspensionPending = true; if (dispatch_get_current_queue() != mWorkQueue) { // dispatch_sync is used in order to guarantee serialization of the caller with @@ -90,6 +90,9 @@ CHIP_ERROR PlatformManagerImpl::_StopEventLoopTask() dispatch_sync(mWorkQueue, ^{ dispatch_suspend(mWorkQueue); }); + + mIsWorkQueueSuspended = true; + mIsWorkQueueSuspensionPending = false; } else { @@ -99,6 +102,8 @@ CHIP_ERROR PlatformManagerImpl::_StopEventLoopTask() // that no more tasks will run on the queue. dispatch_async(mWorkQueue, ^{ dispatch_suspend(mWorkQueue); + mIsWorkQueueSuspended = true; + mIsWorkQueueSuspensionPending = false; dispatch_semaphore_signal(mRunLoopSem); }); } @@ -138,5 +143,14 @@ CHIP_ERROR PlatformManagerImpl::_PostEvent(const ChipDeviceEvent * event) return CHIP_NO_ERROR; } +#if CHIP_STACK_LOCK_TRACKING_ENABLED +bool PlatformManagerImpl::_IsChipStackLockedByCurrentThread() const +{ + // If we have no work queue, or it's suspended, then we assume our caller + // knows what they are doing in terms of their own concurrency. + return !mWorkQueue || mIsWorkQueueSuspended || dispatch_get_current_queue() == mWorkQueue; +}; +#endif + } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/Darwin/PlatformManagerImpl.h b/src/platform/Darwin/PlatformManagerImpl.h index 72b0bd9d7103d2..e8b8decf2fcfb3 100644 --- a/src/platform/Darwin/PlatformManagerImpl.h +++ b/src/platform/Darwin/PlatformManagerImpl.h @@ -49,6 +49,7 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener { mWorkQueue = dispatch_queue_create(CHIP_CONTROLLER_QUEUE, DISPATCH_QUEUE_SERIAL); dispatch_suspend(mWorkQueue); + mIsWorkQueueSuspended = true; } return mWorkQueue; } @@ -71,7 +72,7 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener CHIP_ERROR _PostEvent(const ChipDeviceEvent * event); #if CHIP_STACK_LOCK_TRACKING_ENABLED - bool _IsChipStackLockedByCurrentThread() const { return false; }; + bool _IsChipStackLockedByCurrentThread() const; #endif // ===== Members for internal use by the following friends. @@ -88,7 +89,11 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener // Semaphore used to implement blocking behavior in _RunEventLoop. dispatch_semaphore_t mRunLoopSem; - bool mIsWorkQueueRunning = false; + bool mIsWorkQueueSuspended = false; + // TODO: mIsWorkQueueSuspensionPending might need to be an atomic and use + // atomic ops, if we're worried about calls to StopEventLoopTask() from + // multiple threads racing somehow... + bool mIsWorkQueueSuspensionPending = false; inline ImplClass * Impl() { return static_cast(this); } }; diff --git a/src/platform/device.gni b/src/platform/device.gni index 9af7d49c0d3141..ec3de35d4dcaac 100755 --- a/src/platform/device.gni +++ b/src/platform/device.gni @@ -63,7 +63,8 @@ declare_args() { chip_device_platform == "mbed" || chip_device_platform == "tizen" || chip_device_platform == "android" || chip_device_platform == "ameba" || chip_device_platform == "webos" || chip_device_platform == "cc32xx" || - chip_device_platform == "bl602" || chip_device_platform == "bl602" + chip_device_platform == "bl602" || chip_device_platform == "bl602" || + chip_device_platform == "mw320" # Enable ble support. if (chip_device_platform == "fake") { diff --git a/third_party/nxp/mw320_sdk/BUILD.gn b/third_party/nxp/mw320_sdk/BUILD.gn index 4d36aed69d5c01..be6fb914b1c4a6 100644 --- a/third_party/nxp/mw320_sdk/BUILD.gn +++ b/third_party/nxp/mw320_sdk/BUILD.gn @@ -62,11 +62,33 @@ config("mbedtls_mw320_config") { # "MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED", ] + if (mbedtls_use_tinycrypt) { + defines += [ + "MBEDTLS_USE_TINYCRYPT", + "MBEDTLS_OPTIMIZE_TINYCRYPT_ASM", + ] + } include_dirs = [ chip_root ] + + if (mbedtls_use_tinycrypt) { + include_dirs += [ "${mbedtls_repo}/repo/include/tinycrypt" ] + } } mbedtls_target("mbedtls") { + import("${mw320_sdk_build_root}/mw320_sdk.gni") + + if (mbedtls_use_tinycrypt) { + if (!defined(sources)) { + sources = [] + } + sources += [ + "${mbedtls_repo}/repo/tinycrypt/ecc.c", + "${mbedtls_repo}/repo/tinycrypt/ecc_dh.c", + "${mbedtls_repo}/repo/tinycrypt/ecc_dsa.c", + ] + } public_configs = [ ":mbedtls_mw320_config" ] public_deps = [ ":mw320_sdk" ] diff --git a/third_party/nxp/mw320_sdk/mw320_sdk.gni b/third_party/nxp/mw320_sdk/mw320_sdk.gni index 2139089e298a84..4e9de699485dcc 100644 --- a/third_party/nxp/mw320_sdk/mw320_sdk.gni +++ b/third_party/nxp/mw320_sdk/mw320_sdk.gni @@ -19,6 +19,7 @@ import("//build_overrides/mw320_sdk.gni") declare_args() { # Location of the mw320 SDK. mw320_sdk_root = "${chip_root}/third_party/nxp/mw320_sdk/repo" + mbedtls_use_tinycrypt = false } assert(mw320_sdk_root != "", "mw320_sdk_root must be specified") diff --git a/zzz_generated/chip-tool/zap-generated/test/Commands.h b/zzz_generated/chip-tool/zap-generated/test/Commands.h index 8552bab3e3bb58..c4bf101e02c23e 100644 --- a/zzz_generated/chip-tool/zap-generated/test/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/test/Commands.h @@ -56578,7 +56578,7 @@ class DL_UsersAndCredentialsSuite : public TestCommand class DL_LockUnlockSuite : public TestCommand { public: - DL_LockUnlockSuite(CredentialIssuerCommands * credsIssuerConfig) : TestCommand("DL_LockUnlock", 15, credsIssuerConfig) + DL_LockUnlockSuite(CredentialIssuerCommands * credsIssuerConfig) : TestCommand("DL_LockUnlock", 18, credsIssuerConfig) { AddArgument("nodeId", 0, UINT64_MAX, &mNodeId); AddArgument("cluster", &mCluster); @@ -56704,6 +56704,15 @@ class DL_LockUnlockSuite : public TestCommand case 14: VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); break; + case 15: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), EMBER_ZCL_STATUS_FAILURE)); + break; + case 16: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + break; + case 17: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + break; default: LogErrorOnFailure(ContinueOnChipMainThread(CHIP_ERROR_INVALID_ARGUMENT)); } @@ -56821,7 +56830,7 @@ class DL_LockUnlockSuite : public TestCommand chip::NullOptional); } case 12: { - LogStep(12, "Try to unlock the door with valid PIN"); + LogStep(12, "Try to lock the door with valid PIN"); ListFreer listFreer; chip::app::Clusters::DoorLock::Commands::LockDoor::Type value; value.pinCode.Emplace(); @@ -56837,7 +56846,32 @@ class DL_LockUnlockSuite : public TestCommand chip::NullOptional); } case 14: { - LogStep(14, "Clean the created credential"); + LogStep(14, "Set OperatingMode to NoRemoteLockUnlock"); + ListFreer listFreer; + chip::app::Clusters::DoorLock::DlOperatingMode value; + value = static_cast(3); + return WriteAttribute(kIdentityAlpha, GetEndpoint(1), DoorLock::Id, DoorLock::Attributes::OperatingMode::Id, value, + chip::NullOptional, chip::NullOptional); + } + case 15: { + LogStep(15, "Try to unlock the door when OperatingMode is NoRemoteLockUnlock"); + ListFreer listFreer; + chip::app::Clusters::DoorLock::Commands::LockDoor::Type value; + return SendCommand(kIdentityAlpha, GetEndpoint(1), DoorLock::Id, DoorLock::Commands::LockDoor::Id, value, + chip::Optional(10000), chip::NullOptional + + ); + } + case 16: { + LogStep(16, "Set OperatingMode to Normal"); + ListFreer listFreer; + chip::app::Clusters::DoorLock::DlOperatingMode value; + value = static_cast(0); + return WriteAttribute(kIdentityAlpha, GetEndpoint(1), DoorLock::Id, DoorLock::Attributes::OperatingMode::Id, value, + chip::NullOptional, chip::NullOptional); + } + case 17: { + LogStep(17, "Clean the created credential"); ListFreer listFreer; chip::app::Clusters::DoorLock::Commands::ClearCredential::Type value; value.credential.SetNonNull(); diff --git a/zzz_generated/darwin-framework-tool/zap-generated/test/Commands.h b/zzz_generated/darwin-framework-tool/zap-generated/test/Commands.h index 89d7d708a2f923..d882221b974457 100644 --- a/zzz_generated/darwin-framework-tool/zap-generated/test/Commands.h +++ b/zzz_generated/darwin-framework-tool/zap-generated/test/Commands.h @@ -93248,16 +93248,28 @@ class DL_LockUnlock : public TestCommandBridge { err = TestVerifyThatLockStateAttributeValueIsSetToUnlocked_11(); break; case 12: - ChipLogProgress(chipTool, " ***** Test Step 12 : Try to unlock the door with valid PIN\n"); - err = TestTryToUnlockTheDoorWithValidPin_12(); + ChipLogProgress(chipTool, " ***** Test Step 12 : Try to lock the door with valid PIN\n"); + err = TestTryToLockTheDoorWithValidPin_12(); break; case 13: ChipLogProgress(chipTool, " ***** Test Step 13 : Verify that lock state attribute value is set to Locked\n"); err = TestVerifyThatLockStateAttributeValueIsSetToLocked_13(); break; case 14: - ChipLogProgress(chipTool, " ***** Test Step 14 : Clean the created credential\n"); - err = TestCleanTheCreatedCredential_14(); + ChipLogProgress(chipTool, " ***** Test Step 14 : Set OperatingMode to NoRemoteLockUnlock\n"); + err = TestSetOperatingModeToNoRemoteLockUnlock_14(); + break; + case 15: + ChipLogProgress(chipTool, " ***** Test Step 15 : Try to unlock the door when OperatingMode is NoRemoteLockUnlock\n"); + err = TestTryToUnlockTheDoorWhenOperatingModeIsNoRemoteLockUnlock_15(); + break; + case 16: + ChipLogProgress(chipTool, " ***** Test Step 16 : Set OperatingMode to Normal\n"); + err = TestSetOperatingModeToNormal_16(); + break; + case 17: + ChipLogProgress(chipTool, " ***** Test Step 17 : Clean the created credential\n"); + err = TestCleanTheCreatedCredential_17(); break; } @@ -93315,6 +93327,15 @@ class DL_LockUnlock : public TestCommandBridge { case 14: VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); break; + case 15: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), EMBER_ZCL_STATUS_FAILURE)); + break; + case 16: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + break; + case 17: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + break; } // Go on to the next test. @@ -93328,7 +93349,7 @@ class DL_LockUnlock : public TestCommandBridge { private: std::atomic_uint16_t mTestIndex; - const uint16_t mTestCount = 15; + const uint16_t mTestCount = 18; chip::Optional mNodeId; chip::Optional mCluster; @@ -93599,7 +93620,7 @@ class DL_LockUnlock : public TestCommandBridge { return CHIP_NO_ERROR; } - CHIP_ERROR TestTryToUnlockTheDoorWithValidPin_12() + CHIP_ERROR TestTryToLockTheDoorWithValidPin_12() { CHIPDevice * device = GetDevice("alpha"); CHIPTestDoorLock * cluster = [[CHIPTestDoorLock alloc] initWithDevice:device endpoint:1 queue:mCallbackQueue]; @@ -93609,7 +93630,7 @@ class DL_LockUnlock : public TestCommandBridge { params.pinCode = [[NSData alloc] initWithBytes:"123456" length:6]; [cluster lockDoorWithParams:params completionHandler:^(NSError * _Nullable err) { - NSLog(@"Try to unlock the door with valid PIN Error: %@", err); + NSLog(@"Try to lock the door with valid PIN Error: %@", err); VerifyOrReturn(CheckValue("status", err ? err.code : 0, 0)); @@ -93642,7 +93663,65 @@ class DL_LockUnlock : public TestCommandBridge { return CHIP_NO_ERROR; } - CHIP_ERROR TestCleanTheCreatedCredential_14() + CHIP_ERROR TestSetOperatingModeToNoRemoteLockUnlock_14() + { + CHIPDevice * device = GetDevice("alpha"); + CHIPTestDoorLock * cluster = [[CHIPTestDoorLock alloc] initWithDevice:device endpoint:1 queue:mCallbackQueue]; + VerifyOrReturnError(cluster != nil, CHIP_ERROR_INCORRECT_STATE); + + id operatingModeArgument; + operatingModeArgument = [NSNumber numberWithUnsignedChar:3U]; + [cluster writeAttributeOperatingModeWithValue:operatingModeArgument + completionHandler:^(NSError * _Nullable err) { + NSLog(@"Set OperatingMode to NoRemoteLockUnlock Error: %@", err); + + VerifyOrReturn(CheckValue("status", err ? err.code : 0, 0)); + + NextTest(); + }]; + + return CHIP_NO_ERROR; + } + + CHIP_ERROR TestTryToUnlockTheDoorWhenOperatingModeIsNoRemoteLockUnlock_15() + { + CHIPDevice * device = GetDevice("alpha"); + CHIPTestDoorLock * cluster = [[CHIPTestDoorLock alloc] initWithDevice:device endpoint:1 queue:mCallbackQueue]; + VerifyOrReturnError(cluster != nil, CHIP_ERROR_INCORRECT_STATE); + + __auto_type * params = [[CHIPDoorLockClusterLockDoorParams alloc] init]; + [cluster lockDoorWithParams:params + completionHandler:^(NSError * _Nullable err) { + NSLog(@"Try to unlock the door when OperatingMode is NoRemoteLockUnlock Error: %@", err); + + VerifyOrReturn(CheckValue("status", err ? err.code : 0, EMBER_ZCL_STATUS_FAILURE)); + NextTest(); + }]; + + return CHIP_NO_ERROR; + } + + CHIP_ERROR TestSetOperatingModeToNormal_16() + { + CHIPDevice * device = GetDevice("alpha"); + CHIPTestDoorLock * cluster = [[CHIPTestDoorLock alloc] initWithDevice:device endpoint:1 queue:mCallbackQueue]; + VerifyOrReturnError(cluster != nil, CHIP_ERROR_INCORRECT_STATE); + + id operatingModeArgument; + operatingModeArgument = [NSNumber numberWithUnsignedChar:0U]; + [cluster writeAttributeOperatingModeWithValue:operatingModeArgument + completionHandler:^(NSError * _Nullable err) { + NSLog(@"Set OperatingMode to Normal Error: %@", err); + + VerifyOrReturn(CheckValue("status", err ? err.code : 0, 0)); + + NextTest(); + }]; + + return CHIP_NO_ERROR; + } + + CHIP_ERROR TestCleanTheCreatedCredential_17() { CHIPDevice * device = GetDevice("alpha"); CHIPTestDoorLock * cluster = [[CHIPTestDoorLock alloc] initWithDevice:device endpoint:1 queue:mCallbackQueue];