blob: a2f024a9318e3c61185984d65860178690e1999f [file] [log] [blame]
/*
* Copyright (C) 2022, Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef INCLUDE_ZEPHYR_SYS_LINEAR_RANGE_H_
#define INCLUDE_ZEPHYR_SYS_LINEAR_RANGE_H_
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <zephyr/sys/util.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup linear_range Linear Range
*
* The linear range API maps values in a linear range to a range index. A linear
* range can be fully defined by four parameters:
*
* - Minimum value
* - Step value
* - Minimum index value
* - Maximum index value
*
* For example, in a voltage regulator, supported voltages typically map to a
* register index value like this:
*
* - 1000uV: 0x00
* - 1250uV: 0x01
* - 1500uV: 0x02
* - ...
* - 3000uV: 0x08
*
* In this case, we have:
*
* - Minimum value: 1000uV
* - Step value: 250uV
* - Minimum index value: 0x00
* - Maximum index value: 0x08
*
* A linear range may also be constant, that is, step set to zero.
*
* It is often the case where the same device has discontinuous linear ranges.
* The API offers utility functions to deal with groups of linear ranges as
* well.
*
* Implementation uses fixed-width integers. Range is limited to [INT32_MIN,
* INT32_MAX], while number of indices is limited to UINT16_MAX.
*
* Original idea borrowed from Linux.
* @{
*/
/** @brief Linear range. */
struct linear_range {
/** Minimum value. */
int32_t min;
/** Step value. */
uint32_t step;
/** Minimum index (must be <= maximum index). */
uint16_t min_idx;
/** Maximum index (must be >= minimum index). */
uint16_t max_idx;
};
/**
* @brief Initializer for @ref linear_range.
*
* @param _min Minimum value in range.
* @param _step Step value.
* @param _min_idx Minimum index.
* @param _max_idx Maximum index.
*/
#define LINEAR_RANGE_INIT(_min, _step, _min_idx, _max_idx) \
{ \
.min = (_min), \
.step = (_step), \
.min_idx = (_min_idx), \
.max_idx = (_max_idx), \
}
/**
* @brief Obtain the number of values representable in a linear range.
*
* @param[in] r Linear range instance.
*
* @return Number of ranges representable by @p r.
*/
static inline uint32_t linear_range_values_count(const struct linear_range *r)
{
if (r->step == 0U) {
return 1U;
}
return r->max_idx - r->min_idx + 1U;
}
/**
* @brief Obtain the number of values representable by a group of linear ranges.
*
* @param[in] r Array of linear range instances.
* @param r_cnt Number of linear range instances.
*
* @return Number of ranges representable by the @p r group.
*/
static inline uint32_t linear_range_group_values_count(
const struct linear_range *r, size_t r_cnt)
{
uint32_t values = 0U;
for (size_t i = 0U; i < r_cnt; i++) {
values += linear_range_values_count(&r[i]);
}
return values;
}
/**
* @brief Obtain the maximum value representable by a linear range.
*
* @param[in] r Linear range instance.
*
* @return Maximum value representable by @p r.
*/
static inline int32_t linear_range_get_max_value(const struct linear_range *r)
{
return r->min + (int32_t)(r->step * (r->max_idx - r->min_idx));
}
/**
* @brief Obtain value given a linear range index.
*
* @param[in] r Linear range instance.
* @param idx Range index.
* @param[out] val Where value will be stored.
*
* @retval 0 If successful
* @retval -EINVAL If index is out of range.
*/
static inline int linear_range_get_value(const struct linear_range *r,
uint16_t idx, int32_t *val)
{
if ((idx < r->min_idx) || (idx > r->max_idx)) {
return -EINVAL;
}
*val = r->min + (int32_t)(r->step * (idx - r->min_idx));
return 0;
}
/**
* @brief Obtain value in a group given a linear range index.
*
* @param[in] r Array of linear range instances.
* @param r_cnt Number of linear range instances.
* @param idx Range index.
* @param[out] val Where value will be stored.
*
* @retval 0 If successful
* @retval -EINVAL If index is out of range.
*/
static inline int linear_range_group_get_value(const struct linear_range *r,
size_t r_cnt, uint16_t idx,
int32_t *val)
{
int ret = -EINVAL;
for (size_t i = 0U; (ret != 0) && (i < r_cnt); i++) {
ret = linear_range_get_value(&r[i], idx, val);
}
return ret;
}
/**
* @brief Obtain index given a value.
*
* @param[in] r Linear range instance.
* @param val Value.
* @param[out] idx Where index will be stored.
*
* @retval 0 If a valid index is found within the linear range.
* @retval -EINVAL If value is out of range.
*/
static inline int linear_range_get_index(const struct linear_range *r,
int32_t val, uint16_t *idx)
{
if ((val < r->min) || (val > linear_range_get_max_value(r))) {
return -EINVAL;
}
if (r->step == 0U) {
*idx = r->max_idx;
return 0;
}
*idx = r->min_idx + ceiling_fraction((uint32_t)(val - r->min), r->step);
return 0;
}
/**
* @brief Obtain index in a group given a value.
*
* @param[in] r Linear range instances.
* @param r_cnt Number of linear range instances.
* @param val Value.
* @param[out] idx Where index will be stored.
*
* @retval 0 If a valid index is found.
* @retval -EINVAL If value is out of range, or if the given window of values is
* too narrow.
*/
static inline int linear_range_group_get_index(const struct linear_range *r,
size_t r_cnt, int32_t val,
uint16_t *idx)
{
int ret = -EINVAL;
for (size_t i = 0U; (ret != 0) && (i < r_cnt); i++) {
ret = linear_range_get_index(&r[i], val, idx);
}
return ret;
}
/**
* @brief Obtain index given a window of values.
*
* @param[in] r Linear range instance.
* @param val_min Minimum window value.
* @param val_max Maximum window value.
* @param[out] idx Where index will be stored.
*
* @retval 0 If a valid index is found within window.
* @retval -EINVAL If value is out of range, or if the given window of values is
* too narrow.
*/
static inline int linear_range_get_win_index(const struct linear_range *r,
int32_t val_min, int32_t val_max,
uint16_t *idx)
{
if ((val_min < r->min) || (val_max > linear_range_get_max_value(r))) {
return -EINVAL;
}
if (r->step == 0U) {
*idx = r->max_idx;
return 0;
}
*idx = r->min_idx + ceiling_fraction((uint32_t)(val_min - r->min),
r->step);
if ((r->min + r->step * (*idx - r->min_idx)) > val_max) {
return -EINVAL;
}
return 0;
}
/**
* @brief Obtain index in a group given a value that must be within a window of
* values.
*
* @param[in] r Linear range instances.
* @param r_cnt Number of linear range instances.
* @param val_min Minimum window value.
* @param val_max Maximum window value.
* @param[out] idx Where index will be stored.
*
* @retval 0 If a valid index is found within window.
* @retval -EINVAL If value is out of range, or if the given window of values is
* too narrow.
*/
static inline int linear_range_group_get_win_index(const struct linear_range *r,
size_t r_cnt,
int32_t val_min,
int32_t val_max,
uint16_t *idx)
{
int ret = -EINVAL;
for (size_t i = 0U; (ret != 0) && (i < r_cnt); i++) {
ret = linear_range_get_win_index(&r[i], val_min, val_max, idx);
}
return ret;
}
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* INCLUDE_ZEPHYR_SYS_LINEAR_RANGE_H_ */