| /* |
| * Copyright (c) 2019 Peter Bigot Consulting, LLC |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #ifndef ZEPHYR_INCLUDE_SYS_ONOFF_H_ |
| #define ZEPHYR_INCLUDE_SYS_ONOFF_H_ |
| |
| #include <kernel.h> |
| #include <zephyr/types.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /** |
| * @defgroup resource_mgmt_apis Resource Management APIs |
| * @ingroup kernel_apis |
| * @{ |
| */ |
| |
| /** |
| * @brief Flag fields used to specify on-off service behavior. |
| */ |
| enum onoff_service_flags { |
| /** |
| * @brief Flag passed to onoff_service_init(). |
| * |
| * When provided this indicates the start transition function |
| * may cause the calling thread to wait. This blocks attempts |
| * to initiate a transition from a non-thread context. |
| */ |
| ONOFF_SERVICE_START_SLEEPS = BIT(0), |
| |
| /** |
| * @brief Flag passed to onoff_service_init(). |
| * |
| * As with @ref ONOFF_SERVICE_START_SLEEPS but describing the |
| * stop transition function. |
| */ |
| ONOFF_SERVICE_STOP_SLEEPS = BIT(1), |
| |
| /** |
| * @brief Flag passed to onoff_service_init(). |
| * |
| * As with @ref ONOFF_SERVICE_START_SLEEPS but describing the |
| * reset transition function. |
| */ |
| ONOFF_SERVICE_RESET_SLEEPS = BIT(2), |
| |
| /* Internal use. */ |
| ONOFF_SERVICE_HAS_ERROR = BIT(3), |
| |
| /* This and higher bits reserved for internal use. */ |
| ONOFF_SERVICE_INTERNAL_BASE = BIT(4), |
| }; |
| |
| /* Forward declaration */ |
| struct onoff_service; |
| |
| /** |
| * @brief Signature used to notify an on-off service that a transition |
| * has completed. |
| * |
| * Functions of this type are passed to service-specific transition |
| * functions to be used to report the completion of the operation. |
| * The functions may be invoked from any context. |
| * |
| * @param srv the service for which transition was requested. |
| * |
| * @param res the result of the transition. This shall be |
| * non-negative on success, or a negative error code. If an error is |
| * indicated the service shall enter an error state. |
| */ |
| typedef void (*onoff_service_notify_fn)(struct onoff_service *srv, |
| int res); |
| |
| /** |
| * @brief Signature used by service implementations to effect a |
| * transition. |
| * |
| * Service definitions use two function pointers of this type to be |
| * notified that a transition is required, and a third optional one to |
| * reset service state. |
| * |
| * The start function will be called only from the off state. |
| * |
| * The stop function will be called only from the on state. |
| * |
| * The reset function may be called only when |
| * onoff_service_has_error() returns true. |
| * |
| * @param srv the service for which transition was requested. |
| * |
| * @param notify the function to be invoked when the transition has |
| * completed. The callee shall capture this parameter to notify on |
| * completion of asynchronous transitions. If the transition is not |
| * asynchronous, notify shall be invoked before the transition |
| * function returns. |
| */ |
| typedef void (*onoff_service_transition_fn)(struct onoff_service *srv, |
| onoff_service_notify_fn notify); |
| |
| /** |
| * @brief State associated with an on-off service. |
| * |
| * No fields in this structure are intended for use by service |
| * providers or clients. The state is to be initialized once, using |
| * onoff_service_init(), when the service provider is initialized. |
| * In case of error it may be reset through the |
| * onoff_service_reset() API. |
| */ |
| struct onoff_service { |
| /* List of clients waiting for completion of reset or |
| * transition to on. |
| */ |
| sys_slist_t clients; |
| |
| /* Function to invoke to transition the service to on. */ |
| onoff_service_transition_fn start; |
| |
| /* Function to invoke to transition the service to off. */ |
| onoff_service_transition_fn stop; |
| |
| /* Function to force the service state to reset, where |
| * supported. |
| */ |
| onoff_service_transition_fn reset; |
| |
| /* Mutex protection for flags, clients, releaser, and refs. */ |
| struct k_spinlock lock; |
| |
| /* Client to be informed when transition to off completes. */ |
| struct onoff_client *releaser; |
| |
| /* Flags identifying the service state. */ |
| u16_t flags; |
| |
| /* Number of active clients for the service. */ |
| u16_t refs; |
| }; |
| |
| /** @internal */ |
| #define ONOFF_SERVICE_INITIALIZER(_start, _stop, _reset, _flags) { \ |
| .start = _start, \ |
| .stop = _stop, \ |
| .reset = _reset, \ |
| .flags = _flags, \ |
| } |
| |
| /** |
| * @brief Initialize an on-off service to off state. |
| * |
| * This function must be invoked exactly once per service instance, by |
| * the infrastructure that provides the service, and before any other |
| * on-off service API is invoked on the service. |
| * |
| * This function should never be invoked by clients of an on-off service. |
| * |
| * @param srv the service definition object to be initialized. |
| * |
| * @param start the function used to (initiate a) transition from off |
| * to on. This must not be null. Include @ref ONOFF_SERVICE_START_SLEEPS as |
| * appropriate in flags. |
| * |
| * @param stop the function used to (initiate a) transition from on to |
| * off. This must not be null. Include @ref ONOFF_SERVICE_STOP_SLEEPS |
| * as appropriate in flags. |
| * |
| * @param reset the function used to clear errors and force the |
| * service to an off state. Pass null if the service cannot or need |
| * not be reset. (Services where a transition operation can complete |
| * with an error notification should support the reset operation.) |
| * Include @ref ONOFF_SERVICE_RESET_SLEEPS as appropriate in flags. |
| * |
| * @param flags any or all of the flags mentioned above, |
| * e.g. @ref ONOFF_SERVICE_START_SLEEPS. Use of other flags produces an |
| * error. |
| * |
| * @retval 0 on success |
| * @retval -EINVAL if start, stop, or flags are invalid |
| */ |
| int onoff_service_init(struct onoff_service *srv, |
| onoff_service_transition_fn start, |
| onoff_service_transition_fn stop, |
| onoff_service_transition_fn reset, |
| u32_t flags); |
| |
| /** @internal |
| * |
| * Flag fields used to specify on-off client behavior. |
| * |
| * These flags control whether calls to onoff_service_request() and |
| * onoff_service_release() are synchronous or asynchronous, and for |
| * asynchronous operations how the operation result is communicated to |
| * the client. |
| */ |
| enum onoff_client_flags { |
| /* Known-invalid field, used in validation */ |
| ONOFF_CLIENT_NOTIFY_INVALID = 0, |
| |
| /* |
| * Indicates that no notification will be provided. |
| * |
| * Callers must check for completions using |
| * onoff_client_fetch_result(). |
| * |
| * See onoff_client_init_spinwait(). |
| */ |
| ONOFF_CLIENT_NOTIFY_SPINWAIT = 1, |
| |
| /* |
| * Select notification through @ref k_poll signal |
| * |
| * See onoff_client_init_signal(). |
| */ |
| ONOFF_CLIENT_NOTIFY_SIGNAL = 2, |
| |
| /** |
| * Select notification through a user-provided callback. |
| * |
| * See onoff_client_init_callback(). |
| */ |
| ONOFF_CLIENT_NOTIFY_CALLBACK = 3, |
| }; |
| |
| /* Forward declaration */ |
| struct onoff_client; |
| |
| /** |
| * @brief Signature used to notify an on-off service client of the |
| * completion of an operation. |
| * |
| * These functions may be invoked from any context including |
| * pre-kernel, ISR, or cooperative or pre-emptible threads. |
| * Compatible functions must be isr-callable and non-suspendable. |
| * |
| * @param srv the service for which the operation was initiated. |
| * |
| * @param cli the client structure passed to the function that |
| * initiated the operation. |
| * |
| * @param user_data user data provided when the client structure was |
| * initialized with onoff_client_init_callback(). |
| * |
| * @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. |
| */ |
| typedef void (*onoff_client_callback)(struct onoff_service *srv, |
| struct onoff_client *cli, |
| void *user_data, |
| int res); |
| |
| /** |
| * @brief State associated with a client of an on-off service. |
| * |
| * Objects of this type are allocated by a client, which must use an |
| * initialization function (e.g. onoff_client_init_signal()) to |
| * configure them. |
| * |
| * Control of the object content transfers to the service provider |
| * when a pointer to the object is passed to any on-off service |
| * function. 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; |
| * * if the call to the service API succeeds for a no-wait operation; |
| * * when operation completion is posted (signalled or callback |
| * invoked). |
| * |
| * After control has reverted to the client the state object must be |
| * reinitialized for the next operation. |
| * |
| * The content of this structure is not public API: all configuration |
| * and inspection should be done with functions like |
| * onoff_client_init_callback() and onoff_client_fetch_result(). |
| */ |
| struct onoff_client { |
| /* Links the client into the set of waiting service users. */ |
| sys_snode_t node; |
| |
| union async { |
| /* Pointer to signal used to notify client. |
| * |
| * The signal value corresponds to the res parameter |
| * of onoff_client_callback. |
| */ |
| struct k_poll_signal *signal; |
| |
| /* Handler and argument for callback notification. */ |
| struct callback { |
| onoff_client_callback handler; |
| void *user_data; |
| } callback; |
| } async; |
| |
| /* |
| * 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 no-wait synchronous operations. |
| */ |
| int volatile result; |
| |
| /* Flags recording client state. */ |
| u32_t volatile flags; |
| }; |
| |
| /** |
| * @brief Check for and read the result of an asynchronous operation. |
| * |
| * @param op 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 onoff_client_fetch_result(const struct onoff_client *op, |
| int *result) |
| { |
| __ASSERT_NO_MSG(op != NULL); |
| __ASSERT_NO_MSG(result != NULL); |
| |
| int rv = -EAGAIN; |
| |
| if (op->flags == 0U) { |
| rv = 0; |
| *result = op->result; |
| } |
| return rv; |
| } |
| |
| /** |
| * @brief Initialize an on-off client to be used for a spin-wait |
| * operation notification. |
| * |
| * Clients that use this initialization receive no asynchronous |
| * notification, and instead must periodically check for completion |
| * using onoff_client_fetch_result(). |
| * |
| * On completion of the operation the client object must be |
| * reinitialized before it can be re-used. |
| * |
| * @param cli pointer to the client state object. |
| */ |
| static inline void onoff_client_init_spinwait(struct onoff_client *cli) |
| { |
| __ASSERT_NO_MSG(cli != NULL); |
| |
| *cli = (struct onoff_client){ |
| .flags = ONOFF_CLIENT_NOTIFY_SPINWAIT, |
| }; |
| } |
| |
| /** |
| * @brief Initialize an on-off client to be used for a signal |
| * operation notification. |
| * |
| * Clients that use this initialization will be notified of the |
| * completion of operations submitted through onoff_request() and |
| * onoff_release() through the provided signal. |
| * |
| * On completion of the operation the client object must be |
| * reinitialized before it can be re-used. |
| * |
| * @note |
| * @rst |
| * This capability is available only when :option:`CONFIG_POLL` is |
| * selected. |
| * @endrst |
| * |
| * @param cli pointer to the client state 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 onoff_client_init_signal(struct onoff_client *cli, |
| struct k_poll_signal *sigp) |
| { |
| __ASSERT_NO_MSG(cli != NULL); |
| __ASSERT_NO_MSG(sigp != NULL); |
| |
| *cli = (struct onoff_client){ |
| #ifdef CONFIG_POLL |
| .async = { |
| .signal = sigp, |
| }, |
| #endif /* CONFIG_POLL */ |
| .flags = ONOFF_CLIENT_NOTIFY_SIGNAL, |
| }; |
| } |
| |
| /** |
| * @brief Initialize an on-off client to be used for a callback |
| * operation notification. |
| * |
| * Clients that use this initialization will be notified of the |
| * completion of operations submitted through on-off service API |
| * through the provided callback. Note that callbacks may be invoked |
| * from various contexts depending on the specific service; see |
| * @ref onoff_client_callback. |
| * |
| * On completion of the operation the client object must be |
| * reinitialized before it can be re-used. |
| * |
| * @param cli pointer to the client state object. |
| * |
| * @param handler a function pointer to use for notification. |
| * |
| * @param user_data an opaque pointer passed to the handler to provide |
| * additional context. |
| */ |
| static inline void onoff_client_init_callback(struct onoff_client *cli, |
| onoff_client_callback handler, |
| void *user_data) |
| { |
| __ASSERT_NO_MSG(cli != NULL); |
| __ASSERT_NO_MSG(handler != NULL); |
| |
| *cli = (struct onoff_client){ |
| .async = { |
| .callback = { |
| .handler = handler, |
| .user_data = user_data, |
| }, |
| }, |
| .flags = ONOFF_CLIENT_NOTIFY_CALLBACK, |
| }; |
| } |
| |
| /** |
| * @brief Request a reservation to use an on-off service. |
| * |
| * The return value indicates the success or failure of an attempt to |
| * initiate an operation to request the resource be made available. |
| * If initiation of the operation succeeds the result of the request |
| * operation is provided through the configured client notification |
| * method, possibly before this call returns. |
| * |
| * Note that the call to this function may succeed in a case where the |
| * actual request fails. Always check the operation completion |
| * result. |
| * |
| * As a specific example: A call to this function may succeed at a |
| * point while the service is still transitioning to off due to a |
| * previous call to onoff_release(). When the transition completes |
| * the service would normally start a transition to on. However, if |
| * the transition to off completed in a non-thread context, and the |
| * transition to on can sleep, the transition cannot be started and |
| * the request will fail with `-EWOULDBLOCK`. |
| * |
| * @param srv the service that will be used. |
| * |
| * @param cli a non-null pointer to client state providing |
| * instructions on synchronous expectations and how to notify the |
| * client when the request completes. Behavior is undefined if client |
| * passes a pointer object associated with an incomplete service |
| * operation. |
| * |
| * @retval Non-negative on successful (initiation of) request |
| * @retval -EIO if service has recorded an an error |
| * @retval -EINVAL if the parameters are invalid |
| * @retval -EAGAIN if the reference count would overflow |
| * @retval -EWOULDBLOCK if the function was invoked from non-thread |
| * context and successful initiation could result in an attempt to |
| * make the calling thread sleep. |
| */ |
| int onoff_request(struct onoff_service *srv, |
| struct onoff_client *cli); |
| |
| /** |
| * @brief Release a reserved use of an on-off service. |
| * |
| * The return value indicates the success or failure of an attempt to |
| * initiate an operation to release the resource. If initiation of |
| * the operation succeeds the result of the release operation itself |
| * is provided through the configured client notification method, |
| * possibly before this call returns. |
| * |
| * Note that the call to this function may succeed in a case where the |
| * actual release fails. Always check the operation completion |
| * result. |
| * |
| * @param srv the service that will be used. |
| * |
| * @param cli a non-null pointer to client state providing |
| * instructions on how to notify the client when release completes. |
| * Behavior is undefined if cli references an object associated with |
| * an incomplete service operation. |
| * |
| * @retval Non-negative on successful (initiation of) release |
| * @retval -EINVAL if the parameters are invalid |
| * @retval -EIO if service has recorded an an error |
| * @retval -EWOULDBLOCK if a non-blocking request was made and |
| * could not be satisfied without potentially blocking. |
| * @retval -EALREADY if the service is already off or transitioning |
| * to off |
| * @retval -EBUSY if the service is transitioning to on |
| */ |
| int onoff_release(struct onoff_service *srv, |
| struct onoff_client *cli); |
| |
| /** |
| * @brief Test whether an on-off service has recorded an error. |
| * |
| * This function can be used to determine whether the service has |
| * recorded an error. Errors may be cleared by invoking |
| * onoff_service_reset(). |
| * |
| * @return true if and only if the service has an uncleared error. |
| */ |
| static inline bool onoff_service_has_error(const struct onoff_service *srv) |
| { |
| return (srv->flags & ONOFF_SERVICE_HAS_ERROR) != 0; |
| } |
| |
| /** |
| * @brief Clear errors on an on-off service and reset it to its off |
| * state. |
| * |
| * A service can only be reset when it is in an error state as |
| * indicated by onoff_service_has_error(). |
| * |
| * The return value indicates the success or failure of an attempt to |
| * initiate an operation to reset the resource. If initiation of the |
| * operation succeeds the result of the reset operation itself is |
| * provided through the configured client notification method, |
| * possibly before this call returns. Multiple clients may request a |
| * reset; all are notified when it is complete. |
| * |
| * Note that the call to this function may succeed in a case where the |
| * actual reset fails. Always check the operation completion result. |
| * |
| * This function is blocking if the reset transition is blocking, |
| * unless client notification specifies no-wait. |
| * |
| * @note Due to the conditions on state transition all incomplete |
| * asynchronous operations will have been informed of the error when |
| * it occurred. There need be no concern about dangling requests left |
| * after a reset completes. |
| * |
| * @param srv the service to be reset. |
| * |
| * @param cli pointer to client state, including instructions on how |
| * to notify the client when reset completes. Behavior is undefined |
| * if cli references an object associated with an incomplete service |
| * operation. |
| * |
| * @retval 0 on success |
| * @retval -ENOTSUP if reset is not supported |
| * @retval -EINVAL if the parameters are invalid, or if the service |
| * @retval -EALREADY if the service does not have a recorded error |
| */ |
| int onoff_service_reset(struct onoff_service *srv, |
| struct onoff_client *cli); |
| |
| /** |
| * @brief Attempt to cancel an in-progress client operation. |
| * |
| * It may be that a client has initiated an operation but needs to |
| * shut down before the operation has completed. For example, when a |
| * request was made and the need is no longer present. |
| * |
| * There is limited support for cancelling an in-progress operation: |
| * * If a start or reset is in progress, all but one clients |
| * requesting the start can cancel their request. |
| * * If a stop is in progress, all clients requesting a restart can |
| * cancel their request; |
| * * A client requesting a release cannot cancel the release. |
| * |
| * Be aware that any transition that was initiated on behalf of the |
| * client will continue to progress to completion. The restricted |
| * support for cancellation ensures that for any in-progress |
| * transition there will always be at least one client that will be |
| * notified when the operation completes. |
| * |
| * If the cancellation fails the service retains control of the client |
| * object, and the client must wait for operation completion. |
| * |
| * @param srv the service for which an operation is to be cancelled. |
| * |
| * @param cli a pointer to the same client state that was provided |
| * when the operation to be cancelled was issued. If the cancellation |
| * is successful the client will be notified of operation completion |
| * with a result of `-ECANCELED`. |
| * |
| * @retval 0 if the cancellation was completed before the client could |
| * be notified. The client will be notified through cli with an |
| * operation completion of `-ECANCELED`. |
| * @retval -EINVAL if the parameters are invalid. |
| * @retval -EWOULDBLOCK if cancellation was rejected because the |
| * client is the only waiter for an in-progress transition. The |
| * service retains control of the client structure. |
| * @retval -EALREADY if cli was not a record of an uncompleted |
| * notification at the time the cancellation was processed. This |
| * likely indicates that the operation and client notification had |
| * already completed. |
| */ |
| int onoff_cancel(struct onoff_service *srv, |
| struct onoff_client *cli); |
| |
| /** @} */ |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif /* ZEPHYR_INCLUDE_SYS_ONOFF_H_ */ |