sensor_shell: Update to new sensor_read API

Update the sensor shell logic to use the new sensor_read() APIs and
make triggers an option of the sensor_shell sample (this avoids the
trigger stealing the interrupt status from one-shot reads).

Signed-off-by: Yuval Peress <peress@google.com>
diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig
index 1490a72..9f94ff1 100644
--- a/drivers/sensor/Kconfig
+++ b/drivers/sensor/Kconfig
@@ -31,6 +31,7 @@
 	bool "Sensor shell"
 	depends on SHELL
 	select CBPRINTF_FP_SUPPORT
+	select SENSOR_ASYNC_API
 	default y if !SHELL_MINIMAL
 	help
 	  This shell provides access to basic sensor data.
diff --git a/drivers/sensor/default_rtio_sensor.c b/drivers/sensor/default_rtio_sensor.c
index ba9a547..55db893 100644
--- a/drivers/sensor/default_rtio_sensor.c
+++ b/drivers/sensor/default_rtio_sensor.c
@@ -101,7 +101,7 @@
 
 	/* Check that the fetch succeeded */
 	if (rc != 0) {
-		LOG_ERR("Failed to fetch samples");
+		LOG_WRN("Failed to fetch samples");
 		rtio_iodev_sqe_err(iodev_sqe, rc);
 		return;
 	}
@@ -109,7 +109,7 @@
 	/* 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);
+		LOG_WRN("Failed to get a read buffer of size %u bytes", min_buf_len);
 		rtio_iodev_sqe_err(iodev_sqe, rc);
 		return;
 	}
diff --git a/drivers/sensor/sensor_shell.c b/drivers/sensor/sensor_shell.c
index d6fe100..d58c0a8 100644
--- a/drivers/sensor/sensor_shell.c
+++ b/drivers/sensor/sensor_shell.c
@@ -4,12 +4,14 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-#include <zephyr/shell/shell.h>
+#include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
+
 #include <zephyr/device.h>
 #include <zephyr/drivers/sensor.h>
+#include <zephyr/rtio/rtio.h>
+#include <zephyr/shell/shell.h>
 #include <zephyr/sys/iterable_sections.h>
 
 #define SENSOR_GET_HELP                                                                            \
@@ -114,6 +116,19 @@
 
 static enum dynamic_command_context current_cmd_ctx = NONE;
 
+/* Crate a single common config for one-shot reading */
+static enum sensor_channel iodev_sensor_shell_channels[SENSOR_CHAN_ALL];
+static struct sensor_read_config iodev_sensor_shell_read_config = {
+	.sensor = NULL,
+	.channels = iodev_sensor_shell_channels,
+	.count = 0,
+	.max = ARRAY_SIZE(iodev_sensor_shell_channels),
+};
+RTIO_IODEV_DEFINE(iodev_sensor_shell_read, &__sensor_iodev_api, &iodev_sensor_shell_read_config);
+
+/* Create the RTIO context to service the reading */
+RTIO_DEFINE_WITH_MEMPOOL(sensor_read_rtio, 8, 8, 32, 64, 4);
+
 static int parse_named_int(const char *name, const char *heystack[], size_t count)
 {
 	char *endptr;
@@ -176,43 +191,71 @@
 	return 0;
 }
 
