Commit 5671129f authored by Luis Ubieda's avatar Luis Ubieda Committed by Anas Nashif
Browse files

bmp581: Add One-shot Read-Decode support

Decoding one-shot reads through submit API.

Signed-off-by: default avatarLuis Ubieda <luisf@croxel.com>
parent a32d5c15
Loading
Loading
Loading
Loading
+3 −0
Changes for drivers/sensor/bosch/bmp581/CMakeLists.txt: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -10,3 +10,6 @@ zephyr_library_sources(
	bmp581.c
	bmp581_bus.c
)
zephyr_library_sources_ifdef(CONFIG_SENSOR_ASYNC_API
	bmp581_decoder.c
)
+97 −0
Changes for drivers/sensor/bosch/bmp581/bmp581.c: 97 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@

#include "bmp581.h"
#include "bmp581_bus.h"
#include "bmp581_decoder.h"

#include <math.h>

@@ -586,10 +587,106 @@ static int bmp581_init(const struct device *dev)
	return ret;
}

#ifdef CONFIG_SENSOR_ASYNC_API

static void bmp581_complete_result(struct rtio *ctx, const struct rtio_sqe *sqe, void *arg)
{
	struct rtio_iodev_sqe *iodev_sqe = (struct rtio_iodev_sqe *)arg;
	struct rtio_cqe *cqe;
	int err = 0;

	do {
		cqe = rtio_cqe_consume(ctx);
		if (cqe != NULL) {
			err = cqe->result;
			rtio_cqe_release(ctx, cqe);
		}
	} while (cqe != NULL);

	if (err != 0) {
		rtio_iodev_sqe_err(iodev_sqe, err);
	} else {
		rtio_iodev_sqe_ok(iodev_sqe, 0);
	}
}

static void bmp581_submit_one_shot(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
{
	const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
	uint32_t min_buf_len = sizeof(struct bmp581_encoded_data);
	int err;
	uint8_t *buf;
	uint32_t buf_len;
	struct bmp581_encoded_data *edata;
	const struct bmp581_config *conf = dev->config;

	err = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len);
	CHECKIF((err < 0) || (buf_len < min_buf_len) || !buf) {
		LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len);
		rtio_iodev_sqe_err(iodev_sqe, err);
		return;
	}

	edata = (struct bmp581_encoded_data *)buf;

	err = bmp581_encode(dev, cfg, 0, buf);
	if (err != 0) {
		LOG_ERR("Failed to encode sensor data");
		rtio_iodev_sqe_err(iodev_sqe, err);
		return;
	}

	struct rtio_sqe *read_sqe;

	err = bmp581_prep_reg_read_rtio_async(&conf->bus, BMP5_REG_TEMP_DATA_XLSB,
					      edata->payload, sizeof(edata->payload),
					      &read_sqe);
	if (err < 0) {
		LOG_ERR("Failed to prepare async read operation");
		rtio_iodev_sqe_err(iodev_sqe, err);
		return;
	}
	read_sqe->flags |= RTIO_SQE_CHAINED;

	struct rtio_sqe *complete_sqe = rtio_sqe_acquire(conf->bus.rtio.ctx);

	if (!complete_sqe) {
		LOG_ERR("Failed to acquire completion SQE");
		rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
		rtio_sqe_drop_all(conf->bus.rtio.ctx);
		return;
	}

	rtio_sqe_prep_callback_no_cqe(complete_sqe,
				      bmp581_complete_result,
				      iodev_sqe,
				      (void *)dev);

	rtio_submit(conf->bus.rtio.ctx, 0);
}

static void bmp581_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
{
	const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;

	if (!cfg->is_streaming) {
		bmp581_submit_one_shot(dev, iodev_sqe);
	} else {
		LOG_ERR("Streaming not supported");
		rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP);
	}
}

#endif /* #ifdef CONFIG_SENSOR_ASYNC_API */

