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_ */
|