1 /*
2  * Copyright (c) 2023 Google LLC
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include <zephyr/drivers/emul.h>
7 #include <zephyr/drivers/sensor.h>
8 #include <zephyr/drivers/sensor_attribute_types.h>
9 
10 #include <stdint.h>
11 
12 /**
13  * @brief Sensor emulator backend API
14  * @defgroup sensor_emulator_backend Sensor emulator backend API
15  * @ingroup io_interfaces
16  * @{
17  */
18 
19 /**
20  * @cond INTERNAL_HIDDEN
21  *
22  * These are for internal use only, so skip these in public documentation.
23  */
24 
25 /**
26  * @brief Collection of function pointers implementing a common backend API for sensor emulators
27  */
28 __subsystem struct emul_sensor_driver_api {
29 	/** Sets a given fractional value for a given sensor channel. */
30 	int (*set_channel)(const struct emul *target, struct sensor_chan_spec ch,
31 			   const q31_t *value, int8_t shift);
32 	/** Retrieve a range of sensor values to use with test. */
33 	int (*get_sample_range)(const struct emul *target, struct sensor_chan_spec ch, q31_t *lower,
34 				q31_t *upper, q31_t *epsilon, int8_t *shift);
35 	/** Set the attribute value(s) of a given channel. */
36 	int (*set_attribute)(const struct emul *target, struct sensor_chan_spec ch,
37 			     enum sensor_attribute attribute, const void *value);
38 	/** Get metadata about an attribute. */
39 	int (*get_attribute_metadata)(const struct emul *target, struct sensor_chan_spec ch,
40 				      enum sensor_attribute attribute, q31_t *min, q31_t *max,
41 				      q31_t *increment, int8_t *shift);
42 };
43 /**
44  * @endcond
45  */
46 
47 /**
48  * @brief Check if a given sensor emulator supports the backend API
49  *
50  * @param target Pointer to emulator instance to query
51  *
52  * @return True if supported, false if unsupported or if \p target is NULL.
53  */
emul_sensor_backend_is_supported(const struct emul * target)54 static inline bool emul_sensor_backend_is_supported(const struct emul *target)
55 {
56 	return target && target->backend_api;
57 }
58 
59 /**
60  * @brief Set an expected value for a given channel on a given sensor emulator
61  *
62  * @param target Pointer to emulator instance to operate on
63  * @param ch Sensor channel to set expected value for
64  * @param value Expected value in fixed-point format using standard SI unit for sensor type
65  * @param shift Shift value (scaling factor) applied to \p value
66  *
67  * @return 0 if successful
68  * @return -ENOTSUP if no backend API or if channel not supported by emul
69  * @return -ERANGE if provided value is not in the sensor's supported range
70  */
emul_sensor_backend_set_channel(const struct emul * target,struct sensor_chan_spec ch,const q31_t * value,int8_t shift)71 static inline int emul_sensor_backend_set_channel(const struct emul *target,
72 						  struct sensor_chan_spec ch, const q31_t *value,
73 						  int8_t shift)
74 {
75 	if (!target || !target->backend_api) {
76 		return -ENOTSUP;
77 	}
78 
79 	struct emul_sensor_driver_api *api = (struct emul_sensor_driver_api *)target->backend_api;
80 
81 	if (api->set_channel) {
82 		return api->set_channel(target, ch, value, shift);
83 	}
84 	return -ENOTSUP;
85 }
86 
87 /**
88  * @brief Query an emulator for a channel's supported sample value range and tolerance
89  *
90  * @param target Pointer to emulator instance to operate on
91  * @param ch The channel to request info for. If \p ch is unsupported, return `-ENOTSUP`
92  * @param[out] lower Minimum supported sample value in SI units, fixed-point format
93  * @param[out] upper Maximum supported sample value in SI units, fixed-point format
94  * @param[out] epsilon Tolerance to use comparing expected and actual values to account for rounding
95  *             and sensor precision issues. This can usually be set to the minimum sample value step
96  *             size. Uses SI units and fixed-point format.
97  * @param[out] shift The shift value (scaling factor) associated with \p lower, \p upper, and
98  *             \p epsilon.
99  *
100  * @return 0 if successful
101  * @return -ENOTSUP if no backend API or if channel not supported by emul
102  *
103  */
emul_sensor_backend_get_sample_range(const struct emul * target,struct sensor_chan_spec ch,q31_t * lower,q31_t * upper,q31_t * epsilon,int8_t * shift)104 static inline int emul_sensor_backend_get_sample_range(const struct emul *target,
105 						       struct sensor_chan_spec ch, q31_t *lower,
106 						       q31_t *upper, q31_t *epsilon, int8_t *shift)
107 {
108 	if (!target || !target->backend_api) {
109 		return -ENOTSUP;
110 	}
111 
112 	struct emul_sensor_driver_api *api = (struct emul_sensor_driver_api *)target->backend_api;
113 
114 	if (api->get_sample_range) {
115 		return api->get_sample_range(target, ch, lower, upper, epsilon, shift);
116 	}
117 	return -ENOTSUP;
118 }
119 
120 /**
121  * @brief Set the emulator's attribute values
122  *
123  * @param[in] target Pointer to emulator instance to operate on
124  * @param[in] ch The channel to request info for. If \p ch is unsupported, return `-ENOTSUP`
125  * @param[in] attribute The attribute to set
126  * @param[in] value the value to use (cast according to the channel/attribute pair)
127  * @return 0 is successful
128  * @return < 0 on error
129  */
emul_sensor_backend_set_attribute(const struct emul * target,struct sensor_chan_spec ch,enum sensor_attribute attribute,const void * value)130 static inline int emul_sensor_backend_set_attribute(const struct emul *target,
131 						    struct sensor_chan_spec ch,
132 						    enum sensor_attribute attribute,
133 						    const void *value)
134 {
135 	if (!target || !target->backend_api) {
136 		return -ENOTSUP;
137 	}
138 
139 	struct emul_sensor_driver_api *api = (struct emul_sensor_driver_api *)target->backend_api;
140 
141 	if (api->set_attribute == NULL) {
142 		return -ENOTSUP;
143 	}
144 	return api->set_attribute(target, ch, attribute, value);
145 }
146 
147 /**
148  * @brief Get metadata about an attribute.
149  *
150  * Information provided by this function includes the minimum/maximum values of the attribute as
151  * well as the increment (value per LSB) which can be used as an epsilon when comparing results.
152  *
153  * @param[in] target Pointer to emulator instance to operate on
154  * @param[in] ch The channel to request info for. If \p ch is unsupported, return '-ENOTSUP'
155  * @param[in] attribute The attribute to request info for. If \p attribute is unsupported, return
156  *   '-ENOTSUP'
157  * @param[out] min The minimum value the attribute can be set to
158  * @param[out] max The maximum value the attribute can be set to
159  * @param[out] increment The value that the attribute increases by for every LSB
160  * @param[out] shift The shift for \p min, \p max, and \p increment
161  * @return 0 on SUCCESS
162  * @return < 0 on error
163  */
emul_sensor_backend_get_attribute_metadata(const struct emul * target,struct sensor_chan_spec ch,enum sensor_attribute attribute,q31_t * min,q31_t * max,q31_t * increment,int8_t * shift)164 static inline int emul_sensor_backend_get_attribute_metadata(const struct emul *target,
165 							     struct sensor_chan_spec ch,
166 							     enum sensor_attribute attribute,
167 							     q31_t *min, q31_t *max,
168 							     q31_t *increment, int8_t *shift)
169 {
170 	if (!target || !target->backend_api) {
171 		return -ENOTSUP;
172 	}
173 
174 	struct emul_sensor_driver_api *api = (struct emul_sensor_driver_api *)target->backend_api;
175 
176 	if (api->get_attribute_metadata == NULL) {
177 		return -ENOTSUP;
178 	}
179 	return api->get_attribute_metadata(target, ch, attribute, min, max, increment, shift);
180 }
181 
182 /**
183  * @}
184  */
185