static DEVICE_API(sensor, bmp581_driver_api) = {
	.sample_fetch = bmp581_sample_fetch,
	.channel_get = bmp581_channel_get,
	.attr_set = bmp581_attr_set,
#ifdef CONFIG_SENSOR_ASYNC_API
	.submit = bmp581_submit,
	.get_decoder = bmp581_get_decoder,
#endif
};

#define BMP581_INIT(i)                                                                             \
+199 −0
Changes for drivers/sensor/bosch/bmp581/bmp581_decoder.c: 199 added lines, 0 removed lines.
Original line number Diff line number Diff line
/*
 * Copyright (c) 2025 Croxel, Inc.
 * Copyright (c) 2025 CogniPilot Foundation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/drivers/sensor_clock.h>
#include <zephyr/sys/util.h>
#include "bmp581.h"
#include "bmp581_decoder.h"

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(BMP581_DECODER, CONFIG_SENSOR_LOG_LEVEL);

static uint8_t bmp581_encode_channel(enum sensor_channel chan)
{
	uint8_t encode_bmask = 0;

	switch (chan) {
	case SENSOR_CHAN_AMBIENT_TEMP:
		encode_bmask |= BIT(0);
		break;
	case SENSOR_CHAN_PRESS:
		encode_bmask |= BIT(1);
		break;
	case SENSOR_CHAN_ALL:
		encode_bmask |= BIT(0) | BIT(1);
		break;
	default:
		break;
	}

	return encode_bmask;
}

int bmp581_encode(const struct device *dev,
		  const struct sensor_read_config *read_config,
		  uint8_t trigger_status,
		  uint8_t *buf)
{
	struct bmp581_encoded_data *edata = (struct bmp581_encoded_data *)buf;
	struct bmp581_data *data = dev->data;
	uint64_t cycles;
	int err;

	edata->header.channels = 0;
	edata->header.press_en = data->osr_odr_press_config.press_en;

	if (trigger_status) {
		edata->header.channels |= bmp581_encode_channel(SENSOR_CHAN_ALL);
	} else {
		const struct sensor_chan_spec *const channels = read_config->channels;
		size_t num_channels = read_config->count;

		for (size_t i = 0; i < num_channels; i++) {
			edata->header.channels |= bmp581_encode_channel(channels[i].chan_type);
		}
	}

	err = sensor_clock_get_cycles(&cycles);
	if (err != 0) {
		return err;
	}

	edata->header.events = trigger_status ? BIT(0) : 0;
	edata->header.timestamp = sensor_clock_cycles_to_ns(cycles);

	return 0;
}

static int bmp581_decoder_get_frame_count(const uint8_t *buffer,
					 struct sensor_chan_spec chan_spec,
					 uint16_t *frame_count)
{
	const struct bmp581_encoded_data *edata = (const struct bmp581_encoded_data *)buffer;

	if (chan_spec.chan_idx != 0) {
		return -ENOTSUP;
	}

	uint8_t channel_request = bmp581_encode_channel(chan_spec.chan_type);

	/* Filter unknown channels and having no data */
	if ((edata->header.channels & channel_request) != channel_request) {
		return -ENODATA;
	}

	*frame_count = 1;
	return 0;
}

static int bmp581_decoder_get_size_info(struct sensor_chan_spec chan_spec,
					size_t *base_size,
					size_t *frame_size)
{
	switch (chan_spec.chan_type) {
	case SENSOR_CHAN_AMBIENT_TEMP:
	case SENSOR_CHAN_PRESS:
		*base_size = sizeof(struct sensor_q31_data);
		*frame_size = sizeof(struct sensor_q31_sample_data);
		return 0;
	default:
		return -ENOTSUP;
	}
}

