blob: 3a2533ea11ac0bc52603a55d1c8780647ad0ba2e [file] [log] [blame]
/*
*
* Copyright (c) 2016-2017 Nest Labs, Inc.
* 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
* This file contains definitions of the nl::Weave::System::Layer
* class methods and related data and functions.
*/
// Include module header
#include <SystemLayer/SystemLayer.h>
// Include common private header
#include "SystemLayerPrivate.h"
// Include local headers
#include <SystemLayer/SystemTimer.h>
// Include additional Weave headers
#include <Weave/Support/logging/WeaveLogging.h>
#include <Weave/Support/NLDLLUtil.h>
#include <Weave/Support/CodeUtils.h>
// Include system and language headers
#include <stddef.h>
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
#if !WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_EVENT_FUNCTIONS
#include <lwip/err.h>
#include <lwip/sys.h>
#endif // !WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_EVENT_FUNCTIONS
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_POSIX_LOCKING
#include <pthread.h>
// Choose an approximation of PTHREAD_NULL if pthread.h doesn't define one.
#ifndef PTHREAD_NULL
#define PTHREAD_NULL 0
#endif // PTHREAD_NULL
#endif // WEAVE_SYSTEM_CONFIG_POSIX_LOCKING
namespace nl {
namespace Weave {
namespace System {
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
bool LwIPEventHandlerDelegate::IsInitialized() const
{
return this->mFunction != NULL;
}
void LwIPEventHandlerDelegate::Init(LwIPEventHandlerFunction aFunction)
{
this->mFunction = aFunction;
this->mNextDelegate = NULL;
}
void LwIPEventHandlerDelegate::Prepend(const LwIPEventHandlerDelegate*& aDelegateList)
{
this->mNextDelegate = aDelegateList;
aDelegateList = this;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
Layer::Layer()
: mLayerState(kLayerState_NotInitialized),
mContext(NULL), mPlatformData(NULL)
{
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
if (!sSystemEventHandlerDelegate.IsInitialized())
sSystemEventHandlerDelegate.Init(HandleSystemLayerEvent);
this->mEventDelegateList = NULL;
this->mTimerList = NULL;
this->mTimerComplete = false;
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
this->mWakePipeIn = 0;
this->mWakePipeOut = 0;
#if WEAVE_SYSTEM_CONFIG_POSIX_LOCKING
this->mHandleSelectThread = PTHREAD_NULL;
#endif // WEAVE_SYSTEM_CONFIG_POSIX_LOCKING
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
}
Error Layer::Init(void* aContext)
{
Error lReturn;
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
int lPipeFDs[2];
int lOSReturn, lFlags;
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
if (this->mLayerState != kLayerState_NotInitialized)
return WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE;
lReturn = Platform::Layer::WillInit(*this, aContext);
SuccessOrExit(lReturn);
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
this->AddEventHandlerDelegate(sSystemEventHandlerDelegate);
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
// Create a Unix pipe to allow an arbitrary thread to wake the thread in the select loop.
lOSReturn = ::pipe(lPipeFDs);
VerifyOrExit(lOSReturn == 0, lReturn = nl::Weave::System::MapErrorPOSIX(errno));
this->mWakePipeIn = lPipeFDs[0];
this->mWakePipeOut = lPipeFDs[1];
// Enable non-blocking mode for both ends of the pipe.
lFlags = ::fcntl(this->mWakePipeIn, F_GETFL, 0);
lOSReturn = ::fcntl(this->mWakePipeIn, F_SETFL, lFlags | O_NONBLOCK);
VerifyOrExit(lOSReturn == 0, lReturn = nl::Weave::System::MapErrorPOSIX(errno));
lFlags = ::fcntl(this->mWakePipeOut, F_GETFL, 0);
lOSReturn = ::fcntl(this->mWakePipeOut, F_SETFL, lFlags | O_NONBLOCK);
VerifyOrExit(lOSReturn == 0, lReturn = nl::Weave::System::MapErrorPOSIX(errno));
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
this->mLayerState = kLayerState_Initialized;
this->mContext = aContext;
exit:
Platform::Layer::DidInit(*this, aContext, lReturn);
return lReturn;
}
Error Layer::Shutdown()
{
Error lReturn;
void* lContext;
if (this->mLayerState == kLayerState_NotInitialized)
return WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE;
lContext = this->mContext;
lReturn = Platform::Layer::WillShutdown(*this, lContext);
SuccessOrExit(lReturn);
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
if (this->mWakePipeOut != -1)
{
::close(this->mWakePipeOut);
this->mWakePipeOut = -1;
this->mWakePipeIn = -1;
}
#endif
for (size_t i = 0; i < Timer::sPool.Size(); ++i)
{
Timer* lTimer = Timer::sPool.Get(*this, i);
if (lTimer != NULL)
{
lTimer->Cancel();
}
}
this->mContext = NULL;
this->mLayerState = kLayerState_NotInitialized;
exit:
Platform::Layer::DidShutdown(*this, lContext, lReturn);
return lReturn;
}
/**
* This returns any client-specific platform data assigned to the instance, if it has been previously set.
*
* @return Client-specific platform data, if is has been previously set; otherwise, NULL.
*/
void* Layer::GetPlatformData() const
{
return this->mPlatformData;
}
/**
* This sets the specified client-specific platform data to the
* instance for later retrieval by the client platform.
*
* @param[in] aPlatformData The client-specific platform data to set.
*
*/
void Layer::SetPlatformData(void* aPlatformData)
{
this->mPlatformData = aPlatformData;
}
Error Layer::NewTimer(Timer*& aTimerPtr)
{
Timer* lTimer = NULL;
if (this->State() != kLayerState_Initialized)
return WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE;
lTimer = Timer::sPool.TryCreate(*this);
aTimerPtr = lTimer;
if (lTimer == NULL)
{
WeaveLogError(WeaveSystemLayer, "Timer pool EMPTY");
return WEAVE_SYSTEM_ERROR_NO_MEMORY;
}
return WEAVE_SYSTEM_NO_ERROR;
}
/**
* @brief
* This method starts a one-shot timer.
*
* @note
* Only a single timer is allowed to be started with the same @a aComplete and @a aAppState
* arguments. If called with @a aComplete and @a aAppState identical to an existing timer,
* the currently-running timer will first be cancelled.
*
* @param[in] aMilliseconds Expiration time in milliseconds.
* @param[in] aComplete A pointer to the function called when timer expires.
* @param[in] aAppState A pointer to the application state object used when timer expires.
*
* @return #WEAVE_SYSTEM_NO_ERROR On success.
* @return #WEAVE_SYSTEM_ERROR_NO_MEMORY If a timer cannot be allocated.
* @return Other Value indicating timer failed to start.
*
*/
Error Layer::StartTimer(uint32_t aMilliseconds, TimerCompleteFunct aComplete, void* aAppState)
{
Error lReturn;
Timer* lTimer;
this->CancelTimer(aComplete, aAppState);
lReturn = this->NewTimer(lTimer);
SuccessOrExit(lReturn);
lReturn = lTimer->Start(aMilliseconds, aComplete, aAppState);
if (lReturn != WEAVE_SYSTEM_NO_ERROR)
{
lTimer->Release();
}
exit:
return lReturn;
}
/**
* @brief
* This method cancels a one-shot timer, started earlier through @p StartTimer().
*
* @note
* The cancellation could fail silently in two different ways. If the timer specified by the combination of the callback
* function and application state object couldn't be found, cancellation could fail. If the timer has fired, but not yet
* removed from memory, cancellation could also fail.
*
* @param[in] aOnComplete A pointer to the callback function used in calling @p StartTimer().
* @param[in] aAppState A pointer to the application state object used in calling @p StartTimer().
*
*/
void Layer::CancelTimer(Layer::TimerCompleteFunct aOnComplete, void* aAppState)
{
if (this->State() != kLayerState_Initialized)
return;
for (size_t i = 0; i < Timer::sPool.Size(); ++i)
{
Timer* lTimer = Timer::sPool.Get(*this, i);
if (lTimer != NULL && lTimer->OnComplete == aOnComplete && lTimer->AppState == aAppState)
{
lTimer->Cancel();
break;
}
}
}
#if WEAVE_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
void Layer::CancelAllMatchingInetTimers(nl::Inet::InetLayer& aInetLayer, void* aOnCompleteInetLayer, void* aAppState)
{
for (size_t i = 0; i < Timer::sPool.Size(); ++i)
{
Timer* lTimer = Timer::sPool.Get(*this, i);
if (lTimer != NULL && lTimer->mInetLayer == &aInetLayer && lTimer->mOnCompleteInetLayer == aOnCompleteInetLayer &&
lTimer->mAppStateInetLayer == aAppState)
{
lTimer->Cancel();
break;
}
}
}
#endif // WEAVE_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
/**
* @brief
* Schedules a function with a signature identical to
* `TimerCompleteFunct` to be run as soon as possible on the Weave
* thread.
*
* @note
* This function could, in principle, be implemented as
* `StartTimer(0, aComplete, aAppState)`. The specification for
* `SystemTimer` however permits certain optimizations that might
* make that implementation impossible. Specifically, `SystemTimer`
* API may only be called from the thread owning the particular
* `SystemLayer`, whereas the `ScheduleWork` may be called from
* any thread. Additionally, whereas the `SystemTimer` API permits
* the invocation of the already expired handler in line,
* `ScheduleWork` guarantees that the handler function will be
* called only after the current Weave event completes.
*
* @param[in] aComplete A pointer to a callback function to be called
* when this timer fires.
*
* @param[in] aAppState A pointer to an application state object to be
* passed to the callback function as argument.
*
* @retval #WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE If the SystemLayer has
* not been initialized.
*
* @retval #WEAVE_SYSTEM_ERROR_NO_MEMORY If the SystemLayer cannot
* allocate a new timer.
*
* @retval #WEAVE_SYSTEM_NO_ERROR On success.
*/
Error Layer::ScheduleWork(TimerCompleteFunct aComplete, void* aAppState)
{
Error lReturn;
Timer* lTimer;
lReturn = this->NewTimer(lTimer);
SuccessOrExit(lReturn);
lReturn = lTimer->ScheduleWork(aComplete, aAppState);
if (lReturn != WEAVE_SYSTEM_NO_ERROR)
{
lTimer->Release();
}
exit:
return lReturn;
}
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
/**
* Prepare the sets of file descriptors for @p select() to work with.
*
* @param[out] aSetSize The range of file descriptors in the file descriptor set.
* @param[in] aReadSet A pointer to the set of readable file descriptors.
* @param[in] aWriteSet A pointer to the set of writable file descriptors.
* @param[in] aExceptionSet A pointer to the set of file descriptors with errors.
* @param[in] aSleepTime A reference to the maximum sleep time.
*/
void Layer::PrepareSelect(int& aSetSize, fd_set* aReadSet, fd_set* aWriteSet, fd_set* aExceptionSet, struct timeval& aSleepTime)
{
if (this->State() != kLayerState_Initialized)
return;
if (this->mWakePipeIn + 1 > aSetSize)
aSetSize = this->mWakePipeIn + 1;
FD_SET(this->mWakePipeIn, aReadSet);
const Timer::Epoch kCurrentEpoch = Timer::GetCurrentEpoch();
Timer::Epoch lAwakenEpoch = kCurrentEpoch + aSleepTime.tv_sec * 1000 + aSleepTime.tv_usec / 1000;
for (size_t i = 0; i < Timer::sPool.Size(); i++)
{
Timer* lTimer = Timer::sPool.Get(*this, i);
if (lTimer != NULL)
{
if (!Timer::IsEarlierEpoch(kCurrentEpoch, lTimer->mAwakenEpoch))
{
lAwakenEpoch = kCurrentEpoch;
break;
}
if (Timer::IsEarlierEpoch(lTimer->mAwakenEpoch, lAwakenEpoch))
lAwakenEpoch = lTimer->mAwakenEpoch;
}
}
const uint64_t kSleepTime = (lAwakenEpoch - kCurrentEpoch) * kTimerFactor_micro_per_milli;
aSleepTime.tv_sec = kSleepTime / 1000000;
aSleepTime.tv_usec = kSleepTime % 1000000;
}
/**
* Handle I/O from a select call. This method registers the pending I/O event in each active endpoint and then invokes the
* respective I/O handling functions for those endpoints.
*
* @note
* It is important to set the pending I/O fields for all endpoints *before* making any callbacks. This avoids the case where an
* endpoint is closed and then re-opened within the callback for another endpoint. When this happens the new endpoint is likely to
* be assigned the same file descriptor as the old endpoint. However, any pending I/O for that file descriptor number represents
* I/O related to the old incarnation of the endpoint, not the current one. Saving the pending I/O state in each endpoint before
* acting on it allows the endpoint code to clear the I/O flags in the event of a close, thus avoiding any confusion.
*
* @param[in] aSetSize The return value of the select call.
* @param[in] aReadSet A pointer to the set of read file descriptors.
* @param[in] aWriteSet A pointer to the set of write file descriptors.
* @param[in] aExceptionSet A pointer to the set of file descriptors with errors.
*
*/
void Layer::HandleSelectResult(int aSetSize, fd_set* aReadSet, fd_set* aWriteSet, fd_set* aExceptionSet)
{
pthread_t lThreadSelf;
if (this->State() != kLayerState_Initialized)
return;
if (aSetSize < 0)
return;
#if WEAVE_SYSTEM_CONFIG_POSIX_LOCKING
lThreadSelf = pthread_self();
#endif // WEAVE_SYSTEM_CONFIG_POSIX_LOCKING
if (aSetSize > 0)
{
// If we woke because of someone writing to the wake pipe, clear the contents of the pipe before returning.
if (FD_ISSET(this->mWakePipeIn, aReadSet))
{
while (true)
{
uint8_t lBytes[128];
int lTmp = ::read(this->mWakePipeIn, static_cast<void*>(lBytes), sizeof(lBytes));
if (lTmp < static_cast<int>(sizeof(lBytes)))
break;
}
}
}
const Timer::Epoch kCurrentEpoch = Timer::GetCurrentEpoch();
#if WEAVE_SYSTEM_CONFIG_POSIX_LOCKING
this->mHandleSelectThread = lThreadSelf;
#endif // WEAVE_SYSTEM_CONFIG_POSIX_LOCKING
for (size_t i = 0; i < Timer::sPool.Size(); i++)
{
Timer* lTimer = Timer::sPool.Get(*this, i);
if (lTimer != NULL && !Timer::IsEarlierEpoch(kCurrentEpoch, lTimer->mAwakenEpoch))
{
lTimer->HandleComplete();
}
}
#if WEAVE_SYSTEM_CONFIG_POSIX_LOCKING
this->mHandleSelectThread = PTHREAD_NULL;
#endif // WEAVE_SYSTEM_CONFIG_POSIX_LOCKING
}
/**
* Wake up the I/O thread that monitors the file descriptors using select() by writing a single byte to the wake pipe.
*
* @note
* If @p WakeSelect() is being called from within @p HandleSelectResult(), then writing to the wake pipe can be skipped, since
* the I/O thread is already awake.
*
* Furthermore, we don't care if this write fails as the only reasonably likely failure is that the pipe is full, in which
* case the select calling thread is going to wake up anyway.
*/
void Layer::WakeSelect()
{
if (this->State() != kLayerState_Initialized)
return;
#if WEAVE_SYSTEM_CONFIG_POSIX_LOCKING
if (pthread_equal(this->mHandleSelectThread, pthread_self()))
{
return;
}
#endif // WEAVE_SYSTEM_CONFIG_POSIX_LOCKING
// Write a single byte to the wake pipe to wake up the select call.
const uint8_t kByte = 0;
const ssize_t kIOResult = ::write(this->mWakePipeOut, &kByte, 1);
static_cast<void>(kIOResult);
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
LwIPEventHandlerDelegate Layer::sSystemEventHandlerDelegate;
/**
* This is the dispatch handler for system layer events.
*
* @param[inout] aTarget A pointer to the Weave System Layer object making the post request.
* @param[in] aEventType The type of event to post.
* @param[inout] aArgument The argument associated with the event to post.
*/
Error Layer::HandleSystemLayerEvent(Object& aTarget, EventType aEventType, uintptr_t aArgument)
{
Error lReturn = WEAVE_SYSTEM_NO_ERROR;
;
// Dispatch logic specific to the event type
switch (aEventType)
{
case kEvent_ReleaseObj:
aTarget.Release();
break;
case kEvent_ScheduleWork:
static_cast<Timer&>(aTarget).HandleComplete();
break;
default:
lReturn = WEAVE_SYSTEM_ERROR_UNEXPECTED_EVENT;
break;
}
return lReturn;
}
/**
* This adds an event handler delegate to the system layer to extend its ability to handle LwIP events.
*
* @param[in] aDelegate An uninitialied LwIP event handler delegate structure
*
* @retval #WEAVE_SYSTEM_NO_ERROR On success.
* @retval #WEAVE_SYSTEM_ERROR_BAD_ARGS If the function pointer contained in #aDelegate is NULL
*/
Error Layer::AddEventHandlerDelegate(LwIPEventHandlerDelegate& aDelegate)
{
Error lReturn;
VerifyOrExit(aDelegate.mFunction != NULL, lReturn = WEAVE_SYSTEM_ERROR_BAD_ARGS);
aDelegate.Prepend(this->mEventDelegateList);
lReturn = WEAVE_SYSTEM_NO_ERROR;
exit:
return lReturn;
}
/**
* This posts an event / message of the specified type with the provided argument to this instance's platform-specific event queue.
*
* @param[inout] aTarget A pointer to the Weave System Layer object making the post request.
* @param[in] aEventType The type of event to post.
* @param[inout] aArgument The argument associated with the event to post.
*
* @retval #WEAVE_SYSTEM_NO_ERROR On success.
* @retval #WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE If the state of the Layer object is incorrect.
* @retval #WEAVE_SYSTEM_ERROR_NO_MEMORY If the event queue is already full.
* @retval other Platform-specific errors generated indicating the reason for failure.
*/
Error Layer::PostEvent(Object& aTarget, EventType aEventType, uintptr_t aArgument)
{
Error lReturn = WEAVE_SYSTEM_NO_ERROR;
VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE);
// Sanity check that this instance and the target layer haven't been "crossed".
VerifyOrDieWithMsg(aTarget.IsRetained(*this), WeaveSystemLayer, "wrong poster! [target %p != this %p]",
&(aTarget.SystemLayer()), this);
lReturn = Platform::Layer::PostEvent(*this, this->mContext, aTarget, aEventType, aArgument);
if (lReturn != WEAVE_SYSTEM_NO_ERROR)
{
WeaveLogError(WeaveSystemLayer, "Failed to queue Weave System Layer event (type %d): %s", aEventType, ErrorStr(lReturn));
}
SuccessOrExit(lReturn);
exit:
return lReturn;
}
/**
* This is a syntactic wrapper around a platform-specific hook that effects an event loop, waiting on a queue that services this
* instance, pulling events off of that queue, and then dispatching them for handling.
*
* @return #WEAVE_SYSTEM_NO_ERROR on success; otherwise, a specific error indicating the reason for intialization failure.
*/
Error Layer::DispatchEvents()
{
Error lReturn = WEAVE_SYSTEM_NO_ERROR;
VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE);
lReturn = Platform::Layer::DispatchEvents(*this, this->mContext);
SuccessOrExit(lReturn);
exit:
return lReturn;
}
/**
* This dispatches the specified event for handling by this instance.
*
* The unmarshalling of the type and arguments from the event is handled by a platform-specific hook which should then call back
* to ::HandleEvent for the actual dispatch.
*
* @param[in] aEvent The platform-specific event object to dispatch for handling.
*
* @return #WEAVE_SYSTEM_NO_ERROR on success; otherwise, a specific error indicating the reason for intialization failure.
*/
Error Layer::DispatchEvent(Event aEvent)
{
Error lReturn = WEAVE_SYSTEM_NO_ERROR;
VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE);
lReturn = Platform::Layer::DispatchEvent(*this, this->mContext, aEvent);
SuccessOrExit(lReturn);
exit:
return lReturn;
}
/**
* This implements the actual dispatch and handling of a Weave System Layer event.
*
* @param[inout] 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 #WEAVE_SYSTEM_NO_ERROR On success.
* @retval #WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE If the state of the InetLayer object is incorrect.
* @retval #WEAVE_SYSTEM_ERROR_UNEXPECTED_EVENT If the event type is unrecognized.
*/
Error Layer::HandleEvent(Object& aTarget, EventType aEventType, uintptr_t aArgument)
{
const LwIPEventHandlerDelegate* lEventDelegate;
Error lReturn;
VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE);
// Sanity check that this instance and the target layer haven't been "crossed".
VerifyOrDieWithMsg(aTarget.IsRetained(*this), WeaveSystemLayer, "wrong handler! [target %p != this %p]",
&(aTarget.SystemLayer()), this);
lReturn = WEAVE_SYSTEM_ERROR_UNEXPECTED_EVENT;
lEventDelegate = this->mEventDelegateList;
// Prevent the target object from being freed while dispatching the event.
aTarget.Retain();
while (lReturn == WEAVE_SYSTEM_ERROR_UNEXPECTED_EVENT && lEventDelegate != NULL)
{
lReturn = lEventDelegate->mFunction(aTarget, aEventType, aArgument);
lEventDelegate = lEventDelegate->mNextDelegate;
}
if (lReturn == WEAVE_SYSTEM_ERROR_UNEXPECTED_EVENT)
{
WeaveLogError(WeaveSystemLayer, "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();
exit:
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 nl::Weave::System::Timer class when
* one or more timers are active and require deferred execution.
*
* @param[in] aDelayMilliseconds The timer duration in milliseconds.
*
* @return WEAVE_SYSTEM_NO_ERROR on success, error code otherwise.
*
*/
Error Layer::StartPlatformTimer(uint32_t aDelayMilliseconds)
{
Error lReturn = WEAVE_SYSTEM_NO_ERROR;
VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE);
lReturn = Platform::Layer::StartTimer(*this, this->mContext, aDelayMilliseconds);
SuccessOrExit(lReturn);
exit:
return lReturn;
}
/**
* Handle the platform timer expiration event.
*
* @brief
* Calls nl::Weave::System::Timer::HandleExpiredTimers to handle any expired timers. It is assumed that this API is called
* only while on the thread which owns the Weave System Layer object.
*
* @return WEAVE_SYSTEM_NO_ERROR on success, error code otherwise.
*
*/
Error Layer::HandlePlatformTimer()
{
Error lReturn = WEAVE_SYSTEM_NO_ERROR;
VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE);
lReturn = Timer::HandleExpiredTimers(*this);
SuccessOrExit(lReturn);
exit:
return lReturn;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
#if !WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_EVENT_FUNCTIONS
// MARK: Weave System Layer platform- and system-specific functions for LwIP-native eventing.
struct LwIPEvent
{
EventType Type;
Object* Target;
uintptr_t Argument;
};
#endif // !WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_EVENT_FUNCTIONS
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
namespace Platform {
namespace Layer {
#if !WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_XTOR_FUNCTIONS
/**
* This is a platform-specific Weave System Layer pre-initialization hook. This may be overridden by assserting the preprocessor
* definition, #WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_XTOR_FUNCTIONS.
*
* @param[inout] aLayer A reference to the Weave System Layer instance being initialized.
*
* @param[inout] aContext Platform-specific context data passed to the layer initialization method, ::Init.
*
* @return #WEAVE_SYSTEM_NO_ERROR on success; otherwise, a specific error indicating the reason for intialization failure.
* Returning non-successful status will abort initialization.
*/
NL_DLL_EXPORT Error WillInit(Layer& aLayer, void* aContext)
{
static_cast<void>(aLayer);
static_cast<void>(aContext);
return WEAVE_SYSTEM_NO_ERROR;
}
/**
* This is a platform-specific Weave System Layer pre-shutdown hook. This may be overridden by assserting the preprocessor
* definition, #WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_XTOR_FUNCTIONS.
*
* @param[inout] aLayer A pointer to the Weave System Layer instance being shutdown.
*
* @param[inout] aContext Platform-specific context data passed to the layer initialization method, ::Shutdown.
*
* @return #WEAVE_SYSTEM_NO_ERROR on success; otherwise, a specific error indicating the reason for shutdown failure. Returning
* non-successful status will abort shutdown.
*/
NL_DLL_EXPORT Error WillShutdown(Layer& aLayer, void* aContext)
{
static_cast<void>(aLayer);
static_cast<void>(aContext);
return WEAVE_SYSTEM_NO_ERROR;
}
/**
* This is a platform-specific Weave System Layer post-initialization hook. This may be overridden by assserting the preprocessor
* definition, #WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_XTOR_FUNCTIONS.
*
* @param[inout] aLayer A reference to the Weave System Layer instance being initialized.
*
* @param[inout] aContext Platform-specific context data passed to the layer initialization method, ::Init.
*
* @param[in] anError The overall status being returned via the Weave System Layer ::Init method.
*/
NL_DLL_EXPORT void DidInit(Layer& aLayer, void* aContext, Error aStatus)
{
static_cast<void>(aLayer);
static_cast<void>(aContext);
static_cast<void>(aStatus);
}
/**
* This is a platform-specific Weave System Layer pre-shutdown hook. This may be overridden by assserting the preprocessor
* definition, #WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_XTOR_FUNCTIONS.
*
* @param[inout] aLayer A reference to the Weave System Layer instance being shutdown.
*
* @param[inout] aContext Platform-specific context data passed to the layer initialization method, ::Shutdown.
*
* @param[in] anError The overall status being returned via the Weave System Layer ::Shutdown method.
*
* @return #WEAVE_SYSTEM_NO_ERROR on success; otherwise, a specific error indicating the reason for shutdown failure. Returning
* non-successful status will abort shutdown.
*/
NL_DLL_EXPORT void DidShutdown(Layer& aLayer, void* aContext, Error aStatus)
{
static_cast<void>(aLayer);
static_cast<void>(aContext);
static_cast<void>(aStatus);
}
#endif // !WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_XTOR_FUNCTIONS
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
#if !WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_EVENT_FUNCTIONS
using nl::Weave::System::LwIPEvent;
/**
* This is a platform-specific event / message post hook. This may be overridden by assserting the preprocessor definition,
* #WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_EVENT_FUNCTIONS.
*
* This posts an event / message of the specified type with the provided argument to this instance's platform-specific event /
* message queue.
*
* @note
* This is an implementation for LwIP.
*
* @param[inout] aLayer A pointer to the layer instance to which the event / message is being posted.
*
* @param[inout] aContext Platform-specific context data passed to the layer initialization method, ::Init.
*
* @param[inout] aTarget A pointer to the Weave System Layer object making the post request.
*
* @param[in] aType The type of event to post.
*
* @param[inout] anArg The argument associated with the event to post.
*
* @return #WEAVE_SYSTEM_NO_ERROR on success; otherwise, a specific error indicating the reason for intialization failure.
*/
NL_DLL_EXPORT Error PostEvent(Layer& aLayer, void* aContext, Object& aTarget, EventType aType, uintptr_t aArgument)
{
Error lReturn = WEAVE_SYSTEM_NO_ERROR;
sys_mbox_t lSysMbox;
LwIPEvent* ev;
err_t lLwIPError;
VerifyOrExit(aContext != NULL, lReturn = WEAVE_SYSTEM_ERROR_BAD_ARGS);
lSysMbox = reinterpret_cast<sys_mbox_t>(aContext);
ev = new LwIPEvent;
VerifyOrExit(ev != NULL, lReturn = WEAVE_SYSTEM_ERROR_NO_MEMORY);
ev->Type = aType;
ev->Target = &aTarget;
ev->Argument = aArgument;
lLwIPError = sys_mbox_trypost(&lSysMbox, ev);
VerifyOrExit(lLwIPError == ERR_OK, delete ev; lReturn = nl::Weave::System::MapErrorLwIP(lLwIPError));
exit:
return lReturn;
}
/**
* This is a platform-specific event / message dispatch hook. This may be overridden by assserting the preprocessor definition,
* #WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_EVENT_FUNCTIONS.
*
* This effects an event loop, waiting on a queue that services this instance, pulling events off of that queue, and then
* dispatching them for handling.
*
* @note
* This is an implementation for LwIP.
*
* @param[inout] aLayer A reference to the layer instance for which events / messages are being dispatched.
*
* @param[inout] aContext Platform-specific context data passed to the layer initialization method, ::Init.
*
* @retval #WEAVE_SYSTEM_ERROR_BAD_ARGS If #aLayer or #aContext is NULL.
* @retval #WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE If the state of the Weave System Layer object is unexpected.
* @retval #WEAVE_SYSTEM_ERROR_UNEXPECTED_EVENT If an event type is unrecognized.
* @retval #WEAVE_SYSTEM_NO_ERROR On success.
*/
NL_DLL_EXPORT Error DispatchEvents(Layer& aLayer, void* aContext)
{
Error lReturn = WEAVE_SYSTEM_NO_ERROR;
err_t lLwIPError;
sys_mbox_t lSysMbox;
void* lVoidPointer;
const LwIPEvent* lEvent;
// Sanity check the context / queue.
VerifyOrExit(aContext != NULL, lReturn = WEAVE_SYSTEM_ERROR_BAD_ARGS);
lSysMbox = reinterpret_cast<sys_mbox_t>(aContext);
while (true)
{
lLwIPError = sys_arch_mbox_tryfetch(&lSysMbox, &lVoidPointer);
VerifyOrExit(lLwIPError == ERR_OK, lReturn = nl::Weave::System::MapErrorLwIP(lLwIPError));
lEvent = static_cast<const LwIPEvent*>(lVoidPointer);
VerifyOrExit(lEvent != NULL && lEvent->Target != NULL, lReturn = WEAVE_SYSTEM_ERROR_UNEXPECTED_EVENT);
lReturn = aLayer.HandleEvent(*lEvent->Target, lEvent->Type, lEvent->Argument);
delete lEvent;
SuccessOrExit(lReturn);
}
exit:
return lReturn;
}
/**
* This is a platform-specific event / message dispatch hook. This may be overridden by assserting the preprocessor definition,
* #WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_EVENT_FUNCTIONS.
*
* This dispatches the specified event for handling, unmarshalling the type and arguments from the event for hand off to Weave
* System Layer::HandleEvent for the actual dispatch.
*
* @note
* This is an implementation for LwIP.
*
* @param[inout] aLayer A reference to the layer instance for which events / messages are being dispatched.
* @param[inout] aContext Platform-specific context data passed to the layer initialization method, ::Init.
* @param[in] anEvent The platform-specific event object to dispatch for handling.
*
* @retval #WEAVE_SYSTEM_ERROR_BAD_ARGS If #aLayer or the event target is NULL.
* @retval #WEAVE_SYSTEM_ERROR_UNEXPECTED_EVENT If the event type is unrecognized.
* @retval #WEAVE_SYSTEM_ERROR_UNEXPECTED_STATE If the state of the Weave System Layer object is unexpected.
* @retval #WEAVE_SYSTEM_NO_ERROR On success.
*/
NL_DLL_EXPORT Error DispatchEvent(Layer& aLayer, void* aContext, Event aEvent)
{
const EventType type = aEvent->Type;
Object* target = aEvent->Target;
const uint32_t data = aEvent->Argument;
Error lReturn = WEAVE_SYSTEM_NO_ERROR;
// Sanity check the target object.
VerifyOrExit(target != NULL, lReturn = WEAVE_SYSTEM_ERROR_BAD_ARGS);
// Handle the event.
lReturn = aLayer.HandleEvent(*target, type, data);
SuccessOrExit(lReturn);
exit:
return lReturn;
}
/**
* This is a platform-specific event / message dispatch hook. This may be overridden by assserting the preprocessor definition,
* #WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_EVENT_FUNCTIONS.
*
* @note
* This is an implementation for LwIP.
*
* @param[inout] aLayer A reference to the layer instance for which events / messages are being dispatched.
* @param[inout] aContext Platform-specific context data passed to the layer initialization method, ::Init.
* @param[in] aMilliseconds The number of milliseconds to set for the timer.
*
* @retval #WEAVE_SYSTEM_NO_ERROR Always succeeds unless overriden.
*/
NL_DLL_EXPORT Error StartTimer(Layer& aLayer, void* aContext, uint32_t aMilliseconds)
{
Error lReturn = WEAVE_SYSTEM_NO_ERROR;
// At the moment there is no need to do anything for standalone weave + LWIP.
// the Task will periodically call HandleTimer which will process any expired
// timers.
static_cast<void>(aLayer);
static_cast<void>(aContext);
static_cast<void>(aMilliseconds);
return lReturn;
}
#endif // !WEAVE_SYSTEM_CONFIG_PLATFORM_PROVIDES_EVENT_FUNCTIONS
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
} // namespace Layer
} // namespace Platform
} // namespace System
} // namespace Weave
} // namespace nl