lib: os: onoff: add API for on-off service request and release management
There are various situations where it's necessary to support turning devices on or off at runtime, includin power rails, clocks, other peripherals, and binary device power management. The complexity of properly managing multiple consumers of a device in a multithreaded system suggests that a shared implementation is desirable. This commit provides an API that supports managing on-off resources. Signed-off-by: Peter A. Bigot <pab@pabigot.com>
This commit is contained in:
parent
cd2ff16a50
commit
1964bf08bb
11 changed files with 2384 additions and 0 deletions
|
@ -105,6 +105,7 @@
|
|||
/doc/scripts/ @carlescufi
|
||||
/doc/guides/bluetooth/ @joerchan @jhedberg @Vudentz
|
||||
/doc/reference/bluetooth/ @joerchan @jhedberg @Vudentz
|
||||
/doc/reference/kernel/other/resource_mgmt.rst @pabigot
|
||||
/drivers/*/*cc13xx_cc26xx* @bwitherspoon
|
||||
/drivers/*/*mcux* @MaureenHelm
|
||||
/drivers/*/*stm32* @erwango
|
||||
|
|
|
@ -116,6 +116,7 @@ These pages cover other kernel services.
|
|||
other/atomic.rst
|
||||
other/float.rst
|
||||
other/ring_buffers.rst
|
||||
other/resource_mgmt.rst
|
||||
other/cxx_support.rst
|
||||
other/version.rst
|
||||
other/fatal.rst
|
||||
|
|
83
doc/reference/kernel/other/resource_mgmt.rst
Normal file
83
doc/reference/kernel/other/resource_mgmt.rst
Normal file
|
@ -0,0 +1,83 @@
|
|||
.. _resource_mgmt:
|
||||
|
||||
Resource Management
|
||||
###################
|
||||
|
||||
There are various situations where it's necessary to coordinate resource
|
||||
use at runtime among multiple clients. These include power rails,
|
||||
clocks, other peripherals, and binary device power management. The
|
||||
complexity of properly managing multiple consumers of a device in a
|
||||
multithreaded system, especially when transitions may be asynchronous,
|
||||
suggests that a shared implementation is desirable.
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
|
||||
On-Off Services
|
||||
***************
|
||||
|
||||
An on-off service supports an arbitrary number of clients of a service
|
||||
which has a binary state. Example applications are power rails, clocks,
|
||||
and binary device power management.
|
||||
|
||||
The service has the following properties:
|
||||
|
||||
* The stable states are off, on, and error. The service always begins
|
||||
in the off state. The service may also be in a transition to a given
|
||||
state.
|
||||
* The core operations are request (add a dependency) and release (remove
|
||||
a dependency). The service manages the state based on calls to
|
||||
functions that initiate these operations.
|
||||
* The service transitions from off to on when first client request is
|
||||
received.
|
||||
* The service transitions from on to off when last client release is
|
||||
received.
|
||||
* Each service configuration provides functions that implement the
|
||||
transition from off to on, from on to off, and optionally from an
|
||||
error state to off. Transitions that may put a calling thread to
|
||||
sleep must be flagged in the configuration to support safe invocation
|
||||
from non-thread context.
|
||||
* All operations are asynchronous, and are initiated by a function call
|
||||
that references a specific service and is given client notification
|
||||
data. The function call will succeed or fail. On success, the
|
||||
operation is guaranteed to be initiated, but whether the operation
|
||||
itself succeeds or fails is indicated through client notification.
|
||||
The initiation functions can be invoked from pre-kernel, thread, or
|
||||
ISR context. In contexts and states where the operation cannot
|
||||
be started the function will result in an error.
|
||||
* Requests to turn on may be queued while a transition to off is in
|
||||
progress: when the service has turned off successfully it will be
|
||||
immediately turned on again (where context allows) and waiting clients
|
||||
notified when the start completes.
|
||||
|
||||
Requests are reference counted, but not tracked. That means clients are
|
||||
responsible for recording whether their requests were accepted, and for
|
||||
initiating a release only if they have previously successfully completed
|
||||
a request. Improper use of the API can cause an active client to be
|
||||
shut out, and the service does not maintain a record of specific clients
|
||||
that have been granted a request.
|
||||
|
||||
Failures in executing a transition are recorded and inhibit further
|
||||
requests or releases until the service is reset. Pending requests are
|
||||
notified (and cancelled) when errors are discovered.
|
||||
|
||||
Transition operation completion notifications are provided through any
|
||||
of the following mechanisms:
|
||||
|
||||
* Signal: A pointer to a :c:type:`struct k_poll_signal` is provided, and
|
||||
the signal is raised when the transition completes. The operation
|
||||
completion code is stored as the signal value.
|
||||
* Callback: a function pointer is provided by the client along with an
|
||||
opaque pointer, and on completion of the operation the function is
|
||||
invoked with the pointer and the operation completion code.
|
||||
* Spin-wait: the client is required to check for operation completion
|
||||
using the :cpp:func:`onoff_client_fetch_result()` function.
|
||||
|
||||
Synchronous transition may be implemented by a caller based on its
|
||||
context, for example by using :cpp:func:`k_poll()` to wait until the
|
||||
completion is signalled.
|
||||
|
||||
.. doxygengroup:: resource_mgmt_apis
|
||||
:project: Zephyr
|
603
include/sys/onoff.h
Normal file
603
include/sys/onoff.h
Normal file
|
@ -0,0 +1,603 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_ONOFF_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_ONOFF_H_
|
||||
|
||||
#include <kernel.h>
|
||||
#include <zephyr/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup resource_mgmt_apis Resource Management APIs
|
||||
* @ingroup kernel_apis
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Flag fields used to specify on-off service behavior.
|
||||
*/
|
||||
enum onoff_service_flags {
|
||||
/**
|
||||
* @brief Flag passed to onoff_service_init().
|
||||
*
|
||||
* When provided this indicates the start transition function
|
||||
* may cause the calling thread to wait. This blocks attempts
|
||||
* to initiate a transition from a non-thread context.
|
||||
*/
|
||||
ONOFF_SERVICE_START_SLEEPS = BIT(0),
|
||||
|
||||
/**
|
||||
* @brief Flag passed to onoff_service_init().
|
||||
*
|
||||
* As with @ref ONOFF_SERVICE_START_SLEEPS but describing the
|
||||
* stop transition function.
|
||||
*/
|
||||
ONOFF_SERVICE_STOP_SLEEPS = BIT(1),
|
||||
|
||||
/**
|
||||
* @brief Flag passed to onoff_service_init().
|
||||
*
|
||||
* As with @ref ONOFF_SERVICE_START_SLEEPS but describing the
|
||||
* reset transition function.
|
||||
*/
|
||||
ONOFF_SERVICE_RESET_SLEEPS = BIT(2),
|
||||
|
||||
/* Internal use. */
|
||||
ONOFF_SERVICE_HAS_ERROR = BIT(3),
|
||||
|
||||
/* This and higher bits reserved for internal use. */
|
||||
ONOFF_SERVICE_INTERNAL_BASE = BIT(4),
|
||||
};
|
||||
|
||||
/* Forward declaration */
|
||||
struct onoff_service;
|
||||
|
||||
/**
|
||||
* @brief Signature used to notify an on-off service that a transition
|
||||
* has completed.
|
||||
*
|
||||
* Functions of this type are passed to service-specific transition
|
||||
* functions to be used to report the completion of the operation.
|
||||
* The functions may be invoked from any context.
|
||||
*
|
||||
* @param srv the service for which transition was requested.
|
||||
*
|
||||
* @param res the result of the transition. This shall be
|
||||
* non-negative on success, or a negative error code. If an error is
|
||||
* indicated the service shall enter an error state.
|
||||
*/
|
||||
typedef void (*onoff_service_notify_fn)(struct onoff_service *srv,
|
||||
int res);
|
||||
|
||||
/**
|
||||
* @brief Signature used by service implementations to effect a
|
||||
* transition.
|
||||
*
|
||||
* Service definitions use two function pointers of this type to be
|
||||
* notified that a transition is required, and a third optional one to
|
||||
* reset service state.
|
||||
*
|
||||
* The start function will be called only from the off state.
|
||||
*
|
||||
* The stop function will be called only from the on state.
|
||||
*
|
||||
* The reset function may be called only when
|
||||
* onoff_service_has_error() returns true.
|
||||
*
|
||||
* @param srv the service for which transition was requested.
|
||||
*
|
||||
* @param notify the function to be invoked when the transition has
|
||||
* completed. The callee shall capture this parameter to notify on
|
||||
* completion of asynchronous transitions. If the transition is not
|
||||
* asynchronous, notify shall be invoked before the transition
|
||||
* function returns.
|
||||
*/
|
||||
typedef void (*onoff_service_transition_fn)(struct onoff_service *srv,
|
||||
onoff_service_notify_fn notify);
|
||||
|
||||
/**
|
||||
* @brief State associated with an on-off service.
|
||||
*
|
||||
* No fields in this structure are intended for use by service
|
||||
* providers or clients. The state is to be initialized once, using
|
||||
* onoff_service_init(), when the service provider is initialized.
|
||||
* In case of error it may be reset through the
|
||||
* onoff_service_reset() API.
|
||||
*/
|
||||
struct onoff_service {
|
||||
/* List of clients waiting for completion of reset or
|
||||
* transition to on.
|
||||
*/
|
||||
sys_slist_t clients;
|
||||
|
||||
/* Function to invoke to transition the service to on. */
|
||||
onoff_service_transition_fn start;
|
||||
|
||||
/* Function to invoke to transition the service to off. */
|
||||
onoff_service_transition_fn stop;
|
||||
|
||||
/* Function to force the service state to reset, where
|
||||
* supported.
|
||||
*/
|
||||
onoff_service_transition_fn reset;
|
||||
|
||||
/* Mutex protection for flags, clients, releaser, and refs. */
|
||||
struct k_spinlock lock;
|
||||
|
||||
/* Client to be informed when transition to off completes. */
|
||||
struct onoff_client *releaser;
|
||||
|
||||
/* Flags identifying the service state. */
|
||||
u16_t flags;
|
||||
|
||||
/* Number of active clients for the service. */
|
||||
u16_t refs;
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
#define ONOFF_SERVICE_INITIALIZER(_start, _stop, _reset, _flags) { \
|
||||
.start = _start, \
|
||||
.stop = _stop, \
|
||||
.reset = _reset, \
|
||||
.flags = _flags, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize an on-off service to off state.
|
||||
*
|
||||
* This function must be invoked exactly once per service instance, by
|
||||
* the infrastructure that provides the service, and before any other
|
||||
* on-off service API is invoked on the service.
|
||||
*
|
||||
* This function should never be invoked by clients of an on-off service.
|
||||
*
|
||||
* @param srv the service definition object to be initialized.
|
||||
*
|
||||
* @param start the function used to (initiate a) transition from off
|
||||
* to on. This must not be null. Include @ref ONOFF_SERVICE_START_SLEEPS as
|
||||
* appropriate in flags.
|
||||
*
|
||||
* @param stop the function used to (initiate a) transition from on to
|
||||
* off. This must not be null. Include @ref ONOFF_SERVICE_STOP_SLEEPS
|
||||
* as appropriate in flags.
|
||||
*
|
||||
* @param reset the function used to clear errors and force the
|
||||
* service to an off state. Pass null if the service cannot or need
|
||||
* not be reset. (Services where a transition operation can complete
|
||||
* with an error notification should support the reset operation.)
|
||||
* Include @ref ONOFF_SERVICE_RESET_SLEEPS as appropriate in flags.
|
||||
*
|
||||
* @param flags any or all of the flags mentioned above,
|
||||
* e.g. @ref ONOFF_SERVICE_START_SLEEPS. Use of other flags produces an
|
||||
* error.
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval -EINVAL if start, stop, or flags are invalid
|
||||
*/
|
||||
int onoff_service_init(struct onoff_service *srv,
|
||||
onoff_service_transition_fn start,
|
||||
onoff_service_transition_fn stop,
|
||||
onoff_service_transition_fn reset,
|
||||
u32_t flags);
|
||||
|
||||
/** @internal
|
||||
*
|
||||
* Flag fields used to specify on-off client behavior.
|
||||
*
|
||||
* These flags control whether calls to onoff_service_request() and
|
||||
* onoff_service_release() are synchronous or asynchronous, and for
|
||||
* asynchronous operations how the operation result is communicated to
|
||||
* the client.
|
||||
*/
|
||||
enum onoff_client_flags {
|
||||
/* Known-invalid field, used in validation */
|
||||
ONOFF_CLIENT_NOTIFY_INVALID = 0,
|
||||
|
||||
/*
|
||||
* Indicates that no notification will be provided.
|
||||
*
|
||||
* Callers must check for completions using
|
||||
* onoff_client_fetch_result().
|
||||
*
|
||||
* See onoff_client_init_spinwait().
|
||||
*/
|
||||
ONOFF_CLIENT_NOTIFY_SPINWAIT = 1,
|
||||
|
||||
/*
|
||||
* Select notification through @ref k_poll signal
|
||||
*
|
||||
* See onoff_client_init_signal().
|
||||
*/
|
||||
ONOFF_CLIENT_NOTIFY_SIGNAL = 2,
|
||||
|
||||
/**
|
||||
* Select notification through a user-provided callback.
|
||||
*
|
||||
* See onoff_client_init_callback().
|
||||
*/
|
||||
ONOFF_CLIENT_NOTIFY_CALLBACK = 3,
|
||||
};
|
||||
|
||||
/* Forward declaration */
|
||||
struct onoff_client;
|
||||
|
||||
/**
|
||||
* @brief Signature used to notify an on-off service client of the
|
||||
* completion of an operation.
|
||||
*
|
||||
* These functions may be invoked from any context including
|
||||
* pre-kernel, ISR, or cooperative or pre-emptible threads.
|
||||
* Compatible functions must be isr-callable and non-suspendable.
|
||||
*
|
||||
* @param srv the service for which the operation was initiated.
|
||||
*
|
||||
* @param cli the client structure passed to the function that
|
||||
* initiated the operation.
|
||||
*
|
||||
* @param user_data user data provided when the client structure was
|
||||
* initialized with onoff_client_init_callback().
|
||||
*
|
||||
* @param res the result of the operation. Expected values are
|
||||
* service-specific, but the value shall be non-negative if the
|
||||
* operation succeeded, and negative if the operation failed.
|
||||
*/
|
||||
typedef void (*onoff_client_callback)(struct onoff_service *srv,
|
||||
struct onoff_client *cli,
|
||||
void *user_data,
|
||||
int res);
|
||||
|
||||
/**
|
||||
* @brief State associated with a client of an on-off service.
|
||||
*
|
||||
* Objects of this type are allocated by a client, which must use an
|
||||
* initialization function (e.g. onoff_client_init_signal()) to
|
||||
* configure them.
|
||||
*
|
||||
* Control of the object content transfers to the service provider
|
||||
* when a pointer to the object is passed to any on-off service
|
||||
* function. While the service provider controls the object the
|
||||
* client must not change any object fields. Control reverts to the
|
||||
* client:
|
||||
* * if the call to the service API returns an error;
|
||||
* * if the call to the service API succeeds for a no-wait operation;
|
||||
* * when operation completion is posted (signalled or callback
|
||||
* invoked).
|
||||
*
|
||||
* Only the result field is intended for direct use by clients, and it
|
||||
* is available for inspection only after control reverts to the
|
||||
* client.
|
||||
*/
|
||||
struct onoff_client {
|
||||
/* Links the client into the set of waiting service users. */
|
||||
sys_snode_t node;
|
||||
|
||||
union async {
|
||||
/* Pointer to signal used to notify client.
|
||||
*
|
||||
* The signal value corresponds to the res parameter
|
||||
* of onoff_client_callback.
|
||||
*/
|
||||
struct k_poll_signal *signal;
|
||||
|
||||
/* Handler and argument for callback notification. */
|
||||
struct callback {
|
||||
onoff_client_callback handler;
|
||||
void *user_data;
|
||||
} callback;
|
||||
} async;
|
||||
|
||||
/*
|
||||
* The result of the operation.
|
||||
*
|
||||
* This is the value that was (or would be) passed to the
|
||||
* async infrastructure. This field is the sole record of
|
||||
* success or failure for no-wait synchronous operations.
|
||||
*/
|
||||
int volatile result;
|
||||
|
||||
/* Flags recording client state. */
|
||||
u32_t volatile flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Check for and read the result of an asynchronous operation.
|
||||
*
|
||||
* @param op pointer to the object used to specify asynchronous
|
||||
* function behavior and store completion information.
|
||||
*
|
||||
* @param result pointer to storage for the result of the operation.
|
||||
* The result is stored only if the operation has completed.
|
||||
*
|
||||
* @retval 0 if the operation has completed.
|
||||
* @retval -EAGAIN if the operation has not completed.
|
||||
*/
|
||||
static inline int onoff_client_fetch_result(const struct onoff_client *op,
|
||||
int *result)
|
||||
{
|
||||
__ASSERT_NO_MSG(op != NULL);
|
||||
__ASSERT_NO_MSG(result != NULL);
|
||||
|
||||
int rv = -EAGAIN;
|
||||
|
||||
if (op->flags == 0U) {
|
||||
rv = 0;
|
||||
*result = op->result;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize an on-off client to be used for a spin-wait
|
||||
* operation notification.
|
||||
*
|
||||
* Clients that use this initialization receive no asynchronous
|
||||
* notification, and instead must periodically check for completion
|
||||
* using onoff_client_fetch_result().
|
||||
*
|
||||
* @param cli pointer to the client state object.
|
||||
*/
|
||||
static inline void onoff_client_init_spinwait(struct onoff_client *cli)
|
||||
{
|
||||
__ASSERT_NO_MSG(cli != NULL);
|
||||
|
||||
*cli = (struct onoff_client){
|
||||
.flags = ONOFF_CLIENT_NOTIFY_SPINWAIT,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize an on-off client to be used for a signal
|
||||
* operation notification.
|
||||
*
|
||||
* Clients that use this initialization will be notified of the
|
||||
* completion of operations submitted through onoff_request() and
|
||||
* onoff_release() through the provided signal.
|
||||
*
|
||||
* @note
|
||||
* @rst
|
||||
* This capability is available only when :option:`CONFIG_POLL` is
|
||||
* selected.
|
||||
* @endrst
|
||||
*
|
||||
* @param cli pointer to the client state object.
|
||||
*
|
||||
* @param sigp pointer to the signal to use for notification. The
|
||||
* value must not be null. The signal must be reset before the client
|
||||
* object is passed to the on-off service API.
|
||||
*/
|
||||
static inline void onoff_client_init_signal(struct onoff_client *cli,
|
||||
struct k_poll_signal *sigp)
|
||||
{
|
||||
__ASSERT_NO_MSG(cli != NULL);
|
||||
__ASSERT_NO_MSG(sigp != NULL);
|
||||
|
||||
*cli = (struct onoff_client){
|
||||
#ifdef CONFIG_POLL
|
||||
.async = {
|
||||
.signal = sigp,
|
||||
},
|
||||
#endif /* CONFIG_POLL */
|
||||
.flags = ONOFF_CLIENT_NOTIFY_SIGNAL,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize an on-off client to be used for a callback
|
||||
* operation notification.
|
||||
*
|
||||
* Clients that use this initialization will be notified of the
|
||||
* completion of operations submitted through on-off service API
|
||||
* through the provided callback. Note that callbacks may be invoked
|
||||
* from various contexts depending on the specific service; see
|
||||
* @ref onoff_client_callback.
|
||||
*
|
||||
* @param cli pointer to the client state object.
|
||||
*
|
||||
* @param handler a function pointer to use for notification.
|
||||
*
|
||||
* @param user_data an opaque pointer passed to the handler to provide
|
||||
* additional context.
|
||||
*/
|
||||
static inline void onoff_client_init_callback(struct onoff_client *cli,
|
||||
onoff_client_callback handler,
|
||||
void *user_data)
|
||||
{
|
||||
__ASSERT_NO_MSG(cli != NULL);
|
||||
__ASSERT_NO_MSG(handler != NULL);
|
||||
|
||||
*cli = (struct onoff_client){
|
||||
.async = {
|
||||
.callback = {
|
||||
.handler = handler,
|
||||
.user_data = user_data,
|
||||
},
|
||||
},
|
||||
.flags = ONOFF_CLIENT_NOTIFY_CALLBACK,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request a reservation to use an on-off service.
|
||||
*
|
||||
* The return value indicates the success or failure of an attempt to
|
||||
* initiate an operation to request the resource be made available.
|
||||
* If initiation of the operation succeeds the result of the request
|
||||
* operation is provided through the configured client notification
|
||||
* method, possibly before this call returns.
|
||||
*
|
||||
* Note that the call to this function may succeed in a case where the
|
||||
* actual request fails. Always check the operation completion
|
||||
* result.
|
||||
*
|
||||
* As a specific example: A call to this function may succeed at a
|
||||
* point while the service is still transitioning to off due to a
|
||||
* previous call to onoff_release(). When the transition completes
|
||||
* the service would normally start a transition to on. However, if
|
||||
* the transition to off completed in a non-thread context, and the
|
||||
* transition to on can sleep, the transition cannot be started and
|
||||
* the request will fail with `-EWOULDBLOCK`.
|
||||
*
|
||||
* @param srv the service that will be used.
|
||||
*
|
||||
* @param cli a non-null pointer to client state providing
|
||||
* instructions on synchronous expectations and how to notify the
|
||||
* client when the request completes. Behavior is undefined if client
|
||||
* passes a pointer object associated with an incomplete service
|
||||
* operation.
|
||||
*
|
||||
* @retval Non-negative on successful (initiation of) request
|
||||
* @retval -EIO if service has recorded an an error
|
||||
* @retval -EINVAL if the parameters are invalid
|
||||
* @retval -EAGAIN if the reference count would overflow
|
||||
* @retval -EWOULDBLOCK if the function was invoked from non-thread
|
||||
* context and successful initiation could result in an attempt to
|
||||
* make the calling thread sleep.
|
||||
*/
|
||||
int onoff_request(struct onoff_service *srv,
|
||||
struct onoff_client *cli);
|
||||
|
||||
/**
|
||||
* @brief Release a reserved use of an on-off service.
|
||||
*
|
||||
* The return value indicates the success or failure of an attempt to
|
||||
* initiate an operation to release the resource. If initiation of
|
||||
* the operation succeeds the result of the release operation itself
|
||||
* is provided through the configured client notification method,
|
||||
* possibly before this call returns.
|
||||
*
|
||||
* Note that the call to this function may succeed in a case where the
|
||||
* actual release fails. Always check the operation completion
|
||||
* result.
|
||||
*
|
||||
* @param srv the service that will be used.
|
||||
*
|
||||
* @param cli a non-null pointer to client state providing
|
||||
* instructions on how to notify the client when release completes.
|
||||
* Behavior is undefined if cli references an object associated with
|
||||
* an incomplete service operation.
|
||||
*
|
||||
* @retval Non-negative on successful (initiation of) release
|
||||
* @retval -EINVAL if the parameters are invalid
|
||||
* @retval -EIO if service has recorded an an error
|
||||
* @retval -EWOULDBLOCK if a non-blocking request was made and
|
||||
* could not be satisfied without potentially blocking.
|
||||
* @retval -EALREADY if the service is already off or transitioning
|
||||
* to off
|
||||
* @retval -EBUSY if the service is transitioning to on
|
||||
*/
|
||||
int onoff_release(struct onoff_service *srv,
|
||||
struct onoff_client *cli);
|
||||
|
||||
/**
|
||||
* @brief Test whether an on-off service has recorded an error.
|
||||
*
|
||||
* This function can be used to determine whether the service has
|
||||
* recorded an error. Errors may be cleared by invoking
|
||||
* onoff_service_reset().
|
||||
*
|
||||
* @return true if and only if the service has an uncleared error.
|
||||
*/
|
||||
static inline bool onoff_service_has_error(const struct onoff_service *srv)
|
||||
{
|
||||
return (srv->flags & ONOFF_SERVICE_HAS_ERROR) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear errors on an on-off service and reset it to its off
|
||||
* state.
|
||||
*
|
||||
* A service can only be reset when it is in an error state as
|
||||
* indicated by onoff_service_has_error().
|
||||
*
|
||||
* The return value indicates the success or failure of an attempt to
|
||||
* initiate an operation to reset the resource. If initiation of the
|
||||
* operation succeeds the result of the reset operation itself is
|
||||
* provided through the configured client notification method,
|
||||
* possibly before this call returns. Multiple clients may request a
|
||||
* reset; all are notified when it is complete.
|
||||
*
|
||||
* Note that the call to this function may succeed in a case where the
|
||||
* actual reset fails. Always check the operation completion result.
|
||||
*
|
||||
* This function is blocking if the reset transition is blocking,
|
||||
* unless client notification specifies no-wait.
|
||||
*
|
||||
* @note Due to the conditions on state transition all incomplete
|
||||
* asynchronous operations will have been informed of the error when
|
||||
* it occurred. There need be no concern about dangling requests left
|
||||
* after a reset completes.
|
||||
*
|
||||
* @param srv the service to be reset.
|
||||
*
|
||||
* @param cli pointer to client state, including instructions on how
|
||||
* to notify the client when reset completes. Behavior is undefined
|
||||
* if cli references an object associated with an incomplete service
|
||||
* operation.
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval -ENOTSUP if reset is not supported
|
||||
* @retval -EINVAL if the parameters are invalid, or if the service
|
||||
* @retval -EALREADY if the service does not have a recorded error
|
||||
*/
|
||||
int onoff_service_reset(struct onoff_service *srv,
|
||||
struct onoff_client *cli);
|
||||
|
||||
/**
|
||||
* @brief Attempt to cancel an in-progress client operation.
|
||||
*
|
||||
* It may be that a client has initiated an operation but needs to
|
||||
* shut down before the operation has completed. For example, when a
|
||||
* request was made and the need is no longer present.
|
||||
*
|
||||
* There is limited support for cancelling an in-progress operation:
|
||||
* * If a start or reset is in progress, all but one clients
|
||||
* requesting the start can cancel their request.
|
||||
* * If a stop is in progress, all clients requesting a restart can
|
||||
* cancel their request;
|
||||
* * A client requesting a release cannot cancel the release.
|
||||
*
|
||||
* Be aware that any transition that was initiated on behalf of the
|
||||
* client will continue to progress to completion. The restricted
|
||||
* support for cancellation ensures that for any in-progress
|
||||
* transition there will always be at least one client that will be
|
||||
* notified when the operation completes.
|
||||
*
|
||||
* If the cancellation fails the service retains control of the client
|
||||
* object, and the client must wait for operation completion.
|
||||
*
|
||||
* @param srv the service for which an operation is to be cancelled.
|
||||
*
|
||||
* @param cli a pointer to the same client state that was provided
|
||||
* when the operation to be cancelled was issued. If the cancellation
|
||||
* is successful the client will be notified of operation completion
|
||||
* with a result of `-ECANCELED`.
|
||||
*
|
||||
* @retval 0 if the cancellation was completed before the client could
|
||||
* be notified. The client will be notified through cli with an
|
||||
* operation completion of `-ECANCELED`.
|
||||
* @retval -EINVAL if the parameters are invalid.
|
||||
* @retval -EWOULDBLOCK if cancellation was rejected because the
|
||||
* client is the only waiter for an in-progress transition. The
|
||||
* service retains control of the client structure.
|
||||
* @retval -EALREADY if cli was not a record of an uncompleted
|
||||
* notification at the time the cancellation was processed. This
|
||||
* likely indicates that the operation and client notification had
|
||||
* already completed.
|
||||
*/
|
||||
int onoff_cancel(struct onoff_service *srv,
|
||||
struct onoff_client *cli);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_ONOFF_H_ */
|
|
@ -12,6 +12,7 @@ zephyr_sources(
|
|||
hex.c
|
||||
mempool.c
|
||||
printk.c
|
||||
onoff.c
|
||||
rb.c
|
||||
sem.c
|
||||
thread_entry.c
|
||||
|
|
518
lib/os/onoff.c
Normal file
518
lib/os/onoff.c
Normal file
|
@ -0,0 +1,518 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <sys/onoff.h>
|
||||
#include <syscall_handler.h>
|
||||
|
||||
#define CLIENT_NOTIFY_METHOD_MASK 0x03
|
||||
#define CLIENT_VALID_FLAGS_MASK 0x07
|
||||
|
||||
#define SERVICE_CONFIG_FLAGS \
|
||||
(ONOFF_SERVICE_START_SLEEPS \
|
||||
| ONOFF_SERVICE_STOP_SLEEPS \
|
||||
| ONOFF_SERVICE_RESET_SLEEPS)
|
||||
|
||||
#define SERVICE_REFS_MAX UINT16_MAX
|
||||
|
||||
#define SERVICE_STATE_OFF 0
|
||||
#define SERVICE_STATE_ON ONOFF_SERVICE_INTERNAL_BASE
|
||||
#define SERVICE_STATE_TRANSITION (ONOFF_SERVICE_INTERNAL_BASE << 1)
|
||||
#define SERVICE_STATE_TO_ON (SERVICE_STATE_TRANSITION | SERVICE_STATE_ON)
|
||||
#define SERVICE_STATE_TO_OFF (SERVICE_STATE_TRANSITION | SERVICE_STATE_OFF)
|
||||
|
||||
#define SERVICE_STATE_MASK (SERVICE_STATE_ON | SERVICE_STATE_TRANSITION)
|
||||
|
||||
static void set_service_state(struct onoff_service *srv,
|
||||
u32_t state)
|
||||
{
|
||||
srv->flags &= ~SERVICE_STATE_MASK;
|
||||
srv->flags |= (state & SERVICE_STATE_MASK);
|
||||
}
|
||||
|
||||
static int validate_args(const struct onoff_service *srv,
|
||||
struct onoff_client *cli)
|
||||
{
|
||||
if ((srv == NULL) || (cli == NULL)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int rv = 0;
|
||||
u32_t mode = cli->flags;
|
||||
|
||||
/* Reject unexpected flags. */
|
||||
if (mode != (cli->flags & CLIENT_VALID_FLAGS_MASK)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Validate configuration based on mode */
|
||||
switch (mode & CLIENT_NOTIFY_METHOD_MASK) {
|
||||
case ONOFF_CLIENT_NOTIFY_SPINWAIT:
|
||||
break;
|
||||
case ONOFF_CLIENT_NOTIFY_CALLBACK:
|
||||
if (cli->async.callback.handler == NULL) {
|
||||
rv = -EINVAL;
|
||||
}
|
||||
break;
|
||||
case ONOFF_CLIENT_NOTIFY_SIGNAL:
|
||||
if (cli->async.signal == NULL) {
|
||||
rv = -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rv = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear the result here instead of in all callers. */
|
||||
if (rv == 0) {
|
||||
cli->result = 0;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int onoff_service_init(struct onoff_service *srv,
|
||||
onoff_service_transition_fn start,
|
||||
onoff_service_transition_fn stop,
|
||||
onoff_service_transition_fn reset,
|
||||
u32_t flags)
|
||||
{
|
||||
if ((flags & SERVICE_CONFIG_FLAGS) != flags) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((start == NULL) || (stop == NULL)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*srv = (struct onoff_service)ONOFF_SERVICE_INITIALIZER(start, stop,
|
||||
reset, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void notify_one(struct onoff_service *srv,
|
||||
struct onoff_client *cli,
|
||||
int res)
|
||||
{
|
||||
unsigned int flags = cli->flags;
|
||||
|
||||
/* Store the result, and notify if requested. */
|
||||
cli->result = res;
|
||||
cli->flags = 0;
|
||||
switch (flags & CLIENT_NOTIFY_METHOD_MASK) {
|
||||
case ONOFF_CLIENT_NOTIFY_SPINWAIT:
|
||||
break;
|
||||
case ONOFF_CLIENT_NOTIFY_CALLBACK:
|
||||
cli->async.callback.handler(srv, cli,
|
||||
cli->async.callback.user_data, res);
|
||||
break;
|
||||
#ifdef CONFIG_POLL
|
||||
case ONOFF_CLIENT_NOTIFY_SIGNAL:
|
||||
k_poll_signal_raise(cli->async.signal, res);
|
||||
break;
|
||||
#endif /* CONFIG_POLL */
|
||||
default:
|
||||
__ASSERT_NO_MSG(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void notify_all(struct onoff_service *srv,
|
||||
sys_slist_t *list,
|
||||
int res)
|
||||
{
|
||||
while (!sys_slist_is_empty(list)) {
|
||||
sys_snode_t *node = sys_slist_get_not_empty(list);
|
||||
struct onoff_client *cli =
|
||||
CONTAINER_OF(node,
|
||||
struct onoff_client,
|
||||
node);
|
||||
|
||||
notify_one(srv, cli, res);
|
||||
}
|
||||
}
|
||||
|
||||
static void onoff_start_notify(struct onoff_service *srv,
|
||||
int res)
|
||||
{
|
||||
k_spinlock_key_t key = k_spin_lock(&srv->lock);
|
||||
sys_slist_t clients = srv->clients;
|
||||
|
||||
/* Can't have a queued releaser during start */
|
||||
__ASSERT_NO_MSG(srv->releaser == NULL);
|
||||
|
||||
/* If the start failed log an error and leave the rest of the
|
||||
* state in place for diagnostics.
|
||||
*
|
||||
* If the start succeeded record a reference for all clients
|
||||
* and set the state to ON. There must be at least one client
|
||||
* left to receive the result.
|
||||
*
|
||||
* In either case reset the client queue and notify all
|
||||
* clients of operation completion.
|
||||
*/
|
||||
if (res < 0) {
|
||||
srv->flags &= ~SERVICE_STATE_TRANSITION;
|
||||
srv->flags |= ONOFF_SERVICE_HAS_ERROR;
|
||||
} else {
|
||||
sys_snode_t *node;
|
||||
unsigned int refs = 0U;
|
||||
|
||||
set_service_state(srv, SERVICE_STATE_ON);
|
||||
|
||||
SYS_SLIST_FOR_EACH_NODE(&clients, node) {
|
||||
refs += 1U;
|
||||
}
|
||||
|
||||
/* Update the reference count, or fail if the count
|
||||
* would overflow.
|
||||
*/
|
||||
if (srv->refs > (SERVICE_REFS_MAX - refs)) {
|
||||
srv->flags |= ONOFF_SERVICE_HAS_ERROR;
|
||||
} else {
|
||||
srv->refs += refs;
|
||||
}
|
||||
__ASSERT_NO_MSG(srv->refs > 0U);
|
||||
}
|
||||
|
||||
sys_slist_init(&srv->clients);
|
||||
|
||||
k_spin_unlock(&srv->lock, key);
|
||||
|
||||
notify_all(srv, &clients, res);
|
||||
}
|
||||
|
||||
int onoff_request(struct onoff_service *srv,
|
||||
struct onoff_client *cli)
|
||||
{
|
||||
bool add_client = false; /* add client to pending list */
|
||||
bool start = false; /* invoke start transition */
|
||||
bool notify = false; /* do client notification */
|
||||
int rv = validate_args(srv, cli);
|
||||
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&srv->lock);
|
||||
|
||||
if ((srv->flags & ONOFF_SERVICE_HAS_ERROR) != 0) {
|
||||
rv = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Reject if this would overflow the reference count. */
|
||||
if (srv->refs == SERVICE_REFS_MAX) {
|
||||
rv = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
u32_t state = srv->flags & SERVICE_STATE_MASK;
|
||||
|
||||
switch (state) {
|
||||
case SERVICE_STATE_TO_OFF:
|
||||
/* Queue to start after release */
|
||||
__ASSERT_NO_MSG(srv->releaser != NULL);
|
||||
add_client = true;
|
||||
rv = 3;
|
||||
break;
|
||||
case SERVICE_STATE_OFF:
|
||||
/* Reject if in a non-thread context and start could
|
||||
* wait.
|
||||
*/
|
||||
if ((k_is_in_isr() || k_is_pre_kernel())
|
||||
&& ((srv->flags & ONOFF_SERVICE_START_SLEEPS) != 0U)) {
|
||||
rv = -EWOULDBLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Start with first request while off */
|
||||
__ASSERT_NO_MSG(srv->refs == 0);
|
||||
set_service_state(srv, SERVICE_STATE_TO_ON);
|
||||
start = true;
|
||||
add_client = true;
|
||||
rv = 2;
|
||||
break;
|
||||
case SERVICE_STATE_TO_ON:
|
||||
/* Already starting, just queue it */
|
||||
add_client = true;
|
||||
rv = 1;
|
||||
break;
|
||||
case SERVICE_STATE_ON:
|
||||
/* Just increment the reference count */
|
||||
notify = true;
|
||||
break;
|
||||
default:
|
||||
rv = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if (add_client) {
|
||||
sys_slist_append(&srv->clients, &cli->node);
|
||||
} else if (notify) {
|
||||
srv->refs += 1;
|
||||
}
|
||||
|
||||
k_spin_unlock(&srv->lock, key);
|
||||
|
||||
if (start) {
|
||||
__ASSERT_NO_MSG(srv->start != NULL);
|
||||
srv->start(srv, onoff_start_notify);
|
||||
} else if (notify) {
|
||||
notify_one(srv, cli, 0);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void onoff_stop_notify(struct onoff_service *srv,
|
||||
int res)
|
||||
{
|
||||
bool notify_clients = false;
|
||||
int client_res = res;
|
||||
bool start = false;
|
||||
k_spinlock_key_t key = k_spin_lock(&srv->lock);
|
||||
sys_slist_t clients = srv->clients;
|
||||
struct onoff_client *releaser = srv->releaser;
|
||||
|
||||
/* If the stop operation failed log an error and leave the
|
||||
* rest of the state in place.
|
||||
*
|
||||
* If it succeeded remove the last reference and transition to
|
||||
* off.
|
||||
*
|
||||
* In either case remove the last reference, and notify all
|
||||
* waiting clients of operation completion.
|
||||
*/
|
||||
if (res < 0) {
|
||||
srv->flags &= ~SERVICE_STATE_TRANSITION;
|
||||
srv->flags |= ONOFF_SERVICE_HAS_ERROR;
|
||||
notify_clients = true;
|
||||
} else if (sys_slist_is_empty(&clients)) {
|
||||
set_service_state(srv, SERVICE_STATE_OFF);
|
||||
} else if ((k_is_in_isr() || k_is_pre_kernel())
|
||||
&& ((srv->flags & ONOFF_SERVICE_START_SLEEPS) != 0U)) {
|
||||
set_service_state(srv, SERVICE_STATE_OFF);
|
||||
notify_clients = true;
|
||||
client_res = -EWOULDBLOCK;
|
||||
} else {
|
||||
set_service_state(srv, SERVICE_STATE_TO_ON);
|
||||
start = true;
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(releaser);
|
||||
srv->refs -= 1U;
|
||||
srv->releaser = NULL;
|
||||
__ASSERT_NO_MSG(srv->refs == 0);
|
||||
|
||||
/* Remove the clients if there was an error or a delayed start
|
||||
* couldn't be initiated, because we're resolving their
|
||||
* operation with an error.
|
||||
*/
|
||||
if (notify_clients) {
|
||||
sys_slist_init(&srv->clients);
|
||||
}
|
||||
|
||||
k_spin_unlock(&srv->lock, key);
|
||||
|
||||
/* Notify the releaser. If there was an error, notify any
|
||||
* pending requests; otherwise if there are pending requests
|
||||
* start the transition to ON.
|
||||
*/
|
||||
notify_one(srv, releaser, res);
|
||||
if (notify_clients) {
|
||||
notify_all(srv, &clients, client_res);
|
||||
} else if (start) {
|
||||
srv->start(srv, onoff_start_notify);
|
||||
}
|
||||
}
|
||||
|
||||
int onoff_release(struct onoff_service *srv,
|
||||
struct onoff_client *cli)
|
||||
{
|
||||
bool stop = false; /* invoke stop transition */
|
||||
bool notify = false; /* do client notification */
|
||||
int rv = validate_args(srv, cli);
|
||||
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&srv->lock);
|
||||
|
||||
if ((srv->flags & ONOFF_SERVICE_HAS_ERROR) != 0) {
|
||||
rv = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
u32_t state = srv->flags & SERVICE_STATE_MASK;
|
||||
|
||||
switch (state) {
|
||||
case SERVICE_STATE_ON:
|
||||
/* Stay on if release leaves a client. */
|
||||
if (srv->refs > 1U) {
|
||||
notify = true;
|
||||
rv = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Reject if in non-thread context but stop could
|
||||
* wait
|
||||
*/
|
||||
if ((k_is_in_isr() || k_is_pre_kernel())
|
||||
&& ((srv->flags & ONOFF_SERVICE_STOP_SLEEPS) != 0)) {
|
||||
rv = -EWOULDBLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
stop = true;
|
||||
|
||||
set_service_state(srv, SERVICE_STATE_TO_OFF);
|
||||
srv->releaser = cli;
|
||||
rv = 2;
|
||||
|
||||
break;
|
||||
case SERVICE_STATE_TO_ON:
|
||||
rv = -EBUSY;
|
||||
break;
|
||||
case SERVICE_STATE_OFF:
|
||||
case SERVICE_STATE_TO_OFF:
|
||||
rv = -EALREADY;
|
||||
break;
|
||||
default:
|
||||
rv = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
if (notify) {
|
||||
srv->refs -= 1U;
|
||||
}
|
||||
|
||||
k_spin_unlock(&srv->lock, key);
|
||||
|
||||
if (stop) {
|
||||
__ASSERT_NO_MSG(srv->stop != NULL);
|
||||
srv->stop(srv, onoff_stop_notify);
|
||||
} else if (notify) {
|
||||
notify_one(srv, cli, 0);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void onoff_reset_notify(struct onoff_service *srv,
|
||||
int res)
|
||||
{
|
||||
k_spinlock_key_t key = k_spin_lock(&srv->lock);
|
||||
sys_slist_t clients = srv->clients;
|
||||
|
||||
/* If the reset failed clear the transition flag but otherwise
|
||||
* leave the state unchanged.
|
||||
*
|
||||
* If it was successful clear the reference count and all
|
||||
* flags except capability flags (sets to SERVICE_STATE_OFF).
|
||||
*/
|
||||
if (res < 0) {
|
||||
srv->flags &= ~SERVICE_STATE_TRANSITION;
|
||||
} else {
|
||||
__ASSERT_NO_MSG(srv->refs == 0U);
|
||||
srv->refs = 0U;
|
||||
srv->flags &= SERVICE_CONFIG_FLAGS;
|
||||
}
|
||||
|
||||
sys_slist_init(&srv->clients);
|
||||
|
||||
k_spin_unlock(&srv->lock, key);
|
||||
|
||||
notify_all(srv, &clients, res);
|
||||
}
|
||||
|
||||
int onoff_service_reset(struct onoff_service *srv,
|
||||
struct onoff_client *cli)
|
||||
{
|
||||
if (srv->reset == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
bool reset = false;
|
||||
int rv = validate_args(srv, cli);
|
||||
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Reject if in a non-thread context and reset could wait. */
|
||||
if ((k_is_in_isr() || k_is_pre_kernel())
|
||||
&& ((srv->flags & ONOFF_SERVICE_RESET_SLEEPS) != 0U)) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&srv->lock);
|
||||
|
||||
if ((srv->flags & ONOFF_SERVICE_HAS_ERROR) == 0) {
|
||||
rv = -EALREADY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((srv->flags & SERVICE_STATE_TRANSITION) == 0) {
|
||||
reset = true;
|
||||
srv->flags |= SERVICE_STATE_TRANSITION;
|
||||
}
|
||||
|
||||
out:
|
||||
if (rv >= 0) {
|
||||
sys_slist_append(&srv->clients, &cli->node);
|
||||
}
|
||||
|
||||
k_spin_unlock(&srv->lock, key);
|
||||
|
||||
if (reset) {
|
||||
srv->reset(srv, onoff_reset_notify);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int onoff_cancel(struct onoff_service *srv,
|
||||
struct onoff_client *cli)
|
||||
{
|
||||
int rv = validate_args(srv, cli);
|
||||
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = -EALREADY;
|
||||
k_spinlock_key_t key = k_spin_lock(&srv->lock);
|
||||
u32_t state = srv->flags & SERVICE_STATE_MASK;
|
||||
|
||||
/* Can't remove the last client waiting for the in-progress
|
||||
* transition, as there would be nobody to receive the
|
||||
* completion notification, which might indicate a service
|
||||
* error.
|
||||
*/
|
||||
if (sys_slist_find_and_remove(&srv->clients, &cli->node)) {
|
||||
rv = 0;
|
||||
if (sys_slist_is_empty(&srv->clients)
|
||||
&& (state != SERVICE_STATE_TO_OFF)) {
|
||||
rv = -EWOULDBLOCK;
|
||||
sys_slist_append(&srv->clients, &cli->node);
|
||||
}
|
||||
} else if (srv->releaser == cli) {
|
||||
/* must be waiting for TO_OFF to complete */
|
||||
rv = -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
k_spin_unlock(&srv->lock, key);
|
||||
|
||||
if (rv == 0) {
|
||||
notify_one(srv, cli, -ECANCELED);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
8
tests/lib/onoff/CMakeLists.txt
Normal file
8
tests/lib/onoff/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.13.1)
|
||||
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
|
||||
project(onoff)
|
||||
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app PRIVATE ${app_sources})
|
0
tests/lib/onoff/README
Normal file
0
tests/lib/onoff/README
Normal file
2
tests/lib/onoff/prj.conf
Normal file
2
tests/lib/onoff/prj.conf
Normal file
|
@ -0,0 +1,2 @@
|
|||
CONFIG_POLL=y
|
||||
CONFIG_ZTEST=y
|
1164
tests/lib/onoff/src/main.c
Normal file
1164
tests/lib/onoff/src/main.c
Normal file
File diff suppressed because it is too large
Load diff
3
tests/lib/onoff/testcase.yaml
Normal file
3
tests/lib/onoff/testcase.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
tests:
|
||||
libraries.onoff:
|
||||
tags: onoff timer
|
Loading…
Add table
Add a link
Reference in a new issue