/** * @file drivers/sensor.h * * @brief Public APIs for the sensor driver. */ /* * Copyright (c) 2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_H_ #define ZEPHYR_INCLUDE_DRIVERS_SENSOR_H_ /** * @brief Sensor Interface * @defgroup sensor_interface Sensor Interface * @since 1.2 * @version 1.0.0 * @ingroup io_interfaces * @{ */ #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /** * @brief Representation of a sensor readout value. * * The value is represented as having an integer and a fractional part, * and can be obtained using the formula val1 + val2 * 10^(-6). Negative * values also adhere to the above formula, but may need special attention. * Here are some examples of the value representation: * * 0.5: val1 = 0, val2 = 500000 * -0.5: val1 = 0, val2 = -500000 * -1.0: val1 = -1, val2 = 0 * -1.5: val1 = -1, val2 = -500000 */ struct sensor_value { /** Integer part of the value. */ int32_t val1; /** Fractional part of the value (in one-millionth parts). */ int32_t val2; }; /** * @brief Sensor channels. */ enum sensor_channel { /** Acceleration on the X axis, in m/s^2. */ SENSOR_CHAN_ACCEL_X, /** Acceleration on the Y axis, in m/s^2. */ SENSOR_CHAN_ACCEL_Y, /** Acceleration on the Z axis, in m/s^2. */ SENSOR_CHAN_ACCEL_Z, /** Acceleration on the X, Y and Z axes. */ SENSOR_CHAN_ACCEL_XYZ, /** Angular velocity around the X axis, in radians/s. */ SENSOR_CHAN_GYRO_X, /** Angular velocity around the Y axis, in radians/s. */ SENSOR_CHAN_GYRO_Y, /** Angular velocity around the Z axis, in radians/s. */ SENSOR_CHAN_GYRO_Z, /** Angular velocity around the X, Y and Z axes. */ SENSOR_CHAN_GYRO_XYZ, /** Magnetic field on the X axis, in Gauss. */ SENSOR_CHAN_MAGN_X, /** Magnetic field on the Y axis, in Gauss. */ SENSOR_CHAN_MAGN_Y, /** Magnetic field on the Z axis, in Gauss. */ SENSOR_CHAN_MAGN_Z, /** Magnetic field on the X, Y and Z axes. */ SENSOR_CHAN_MAGN_XYZ, /** Device die temperature in degrees Celsius. */ SENSOR_CHAN_DIE_TEMP, /** Ambient temperature in degrees Celsius. */ SENSOR_CHAN_AMBIENT_TEMP, /** Pressure in kilopascal. */ SENSOR_CHAN_PRESS, /** * Proximity. Adimensional. A value of 1 indicates that an * object is close. */ SENSOR_CHAN_PROX, /** Humidity, in percent. */ SENSOR_CHAN_HUMIDITY, /** Illuminance in visible spectrum, in lux. */ SENSOR_CHAN_LIGHT, /** Illuminance in infra-red spectrum, in lux. */ SENSOR_CHAN_IR, /** Illuminance in red spectrum, in lux. */ SENSOR_CHAN_RED, /** Illuminance in green spectrum, in lux. */ SENSOR_CHAN_GREEN, /** Illuminance in blue spectrum, in lux. */ SENSOR_CHAN_BLUE, /** Altitude, in meters */ SENSOR_CHAN_ALTITUDE, /** 1.0 micro-meters Particulate Matter, in ug/m^3 */ SENSOR_CHAN_PM_1_0, /** 2.5 micro-meters Particulate Matter, in ug/m^3 */ SENSOR_CHAN_PM_2_5, /** 10 micro-meters Particulate Matter, in ug/m^3 */ SENSOR_CHAN_PM_10, /** Distance. From sensor to target, in meters */ SENSOR_CHAN_DISTANCE, /** CO2 level, in parts per million (ppm) **/ SENSOR_CHAN_CO2, /** O2 level, in parts per million (ppm) **/ SENSOR_CHAN_O2, /** VOC level, in parts per billion (ppb) **/ SENSOR_CHAN_VOC, /** Gas sensor resistance in ohms. */ SENSOR_CHAN_GAS_RES, /** Voltage, in volts **/ SENSOR_CHAN_VOLTAGE, /** Current Shunt Voltage in milli-volts **/ SENSOR_CHAN_VSHUNT, /** Current, in amps **/ SENSOR_CHAN_CURRENT, /** Power in watts **/ SENSOR_CHAN_POWER, /** Resistance , in Ohm **/ SENSOR_CHAN_RESISTANCE, /** Angular rotation, in degrees */ SENSOR_CHAN_ROTATION, /** Position change on the X axis, in points. */ SENSOR_CHAN_POS_DX, /** Position change on the Y axis, in points. */ SENSOR_CHAN_POS_DY, /** Position change on the Z axis, in points. */ SENSOR_CHAN_POS_DZ, /** Position change on the X, Y and Z axis, in points. */ SENSOR_CHAN_POS_DXYZ, /** Revolutions per minute, in RPM. */ SENSOR_CHAN_RPM, /** Voltage, in volts **/ SENSOR_CHAN_GAUGE_VOLTAGE, /** Average current, in amps **/ SENSOR_CHAN_GAUGE_AVG_CURRENT, /** Standby current, in amps **/ SENSOR_CHAN_GAUGE_STDBY_CURRENT, /** Max load current, in amps **/ SENSOR_CHAN_GAUGE_MAX_LOAD_CURRENT, /** Gauge temperature **/ SENSOR_CHAN_GAUGE_TEMP, /** State of charge measurement in % **/ SENSOR_CHAN_GAUGE_STATE_OF_CHARGE, /** Full Charge Capacity in mAh **/ SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY, /** Remaining Charge Capacity in mAh **/ SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY, /** Nominal Available Capacity in mAh **/ SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY, /** Full Available Capacity in mAh **/ SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY, /** Average power in mW **/ SENSOR_CHAN_GAUGE_AVG_POWER, /** State of health measurement in % **/ SENSOR_CHAN_GAUGE_STATE_OF_HEALTH, /** Time to empty in minutes **/ SENSOR_CHAN_GAUGE_TIME_TO_EMPTY, /** Time to full in minutes **/ SENSOR_CHAN_GAUGE_TIME_TO_FULL, /** Cycle count (total number of charge/discharge cycles) **/ SENSOR_CHAN_GAUGE_CYCLE_COUNT, /** Design voltage of cell in V (max voltage)*/ SENSOR_CHAN_GAUGE_DESIGN_VOLTAGE, /** Desired voltage of cell in V (nominal voltage) */ SENSOR_CHAN_GAUGE_DESIRED_VOLTAGE, /** Desired charging current in mA */ SENSOR_CHAN_GAUGE_DESIRED_CHARGING_CURRENT, /** Game Rotation Vector (unit quaternion components X/Y/Z/W) */ SENSOR_CHAN_GAME_ROTATION_VECTOR, /** Gravity Vector (X/Y/Z components in m/s^2) */ SENSOR_CHAN_GRAVITY_VECTOR, /** Gyroscope bias (X/Y/Z components in radians/s) */ SENSOR_CHAN_GBIAS_XYZ, /** All channels. */ SENSOR_CHAN_ALL, /** * Number of all common sensor channels. */ SENSOR_CHAN_COMMON_COUNT, /** * This and higher values are sensor specific. * Refer to the sensor header file. */ SENSOR_CHAN_PRIV_START = SENSOR_CHAN_COMMON_COUNT, /** * Maximum value describing a sensor channel type. */ SENSOR_CHAN_MAX = INT16_MAX, }; /** * @brief Sensor trigger types. */ enum sensor_trigger_type { /** * Timer-based trigger, useful when the sensor does not have an * interrupt line. */ SENSOR_TRIG_TIMER, /** Trigger fires whenever new data is ready. */ SENSOR_TRIG_DATA_READY, /** * Trigger fires when the selected channel varies significantly. * This includes any-motion detection when the channel is * acceleration or gyro. If detection is based on slope between * successive channel readings, the slope threshold is configured * via the @ref SENSOR_ATTR_SLOPE_TH and @ref SENSOR_ATTR_SLOPE_DUR * attributes. */ SENSOR_TRIG_DELTA, /** Trigger fires when a near/far event is detected. */ SENSOR_TRIG_NEAR_FAR, /** * Trigger fires when channel reading transitions configured * thresholds. The thresholds are configured via the @ref * SENSOR_ATTR_LOWER_THRESH, @ref SENSOR_ATTR_UPPER_THRESH, and * @ref SENSOR_ATTR_HYSTERESIS attributes. */ SENSOR_TRIG_THRESHOLD, /** Trigger fires when a single tap is detected. */ SENSOR_TRIG_TAP, /** Trigger fires when a double tap is detected. */ SENSOR_TRIG_DOUBLE_TAP, /** Trigger fires when a free fall is detected. */ SENSOR_TRIG_FREEFALL, /** Trigger fires when motion is detected. */ SENSOR_TRIG_MOTION, /** Trigger fires when no motion has been detected for a while. */ SENSOR_TRIG_STATIONARY, /** Trigger fires when the FIFO watermark has been reached. */ SENSOR_TRIG_FIFO_WATERMARK, /** Trigger fires when the FIFO becomes full. */ SENSOR_TRIG_FIFO_FULL, /** * Number of all common sensor triggers. */ SENSOR_TRIG_COMMON_COUNT, /** * This and higher values are sensor specific. * Refer to the sensor header file. */ SENSOR_TRIG_PRIV_START = SENSOR_TRIG_COMMON_COUNT, /** * Maximum value describing a sensor trigger type. */ SENSOR_TRIG_MAX = INT16_MAX, }; /** * @brief Sensor trigger spec. */ struct sensor_trigger { /** Trigger type. */ enum sensor_trigger_type type; /** Channel the trigger is set on. */ enum sensor_channel chan; }; /** * @brief Sensor attribute types. */ enum sensor_attribute { /** * Sensor sampling frequency, i.e. how many times a second the * sensor takes a measurement. */ SENSOR_ATTR_SAMPLING_FREQUENCY, /** Lower threshold for trigger. */ SENSOR_ATTR_LOWER_THRESH, /** Upper threshold for trigger. */ SENSOR_ATTR_UPPER_THRESH, /** Threshold for any-motion (slope) trigger. */ SENSOR_ATTR_SLOPE_TH, /** * Duration for which the slope values needs to be * outside the threshold for the trigger to fire. */ SENSOR_ATTR_SLOPE_DUR, /* Hysteresis for trigger thresholds. */ SENSOR_ATTR_HYSTERESIS, /** Oversampling factor */ SENSOR_ATTR_OVERSAMPLING, /** Sensor range, in SI units. */ SENSOR_ATTR_FULL_SCALE, /** * The sensor value returned will be altered by the amount indicated by * offset: final_value = sensor_value + offset. */ SENSOR_ATTR_OFFSET, /** * Calibration target. This will be used by the internal chip's * algorithms to calibrate itself on a certain axis, or all of them. */ SENSOR_ATTR_CALIB_TARGET, /** Configure the operating modes of a sensor. */ SENSOR_ATTR_CONFIGURATION, /** Set a calibration value needed by a sensor. */ SENSOR_ATTR_CALIBRATION, /** Enable/disable sensor features */ SENSOR_ATTR_FEATURE_MASK, /** Alert threshold or alert enable/disable */ SENSOR_ATTR_ALERT, /** Free-fall duration represented in milliseconds. * If the sampling frequency is changed during runtime, * this attribute should be set to adjust freefall duration * to the new sampling frequency. */ SENSOR_ATTR_FF_DUR, /** Hardware batch duration in ticks */ SENSOR_ATTR_BATCH_DURATION, /* Configure the gain of a sensor. */ SENSOR_ATTR_GAIN, /* Configure the resolution of a sensor. */ SENSOR_ATTR_RESOLUTION, /** * Number of all common sensor attributes. */ SENSOR_ATTR_COMMON_COUNT, /** * This and higher values are sensor specific. * Refer to the sensor header file. */ SENSOR_ATTR_PRIV_START = SENSOR_ATTR_COMMON_COUNT, /** * Maximum value describing a sensor attribute type. */ SENSOR_ATTR_MAX = INT16_MAX, }; /** * @typedef sensor_trigger_handler_t * @brief Callback API upon firing of a trigger * * @param dev Pointer to the sensor device * @param trigger The trigger */ typedef void (*sensor_trigger_handler_t)(const struct device *dev, const struct sensor_trigger *trigger); /** * @typedef sensor_attr_set_t * @brief Callback API upon setting a sensor's attributes * * See sensor_attr_set() for argument description */ typedef int (*sensor_attr_set_t)(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val); /** * @typedef sensor_attr_get_t * @brief Callback API upon getting a sensor's attributes * * See sensor_attr_get() for argument description */ typedef int (*sensor_attr_get_t)(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, struct sensor_value *val); /** * @typedef sensor_trigger_set_t * @brief Callback API for setting a sensor's trigger and handler * * See sensor_trigger_set() for argument description */ typedef int (*sensor_trigger_set_t)(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler); /** * @typedef sensor_sample_fetch_t * @brief Callback API for fetching data from a sensor * * See sensor_sample_fetch() for argument description */ typedef int (*sensor_sample_fetch_t)(const struct device *dev, enum sensor_channel chan); /** * @typedef sensor_channel_get_t * @brief Callback API for getting a reading from a sensor * * See sensor_channel_get() for argument description */ typedef int (*sensor_channel_get_t)(const struct device *dev, enum sensor_channel chan, struct sensor_value *val); /** * @brief Sensor Channel Specification * * A sensor channel specification is a unique identifier per sensor device describing * a measurement channel. * * @note Typically passed by value as the size of a sensor_chan_spec is a single word. */ struct sensor_chan_spec { uint16_t chan_type; /**< A sensor channel type */ uint16_t chan_idx; /**< A sensor channel index */ }; /** @cond INTERNAL_HIDDEN */ /* Ensure sensor_chan_spec is sensibly sized to pass by value */ BUILD_ASSERT(sizeof(struct sensor_chan_spec) <= sizeof(uintptr_t), "sensor_chan_spec size should be equal or less than the size of a machine word"); /** @endcond */ /** * @brief Check if channel specs are equivalent * * @param chan_spec0 First chan spec * @param chan_spec1 Second chan spec * @retval true If equivalent * @retval false If not equivalent */ static inline bool sensor_chan_spec_eq(struct sensor_chan_spec chan_spec0, struct sensor_chan_spec chan_spec1) { return chan_spec0.chan_type == chan_spec1.chan_type && chan_spec0.chan_idx == chan_spec1.chan_idx; } /** * @brief Decodes a single raw data buffer * * Data buffers are provided on the @ref rtio context that's supplied to * @ref sensor_read. */ struct sensor_decoder_api { /** * @brief Get the number of frames in the current buffer. * * @param[in] buffer The buffer provided on the @ref rtio context. * @param[in] channel The channel to get the count for * @param[out] frame_count The number of frames on the buffer (at least 1) * @return 0 on success * @return -ENOTSUP if the channel/channel_idx aren't found */ int (*get_frame_count)(const uint8_t *buffer, struct sensor_chan_spec channel, uint16_t *frame_count); /** * @brief Get the size required to decode a given channel * * When decoding a single frame, use @p base_size. For every additional frame, add another * @p frame_size. As an example, to decode 3 frames use: 'base_size + 2 * frame_size'. * * @param[in] channel The channel to query * @param[out] base_size The size of decoding the first frame * @param[out] frame_size The additional size of every additional frame * @return 0 on success * @return -ENOTSUP if the channel is not supported */ int (*get_size_info)(struct sensor_chan_spec channel, size_t *base_size, size_t *frame_size); /** * @brief Decode up to @p max_count samples from the buffer * * Decode samples of channel @ref sensor_channel across multiple frames. If there exist * multiple instances of the same channel, @p channel_index is used to differentiate them. * As an example, assume a sensor provides 2 distance measurements: * * @code{.c} * // Decode the first channel instance of 'distance' * decoder->decode(buffer, SENSOR_CHAN_DISTANCE, 0, &fit, 5, out); * ... * * // Decode the second channel instance of 'distance' * decoder->decode(buffer, SENSOR_CHAN_DISTANCE, 1, &fit, 5, out); * @endcode * * @param[in] buffer The buffer provided on the @ref rtio context * @param[in] channel The channel to decode * @param[in,out] fit The current frame iterator * @param[in] max_count The maximum number of channels to decode. * @param[out] data_out The decoded data * @return 0 no more samples to decode * @return >0 the number of decoded frames * @return <0 on error */ int (*decode)(const uint8_t *buffer, struct sensor_chan_spec channel, uint32_t *fit, uint16_t max_count, void *data_out); /** * @brief Check if the given trigger type is present * * @param[in] buffer The buffer provided on the @ref rtio context * @param[in] trigger The trigger type in question * @return Whether the trigger is present in the buffer */ bool (*has_trigger)(const uint8_t *buffer, enum sensor_trigger_type trigger); }; /** * @brief Used for iterating over the data frames via the sensor_decoder_api. * * Example usage: * * @code(.c) * struct sensor_decode_context ctx = SENSOR_DECODE_CONTEXT_INIT( * decoder, buffer, SENSOR_CHAN_ACCEL_XYZ, 0); * * while (true) { * struct sensor_three_axis_data accel_out_data; * * num_decoded_channels = sensor_decode(ctx, &accel_out_data, 1); * * if (num_decoded_channels <= 0) { * printk("Done decoding buffer\n"); * break; * } * * printk("Decoded (%" PRId32 ", %" PRId32 ", %" PRId32 ")\n", accel_out_data.readings[0].x, * accel_out_data.readings[0].y, accel_out_data.readings[0].z); * } * @endcode */ struct sensor_decode_context { const struct sensor_decoder_api *decoder; const uint8_t *buffer; struct sensor_chan_spec channel; uint32_t fit; }; /** * @brief Initialize a sensor_decode_context */ #define SENSOR_DECODE_CONTEXT_INIT(decoder_, buffer_, channel_type_, channel_index_) \ { \ .decoder = (decoder_), \ .buffer = (buffer_), \ .channel = {.chan_type = (channel_type_), .chan_idx = (channel_index_)}, \ .fit = 0, \ } /** * @brief Decode N frames using a sensor_decode_context * * @param[in,out] ctx The context to use for decoding * @param[out] out The output buffer * @param[in] max_count Maximum number of frames to decode * @return The decode result from sensor_decoder_api's decode function */ static inline int sensor_decode(struct sensor_decode_context *ctx, void *out, uint16_t max_count) { return ctx->decoder->decode(ctx->buffer, ctx->channel, &ctx->fit, max_count, out); } int sensor_natively_supported_channel_size_info(struct sensor_chan_spec channel, size_t *base_size, size_t *frame_size); /** * @typedef sensor_get_decoder_t * @brief Get the decoder associate with the given device * * @see sensor_get_decoder for more details */ typedef int (*sensor_get_decoder_t)(const struct device *dev, const struct sensor_decoder_api **api); /** * @brief Options for what to do with the associated data when a trigger is consumed */ enum sensor_stream_data_opt { /** @brief Include whatever data is associated with the trigger */ SENSOR_STREAM_DATA_INCLUDE = 0, /** @brief Do nothing with the associated trigger data, it may be consumed later */ SENSOR_STREAM_DATA_NOP = 1, /** @brief Flush/clear whatever data is associated with the trigger */ SENSOR_STREAM_DATA_DROP = 2, }; struct sensor_stream_trigger { enum sensor_trigger_type trigger; enum sensor_stream_data_opt opt; }; #define SENSOR_STREAM_TRIGGER_PREP(_trigger, _opt) \ { \ .trigger = (_trigger), .opt = (_opt), \ } /* * Internal data structure used to store information about the IODevice for async reading and * streaming sensor data. */ struct sensor_read_config { const struct device *sensor; const bool is_streaming; union { struct sensor_chan_spec *const channels; struct sensor_stream_trigger *const triggers; }; size_t count; const size_t max; }; /** * @brief Define a reading instance of a sensor * * Use this macro to generate a @ref rtio_iodev for reading specific channels. Example: * * @code(.c) * SENSOR_DT_READ_IODEV(icm42688_accelgyro, DT_NODELABEL(icm42688), * { SENSOR_CHAN_ACCEL_XYZ, 0 }, * { SENSOR_CHAN_GYRO_XYZ, 0 }); * * int main(void) { * sensor_read_async_mempool(&icm42688_accelgyro, &rtio); * } * @endcode */ #define SENSOR_DT_READ_IODEV(name, dt_node, ...) \ static struct sensor_chan_spec _CONCAT(__channel_array_, name)[] = {__VA_ARGS__}; \ static struct sensor_read_config _CONCAT(__sensor_read_config_, name) = { \ .sensor = DEVICE_DT_GET(dt_node), \ .is_streaming = false, \ .channels = _CONCAT(__channel_array_, name), \ .count = ARRAY_SIZE(_CONCAT(__channel_array_, name)), \ .max = ARRAY_SIZE(_CONCAT(__channel_array_, name)), \ }; \ RTIO_IODEV_DEFINE(name, &__sensor_iodev_api, _CONCAT(&__sensor_read_config_, name)) /** * @brief Define a stream instance of a sensor * * Use this macro to generate a @ref rtio_iodev for starting a stream that's triggered by specific * interrupts. Example: * * @code(.c) * SENSOR_DT_STREAM_IODEV(imu_stream, DT_ALIAS(imu), * {SENSOR_TRIG_FIFO_WATERMARK, SENSOR_STREAM_DATA_INCLUDE}, * {SENSOR_TRIG_FIFO_FULL, SENSOR_STREAM_DATA_NOP}); * * int main(void) { * struct rtio_sqe *handle; * sensor_stream(&imu_stream, &rtio, NULL, &handle); * k_msleep(1000); * rtio_sqe_cancel(handle); * } * @endcode */ #define SENSOR_DT_STREAM_IODEV(name, dt_node, ...) \ static struct sensor_stream_trigger _CONCAT(__trigger_array_, name)[] = {__VA_ARGS__}; \ static struct sensor_read_config _CONCAT(__sensor_read_config_, name) = { \ .sensor = DEVICE_DT_GET(dt_node), \ .is_streaming = true, \ .triggers = _CONCAT(__trigger_array_, name), \ .count = ARRAY_SIZE(_CONCAT(__trigger_array_, name)), \ .max = ARRAY_SIZE(_CONCAT(__trigger_array_, name)), \ }; \ RTIO_IODEV_DEFINE(name, &__sensor_iodev_api, &_CONCAT(__sensor_read_config_, name)) /* Used to submit an RTIO sqe to the sensor's iodev */ typedef void (*sensor_submit_t)(const struct device *sensor, struct rtio_iodev_sqe *sqe); /* The default decoder API */ extern const struct sensor_decoder_api __sensor_default_decoder; /* The default sensor iodev API */ extern const struct rtio_iodev_api __sensor_iodev_api; __subsystem struct sensor_driver_api { sensor_attr_set_t attr_set; sensor_attr_get_t attr_get; sensor_trigger_set_t trigger_set; sensor_sample_fetch_t sample_fetch; sensor_channel_get_t channel_get; sensor_get_decoder_t get_decoder; sensor_submit_t submit; }; /** * @brief Set an attribute for a sensor * * @param dev Pointer to the sensor device * @param chan The channel the attribute belongs to, if any. Some * attributes may only be set for all channels of a device, depending on * device capabilities. * @param attr The attribute to set * @param val The value to set the attribute to * * @return 0 if successful, negative errno code if failure. */ __syscall int sensor_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val); static inline int z_impl_sensor_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { const struct sensor_driver_api *api = (const struct sensor_driver_api *)dev->api; if (api->attr_set == NULL) { return -ENOSYS; } return api->attr_set(dev, chan, attr, val); } /** * @brief Get an attribute for a sensor * * @param dev Pointer to the sensor device * @param chan The channel the attribute belongs to, if any. Some * attributes may only be set for all channels of a device, depending on * device capabilities. * @param attr The attribute to get * @param val Pointer to where to store the attribute * * @return 0 if successful, negative errno code if failure. */ __syscall int sensor_attr_get(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, struct sensor_value *val); static inline int z_impl_sensor_attr_get(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, struct sensor_value *val) { const struct sensor_driver_api *api = (const struct sensor_driver_api *)dev->api; if (api->attr_get == NULL) { return -ENOSYS; } return api->attr_get(dev, chan, attr, val); } /** * @brief Activate a sensor's trigger and set the trigger handler * * The handler will be called from a thread, so I2C or SPI operations are * safe. However, the thread's stack is limited and defined by the * driver. It is currently up to the caller to ensure that the handler * does not overflow the stack. * * The user-allocated trigger will be stored by the driver as a pointer, rather * than a copy, and passed back to the handler. This enables the handler to use * CONTAINER_OF to retrieve a context pointer when the trigger is embedded in a * larger struct and requires that the trigger is not allocated on the stack. * * @funcprops \supervisor * * @param dev Pointer to the sensor device * @param trig The trigger to activate * @param handler The function that should be called when the trigger * fires * * @return 0 if successful, negative errno code if failure. */ static inline int sensor_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { const struct sensor_driver_api *api = (const struct sensor_driver_api *)dev->api; if (api->trigger_set == NULL) { return -ENOSYS; } return api->trigger_set(dev, trig, handler); } /** * @brief Fetch a sample from the sensor and store it in an internal * driver buffer * * Read all of a sensor's active channels and, if necessary, perform any * additional operations necessary to make the values useful. The user * may then get individual channel values by calling @ref * sensor_channel_get. * * The function blocks until the fetch operation is complete. * * Since the function communicates with the sensor device, it is unsafe * to call it in an ISR if the device is connected via I2C or SPI. * * @param dev Pointer to the sensor device * * @return 0 if successful, negative errno code if failure. */ __syscall int sensor_sample_fetch(const struct device *dev); static inline int z_impl_sensor_sample_fetch(const struct device *dev) { const struct sensor_driver_api *api = (const struct sensor_driver_api *)dev->api; return api->sample_fetch(dev, SENSOR_CHAN_ALL); } /** * @brief Fetch a sample from the sensor and store it in an internal * driver buffer * * Read and compute compensation for one type of sensor data (magnetometer, * accelerometer, etc). The user may then get individual channel values by * calling @ref sensor_channel_get. * * This is mostly implemented by multi function devices enabling reading at * different sampling rates. * * The function blocks until the fetch operation is complete. * * Since the function communicates with the sensor device, it is unsafe * to call it in an ISR if the device is connected via I2C or SPI. * * @param dev Pointer to the sensor device * @param type The channel that needs updated * * @return 0 if successful, negative errno code if failure. */ __syscall int sensor_sample_fetch_chan(const struct device *dev, enum sensor_channel type); static inline int z_impl_sensor_sample_fetch_chan(const struct device *dev, enum sensor_channel type) { const struct sensor_driver_api *api = (const struct sensor_driver_api *)dev->api; return api->sample_fetch(dev, type); } /** * @brief Get a reading from a sensor device * * Return a useful value for a particular channel, from the driver's * internal data. Before calling this function, a sample must be * obtained by calling @ref sensor_sample_fetch or * @ref sensor_sample_fetch_chan. It is guaranteed that two subsequent * calls of this function for the same channels will yield the same * value, if @ref sensor_sample_fetch or @ref sensor_sample_fetch_chan * has not been called in the meantime. * * For vectorial data samples you can request all axes in just one call * by passing the specific channel with _XYZ suffix. The sample will be * returned at val[0], val[1] and val[2] (X, Y and Z in that order). * * @param dev Pointer to the sensor device * @param chan The channel to read * @param val Where to store the value * * @return 0 if successful, negative errno code if failure. */ __syscall int sensor_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val); static inline int z_impl_sensor_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { const struct sensor_driver_api *api = (const struct sensor_driver_api *)dev->api; return api->channel_get(dev, chan, val); } #if defined(CONFIG_SENSOR_ASYNC_API) || defined(__DOXYGEN__) /* * Generic data structure used for encoding the sample timestamp and number of channels sampled. */ struct __attribute__((__packed__)) sensor_data_generic_header { /* The timestamp at which the data was collected from the sensor */ uint64_t timestamp_ns; /* * The number of channels present in the frame. This will be the true number of elements in * channel_info and in the q31 values that follow the header. */ uint32_t num_channels; /* Shift value for all samples in the frame */ int8_t shift; /* This padding is needed to make sure that the 'channels' field is aligned */ int8_t _padding[sizeof(struct sensor_chan_spec) - 1]; /* Channels present in the frame */ struct sensor_chan_spec channels[0]; }; /** * @brief checks if a given channel is a 3-axis channel * * @param[in] chan The channel to check * @retval true if @p chan is any of @ref SENSOR_CHAN_ACCEL_XYZ, @ref SENSOR_CHAN_GYRO_XYZ, or * @ref SENSOR_CHAN_MAGN_XYZ, or @ref SENSOR_CHAN_POS_DXYZ * @retval false otherwise */ #define SENSOR_CHANNEL_3_AXIS(chan) \ ((chan) == SENSOR_CHAN_ACCEL_XYZ || (chan) == SENSOR_CHAN_GYRO_XYZ || \ (chan) == SENSOR_CHAN_MAGN_XYZ || (chan) == SENSOR_CHAN_POS_DXYZ) /** * @brief checks if a given channel is an Accelerometer * * @param[in] chan The channel to check * @retval true if @p chan is any of @ref SENSOR_CHAN_ACCEL_XYZ, @ref SENSOR_CHAN_ACCEL_X, or * @ref SENSOR_CHAN_ACCEL_Y, or @ref SENSOR_CHAN_ACCEL_Z * @retval false otherwise */ #define SENSOR_CHANNEL_IS_ACCEL(chan) \ ((chan) == SENSOR_CHAN_ACCEL_XYZ || (chan) == SENSOR_CHAN_ACCEL_X || \ (chan) == SENSOR_CHAN_ACCEL_Y || (chan) == SENSOR_CHAN_ACCEL_Z) /** * @brief checks if a given channel is a Gyroscope * * @param[in] chan The channel to check * @retval true if @p chan is any of @ref SENSOR_CHAN_GYRO_XYZ, @ref SENSOR_CHAN_GYRO_X, or * @ref SENSOR_CHAN_GYRO_Y, or @ref SENSOR_CHAN_GYRO_Z * @retval false otherwise */ #define SENSOR_CHANNEL_IS_GYRO(chan) \ ((chan) == SENSOR_CHAN_GYRO_XYZ || (chan) == SENSOR_CHAN_GYRO_X || \ (chan) == SENSOR_CHAN_GYRO_Y || (chan) == SENSOR_CHAN_GYRO_Z) /** * @brief Get the sensor's decoder API * * @param[in] dev The sensor device * @param[in] decoder Pointer to the decoder which will be set upon success * @return 0 on success * @return < 0 on error */ __syscall int sensor_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder); static inline int z_impl_sensor_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder) { const struct sensor_driver_api *api = (const struct sensor_driver_api *)dev->api; __ASSERT_NO_MSG(api != NULL); if (api->get_decoder == NULL) { *decoder = &__sensor_default_decoder; return 0; } return api->get_decoder(dev, decoder); } /** * @brief Reconfigure a reading iodev * * Allows a reconfiguration of the iodev associated with reading a sample from a sensor. * * Important: If the iodev is currently servicing a read operation, the data will likely be * invalid. Please be sure the flush or wait for all pending operations to complete before using the * data after a configuration change. * * It is also important that the `data` field of the iodev is a @ref sensor_read_config. * * @param[in] iodev The iodev to reconfigure * @param[in] sensor The sensor to read from * @param[in] channels One or more channels to read * @param[in] num_channels The number of channels in @p channels * @return 0 on success * @return < 0 on error */ __syscall int sensor_reconfigure_read_iodev(struct rtio_iodev *iodev, const struct device *sensor, const struct sensor_chan_spec *channels, size_t num_channels); static inline int z_impl_sensor_reconfigure_read_iodev(struct rtio_iodev *iodev, const struct device *sensor, const struct sensor_chan_spec *channels, size_t num_channels) { struct sensor_read_config *cfg = (struct sensor_read_config *)iodev->data; if (cfg->max < num_channels || cfg->is_streaming) { return -ENOMEM; } cfg->sensor = sensor; memcpy(cfg->channels, channels, num_channels * sizeof(struct sensor_chan_spec)); cfg->count = num_channels; return 0; } static inline int sensor_stream(struct rtio_iodev *iodev, struct rtio *ctx, void *userdata, struct rtio_sqe **handle) { if (IS_ENABLED(CONFIG_USERSPACE)) { struct rtio_sqe sqe; rtio_sqe_prep_read_multishot(&sqe, iodev, RTIO_PRIO_NORM, userdata); rtio_sqe_copy_in_get_handles(ctx, &sqe, handle, 1); } else { struct rtio_sqe *sqe = rtio_sqe_acquire(ctx); if (sqe == NULL) { return -ENOMEM; } if (handle != NULL) { *handle = sqe; } rtio_sqe_prep_read_multishot(sqe, iodev, RTIO_PRIO_NORM, userdata); } rtio_submit(ctx, 0); return 0; } /** * @brief Blocking one shot read of samples from a sensor into a buffer * * Using @p cfg, read data from the device by using the provided RTIO context * @p ctx. This call will generate a @ref rtio_sqe that will be given the provided buffer. The call * will wait for the read to complete before returning to the caller. * * @param[in] iodev The iodev created by @ref SENSOR_DT_READ_IODEV * @param[in] ctx The RTIO context to service the read * @param[in] buf Pointer to memory to read sample data into * @param[in] buf_len Size in bytes of the given memory that are valid to read into * @return 0 on success * @return < 0 on error */ static inline int sensor_read(struct rtio_iodev *iodev, struct rtio *ctx, uint8_t *buf, size_t buf_len) { if (IS_ENABLED(CONFIG_USERSPACE)) { struct rtio_sqe sqe; rtio_sqe_prep_read(&sqe, iodev, RTIO_PRIO_NORM, buf, buf_len, buf); rtio_sqe_copy_in(ctx, &sqe, 1); } else { struct rtio_sqe *sqe = rtio_sqe_acquire(ctx); if (sqe == NULL) { return -ENOMEM; } rtio_sqe_prep_read(sqe, iodev, RTIO_PRIO_NORM, buf, buf_len, buf); } rtio_submit(ctx, 0); struct rtio_cqe *cqe = rtio_cqe_consume_block(ctx); int res = cqe->result; __ASSERT(cqe->userdata == buf, "consumed non-matching completion for sensor read into buffer %p\n", buf); rtio_cqe_release(ctx, cqe); return res; } /** * @brief One shot non-blocking read with pool allocated buffer * * Using @p cfg, read one snapshot of data from the device by using the provided RTIO context * @p ctx. This call will generate a @ref rtio_sqe that will leverage the RTIO's internal * mempool when the time comes to service the read. * * @param[in] iodev The iodev created by @ref SENSOR_DT_READ_IODEV * @param[in] ctx The RTIO context to service the read * @param[in] userdata Optional userdata that will be available when the read is complete * @return 0 on success * @return < 0 on error */ static inline int sensor_read_async_mempool(struct rtio_iodev *iodev, struct rtio *ctx, void *userdata) { if (IS_ENABLED(CONFIG_USERSPACE)) { struct rtio_sqe sqe; rtio_sqe_prep_read_with_pool(&sqe, iodev, RTIO_PRIO_NORM, userdata); rtio_sqe_copy_in(ctx, &sqe, 1); } else { struct rtio_sqe *sqe = rtio_sqe_acquire(ctx); if (sqe == NULL) { return -ENOMEM; } rtio_sqe_prep_read_with_pool(sqe, iodev, RTIO_PRIO_NORM, userdata); } rtio_submit(ctx, 0); return 0; } /** * @typedef sensor_processing_callback_t * @brief Callback function used with the helper processing function. * * @see sensor_processing_with_callback * * @param[in] result The result code of the read (0 being success) * @param[in] buf The data buffer holding the sensor data * @param[in] buf_len The length (in bytes) of the @p buf * @param[in] userdata The optional userdata passed to sensor_read_async_mempool() */ typedef void (*sensor_processing_callback_t)(int result, uint8_t *buf, uint32_t buf_len, void *userdata); /** * @brief Helper function for common processing of sensor data. * * This function can be called in a blocking manner after sensor_read() or in a standalone * thread dedicated to processing. It will wait for a cqe from the RTIO context, once received, it * will decode the userdata and call the @p cb. Once the @p cb returns, the buffer will be released * back into @p ctx's mempool if available. * * @param[in] ctx The RTIO context to wait on * @param[in] cb Callback to call when data is ready for processing */ void sensor_processing_with_callback(struct rtio *ctx, sensor_processing_callback_t cb); #endif /* defined(CONFIG_SENSOR_ASYNC_API) || defined(__DOXYGEN__) */ /** * @brief The value of gravitational constant in micro m/s^2. */ #define SENSOR_G 9806650LL /** * @brief The value of constant PI in micros. */ #define SENSOR_PI 3141592LL /** * @brief Helper function to convert acceleration from m/s^2 to Gs * * @param ms2 A pointer to a sensor_value struct holding the acceleration, * in m/s^2. * * @return The converted value, in Gs. */ static inline int32_t sensor_ms2_to_g(const struct sensor_value *ms2) { int64_t micro_ms2 = ms2->val1 * 1000000LL + ms2->val2; if (micro_ms2 > 0) { return (micro_ms2 + SENSOR_G / 2) / SENSOR_G; } else { return (micro_ms2 - SENSOR_G / 2) / SENSOR_G; } } /** * @brief Helper function to convert acceleration from Gs to m/s^2 * * @param g The G value to be converted. * @param ms2 A pointer to a sensor_value struct, where the result is stored. */ static inline void sensor_g_to_ms2(int32_t g, struct sensor_value *ms2) { ms2->val1 = ((int64_t)g * SENSOR_G) / 1000000LL; ms2->val2 = ((int64_t)g * SENSOR_G) % 1000000LL; } /** * @brief Helper function to convert acceleration from m/s^2 to micro Gs * * @param ms2 A pointer to a sensor_value struct holding the acceleration, * in m/s^2. * * @return The converted value, in micro Gs. */ static inline int32_t sensor_ms2_to_ug(const struct sensor_value *ms2) { int64_t micro_ms2 = (ms2->val1 * INT64_C(1000000)) + ms2->val2; return (micro_ms2 * 1000000LL) / SENSOR_G; } /** * @brief Helper function to convert acceleration from micro Gs to m/s^2 * * @param ug The micro G value to be converted. * @param ms2 A pointer to a sensor_value struct, where the result is stored. */ static inline void sensor_ug_to_ms2(int32_t ug, struct sensor_value *ms2) { ms2->val1 = ((int64_t)ug * SENSOR_G / 1000000LL) / 1000000LL; ms2->val2 = ((int64_t)ug * SENSOR_G / 1000000LL) % 1000000LL; } /** * @brief Helper function for converting radians to degrees. * * @param rad A pointer to a sensor_value struct, holding the value in radians. * * @return The converted value, in degrees. */ static inline int32_t sensor_rad_to_degrees(const struct sensor_value *rad) { int64_t micro_rad_s = rad->val1 * 1000000LL + rad->val2; if (micro_rad_s > 0) { return (micro_rad_s * 180LL + SENSOR_PI / 2) / SENSOR_PI; } else { return (micro_rad_s * 180LL - SENSOR_PI / 2) / SENSOR_PI; } } /** * @brief Helper function for converting degrees to radians. * * @param d The value (in degrees) to be converted. * @param rad A pointer to a sensor_value struct, where the result is stored. */ static inline void sensor_degrees_to_rad(int32_t d, struct sensor_value *rad) { rad->val1 = ((int64_t)d * SENSOR_PI / 180LL) / 1000000LL; rad->val2 = ((int64_t)d * SENSOR_PI / 180LL) % 1000000LL; } /** * @brief Helper function for converting radians to 10 micro degrees. * * When the unit is 1 micro degree, the range that the int32_t can represent is * +/-2147.483 degrees. In order to increase this range, here we use 10 micro * degrees as the unit. * * @param rad A pointer to a sensor_value struct, holding the value in radians. * * @return The converted value, in 10 micro degrees. */ static inline int32_t sensor_rad_to_10udegrees(const struct sensor_value *rad) { int64_t micro_rad_s = rad->val1 * 1000000LL + rad->val2; return (micro_rad_s * 180LL * 100000LL) / SENSOR_PI; } /** * @brief Helper function for converting 10 micro degrees to radians. * * @param d The value (in 10 micro degrees) to be converted. * @param rad A pointer to a sensor_value struct, where the result is stored. */ static inline void sensor_10udegrees_to_rad(int32_t d, struct sensor_value *rad) { rad->val1 = ((int64_t)d * SENSOR_PI / 180LL / 100000LL) / 1000000LL; rad->val2 = ((int64_t)d * SENSOR_PI / 180LL / 100000LL) % 1000000LL; } /** * @brief Helper function for converting struct sensor_value to double. * * @param val A pointer to a sensor_value struct. * @return The converted value. */ static inline double sensor_value_to_double(const struct sensor_value *val) { return (double)val->val1 + (double)val->val2 / 1000000; } /** * @brief Helper function for converting struct sensor_value to float. * * @param val A pointer to a sensor_value struct. * @return The converted value. */ static inline float sensor_value_to_float(const struct sensor_value *val) { return (float)val->val1 + (float)val->val2 / 1000000; } /** * @brief Helper function for converting double to struct sensor_value. * * @param val A pointer to a sensor_value struct. * @param inp The converted value. * @return 0 if successful, negative errno code if failure. */ static inline int sensor_value_from_double(struct sensor_value *val, double inp) { if (inp < INT32_MIN || inp > INT32_MAX) { return -ERANGE; } double val2 = (inp - (int32_t)inp) * 1000000.0; if (val2 < INT32_MIN || val2 > INT32_MAX) { return -ERANGE; } val->val1 = (int32_t)inp; val->val2 = (int32_t)val2; return 0; } /** * @brief Helper function for converting float to struct sensor_value. * * @param val A pointer to a sensor_value struct. * @param inp The converted value. * @return 0 if successful, negative errno code if failure. */ static inline int sensor_value_from_float(struct sensor_value *val, float inp) { float val2 = (inp - (int32_t)inp) * 1000000.0f; if (val2 < INT32_MIN || val2 > (float)(INT32_MAX - 1)) { return -ERANGE; } val->val1 = (int32_t)inp; val->val2 = (int32_t)val2; return 0; } #ifdef CONFIG_SENSOR_INFO struct sensor_info { const struct device *dev; const char *vendor; const char *model; const char *friendly_name; }; #define SENSOR_INFO_INITIALIZER(_dev, _vendor, _model, _friendly_name) \ { \ .dev = _dev, \ .vendor = _vendor, \ .model = _model, \ .friendly_name = _friendly_name, \ } #define SENSOR_INFO_DEFINE(name, ...) \ static const STRUCT_SECTION_ITERABLE(sensor_info, name) = \ SENSOR_INFO_INITIALIZER(__VA_ARGS__) #define SENSOR_INFO_DT_NAME(node_id) \ _CONCAT(__sensor_info, DEVICE_DT_NAME_GET(node_id)) #define SENSOR_INFO_DT_DEFINE(node_id) \ SENSOR_INFO_DEFINE(SENSOR_INFO_DT_NAME(node_id), \ DEVICE_DT_GET(node_id), \ DT_NODE_VENDOR_OR(node_id, NULL), \ DT_NODE_MODEL_OR(node_id, NULL), \ DT_PROP_OR(node_id, friendly_name, NULL)) \ #else #define SENSOR_INFO_DEFINE(name, ...) #define SENSOR_INFO_DT_DEFINE(node_id) #endif /* CONFIG_SENSOR_INFO */ /** * @brief Like DEVICE_DT_DEFINE() with sensor specifics. * * @details Defines a device which implements the sensor API. May define an * element in the sensor info iterable section used to enumerate all sensor * devices. * * @param node_id The devicetree node identifier. * * @param init_fn Name of the init function of the driver. * * @param pm_device PM device resources reference (NULL if device does not use * PM). * * @param data_ptr Pointer to the device's private data. * * @param cfg_ptr The address to the structure containing the configuration * information for this instance of the driver. * * @param level The initialization level. See SYS_INIT() for details. * * @param prio Priority within the selected initialization level. See * SYS_INIT() for details. * * @param api_ptr Provides an initial pointer to the API function struct used * by the driver. Can be NULL. */ #define SENSOR_DEVICE_DT_DEFINE(node_id, init_fn, pm_device, \ data_ptr, cfg_ptr, level, prio, \ api_ptr, ...) \ DEVICE_DT_DEFINE(node_id, init_fn, pm_device, \ data_ptr, cfg_ptr, level, prio, \ api_ptr, __VA_ARGS__); \ \ SENSOR_INFO_DT_DEFINE(node_id); /** * @brief Like SENSOR_DEVICE_DT_DEFINE() for an instance of a DT_DRV_COMPAT * compatible * * @param inst instance number. This is replaced by * DT_DRV_COMPAT(inst) in the call to SENSOR_DEVICE_DT_DEFINE(). * * @param ... other parameters as expected by SENSOR_DEVICE_DT_DEFINE(). */ #define SENSOR_DEVICE_DT_INST_DEFINE(inst, ...) \ SENSOR_DEVICE_DT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__) /** * @brief Helper function for converting struct sensor_value to integer milli units. * * @param val A pointer to a sensor_value struct. * @return The converted value. */ static inline int64_t sensor_value_to_milli(const struct sensor_value *val) { return ((int64_t)val->val1 * 1000) + val->val2 / 1000; } /** * @brief Helper function for converting struct sensor_value to integer micro units. * * @param val A pointer to a sensor_value struct. * @return The converted value. */ static inline int64_t sensor_value_to_micro(const struct sensor_value *val) { return ((int64_t)val->val1 * 1000000) + val->val2; } /** * @brief Helper function for converting integer milli units to struct sensor_value. * * @param val A pointer to a sensor_value struct. * @param milli The converted value. * @return 0 if successful, negative errno code if failure. */ static inline int sensor_value_from_milli(struct sensor_value *val, int64_t milli) { if (milli < ((int64_t)INT32_MIN - 1) * 1000LL || milli > ((int64_t)INT32_MAX + 1) * 1000LL) { return -ERANGE; } val->val1 = (int32_t)(milli / 1000); val->val2 = (int32_t)(milli % 1000) * 1000; return 0; } /** * @brief Helper function for converting integer micro units to struct sensor_value. * * @param val A pointer to a sensor_value struct. * @param micro The converted value. * @return 0 if successful, negative errno code if failure. */ static inline int sensor_value_from_micro(struct sensor_value *val, int64_t micro) { if (micro < ((int64_t)INT32_MIN - 1) * 1000000LL || micro > ((int64_t)INT32_MAX + 1) * 1000000LL) { return -ERANGE; } val->val1 = (int32_t)(micro / 1000000LL); val->val2 = (int32_t)(micro % 1000000LL); return 0; } /** * @} */ /** * @brief Get the decoder name for the current driver * * This function depends on `DT_DRV_COMPAT` being defined. */ #define SENSOR_DECODER_NAME() UTIL_CAT(DT_DRV_COMPAT, __decoder_api) /** * @brief Statically get the decoder for a given node * * @code{.c} * static const sensor_decoder_api *decoder = SENSOR_DECODER_DT_GET(DT_ALIAS(accel)); * @endcode */ #define SENSOR_DECODER_DT_GET(node_id) \ &UTIL_CAT(DT_STRING_TOKEN_BY_IDX(node_id, compatible, 0), __decoder_api) /** * @brief Define a decoder API * * This macro should be created once per compatible string of a sensor and will create a statically * referenceable decoder API. * * @code{.c} * SENSOR_DECODER_API_DT_DEFINE() = { * .get_frame_count = my_driver_get_frame_count, * .get_timestamp = my_driver_get_timestamp, * .get_shift = my_driver_get_shift, * .decode = my_driver_decode, * }; * @endcode */ #define SENSOR_DECODER_API_DT_DEFINE() \ COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT), (), (static)) \ const STRUCT_SECTION_ITERABLE(sensor_decoder_api, SENSOR_DECODER_NAME()) #define Z_MAYBE_SENSOR_DECODER_DECLARE_INTERNAL_IDX(node_id, prop, idx) \ extern const struct sensor_decoder_api UTIL_CAT( \ DT_STRING_TOKEN_BY_IDX(node_id, prop, idx), __decoder_api); #define Z_MAYBE_SENSOR_DECODER_DECLARE_INTERNAL(node_id) \ COND_CODE_1(DT_NODE_HAS_PROP(node_id, compatible), \ (DT_FOREACH_PROP_ELEM(node_id, compatible, \ Z_MAYBE_SENSOR_DECODER_DECLARE_INTERNAL_IDX)), \ ()) DT_FOREACH_STATUS_OKAY_NODE(Z_MAYBE_SENSOR_DECODER_DECLARE_INTERNAL) #ifdef __cplusplus } #endif #include #endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_H_ */