static int bmp581_decoder_decode(const uint8_t *buffer,
				struct sensor_chan_spec chan_spec,
				uint32_t *fit,
				uint16_t max_count,
				void *data_out)
{
	const struct bmp581_encoded_data *edata = (const struct bmp581_encoded_data *)buffer;
	uint8_t channel_request;

	if (*fit != 0) {
		return 0;
	}

	if (max_count == 0 || chan_spec.chan_idx != 0) {
		return -EINVAL;
	}

	channel_request = bmp581_encode_channel(chan_spec.chan_type);
	if ((channel_request & edata->header.channels) != channel_request) {
		return -ENODATA;
	}

	struct sensor_q31_data *out = data_out;

	out->header.base_timestamp_ns = edata->header.timestamp;
	out->header.reading_count = 1;

	switch (chan_spec.chan_type) {
	case SENSOR_CHAN_AMBIENT_TEMP: {
		/* Temperature is in data[2:0], data[2] is integer part */
		uint32_t raw_temp = ((uint32_t)edata->payload[2] << 16) |
				    ((uint16_t)edata->payload[1] << 8) |
				    edata->payload[0];
		int32_t raw_temp_signed = sign_extend(raw_temp, 23);

		out->shift = (31 - 16); /* 16 left shifts gives us the value in celsius */
		out->readings[0].value = raw_temp_signed;
		break;
	}
	case SENSOR_CHAN_PRESS:
		if (!edata->header.press_en) {
			return -ENODATA;
		}
		/* Shift by 10 bits because we'll divide by 1000 to make it kPa */
		uint64_t raw_press = (((uint32_t)edata->payload[5] << 16) |
				       ((uint16_t)edata->payload[4] << 8) |
				       edata->payload[3]);

		int64_t raw_press_signed = sign_extend_64(raw_press, 23);

		raw_press_signed *= 1024;
		raw_press_signed /= 1000;

		/* Original value was in Pa by left-shifting 6 spaces, but
		 * we've multiplied by 2^10 to not lose precision when
		 * converting to kPa. Hence, left-shift 16 spaces.
		 */
		out->shift = (31 - 6 - 10);
		out->readings[0].value = (int32_t)raw_press_signed;
		break;
	default:
		return -EINVAL;
	}

	*fit = 1;
	return 1;
}

static bool bmp581_decoder_has_trigger(const uint8_t *buffer, enum sensor_trigger_type trigger)
{
	const struct bmp581_encoded_data *edata = (const struct bmp581_encoded_data *)buffer;

	if ((trigger == SENSOR_TRIG_DATA_READY) && (edata->header.events != 0)) {
		return true;
	}

	return false;
}

SENSOR_DECODER_API_DT_DEFINE() = {
	.get_frame_count = bmp581_decoder_get_frame_count,
	.get_size_info = bmp581_decoder_get_size_info,
	.decode = bmp581_decoder_decode,
	.has_trigger = bmp581_decoder_has_trigger,
};

int bmp581_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder)
{
	ARG_UNUSED(dev);
	*decoder = &SENSOR_DECODER_NAME();
	return 0;
}
+33 −0
Changes for drivers/sensor/bosch/bmp581/bmp581_decoder.h: 33 added lines, 0 removed lines.
Original line number Diff line number Diff line
/*
 * Copyright (c) 2025 Croxel, Inc.
 * Copyright (c) 2025 CogniPilot Foundation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef ZEPHYR_DRIVERS_SENSOR_BMP581_DECODER_H_
#define ZEPHYR_DRIVERS_SENSOR_BMP581_DECODER_H_

#include <stdint.h>
#include <zephyr/drivers/sensor.h>
#include "bmp581.h"

struct bmp581_encoded_data {
	struct {
		uint8_t channels;
		uint8_t events;
		uint64_t timestamp;
		uint8_t press_en;
	} header;
	uint8_t payload[6]; /* 3 bytes temp + 3 bytes press */
};

int bmp581_encode(const struct device *dev,
		   const struct sensor_read_config *read_config,
		   uint8_t trigger_status,
		   uint8_t *buf);

int bmp581_get_decoder(const struct device *dev,
		       const struct sensor_decoder_api **decoder);

#endif /* ZEPHYR_DRIVERS_SENSOR_BMP581_DECODER_H_ */