-static int handle_channel_by_name(const struct shell *shell_ptr, const struct device *dev,
-				  const char *channel_name)
+struct sensor_shell_processing_context {
+	const struct device *dev;
+	const struct shell *sh;
+};
+
+static void sensor_shell_processing_callback(int result, uint8_t *buf, uint32_t buf_len,
+					     void *userdata)
 {
-	struct sensor_value value[3];
-	int err;
-	const int i =
-		parse_named_int(channel_name, sensor_channel_name, ARRAY_SIZE(sensor_channel_name));
+	struct sensor_shell_processing_context *ctx = userdata;
+	const struct sensor_decoder_api *decoder;
+	sensor_frame_iterator_t fit = {0};
+	sensor_channel_iterator_t cit = {0};
+	uint64_t timestamp;
+	enum sensor_channel channel;
+	q31_t q;
+	int rc;
 
-	if (i < 0) {
-		shell_error(shell_ptr, "Channel not supported (%s)", channel_name);
-		return i;
+	ARG_UNUSED(buf_len);
+
+	if (result < 0) {
+		shell_error(ctx->sh, "Read failed");
+		return;
 	}
 
-	err = sensor_channel_get(dev, i, value);
-	if (err < 0) {
-		return err;
+	rc = sensor_get_decoder(ctx->dev, &decoder);
+	if (rc != 0) {
+		shell_error(ctx->sh, "Failed to get decoder for '%s'", ctx->dev->name);
+		return;
 	}
 
-	if (i >= ARRAY_SIZE(sensor_channel_name)) {
-		shell_print(shell_ptr, "channel idx=%d value = %10.6f", i,
-			    sensor_value_to_double(&value[0]));
-	} else if (i != SENSOR_CHAN_ACCEL_XYZ && i != SENSOR_CHAN_GYRO_XYZ &&
-		   i != SENSOR_CHAN_MAGN_XYZ) {
-		shell_print(shell_ptr, "channel idx=%d %s = %10.6f", i, sensor_channel_name[i],
-			    sensor_value_to_double(&value[0]));
-	} else {
-		/* clang-format off */
-		shell_print(shell_ptr,
-			"channel idx=%d %s x = %10.6f y = %10.6f z = %10.6f",
-			i, sensor_channel_name[i],
-			sensor_value_to_double(&value[0]),
-			sensor_value_to_double(&value[1]),
-			sensor_value_to_double(&value[2]));
-		/* clang-format on */
+	rc = decoder->get_timestamp(buf, &timestamp);
+	if (rc != 0) {
+		shell_error(ctx->sh, "Failed to get fetch timestamp for '%s'", ctx->dev->name);
+		return;
 	}
+	shell_print(ctx->sh, "Got samples at %" PRIu64 " ns", timestamp);
 
-	return 0;
+	while (decoder->decode(buf, &fit, &cit, &channel, &q, 1) > 0) {
+		int8_t shift;
+
+		rc = decoder->get_shift(buf, channel, &shift);
+		if (rc != 0) {
+			shell_error(ctx->sh, "Failed to get bitshift for channel %d", channel);
+			continue;
+		}
+
+		int64_t scaled_value = (int64_t)q << shift;
+		bool is_negative = scaled_value < 0;
+		int numerator;
+		int denominator;
+
+		scaled_value = llabs(scaled_value);
+		numerator = (int)FIELD_GET(GENMASK64(31 + shift, 31), scaled_value);
+		denominator =
+			(int)((FIELD_GET(GENMASK64(30, 0), scaled_value) * 1000000) / INT32_MAX);
+
+		if (channel >= ARRAY_SIZE(sensor_channel_name)) {
+			shell_print(ctx->sh, "channel idx=%d value=%s%d.%06d", channel,
+				    is_negative ? "-" : "", numerator, denominator);
+		} else {
+			shell_print(ctx->sh, "channel idx=%d %s value=%s%d.%06d", channel,
+				    sensor_channel_name[channel], is_negative ? "-" : "", numerator,
+				    denominator);
+		}
+	}
 }
 
 static int cmd_get_sensor(const struct shell *sh, size_t argc, char *argv[])
@@ -226,26 +269,48 @@
 		return -ENODEV;
 	}
 
-	err = sensor_sample_fetch(dev);
+	if (argc == 2) {
+		/* read all channels */
+		int count = 0;
+
+		for (int i = 0; i < ARRAY_SIZE(iodev_sensor_shell_channels); ++i) {
+			if (SENSOR_CHANNEL_3_AXIS(i)) {
+				continue;
+			}
+			iodev_sensor_shell_channels[count++] = i;
+		}
+		iodev_sensor_shell_read_config.count = count;
+	} else {
+		/* read specific channels */
+		iodev_sensor_shell_read_config.count = 0;
+		for (int i = 2; i < argc; ++i) {
+			int chan = parse_named_int(argv[i], sensor_channel_name,
+						   ARRAY_SIZE(sensor_channel_name));
+
+			if (chan < 0) {
+				shell_error(sh, "Failed to read channel (%s)", argv[i]);
+				continue;
+			}
+			iodev_sensor_shell_channels[iodev_sensor_shell_read_config.count++] =
+				chan;
+		}
+	}
+
+	if (iodev_sensor_shell_read_config.count == 0) {
+		shell_error(sh, "No channels to read, bailing");
+		return -EINVAL;
+	}
+	iodev_sensor_shell_read_config.sensor = dev;
+
+	struct sensor_shell_processing_context ctx = {
+		.dev = dev,
+		.sh = sh,
+	};
+	err = sensor_read(&iodev_sensor_shell_read, &sensor_read_rtio, &ctx);
 	if (err < 0) {
 		shell_error(sh, "Failed to read sensor: %d", err);
 	}
-
-	if (argc == 2) {
-		/* read all channels */
-		for (int i = 0; i < ARRAY_SIZE(sensor_channel_name); i++) {
-			if (sensor_channel_name[i]) {
-				handle_channel_by_name(sh, dev, sensor_channel_name[i]);
-			}
-		}
-	} else {
-		for (int i = 2; i < argc; i++) {
-			err = handle_channel_by_name(sh, dev, argv[i]);
-			if (err < 0) {
-				shell_error(sh, "Failed to read channel (%s)", argv[i]);
-			}
-		}
-	}
+	sensor_processing_with_callback(&sensor_read_rtio, sensor_shell_processing_callback);
 
 	return 0;
 }
diff --git a/include/zephyr/linker/common-rom/common-rom-misc.ld b/include/zephyr/linker/common-rom/common-rom-misc.ld
index 2e1812b..51d4865 100644
--- a/include/zephyr/linker/common-rom/common-rom-misc.ld
+++ b/include/zephyr/linker/common-rom/common-rom-misc.ld
@@ -14,7 +14,7 @@
 	ITERABLE_SECTION_ROM(sensor_info, 4)
 #endif
 
-#if defined(CONFIG_SENSOR)
+#if defined(CONFIG_SENSOR_ASYNC_API)
 	ITERABLE_SECTION_ROM(sensor_decoder_api, 4)
 #endif
 
diff --git a/samples/sensor/sensor_shell/CMakeLists.txt b/samples/sensor/sensor_shell/CMakeLists.txt
index 86a0a84..b33730c 100644
--- a/samples/sensor/sensor_shell/CMakeLists.txt
+++ b/samples/sensor/sensor_shell/CMakeLists.txt
@@ -5,5 +5,7 @@
 find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
 project(sensor_shell)
 
-FILE(GLOB app_sources src/*.c)
-target_sources(app PRIVATE ${app_sources})
+target_sources(app PRIVATE src/main.c)
+target_sources_ifdef(CONFIG_INIT_TRIG_DATA_READY app PRIVATE src/trigger.c)
+
+target_include_directories(app PRIVATE include)
diff --git a/samples/sensor/sensor_shell/Kconfig b/samples/sensor/sensor_shell/Kconfig
index db47d9f..370c79d 100644
--- a/samples/sensor/sensor_shell/Kconfig
+++ b/samples/sensor/sensor_shell/Kconfig
@@ -9,3 +9,9 @@
 	  interrupt handler will collect data.
 
 source "Kconfig.zephyr"
+
+config INIT_TRIG_DATA_READY
+	bool "Register data ready triggers for all sensors on start"
+	help
+	  When the application starts, automatically register data ready trigger
+	  listeners to all available sensors.
diff --git a/samples/sensor/sensor_shell/include/trigger.h b/samples/sensor/sensor_shell/include/trigger.h
new file mode 100644
index 0000000..eb5a35f
--- /dev/null
+++ b/samples/sensor/sensor_shell/include/trigger.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2023 Google LLC
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef ZEPHYR_SAMPLES_SENSOR_SENSOR_SHELL_INCLUDE_TRIGGER_H
+#define ZEPHYR_SAMPLES_SENSOR_SENSOR_SHELL_INCLUDE_TRIGGER_H
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/sensor.h>
+
+void sensor_shell_data_ready_trigger_handler(const struct device *sensor,
+					     const struct sensor_trigger *trigger);
+
+#endif /* ZEPHYR_SAMPLES_SENSOR_SENSOR_SHELL_INCLUDE_TRIGGER_H */
diff --git a/samples/sensor/sensor_shell/prj.conf b/samples/sensor/sensor_shell/prj.conf
index 2a9e6a3..d3189e3 100644
--- a/samples/sensor/sensor_shell/prj.conf
+++ b/samples/sensor/sensor_shell/prj.conf
@@ -5,3 +5,4 @@
 CONFIG_SENSOR_INFO=y
 
 CONFIG_LOG=y
+CONFIG_RTIO_CONSUME_SEM=y
diff --git a/samples/sensor/sensor_shell/src/main.c b/samples/sensor/sensor_shell/src/main.c
index 61f2273..31065c6 100644
--- a/samples/sensor/sensor_shell/src/main.c
+++ b/samples/sensor/sensor_shell/src/main.c
@@ -10,80 +10,22 @@
 #include <zephyr/logging/log.h>
 #include <zephyr/sys/iterable_sections.h>
 
+#include "trigger.h"
+
 LOG_MODULE_REGISTER(app);
 
-enum sample_stats_state {
-	SAMPLE_STATS_STATE_UNINITIALIZED = 0,
-	SAMPLE_STATS_STATE_ENABLED,
-	SAMPLE_STATS_STATE_DISABLED,
-};
-
-struct sample_stats {
-	int64_t accumulator;
-	uint32_t count;
-	uint64_t sample_window_start;
-	enum sample_stats_state state;
-};
-
-static void data_ready_trigger_handler(const struct device *sensor,
-				       const struct sensor_trigger *trigger)
-{
-	static struct sample_stats stats[SENSOR_CHAN_ALL];
-	const int64_t now = k_uptime_get();
-	struct sensor_value value;
-
-	if (sensor_sample_fetch(sensor)) {
-		LOG_ERR("Failed to fetch samples on data ready handler");
-	}
-	for (int i = 0; i < SENSOR_CHAN_ALL; ++i) {
-		int rc;
-
-		/* Skip disabled channels */
-		if (stats[i].state == SAMPLE_STATS_STATE_DISABLED) {
-			continue;
-		}
-		/* Skip 3 axis channels */
-		if (i == SENSOR_CHAN_ACCEL_XYZ || i == SENSOR_CHAN_GYRO_XYZ ||
-		    i == SENSOR_CHAN_MAGN_XYZ) {
-			continue;
-		}
-
-		rc = sensor_channel_get(sensor, i, &value);
-		if (rc == -ENOTSUP && stats[i].state == SAMPLE_STATS_STATE_UNINITIALIZED) {
-			/* Stop reading this channel if the driver told us it's not supported. */
-			stats[i].state = SAMPLE_STATS_STATE_DISABLED;
-		}
-		if (rc != 0) {
-			/* Skip on any error. */
-			continue;
-		}
-		/* Do something with the data */
-		stats[i].accumulator += value.val1 * INT64_C(1000000) + value.val2;
-		if (stats[i].count++ == 0) {
-			stats[i].sample_window_start = now;
-		} else if (now > stats[i].sample_window_start + CONFIG_SAMPLE_PRINT_TIMEOUT_MS) {
-			int64_t micro_value = stats[i].accumulator / stats[i].count;
-
-			value.val1 = micro_value / 1000000;
-			value.val2 = (int32_t)llabs(micro_value - (value.val1 * 1000000));
-			LOG_INF("chan=%d, num_samples=%u, data=%d.%06d", i, stats[i].count,
-				value.val1, value.val2);
-
-			stats[i].accumulator = 0;
-			stats[i].count = 0;
-		}
-	}
-}
-
 int main(void)
 {
-	STRUCT_SECTION_FOREACH(sensor_info, sensor)
-	{
-		struct sensor_trigger trigger = {
-			.chan = SENSOR_CHAN_ALL,
-			.type = SENSOR_TRIG_DATA_READY,
-		};
-		sensor_trigger_set(sensor->dev, &trigger, data_ready_trigger_handler);
+	if (IS_ENABLED(CONFIG_INIT_TRIG_DATA_READY)) {
+		STRUCT_SECTION_FOREACH(sensor_info, sensor)
+		{
+			struct sensor_trigger trigger = {
+				.chan = SENSOR_CHAN_ALL,
+				.type = SENSOR_TRIG_DATA_READY,
+			};
+			sensor_trigger_set(sensor->dev, &trigger,
+					   sensor_shell_data_ready_trigger_handler);
+		}
 	}
 	return 0;
 }
diff --git a/samples/sensor/sensor_shell/src/trigger.c b/samples/sensor/sensor_shell/src/trigger.c
new file mode 100644
index 0000000..ee92723
--- /dev/null
+++ b/samples/sensor/sensor_shell/src/trigger.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2023 Google LLC
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "trigger.h"
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/sensor.h>
+#include <zephyr/logging/log.h>
+
+LOG_MODULE_DECLARE(app);
+
+enum sample_stats_state {
+	SAMPLE_STATS_STATE_UNINITIALIZED = 0,
+	SAMPLE_STATS_STATE_ENABLED,
+	SAMPLE_STATS_STATE_DISABLED,
+};
+
+struct sample_stats {
+	int64_t accumulator;
+	uint32_t count;
+	uint64_t sample_window_start;
+	enum sample_stats_state state;
+};
+
+void sensor_shell_data_ready_trigger_handler(const struct device *sensor,
+					     const struct sensor_trigger *trigger)
+{
+	static struct sample_stats stats[SENSOR_CHAN_ALL];
+	const int64_t now = k_uptime_get();
+	struct sensor_value value;
+
+	ARG_UNUSED(trigger);
+
+	if (sensor_sample_fetch(sensor)) {
+		LOG_ERR("Failed to fetch samples on data ready handler");
+	}
+	for (int i = 0; i < SENSOR_CHAN_ALL; ++i) {
+		int rc;
+
+		/* Skip disabled channels */
+		if (stats[i].state == SAMPLE_STATS_STATE_DISABLED) {
+			continue;
+		}
+		/* Skip 3 axis channels */
+		if (i == SENSOR_CHAN_ACCEL_XYZ || i == SENSOR_CHAN_GYRO_XYZ ||
+		    i == SENSOR_CHAN_MAGN_XYZ) {
+			continue;
+		}
+
+		rc = sensor_channel_get(sensor, i, &value);
+		if (rc == -ENOTSUP && stats[i].state == SAMPLE_STATS_STATE_UNINITIALIZED) {
+			/* Stop reading this channel if the driver told us it's not supported. */
+			stats[i].state = SAMPLE_STATS_STATE_DISABLED;
+		}
+		if (rc != 0) {
+			/* Skip on any error. */
+			continue;
+		}
+		/* Do something with the data */
+		stats[i].accumulator += value.val1 * INT64_C(1000000) + value.val2;
+		if (stats[i].count++ == 0) {
+			stats[i].sample_window_start = now;
+		} else if (now > stats[i].sample_window_start + CONFIG_SAMPLE_PRINT_TIMEOUT_MS) {
+			int64_t micro_value = stats[i].accumulator / stats[i].count;
+
+			value.val1 = micro_value / 1000000;
+			value.val2 = (int32_t)llabs(micro_value - (value.val1 * 1000000));
+			LOG_INF("chan=%d, num_samples=%u, data=%d.%06d", i, stats[i].count,
+				value.val1, value.val2);
+
+			stats[i].accumulator = 0;
+			stats[i].count = 0;
+		}
+	}
+}
diff --git a/scripts/build/gen_kobject_list.py b/scripts/build/gen_kobject_list.py
index 02cbcc6..64524c6 100755
--- a/scripts/build/gen_kobject_list.py
+++ b/scripts/build/gen_kobject_list.py
@@ -114,7 +114,7 @@
     ("ztest_test_rule", ("CONFIG_ZTEST_NEW_API", True, False)),
     ("rtio", ("CONFIG_RTIO", False, False)),
     ("rtio_iodev", ("CONFIG_RTIO", False, False)),
-    ("sensor_decoder_api", ("CONFIG_SENSOR", True, False))
+    ("sensor_decoder_api", ("CONFIG_SENSOR_ASYNC_API", True, False))
 ])
 
 def kobject_to_enum(kobj):