/* * * Copyright (c) 2020 Project CHIP Authors * Copyright (c) 2019 Nest Labs, Inc. * * 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 an implementation of the PlatformManager object. */ /* this file behaves like a config.h, comes first */ #include #include #include #if CONFIG_ENABLE_ASR_LEGA_RTOS #include #else #include #endif #include namespace chip { namespace DeviceLayer { namespace Internal { extern CHIP_ERROR InitLwIPCoreLock(void); } PlatformManagerImpl PlatformManagerImpl::sInstance; CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) { #if CONFIG_ENABLE_ASR_LEGA_RTOS CHIP_ERROR err = CHIP_NO_ERROR; OSStatus result; /* Initialize LwIP lock. */ err = Internal::InitLwIPCoreLock(); SuccessOrExit(err); if (mEventQueue == NULL) { /* Initialize the event queue. */ result = lega_rtos_init_queue(&mEventQueue, "EventQueue", sizeof(ChipDeviceEvent), CHIP_DEVICE_CONFIG_MAX_EVENT_QUEUE_SIZE); VerifyOrExit(result == kNoErr, err = CHIP_ERROR_NO_MEMORY); } /* Initialize the timeout. */ lega_rtos_set_timeout(&mNextTimerBaseTime); if (mChipMutex == NULL) { /* Initialize the mutex. */ result = lega_rtos_init_mutex(&mChipMutex); VerifyOrExit(result == kNoErr, err = CHIP_ERROR_INTERNAL); } mChipTimerActive = false; mShouldRunEventLoop.store(false); ReturnErrorOnFailure(GenericPlatformManagerImpl::_InitChipStack()); #else CHIP_ERROR err; // Make sure the LwIP core lock has been initialized err = Internal::InitLwIPCoreLock(); SuccessOrExit(err); mStartTime = System::SystemClock().GetMonotonicTimestamp(); // Call _InitChipStack() on the generic implementation base class // to finish the initialization process. err = Internal::GenericPlatformManagerImpl_FreeRTOS::_InitChipStack(); SuccessOrExit(err); #endif exit: return err; } #if CONFIG_ENABLE_ASR_LEGA_RTOS void PlatformManagerImpl::_RunEventLoop() { RunEventLoopInternal(); } void PlatformManagerImpl::RunEventLoopInternal(void) { CHIP_ERROR err; ChipDeviceEvent event; // Lock the CHIP stack. StackLock lock; bool oldShouldRunEventLoop = false; if (!mShouldRunEventLoop.compare_exchange_strong(oldShouldRunEventLoop /* expected */, true /* desired */)) { ChipLogError(DeviceLayer, "Error trying to run the event loop while it is already running"); return; } while (mShouldRunEventLoop.load()) { uint32_t waitTime; // If one or more CHIP timers are active... if (mChipTimerActive) { // Adjust the base time and remaining duration for the next scheduled timer based on the // amount of time that has elapsed since it was started. // IF the timer's expiration time has already arrived... if (lega_rtos_check_timeout(&mNextTimerBaseTime, &mNextTimerDurationTicks) == TRUE) { // Reset the 'timer active' flag. This will be set to true again by _StartChipTimer() // if there are further timers beyond the expired one that are still active. mChipTimerActive = false; // Call into the system layer to dispatch the callback functions for all timers // that have expired. err = static_cast(DeviceLayer::SystemLayer()).HandlePlatformTimer(); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Error handling CHIP timers: %" CHIP_ERROR_FORMAT, err.Format()); } // When processing the event queue below, do not wait if the queue is empty. Instead // immediately loop around and process timers again waitTime = 0; } // If there is still time before the next timer expires, arrange to wait on the event queue // until that timer expires. else { waitTime = mNextTimerDurationTicks; } } // Otherwise no CHIP timers are active, so wait indefinitely for an event to arrive on the event // queue. else { waitTime = LEGA_WAIT_FOREVER; } OSStatus result; { // Unlock the CHIP stack, allowing other threads to enter CHIP while // the event loop thread is sleeping. StackUnlock unlock; result = lega_rtos_pop_from_queue(&mEventQueue, &event, waitTime); } // If an event was received, dispatch it and continue until the queue is empty. while (result == kNoErr) { DispatchEvent(&event); result = lega_rtos_pop_from_queue(&mEventQueue, &event, LEGA_NO_WAIT); } } } CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask(void) { lega_task_config_t cfg; MatterInitializer::Matter_Task_Config(&cfg); OSStatus result = lega_rtos_create_thread(&mThread, cfg.task_priority, CHIP_DEVICE_CONFIG_CHIP_TASK_NAME, (lega_thread_function_t) EventLoopTaskMain, cfg.stack_size, (lega_thread_arg_t) this); if (result != kNoErr) { return CHIP_ERROR_INTERNAL; } return CHIP_NO_ERROR; } CHIP_ERROR PlatformManagerImpl::_StopEventLoopTask() { mShouldRunEventLoop.store(false); return CHIP_NO_ERROR; } void PlatformManagerImpl::_LockChipStack(void) { OSStatus result = lega_rtos_lock_mutex(&mChipMutex, LEGA_WAIT_FOREVER); VerifyOrReturn(result == kNoErr, ChipLogError(DeviceLayer, "%s %x", __func__, result)); } bool PlatformManagerImpl::_TryLockChipStack(void) { if (lega_rtos_lock_mutex(&mChipMutex, LEGA_NO_WAIT) == kNoErr) { return true; } else { return false; } } void PlatformManagerImpl::_UnlockChipStack(void) { OSStatus result = lega_rtos_unlock_mutex(&mChipMutex); VerifyOrReturn(result == kNoErr, ChipLogError(DeviceLayer, "%s %x", __func__, result)); } CHIP_ERROR PlatformManagerImpl::_PostEvent(const ChipDeviceEvent * event) { OSStatus result = lega_rtos_push_to_queue(&mEventQueue, const_cast(event), LEGA_NO_WAIT); if (kNoErr != result) { ChipLogError(DeviceLayer, "lega_rtos_push_to_queue %u", result); return CHIP_ERROR_INTERNAL; } return CHIP_NO_ERROR; } CHIP_ERROR PlatformManagerImpl::_StartChipTimer(System::Clock::Timeout durationMS) { mChipTimerActive = true; lega_rtos_set_timeout(&mNextTimerBaseTime); mNextTimerDurationTicks = (lega_tick_t) ms_to_tick(System::Clock::Milliseconds64(durationMS).count()); // If the platform timer is being updated by a thread other than the event loop thread, // trigger the event loop thread to recalculate its wait time by posting a no-op event // to the event queue. if (lega_rtos_get_current_thread() != mThread) { ChipDeviceEvent noop{ .Type = DeviceEventType::kNoOp }; ReturnErrorOnFailure(PostEvent(&noop)); } return CHIP_NO_ERROR; } void PlatformManagerImpl::EventLoopTaskMain(uint32_t arg) { ChipLogDetail(DeviceLayer, "CHIP task running"); PlatformMgrImpl().RunEventLoopInternal(); lega_rtos_delete_thread(NULL); } #else CHIP_ERROR PlatformManagerImpl::InitLwIPCoreLock(void) { return Internal::InitLwIPCoreLock(); } #endif void PlatformManagerImpl::_Shutdown() { uint64_t upTime = 0; if (GetDiagnosticDataProvider().GetUpTime(upTime) == CHIP_NO_ERROR) { uint32_t totalOperationalHours = 0; if (ConfigurationMgr().GetTotalOperationalHours(totalOperationalHours) == CHIP_NO_ERROR) { ConfigurationMgr().StoreTotalOperationalHours(totalOperationalHours + static_cast(upTime / 3600)); } else { ChipLogError(DeviceLayer, "Failed to get total operational hours of the Node"); } } else { ChipLogError(DeviceLayer, "Failed to get current uptime since the Node’s last reboot"); } #if CONFIG_ENABLE_ASR_LEGA_RTOS // // Call up to the base class _Shutdown() to perform the actual stack de-initialization // and clean-up // Internal::GenericPlatformManagerImpl::_Shutdown(); #else Internal::GenericPlatformManagerImpl_FreeRTOS::_Shutdown(); #endif } } // namespace DeviceLayer } // namespace chip