570 lines
19 KiB
C
570 lines
19 KiB
C
/*
|
|
* Copyright (c) 2019 Peter Bigot Consulting, LLC
|
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
|
*
|
|
* 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>
|
|
#include <sys/notify.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/**
|
|
* @defgroup resource_mgmt_onoff_apis On-Off Service APIs
|
|
* @ingroup kernel_apis
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Flag indicating an error state.
|
|
*
|
|
* Error states are cleared using onoff_reset().
|
|
*/
|
|
#define ONOFF_FLAG_ERROR BIT(0)
|
|
|
|
/** @internal */
|
|
#define ONOFF_FLAG_ONOFF BIT(1)
|
|
/** @internal */
|
|
#define ONOFF_FLAG_TRANSITION BIT(2)
|
|
|
|
/**
|
|
* @brief Mask used to isolate bits defining the service state.
|
|
*
|
|
* Mask a value with this then test for ONOFF_FLAG_ERROR to determine
|
|
* whether the machine has an unfixed error, or compare against
|
|
* ONOFF_STATE_ON, ONOFF_STATE_OFF, ONOFF_STATE_TO_ON,
|
|
* ONOFF_STATE_TO_OFF, or ONOFF_STATE_RESETTING.
|
|
*/
|
|
#define ONOFF_STATE_MASK (ONOFF_FLAG_ERROR \
|
|
| ONOFF_FLAG_ONOFF \
|
|
| ONOFF_FLAG_TRANSITION)
|
|
|
|
/**
|
|
* @brief Value exposed by ONOFF_STATE_MASK when service is off.
|
|
*/
|
|
#define ONOFF_STATE_OFF 0
|
|
|
|
/**
|
|
* @brief Value exposed by ONOFF_STATE_MASK when service is on.
|
|
*/
|
|
#define ONOFF_STATE_ON ONOFF_FLAG_ONOFF
|
|
|
|
/**
|
|
* @brief Value exposed by ONOFF_STATE_MASK when the service is in an
|
|
* error state (and not in the process of resetting its state).
|
|
*/
|
|
#define ONOFF_STATE_ERROR ONOFF_FLAG_ERROR
|
|
|
|
/**
|
|
* @brief Value exposed by ONOFF_STATE_MASK when service is
|
|
* transitioning to on.
|
|
*/
|
|
#define ONOFF_STATE_TO_ON (ONOFF_FLAG_TRANSITION | ONOFF_STATE_ON)
|
|
|
|
/**
|
|
* @brief Value exposed by ONOFF_STATE_MASK when service is
|
|
* transitioning to off.
|
|
*/
|
|
#define ONOFF_STATE_TO_OFF (ONOFF_FLAG_TRANSITION | ONOFF_STATE_OFF)
|
|
|
|
/**
|
|
* @brief Value exposed by ONOFF_STATE_MASK when service is in the
|
|
* process of resetting.
|
|
*/
|
|
#define ONOFF_STATE_RESETTING (ONOFF_FLAG_TRANSITION | ONOFF_STATE_ERROR)
|
|
|
|
/* Forward declarations */
|
|
struct onoff_manager;
|
|
struct onoff_monitor;
|
|
|
|
/**
|
|
* @brief Signature used to notify an on-off manager 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 mgr the manager 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_notify_fn)(struct onoff_manager *mgr,
|
|
int res);
|
|
|
|
/**
|
|
* @brief Signature used by service implementations to effect a
|
|
* transition.
|
|
*
|
|
* Service definitions use two required function pointers of this type
|
|
* to be notified that a transition is required, and a third optional
|
|
* one to reset the service when it is in an error 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 (where supported) will be called only when
|
|
* onoff_has_error() returns true.
|
|
*
|
|
* @note All transitions functions must be isr-ok.
|
|
*
|
|
* @param mgr the manager for which transition was requested.
|
|
*
|
|
* @param notify the function to be invoked when the transition has
|
|
* completed. If the transition is synchronous, notify shall be
|
|
* invoked by the implementation before the transition function
|
|
* returns. Otherwise the implementation shall capture this parameter
|
|
* and invoke it when the transition completes.
|
|
*/
|
|
typedef void (*onoff_transition_fn)(struct onoff_manager *mgr,
|
|
onoff_notify_fn notify);
|
|
|
|
/** @brief On-off service transition functions. */
|
|
struct onoff_transitions {
|
|
/* Function to invoke to transition the service to on. */
|
|
onoff_transition_fn start;
|
|
|
|
/* Function to invoke to transition the service to off. */
|
|
onoff_transition_fn stop;
|
|
|
|
/* Function to force the service state to reset, where
|
|
* supported.
|
|
*/
|
|
onoff_transition_fn reset;
|
|
};
|
|
|
|
/**
|
|
* @brief State associated with an on-off manager.
|
|
*
|
|
* No fields in this structure are intended for use by service
|
|
* providers or clients. The state is to be initialized once, using
|
|
* onoff_manager_init(), when the service provider is initialized. In
|
|
* case of error it may be reset through the onoff_reset() API.
|
|
*/
|
|
struct onoff_manager {
|
|
/* List of clients waiting for request or reset completion
|
|
* notifications.
|
|
*/
|
|
sys_slist_t clients;
|
|
|
|
/* List of monitors to be notified of state changes including
|
|
* errors and transition completion.
|
|
*/
|
|
sys_slist_t monitors;
|
|
|
|
/* Transition functions. */
|
|
const struct onoff_transitions *transitions;
|
|
|
|
/* Mutex protection for other fields. */
|
|
struct k_spinlock lock;
|
|
|
|
/* The result of the last transition. */
|
|
int last_res;
|
|
|
|
/* Flags identifying the service state. */
|
|
u16_t flags;
|
|
|
|
/* Number of active clients for the service. */
|
|
u16_t refs;
|
|
};
|
|
|
|
/** @brief Initializer for a onoff_transitions object.
|
|
*
|
|
* @param _start a function used to transition from off to on state.
|
|
*
|
|
* @param _stop a function used to transition from on to off state.
|
|
*
|
|
* @param _reset a function used to clear errors and force the service
|
|
* to an off state. Can be null.
|
|
*/
|
|
#define ONOFF_TRANSITIONS_INITIALIZER(_start, _stop, _reset) { \
|
|
.start = _start, \
|
|
.stop = _stop, \
|
|
.reset = _reset, \
|
|
}
|
|
|
|
/** @internal */
|
|
#define ONOFF_MANAGER_INITIALIZER(_transitions) { \
|
|
.transitions = _transitions, \
|
|
}
|
|
|
|
/**
|
|
* @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 mgr the manager definition object to be initialized.
|
|
*
|
|
* @param transitions pointer to a structure providing transition
|
|
* functions. The referenced object must persist as long as the
|
|
* manager can be referenced.
|
|
*
|
|
* @retval 0 on success
|
|
* @retval -EINVAL if start, stop, or flags are invalid
|
|
*/
|
|
int onoff_manager_init(struct onoff_manager *mgr,
|
|
const struct onoff_transitions *transitions);
|
|
|
|
/* 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 mgr the manager for which the operation was initiated.
|
|
*
|
|
* @param cli the client structure passed to the function that
|
|
* initiated the operation.
|
|
*
|
|
* @param state the state of the machine at the time of completion,
|
|
* restricted by ONOFF_STATE_MASK. ONOFF_FLAG_ERROR must be checked
|
|
* independently of whether res is negative as a machine error may
|
|
* indicate that all future operations except onoff_reset() will fail.
|
|
*
|
|
* @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. If res
|
|
* is negative ONOFF_FLAG_ERROR will be set in state, but if res is
|
|
* non-negative ONOFF_FLAG_ERROR may still be set in state.
|
|
*/
|
|
typedef void (*onoff_client_callback)(struct onoff_manager *mgr,
|
|
struct onoff_client *cli,
|
|
u32_t state,
|
|
int res);
|
|
|
|
/**
|
|
* @brief State associated with a client of an on-off service.
|
|
*
|
|
* Objects of this type are allocated by a client, which is
|
|
* responsible for zero-initializing the node field and invoking the
|
|
* approprite sys_notify init function to configure notification.
|
|
*
|
|
* Control of the object content transfers to the service provider
|
|
* when a pointer to the object is passed to any on-off manager
|
|
* function. While the service provider controls the object the
|
|
* client must not change any object fields. Control reverts to the
|
|
* client concurrent with release of the owned sys_notify structure,
|
|
* or when indicated by an onoff_cancel() return value.
|
|
*
|
|
* After control has reverted to the client the notify field must be
|
|
* reinitialized for the next operation.
|
|
*/
|
|
struct onoff_client {
|
|
/** @internal
|
|
*
|
|
* Links the client into the set of waiting service users.
|
|
* Applications must ensure this field is zero-initialized
|
|
* before use.
|
|
*/
|
|
sys_snode_t node;
|
|
|
|
/** @brief Notification configuration. */
|
|
struct sys_notify notify;
|
|
};
|
|
|
|
/**
|
|
* @brief Identify region of sys_notify flags available for
|
|
* containing services.
|
|
*
|
|
* Bits of the flags field of the sys_notify structure contained
|
|
* within the queued_operation structure at and above this position
|
|
* may be used by extensions to the onoff_client structure.
|
|
*
|
|
* These bits are intended for use by containing service
|
|
* implementations to record client-specific information and are
|
|
* subject to other conditions of use specified on the sys_notify API.
|
|
*/
|
|
#define ONOFF_CLIENT_EXTENSION_POS SYS_NOTIFY_EXTENSION_POS
|
|
|
|
/**
|
|
* @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_reset().
|
|
*
|
|
* This is an unlocked convenience function suitable for use only when
|
|
* it is known that no other process might invoke an operation that
|
|
* transitions the service between an error and non-error state.
|
|
*
|
|
* @return true if and only if the service has an uncleared error.
|
|
*/
|
|
static inline bool onoff_has_error(const struct onoff_manager *mgr)
|
|
{
|
|
return (mgr->flags & ONOFF_FLAG_ERROR) != 0;
|
|
}
|
|
|
|
/**
|
|
* @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.
|
|
*
|
|
* @param mgr the manager 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 the observed state of the machine at the time
|
|
* the request was processed, if successful.
|
|
* @retval -EIO if service has recorded an an error.
|
|
* @retval -EINVAL if the parameters are invalid.
|
|
* @retval -EAGAIN if the reference count would overflow.
|
|
*/
|
|
int onoff_request(struct onoff_manager *mgr,
|
|
struct onoff_client *cli);
|
|
|
|
/**
|
|
* @brief Release a reserved use of an on-off service.
|
|
*
|
|
* This synchronously releases the caller's previous request. If the
|
|
* last request is released the manager will initiate a transition to
|
|
* off, which can be observed by registering an onoff_monitor.
|
|
*
|
|
* @note Behavior is undefined if this is not paired with a preceding
|
|
* onoff_request() call that completed successfully.
|
|
*
|
|
* @param mgr the manager for which a request was successful.
|
|
*
|
|
* @retval non-negative the observed state (ONOFF_STATE_ON) of the
|
|
* machine at the time of the release, if the release succeeds.
|
|
* @retval -EIO if service has recorded an an error.
|
|
* @retval -ENOTSUP if the machine is not in a state that permits
|
|
* release.
|
|
*/
|
|
int onoff_release(struct onoff_manager *mgr);
|
|
|
|
/**
|
|
* @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.
|
|
*
|
|
* Cancelling is supported only for onoff_request() and onoff_reset()
|
|
* operations, and is a synchronous operation. Be aware that any
|
|
* transition that was initiated on behalf of the client will continue
|
|
* to progress to completion: it is only notification of transition
|
|
* completion that may be eliminated. If there are no active requests
|
|
* when a transition to on completes the manager will initiate a
|
|
* transition to off.
|
|
*
|
|
* Client notification does not occur for cancelled operations.
|
|
*
|
|
* @param mgr the manager 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.
|
|
*
|
|
* @retval non-negative the observed state of the machine at the time
|
|
* of the cancellation, if the cancellation succeeds. On successful
|
|
* cancellation ownership of @c *cli reverts to the client.
|
|
* @retval -EINVAL if the parameters are invalid.
|
|
* @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_manager *mgr,
|
|
struct onoff_client *cli);
|
|
|
|
/**
|
|
* @brief Helper function to safely cancel a request.
|
|
*
|
|
* Some applications may want to issue requests on an asynchronous
|
|
* event (such as connection to a USB bus) and to release on a paired
|
|
* event (such as loss of connection to a USB bus). Applications
|
|
* cannot precisely determine that an in-progress request is still
|
|
* pending without using onoff_monitor and carefully avoiding race
|
|
* conditions.
|
|
*
|
|
* This function is a helper that attempts to cancel the operation and
|
|
* issues a release if cancellation fails because the request was
|
|
* completed. This synchronously ensures that ownership of the client
|
|
* data reverts to the client so is available for a future request.
|
|
*
|
|
* @param mgr the manager for which an operation is to be cancelled.
|
|
*
|
|
* @param cli a pointer to the same client state that was provided
|
|
* when onoff_request() was invoked. Behavior is undefined if this is
|
|
* a pointer to client data associated with an onoff_reset() request.
|
|
*
|
|
* @retval ONOFF_STATE_TO_ON if the cancellation occurred before the
|
|
* transition completed.
|
|
*
|
|
* @retval ONOFF_STATE_ON if the cancellation occurred after the
|
|
* transition completed.
|
|
*
|
|
* @retval -EINVAL if the parameters are invalid.
|
|
*
|
|
* @retval negative other errors produced by onoff_release().
|
|
*/
|
|
static inline int onoff_cancel_or_release(struct onoff_manager *mgr,
|
|
struct onoff_client *cli)
|
|
{
|
|
int rv = onoff_cancel(mgr, cli);
|
|
|
|
if (rv == -EALREADY) {
|
|
rv = onoff_release(mgr);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* @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_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.
|
|
*
|
|
* @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 mgr the manager 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 non-negative the observed state of the machine at the time
|
|
* of the reset, if the reset succeeds.
|
|
* @retval -ENOTSUP if reset is not supported by the service.
|
|
* @retval -EINVAL if the parameters are invalid.
|
|
* @retval -EALREADY if the service does not have a recorded error.
|
|
*/
|
|
int onoff_reset(struct onoff_manager *mgr,
|
|
struct onoff_client *cli);
|
|
|
|
/**
|
|
* @brief Signature used to notify a monitor of an onoff service of
|
|
* errors or completion of a state transition.
|
|
*
|
|
* This is similar to onoff_client_callback but provides information
|
|
* about all transitions, not just ones associated with a specific
|
|
* client. Monitor callbacks are invoked before any completion
|
|
* notifications associated with the state change are made.
|
|
*
|
|
* 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.
|
|
*
|
|
* The callback is permitted to unregister itself from the manager,
|
|
* but must not register or unregister any other monitors.
|
|
*
|
|
* @param mgr the manager for which a transition has completed.
|
|
*
|
|
* @param mon the monitor instance through which this notification
|
|
* arrived.
|
|
*
|
|
* @param state the state of the machine at the time of completion,
|
|
* restricted by ONOFF_STATE_MASK. All valid states may be observed.
|
|
*
|
|
* @param res the result of the operation. Expected values are
|
|
* service- and state-specific, but the value shall be non-negative if
|
|
* the operation succeeded, and negative if the operation failed.
|
|
*/
|
|
typedef void (*onoff_monitor_callback)(struct onoff_manager *mgr,
|
|
struct onoff_monitor *mon,
|
|
u32_t state,
|
|
int res);
|
|
|
|
/**
|
|
* @brief Registration state for notifications of onoff service
|
|
* transitions.
|
|
*
|
|
* Any given onoff_monitor structure can be associated with at most
|
|
* one onoff_manager instance.
|
|
*/
|
|
struct onoff_monitor {
|
|
/* Links the client into the set of waiting service users.
|
|
*
|
|
* This must be zero-initialized.
|
|
*/
|
|
sys_snode_t node;
|
|
|
|
/** @brief Callback to be invoked on state change.
|
|
*
|
|
* This must not be null.
|
|
*/
|
|
onoff_monitor_callback callback;
|
|
};
|
|
|
|
/**
|
|
* @brief Add a monitor of state changes for a manager.
|
|
*
|
|
* @param mgr the manager for which a state changes are to be monitored.
|
|
*
|
|
* @param mon a linkable node providing a non-null callback to be
|
|
* invoked on state changes.
|
|
*
|
|
* @return non-negative on successful addition, or a negative error
|
|
* code.
|
|
*/
|
|
int onoff_monitor_register(struct onoff_manager *mgr,
|
|
struct onoff_monitor *mon);
|
|
|
|
/**
|
|
* @brief Remove a monitor of state changes from a manager.
|
|
*
|
|
* @param mgr the manager for which a state changes are to be monitored.
|
|
*
|
|
* @param mon a linkable node providing the callback to be invoked on
|
|
* state changes.
|
|
*
|
|
* @return non-negative on successful removal, or a negative error
|
|
* code.
|
|
*/
|
|
int onoff_monitor_unregister(struct onoff_manager *mgr,
|
|
struct onoff_monitor *mon);
|
|
|
|
/** @} */
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* ZEPHYR_INCLUDE_SYS_ONOFF_H_ */
|