Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sensors: Implement a vertical decoder API #61022

Merged
merged 5 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion drivers/sensor/akm09918c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
zephyr_library()

zephyr_library_sources(akm09918c.c)
zephyr_library_sources_ifdef(CONFIG_EMUL_AKM09918C akm09918c_emul.c)
zephyr_library_sources_ifdef(CONFIG_EMUL_AKM09918C akm09918c_emul.c)
zephyr_library_sources_ifdef(CONFIG_SENSOR_ASYNC_API akm09918c_async.c akm09918c_decoder.c)
zephyr_include_directories_ifdef(CONFIG_EMUL_AKM09918C .)
50 changes: 37 additions & 13 deletions drivers/sensor/akm09918c/akm09918c.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <zephyr/drivers/i2c.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/sensor_data_types.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
Expand All @@ -19,15 +20,26 @@

LOG_MODULE_REGISTER(AKM09918C, CONFIG_SENSOR_LOG_LEVEL);

static int akm09918c_sample_fetch(const struct device *dev, enum sensor_channel chan)
/**
* @brief Perform the bus transaction to fetch samples
*
* @param dev Sensor device to operate on
* @param chan Channel ID to fetch
* @param x Location to write X channel sample.
* @param y Location to write Y channel sample.
* @param z Location to write Z channel sample.
* @return int 0 if successful or error code
*/
int akm09918c_sample_fetch_helper(const struct device *dev, enum sensor_channel chan, int16_t *x,
int16_t *y, int16_t *z)
{
struct akm09918c_data *data = dev->data;
const struct akm09918c_config *cfg = dev->config;
uint8_t buf[9] = {0};

if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_MAGN_X && chan != SENSOR_CHAN_MAGN_Y &&
chan != SENSOR_CHAN_MAGN_Z && chan != SENSOR_CHAN_MAGN_XYZ) {
LOG_WRN("Invalid channel %d", chan);
LOG_DBG("Invalid channel %d", chan);
return -EINVAL;
}

Expand All @@ -54,13 +66,21 @@ static int akm09918c_sample_fetch(const struct device *dev, enum sensor_channel
return -EBUSY;
}

data->x_sample = sys_le16_to_cpu(buf[1] | (buf[2] << 8));
data->y_sample = sys_le16_to_cpu(buf[3] | (buf[4] << 8));
data->z_sample = sys_le16_to_cpu(buf[5] | (buf[6] << 8));
*x = sys_le16_to_cpu(buf[1] | (buf[2] << 8));
*y = sys_le16_to_cpu(buf[3] | (buf[4] << 8));
*z = sys_le16_to_cpu(buf[5] | (buf[6] << 8));

return 0;
}

static int akm09918c_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
struct akm09918c_data *data = dev->data;

return akm09918c_sample_fetch_helper(dev, chan, &data->x_sample, &data->y_sample,
&data->z_sample);
}

static void akm09918c_convert(struct sensor_value *val, int16_t sample)
{
int64_t conv_val = sample * AKM09918C_MICRO_GAUSS_PER_BIT;
Expand All @@ -85,7 +105,7 @@ static int akm09918c_channel_get(const struct device *dev, enum sensor_channel c
} else if (chan == SENSOR_CHAN_MAGN_Z) {
akm09918c_convert(val, data->z_sample);
} else {
LOG_WRN("Invalid channel %d", chan);
LOG_DBG("Invalid channel %d", chan);
asemjonovs marked this conversation as resolved.
Show resolved Hide resolved
teburd marked this conversation as resolved.
Show resolved Hide resolved
return -ENOTSUP;
}

Expand Down Expand Up @@ -149,13 +169,6 @@ static int akm09918c_attr_set(const struct device *dev, enum sensor_channel chan
return 0;
}

static const struct sensor_driver_api akm09918c_driver_api = {
.sample_fetch = akm09918c_sample_fetch,
.channel_get = akm09918c_channel_get,
.attr_get = akm09918c_attr_get,
.attr_set = akm09918c_attr_set,
};

static inline int akm09918c_check_who_am_i(const struct i2c_dt_spec *i2c)
{
uint8_t buffer[2];
Expand Down Expand Up @@ -204,6 +217,17 @@ static int akm09918c_init(const struct device *dev)
return 0;
}

static const struct sensor_driver_api akm09918c_driver_api = {
.sample_fetch = akm09918c_sample_fetch,
.channel_get = akm09918c_channel_get,
.attr_get = akm09918c_attr_get,
.attr_set = akm09918c_attr_set,
#ifdef CONFIG_SENSOR_ASYNC_API
.submit = akm09918c_submit,
.get_decoder = akm09918c_get_decoder,
#endif
};

#define AKM09918C_DEFINE(inst) \
static struct akm09918c_data akm09918c_data_##inst; \
\
Expand Down
21 changes: 21 additions & 0 deletions drivers/sensor/akm09918c/akm09918c.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/rtio/rtio.h>

#include "akm09918c_reg.h"

Expand Down Expand Up @@ -74,4 +75,24 @@ static inline void akm09918c_reg_to_hz(uint8_t reg, struct sensor_value *val)
}
}

/*
* RTIO types
*/

