diff --git a/examples/light-switch-app/silabs/src/AppTask.cpp b/examples/light-switch-app/silabs/src/AppTask.cpp index b59b99ceb1..70a6354557 100644 --- a/examples/light-switch-app/silabs/src/AppTask.cpp +++ b/examples/light-switch-app/silabs/src/AppTask.cpp @@ -319,7 +319,10 @@ void AppTask::AppEventHandler(AppEvent * aEvent) closureData->isGroup = false; closureData->commandId = Clusters::ClosureControl::Commands::MoveTo::Id; if (BaseApplication::ScheduleWorkGatedOnThreadDirectLink(AppTask::SwitchWorkerFunction, - reinterpret_cast(closureData)) != CHIP_NO_ERROR) + reinterpret_cast(closureData), + [](intptr_t context) { + Platform::Delete(reinterpret_cast(context)); + }) != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to schedule closure switch worker"); Platform::Delete(closureData); @@ -339,7 +342,10 @@ void AppTask::AppEventHandler(AppEvent * aEvent) toggleData->isGroup = false; toggleData->commandId = OnOff::Commands::Toggle::Id; if (BaseApplication::ScheduleWorkGatedOnThreadDirectLink(AppTask::SwitchWorkerFunction, - reinterpret_cast(toggleData)) != CHIP_NO_ERROR) + reinterpret_cast(toggleData), + [](intptr_t context) { + Platform::Delete(reinterpret_cast(context)); + }) != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to schedule switch worker"); Platform::Delete(toggleData); @@ -384,8 +390,10 @@ void AppTask::AppEventHandler(AppEvent * aEvent) stepData.optionsMask.Set(kStepCommand.optionsMask); stepData.optionsOverride.Set(kStepCommand.optionsOverride); data->commandData = stepData; - if (BaseApplication::ScheduleWorkGatedOnThreadDirectLink(AppTask::SwitchWorkerFunction, reinterpret_cast(data)) != - CHIP_NO_ERROR) + if (BaseApplication::ScheduleWorkGatedOnThreadDirectLink(AppTask::SwitchWorkerFunction, reinterpret_cast(data), + [](intptr_t context) { + Platform::Delete(reinterpret_cast(context)); + }) != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to schedule switch worker"); Platform::Delete(data); @@ -789,7 +797,8 @@ void AppTask::ProcessClosureControlBindingCommand(CommandId commandId, const Bin { VerifyOrDie(peer_device->ConnectionReady()); Messaging::ExchangeManager * exchangeMgr = peer_device->GetExchangeManager(); - const SessionHandle & sessionHandle = peer_device->GetSecureSession().Value(); + auto optionalSession = peer_device->GetSecureSession(); + const SessionHandle & sessionHandle = optionalSession.Value(); switch (commandId) { diff --git a/examples/platform/silabs/BaseApplication.cpp b/examples/platform/silabs/BaseApplication.cpp index 98cadf620d..ce0665d0fc 100644 --- a/examples/platform/silabs/BaseApplication.cpp +++ b/examples/platform/silabs/BaseApplication.cpp @@ -76,6 +76,7 @@ #include #if OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE #include +#include #endif // OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE #endif // SL_USE_THREAD_DIRECT @@ -222,6 +223,7 @@ struct PendingThreadDirectWork { chip::DeviceLayer::AsyncWorkFunct work; intptr_t arg; + chip::DeviceLayer::AsyncWorkFunct onCancel; }; constexpr size_t kPendingThreadDirectWorkQueueSize = 4; @@ -234,7 +236,6 @@ constexpr osMessageQueueAttr_t pendingThreadDirectWorkQueueAttr = { .cb_mem = & .mq_size = sizeof(sPendingThreadDirectWorkQueueBuffer) }; -// Written from the OpenThread task, read from the CHIP/App task. std::atomic sThreadDirectLinked{ false }; void DrainPendingThreadDirectWork(AppEvent *) @@ -242,25 +243,69 @@ void DrainPendingThreadDirectWork(AppEvent *) PendingThreadDirectWork pending; while (osMessageQueueGet(sPendingThreadDirectWorkQueue, &pending, nullptr, 0) == osOK) { - TEMPORARY_RETURN_IGNORED PlatformMgr().ScheduleWork(pending.work, pending.arg); + CHIP_ERROR err = PlatformMgr().ScheduleWork(pending.work, pending.arg); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Failed to schedule pending Thread Direct work: %" CHIP_ERROR_FORMAT, err.Format()); + VerifyOrDo(pending.onCancel == nullptr, pending.onCancel(pending.arg)); + } } } #if OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE -void NotifyResolverThreadDirectLinked(intptr_t) +CHIP_ERROR InvalidateThreadDirectPeerSessions() { - static_cast(chip::AddressResolve::Resolver::Instance()).OnThreadDirectLinked(); + chip::PeerId peerId; + CHIP_ERROR err = chip::DeviceLayer::Internal::PreCommissioning::GetInstance().GetTargetPeerId(peerId); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(AppServer, "Failed to get Thread Direct peer: %" CHIP_ERROR_FORMAT, err.Format())); + + auto & server = chip::Server::GetInstance(); + const auto * fabric = server.GetFabricTable().FindFabricWithCompressedId(peerId.GetCompressedFabricId()); + VerifyOrReturnError(fabric != nullptr, CHIP_ERROR_NOT_FOUND, ChipLogError(AppServer, "Thread Direct peer fabric not found")); + + server.GetSecureSessionManager().MarkSessionsAsDefunct( + chip::ScopedNodeId(peerId.GetNodeId(), fabric->GetFabricIndex()), + chip::MakeOptional(chip::Transport::SecureSession::Type::kCASE)); + ChipLogProgress(AppServer, "TD linked: prior peer CASE sessions marked defunct"); + + return CHIP_NO_ERROR; } -void NotifyResolverThreadDirectUnlinked(intptr_t) +void CancelPendingThreadDirectWork() { - static_cast(chip::AddressResolve::Resolver::Instance()).OnThreadDirectUnlinked(); + PendingThreadDirectWork pending; + for (size_t count = 0; count < kPendingThreadDirectWorkQueueSize; ++count) + { + VerifyOrReturn(osMessageQueueGet(sPendingThreadDirectWorkQueue, &pending, nullptr, 0) == osOK); + VerifyOrDo(pending.onCancel == nullptr, pending.onCancel(pending.arg)); + } } void NotifyResolverThreadDirectLinkFailed(intptr_t) { + sThreadDirectLinked.store(false, std::memory_order_relaxed); + CancelPendingThreadDirectWork(); static_cast(chip::AddressResolve::Resolver::Instance()).OnThreadDirectLinkFailed(); } + +void NotifyResolverThreadDirectLinked(intptr_t) +{ + CHIP_ERROR err = InvalidateThreadDirectPeerSessions(); + if (err != CHIP_NO_ERROR) + { + NotifyResolverThreadDirectLinkFailed(0); + return; + } + sThreadDirectLinked.store(true, std::memory_order_relaxed); + static_cast(chip::AddressResolve::Resolver::Instance()).OnThreadDirectLinked(); + DrainPendingThreadDirectWork(nullptr); +} + +void NotifyResolverThreadDirectUnlinked(intptr_t) +{ + static_cast(chip::AddressResolve::Resolver::Instance()).OnThreadDirectUnlinked(); +} #endif // OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE class ThreadDirectLinkDelegate : public chip::DeviceLayer::ThreadDirectDelegate @@ -268,27 +313,40 @@ class ThreadDirectLinkDelegate : public chip::DeviceLayer::ThreadDirectDelegate public: void OnThreadDirectLinked() override { - sThreadDirectLinked.store(true, std::memory_order_relaxed); + VerifyOrReturn(!sThreadDirectLinked.load(std::memory_order_relaxed)); #if OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE - // OT callbacks run off the Matter stack; hop before touching AddressResolve / SystemLayer. - TEMPORARY_RETURN_IGNORED PlatformMgr().ScheduleWork(NotifyResolverThreadDirectLinked); -#endif // OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE + CHIP_ERROR err = PlatformMgr().ScheduleWork(NotifyResolverThreadDirectLinked); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Failed to schedule Thread Direct link preparation: %" CHIP_ERROR_FORMAT, err.Format()); + } +#else + sThreadDirectLinked.store(true, std::memory_order_relaxed); AppEvent event = {}; event.Handler = DrainPendingThreadDirectWork; BaseApplication::PostEvent(&event); +#endif // OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE } void OnThreadDirectUnlinked() override { sThreadDirectLinked.store(false, std::memory_order_relaxed); #if OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE - TEMPORARY_RETURN_IGNORED PlatformMgr().ScheduleWork(NotifyResolverThreadDirectUnlinked); + CHIP_ERROR err = PlatformMgr().ScheduleWork(NotifyResolverThreadDirectUnlinked); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Failed to schedule Thread Direct unlink notification: %" CHIP_ERROR_FORMAT, err.Format()); + } #endif // OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE } void OnThreadDirectLinkFailed() override { sThreadDirectLinked.store(false, std::memory_order_relaxed); #if OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE - TEMPORARY_RETURN_IGNORED PlatformMgr().ScheduleWork(NotifyResolverThreadDirectLinkFailed); + CHIP_ERROR err = PlatformMgr().ScheduleWork(NotifyResolverThreadDirectLinkFailed); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Failed to schedule Thread Direct failure notification: %" CHIP_ERROR_FORMAT, err.Format()); + } #endif // OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE } }; @@ -1066,20 +1124,22 @@ void BaseApplication::DispatchEvent(AppEvent * aEvent) } } -CHIP_ERROR BaseApplication::ScheduleWorkGatedOnThreadDirectLink(chip::DeviceLayer::AsyncWorkFunct work, intptr_t arg) +CHIP_ERROR BaseApplication::ScheduleWorkGatedOnThreadDirectLink(chip::DeviceLayer::AsyncWorkFunct work, intptr_t arg, + chip::DeviceLayer::AsyncWorkFunct onCancel) { #if SL_USE_THREAD_DIRECT && OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE if (sThreadDirectLinked.load(std::memory_order_relaxed)) { return PlatformMgr().ScheduleWork(work, arg); } - - PendingThreadDirectWork pending{ work, arg }; - VerifyOrReturnError(osMessageQueuePut(sPendingThreadDirectWorkQueue, &pending, osPriorityNormal, 0) == osOK, - CHIP_ERROR_NO_MEMORY, ChipLogError(AppServer, "Failed to queue pending Thread Direct work")); + PendingThreadDirectWork pending{ work, arg, onCancel }; + osStatus_t queueStatus = osMessageQueuePut(sPendingThreadDirectWorkQueue, &pending, osPriorityNormal, 0); + VerifyOrReturnError(queueStatus == osOK, CHIP_ERROR_NO_MEMORY, + ChipLogError(AppServer, "Failed to queue pending Thread Direct work")); ThreadStackMgrImpl().ThreadDirectSendWakeup(); return CHIP_NO_ERROR; #else + static_cast(onCancel); return PlatformMgr().ScheduleWork(work, arg); #endif // SL_USE_THREAD_DIRECT } diff --git a/examples/platform/silabs/BaseApplication.h b/examples/platform/silabs/BaseApplication.h index 3c0e9d189b..e5696b1920 100644 --- a/examples/platform/silabs/BaseApplication.h +++ b/examples/platform/silabs/BaseApplication.h @@ -141,8 +141,11 @@ class BaseApplication * * @param work Work function to run on the CHIP task once the link is established * @param arg Context passed to work function + * @param onCancel Optional cleanup if accepted work fails or times out waiting for the link. + * @note If this function returns an error, the caller remains responsible for arg. */ - static CHIP_ERROR ScheduleWorkGatedOnThreadDirectLink(chip::DeviceLayer::AsyncWorkFunct work, intptr_t arg = 0); + static CHIP_ERROR ScheduleWorkGatedOnThreadDirectLink(chip::DeviceLayer::AsyncWorkFunct work, intptr_t arg = 0, + chip::DeviceLayer::AsyncWorkFunct onCancel = nullptr); #if SL_MATTER_DISPLAY_ENABLED /**