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