1 /*
2 * Copyright (c) 2019 Peter Bigot Consulting, LLC
3 * Copyright (c) 2020 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #ifndef ZEPHYR_INCLUDE_SYS_NOTIFY_H_
9 #define ZEPHYR_INCLUDE_SYS_NOTIFY_H_
10
11 #include <zephyr/kernel.h>
12 #include <zephyr/types.h>
13
14 #ifdef __cplusplus
15 extern "C" {
16 #endif
17
18 struct sys_notify;
19
20 /*
21 * Flag value that overwrites the method field when the operation has
22 * completed.
23 */
24 #define SYS_NOTIFY_METHOD_COMPLETED 0
25
26 /*
27 * Indicates that no notification will be provided.
28 *
29 * Callers must check for completions using
30 * sys_notify_fetch_result().
31 *
32 * See sys_notify_init_spinwait().
33 */
34 #define SYS_NOTIFY_METHOD_SPINWAIT 1
35
36 /*
37 * Select notification through @ref k_poll signal
38 *
39 * See sys_notify_init_signal().
40 */
41 #define SYS_NOTIFY_METHOD_SIGNAL 2
42
43 /*
44 * Select notification through a user-provided callback.
45 *
46 * See sys_notify_init_callback().
47 */
48 #define SYS_NOTIFY_METHOD_CALLBACK 3
49
50 #define SYS_NOTIFY_METHOD_MASK 0x03U
51 #define SYS_NOTIFY_METHOD_POS 0
52
53 /**
54 * @brief Identify the region of sys_notify flags available for
55 * containing services.
56 *
57 * Bits of the flags field of the sys_notify structure at and above
58 * this position may be used by extensions to the sys_notify
59 * structure.
60 *
61 * These bits are intended for use by containing service
62 * implementations to record client-specific information. The bits
63 * are cleared by sys_notify_validate(). Use of these does not
64 * imply that the flags field becomes public API.
65 */
66 #define SYS_NOTIFY_EXTENSION_POS 2
67
68 /*
69 * Mask isolating the bits of sys_notify::flags that are available
70 * for extension.
71 */
72 #define SYS_NOTIFY_EXTENSION_MASK (~BIT_MASK(SYS_NOTIFY_EXTENSION_POS))
73
74 /**
75 * @defgroup sys_notify_apis Asynchronous Notification APIs
76 * @ingroup kernel_apis
77 * @{
78 */
79
80 /**
81 * @brief Generic signature used to notify of result completion by
82 * callback.
83 *
84 * Functions with this role may be invoked from any context including
85 * pre-kernel, ISR, or cooperative or pre-emptible threads.
86 * Compatible functions must be isr-ok and not sleep.
87 *
88 * Parameters that should generally be passed to such functions include:
89 *
90 * * a pointer to a specific client request structure, i.e. the one
91 * that contains the sys_notify structure.
92 * * the result of the operation, either as passed to
93 * sys_notify_finalize() or extracted afterwards using
94 * sys_notify_fetch_result(). Expected values are
95 * service-specific, but the value shall be non-negative if the
96 * operation succeeded, and negative if the operation failed.
97 */
98 typedef void (*sys_notify_generic_callback)();
99
100 /**
101 * @brief State associated with notification for an asynchronous
102 * operation.
103 *
104 * Objects of this type are allocated by a client, which must use an
105 * initialization function (e.g. sys_notify_init_signal()) to
106 * configure them. Generally the structure is a member of a
107 * service-specific client structure, such as onoff_client.
108 *
109 * Control of the containing object transfers to the service provider
110 * when a pointer to the object is passed to a service function that
111 * is documented to take control of the object, such as
112 * onoff_service_request(). While the service provider controls the
113 * object the client must not change any object fields. Control
114 * reverts to the client:
115 * * if the call to the service API returns an error;
116 * * when operation completion is posted. This may occur before the
117 * call to the service API returns.
118 *
119 * Operation completion is technically posted when the flags field is
120 * updated so that sys_notify_fetch_result() returns success. This
121 * will happen before the signal is posted or callback is invoked.
122 * Note that although the manager will no longer reference the
123 * sys_notify object past this point, the containing object may have
124 * state that will be referenced within the callback. Where callbacks
125 * are used control of the containing object does not revert to the
126 * client until the callback has been invoked. (Re-use within the
127 * callback is explicitly permitted.)
128 *
129 * After control has reverted to the client the notify object must be
130 * reinitialized for the next operation.
131 *
132 * The content of this structure is not public API to clients: all
133 * configuration and inspection should be done with functions like
134 * sys_notify_init_callback() and sys_notify_fetch_result().
135 * However, services that use this structure may access certain
136 * fields directly.
137 */
138 struct sys_notify {
139 union method {
140 /* Pointer to signal used to notify client.
141 *
142 * The signal value corresponds to the res parameter
143 * of sys_notify_callback.
144 */
145 struct k_poll_signal *signal;
146
147 /* Generic callback function for callback notification. */
148 sys_notify_generic_callback callback;
149 } method;
150
151 /*
152 * Flags recording information about the operation.
153 *
154 * Bits below SYS_NOTIFY_EXTENSION_POS are initialized by
155 * async notify API init functions like
156 * sys_notify_init_callback(), and must not by modified by
157 * extensions or client code.
158 *
159 * Bits at and above SYS_NOTIFY_EXTENSION_POS are available
160 * for use by service extensions while the containing object
161 * is managed by the service. They are not for client use,
162 * are zeroed by the async notify API init functions, and will
163 * be zeroed by sys_notify_finalize().
164 */
165 uint32_t volatile flags;
166
167 /*
168 * The result of the operation.
169 *
170 * This is the value that was (or would be) passed to the
171 * async infrastructure. This field is the sole record of
172 * success or failure for spin-wait synchronous operations.
173 */
174 int volatile result;
175 };
176
177 /** @internal */
sys_notify_get_method(const struct sys_notify * notify)178 static inline uint32_t sys_notify_get_method(const struct sys_notify *notify)
179 {
180 uint32_t method = notify->flags >> SYS_NOTIFY_METHOD_POS;
181
182 return method & SYS_NOTIFY_METHOD_MASK;
183 }
184
185 /**
186 * @brief Validate and initialize the notify structure.
187 *
188 * This should be invoked at the start of any service-specific
189 * configuration validation. It ensures that the basic asynchronous
190 * notification configuration is consistent, and clears the result.
191 *
192 * Note that this function does not validate extension bits (zeroed by
193 * async notify API init functions like sys_notify_init_callback()).
194 * It may fail to recognize that an uninitialized structure has been
195 * passed because only method bits of flags are tested against method
196 * settings. To reduce the chance of accepting an uninitialized
197 * operation service validation of structures that contain an
198 * sys_notify instance should confirm that the extension bits are
199 * set or cleared as expected.
200 *
201 * @retval 0 on successful validation and reinitialization
202 * @retval -EINVAL if the configuration is not valid.
203 */
204 int sys_notify_validate(struct sys_notify *notify);
205
206 /**
207 * @brief Record and signal the operation completion.
208 *
209 * @param notify pointer to the notification state structure.
210 *
211 * @param res the result of the operation. Expected values are
212 * service-specific, but the value shall be non-negative if the
213 * operation succeeded, and negative if the operation failed.
214 *
215 * @return If the notification is to be done by callback this returns
216 * the generic version of the function to be invoked. The caller must
217 * immediately invoke that function with whatever arguments are
218 * expected by the callback. If notification is by spin-wait or
219 * signal, the notification has been completed by the point this
220 * function returns, and a null pointer is returned.
221 */
222 sys_notify_generic_callback sys_notify_finalize(struct sys_notify *notify,
223 int res);
224
225 /**
226 * @brief Check for and read the result of an asynchronous operation.
227 *
228 * @param notify pointer to the object used to specify asynchronous
229 * function behavior and store completion information.
230 *
231 * @param result pointer to storage for the result of the operation.
232 * The result is stored only if the operation has completed.
233 *
234 * @retval 0 if the operation has completed.
235 * @retval -EAGAIN if the operation has not completed.
236 */
sys_notify_fetch_result(const struct sys_notify * notify,int * result)237 static inline int sys_notify_fetch_result(const struct sys_notify *notify,
238 int *result)
239 {
240 __ASSERT_NO_MSG(notify != NULL);
241 __ASSERT_NO_MSG(result != NULL);
242 int rv = -EAGAIN;
243
244 if (sys_notify_get_method(notify) == SYS_NOTIFY_METHOD_COMPLETED) {
245 rv = 0;
246 *result = notify->result;
247 }
248
249 return rv;
250 }
251
252 /**
253 * @brief Initialize a notify object for spin-wait notification.
254 *
255 * Clients that use this initialization receive no asynchronous
256 * notification, and instead must periodically check for completion
257 * using sys_notify_fetch_result().
258 *
259 * On completion of the operation the client object must be
260 * reinitialized before it can be re-used.
261 *
262 * @param notify pointer to the notification configuration object.
263 */
sys_notify_init_spinwait(struct sys_notify * notify)264 static inline void sys_notify_init_spinwait(struct sys_notify *notify)
265 {
266 __ASSERT_NO_MSG(notify != NULL);
267
268 *notify = (struct sys_notify){
269 .flags = SYS_NOTIFY_METHOD_SPINWAIT,
270 };
271 }
272
273 /**
274 * @brief Initialize a notify object for (k_poll) signal notification.
275 *
276 * Clients that use this initialization will be notified of the
277 * completion of operations through the provided signal.
278 *
279 * On completion of the operation the client object must be
280 * reinitialized before it can be re-used.
281 *
282 * @note
283 * This capability is available only when @kconfig{CONFIG_POLL} is
284 * selected.
285 *
286 * @param notify pointer to the notification configuration object.
287 *
288 * @param sigp pointer to the signal to use for notification. The
289 * value must not be null. The signal must be reset before the client
290 * object is passed to the on-off service API.
291 */
sys_notify_init_signal(struct sys_notify * notify,struct k_poll_signal * sigp)292 static inline void sys_notify_init_signal(struct sys_notify *notify,
293 struct k_poll_signal *sigp)
294 {
295 __ASSERT_NO_MSG(notify != NULL);
296 __ASSERT_NO_MSG(sigp != NULL);
297
298 *notify = (struct sys_notify){
299 .method = {
300 .signal = sigp,
301 },
302 .flags = SYS_NOTIFY_METHOD_SIGNAL,
303 };
304 }
305
306 /**
307 * @brief Initialize a notify object for callback notification.
308 *
309 * Clients that use this initialization will be notified of the
310 * completion of operations through the provided callback. Note that
311 * callbacks may be invoked from various contexts depending on the
312 * specific service; see @ref sys_notify_generic_callback.
313 *
314 * On completion of the operation the client object must be
315 * reinitialized before it can be re-used.
316 *
317 * @param notify pointer to the notification configuration object.
318 *
319 * @param handler a function pointer to use for notification.
320 */
sys_notify_init_callback(struct sys_notify * notify,sys_notify_generic_callback handler)321 static inline void sys_notify_init_callback(struct sys_notify *notify,
322 sys_notify_generic_callback handler)
323 {
324 __ASSERT_NO_MSG(notify != NULL);
325 __ASSERT_NO_MSG(handler != NULL);
326
327 *notify = (struct sys_notify){
328 .method = {
329 .callback = handler,
330 },
331 .flags = SYS_NOTIFY_METHOD_CALLBACK,
332 };
333 }
334
335 /**
336 * @brief Detect whether a particular notification uses a callback.
337 *
338 * The generic handler does not capture the signature expected by the
339 * callback, and the translation to a service-specific callback must
340 * be provided by the service. This check allows abstracted services
341 * to reject callback notification requests when the service doesn't
342 * provide a translation function.
343 *
344 * @return true if and only if a callback is to be used for notification.
345 */
sys_notify_uses_callback(const struct sys_notify * notify)346 static inline bool sys_notify_uses_callback(const struct sys_notify *notify)
347 {
348 __ASSERT_NO_MSG(notify != NULL);
349
350 return sys_notify_get_method(notify) == SYS_NOTIFY_METHOD_CALLBACK;
351 }
352
353 /** @} */
354
355 #ifdef __cplusplus
356 }
357 #endif
358
359 #endif /* ZEPHYR_INCLUDE_SYS_NOTIFY_H_ */
360