struct akm09918c_decoder_header {
uint64_t timestamp;
} __attribute__((__packed__));

struct akm09918c_encoded_data {
struct akm09918c_decoder_header header;
int16_t readings[3];
};

int akm09918c_sample_fetch_helper(const struct device *dev, enum sensor_channel chan, int16_t *x,
int16_t *y, int16_t *z);

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

int akm09918c_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe);

#endif /* ZEPHYR_DRIVERS_SENSOR_AKM09918C_AKM09918C_H_ */
42 changes: 42 additions & 0 deletions drivers/sensor/akm09918c/akm09918c_async.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/logging/log.h>

#include "akm09918c.h"

LOG_MODULE_DECLARE(AKM09918C, CONFIG_SENSOR_LOG_LEVEL);

int akm09918c_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
{
uint32_t min_buf_len = sizeof(struct akm09918c_encoded_data);
int rc;
uint8_t *buf;
uint32_t buf_len;
struct akm09918c_encoded_data *edata;

/* Get the buffer for the frame, it may be allocated dynamically by the rtio context */
rc = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len);
if (rc != 0) {
LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len);
rtio_iodev_sqe_err(iodev_sqe, rc);
return rc;
}

edata = (struct akm09918c_encoded_data *)buf;
edata->header.timestamp = k_ticks_to_ns_floor64(k_uptime_ticks());

rc = akm09918c_sample_fetch_helper(dev, SENSOR_CHAN_MAGN_XYZ, &edata->readings[0],
Copy link
Collaborator

@teburd teburd Sep 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good reason I need to add the iodev poll API, as a blocking call like this should be punted out.

Submit should signal to RTIO the device needs polling, and upon poll the device does blocking work if needed.

By blocking in the submit() its possible there's deep call trees which I saw can blow stacks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an issue for it? I'm happy to add a TODO and link to it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&edata->readings[1], &edata->readings[2]);
if (rc != 0) {
LOG_ERR("Failed to fetch samples");
rtio_iodev_sqe_err(iodev_sqe, rc);
return rc;
}

rtio_iodev_sqe_ok(iodev_sqe, 0);

return 0;
}
96 changes: 96 additions & 0 deletions drivers/sensor/akm09918c/akm09918c_decoder.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

#include "akm09918c.h"

static int akm09918c_decoder_get_frame_count(const uint8_t *buffer, enum sensor_channel channel,
size_t channel_idx, uint16_t *frame_count)
{
ARG_UNUSED(buffer);
ARG_UNUSED(channel);
ARG_UNUSED(channel_idx);

/* This sensor lacks a FIFO; there will always only be one frame at a time. */
*frame_count = 1;
return 0;
}

static int akm09918c_decoder_get_size_info(enum sensor_channel channel, size_t *base_size,
size_t *frame_size)
{
switch (channel) {
case SENSOR_CHAN_MAGN_X:
case SENSOR_CHAN_MAGN_Y:
case SENSOR_CHAN_MAGN_Z:
case SENSOR_CHAN_MAGN_XYZ:
*base_size = sizeof(struct sensor_three_axis_data);
*frame_size = sizeof(struct sensor_three_axis_sample_data);
return 0;
default:
return -ENOTSUP;
}
}

/** Fixed shift value to use. All channels (MAGN_X, _Y, and _Z) have the same fixed range of
* +/- 49.12 Gauss.
*/
#define AKM09918C_SHIFT (6)

static int akm09918c_convert_raw_to_q31(int16_t reading, q31_t *out)
{
int64_t intermediate = ((int64_t)reading * AKM09918C_MICRO_GAUSS_PER_BIT) *
((int64_t)INT32_MAX + 1) /
((1 << AKM09918C_SHIFT) * INT64_C(1000000));

*out = CLAMP(intermediate, INT32_MIN, INT32_MAX);
return 0;
}

static int akm09918c_decoder_decode(const uint8_t *buffer, enum sensor_channel channel,
size_t channel_idx, uint32_t *fit,
uint16_t max_count, void *data_out)
{
const struct akm09918c_encoded_data *edata = (const struct akm09918c_encoded_data *)buffer;

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

switch (channel) {
case SENSOR_CHAN_MAGN_X:
case SENSOR_CHAN_MAGN_Y:
case SENSOR_CHAN_MAGN_Z:
case SENSOR_CHAN_MAGN_XYZ: {
struct sensor_three_axis_data *out = data_out;

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

akm09918c_convert_raw_to_q31(edata->readings[0], &out->readings[0].x);
akm09918c_convert_raw_to_q31(edata->readings[1], &out->readings[0].y);
akm09918c_convert_raw_to_q31(edata->readings[2], &out->readings[0].z);
*fit = 1;

return 1;
}
default:
return -EINVAL;
}
}

SENSOR_DECODER_API_DT_DEFINE() = {
.get_frame_count = akm09918c_decoder_get_frame_count,
.get_size_info = akm09918c_decoder_get_size_info,
.decode = akm09918c_decoder_decode,
};

int akm09918c_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder)
{
ARG_UNUSED(dev);
*decoder = &SENSOR_DECODER_NAME();

return 0;
}
Loading