Line data Source code
1 0 : /*
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_ONOFF_H_
9 : #define ZEPHYR_INCLUDE_SYS_ONOFF_H_
10 :
11 : #include <zephyr/kernel.h>
12 : #include <zephyr/types.h>
13 : #include <zephyr/sys/notify.h>
14 :
15 : #ifdef __cplusplus
16 : extern "C" {
17 : #endif
18 :
19 : /**
20 : * @defgroup resource_mgmt_onoff_apis On-Off Service APIs
21 : * @ingroup kernel_apis
22 : * @{
23 : */
24 :
25 : /**
26 : * @brief Flag indicating an error state.
27 : *
28 : * Error states are cleared using onoff_reset().
29 : */
30 1 : #define ONOFF_FLAG_ERROR BIT(0)
31 :
32 : /** @cond INTERNAL_HIDDEN */
33 : #define ONOFF_FLAG_ONOFF BIT(1)
34 : #define ONOFF_FLAG_TRANSITION BIT(2)
35 : /** @endcond */
36 :
37 : /**
38 : * @brief Mask used to isolate bits defining the service state.
39 : *
40 : * Mask a value with this then test for ONOFF_FLAG_ERROR to determine
41 : * whether the machine has an unfixed error, or compare against
42 : * ONOFF_STATE_ON, ONOFF_STATE_OFF, ONOFF_STATE_TO_ON,
43 : * ONOFF_STATE_TO_OFF, or ONOFF_STATE_RESETTING.
44 : */
45 1 : #define ONOFF_STATE_MASK (ONOFF_FLAG_ERROR \
46 : | ONOFF_FLAG_ONOFF \
47 : | ONOFF_FLAG_TRANSITION)
48 :
49 : /**
50 : * @brief Value exposed by ONOFF_STATE_MASK when service is off.
51 : */
52 1 : #define ONOFF_STATE_OFF 0U
53 :
54 : /**
55 : * @brief Value exposed by ONOFF_STATE_MASK when service is on.
56 : */
57 1 : #define ONOFF_STATE_ON ONOFF_FLAG_ONOFF
58 :
59 : /**
60 : * @brief Value exposed by ONOFF_STATE_MASK when the service is in an
61 : * error state (and not in the process of resetting its state).
62 : */
63 1 : #define ONOFF_STATE_ERROR ONOFF_FLAG_ERROR
64 :
65 : /**
66 : * @brief Value exposed by ONOFF_STATE_MASK when service is
67 : * transitioning to on.
68 : */
69 1 : #define ONOFF_STATE_TO_ON (ONOFF_FLAG_TRANSITION | ONOFF_STATE_ON)
70 :
71 : /**
72 : * @brief Value exposed by ONOFF_STATE_MASK when service is
73 : * transitioning to off.
74 : */
75 1 : #define ONOFF_STATE_TO_OFF (ONOFF_FLAG_TRANSITION | ONOFF_STATE_OFF)
76 :
77 : /**
78 : * @brief Value exposed by ONOFF_STATE_MASK when service is in the
79 : * process of resetting.
80 : */
81 1 : #define ONOFF_STATE_RESETTING (ONOFF_FLAG_TRANSITION | ONOFF_STATE_ERROR)
82 :
83 : /* Forward declarations */
84 : struct onoff_manager;
85 : struct onoff_monitor;
86 :
87 : /**
88 : * @brief Signature used to notify an on-off manager that a transition
89 : * has completed.
90 : *
91 : * Functions of this type are passed to service-specific transition
92 : * functions to be used to report the completion of the operation.
93 : * The functions may be invoked from any context.
94 : *
95 : * @param mgr the manager for which transition was requested.
96 : *
97 : * @param res the result of the transition. This shall be
98 : * non-negative on success, or a negative error code. If an error is
99 : * indicated the service shall enter an error state.
100 : */
101 1 : typedef void (*onoff_notify_fn)(struct onoff_manager *mgr,
102 : int res);
103 :
104 : /**
105 : * @brief Signature used by service implementations to effect a
106 : * transition.
107 : *
108 : * Service definitions use two required function pointers of this type
109 : * to be notified that a transition is required, and a third optional
110 : * one to reset the service when it is in an error state.
111 : *
112 : * The start function will be called only from the off state.
113 : *
114 : * The stop function will be called only from the on state.
115 : *
116 : * The reset function (where supported) will be called only when
117 : * onoff_has_error() returns true.
118 : *
119 : * @note All transitions functions must be isr-ok.
120 : *
121 : * @param mgr the manager for which transition was requested.
122 : *
123 : * @param notify the function to be invoked when the transition has
124 : * completed. If the transition is synchronous, notify shall be
125 : * invoked by the implementation before the transition function
126 : * returns. Otherwise the implementation shall capture this parameter
127 : * and invoke it when the transition completes.
128 : */
129 1 : typedef void (*onoff_transition_fn)(struct onoff_manager *mgr,
130 : onoff_notify_fn notify);
131 :
132 : /** @brief On-off service transition functions. */
133 1 : struct onoff_transitions {
134 : /** Function to invoke to transition the service to on. */
135 1 : onoff_transition_fn start;
136 :
137 : /** Function to invoke to transition the service to off. */
138 1 : onoff_transition_fn stop;
139 :
140 : /** Function to force the service state to reset, where
141 : * supported.
142 : */
143 1 : onoff_transition_fn reset;
144 : };
145 :
146 : /**
147 : * @brief State associated with an on-off manager.
148 : *
149 : * No fields in this structure are intended for use by service
150 : * providers or clients. The state is to be initialized once, using
151 : * onoff_manager_init(), when the service provider is initialized. In
152 : * case of error it may be reset through the onoff_reset() API.
153 : */
154 1 : struct onoff_manager {
155 : /** List of clients waiting for request or reset completion
156 : * notifications.
157 : */
158 1 : sys_slist_t clients;
159 :
160 : /** List of monitors to be notified of state changes including
161 : * errors and transition completion.
162 : */
163 1 : sys_slist_t monitors;
164 :
165 : /** Transition functions. */
166 1 : const struct onoff_transitions *transitions;
167 :
168 : /** Mutex protection for other fields. */
169 1 : struct k_spinlock lock;
170 :
171 : /** The result of the last transition. */
172 1 : int last_res;
173 :
174 : /** Flags identifying the service state. */
175 1 : uint16_t flags;
176 :
177 : /** Number of active clients for the service. */
178 1 : uint16_t refs;
179 : };
180 :
181 : /** @brief Initializer for a onoff_transitions object.
182 : *
183 : * @param _start a function used to transition from off to on state.
184 : *
185 : * @param _stop a function used to transition from on to off state.
186 : *
187 : * @param _reset a function used to clear errors and force the service
188 : * to an off state. Can be null.
189 : */
190 1 : #define ONOFF_TRANSITIONS_INITIALIZER(_start, _stop, _reset) { \
191 : .start = (_start), \
192 : .stop = (_stop), \
193 : .reset = (_reset), \
194 : }
195 :
196 : /** @cond INTERNAL_HIDDEN */
197 : #define ONOFF_MANAGER_INITIALIZER(_transitions) { \
198 : .transitions = (_transitions), \
199 : }
200 : /** @endcond */
201 :
202 : /**
203 : * @brief Initialize an on-off service to off state.
204 : *
205 : * This function must be invoked exactly once per service instance, by
206 : * the infrastructure that provides the service, and before any other
207 : * on-off service API is invoked on the service.
208 : *
209 : * This function should never be invoked by clients of an on-off
210 : * service.
211 : *
212 : * @param mgr the manager definition object to be initialized.
213 : *
214 : * @param transitions pointer to a structure providing transition
215 : * functions. The referenced object must persist as long as the
216 : * manager can be referenced.
217 : *
218 : * @retval 0 on success
219 : * @retval -EINVAL if start, stop, or flags are invalid
220 : */
221 1 : int onoff_manager_init(struct onoff_manager *mgr,
222 : const struct onoff_transitions *transitions);
223 :
224 : /* Forward declaration */
225 : struct onoff_client;
226 :
227 : /**
228 : * @brief Signature used to notify an on-off service client of the
229 : * completion of an operation.
230 : *
231 : * These functions may be invoked from any context including
232 : * pre-kernel, ISR, or cooperative or pre-emptible threads.
233 : * Compatible functions must be isr-ok and not sleep.
234 : *
235 : * @param mgr the manager for which the operation was initiated. This may be
236 : * null if the on-off service uses synchronous transitions.
237 : *
238 : * @param cli the client structure passed to the function that
239 : * initiated the operation.
240 : *
241 : * @param state the state of the machine at the time of completion,
242 : * restricted by ONOFF_STATE_MASK. ONOFF_FLAG_ERROR must be checked
243 : * independently of whether res is negative as a machine error may
244 : * indicate that all future operations except onoff_reset() will fail.
245 : *
246 : * @param res the result of the operation. Expected values are
247 : * service-specific, but the value shall be non-negative if the
248 : * operation succeeded, and negative if the operation failed. If res
249 : * is negative ONOFF_FLAG_ERROR will be set in state, but if res is
250 : * non-negative ONOFF_FLAG_ERROR may still be set in state.
251 : */
252 1 : typedef void (*onoff_client_callback)(struct onoff_manager *mgr,
253 : struct onoff_client *cli,
254 : uint32_t state,
255 : int res);
256 :
257 : /**
258 : * @brief State associated with a client of an on-off service.
259 : *
260 : * Objects of this type are allocated by a client, which is
261 : * responsible for zero-initializing the node field and invoking the
262 : * appropriate sys_notify init function to configure notification.
263 : *
264 : * Control of the object content transfers to the service provider
265 : * when a pointer to the object is passed to any on-off manager
266 : * function. While the service provider controls the object the
267 : * client must not change any object fields. Control reverts to the
268 : * client concurrent with release of the owned sys_notify structure,
269 : * or when indicated by an onoff_cancel() return value.
270 : *
271 : * After control has reverted to the client the notify field must be
272 : * reinitialized for the next operation.
273 : */
274 1 : struct onoff_client {
275 : /** @cond INTERNAL_HIDDEN
276 : *
277 : * Links the client into the set of waiting service users.
278 : * Applications must ensure this field is zero-initialized
279 : * before use.
280 : */
281 : sys_snode_t node;
282 : /** @endcond */
283 :
284 : /** @brief Notification configuration. */
285 1 : struct sys_notify notify;
286 : };
287 :
288 : /**
289 : * @brief Identify region of sys_notify flags available for
290 : * containing services.
291 : *
292 : * Bits of the flags field of the sys_notify structure contained
293 : * within the queued_operation structure at and above this position
294 : * may be used by extensions to the onoff_client structure.
295 : *
296 : * These bits are intended for use by containing service
297 : * implementations to record client-specific information and are
298 : * subject to other conditions of use specified on the sys_notify API.
299 : */
300 1 : #define ONOFF_CLIENT_EXTENSION_POS SYS_NOTIFY_EXTENSION_POS
301 :
302 : /**
303 : * @brief Test whether an on-off service has recorded an error.
304 : *
305 : * This function can be used to determine whether the service has
306 : * recorded an error. Errors may be cleared by invoking
307 : * onoff_reset().
308 : *
309 : * This is an unlocked convenience function suitable for use only when
310 : * it is known that no other process might invoke an operation that
311 : * transitions the service between an error and non-error state.
312 : *
313 : * @return true if and only if the service has an uncleared error.
314 : */
315 1 : static inline bool onoff_has_error(const struct onoff_manager *mgr)
316 : {
317 : return (mgr->flags & ONOFF_FLAG_ERROR) != 0;
318 : }
319 :
320 : /**
321 : * @brief Request a reservation to use an on-off service.
322 : *
323 : * The return value indicates the success or failure of an attempt to
324 : * initiate an operation to request the resource be made available.
325 : * If initiation of the operation succeeds the result of the request
326 : * operation is provided through the configured client notification
327 : * method, possibly before this call returns.
328 : *
329 : * Note that the call to this function may succeed in a case where the
330 : * actual request fails. Always check the operation completion
331 : * result.
332 : *
333 : * @param mgr the manager that will be used.
334 : *
335 : * @param cli a non-null pointer to client state providing
336 : * instructions on synchronous expectations and how to notify the
337 : * client when the request completes. Behavior is undefined if client
338 : * passes a pointer object associated with an incomplete service
339 : * operation.
340 : *
341 : * @retval non-negative the observed state of the machine at the time
342 : * the request was processed, if successful.
343 : * @retval -EIO if service has recorded an error.
344 : * @retval -EINVAL if the parameters are invalid.
345 : * @retval -EAGAIN if the reference count would overflow.
346 : */
347 1 : int onoff_request(struct onoff_manager *mgr,
348 : struct onoff_client *cli);
349 :
350 : /**
351 : * @brief Release a reserved use of an on-off service.
352 : *
353 : * This synchronously releases the caller's previous request. If the
354 : * last request is released the manager will initiate a transition to
355 : * off, which can be observed by registering an onoff_monitor.
356 : *
357 : * @note Behavior is undefined if this is not paired with a preceding
358 : * onoff_request() call that completed successfully.
359 : *
360 : * @param mgr the manager for which a request was successful.
361 : *
362 : * @retval non-negative the observed state (ONOFF_STATE_ON) of the
363 : * machine at the time of the release, if the release succeeds.
364 : * @retval -EIO if service has recorded an error.
365 : * @retval -ENOTSUP if the machine is not in a state that permits
366 : * release.
367 : */
368 1 : int onoff_release(struct onoff_manager *mgr);
369 :
370 : /**
371 : * @brief Attempt to cancel an in-progress client operation.
372 : *
373 : * It may be that a client has initiated an operation but needs to
374 : * shut down before the operation has completed. For example, when a
375 : * request was made and the need is no longer present.
376 : *
377 : * Cancelling is supported only for onoff_request() and onoff_reset()
378 : * operations, and is a synchronous operation. Be aware that any
379 : * transition that was initiated on behalf of the client will continue
380 : * to progress to completion: it is only notification of transition
381 : * completion that may be eliminated. If there are no active requests
382 : * when a transition to on completes the manager will initiate a
383 : * transition to off.
384 : *
385 : * Client notification does not occur for cancelled operations.
386 : *
387 : * @param mgr the manager for which an operation is to be cancelled.
388 : *
389 : * @param cli a pointer to the same client state that was provided
390 : * when the operation to be cancelled was issued.
391 : *
392 : * @retval non-negative the observed state of the machine at the time
393 : * of the cancellation, if the cancellation succeeds. On successful
394 : * cancellation ownership of @c *cli reverts to the client.
395 : * @retval -EINVAL if the parameters are invalid.
396 : * @retval -EALREADY if cli was not a record of an uncompleted
397 : * notification at the time the cancellation was processed. This
398 : * likely indicates that the operation and client notification had
399 : * already completed.
400 : */
401 1 : int onoff_cancel(struct onoff_manager *mgr,
402 : struct onoff_client *cli);
403 :
404 : /**
405 : * @brief Helper function to safely cancel a request.
406 : *
407 : * Some applications may want to issue requests on an asynchronous
408 : * event (such as connection to a USB bus) and to release on a paired
409 : * event (such as loss of connection to a USB bus). Applications
410 : * cannot precisely determine that an in-progress request is still
411 : * pending without using onoff_monitor and carefully avoiding race
412 : * conditions.
413 : *
414 : * This function is a helper that attempts to cancel the operation and
415 : * issues a release if cancellation fails because the request was
416 : * completed. This synchronously ensures that ownership of the client
417 : * data reverts to the client so is available for a future request.
418 : *
419 : * @param mgr the manager for which an operation is to be cancelled.
420 : *
421 : * @param cli a pointer to the same client state that was provided
422 : * when onoff_request() was invoked. Behavior is undefined if this is
423 : * a pointer to client data associated with an onoff_reset() request.
424 : *
425 : * @retval ONOFF_STATE_TO_ON if the cancellation occurred before the
426 : * transition completed.
427 : *
428 : * @retval ONOFF_STATE_ON if the cancellation occurred after the
429 : * transition completed.
430 : *
431 : * @retval -EINVAL if the parameters are invalid.
432 : *
433 : * @retval negative other errors produced by onoff_release().
434 : */
435 1 : static inline int onoff_cancel_or_release(struct onoff_manager *mgr,
436 : struct onoff_client *cli)
437 : {
438 : int rv = onoff_cancel(mgr, cli);
439 :
440 : if (rv == -EALREADY) {
441 : rv = onoff_release(mgr);
442 : }
443 : return rv;
444 : }
445 :
446 : /**
447 : * @brief Clear errors on an on-off service and reset it to its off
448 : * state.
449 : *
450 : * A service can only be reset when it is in an error state as
451 : * indicated by onoff_has_error().
452 : *
453 : * The return value indicates the success or failure of an attempt to
454 : * initiate an operation to reset the resource. If initiation of the
455 : * operation succeeds the result of the reset operation itself is
456 : * provided through the configured client notification method,
457 : * possibly before this call returns. Multiple clients may request a
458 : * reset; all are notified when it is complete.
459 : *
460 : * Note that the call to this function may succeed in a case where the
461 : * actual reset fails. Always check the operation completion result.
462 : *
463 : * @note Due to the conditions on state transition all incomplete
464 : * asynchronous operations will have been informed of the error when
465 : * it occurred. There need be no concern about dangling requests left
466 : * after a reset completes.
467 : *
468 : * @param mgr the manager to be reset.
469 : *
470 : * @param cli pointer to client state, including instructions on how
471 : * to notify the client when reset completes. Behavior is undefined
472 : * if cli references an object associated with an incomplete service
473 : * operation.
474 : *
475 : * @retval non-negative the observed state of the machine at the time
476 : * of the reset, if the reset succeeds.
477 : * @retval -ENOTSUP if reset is not supported by the service.
478 : * @retval -EINVAL if the parameters are invalid.
479 : * @retval -EALREADY if the service does not have a recorded error.
480 : */
481 1 : int onoff_reset(struct onoff_manager *mgr,
482 : struct onoff_client *cli);
483 :
484 : /**
485 : * @brief Signature used to notify a monitor of an onoff service of
486 : * errors or completion of a state transition.
487 : *
488 : * This is similar to onoff_client_callback but provides information
489 : * about all transitions, not just ones associated with a specific
490 : * client. Monitor callbacks are invoked before any completion
491 : * notifications associated with the state change are made.
492 : *
493 : * These functions may be invoked from any context including
494 : * pre-kernel, ISR, or cooperative or pre-emptible threads.
495 : * Compatible functions must be isr-ok and not sleep.
496 : *
497 : * The callback is permitted to unregister itself from the manager,
498 : * but must not register or unregister any other monitors.
499 : *
500 : * @param mgr the manager for which a transition has completed.
501 : *
502 : * @param mon the monitor instance through which this notification
503 : * arrived.
504 : *
505 : * @param state the state of the machine at the time of completion,
506 : * restricted by ONOFF_STATE_MASK. All valid states may be observed.
507 : *
508 : * @param res the result of the operation. Expected values are
509 : * service- and state-specific, but the value shall be non-negative if
510 : * the operation succeeded, and negative if the operation failed.
511 : */
512 1 : typedef void (*onoff_monitor_callback)(struct onoff_manager *mgr,
513 : struct onoff_monitor *mon,
514 : uint32_t state,
515 : int res);
516 :
517 : /**
518 : * @brief Registration state for notifications of onoff service
519 : * transitions.
520 : *
521 : * Any given onoff_monitor structure can be associated with at most
522 : * one onoff_manager instance.
523 : */
524 1 : struct onoff_monitor {
525 : /** Links the client into the set of waiting service users.
526 : *
527 : * This must be zero-initialized.
528 : */
529 1 : sys_snode_t node;
530 :
531 : /** Callback to be invoked on state change.
532 : *
533 : * This must not be null.
534 : */
535 1 : onoff_monitor_callback callback;
536 : };
537 :
538 : /**
539 : * @brief Add a monitor of state changes for a manager.
540 : *
541 : * @param mgr the manager for which a state changes are to be monitored.
542 : *
543 : * @param mon a linkable node providing a non-null callback to be
544 : * invoked on state changes.
545 : *
546 : * @return non-negative on successful addition, or a negative error
547 : * code.
548 : */
549 1 : int onoff_monitor_register(struct onoff_manager *mgr,
550 : struct onoff_monitor *mon);
551 :
552 : /**
553 : * @brief Remove a monitor of state changes from a manager.
554 : *
555 : * @param mgr the manager for which a state changes are to be monitored.
556 : *
557 : * @param mon a linkable node providing the callback to be invoked on
558 : * state changes.
559 : *
560 : * @return non-negative on successful removal, or a negative error
561 : * code.
562 : */
563 1 : int onoff_monitor_unregister(struct onoff_manager *mgr,
564 : struct onoff_monitor *mon);
565 :
566 : /**
567 : * @brief State used when a driver uses the on-off service API for synchronous
568 : * operations.
569 : *
570 : * This is useful when a subsystem API uses the on-off API to support
571 : * asynchronous operations but the transitions required by a
572 : * particular driver are isr-ok and not sleep. It serves as a
573 : * substitute for #onoff_manager, with locking and persisted state
574 : * updates supported by onoff_sync_lock() and onoff_sync_finalize().
575 : */
576 1 : struct onoff_sync_service {
577 : /** Mutex protection for other fields. */
578 1 : struct k_spinlock lock;
579 :
580 : /** Negative is error, non-negative is reference count. */
581 1 : int32_t count;
582 : };
583 :
584 : /**
585 : * @brief Lock a synchronous onoff service and provide its state.
586 : *
587 : * @note If an error state is returned it is the caller's responsibility to
588 : * decide whether to preserve it (finalize with the same error state) or clear
589 : * the error (finalize with a non-error result).
590 : *
591 : * @param srv pointer to the synchronous service state.
592 : *
593 : * @param keyp pointer to where the lock key should be stored
594 : *
595 : * @return negative if the service is in an error state, otherwise the
596 : * number of active requests at the time the lock was taken. The lock
597 : * is held on return regardless of whether a negative state is
598 : * returned.
599 : */
600 1 : int onoff_sync_lock(struct onoff_sync_service *srv,
601 : k_spinlock_key_t *keyp);
602 :
603 : /**
604 : * @brief Process the completion of a transition in a synchronous
605 : * service and release lock.
606 : *
607 : * This function updates the service state on the @p res and @p on parameters
608 : * then releases the lock. If @p cli is not null it finalizes the client
609 : * notification using @p res.
610 : *
611 : * If the service was in an error state when locked, and @p res is non-negative
612 : * when finalized, the count is reset to zero before completing finalization.
613 : *
614 : * @param srv pointer to the synchronous service state
615 : *
616 : * @param key the key returned by the preceding invocation of onoff_sync_lock().
617 : *
618 : * @param cli pointer to the onoff client through which completion
619 : * information is returned. If a null pointer is passed only the
620 : * state of the service is updated. For compatibility with the
621 : * behavior of callbacks used with the manager API @p cli must be null
622 : * when @p on is false (the manager does not support callbacks when
623 : * turning off devices).
624 : *
625 : * @param res the result of the transition. A negative value places the service
626 : * into an error state. A non-negative value increments or decrements the
627 : * reference count as specified by @p on.
628 : *
629 : * @param on Only when @p res is non-negative, the service reference count will
630 : * be incremented if@p on is @c true, and decremented if @p on is @c false.
631 : *
632 : * @return negative if the service is left or put into an error state, otherwise
633 : * the number of active requests at the time the lock was released.
634 : */
635 1 : int onoff_sync_finalize(struct onoff_sync_service *srv,
636 : k_spinlock_key_t key,
637 : struct onoff_client *cli,
638 : int res,
639 : bool on);
640 :
641 : /** @} */
642 :
643 : #ifdef __cplusplus
644 : }
645 : #endif
646 :
647 : #endif /* ZEPHYR_INCLUDE_SYS_ONOFF_H_ */
|