LCOV - code coverage report
Current view: top level - zephyr/sys - linear_range.h Hit Total Coverage
Test: new.info Lines: 15 16 93.8 %
Date: 2024-12-22 00:14:23

          Line data    Source code
       1           0 : /*
       2             :  * Copyright (C) 2022, Nordic Semiconductor ASA
       3             :  * SPDX-License-Identifier: Apache-2.0
       4             :  */
       5             : 
       6             : #ifndef INCLUDE_ZEPHYR_SYS_LINEAR_RANGE_H_
       7             : #define INCLUDE_ZEPHYR_SYS_LINEAR_RANGE_H_
       8             : 
       9             : #include <errno.h>
      10             : #include <stdint.h>
      11             : #include <stdlib.h>
      12             : 
      13             : #include <zephyr/sys/util.h>
      14             : 
      15             : #ifdef __cplusplus
      16             : extern "C" {
      17             : #endif
      18             : 
      19             : /**
      20             :  * @defgroup linear_range Linear Range
      21             :  * @ingroup utilities
      22             :  *
      23             :  * The linear range API maps values in a linear range to a range index. A linear
      24             :  * range can be fully defined by four parameters:
      25             :  *
      26             :  * - Minimum value
      27             :  * - Step value
      28             :  * - Minimum index value
      29             :  * - Maximum index value
      30             :  *
      31             :  * For example, in a voltage regulator, supported voltages typically map to a
      32             :  * register index value like this:
      33             :  *
      34             :  * - 1000uV: 0x00
      35             :  * - 1250uV: 0x01
      36             :  * - 1500uV: 0x02
      37             :  * - ...
      38             :  * - 3000uV: 0x08
      39             :  *
      40             :  * In this case, we have:
      41             :  *
      42             :  * - Minimum value: 1000uV
      43             :  * - Step value: 250uV
      44             :  * - Minimum index value: 0x00
      45             :  * - Maximum index value: 0x08
      46             :  *
      47             :  * A linear range may also be constant, that is, step set to zero.
      48             :  *
      49             :  * It is often the case where the same device has discontinuous linear ranges.
      50             :  * The API offers utility functions to deal with groups of linear ranges as
      51             :  * well.
      52             :  *
      53             :  * Implementation uses fixed-width integers. Range is limited to [INT32_MIN,
      54             :  * INT32_MAX], while number of indices is limited to UINT16_MAX.
      55             :  *
      56             :  * Original idea borrowed from Linux.
      57             :  * @{
      58             :  */
      59             : 
      60             : /** @brief Linear range. */
      61           1 : struct linear_range {
      62             :         /** Minimum value. */
      63           1 :         int32_t min;
      64             :         /** Step value. */
      65           1 :         uint32_t step;
      66             :         /** Minimum index (must be <= maximum index). */
      67           1 :         uint16_t min_idx;
      68             :         /** Maximum index (must be >= minimum index). */
      69           1 :         uint16_t max_idx;
      70             : };
      71             : 
      72             : /**
      73             :  * @brief Initializer for @ref linear_range.
      74             :  *
      75             :  * @param _min Minimum value in range.
      76             :  * @param _step Step value.
      77             :  * @param _min_idx Minimum index.
      78             :  * @param _max_idx Maximum index.
      79             :  */
      80           1 : #define LINEAR_RANGE_INIT(_min, _step, _min_idx, _max_idx)                     \
      81             :         {                                                                      \
      82             :                 .min = (_min),                                                 \
      83             :                 .step = (_step),                                               \
      84             :                 .min_idx = (_min_idx),                                         \
      85             :                 .max_idx = (_max_idx),                                         \
      86             :         }
      87             : 
      88             : /**
      89             :  * @brief Obtain the number of values representable in a linear range.
      90             :  *
      91             :  * @param[in] r Linear range instance.
      92             :  *
      93             :  * @return Number of ranges representable by @p r.
      94             :  */
      95           1 : static inline uint32_t linear_range_values_count(const struct linear_range *r)
      96             : {
      97             :         return r->max_idx - r->min_idx + 1U;
      98             : }
      99             : 
     100             : /**
     101             :  * @brief Obtain the number of values representable by a group of linear ranges.
     102             :  *
     103             :  * @param[in] r Array of linear range instances.
     104             :  * @param r_cnt Number of linear range instances.
     105             :  *
     106             :  * @return Number of ranges representable by the @p r group.
     107             :  */
     108           1 : static inline uint32_t linear_range_group_values_count(
     109             :         const struct linear_range *r, size_t r_cnt)
     110             : {
     111             :         uint32_t values = 0U;
     112             : 
     113             :         for (size_t i = 0U; i < r_cnt; i++) {
     114             :                 values += linear_range_values_count(&r[i]);
     115             :         }
     116             : 
     117             :         return values;
     118             : }
     119             : 
     120             : /**
     121             :  * @brief Obtain the maximum value representable by a linear range.
     122             :  *
     123             :  * @param[in] r Linear range instance.
     124             :  *
     125             :  * @return Maximum value representable by @p r.
     126             :  */
     127           1 : static inline int32_t linear_range_get_max_value(const struct linear_range *r)
     128             : {
     129             :         return r->min + (int32_t)(r->step * (r->max_idx - r->min_idx));
     130             : }
     131             : 
     132             : /**
     133             :  * @brief Obtain value given a linear range index.
     134             :  *
     135             :  * @param[in] r Linear range instance.
     136             :  * @param idx Range index.
     137             :  * @param[out] val Where value will be stored.
     138             :  *
     139             :  * @retval 0 If successful
     140             :  * @retval -EINVAL If index is out of range.
     141             :  */
     142           1 : static inline int linear_range_get_value(const struct linear_range *r,
     143             :                                          uint16_t idx, int32_t *val)
     144             : {
     145             :         if ((idx < r->min_idx) || (idx > r->max_idx)) {
     146             :                 return -EINVAL;
     147             :         }
     148             : 
     149             :         *val = r->min + (int32_t)(r->step * (idx - r->min_idx));
     150             : 
     151             :         return 0;
     152             : }
     153             : 
     154             : /**
     155             :  * @brief Obtain value in a group given a linear range index.
     156             :  *
     157             :  * @param[in] r Array of linear range instances.
     158             :  * @param r_cnt Number of linear range instances.
     159             :  * @param idx Range index.
     160             :  * @param[out] val Where value will be stored.
     161             :  *
     162             :  * @retval 0 If successful
     163             :  * @retval -EINVAL If index is out of range.
     164             :  */
     165           1 : static inline int linear_range_group_get_value(const struct linear_range *r,
     166             :                                                size_t r_cnt, uint16_t idx,
     167             :                                                int32_t *val)
     168             : {
     169             :         int ret = -EINVAL;
     170             : 
     171             :         for (size_t i = 0U; (ret != 0) && (i < r_cnt); i++) {
     172             :                 ret = linear_range_get_value(&r[i], idx, val);
     173             :         }
     174             : 
     175             :         return ret;
     176             : }
     177             : 
     178             : /**
     179             :  * @brief Obtain index given a value.
     180             :  *
     181             :  * If the value falls outside the range, the nearest index will be stored and
     182             :  * -ERANGE returned. That is, if the value falls below or above the range, the
     183             :  * index will take the minimum or maximum value, respectively. For constant
     184             :  * ranges, the minimum index will be returned.
     185             :  *
     186             :  * @param[in] r Linear range instance.
     187             :  * @param val Value.
     188             :  * @param[out] idx Where index will be stored.
     189             :  *
     190             :  * @retval 0 If value falls within the range.
     191             :  * @retval -ERANGE If the value falls out of the range.
     192             :  */
     193           1 : static inline int linear_range_get_index(const struct linear_range *r,
     194             :                                          int32_t val, uint16_t *idx)
     195             : {
     196             :         if (val < r->min) {
     197             :                 *idx = r->min_idx;
     198             :                 return -ERANGE;
     199             :         }
     200             : 
     201             :         if (val > linear_range_get_max_value(r)) {
     202             :                 *idx = r->max_idx;
     203             :                 return -ERANGE;
     204             :         }
     205             : 
     206             :         if (r->step == 0U) {
     207             :                 *idx = r->min_idx;
     208             :         } else {
     209             :                 *idx = r->min_idx + DIV_ROUND_UP((uint32_t)(val - r->min),
     210             :                                                  r->step);
     211             :         }
     212             : 
     213             :         return 0;
     214             : }
     215             : 
     216             : /**
     217             :  * @brief Obtain index in a group given a value.
     218             :  *
     219             :  * This function works the same way as linear_range_get_index(), but considering
     220             :  * all ranges in the group.
     221             :  *
     222             :  * @param[in] r Linear range instances.
     223             :  * @param r_cnt Number of linear range instances.
     224             :  * @param val Value.
     225             :  * @param[out] idx Where index will be stored.
     226             :  *
     227             :  * @retval 0 If value falls within the range group.
     228             :  * @retval -ERANGE If the value falls out of the range group.
     229             :  * @retval -EINVAL If input is not valid (i.e. zero groups).
     230             :  */
     231           1 : static inline int linear_range_group_get_index(const struct linear_range *r,
     232             :                                                size_t r_cnt, int32_t val,
     233             :                                                uint16_t *idx)
     234             : {
     235             :         for (size_t i = 0U; i < r_cnt; i++) {
     236             :                 if ((val > linear_range_get_max_value(&r[i])) &&
     237             :                     (i < (r_cnt - 1U))) {
     238             :                         continue;
     239             :                 }
     240             : 
     241             :                 return linear_range_get_index(&r[i], val, idx);
     242             :         }
     243             : 
     244             :         return -EINVAL;
     245             : }
     246             : 
     247             : /**
     248             :  * @brief Obtain index given a window of values.
     249             :  *
     250             :  * If the window of values does not intersect with the range, -EINVAL will be
     251             :  * returned. If intersection is partial (any of the window edges does not
     252             :  * intersect), the nearest index will be stored and -ERANGE returned.
     253             :  *
     254             :  * @param[in] r Linear range instance.
     255             :  * @param val_min Minimum window value.
     256             :  * @param val_max Maximum window value.
     257             :  * @param[out] idx Where index will be stored.
     258             :  *
     259             :  * @retval 0 If a valid index is found within linear range.
     260             :  * @retval -ERANGE If the given window of values falls partially out of the
     261             :  * linear range.
     262             :  * @retval -EINVAL If the given window of values does not intersect with the
     263             :  * linear range or if they are too narrow.
     264             :  */
     265           1 : static inline int linear_range_get_win_index(const struct linear_range *r,
     266             :                                              int32_t val_min, int32_t val_max,
     267             :                                              uint16_t *idx)
     268             : {
     269             :         int32_t r_max = linear_range_get_max_value(r);
     270             : 
     271             :         if ((val_max < r->min) || (val_min > r_max)) {
     272             :                 return -EINVAL;
     273             :         }
     274             : 
     275             :         if (val_min < r->min) {
     276             :                 *idx = r->min_idx;
     277             :                 return -ERANGE;
     278             :         }
     279             : 
     280             :         if (val_max > r_max) {
     281             :                 *idx = r->max_idx;
     282             :                 return -ERANGE;
     283             :         }
     284             : 
     285             :         if (r->step == 0U) {
     286             :                 *idx = r->min_idx;
     287             :                 return 0;
     288             :         }
     289             : 
     290             :         *idx = r->min_idx + DIV_ROUND_UP((uint32_t)(val_min - r->min), r->step);
     291             :         if ((r->min + r->step * (*idx - r->min_idx)) > val_max) {
     292             :                 return -EINVAL;
     293             :         }
     294             : 
     295             :         return 0;
     296             : }
     297             : 
     298             : /**
     299             :  * @brief Obtain index in a group given a value that must be within a window of
     300             :  * values.
     301             :  *
     302             :  * This function works the same way as linear_range_get_win_index(), but
     303             :  * considering all ranges in the group.
     304             :  *
     305             :  * @param[in] r Linear range instances.
     306             :  * @param r_cnt Number of linear range instances.
     307             :  * @param val_min Minimum window value.
     308             :  * @param val_max Maximum window value.
     309             :  * @param[out] idx Where index will be stored.
     310             :  *
     311             :  * @retval 0 If a valid index is found within linear range group.
     312             :  * @retval -ERANGE If the given window of values falls partially out of the
     313             :  * linear range group.
     314             :  * @retval -EINVAL If the given window of values does not intersect with the
     315             :  * linear range group, if they are too narrow, or if input is invalid (i.e.
     316             :  * zero groups).
     317             :  */
     318           1 : static inline int linear_range_group_get_win_index(const struct linear_range *r,
     319             :                                                    size_t r_cnt,
     320             :                                                    int32_t val_min,
     321             :                                                    int32_t val_max,
     322             :                                                    uint16_t *idx)
     323             : {
     324             :         for (size_t i = 0U; i < r_cnt; i++) {
     325             :                 if (val_min > linear_range_get_max_value(&r[i])) {
     326             :                         continue;
     327             :                 }
     328             : 
     329             :                 return linear_range_get_win_index(&r[i], val_min, val_max, idx);
     330             :         }
     331             : 
     332             :         return -EINVAL;
     333             : }
     334             : 
     335             : /** @} */
     336             : 
     337             : #ifdef __cplusplus
     338             : }
     339             : #endif
     340             : 
     341             : #endif /* INCLUDE_ZEPHYR_SYS_LINEAR_RANGE_H_ */

Generated by: LCOV version 1.14