Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 | /*
* 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().
*/
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_ */
|