zephyr/include/sys/onoff.h
Aastha Grover 83b9f69755 code-guideline: Fixing code violation 10.4 Rule
Both operands of an operator in the arithmetic conversions
performed shall have the same essential type category.

Changes are related to converting the integer constants to the
unsigned integer constants

Signed-off-by: Aastha Grover <aastha.grover@intel.com>
2020-10-01 17:13:29 -04:00

645 lines
22 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 0U
/**
* @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. */
uint16_t flags;
/* Number of active clients for the service. */
uint16_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-ok and not sleep.
*
* @param mgr the manager for which the operation was initiated. This may be
* null if the on-off service uses synchronous transitions.
*
* @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,
uint32_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-ok and not sleep.
*
* 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,
uint32_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);
/**
* @brief State used when a driver uses the on-off service API for synchronous
* operations.
*
* This is useful when a subsystem API uses the on-off API to support
* asynchronous operations but the transitions required by a
* particular driver are isr-ok and not sleep. It serves as a
* substitute for #onoff_manager, with locking and persisted state
* updates supported by onoff_sync_lock() and onoff_sync_finalize().
*/
struct onoff_sync_service {
/* Mutex protection for other fields. */
struct k_spinlock lock;
/* Negative is error, non-negative is reference count. */
int32_t count;
};
/**
* @brief Lock a synchronous onoff service and provide its state.
*
* @note If an error state is returned it is the caller's responsibility to
* decide whether to preserve it (finalize with the same error state) or clear
* the error (finalize with a non-error result).
*
* @param srv pointer to the synchronous service state.
*
* @param keyp pointer to where the lock key should be stored
*
* @return negative if the service is in an error state, otherwise the
* number of active requests at the time the lock was taken. The lock
* is held on return regardless of whether a negative state is
* returned.
*/
int onoff_sync_lock(struct onoff_sync_service *srv,
k_spinlock_key_t *keyp);
/**
* @brief Process the completion of a transition in a synchronous
* service and release lock.
*
* This function updates the service state on the @p res and @p on parameters
* then releases the lock. If @p cli is not null it finalizes the client
* notification using @p res.
*
* If the service was in an error state when locked, and @p res is non-negative
* when finalized, the count is reset to zero before completing finalization.
*
* @param srv pointer to the synchronous service state
*
* @param key the key returned by the preceding invocation of onoff_sync_lock().
*
* @param cli pointer to the onoff client through which completion
* information is returned. If a null pointer is passed only the
* state of the service is updated. For compatibility with the
* behavior of callbacks used with the manager API @p cli must be null
* when @p on is false (the manager does not support callbacks when
* turning off devices).
*
* @param res the result of the transition. A negative value places the service
* into an error state. A non-negative value increments or decrements the
* reference count as specified by @p on.
*
* @param on Only when @p res is non-negative, the service reference count will
* be incremented if@p on is @c true, and decremented if @p on is @c false.
*
* @return negative if the service is left or put into an error state, otherwise
* the number of active requests at the time the lock was released.
*/
int onoff_sync_finalize(struct onoff_sync_service *srv,
k_spinlock_key_t key,
struct onoff_client *cli,
int res,
bool on);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_SYS_ONOFF_H_ */