sys: add generic asynchronous notification infrastructure
k_poll() for a signal is often desired for notification of completion of asynchronous operations, but there are APIs where it may be necessary to invoke "asynchronous" operations from contexts where sleep is disallowed, or before the kernel has been initialized. Extract the general notification solution from the on-off service into a utility that can be used for other APIs. Also move documentation out to a resource management section. Signed-off-by: Peter Bigot <peter.bigot@nordicsemi.no>
This commit is contained in:
parent
e2ca46c329
commit
fadd98aad2
|
@ -125,7 +125,7 @@
|
|||
/doc/guides/dts/ @galak @mbolivar-nordic
|
||||
/doc/reference/bluetooth/ @joerchan @jhedberg @Vudentz
|
||||
/doc/reference/devicetree/ @galak @mbolivar-nordic
|
||||
/doc/reference/kernel/other/resource_mgmt.rst @pabigot
|
||||
/doc/reference/resource_management/ @pabigot
|
||||
/doc/reference/networking/can* @alexanderwachter
|
||||
/drivers/debug/ @nashif
|
||||
/drivers/*/*cc13xx_cc26xx* @bwitherspoon
|
||||
|
|
|
@ -9,6 +9,7 @@ API Reference
|
|||
overview.rst
|
||||
terminology.rst
|
||||
audio/index.rst
|
||||
misc/notify.rst
|
||||
bluetooth/index.rst
|
||||
kconfig/index.rst
|
||||
crypto/index.rst
|
||||
|
@ -23,6 +24,7 @@ API Reference
|
|||
peripherals/index.rst
|
||||
power_management/index.rst
|
||||
random/index.rst
|
||||
resource_management/index.rst
|
||||
shell/index.rst
|
||||
storage/index.rst
|
||||
usb/index.rst
|
||||
|
|
|
@ -116,7 +116,6 @@ 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
|
||||
|
|
25
doc/reference/misc/notify.rst
Normal file
25
doc/reference/misc/notify.rst
Normal file
|
@ -0,0 +1,25 @@
|
|||
.. _async_notification:
|
||||
|
||||
Asynchronous Notification APIs
|
||||
##############################
|
||||
|
||||
Zephyr APIs often include :ref:`api_term_async` functions where an
|
||||
operation is initiated and the application needs to be informed when it
|
||||
completes, and whether it succeeded. Using :cpp:func:`k_poll()` is
|
||||
often a good method, but some application architectures may be more
|
||||
suited to a callback notification, and operations like enabling clocks
|
||||
and power rails may need to be invoked before kernel functions are
|
||||
available so a busy-wait for completion may be needed.
|
||||
|
||||
This API is intended to be embedded within specific subsystems such as
|
||||
:ref:`resource_mgmt_onoff` and other APIs that support async
|
||||
transactions. The subsystem wrappers are responsible for extracting
|
||||
operation-specific data from requests that include a notification
|
||||
element, and for invoking callbacks with the parameters required by the
|
||||
API.
|
||||
|
||||
API Reference
|
||||
*************
|
||||
|
||||
.. doxygengroup:: sys_notify_apis
|
||||
:project: Zephyr
|
|
@ -15,6 +15,8 @@ suggests that a shared implementation is desirable.
|
|||
:depth: 2
|
||||
|
||||
|
||||
.. _resource_mgmt_onoff:
|
||||
|
||||
On-Off Services
|
||||
***************
|
||||
|
361
include/sys/notify.h
Normal file
361
include/sys/notify.h
Normal file
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_NOTIFY_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_NOTIFY_H_
|
||||
|
||||
#include <kernel.h>
|
||||
#include <zephyr/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct sys_notify;
|
||||
|
||||
/*
|
||||
* Flag value that overwrites the method field when the operation has
|
||||
* completed.
|
||||
*/
|
||||
#define SYS_NOTIFY_METHOD_COMPLETED 0
|
||||
|
||||
/*
|
||||
* Indicates that no notification will be provided.
|
||||
*
|
||||
* Callers must check for completions using
|
||||
* sys_notify_fetch_result().
|
||||
*
|
||||
* See sys_notify_init_spinwait().
|
||||
*/
|
||||
#define SYS_NOTIFY_METHOD_SPINWAIT 1
|
||||
|
||||
/*
|
||||
* Select notification through @ref k_poll signal
|
||||
*
|
||||
* See sys_notify_init_signal().
|
||||
*/
|
||||
#define SYS_NOTIFY_METHOD_SIGNAL 2
|
||||
|
||||
/*
|
||||
* Select notification through a user-provided callback.
|
||||
*
|
||||
* See sys_notify_init_callback().
|
||||
*/
|
||||
#define SYS_NOTIFY_METHOD_CALLBACK 3
|
||||
|
||||
#define SYS_NOTIFY_METHOD_MASK 0x03
|
||||
#define SYS_NOTIFY_METHOD_POS 0
|
||||
|
||||
/**
|
||||
* @brief Identify the region of sys_notify flags available for
|
||||
* containing services.
|
||||
*
|
||||
* Bits of the flags field of the sys_notify structure at and above
|
||||
* this position may be used by extensions to the sys_notify
|
||||
* structure.
|
||||
*
|
||||
* These bits are intended for use by containing service
|
||||
* implementations to record client-specific information. The bits
|
||||
* are cleared by sys_notify_validate(). Use of these does not
|
||||
* imply that the flags field becomes public API.
|
||||
*/
|
||||
#define SYS_NOTIFY_EXTENSION_POS 2
|
||||
|
||||
/*
|
||||
* Mask isolating the bits of sys_notify::flags that are available
|
||||
* for extension.
|
||||
*/
|
||||
#define SYS_NOTIFY_EXTENSION_MASK (~BIT_MASK(SYS_NOTIFY_EXTENSION_POS))
|
||||
|
||||
/**
|
||||
* @defgroup sys_notify_apis Asynchronous Notification APIs
|
||||
* @ingroup kernel_apis
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Generic signature used to notify of result completion by
|
||||
* callback.
|
||||
*
|
||||
* Functions with this role may be invoked from any context including
|
||||
* pre-kernel, ISR, or cooperative or pre-emptible threads.
|
||||
* Compatible functions must be isr-ok and not sleep.
|
||||
*
|
||||
* Parameters that should generally be passed to such functions include:
|
||||
*
|
||||
* * a pointer to a specific client request structure, i.e. the one
|
||||
* that contains the sys_notify structure.
|
||||
* * the result of the operation, either as passed to
|
||||
* sys_notify_finalize() or extracted afterwards using
|
||||
* sys_notify_fetch_result(). Expected values are
|
||||
* service-specific, but the value shall be non-negative if the
|
||||
* operation succeeded, and negative if the operation failed.
|
||||
*/
|
||||
typedef void (*sys_notify_generic_callback)();
|
||||
|
||||
/**
|
||||
* @brief State associated with notification for an asynchronous
|
||||
* operation.
|
||||
*
|
||||
* Objects of this type are allocated by a client, which must use an
|
||||
* initialization function (e.g. sys_notify_init_signal()) to
|
||||
* configure them. Generally the structure is a member of a
|
||||
* service-specific client structure, such as onoff_client.
|
||||
*
|
||||
* Control of the containing object transfers to the service provider
|
||||
* when a pointer to the object is passed to a service function that
|
||||
* is documented to take control of the object, such as
|
||||
* onoff_service_request(). 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;
|
||||
* * when operation completion is posted. This may occur before the
|
||||
* call to the service API returns.
|
||||
*
|
||||
* Operation completion is technically posted when the flags field is
|
||||
* updated so that sys_notify_fetch_result() returns success. This
|
||||
* will happen before the signal is posted or callback is invoked.
|
||||
* Note that although the manager will no longer reference the
|
||||
* sys_notify object past this point, the containing object may have
|
||||
* state that will be referenced within the callback. Where callbacks
|
||||
* are used control of the containing object does not revert to the
|
||||
* client until the callback has been invoked. (Re-use within the
|
||||
* callback is explicitly permitted.)
|
||||
*
|
||||
* After control has reverted to the client the notify object must be
|
||||
* reinitialized for the next operation.
|
||||
*
|
||||
* The content of this structure is not public API to clients: all
|
||||
* configuration and inspection should be done with functions like
|
||||
* sys_notify_init_callback() and sys_notify_fetch_result().
|
||||
* However, services that use this structure may access certain
|
||||
* fields directly.
|
||||
*/
|
||||
struct sys_notify {
|
||||
union method {
|
||||
/* Pointer to signal used to notify client.
|
||||
*
|
||||
* The signal value corresponds to the res parameter
|
||||
* of sys_notify_callback.
|
||||
*/
|
||||
struct k_poll_signal *signal;
|
||||
|
||||
/* Generic callback function for callback notification. */
|
||||
sys_notify_generic_callback callback;
|
||||
} method;
|
||||
|
||||
/*
|
||||
* Flags recording information about the operation.
|
||||
*
|
||||
* Bits below SYS_NOTIFY_EXTENSION_POS are initialized by
|
||||
* async notify API init functions like
|
||||
* sys_notify_init_callback(), and must not by modified by
|
||||
* extensions or client code.
|
||||
*
|
||||
* Bits at and above SYS_NOTIFY_EXTENSION_POS are available
|
||||
* for use by service extensions while the containing object
|
||||
* is managed by the service. They are not for client use,
|
||||
* are zeroed by the async notify API init functions, and will
|
||||
* be zeroed by sys_notify_finalize().
|
||||
*/
|
||||
u32_t volatile flags;
|
||||
|
||||
/*
|
||||
* 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 spin-wait synchronous operations.
|
||||
*/
|
||||
int volatile result;
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
static inline u32_t sys_notify_get_method(const struct sys_notify *notify)
|
||||
{
|
||||
u32_t method = notify->flags >> SYS_NOTIFY_METHOD_POS;
|
||||
|
||||
return method & SYS_NOTIFY_METHOD_MASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate and initialize the notify structure.
|
||||
*
|
||||
* This should be invoked at the start of any service-specific
|
||||
* configuration validation. It ensures that the basic asynchronous
|
||||
* notification configuration is consistent, and clears the result.
|
||||
*
|
||||
* Note that this function does not validate extension bits (zeroed by
|
||||
* async notify API init functions like sys_notify_init_callback()).
|
||||
* It may fail to recognize that an uninitialized structure has been
|
||||
* passed because only method bits of flags are tested against method
|
||||
* settings. To reduce the chance of accepting an uninititalized
|
||||
* operation service validation of structures that contain an
|
||||
* sys_notify instance should confirm that the extension bits are
|
||||
* set or cleared as expected.
|
||||
*
|
||||
* @retval 0 on successful validation and reinitialization
|
||||
* @retval -EINVAL if the configuration is not valid.
|
||||
*/
|
||||
int sys_notify_validate(struct sys_notify *notify);
|
||||
|
||||
/**
|
||||
* @brief Record and signal the operation completion.
|
||||
*
|
||||
* @param notify pointer to the notification state structure.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return If the notification is to be done by callback this returns
|
||||
* the generic version of the function to be invoked. The caller must
|
||||
* immediately invoke that function with whatever arguments are
|
||||
* expected by the callback. If notification is by spin-wait or
|
||||
* signal, the notification has been completed by the point this
|
||||
* function returns, and a null pointer is returned.
|
||||
*/
|
||||
sys_notify_generic_callback sys_notify_finalize(struct sys_notify *notify,
|
||||
int res);
|
||||
|
||||
/**
|
||||
* @brief Check for and read the result of an asynchronous operation.
|
||||
*
|
||||
* @param notify 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 sys_notify_fetch_result(const struct sys_notify *notify,
|
||||
int *result)
|
||||
{
|
||||
__ASSERT_NO_MSG(notify != NULL);
|
||||
__ASSERT_NO_MSG(result != NULL);
|
||||
int rv = -EAGAIN;
|
||||
|
||||
if (sys_notify_get_method(notify) == SYS_NOTIFY_METHOD_COMPLETED) {
|
||||
rv = 0;
|
||||
*result = notify->result;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize a notify object for spin-wait notification.
|
||||
*
|
||||
* Clients that use this initialization receive no asynchronous
|
||||
* notification, and instead must periodically check for completion
|
||||
* using sys_notify_fetch_result().
|
||||
*
|
||||
* On completion of the operation the client object must be
|
||||
* reinitialized before it can be re-used.
|
||||
*
|
||||
* @param notify pointer to the notification configuration object.
|
||||
*/
|
||||
static inline void sys_notify_init_spinwait(struct sys_notify *notify)
|
||||
{
|
||||
__ASSERT_NO_MSG(notify != NULL);
|
||||
|
||||
*notify = (struct sys_notify){
|
||||
.flags = SYS_NOTIFY_METHOD_SPINWAIT,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize a notify object for (k_poll) signal notification.
|
||||
*
|
||||
* Clients that use this initialization will be notified of the
|
||||
* completion of operations through the provided signal.
|
||||
*
|
||||
* On completion of the operation the client object must be
|
||||
* reinitialized before it can be re-used.
|
||||
*
|
||||
* @note
|
||||
* @rst
|
||||
* This capability is available only when :option:`CONFIG_POLL` is
|
||||
* selected.
|
||||
* @endrst
|
||||
*
|
||||
* @param notify pointer to the notification configuration 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 sys_notify_init_signal(struct sys_notify *notify,
|
||||
struct k_poll_signal *sigp)
|
||||
{
|
||||
__ASSERT_NO_MSG(notify != NULL);
|
||||
__ASSERT_NO_MSG(sigp != NULL);
|
||||
|
||||
*notify = (struct sys_notify){
|
||||
.method = {
|
||||
.signal = sigp,
|
||||
},
|
||||
.flags = SYS_NOTIFY_METHOD_SIGNAL,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize a notify object for callback notification.
|
||||
*
|
||||
* Clients that use this initialization will be notified of the
|
||||
* completion of operations through the provided callback. Note that
|
||||
* callbacks may be invoked from various contexts depending on the
|
||||
* specific service; see @ref sys_notify_generic_callback.
|
||||
*
|
||||
* On completion of the operation the client object must be
|
||||
* reinitialized before it can be re-used.
|
||||
*
|
||||
* @param notify pointer to the notification configuration object.
|
||||
*
|
||||
* @param handler a function pointer to use for notification.
|
||||
*/
|
||||
static inline void sys_notify_init_callback(struct sys_notify *notify,
|
||||
sys_notify_generic_callback handler)
|
||||
{
|
||||
__ASSERT_NO_MSG(notify != NULL);
|
||||
__ASSERT_NO_MSG(handler != NULL);
|
||||
|
||||
*notify = (struct sys_notify){
|
||||
.method = {
|
||||
.callback = handler,
|
||||
},
|
||||
.flags = SYS_NOTIFY_METHOD_CALLBACK,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Detect whether a particular notification uses a callback.
|
||||
*
|
||||
* The generic handler does not capture the signature expected by the
|
||||
* callback, and the translation to a service-specific callback must
|
||||
* be provided by the service. This check allows abstracted services
|
||||
* to reject callback notification requests when the service doesn't
|
||||
* provide a translation function.
|
||||
*
|
||||
* @return true if and only if a callback is to be used for notification.
|
||||
*/
|
||||
static inline bool sys_notify_uses_callback(const struct sys_notify *notify)
|
||||
{
|
||||
__ASSERT_NO_MSG(notify != NULL);
|
||||
|
||||
return sys_notify_get_method(notify) == SYS_NOTIFY_METHOD_CALLBACK;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_NOTIFY_H_ */
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <kernel.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <sys/notify.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -192,44 +193,6 @@ struct onoff_service {
|
|||
int onoff_service_init(struct onoff_service *srv,
|
||||
const struct onoff_service_transitions *transitions);
|
||||
|
||||
/** @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;
|
||||
|
||||
|
@ -286,32 +249,11 @@ 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;
|
||||
/* Notification configuration. */
|
||||
struct sys_notify notify;
|
||||
|
||||
/* 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;
|
||||
/* User data for callback-based notification. */
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -330,15 +272,8 @@ 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;
|
||||
return sys_notify_fetch_result(&op->notify, result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -358,9 +293,8 @@ 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,
|
||||
};
|
||||
*cli = (struct onoff_client){};
|
||||
sys_notify_init_spinwait(&cli->notify);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -390,16 +324,9 @@ 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,
|
||||
};
|
||||
*cli = (struct onoff_client){};
|
||||
sys_notify_init_signal(&cli->notify, sigp);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -430,14 +357,9 @@ static inline void onoff_client_init_callback(struct onoff_client *cli,
|
|||
__ASSERT_NO_MSG(handler != NULL);
|
||||
|
||||
*cli = (struct onoff_client){
|
||||
.async = {
|
||||
.callback = {
|
||||
.handler = handler,
|
||||
.user_data = user_data,
|
||||
},
|
||||
},
|
||||
.flags = ONOFF_CLIENT_NOTIFY_CALLBACK,
|
||||
.user_data = user_data,
|
||||
};
|
||||
sys_notify_init_callback(&cli->notify, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,6 +11,7 @@ zephyr_sources(
|
|||
fdtable.c
|
||||
hex.c
|
||||
mempool.c
|
||||
notify.c
|
||||
printk.c
|
||||
onoff.c
|
||||
rb.c
|
||||
|
|
84
lib/os/notify.c
Normal file
84
lib/os/notify.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <sys/notify.h>
|
||||
|
||||
int sys_notify_validate(struct sys_notify *notify)
|
||||
{
|
||||
int rv = 0;
|
||||
|
||||
if (notify == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Validate configuration based on mode */
|
||||
switch (sys_notify_get_method(notify)) {
|
||||
case SYS_NOTIFY_METHOD_SPINWAIT:
|
||||
break;
|
||||
case SYS_NOTIFY_METHOD_CALLBACK:
|
||||
if (notify->method.callback == NULL) {
|
||||
rv = -EINVAL;
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_POLL
|
||||
case SYS_NOTIFY_METHOD_SIGNAL:
|
||||
if (notify->method.signal == NULL) {
|
||||
rv = -EINVAL;
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_POLL */
|
||||
default:
|
||||
rv = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear the result here instead of in all callers. */
|
||||
if (rv == 0) {
|
||||
notify->result = 0;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
sys_notify_generic_callback sys_notify_finalize(struct sys_notify *notify,
|
||||
int res)
|
||||
{
|
||||
struct k_poll_signal *sig = NULL;
|
||||
sys_notify_generic_callback rv = 0;
|
||||
u32_t method = sys_notify_get_method(notify);
|
||||
|
||||
/* Store the result and capture secondary notification
|
||||
* information.
|
||||
*/
|
||||
notify->result = res;
|
||||
switch (method) {
|
||||
case SYS_NOTIFY_METHOD_SPINWAIT:
|
||||
break;
|
||||
case SYS_NOTIFY_METHOD_CALLBACK:
|
||||
rv = notify->method.callback;
|
||||
break;
|
||||
case SYS_NOTIFY_METHOD_SIGNAL:
|
||||
sig = notify->method.signal;
|
||||
break;
|
||||
default:
|
||||
__ASSERT_NO_MSG(false);
|
||||
}
|
||||
|
||||
/* Mark completion by clearing the flags field to the
|
||||
* completed state, releasing any spin-waiters, then complete
|
||||
* secondary notification.
|
||||
*/
|
||||
compiler_barrier();
|
||||
notify->flags = SYS_NOTIFY_METHOD_COMPLETED;
|
||||
|
||||
if (IS_ENABLED(CONFIG_POLL) && (sig != NULL)) {
|
||||
k_poll_signal_raise(sig, res);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
|
@ -6,10 +6,6 @@
|
|||
|
||||
#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 \
|
||||
|
@ -41,36 +37,11 @@ static int validate_args(const struct onoff_service *srv,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
int rv = 0;
|
||||
u32_t mode = cli->flags;
|
||||
int rv = sys_notify_validate(&cli->notify);
|
||||
|
||||
/* 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:
|
||||
if ((rv == 0)
|
||||
&& ((cli->notify.flags & SYS_NOTIFY_EXTENSION_MASK) != 0)) {
|
||||
rv = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear the result here instead of in all callers. */
|
||||
if (rv == 0) {
|
||||
cli->result = 0;
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -96,25 +67,11 @@ static void notify_one(struct onoff_service *srv,
|
|||
struct onoff_client *cli,
|
||||
int res)
|
||||
{
|
||||
unsigned int flags = cli->flags;
|
||||
onoff_client_callback cb =
|
||||
(onoff_client_callback)sys_notify_finalize(&cli->notify, res);
|
||||
|
||||
/* 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);
|
||||
if (cb) {
|
||||
cb(srv, cli, cli->user_data, res);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
8
tests/lib/notify/CMakeLists.txt
Normal file
8
tests/lib/notify/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(sys_notify)
|
||||
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app PRIVATE ${app_sources})
|
2
tests/lib/notify/prj.conf
Normal file
2
tests/lib/notify/prj.conf
Normal file
|
@ -0,0 +1,2 @@
|
|||
CONFIG_POLL=y
|
||||
CONFIG_ZTEST=y
|
239
tests/lib/notify/src/main.c
Normal file
239
tests/lib/notify/src/main.c
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <ztest.h>
|
||||
#include <sys/notify.h>
|
||||
|
||||
static u32_t get_extflags(const struct sys_notify *anp)
|
||||
{
|
||||
u32_t flags = anp->flags & SYS_NOTIFY_EXTENSION_MASK;
|
||||
|
||||
return flags >> SYS_NOTIFY_EXTENSION_POS;
|
||||
}
|
||||
|
||||
static void set_extflags(struct sys_notify *anp,
|
||||
u32_t flags)
|
||||
{
|
||||
anp->flags = (anp->flags & ~SYS_NOTIFY_EXTENSION_MASK)
|
||||
| (flags << SYS_NOTIFY_EXTENSION_POS);
|
||||
}
|
||||
|
||||
static void callback(struct sys_notify *anp,
|
||||
int *resp)
|
||||
{
|
||||
zassert_equal(sys_notify_fetch_result(anp, resp), 0,
|
||||
"failed callback fetch");
|
||||
}
|
||||
|
||||
static void test_validate(void)
|
||||
{
|
||||
struct sys_notify notify = {
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
zassert_equal(sys_notify_validate(NULL), -EINVAL,
|
||||
"accepted null pointer");
|
||||
zassert_equal(sys_notify_validate(¬ify), -EINVAL,
|
||||
"accepted bad method");
|
||||
}
|
||||
|
||||
|
||||
static void test_spinwait(void)
|
||||
{
|
||||
int rc;
|
||||
int set_res = 423;
|
||||
int res;
|
||||
sys_notify_generic_callback cb;
|
||||
struct sys_notify notify;
|
||||
u32_t xflags = 0x1234;
|
||||
|
||||
memset(¬ify, 0xac, sizeof(notify));
|
||||
rc = sys_notify_validate(¬ify);
|
||||
zassert_equal(rc, -EINVAL,
|
||||
"invalid not diagnosed");
|
||||
|
||||
sys_notify_init_spinwait(¬ify);
|
||||
rc = sys_notify_validate(¬ify);
|
||||
zassert_equal(rc, 0,
|
||||
"init_spinwait invalid");
|
||||
|
||||
zassert_false(sys_notify_uses_callback(¬ify),
|
||||
"uses callback");
|
||||
|
||||
zassert_equal(notify.flags, SYS_NOTIFY_METHOD_SPINWAIT,
|
||||
"flags mismatch");
|
||||
|
||||
set_extflags(¬ify, xflags);
|
||||
zassert_equal(sys_notify_get_method(¬ify),
|
||||
SYS_NOTIFY_METHOD_SPINWAIT,
|
||||
"method corrupted");
|
||||
zassert_equal(get_extflags(¬ify), xflags,
|
||||
"xflags extract failed");
|
||||
|
||||
rc = sys_notify_fetch_result(¬ify, &res);
|
||||
zassert_equal(rc, -EAGAIN,
|
||||
"spinwait ready too soon");
|
||||
|
||||
zassert_not_equal(notify.flags, 0,
|
||||
"flags cleared");
|
||||
|
||||
cb = sys_notify_finalize(¬ify, set_res);
|
||||
zassert_equal(cb, (sys_notify_generic_callback)NULL,
|
||||
"callback not null");
|
||||
zassert_equal(notify.flags, 0,
|
||||
"flags not cleared");
|
||||
|
||||
rc = sys_notify_fetch_result(¬ify, &res);
|
||||
zassert_equal(rc, 0,
|
||||
"spinwait not ready");
|
||||
zassert_equal(res, set_res,
|
||||
"result not set");
|
||||
}
|
||||
|
||||
static void test_signal(void)
|
||||
{
|
||||
#ifdef CONFIG_POLL
|
||||
int rc;
|
||||
int set_res = 423;
|
||||
int res;
|
||||
struct k_poll_signal sig;
|
||||
sys_notify_generic_callback cb;
|
||||
struct sys_notify notify;
|
||||
u32_t xflags = 0x1234;
|
||||
|
||||
memset(¬ify, 0xac, sizeof(notify));
|
||||
rc = sys_notify_validate(¬ify);
|
||||
zassert_equal(rc, -EINVAL,
|
||||
"invalid not diagnosed");
|
||||
|
||||
k_poll_signal_init(&sig);
|
||||
k_poll_signal_check(&sig, &rc, &res);
|
||||
zassert_equal(rc, 0,
|
||||
"signal set");
|
||||
|
||||
sys_notify_init_signal(¬ify, &sig);
|
||||
notify.method.signal = NULL;
|
||||
rc = sys_notify_validate(¬ify);
|
||||
zassert_equal(rc, -EINVAL,
|
||||
"null signal not invalid");
|
||||
|
||||
memset(¬ify, 0xac, sizeof(notify));
|
||||
sys_notify_init_signal(¬ify, &sig);
|
||||
rc = sys_notify_validate(¬ify);
|
||||
zassert_equal(rc, 0,
|
||||
"init_spinwait invalid");
|
||||
|
||||
zassert_false(sys_notify_uses_callback(¬ify),
|
||||
"uses callback");
|
||||
|
||||
zassert_equal(notify.flags, SYS_NOTIFY_METHOD_SIGNAL,
|
||||
"flags mismatch");
|
||||
zassert_equal(notify.method.signal, &sig,
|
||||
"signal pointer mismatch");
|
||||
|
||||
set_extflags(¬ify, xflags);
|
||||
zassert_equal(sys_notify_get_method(¬ify),
|
||||
SYS_NOTIFY_METHOD_SIGNAL,
|
||||
"method corrupted");
|
||||
zassert_equal(get_extflags(¬ify), xflags,
|
||||
"xflags extract failed");
|
||||
|
||||
rc = sys_notify_fetch_result(¬ify, &res);
|
||||
zassert_equal(rc, -EAGAIN,
|
||||
"spinwait ready too soon");
|
||||
|
||||
zassert_not_equal(notify.flags, 0,
|
||||
"flags cleared");
|
||||
|
||||
cb = sys_notify_finalize(¬ify, set_res);
|
||||
zassert_equal(cb, (sys_notify_generic_callback)NULL,
|
||||
"callback not null");
|
||||
zassert_equal(notify.flags, 0,
|
||||
"flags not cleared");
|
||||
k_poll_signal_check(&sig, &rc, &res);
|
||||
zassert_equal(rc, 1,
|
||||
"signal not set");
|
||||
zassert_equal(res, set_res,
|
||||
"signal result wrong");
|
||||
|
||||
rc = sys_notify_fetch_result(¬ify, &res);
|
||||
zassert_equal(rc, 0,
|
||||
"signal not ready");
|
||||
zassert_equal(res, set_res,
|
||||
"result not set");
|
||||
#endif /* CONFIG_POLL */
|
||||
}
|
||||
|
||||
static void test_callback(void)
|
||||
{
|
||||
int rc;
|
||||
int set_res = 423;
|
||||
int res;
|
||||
sys_notify_generic_callback cb;
|
||||
struct sys_notify notify;
|
||||
u32_t xflags = 0x8765432;
|
||||
|
||||
memset(¬ify, 0xac, sizeof(notify));
|
||||
rc = sys_notify_validate(¬ify);
|
||||
zassert_equal(rc, -EINVAL,
|
||||
"invalid not diagnosed");
|
||||
|
||||
sys_notify_init_callback(¬ify, callback);
|
||||
notify.method.callback = NULL;
|
||||
rc = sys_notify_validate(¬ify);
|
||||
zassert_equal(rc, -EINVAL,
|
||||
"null callback not invalid");
|
||||
|
||||
memset(¬ify, 0xac, sizeof(notify));
|
||||
sys_notify_init_callback(¬ify, callback);
|
||||
rc = sys_notify_validate(¬ify);
|
||||
zassert_equal(rc, 0,
|
||||
"init_spinwait invalid");
|
||||
|
||||
zassert_true(sys_notify_uses_callback(¬ify),
|
||||
"not using callback");
|
||||
|
||||
zassert_equal(notify.flags, SYS_NOTIFY_METHOD_CALLBACK,
|
||||
"flags mismatch");
|
||||
zassert_equal(notify.method.callback,
|
||||
(sys_notify_generic_callback)callback,
|
||||
"callback mismatch");
|
||||
|
||||
set_extflags(¬ify, xflags);
|
||||
zassert_equal(sys_notify_get_method(¬ify),
|
||||
SYS_NOTIFY_METHOD_CALLBACK,
|
||||
"method corrupted");
|
||||
zassert_equal(get_extflags(¬ify), xflags,
|
||||
"xflags extract failed");
|
||||
|
||||
rc = sys_notify_fetch_result(¬ify, &res);
|
||||
zassert_equal(rc, -EAGAIN,
|
||||
"callback ready too soon");
|
||||
|
||||
zassert_not_equal(notify.flags, 0,
|
||||
"flags cleared");
|
||||
|
||||
cb = sys_notify_finalize(¬ify, set_res);
|
||||
zassert_equal(cb, (sys_notify_generic_callback)callback,
|
||||
"callback wrong");
|
||||
zassert_equal(notify.flags, 0,
|
||||
"flags not cleared");
|
||||
|
||||
res = ~set_res;
|
||||
((sys_notify_generic_callback)cb)(¬ify, &res);
|
||||
zassert_equal(res, set_res,
|
||||
"result not set");
|
||||
}
|
||||
|
||||
void test_main(void)
|
||||
{
|
||||
ztest_test_suite(sys_notify_api,
|
||||
ztest_unit_test(test_validate),
|
||||
ztest_unit_test(test_spinwait),
|
||||
ztest_unit_test(test_signal),
|
||||
ztest_unit_test(test_callback));
|
||||
ztest_run_test_suite(sys_notify_api);
|
||||
}
|
3
tests/lib/notify/testcase.yaml
Normal file
3
tests/lib/notify/testcase.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
tests:
|
||||
libraries.data_structures:
|
||||
tags: notify
|
|
@ -233,7 +233,7 @@ static void test_client_init_validation(void)
|
|||
onoff_client_init_spinwait(&cli);
|
||||
zassert_equal(z_snode_next_peek(&cli.node), NULL,
|
||||
"cli node mismatch");
|
||||
zassert_equal(cli.flags, ONOFF_CLIENT_NOTIFY_SPINWAIT,
|
||||
zassert_equal(cli.notify.flags, SYS_NOTIFY_METHOD_SPINWAIT,
|
||||
"cli spinwait flags");
|
||||
|
||||
struct k_poll_signal sig;
|
||||
|
@ -242,20 +242,20 @@ static void test_client_init_validation(void)
|
|||
onoff_client_init_signal(&cli, &sig);
|
||||
zassert_equal(z_snode_next_peek(&cli.node), NULL,
|
||||
"cli signal node");
|
||||
zassert_equal(cli.flags, ONOFF_CLIENT_NOTIFY_SIGNAL,
|
||||
zassert_equal(cli.notify.flags, SYS_NOTIFY_METHOD_SIGNAL,
|
||||
"cli signal flags");
|
||||
zassert_equal(cli.async.signal, &sig,
|
||||
zassert_equal(cli.notify.method.signal, &sig,
|
||||
"cli signal async");
|
||||
|
||||
memset(&cli, 0xA5, sizeof(cli));
|
||||
onoff_client_init_callback(&cli, callback, &sig);
|
||||
zassert_equal(z_snode_next_peek(&cli.node), NULL,
|
||||
"cli callback node");
|
||||
zassert_equal(cli.flags, ONOFF_CLIENT_NOTIFY_CALLBACK,
|
||||
zassert_equal(cli.notify.flags, SYS_NOTIFY_METHOD_CALLBACK,
|
||||
"cli callback flags");
|
||||
zassert_equal(cli.async.callback.handler, callback,
|
||||
zassert_equal(cli.notify.method.callback, callback,
|
||||
"cli callback handler");
|
||||
zassert_equal(cli.async.callback.user_data, &sig,
|
||||
zassert_equal(cli.user_data, &sig,
|
||||
"cli callback user_data");
|
||||
}
|
||||
|
||||
|
@ -305,7 +305,7 @@ static void test_validate_args(void)
|
|||
"validate req cli flags");
|
||||
|
||||
init_spinwait(&cli);
|
||||
cli.flags = ONOFF_CLIENT_NOTIFY_INVALID;
|
||||
cli.notify.flags = SYS_NOTIFY_METHOD_COMPLETED;
|
||||
rc = onoff_request(&srv, &cli);
|
||||
zassert_equal(rc, -EINVAL,
|
||||
"validate req cli mode");
|
||||
|
@ -315,7 +315,7 @@ static void test_validate_args(void)
|
|||
zassert_equal(rc, 0,
|
||||
"validate req cli signal: %d", rc);
|
||||
init_notify_sig(&cli, &sig);
|
||||
cli.async.signal = NULL;
|
||||
cli.notify.method.signal = NULL;
|
||||
rc = onoff_request(&srv, &cli);
|
||||
zassert_equal(rc, -EINVAL,
|
||||
"validate req cli signal null");
|
||||
|
@ -326,7 +326,7 @@ static void test_validate_args(void)
|
|||
"validate req cli callback");
|
||||
|
||||
init_notify_cb(&cli);
|
||||
cli.async.callback.handler = NULL;
|
||||
cli.notify.method.callback = NULL;
|
||||
rc = onoff_request(&srv, &cli);
|
||||
zassert_equal(rc, -EINVAL,
|
||||
"validate req cli callback null");
|
||||
|
@ -822,7 +822,7 @@ static void test_async(void)
|
|||
"rel to-off: %d", rc);
|
||||
|
||||
/* Finalize queued start, gets us to on */
|
||||
cli[0].result = 1 + start_state.retval;
|
||||
cli[0].notify.result = 1 + start_state.retval;
|
||||
zassert_equal(cli_result(&cli[0]), -EAGAIN,
|
||||
"fetch failed");
|
||||
zassert_false(start_state.notify == NULL,
|
||||
|
|
Loading…
Reference in a new issue