From 3cef8e9c6a983718ddc9f87060b5fd9d4f972560 Mon Sep 17 00:00:00 2001 From: Yuval Peress Date: Mon, 20 May 2024 11:46:53 -0600 Subject: [PATCH] WIP --- boards/tdk/robokit1/support/tdk_robokit1.resc | 8 + .../asahi_kasei/akm09918c/akm09918c_emul.c | 12 + .../sensor/asahi_kasei/akm09918c/sensor.yaml | 0 drivers/sensor/attributes.yaml | 97 +++++ drivers/sensor/bosch/bma4xx/sensor.yaml | 15 + drivers/sensor/channels.yaml | 351 ++++++++++++++++ drivers/sensor/triggers.yaml | 49 +++ dts/bindings/sensor/bosch,bma4xx-common.yaml | 7 - include/zephyr/dt-bindings/sensor/sensor.h | 4 +- scripts/sensors/gen_defines.py | 386 ++++++++++++++++++ .../build_all/sensor/src/static_test.c | 0 11 files changed, 920 insertions(+), 9 deletions(-) create mode 100644 boards/tdk/robokit1/support/tdk_robokit1.resc create mode 100644 drivers/sensor/asahi_kasei/akm09918c/sensor.yaml create mode 100644 drivers/sensor/attributes.yaml create mode 100644 drivers/sensor/bosch/bma4xx/sensor.yaml create mode 100644 drivers/sensor/channels.yaml create mode 100644 drivers/sensor/triggers.yaml create mode 100644 scripts/sensors/gen_defines.py create mode 100644 tests/drivers/build_all/sensor/src/static_test.c diff --git a/boards/tdk/robokit1/support/tdk_robokit1.resc b/boards/tdk/robokit1/support/tdk_robokit1.resc new file mode 100644 index 00000000000000..32f99da07d9507 --- /dev/null +++ b/boards/tdk/robokit1/support/tdk_robokit1.resc @@ -0,0 +1,8 @@ +:name: TDK-Robokit1 +:description: This script is prepared to run Zephyr on TDK Robokit1 board. + +$name?="TDK-Robokit1" + +using sysbus +mach create $name +machine LoadPlatformDescription @platforms/boards/ diff --git a/drivers/sensor/asahi_kasei/akm09918c/akm09918c_emul.c b/drivers/sensor/asahi_kasei/akm09918c/akm09918c_emul.c index 5dce0d0d82716a..ec71975637945d 100644 --- a/drivers/sensor/asahi_kasei/akm09918c/akm09918c_emul.c +++ b/drivers/sensor/asahi_kasei/akm09918c/akm09918c_emul.c @@ -130,6 +130,10 @@ static int akm09918c_emul_init(const struct emul *target, const struct device *p ARG_UNUSED(parent); akm09918c_emul_reset(target); + struct akm09918c_emul_data *data = target->data; + + data->reg[AKM09918C_REG_CNTL2] = AKM09918C_CNTL2_CONTINUOUS_4; + return 0; } @@ -204,6 +208,13 @@ static int akm09918c_emul_backend_get_sample_range(const struct emul *target, return 0; } +static int akm09918c_emul_backend_set_attribute(const struct emul *target, + struct sensor_chan_spec ch, + enum sensor_attribute attribute, const void *value) +{ + return -ENOTSUP; +} + static const struct i2c_emul_api akm09918c_emul_api_i2c = { .transfer = akm09918c_emul_transfer_i2c, }; @@ -211,6 +222,7 @@ static const struct i2c_emul_api akm09918c_emul_api_i2c = { static const struct emul_sensor_driver_api akm09918c_emul_sensor_driver_api = { .set_channel = akm09918c_emul_backend_set_channel, .get_sample_range = akm09918c_emul_backend_get_sample_range, + .set_attribute = akm09918c_emul_backend_set_attribute, }; #define AKM09918C_EMUL(n) \ diff --git a/drivers/sensor/asahi_kasei/akm09918c/sensor.yaml b/drivers/sensor/asahi_kasei/akm09918c/sensor.yaml new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/drivers/sensor/attributes.yaml b/drivers/sensor/attributes.yaml new file mode 100644 index 00000000000000..cf4abdc25578c4 --- /dev/null +++ b/drivers/sensor/attributes.yaml @@ -0,0 +1,97 @@ +attributes: + sampling_frequency: + name: "sampling rate" + description: "Sensor sampling frequency, i.e. how many times a second the sensor takes a measurement." + units: + name: "frequency" + symbol: "Hz" + lower_thresh: + name: "lower threshold" + description: "Lower threshold for trigger." + units: + name: "unknown" + symbol: "unknown" + upper_thresh: + name: "upper threshold" + description: "Upper threshold for trigger." + units: + name: "unknown" + symbol: "unknown" + slope_th: + name: "any motion threshold" + description: "Threshold for any-motion (slope) trigger." + units: + name: "unknown" + symbol: "unknown" + slope_dur: + name: "slope value threshold duration" + description: "Duration for which the slope values needs to be outside the threshold for the trigger to fire." + units: + name: "time" + symbol: "s" + hysteresis: + name: "hysteresis" + description: "Hysteresis for trigger thresholds." + units: + name: "unknown" + symbol: "unknown" + oversampling: + name: "oversampling factor" + description: "Oversampling factor" + units: + name: "scale" + symbol: "" + full_scale: + name: "range" + description: "Sensor range, in SI units." + units: + name: "unknown" + symbol: "unknown" + offset: + name: "value offset" + description: "The sensor value returned will be altered by the amount indicated by offset: final_value = sensor_value + offset." + units: + name: "unknown" + symbol: "unknown" + calib_target: + name: "calibration target" + description: "Calibration target. This will be used by the internal chip's algorithms to calibrate itself on a certain axis, or all of them." + units: + name: "unknown" + symbol: "unknown" + configuration: + name: "operating mode" + description: "Configure the operating modes of a sensor." + units: + name: "unknown" + symbol: "unknown" + calibration: + name: "calibration value" + description: "Set a calibration value needed by a sensor." + units: + name: "unknown" + symbol: "unknown" + feature_mask: + name: "enabled/disabled feature" + description: "Enable/disable sensor features" + units: + name: "unknown" + symbol: "unknown" + alert: + name: "alert threshold" + description: "Alert threshold or alert enable/disable" + units: + name: "unknown" + symbol: "unknown" + ff_dur: + name: "free-fall duration" + description: "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." + units: + name: "time" + symbol: "ms" + batch_duration: + name: "batch duration" + description: "Hardware batch duration in ticks" + units: + name: "time" + symbol: "ticks" diff --git a/drivers/sensor/bosch/bma4xx/sensor.yaml b/drivers/sensor/bosch/bma4xx/sensor.yaml new file mode 100644 index 00000000000000..d7bc1937d5c599 --- /dev/null +++ b/drivers/sensor/bosch/bma4xx/sensor.yaml @@ -0,0 +1,15 @@ +deps: + - "drivers/sensor/attributes.yaml" + - "drivers/sensor/channels.yaml" + - "drivers/sensor/triggers.yaml" +compatible: + org: "bosch" + part: "bma4xx" +channels: + accel_xyz: {} + die_temp: {} +attributes: + sampling_frequency: {} + full_scale: {} + offset: {} + configuration: {} diff --git a/drivers/sensor/channels.yaml b/drivers/sensor/channels.yaml new file mode 100644 index 00000000000000..2df419637eea0b --- /dev/null +++ b/drivers/sensor/channels.yaml @@ -0,0 +1,351 @@ +channels: + accel_x: + name: "ACCEL_X" + description: "Acceleration on the X axis, in m/s^2." + units: + symbol: "m/s^2" + accel_y: + name: "ACCEL_Y" + description: "Acceleration on the Y axis, in m/s^2." + units: + symbol: "m/s^2" + accel_z: + name: "ACCEL_Z" + description: "Acceleration on the Z axis, in m/s^2." + units: + symbol: "m/s^2" + accel_xyz: + name: "ACCEL_XYZ" + description: "Acceleration on the X, Y and Z axes." + units: + symbol: "m/s^2" + gyro_x: + name: "GYRO_X" + description: "Angular velocity around the X axis, in radians/s." + units: + name: "angular velocity" + symbol: "rad/s" + gyro_y: + name: "GYRO_Y" + description: "Angular velocity around the Y axis, in radians/s." + units: + name: "angular velocity" + symbol: "rad/s" + gyro_z: + name: "GYRO_Z" + description: "Angular velocity around the Z axis, in radians/s." + units: + name: "angular velocity" + symbol: "rad/s" + gyro_xyz: + name: "GYRO_XYZ" + description: "Angular velocity around the X, Y and Z axes." + units: + name: "angular velocity" + symbol: "rad/s" + magn_x: + name: "MAGN_X" + description: "Magnetic field on the X axis, in Gauss." + units: + name: "magnetic field" + symbol: "Gs" + magn_y: + name: "MAGN_Y" + description: "Magnetic field on the Y axis, in Gauss." + units: + name: "magnetic field" + symbol: "Gs" + magn_z: + name: "MAGN_Z" + description: "Magnetic field on the Z axis, in Gauss." + units: + name: "magnetic field" + symbol: "Gs" + magn_xyz: + name: "MAGN_XYZ" + description: "Magnetic field on the X, Y and Z axes." + units: + name: "magnetic field" + symbol: "Gs" + die_temp: + name: "DIE_TEMP" + description: "Device die temperature in degrees Celsius." + units: + name: "temperature" + symbol: "C" + ambient_temp: + name: "AMBIENT_TEMP" + description: "Ambient temperature in degrees Celsius." + units: + name: "temperature" + symbol: "C" + press: + name: "PRESS" + description: "Pressure in kilopascal." + units: + name: "pressure" + symbol: "kPa" + prox: + name: "PROX" + description: "Proximity. Adimensional. A value of 1 indicates that an object is close." + units: + name: "is near" + symbol: "near" + humidity: + name: "HUMIDITY" + description: "Humidity, in percent." + units: + name: "percent" + symbol: "%" + light: + name: "LIGHT" + description: "Illuminance in visible spectrum, in lux." + units: + name: "illuminance" + symbol: "lux" + ir: + name: "IR" + description: "Illuminance in infra-red spectrum, in lux." + units: + name: "illuminance" + symbol: "lux" + red: + name: "RED" + description: "Illuminance in red spectrum, in lux." + units: + name: "illuminance" + symbol: "lux" + green: + name: "GREEN" + description: "Illuminance in green spectrum, in lux." + units: + name: "illuminance" + symbol: "lux" + blue: + name: "BLUE" + description: "Illuminance in blue spectrum, in lux." + units: + name: "illuminance" + symbol: "lux" + altitude: + name: "ALTITUDE" + description: "Altitude, in meters" + units: + name: "distance" + symbol: "m" + pm_1_0: + name: "PM_1_0" + description: "1.0 micro-meters Particulate Matter, in ug/m^3" + units: + name: "density" + symbol: "ug/m^3" + pm_2_5: + name: "PM_2_5" + description: "2.5 micro-meters Particulate Matter, in ug/m^3" + units: + name: "density" + symbol: "ug/m^3" + pm_10: + name: "PM_10" + description: "10 micro-meters Particulate Matter, in ug/m^3" + units: + name: "density" + symbol: "ug/m^3" + distance: + name: "DISTANCE" + description: "Distance. From sensor to target, in meters" + units: + name: "distance" + symbol: "m" + co2: + name: "CO2" + description: "CO2 level, in parts per million (ppm)" + units: + name: "concentration" + symbol: "ppm" + o2: + name: "CO2" + description: "O2 level, in parts per million (ppm)" + units: + name: "concentration" + symbol: "ppm" + voc: + name: "VOC" + description: "VOC level, in parts per billion (ppb)" + units: + name: "concentration" + symbol: "ppb" + gas_res: + name: "GAS_RES" + description: "Gas sensor resistance in ohms." + units: + name: "electrical resistance" + symbol: "Ohm" + voltage: + name: "VOLTAGE" + description: "Voltage, in volts" + units: + name: "volts" + symbol: "V" + vshunt: + name: "VSHUNT" + description: "Current Shunt Voltage in milli-volts" + units: + name: "volts" + symbol: "mV" + current: + name: "CURRENT" + description: "Current, in amps" + units: + name: "current" + symbol: "A" + power: + name: "POWER" + description: "Power in watts" + units: + name: "power" + symbol: "W" + resistance: + name: "RESISTANCE" + description: "Resistance , in Ohm" + units: + name: "electrical resistance" + symbol: "Ohm" + rotation: + name: "ROTATION" + description: "Angular rotation, in degrees" + units: + name: "angle" + symbol: "deg" + pos_dx: + name: "POS_DX" + description: "Position change on the X axis, in points." + units: + name: "points" + symbol: "pts" + pos_dy: + name: "POS_DY" + description: "Position change on the Y axis, in points." + units: + name: "points" + symbol: "pts" + pos_dz: + name: "POS_DZ" + description: "Position change on the Z axis, in points." + units: + name: "points" + symbol: "pts" + rpm: + name: "RPM" + description: "Revolutions per minute, in RPM." + units: + name: "rpms" + symbol: "rpm" + gauge_voltage: + name: "GAUGE_VOLTAGE" + description: "Voltage, in volts" + units: + name: "volts" + symbol: "V" + gauge_avg_current: + name: "GAUGE_AVG_CURRENT" + description: "Average current, in amps" + units: + name: "current" + symbol: "A" + gauge_stdby_current: + name: "GAUGE_STDBY_CURRENT" + description: "Standby current, in amps" + units: + name: "current" + symbol: "A" + gauge_max_load_current: + name: "GAUGE_MAX_LOAD_CURRENT" + description: "Max load current, in amps" + units: + name: "current" + symbol: "A" + gauge_temp: + name: "GAUGE_TEMP" + description: "Gauge temperature" + units: + name: "temperature" + symbol: "C" + gauge_state_of_charge: + name: "GAUGE_STATE_OF_CHARGE" + description: "State of charge measurement in %" + units: + name: "percent" + symbol: "%" + gauge_full_charge_capacity: + name: "GAUGE_FULL_CHARGE_CAPACITY" + description: "Full Charge Capacity in mAh" + units: + name: "energy" + symbol: "mAh" + gauge_remaining_charge_capacity: + name: "GAUGE_REMAINING_CHARGE_CAPACITY" + description: "Remaining Charge Capacity in mAh" + units: + name: "energy" + symbol: "mAh" + gauge_nom_avail_capacity: + name: "GAUGE_NOM_AVAIL_CAPACITY" + description: "Nominal Available Capacity in mAh" + units: + name: "energy" + symbol: "mAh" + gauge_full_avail_capacity: + name: "GAUGE_FULL_AVAIL_CAPACITY" + description: "Full Available Capacity in mAh" + units: + name: "energy" + symbol: "mAh" + gauge_avg_power: + name: "GAUGE_AVG_POWER" + description: "Average power in mW" + units: + name: "power" + symbol: "mW" + gauge_state_of_health: + name: "GAUGE_STATE_OF_HEALTH" + description: "State of health measurement in %" + units: + name: "percent" + symbol: "%" + gauge_time_to_empty: + name: "GAUGE_TIME_TO_EMPTY" + description: "Time to empty in minutes" + units: + name: "time" + symbol: "min" + gauge_time_to_full: + name: "GAUGE_TIME_TO_FULL" + description: "Time to full in minutes" + units: + name: "time" + symbol: "min" + gauge_cycle_count: + name: "GAUGE_CYCLE_COUNT" + description: "Cycle count (total number of charge/discharge cycles)" + units: + name: "cycles" + symbol: "cycles" + gauge_design_voltage: + name: "GAUGE_DESIGN_VOLTAGE" + description: "Design voltage of cell in V (max voltage)" + units: + name: "volts" + symbol: "V" + gauge_desired_voltage: + name: "GAUGE_DESIRED_VOLTAGE" + description: "Desired voltage of cell in V (nominal voltage)" + units: + name: "volts" + symbol: "V" + gauge_desired_charging_current: + name: "GAUGE_DESIRED_CHARGING_CURRENT" + description: "Desired charging current in mA" + units: + name: "current" + symbol: "mA" diff --git a/drivers/sensor/triggers.yaml b/drivers/sensor/triggers.yaml new file mode 100644 index 00000000000000..18aa705bc36e30 --- /dev/null +++ b/drivers/sensor/triggers.yaml @@ -0,0 +1,49 @@ +triggers: + timer: + name: "timer" + description: > + Timer-based trigger, useful when the sensor does not have an interrupt + line. + data_ready: + name: " data ready" + description: "Trigger fires whenever new data is ready." + delta: + name: "significant change" + description: > + 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. + near_far: + name: "near/far" + description: "Trigger fires when a near/far event is detected." + threshold: + name: "threshold reached" + description: > + 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. + tap: + name: "tap" + description: "Trigger fires when a single tap is detected." + double_tap: + name: "double tap" + description: "Trigger fires when a double tap is detected." + freefall: + name: "free fall" + description: "Trigger fires when a free fall is detected." + motion: + name: "motion" + description: "Trigger fires when motion is detected." + stationary: + name: "stationary" + description: "Trigger fires when no motion has been detected for a while." + fifo_watermark: + name: "FIFO watermark" + description: "Trigger fires when the FIFO watermark has been reached." + fifo_full: + name: "FIFO full" + description: "Trigger fires when the FIFO becomes full." diff --git a/dts/bindings/sensor/bosch,bma4xx-common.yaml b/dts/bindings/sensor/bosch,bma4xx-common.yaml index 5ab3756a89b0fb..f7053021850c38 100644 --- a/dts/bindings/sensor/bosch,bma4xx-common.yaml +++ b/dts/bindings/sensor/bosch,bma4xx-common.yaml @@ -7,10 +7,3 @@ properties: description: | Identifies pin for the INT1 signal on the sensor. The sensor INT2,3,4 signals are not supported by the driver. - supported-channels: - default: - - SENSOR_CHAN_ACCEL_X - - SENSOR_CHAN_ACCEL_Y - - SENSOR_CHAN_ACCEL_Z - - SENSOR_CHAN_ACCEL_XYZ - - SENSOR_CHAN_DIE_TEMP diff --git a/include/zephyr/dt-bindings/sensor/sensor.h b/include/zephyr/dt-bindings/sensor/sensor.h index 978ab5cb31b29b..027abbf0d2c9e6 100644 --- a/include/zephyr/dt-bindings/sensor/sensor.h +++ b/include/zephyr/dt-bindings/sensor/sensor.h @@ -143,7 +143,7 @@ /** * Number of all common sensor channels. */ -#define SENSOR_CHAN_COMMON_COUNT 60 +#define SENSOR_CHAN_COMMON_COUNT (SENSOR_CHAN_ALL + 1) /** * This and higher values are sensor specific. @@ -156,4 +156,4 @@ */ #define SENSOR_CHAN_MAX INT16_MAX -#endif /* INCLUDE_ZEPHYR_DT_BINDINGS_SENSOR_H_ * +#endif /* INCLUDE_ZEPHYR_DT_BINDINGS_SENSOR_H_ */ diff --git a/scripts/sensors/gen_defines.py b/scripts/sensors/gen_defines.py new file mode 100644 index 00000000000000..aa375b4093a2b0 --- /dev/null +++ b/scripts/sensors/gen_defines.py @@ -0,0 +1,386 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause +import io +import yaml +import sys +from collections.abc import Sequence + + +_CHANNEL_BACKWARDS_COMPATIBILITY_MAP: list[str] = [ + "ACCEL_X", + "ACCEL_Y", + "ACCEL_Z", + "ACCEL_XYZ", + "GYRO_X", + "GYRO_Y", + "GYRO_Z", + "GYRO_XYZ", + "MAGN_X", + "MAGN_Y", + "MAGN_Z", + "MAGN_XYZ", + "DIE_TEMP", + "AMBIENT_TEMP", + "PRESS", + "PROX", + "HUMIDITY", + "LIGHT", + "IR", + "RED", + "GREEN", + "BLUE", + "ALTITUDE", + "PM_1_0", + "PM_2_5", + "PM_10", + "DISTANCE", + "CO2", + "O2", + "VOC", + "GAS_RES", + "VOLTAGE", + "VSHUNT", + "CURRENT", + "POWER", + "RESISTANCE", + "ROTATION", + "POS_DX", + "POS_DY", + "POS_DZ", + "RPM", + "GAUGE_VOLTAGE", + "GAUGE_AVG_CURRENT", + "GAUGE_STDBY_CURRENT", + "GAUGE_MAX_LOAD_CURRENT", + "GAUGE_TEMP", + "GAUGE_STATE_OF_CHARGE", + "GAUGE_FULL_CHARGE_CAPACITY", + "GAUGE_REMAINING_CHARGE_CAPACITY", + "GAUGE_NOM_AVAIL_CAPACITY", + "GAUGE_FULL_AVAIL_CAPACITY", + "GAUGE_AVG_POWER", + "GAUGE_STATE_OF_HEALTH", + "GAUGE_TIME_TO_EMPTY", + "GAUGE_TIME_TO_FULL", + "GAUGE_CYCLE_COUNT", + "GAUGE_DESIGN_VOLTAGE", + "GAUGE_DESIRED_VOLTAGE", + "GAUGE_DESIRED_CHARGING_CURRENT", +] + +_ATTRIBUTE_BACKWARDS_COMPATIBILITY_MAP: list[str] = [ + "SAMPLING_FREQUENCY", + "LOWER_THRESH", + "UPPER_THRESH", + "SLOPE_TH", + "SLOPE_DUR", + "HYSTERESIS", + "OVERSAMPLING", + "FULL_SCALE", + "OFFSET", + "CALIB_TARGET", + "CONFIGURATION", + "CALIBRATION", + "FEATURE_MASK", + "ALERT", + "FF_DUR", + "BATCH_DURATION", +] + +_TRIGGER_BACKWARDS_COMPATIBILITY_MAP: list[str] = [ + "TIMER", + "DATA_READY", + "DELTA", + "NEAR_FAR", + "THRESHOLD", + "TAP", + "DOUBLE_TAP", + "FREEFALL", + "MOTION", + "STATIONARY", + "FIFO_WATERMARK", + "FIFO_FULL", + "COMMON_COUNT", +] + + +def main() -> None: + definition = yaml.safe_load(sys.stdin) + sensors: list[Sensor] = [] + + for _, sensor in definition["sensors"].items(): + sensors.append(Sensor(spec=sensor)) + + print( + ZephyrHeader( + channels=[Channel(identifier, value) for identifier, value in definition["channels"].items()], + attributes=[Attribute(identifier, value) for identifier, value in definition["attributes"].items()], + triggers=[Trigger(identifier, value) for identifier, value in definition["triggers"].items()], + sensors=sensors, + ) + ) + + +class Attribute: + _attribute_count: int = len(_ATTRIBUTE_BACKWARDS_COMPATIBILITY_MAP) + def __init__(self, identifier: str, definition: dict) -> None: + self._identifier: str = identifier.upper() + self._name: str = definition["name"] + self._description: str = definition["description"] + self._units_name: str = definition["units"]["name"] + self._units_symbol: str = definition["units"]["symbol"] + try: + self.value = _ATTRIBUTE_BACKWARDS_COMPATIBILITY_MAP.index(self._identifier) + except ValueError: + self.value = Attribute._attribute_count + Attribute._attribute_count += 1 + + @staticmethod + def tail_str() -> str: + writer = io.StringIO() + writer.write(f""" +/** Number of all common sensor attributes. */ +#define SENSOR_ATTR_COMMON_COUNT {Attribute._attribute_count} + +/** + * This and higher values are sensor specific. + * Refer to the sensor header file. + */ +#define SENSOR_ATTR_PRIV_START SENSOR_ATTR_COMMON_COUNT + +/** + * Maximum value describing a sensor attribute type. + */ +#define SENSOR_ATTR_MAX INT16_MAX +""") + return writer.getvalue() + + def __str__(self) -> str: + writer = io.StringIO() + writer.write(f""" +/** + * {self._description} + * + * Units: {self._units_name} ({self._units_symbol}) + */ +#define SENSOR_ATTR_{self._identifier} {self.value} +""") + return writer.getvalue() + + def __hash__(self) -> int: + return hash((self._identifier, self._name, self._description, self._units_name, self._units_symbol)) + + +class Channel: + _channel_count: int = len(_CHANNEL_BACKWARDS_COMPATIBILITY_MAP) + def __init__(self, identifier: str, definition: dict) -> None: + self._identifier: str = identifier.upper() + self._name: str = definition["name"] + self._description: str = definition["description"] + self._units_name: str = definition["units"]["name"] + self._units_symbol: str = definition["units"]["symbol"] + try: + self.value = _CHANNEL_BACKWARDS_COMPATIBILITY_MAP.index(self._identifier) + except ValueError: + self.value = Channel._channel_count + Channel._channel_count += 1 + + @staticmethod + def tail_str() -> str: + writer = io.StringIO() + writer.write(f""" +/** All channels */ +#define SENSOR_CHAN_ALL {Channel._channel_count} + +/** Number of all common sensor channels. */ +#define SENSOR_CHAN_COMMON_COUNT (SENSOR_CHAN_ALL + 1) + +/** + * This and higher values are sensor specific. + * Refer to the sensor header file. + */ +#define SENSOR_CHAN_PRIV_START SENSOR_CHAN_COMMON_COUNT + +/** + * Maximum value describing a sensor channel type. + */ +#define SENSOR_CHAN_MAX INT16_MAX +""") + return writer.getvalue() + + def __str__(self) -> str: + writer = io.StringIO() + writer.write(f""" +/** + * {self._description} + * + * Units: {self._units_name} ({self._units_symbol}) + */ +#define SENSOR_CHAN_{self._identifier} {self.value} +""") + return writer.getvalue() + + def __hash__(self) -> int: + return hash((self._identifier, self._name, self._description, self._units_name, self._units_symbol)) + + +class Trigger: + _trigger_count: int = len(_TRIGGER_BACKWARDS_COMPATIBILITY_MAP) + def __init__(self, identifier: str, definition: dict) -> None: + self._identifier: str = identifier.upper() + self._name: str = definition["name"] + self._description: str = definition["description"] + self.value: int + try: + self.value = _TRIGGER_BACKWARDS_COMPATIBILITY_MAP.index(self._identifier) + except ValueError: + self.value = Trigger._trigger_count + Trigger._trigger_count += 1 + + @staticmethod + def tail_str() -> str: + writer = io.StringIO() + writer.write(f""" +/** Number of all common sensor triggers. */ +#define SENSOR_TRIG_COMMON_COUNT {Trigger._trigger_count} + +/** + * This and higher values are sensor specific. + * Refer to the sensor header file. + */ +#define SENSOR_TRIG_PRIV_START SENSOR_TRIG_COMMON_COUNT + +/** + * Maximum value describing a sensor trigger type. + */ +#define SENSOR_TRIG_MAX INT16_MAX +""") + return writer.getvalue() + + def __str__(self) -> str: + writer = io.StringIO() + writer.write(f""" +/** + * {self._description} + */ +#define SENSOR_TRIG_{self._identifier} {self.value} +""") + return writer.getvalue() + + def __hash__(self) -> int: + return hash((self._identifier, self._name, self._description)) + +class Sensor: + def __init__(self, spec: dict) -> None: + self._spec = spec + + @property + def name(self) -> str: + return self._spec["compatible"]["org"] + "_" + self._spec["compatible"]["part"] + + @property + def def_prefix(self) -> str: + return f"SENSOR_SPEC_{self.name}" + + @property + def chan_count(self) -> int: + return len(self._spec["channels"]) + + @property + def attr_count(self) -> int: + return len(self._spec["attributes"]) + + @property + def trig_count(self) -> int: + return len(self._spec["triggers"]) + + def _print_channel_spec(self, writer: io.TextIOWrapper) -> None: + for chan_id, chan in self._spec["channels"].items(): + prefix = f"{self.def_prefix}_CHAN_SENSOR_CHAN_{str(chan_id).upper()}" + writer.write( + f"#define {prefix}_EXISTS 1\n" + f"#define {prefix}_NAME \"{chan['name']}\"\n" + f"#define {prefix}_DESC \"{chan['description']}\"\n" + f"#define {prefix}_UNITS_NAME \"{chan['units']['name']}\"\n" + f"#define {prefix}_UNITS_SYMBOL \"{chan['units']['symbol']}\"\n" + ) + + def _print_attribute_spec(self, writer: io.TextIOWrapper) -> None: + for attr_id, attr in self._spec["attributes"].items(): + prefix = f"{self.def_prefix}_ATTR_SENSOR_ATTR_{str(attr_id).upper()}" + writer.write( + f"#define {prefix}_EXISTS 1\n" + f"#define {prefix}_NAME \"{attr['name']}\"\n" + f"#define {prefix}_DESC \"{attr['description']}\"\n" + f"#define {prefix}_UNITS_NAME \"{attr['units']['name']}\"\n" + f"#define {prefix}_UNITS_SYMBOL \"{attr['units']['symbol']}\"\n" + ) + + def _print_trigger_spec(self, writer: io.TextIOWrapper) -> None: + for trig_id, trig in self._spec["triggers"].items(): + prefix = f"{self.def_prefix}_TRIG_SENSOR_TRIG_{str(trig_id).upper()}" + writer.write( + f"#define {prefix}_EXISTS 1\n" + f"#define {prefix}_NAME \"{trig['name']}\"\n" + f"#define {prefix}_DESC \"{trig['description']}\"\n" + ) + + def __str__(self) -> str: + writer = io.StringIO() + writer.write(f"""/* + * Definitions for {self.name} + */ +#define {self.def_prefix}_EXISTS 1 +#define {self.def_prefix}_CHAN_COUNT {self.chan_count} +#define {self.def_prefix}_ATTR_COUNT {self.attr_count} +#define {self.def_prefix}_TRIG_COUNT {self.trig_count} +""") + self._print_channel_spec(writer=writer) + self._print_attribute_spec(writer=writer) + self._print_trigger_spec(writer=writer) + return writer.getvalue() + + +class ZephyrHeader: + def __init__(self, channels: Sequence[Channel], attributes: Sequence[Attribute], triggers: Sequence[Trigger], sensors: Sequence[Sensor]) -> None: + self._sensors = sensors + self._channels = list(channels) + self._channels.sort(key=lambda chan: chan.value) + self._attributes = list(attributes) + self._attributes.sort(key=lambda attr: attr.value) + self._triggers = list(triggers) + self._triggers.sort(key=lambda attr: attr.value) + assert len(self._channels) == len(set(self._channels)) + assert len(self._attributes) == len(set(self._attributes)) + assert len(self._triggers) == len(set(self._triggers)) + + def __str__(self) -> str: + writer = io.StringIO() + writer.write("/* Auto-generated file, do not edit */\n") + writer.write("#ifndef _INCLUDE_ZEPHYR_DT_BINDINGS_SENSOR_H_\n") + writer.write("#define _INCLUDE_ZEPHYR_DT_BINDINGS_SENSOR_H_\n") + writer.write("\n") + for channel in self._channels: + writer.write(str(channel)) + writer.write("\n") + writer.write(Channel.tail_str()) + writer.write("\n") + for attribute in self._attributes: + writer.write(str(attribute)) + writer.write("\n") + writer.write(Attribute.tail_str()) + writer.write("\n") + for trigger in self._triggers: + writer.write(str(trigger)) + writer.write("\n") + writer.write(Trigger.tail_str()) + writer.write("\n") + for sensor in self._sensors: + writer.write(str(sensor)) + writer.write("#endif /* _INCLUDE_ZEPHYR_DT_BINDINGS_SENSOR_H_ */\n") + return writer.getvalue() + +if __name__ == "__main__": + main() diff --git a/tests/drivers/build_all/sensor/src/static_test.c b/tests/drivers/build_all/sensor/src/static_test.c new file mode 100644 index 00000000000000..e69de29bb2d1d6