360 lines
11 KiB
C
360 lines
11 KiB
C
/*
|
|
* 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 0x03U
|
|
#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().
|
|
*/
|
|
uint32_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 uint32_t sys_notify_get_method(const struct sys_notify *notify)
|
|
{
|
|
uint32_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
|
|
* This capability is available only when @option{CONFIG_POLL} is
|
|
* selected.
|
|
*
|
|
* @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_ */
|