| /* |
| * |
| * 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 |