/* * * Copyright (c) 2022 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * Provides the implementation of the Device Layer Platform Manager class * for Open IOT SDK platform. */ #include "OpenIoTSDKArchUtils.h" #include "platform/internal/CHIPDeviceLayerInternal.h" #include #include #include #include #include using namespace ::chip; using namespace ::chip::Inet; using namespace ::chip::System; using namespace chip::System::Clock; namespace chip { namespace DeviceLayer { struct ScopedLock { ScopedLock(osMutexId_t & lockable) : _lockable(lockable) { osMutexAcquire(_lockable, osWaitForever); } ScopedLock(const ScopedLock &) = delete; ScopedLock & operator=(const ScopedLock &) = delete; ~ScopedLock() { osMutexRelease(_lockable); } private: osMutexId_t & _lockable; }; namespace { LayerImpl & SystemLayerImpl() { return static_cast(DeviceLayer::SystemLayer()); } } // anonymous namespace CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) { // Members are initialized by the stack osMutexAttr_t mut_att = { .attr_bits = osMutexRecursive }; // Reinitialize the Mutexes if (mChipStackMutex == nullptr) { mChipStackMutex = osMutexNew(&mut_att); } if (mEventTaskMutex == nullptr) { mEventTaskMutex = osMutexNew(nullptr); } if (mPlatformFlags == nullptr) { mPlatformFlags = osEventFlagsNew(nullptr); } if (mQueue == nullptr) { mQueue = osMessageQueueNew(CHIP_DEVICE_CONFIG_MAX_EVENT_QUEUE_SIZE, sizeof(ChipDeviceEvent), nullptr); } else { osMessageQueueReset(mQueue); } if (!mChipStackMutex || !mEventTaskMutex || !mPlatformFlags || !mQueue) { osMutexDelete(mChipStackMutex); osMutexDelete(mEventTaskMutex); osEventFlagsDelete(mPlatformFlags); osMessageQueueDelete(mQueue); mChipStackMutex = mEventTaskMutex = mPlatformFlags = mQueue = nullptr; return CHIP_ERROR_INTERNAL; } ReturnLogErrorOnFailure(PlatformTimerInit()); mRunEventLoop.store(false); mInitialized = true; // Call up to the base class _InitChipStack() to perform the bulk of the initialization. ReturnLogErrorOnFailure(GenericPlatformManagerImpl::_InitChipStack()); SetConfigurationMgr(&ConfigurationManagerImpl::GetDefaultInstance()); SetDiagnosticDataProvider(&DiagnosticDataProviderImpl::GetDefaultInstance()); return CHIP_NO_ERROR; } void PlatformManagerImpl::_LockChipStack() { osMutexAcquire(mChipStackMutex, osWaitForever); } bool PlatformManagerImpl::_TryLockChipStack() { if (osMutexAcquire(mChipStackMutex, 0U) == osOK) { return true; } else { return false; } } void PlatformManagerImpl::_UnlockChipStack() { osMutexRelease(mChipStackMutex); } CHIP_ERROR PlatformManagerImpl::_PostEvent(const ChipDeviceEvent * eventPtr) { if (!mInitialized) { ChipLogError(DeviceLayer, "_PostEvent: stack not initialized"); return CHIP_ERROR_INCORRECT_STATE; } osStatus_t status = osMessageQueuePut(mQueue, eventPtr, 0, 0); osEventFlagsSet(mPlatformFlags, kPostEventFlag); return (status == osOK) ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; } void PlatformManagerImpl::HandlePostEvent() { /* handle an event */ ChipDeviceEvent event; uint32_t count = osMessageQueueGetCount(mQueue); while (count) { if (osMessageQueueGet(mQueue, &event, nullptr, 0) != osOK) { break; } DispatchEvent(&event); count--; } } void PlatformManagerImpl::HandleTimerEvent(void) { CHIP_ERROR err = SystemLayerImpl().HandlePlatformTimer(); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "HandlePlatformTimer %ld", err.AsInteger()); } } void PlatformManagerImpl::_RunEventLoop() { uint32_t flags = 0; if (!mInitialized) { ChipLogError(DeviceLayer, "_PostEvent: stack not initialized"); return; } { ScopedLock lock(mEventTaskMutex); bool expectedValue = false; if (!mRunEventLoop.compare_exchange_strong(expectedValue /* expected */, true /* desired */)) { ChipLogError(DeviceLayer, "Error trying to run the event loop while it is already running"); return; } // Look if a task ID has already been assigned or not. // If not, it means we run in the thread that called RunEventLoop if (!mEventTask) { ChipLogDetail(DeviceLayer, "Run CHIP event loop on external thread"); mEventTask = osThreadGetId(); } else { osEventFlagsSet(mPlatformFlags, kTaskHasEventLoopRunFlag); } } LockChipStack(); while (mRunEventLoop.load()) { UnlockChipStack(); flags = osEventFlagsWait(mPlatformFlags, kPostEventFlag | kTimerEventFlag, osFlagsWaitAny, osWaitForever); LockChipStack(); // In case of error we still need to know the value of flags we're not waiting for if (flags & osFlagsError) { flags = osEventFlagsGet(mPlatformFlags); } if (flags & kTimerEventFlag) { HandleTimerEvent(); } if (flags & kPostEventFlag) { HandlePostEvent(); } } UnlockChipStack(); osEventFlagsSet(mPlatformFlags, kTaskHasEventLoopStopFlag); mEventTask = nullptr; } void PlatformManagerImpl::EventLoopTask(void * arg) { (void) arg; PlatformMgrImpl().RunEventLoop(); osThreadTerminate(osThreadGetId()); } CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask() { if (!mInitialized) { ChipLogError(DeviceLayer, "_StartEventLoopTask: stack not initialized"); return CHIP_ERROR_INCORRECT_STATE; } { ScopedLock lock(mEventTaskMutex); const osThreadAttr_t tread_attr = { .name = CHIP_DEVICE_CONFIG_CHIP_TASK_NAME, .stack_size = CHIP_DEVICE_CONFIG_CHIP_TASK_STACK_SIZE, .priority = osPriorityNormal, }; mEventTask = osThreadNew(EventLoopTask, NULL, &tread_attr); if (mEventTask == nullptr) { ChipLogError(DeviceLayer, "Create event loop thread failed"); return CHIP_ERROR_INTERNAL; } } if (osEventFlagsWait(mPlatformFlags, kTaskHasEventLoopRunFlag, osFlagsWaitAny, osWaitForever) & osFlagsError) { ChipLogError(DeviceLayer, "Start event loop thread failed"); return CHIP_ERROR_INTERNAL; } return CHIP_NO_ERROR; } CHIP_ERROR PlatformManagerImpl::_StopEventLoopTask() { { ScopedLock lock(mEventTaskMutex); // Early return if the event loop is not running if (!mRunEventLoop.load()) { return CHIP_NO_ERROR; } // Indicate that the event loop store mRunEventLoop.store(false); } osEventFlagsSet(mPlatformFlags, kPostEventFlag); // If the thread running the event loop is different from the caller // then wait it to finish if (mEventTask != nullptr && mEventTask != osThreadGetId()) { if (osEventFlagsWait(mPlatformFlags, kTaskHasEventLoopStopFlag, osFlagsWaitAny, ms2tick(1000)) & osFlagsError) { ChipLogError(DeviceLayer, "Stop event loop thread failed"); return CHIP_ERROR_INTERNAL; } } return CHIP_NO_ERROR; } void PlatformManagerImpl::SetEventFlags(uint32_t flags) { osEventFlagsSet(mPlatformFlags, flags); } void PlatformManagerImpl::TimerCallback(void * arg) { PlatformMgrImpl().SetEventFlags(kTimerEventFlag); } CHIP_ERROR PlatformManagerImpl::PlatformTimerInit() { if (mTimer != nullptr) { return CHIP_NO_ERROR; } mTimer = osTimerNew(TimerCallback, osTimerOnce, NULL, NULL); if (!mTimer) { return CHIP_ERROR_INTERNAL; } return CHIP_NO_ERROR; } CHIP_ERROR PlatformManagerImpl::PlatformTimerDeinit() { if (mTimer == nullptr) { return CHIP_NO_ERROR; } osTimerDelete(mTimer); mTimer = nullptr; return CHIP_NO_ERROR; } CHIP_ERROR PlatformManagerImpl::_StartChipTimer(System::Clock::Timeout duration) { if (duration.count() == 0) { TimerCallback(0); } else { Milliseconds32 msec = std::chrono::duration_cast(duration); auto res = osTimerStart(mTimer, ms2tick(msec.count())); if (res) { ChipLogError(DeviceLayer, "osTimerStart failed %d", res); return CHIP_ERROR_INTERNAL; } } return CHIP_NO_ERROR; } void PlatformManagerImpl::_Shutdown() { // // Call up to the base class _Shutdown() to perform the actual stack de-initialization // and clean-up // (void) _StopEventLoopTask(); osMutexDelete(mChipStackMutex); osMutexDelete(mEventTaskMutex); osEventFlagsDelete(mPlatformFlags); osMessageQueueDelete(mQueue); PlatformTimerDeinit(); mChipStackMutex = nullptr; mPlatformFlags = nullptr; mEventTaskMutex = nullptr; mQueue = nullptr; mInitialized = false; GenericPlatformManagerImpl::_Shutdown(); } // ===== Members for internal use by the following friends. PlatformManagerImpl PlatformManagerImpl::sInstance; } // namespace DeviceLayer } // namespace chip