blob: 896617d781e31fc46aed2d7e7028edb297980ccf [file] [log] [blame]
/*
*
* 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 <platform/internal/testing/ConfigUnitTest.h>
#include <platform/CHIPDeviceLayer.h>
#include <platform/PlatformManager.h>
#include <platform/internal/GenericPlatformManagerImpl.ipp>
#include <platform/openiotsdk/DiagnosticDataProviderImpl.h>
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<LayerImpl &>(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<ImplClass>::_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<Milliseconds32>(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<ImplClass>::_Shutdown();
}
// ===== Members for internal use by the following friends.
PlatformManagerImpl PlatformManagerImpl::sInstance;
} // namespace DeviceLayer
} // namespace chip