blob: 6c464176d92bce65b36a38542972533e68d2f73b [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* Copyright (c) 2014-2017 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
* This file implements LayerImplLwIP using LwIP.
*/
#include <lib/support/CodeUtils.h>
#include <system/LwIPEventSupport.h>
#include <system/SystemFaultInjection.h>
#include <system/SystemLayer.h>
#include <system/SystemLayerImplLwIP.h>
namespace chip {
namespace System {
LayerImplLwIP::LayerImplLwIP() : mHandlingTimerComplete(false), mEventDelegateList(nullptr) {}
CHIP_ERROR LayerImplLwIP::Init()
{
VerifyOrReturnError(!mLayerState.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
RegisterLwIPErrorFormatter();
ReturnErrorOnFailure(mTimerList.Init());
VerifyOrReturnError(mLayerState.Init(), CHIP_ERROR_INCORRECT_STATE);
return CHIP_NO_ERROR;
}
CHIP_ERROR LayerImplLwIP::Shutdown()
{
VerifyOrReturnError(mLayerState.Shutdown(), CHIP_ERROR_INCORRECT_STATE);
mLayerState.Reset(); // Return to uninitialized state to permit re-initialization.
return CHIP_NO_ERROR;
}
CHIP_ERROR LayerImplLwIP::StartTimer(uint32_t delayMilliseconds, TimerCompleteCallback onComplete, void * appState)
{
VerifyOrReturnError(mLayerState.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
CHIP_SYSTEM_FAULT_INJECT(FaultInjection::kFault_TimeoutImmediate, delayMilliseconds = 0);
CancelTimer(onComplete, appState);
Timer * timer = Timer::New(*this, delayMilliseconds, onComplete, appState);
VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY);
if (mTimerList.Add(timer) == timer)
{
// this is the new earliest timer and so the timer needs (re-)starting provided that
// the system is not currently processing expired timers, in which case it is left to
// HandleExpiredTimers() to re-start the timer.
if (!mHandlingTimerComplete)
{
StartPlatformTimer(delayMilliseconds);
}
}
return CHIP_NO_ERROR;
}
void LayerImplLwIP::CancelTimer(TimerCompleteCallback onComplete, void * appState)
{
VerifyOrReturn(mLayerState.IsInitialized());
Timer * timer = mTimerList.Remove(onComplete, appState);
VerifyOrReturn(timer != nullptr);
timer->Clear();
timer->Release();
}
CHIP_ERROR LayerImplLwIP::ScheduleWork(TimerCompleteCallback onComplete, void * appState)
{
VerifyOrReturnError(mLayerState.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
Timer * timer = Timer::New(*this, 0, onComplete, appState);
VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY);
return ScheduleLambda([timer] { timer->HandleComplete(); });
}
bool LayerLwIP::EventHandlerDelegate::IsInitialized() const
{
return mFunction != nullptr;
}
void LayerLwIP::EventHandlerDelegate::Init(EventHandlerFunction aFunction)
{
mFunction = aFunction;
mNextDelegate = nullptr;
}
void LayerLwIP::EventHandlerDelegate::Prepend(const LayerLwIP::EventHandlerDelegate *& aDelegateList)
{
mNextDelegate = aDelegateList;
aDelegateList = this;
}
CHIP_ERROR LayerImplLwIP::AddEventHandlerDelegate(EventHandlerDelegate & aDelegate)
{
LwIPEventHandlerDelegate & lDelegate = static_cast<LwIPEventHandlerDelegate &>(aDelegate);
VerifyOrReturnError(lDelegate.GetFunction() != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
lDelegate.Prepend(mEventDelegateList);
return CHIP_NO_ERROR;
}
CHIP_ERROR LayerImplLwIP::ScheduleLambdaBridge(const LambdaBridge & bridge)
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
CHIP_ERROR lReturn = PlatformEventing::ScheduleLambdaBridge(*this, bridge);
if (lReturn != CHIP_NO_ERROR)
{
ChipLogError(chipSystemLayer, "Failed to queue CHIP System Layer lambda event: %s", ErrorStr(lReturn));
}
return lReturn;
}
CHIP_ERROR LayerImplLwIP::PostEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument)
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
CHIP_ERROR lReturn = PlatformEventing::PostEvent(*this, aTarget, aEventType, aArgument);
if (lReturn != CHIP_NO_ERROR)
{
ChipLogError(chipSystemLayer, "Failed to queue CHIP System Layer event (type %d): %s", aEventType, ErrorStr(lReturn));
}
return lReturn;
}
/**
* This implements the actual dispatch and handling of a CHIP System Layer event.
*
* @param[in,out] aTarget A reference to the layer object to which the event is targeted.
* @param[in] aEventType The event / message type to handle.
* @param[in] aArgument The argument associated with the event / message.
*
* @retval CHIP_NO_ERROR On success.
* @retval CHIP_ERROR_INCORRECT_STATE If the state of the InetLayer object is incorrect.
* @retval CHIP_ERROR_UNEXPECTED_EVENT If the event type is unrecognized.
*/
CHIP_ERROR LayerImplLwIP::HandleEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument)
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
// Prevent the target object from being freed while dispatching the event.
aTarget.Retain();
CHIP_ERROR lReturn = CHIP_ERROR_UNEXPECTED_EVENT;
const LwIPEventHandlerDelegate * lEventDelegate = static_cast<const LwIPEventHandlerDelegate *>(mEventDelegateList);
while (lReturn == CHIP_ERROR_UNEXPECTED_EVENT && lEventDelegate != nullptr)
{
lReturn = lEventDelegate->GetFunction()(aTarget, aEventType, aArgument);
lEventDelegate = lEventDelegate->GetNextDelegate();
}
if (lReturn == CHIP_ERROR_UNEXPECTED_EVENT)
{
ChipLogError(chipSystemLayer, "Unexpected event type %d", aEventType);
}
/*
Release the reference to the target object. When the object's lifetime finally comes to an end, in most cases this will be
the release call that decrements the ref count to zero.
*/
aTarget.Release();
return lReturn;
}
/**
* Start the platform timer with specified millsecond duration.
*
* @brief
* Calls the Platform specific API to start a platform timer. This API is called by the chip::System::Timer class when
* one or more timers are active and require deferred execution.
*
* @param[in] aDelayMilliseconds The timer duration in milliseconds.
*
* @return CHIP_NO_ERROR on success, error code otherwise.
*
*/
CHIP_ERROR LayerImplLwIP::StartPlatformTimer(uint32_t aDelayMilliseconds)
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
CHIP_ERROR status = PlatformEventing::StartTimer(*this, aDelayMilliseconds);
return status;
}
/**
* Handle the platform timer expiration event. Completes any timers that have expired.
*
* A static API that gets called when the platform timer expires. Any expired timers are completed and removed from the list
* of active timers in the layer object. If unexpired timers remain on completion, StartPlatformTimer will be called to
* restart the platform timer.
*
* It is assumed that this API is called only while on the thread which owns the CHIP System Layer object.
*
* @note
* It's harmless if this API gets called and there are no expired timers.
*
* @return CHIP_NO_ERROR on success, error code otherwise.
*
*/
CHIP_ERROR LayerImplLwIP::HandlePlatformTimer()
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
// Expire each timer in turn until an unexpired timer is reached or the timerlist is emptied. We set the current expiration
// time outside the loop; that way timers set after the current tick will not be executed within this expiration window
// regardless how long the processing of the currently expired timers took
Clock::MonotonicMilliseconds currentTime = SystemClock().GetMonotonicMilliseconds();
// limit the number of timers handled before the control is returned to the event queue. The bound is similar to
// (though not exactly same) as that on the sockets-based systems.
// The platform timer API has MSEC resolution so expire any timer with less than 1 msec remaining.
size_t timersHandled = 0;
Timer * timer = nullptr;
while ((timersHandled < CHIP_SYSTEM_CONFIG_NUM_TIMERS) && ((timer = mTimerList.PopIfEarlier(currentTime + 1)) != nullptr))
{
mHandlingTimerComplete = true;
timer->HandleComplete();
mHandlingTimerComplete = false;
timersHandled++;
}
if (!mTimerList.Empty())
{
// timers still exist so restart the platform timer.
uint64_t delayMilliseconds = 0ULL;
currentTime = SystemClock().GetMonotonicMilliseconds();
// the next timer expires in the future, so set the delayMilliseconds to a non-zero value
if (currentTime < mTimerList.Earliest()->mAwakenTime)
{
delayMilliseconds = mTimerList.Earliest()->mAwakenTime - currentTime;
}
/*
* StartPlatformTimer() accepts a 32bit value in milliseconds. Timestamps are 64bit numbers. The only way in which this
* could overflow is if time went backwards (e.g. as a result of a time adjustment from time synchronization). Verify
* that the timer can still be executed (even if it is very late) and exit if that is the case. Note: if the time sync
* ever ends up adjusting the clock, we should implement a method that deals with all the timers in the system.
*/
VerifyOrDie(delayMilliseconds <= UINT32_MAX);
StartPlatformTimer(static_cast<uint32_t>(delayMilliseconds));
}
return CHIP_NO_ERROR;
}
} // namespace System
} // namespace chip