diff --git a/drivers/sensor/akm09918c/akm09918c_emul.c b/drivers/sensor/akm09918c/akm09918c_emul.c index 02dc28b35db..adb237e1256 100644 --- a/drivers/sensor/akm09918c/akm09918c_emul.c +++ b/drivers/sensor/akm09918c/akm09918c_emul.c @@ -134,7 +134,7 @@ static int akm09918c_emul_init(const struct emul *target, const struct device *p } static int akm09918c_emul_backend_set_channel(const struct emul *target, enum sensor_channel ch, - q31_t value, int8_t shift) + const q31_t *value, int8_t shift) { if (!target || !target->data) { return -EINVAL; @@ -162,8 +162,9 @@ static int akm09918c_emul_backend_set_channel(const struct emul *target, enum se data->reg[AKM09918C_REG_ST1] |= AKM09918C_ST1_DRDY; /* Convert fixed-point Gauss values into microgauss and then into its bit representation */ - int32_t microgauss = (shift < 0 ? ((int64_t)value >> -shift) : ((int64_t)value << shift)) * - 1000000 / ((int64_t)INT32_MAX + 1); + int32_t microgauss = + (shift < 0 ? ((int64_t)*value >> -shift) : ((int64_t)*value << shift)) * 1000000 / + ((int64_t)INT32_MAX + 1); int16_t reg_val = CLAMP(microgauss, AKM09918C_MAGN_MIN_MICRO_GAUSS, AKM09918C_MAGN_MAX_MICRO_GAUSS) / diff --git a/drivers/sensor/amd_sb_tsi/sb_tsi_emul.c b/drivers/sensor/amd_sb_tsi/sb_tsi_emul.c index fee197f1d90..1c12942c876 100644 --- a/drivers/sensor/amd_sb_tsi/sb_tsi_emul.c +++ b/drivers/sensor/amd_sb_tsi/sb_tsi_emul.c @@ -100,7 +100,7 @@ static int sb_tsi_emul_init(const struct emul *target, const struct device *pare } static int sb_tsi_emul_set_channel(const struct emul *target, enum sensor_channel chan, - q31_t value, int8_t shift) + const q31_t *value, int8_t shift) { struct sb_tsi_emul_data *data = target->data; int64_t scaled_value; @@ -111,7 +111,7 @@ static int sb_tsi_emul_set_channel(const struct emul *target, enum sensor_channe return -ENOTSUP; } - scaled_value = (int64_t)value << shift; + scaled_value = (int64_t)*value << shift; millicelsius = scaled_value * 1000 / ((int64_t)INT32_MAX + 1); reg_value = CLAMP(millicelsius / 125, 0, 0x7ff); diff --git a/drivers/sensor/bmi160/emul_bmi160.c b/drivers/sensor/bmi160/emul_bmi160.c index 3870346cd30..50530eae9dd 100644 --- a/drivers/sensor/bmi160/emul_bmi160.c +++ b/drivers/sensor/bmi160/emul_bmi160.c @@ -17,10 +17,12 @@ LOG_MODULE_REGISTER(bosch_bmi160); #include #include #include +#include #include #include #include #include +#include /** Run-time data used by the emulator */ struct bmi160_emul_data { @@ -42,25 +44,18 @@ struct bmi160_emul_cfg { }; /* Names for the PMU components */ -static const char *const pmu_name[] = { "acc", "gyr", "mag", "INV" }; +static const char *const pmu_name[] = {"acc", "gyr", "mag", "INV"}; -static void sample_read(union bmi160_sample *buf) +int emul_bmi160_get_reg_value(const struct emul *target, int reg_number, uint8_t *out, size_t count) { - /* - * Use hard-coded scales to get values just above 0, 1, 2 and - * 3, 4, 5. Values are stored in little endianness. - * gyr[x] = 0x0b01 // 3 * 1000000 / BMI160_GYR_SCALE(2000) + 1 - * gyr[y] = 0x0eac // 4 * 1000000 / BMI160_GYR_SCALE(2000) + 1 - * gyr[z] = 0x1257 // 5 * 1000000 / BMI160_GYR_SCALE(2000) + 1 - * acc[x] = 0x0001 // 0 * 1000000 / BMI160_ACC_SCALE(2) + 1 - * acc[y] = 0x0689 // 1 * 1000000 / BMI160_ACC_SCALE(2) + 1 - * acc[z] = 0x0d11 // 2 * 1000000 / BMI160_ACC_SCALE(2) + 1 - */ - static uint8_t raw_data[] = { 0x01, 0x0b, 0xac, 0x0e, 0x57, 0x12, - 0x01, 0x00, 0x89, 0x06, 0x11, 0x0d }; + const struct bmi160_emul_cfg *cfg = target->cfg; - LOG_INF("Sample read"); - memcpy(buf->raw, raw_data, ARRAY_SIZE(raw_data)); + if (reg_number < 0 || reg_number + count > BMI160_REG_COUNT) { + return -EINVAL; + } + + memcpy(out, cfg->reg + reg_number, count); + return 0; } static void reg_write(const struct emul *target, int regn, int val) @@ -68,25 +63,25 @@ static void reg_write(const struct emul *target, int regn, int val) struct bmi160_emul_data *data = target->data; const struct bmi160_emul_cfg *cfg = target->cfg; - LOG_INF("write %x = %x", regn, val); + LOG_DBG("write %x = %x", regn, val); cfg->reg[regn] = val; switch (regn) { case BMI160_REG_ACC_CONF: - LOG_INF(" * acc conf"); + LOG_DBG(" * acc conf"); break; case BMI160_REG_ACC_RANGE: - LOG_INF(" * acc range"); + LOG_DBG(" * acc range"); break; case BMI160_REG_GYR_CONF: - LOG_INF(" * gyr conf"); + LOG_DBG(" * gyr conf"); break; case BMI160_REG_GYR_RANGE: - LOG_INF(" * gyr range"); + LOG_DBG(" * gyr range"); break; case BMI160_REG_CMD: switch (val) { case BMI160_CMD_SOFT_RESET: - LOG_INF(" * soft reset"); + LOG_DBG(" * soft reset"); break; default: if ((val & BMI160_CMD_PMU_BIT) == BMI160_CMD_PMU_BIT) { @@ -108,16 +103,16 @@ static void reg_write(const struct emul *target, int regn, int val) } data->pmu_status &= 3 << shift; data->pmu_status |= pmu_val << shift; - LOG_INF(" * pmu %s = %x, new status %x", pmu_name[which], pmu_val, + LOG_DBG(" * pmu %s = %x, new status %x", pmu_name[which], pmu_val, data->pmu_status); } else { - LOG_INF("Unknown command %x", val); + LOG_DBG("Unknown command %x", val); } break; } break; default: - LOG_INF("Unknown write %x", regn); + LOG_DBG("Unknown write %x", regn); } } @@ -127,39 +122,39 @@ static int reg_read(const struct emul *target, int regn) const struct bmi160_emul_cfg *cfg = target->cfg; int val; - LOG_INF("read %x =", regn); + LOG_DBG("read %x =", regn); val = cfg->reg[regn]; switch (regn) { case BMI160_REG_CHIPID: - LOG_INF(" * get chipid"); + LOG_DBG(" * get chipid"); break; case BMI160_REG_PMU_STATUS: - LOG_INF(" * get pmu"); + LOG_DBG(" * get pmu"); val = data->pmu_status; break; case BMI160_REG_STATUS: - LOG_INF(" * status"); + LOG_DBG(" * status"); val |= BMI160_DATA_READY_BIT_MASK; break; case BMI160_REG_ACC_CONF: - LOG_INF(" * acc conf"); + LOG_DBG(" * acc conf"); break; case BMI160_REG_GYR_CONF: - LOG_INF(" * gyr conf"); + LOG_DBG(" * gyr conf"); break; case BMI160_SPI_START: - LOG_INF(" * Bus start"); + LOG_DBG(" * Bus start"); break; case BMI160_REG_ACC_RANGE: - LOG_INF(" * acc range"); + LOG_DBG(" * acc range"); break; case BMI160_REG_GYR_RANGE: - LOG_INF(" * gyr range"); + LOG_DBG(" * gyr range"); break; default: - LOG_INF("Unknown read %x", regn); + LOG_DBG("Unknown read %x", regn); } - LOG_INF(" = %x", val); + LOG_DBG(" = %x", val); return val; } @@ -206,23 +201,26 @@ static int bmi160_emul_io_spi(const struct emul *target, const struct spi_config break; case BMI160_SAMPLE_SIZE: if (regn & BMI160_REG_READ) { - sample_read(rxd->buf); + for (int i = 0; i < BMI160_SAMPLE_SIZE; ++i) { + ((uint8_t *)rxd->buf)[i] = reg_read( + target, (regn & BMI160_REG_MASK) + i); + } } else { - LOG_INF("Unknown sample write"); + LOG_DBG("Unknown sample write"); } break; default: - LOG_INF("Unknown A txd->len %d", txd->len); + LOG_DBG("Unknown A txd->len %d", txd->len); break; } break; default: - LOG_INF("Unknown tx->len %d", tx->len); + LOG_DBG("Unknown tx->len %d", tx->len); break; } break; default: - LOG_INF("Unknown tx_bufs->count %d", count); + LOG_DBG("Unknown tx_bufs->count %d", count); break; } @@ -235,7 +233,6 @@ static int bmi160_emul_transfer_i2c(const struct emul *target, struct i2c_msg *m int addr) { struct bmi160_emul_data *data; - unsigned int val; data = target->data; @@ -257,17 +254,8 @@ static int bmi160_emul_transfer_i2c(const struct emul *target, struct i2c_msg *m /* Now process the 'read' part of the message */ msgs++; if (msgs->flags & I2C_MSG_READ) { - switch (msgs->len) { - case 1: - val = reg_read(target, data->cur_reg); - msgs->buf[0] = val; - break; - case BMI160_SAMPLE_SIZE: - sample_read((void *)msgs->buf); - break; - default: - LOG_ERR("Unexpected msg1 length %d", msgs->len); - return -EIO; + for (int i = 0; i < msgs->len; ++i) { + msgs->buf[i] = reg_read(target, data->cur_reg + i); } } else { if (msgs->len != 1) { @@ -299,6 +287,304 @@ static struct i2c_emul_api bmi160_emul_api_i2c = { }; #endif +static int bmi160_emul_backend_set_channel(const struct emul *target, enum sensor_channel ch, + const q31_t *value, int8_t shift) +{ + const struct bmi160_emul_cfg *cfg = target->cfg; + int64_t intermediate = *value; + q31_t scale; + int8_t scale_shift = 0; + int reg_lsb; + + switch (ch) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + reg_lsb = BMI160_REG_DATA_ACC_X + (ch - SENSOR_CHAN_ACCEL_X) * 2; + scale = 0x4e7404ea; + + switch (FIELD_GET(GENMASK(3, 0), cfg->reg[BMI160_REG_ACC_RANGE])) { + case BMI160_ACC_RANGE_4G: + scale_shift = 6; + break; + case BMI160_ACC_RANGE_8G: + scale_shift = 7; + break; + case BMI160_ACC_RANGE_16G: + scale_shift = 8; + break; + default: + scale_shift = 5; + break; + } + break; + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + reg_lsb = BMI160_REG_DATA_GYR_X + (ch - SENSOR_CHAN_GYRO_X) * 2; + scale = 0x45d02bea; + + switch (FIELD_GET(GENMASK(2, 0), cfg->reg[BMI160_REG_GYR_RANGE])) { + case BMI160_GYR_RANGE_2000DPS: + scale_shift = 6; + break; + case BMI160_GYR_RANGE_1000DPS: + scale_shift = 5; + break; + case BMI160_GYR_RANGE_500DPS: + scale_shift = 4; + break; + case BMI160_GYR_RANGE_250DPS: + scale_shift = 3; + break; + case BMI160_GYR_RANGE_125DPS: + scale_shift = 2; + break; + default: + return -EINVAL; + } + break; + case SENSOR_CHAN_DIE_TEMP: + reg_lsb = BMI160_REG_TEMPERATURE0; + scale = 0x8000; + scale_shift = 7; + break; + default: + return -EINVAL; + } + + if (shift < scale_shift) { + /* Original value doesn't have enough int bits, fix it */ + intermediate >>= scale_shift - shift; + } else if (shift > 0 && shift > scale_shift) { + /* Original value might be out-of-bounds, fix it (we're going to lose precision) */ + intermediate <<= shift - scale_shift; + } + + if (ch == SENSOR_CHAN_DIE_TEMP) { + /* Need to subtract 23C */ + intermediate -= INT64_C(23) << (31 - scale_shift); + } + + intermediate = + CLAMP(DIV_ROUND_CLOSEST(intermediate * INT16_MAX, scale), INT16_MIN, INT16_MAX); + + cfg->reg[reg_lsb] = FIELD_GET(GENMASK64(7, 0), intermediate); + cfg->reg[reg_lsb + 1] = FIELD_GET(GENMASK64(15, 8), intermediate); + return 0; +} + +static int bmi160_emul_backend_get_sample_range(const struct emul *target, enum sensor_channel ch, + q31_t *lower, q31_t *upper, q31_t *epsilon, + int8_t *shift) +{ + const struct bmi160_emul_cfg *cfg = target->cfg; + + switch (ch) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: { + uint8_t acc_range = cfg->reg[BMI160_REG_ACC_RANGE]; + + switch (acc_range) { + case BMI160_ACC_RANGE_2G: + *shift = 5; + break; + case BMI160_ACC_RANGE_4G: + *shift = 6; + break; + case BMI160_ACC_RANGE_8G: + *shift = 7; + break; + case BMI160_ACC_RANGE_16G: + *shift = 8; + break; + default: + return -EINVAL; + } + int64_t intermediate = ((int64_t)(2 * 9.80665 * INT32_MAX)) >> 5; + + *upper = intermediate; + *lower = -(*upper); + *epsilon = intermediate * 2 / (1 << (16 - *shift)); + return 0; + } + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + case SENSOR_CHAN_GYRO_XYZ: { + uint8_t gyro_range = cfg->reg[BMI160_REG_GYR_RANGE]; + + switch (gyro_range) { + case BMI160_GYR_RANGE_125DPS: + *shift = 2; + break; + case BMI160_GYR_RANGE_250DPS: + *shift = 3; + break; + case BMI160_GYR_RANGE_500DPS: + *shift = 4; + break; + case BMI160_GYR_RANGE_1000DPS: + *shift = 5; + break; + case BMI160_GYR_RANGE_2000DPS: + *shift = 6; + break; + default: + return -EINVAL; + } + + int64_t intermediate = (int64_t)(125 * 3.141592654 * INT32_MAX / 180) >> 2; + + *upper = intermediate; + *lower = -(*upper); + *epsilon = intermediate * 2 / (1 << (16 - *shift)); + return 0; + } + default: + return -EINVAL; + } + +} + +static int bmi160_emul_backend_set_offset(const struct emul *target, enum sensor_channel ch, + const q31_t *values, int8_t shift) +{ + if (ch != SENSOR_CHAN_ACCEL_XYZ && ch != SENSOR_CHAN_GYRO_XYZ) { + return -EINVAL; + } + + const struct bmi160_emul_cfg *cfg = target->cfg; + q31_t scale; + int8_t scale_shift = 0; + + if (values[0] == 0 && values[1] == 0 && values[2] == 0) { + if (ch == SENSOR_CHAN_ACCEL_XYZ) { + cfg->reg[BMI160_REG_OFFSET_EN] &= ~BIT(BMI160_ACC_OFS_EN_POS); + } else { + cfg->reg[BMI160_REG_OFFSET_EN] &= ~BIT(BMI160_GYR_OFS_EN_POS); + } + } else { + if (ch == SENSOR_CHAN_ACCEL_XYZ) { + cfg->reg[BMI160_REG_OFFSET_EN] |= BIT(BMI160_ACC_OFS_EN_POS); + } else { + cfg->reg[BMI160_REG_OFFSET_EN] |= BIT(BMI160_GYR_OFS_EN_POS); + } + } + + if (ch == SENSOR_CHAN_ACCEL_XYZ) { + /* + * bits = (values[i]mps2 / 9.80665g/mps2) / 0.0039g + * = values[i] / 0.038245935mps2/bit + * 0.038245935 in Q31 format is 0x4e53e28 with shift 0 + */ + scale = 0x4e53e28; + } else { + /* + * bits = (values[i]rad/s * 180 / pi) / 0.061deg/s + * = values[i] / 0.001064651rad/s + */ + scale = 0x22e2f0; + } + + for (int i = 0; i < 3; ++i) { + int64_t intermediate = values[i]; + + if (shift > scale_shift) { + /* Input uses a bigger scale, we need to increase its value to match */ + intermediate <<= (shift - scale_shift); + } else if (shift < scale_shift) { + /* Scale uses a bigger shift, we need to decrease its value to match */ + scale >>= (scale_shift - shift); + } + + int64_t reg_value = intermediate / scale; + + __ASSERT_NO_MSG(ch != SENSOR_CHAN_ACCEL_XYZ || + (reg_value >= INT8_MIN && reg_value <= INT8_MAX)); + __ASSERT_NO_MSG(ch != SENSOR_CHAN_GYRO_XYZ || + (reg_value >= -0x1ff - 1 && reg_value <= 0x1ff)); + if (ch == SENSOR_CHAN_ACCEL_XYZ) { + cfg->reg[BMI160_REG_OFFSET_ACC_X + i] = reg_value & 0xff; + } else { + cfg->reg[BMI160_REG_OFFSET_GYR_X + i] = reg_value & 0xff; + cfg->reg[BMI160_REG_OFFSET_EN] = + (cfg->reg[BMI160_REG_OFFSET_EN] & ~GENMASK(i * 2 + 1, i * 2)) | + (reg_value & GENMASK(9, 8)); + } + } + + return 0; +} + +static int bmi160_emul_backend_set_attribute(const struct emul *target, enum sensor_channel ch, + enum sensor_attribute attribute, const void *value) +{ + if (attribute == SENSOR_ATTR_OFFSET && + (ch == SENSOR_CHAN_ACCEL_XYZ || ch == SENSOR_CHAN_GYRO_XYZ)) { + const struct sensor_three_axis_attribute *attribute_value = value; + + return bmi160_emul_backend_set_offset(target, ch, attribute_value->values, + attribute_value->shift); + } + return -EINVAL; +} + +static int bmi160_emul_backend_get_attribute_metadata(const struct emul *target, + enum sensor_channel ch, + enum sensor_attribute attribute, q31_t *min, + q31_t *max, q31_t *increment, int8_t *shift) +{ + ARG_UNUSED(target); + switch (ch) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + if (attribute == SENSOR_ATTR_OFFSET) { + /* Offset uses 3.9mg per bit in an 8 bit register: + * 0.0039g * 9.8065m/s2: yields the increment in SI units + * * INT8_MIN (or MAX) : yields the minimum (or maximum) values + * * INT32_MAX >> 3 : converts to q31 format within range [-8, 8] + */ + *min = (q31_t)((int64_t)(0.0039 * 9.8065 * INT8_MIN * INT32_MAX) >> 3); + *max = (q31_t)((int64_t)(0.0039 * 9.8065 * INT8_MAX * INT32_MAX) >> 3); + *increment = (q31_t)((int64_t)(0.0039 * 9.8065 * INT32_MAX) >> 3); + *shift = 3; + return 0; + } + return -EINVAL; + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + case SENSOR_CHAN_GYRO_XYZ: + if (attribute == SENSOR_ATTR_OFFSET) { + /* Offset uses 0.061deg/s per bit in an 10 bit register: + * 0.061deg/s * pi / 180: yields the increment in SI units + * * INT10_MIN (or MAX) : yields the minimum (or maximum) values + * * INT32_MAX : converts to q31 format within range [-1, 1] + */ + *min = (q31_t)(0.061 * 3.141593 / 180.0 * -512 * INT32_MAX); + *max = (q31_t)(0.061 * 3.141593 / 180.0 * 511 * INT32_MAX); + *increment = (q31_t)(0.061 * 3.141593 / 180.0 * INT32_MAX); + *shift = 0; + return 0; + } + return -EINVAL; + default: + return -EINVAL; + } +} + +static const struct emul_sensor_backend_api backend_api = { + .set_channel = bmi160_emul_backend_set_channel, + .get_sample_range = bmi160_emul_backend_get_sample_range, + .set_attribute = bmi160_emul_backend_set_attribute, + .get_attribute_metadata = bmi160_emul_backend_get_attribute_metadata, +}; + static int emul_bosch_bmi160_init(const struct emul *target, const struct device *parent) { const struct bmi160_emul_cfg *cfg = target->cfg; @@ -320,22 +606,19 @@ static int emul_bosch_bmi160_init(const struct emul *target, const struct device #define BMI160_EMUL_DEFINE(n, bus_api) \ EMUL_DT_INST_DEFINE(n, emul_bosch_bmi160_init, &bmi160_emul_data_##n, \ - &bmi160_emul_cfg_##n, &bus_api, NULL) + &bmi160_emul_cfg_##n, &bus_api, &backend_api) /* Instantiation macros used when a device is on a SPI bus */ #define BMI160_EMUL_SPI(n) \ BMI160_EMUL_DATA(n) \ - static const struct bmi160_emul_cfg bmi160_emul_cfg_##n = { \ - .reg = bmi160_emul_reg_##n, \ - .chipsel = \ - DT_INST_REG_ADDR(n) }; \ + static const struct bmi160_emul_cfg bmi160_emul_cfg_##n = { \ + .reg = bmi160_emul_reg_##n, .chipsel = DT_INST_REG_ADDR(n)}; \ BMI160_EMUL_DEFINE(n, bmi160_emul_api_spi) #define BMI160_EMUL_I2C(n) \ BMI160_EMUL_DATA(n) \ - static const struct bmi160_emul_cfg bmi160_emul_cfg_##n = { \ - .reg = bmi160_emul_reg_##n, \ - .addr = DT_INST_REG_ADDR(n) }; \ + static const struct bmi160_emul_cfg bmi160_emul_cfg_##n = {.reg = bmi160_emul_reg_##n, \ + .addr = DT_INST_REG_ADDR(n)}; \ BMI160_EMUL_DEFINE(n, bmi160_emul_api_i2c) /* diff --git a/drivers/sensor/bmi160/emul_bmi160.h b/drivers/sensor/bmi160/emul_bmi160.h new file mode 100644 index 00000000000..e16b089c461 --- /dev/null +++ b/drivers/sensor/bmi160/emul_bmi160.h @@ -0,0 +1,98 @@ +/* + * Copyright 2020 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_BMI160_EMUL_BMI160_H_ +#define ZEPHYR_DRIVERS_SENSOR_BMI160_EMUL_BMI160_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Check if I2C messages are touching a given register (R or W) + * + * @param[in] msgs The I2C messages in question + * @param[in] num_msgs The number of messages in the @p msgs array + * @param[in] reg The register to check for + * @return True if @p reg is either read or written to + * @return False otherwise + */ +__maybe_unused static bool emul_bmi160_i2c_is_touching_reg(struct i2c_msg *msgs, int num_msgs, + uint32_t reg) +{ + if (num_msgs != 2) { + return false; + } + if (msgs[0].len != 1) { + return false; + } + if (i2c_is_read_op(msgs)) { + return false; + } + + uint8_t start_reg = msgs[0].buf[0]; + uint8_t read_len = msgs[1].len; + + return (start_reg <= reg) && (reg < start_reg + read_len); +} + +/** + * @brief Check if I2C messages are reading a specific register. + * + * @param[in] msgs The I2C messages in question + * @param[in] num_msgs The number of messages in the @p msgs array + * @param[in] reg The register to check for + * @return True if @p reg is read + * @return False otherwise + */ +__maybe_unused static bool emul_bmi160_i2c_is_reading_reg(struct i2c_msg *msgs, int num_msgs, + uint32_t reg) +{ + if (!emul_bmi160_i2c_is_touching_reg(msgs, num_msgs, reg)) { + return false; + } + return i2c_is_read_op(&msgs[1]); +} + +/** + * @brief Check if I2C messages are writing to a specific register. + * + * @param[in] msgs The I2C messages in question + * @param[in] num_msgs The number of messages in the @p msgs array + * @param[in] reg The register to check for + * @return True if @p reg is written + * @return False otherwise + */ +__maybe_unused static bool emul_bmi160_i2c_is_writing_reg(struct i2c_msg *msgs, int num_msgs, + uint32_t reg) +{ + if (!emul_bmi160_i2c_is_touching_reg(msgs, num_msgs, reg)) { + return false; + } + return !i2c_is_read_op(&msgs[1]); +} + +/** + * @brief Get the internal register value of the emulator + * + * @param[in] target The emulator in question + * @param[in] reg_number The register number to start reading at + * @param[out] out Buffer to store the values into + * @param[in] count The number of registers to read + * @return 0 on success + * @return < 0 on error + */ +int emul_bmi160_get_reg_value(const struct emul *target, int reg_number, uint8_t *out, + size_t count); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_DRIVERS_SENSOR_BMI160_EMUL_BMI160_H_ */ diff --git a/drivers/sensor/f75303/f75303_emul.c b/drivers/sensor/f75303/f75303_emul.c index 3e33a7b6f7b..5bab9cd4294 100644 --- a/drivers/sensor/f75303/f75303_emul.c +++ b/drivers/sensor/f75303/f75303_emul.c @@ -104,7 +104,7 @@ static int f75303_emul_init(const struct emul *target, const struct device *pare } static int f75303_emul_set_channel(const struct emul *target, enum sensor_channel chan, - q31_t value, int8_t shift) + const q31_t *value, int8_t shift) { struct f75303_emul_data *data = target->data; int64_t scaled_value; @@ -129,7 +129,7 @@ static int f75303_emul_set_channel(const struct emul *target, enum sensor_channe return -ENOTSUP; } - scaled_value = (int64_t)value << shift; + scaled_value = (int64_t)*value << shift; millicelsius = scaled_value * 1000 / ((int64_t)INT32_MAX + 1); reg_value = CLAMP(millicelsius / 125, 0, 0x7ff); diff --git a/drivers/sensor/icm42688/icm42688_emul.c b/drivers/sensor/icm42688/icm42688_emul.c index e01b90742e3..732af64289b 100644 --- a/drivers/sensor/icm42688/icm42688_emul.c +++ b/drivers/sensor/icm42688/icm42688_emul.c @@ -329,7 +329,7 @@ static int icm42688_emul_backend_get_sample_range(const struct emul *target, enu } static int icm42688_emul_backend_set_channel(const struct emul *target, enum sensor_channel ch, - q31_t value, int8_t shift) + const q31_t *value, int8_t shift) { if (!target || !target->data) { return -EINVAL; @@ -341,7 +341,7 @@ static int icm42688_emul_backend_set_channel(const struct emul *target, enum sen uint8_t reg_addr; int32_t reg_val; int64_t value_unshifted = - shift < 0 ? ((int64_t)value >> -shift) : ((int64_t)value << shift); + shift < 0 ? ((int64_t)*value >> -shift) : ((int64_t)*value << shift); switch (ch) { case SENSOR_CHAN_DIE_TEMP: diff --git a/include/zephyr/drivers/emul.h b/include/zephyr/drivers/emul.h index 6dfb281e465..01297666dac 100644 --- a/include/zephyr/drivers/emul.h +++ b/include/zephyr/drivers/emul.h @@ -174,6 +174,18 @@ struct emul { */ #define EMUL_DT_GET(node_id) (&EMUL_DT_NAME_GET(node_id)) +/** + * @brief Utility macro to obtain an optional reference to an emulator + * + * If the node identifier referes to a node with status `okay`, this returns `EMUL_DT_GET(node_id)`. + * Otherwise, it returns `NULL`. + * + * @param node_id A devicetree node identifier + * @return a @ref emul reference for the node identifier, which may be `NULL`. + */ +#define EMUL_DT_GET_OR_NULL(node_id) \ + COND_CODE_1(DT_NODE_HAS_STATUS(node_id, okay), (EMUL_DT_GET(node_id)), (NULL)) + /** * @brief Set up a list of emulators * diff --git a/include/zephyr/drivers/emul_sensor.h b/include/zephyr/drivers/emul_sensor.h index 44a497ee401..30345cc6a0b 100644 --- a/include/zephyr/drivers/emul_sensor.h +++ b/include/zephyr/drivers/emul_sensor.h @@ -5,6 +5,7 @@ #include #include +#include #include @@ -26,11 +27,18 @@ */ __subsystem struct emul_sensor_backend_api { /** Sets a given fractional value for a given sensor channel. */ - int (*set_channel)(const struct emul *target, enum sensor_channel ch, q31_t value, + int (*set_channel)(const struct emul *target, enum sensor_channel ch, const q31_t *value, int8_t shift); /** Retrieve a range of sensor values to use with test. */ int (*get_sample_range)(const struct emul *target, enum sensor_channel ch, q31_t *lower, q31_t *upper, q31_t *epsilon, int8_t *shift); + /** Set the attribute value(s) of a given chanel. */ + int (*set_attribute)(const struct emul *target, enum sensor_channel ch, + enum sensor_attribute attribute, const void *value); + /** Get metadata about an attribute. */ + int (*get_attribute_metadata)(const struct emul *target, enum sensor_channel ch, + enum sensor_attribute attribute, q31_t *min, q31_t *max, + q31_t *increment, int8_t *shift); }; /** * @endcond @@ -61,7 +69,7 @@ static inline bool emul_sensor_backend_is_supported(const struct emul *target) * @return -ERANGE if provided value is not in the sensor's supported range */ static inline int emul_sensor_backend_set_channel(const struct emul *target, enum sensor_channel ch, - q31_t value, int8_t shift) + const q31_t *value, int8_t shift) { if (!target || !target->backend_api) { return -ENOTSUP; @@ -108,6 +116,68 @@ static inline int emul_sensor_backend_get_sample_range(const struct emul *target return -ENOTSUP; } +/** + * @brief Set the emulator's attribute values + * + * @param[in] target Pointer to emulator instance to operate on + * @param[in] ch The channel to request info for. If \p ch is unsupported, return `-ENOTSUP` + * @param[in] attribute The attribute to set + * @param[in] value the value to use (cast according to the channel/attribute pair) + * @return 0 is successful + * @return < 0 on error + */ +static inline int emul_sensor_backend_set_attribute(const struct emul *target, + enum sensor_channel ch, + enum sensor_attribute attribute, + const void *value) +{ + if (!target || !target->backend_api) { + return -ENOTSUP; + } + + struct emul_sensor_backend_api *api = (struct emul_sensor_backend_api *)target->backend_api; + + if (api->set_attribute == NULL) { + return -ENOTSUP; + } + return api->set_attribute(target, ch, attribute, value); +} + +/** + * @brief Get metadata about an attribute. + * + * Information provided by this function includes the minimum/maximum values of the attribute as + * well as the increment (value per LSB) which can be used as an epsilon when comparing results. + * + * @param[in] target Pointer to emulator instance to operate on + * @param[in] ch The channel to request info for. If \p ch is unsupported, return '-ENOTSUP' + * @param[in] attribute The attribute to request info for. If \p attribute is unsupported, return + * '-ENOTSUP' + * @param[out] min The minimum value the attribute can be set to + * @param[out] max The maximum value the attribute can be set to + * @param[out] increment The value that the attribute increses by for every LSB + * @param[out] shift The shift for \p min, \p max, and \p increment + * @return 0 on SUCCESS + * @return < 0 on error + */ +static inline int emul_sensor_backend_get_attribute_metadata(const struct emul *target, + enum sensor_channel ch, + enum sensor_attribute attribute, + q31_t *min, q31_t *max, + q31_t *increment, int8_t *shift) +{ + if (!target || !target->backend_api) { + return -ENOTSUP; + } + + struct emul_sensor_backend_api *api = (struct emul_sensor_backend_api *)target->backend_api; + + if (api->get_attribute_metadata == NULL) { + return -ENOTSUP; + } + return api->get_attribute_metadata(target, ch, attribute, min, max, increment, shift); +} + /** * @} */ diff --git a/include/zephyr/drivers/sensor_attribute_types.h b/include/zephyr/drivers/sensor_attribute_types.h new file mode 100644 index 00000000000..8ccb9dba110 --- /dev/null +++ b/include/zephyr/drivers/sensor_attribute_types.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_ATTRIBUTE_TYPES_H +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_ATTRIBUTE_TYPES_H + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Used by the following channel/attribute pairs: + * - SENSOR_CHAN_ACCEL_XYZ + * - SENSOR_ATTR_OFFSET + * - SENSOR_CHAN_GYRO_XYZ + * - SENSOR_ATTR_OFFSET + * - SENSOR_CHAN_MAGN_XYZ + * - SENSOR_ATTR_OFFSET + */ +struct sensor_three_axis_attribute { + int8_t shift; + union { + struct { + q31_t x; + q31_t y; + q31_t z; + }; + q31_t values[3]; + }; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_ATTRIBUTE_TYPES_H */ diff --git a/tests/drivers/build_all/sensor/src/generic_test.c b/tests/drivers/build_all/sensor/src/generic_test.c index 9a1dc537535..2d5e0c1df86 100644 --- a/tests/drivers/build_all/sensor/src/generic_test.c +++ b/tests/drivers/build_all/sensor/src/generic_test.c @@ -158,7 +158,7 @@ static void run_generic_test(const struct device *dev) enum sensor_channel ch = iodev_all_channels[i]; rv = emul_sensor_backend_set_channel( - emul, ch, channel_table[ch].expected_values[iteration], + emul, ch, &channel_table[ch].expected_values[iteration], channel_table[ch].expected_value_shift); zassert_ok( rv,