mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Jinseob Kim <kimjinseob88@gmail.com>
To: jic23@kernel.org, linux-iio@vger.kernel.org
Cc: kimjinseob88@gmail.com, dlechner@baylibre.com,
	nuno.sa@analog.com, andy@kernel.org, robh@kernel.org,
	krzk+dt@kernel.org, conor+dt@kernel.org,
	neil.armstrong@linaro.org, andersson@kernel.org,
	prabhakar.mahadev-lad.rj@bp.renesas.com,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	corbet@lwn.net, skhan@linuxfoundation.org, rdunlap@infradead.org,
	linux-doc@vger.kernel.org
Subject: [PATCH v11 6/8] iio: osf: add IIO devices from capability reports
Date: Mon, 12 Oct 2026 08:13:38 +0900	[thread overview]
Message-ID: <20261011231340.42885-7-kimjinseob88@gmail.com> (raw)
In-Reply-To: <20261011231340.42885-1-kimjinseob88@gmail.com>

Register IIO devices from supported capability descriptors and expose RAW,
descriptor-derived SCALE and initialized active-scan buffers. Validate
complete inventories and sample meaning, and fault incompatible bound
sessions.

Hold an IIO buffer-mode claim while packing and publishing samples so that
the scan layout and buffer lifetime remain stable through reconfiguration
and unregister.

Assisted-by: LLM
Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
---
 drivers/iio/sensorhubs/osf/Kconfig      |  13 +-
 drivers/iio/sensorhubs/osf/Makefile     |   3 +-
 drivers/iio/sensorhubs/osf/osf_core.c   | 364 ++++++++++++++++++++++++
 drivers/iio/sensorhubs/osf/osf_core.h   |  46 +++
 drivers/iio/sensorhubs/osf/osf_iio.c    | 288 +++++++++++++++++++
 drivers/iio/sensorhubs/osf/osf_iio.h    |  22 ++
 drivers/iio/sensorhubs/osf/osf_serdev.c |   7 +-
 7 files changed, 736 insertions(+), 7 deletions(-)
 create mode 100644 drivers/iio/sensorhubs/osf/osf_iio.c
 create mode 100644 drivers/iio/sensorhubs/osf/osf_iio.h

diff --git a/drivers/iio/sensorhubs/osf/Kconfig b/drivers/iio/sensorhubs/osf/Kconfig
index fa25ad24bef6e37ea53617894e121cb9f625ddb5..f955a1f2993d244369943efbd853b9d2c6d47139 100644
--- a/drivers/iio/sensorhubs/osf/Kconfig
+++ b/drivers/iio/sensorhubs/osf/Kconfig
@@ -1,12 +1,15 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
 config OPEN_SENSOR_FUSION
-	tristate "Open Sensor Fusion UART receiver"
+	tristate "Open Sensor Fusion UART IIO driver"
 	depends on SERIAL_DEV_BUS
 	select CRC32
+	select IIO_BUFFER
+	select IIO_KFIFO_BUF
 	help
-	  Build the Open Sensor Fusion UART receive path.
+	  Build the Open Sensor Fusion UART IIO driver.
 
-	  The driver receives and validates OSF protocol frames over a serdev
-	  UART and caches device status reported by the sensor hub.
-	  Message types without an application handler are ignored.
+	  The driver receives OSF protocol frames over a serdev UART and
+	  registers IIO devices for supported capability entries. It exposes
+	  accelerometer, gyroscope, magnetometer, and temperature samples
+	  through IIO direct reads and software buffers.
diff --git a/drivers/iio/sensorhubs/osf/Makefile b/drivers/iio/sensorhubs/osf/Makefile
index 940c82eddc2e54d5a04dccf3ecb36c98ac638244..b4e03b80cfa4cca6a67e6cd8228a4776a38dcb72 100644
--- a/drivers/iio/sensorhubs/osf/Makefile
+++ b/drivers/iio/sensorhubs/osf/Makefile
@@ -2,4 +2,5 @@
 
 obj-$(CONFIG_OPEN_SENSOR_FUSION) += open-sensor-fusion.o
 
-open-sensor-fusion-y := osf_core.o osf_protocol.o osf_serdev.o osf_stream.o
+open-sensor-fusion-y := osf_core.o osf_iio.o osf_protocol.o osf_serdev.o \
+			 osf_stream.o
diff --git a/drivers/iio/sensorhubs/osf/osf_core.c b/drivers/iio/sensorhubs/osf/osf_core.c
index b76619335335f5b197374af90056a616c1c08f82..4f73ff5e9572b95e1cd237671f01e149784b92b5 100644
--- a/drivers/iio/sensorhubs/osf/osf_core.c
+++ b/drivers/iio/sensorhubs/osf/osf_core.c
@@ -1,10 +1,13 @@
 // SPDX-License-Identifier: GPL-2.0-only
 
+#include <linux/cleanup.h>
 #include <linux/device.h>
 #include <linux/errno.h>
+#include <linux/string.h>
 #include <linux/types.h>
 
 #include "osf_core.h"
+#include "osf_iio.h"
 #include "osf_stream.h"
 
 #define OSF_RESERVED_MSG_FIRST		0x7f00
@@ -16,6 +19,247 @@ void osf_core_init(struct osf_device *osf, struct device *dev)
 	*osf = (struct osf_device) {
 		.dev = dev,
 	};
+	mutex_init(&osf->latest_lock);
+}
+
+void osf_core_unregister_iio(struct osf_device *osf)
+{
+	for (unsigned int i = 0; i < osf->iio_dev_count; i++)
+		osf_iio_unregister_sensor(osf->iio_devs[i].indio_dev);
+
+	osf->iio_dev_count = 0;
+}
+
+static struct iio_dev *osf_core_find_iio_dev(struct osf_device *osf,
+					     u16 sensor_type, u16 sensor_index)
+{
+	const struct osf_iio_binding *binding;
+
+	for (unsigned int i = 0; i < osf->iio_dev_count; i++) {
+		binding = &osf->iio_devs[i];
+		if (binding->sensor_type == sensor_type &&
+		    binding->sensor_index == sensor_index)
+			return binding->indio_dev;
+	}
+
+	return NULL;
+}
+
+static struct osf_latest_sample *
+osf_core_find_latest_sample(struct osf_device *osf, u16 sensor_type,
+			    u16 sensor_index)
+{
+	struct osf_latest_sample *latest;
+
+	for (unsigned int i = 0; i < osf->latest_sample_count; i++) {
+		latest = &osf->latest_samples[i];
+		if (latest->sensor_type == sensor_type &&
+		    latest->sensor_index == sensor_index)
+			return latest;
+	}
+
+	if (osf->latest_sample_count >= OSF_MAX_CAPABILITIES)
+		return NULL;
+
+	return &osf->latest_samples[osf->latest_sample_count++];
+}
+
+static bool
+osf_core_capability_supported(const struct osf_capability_entry *entry)
+{
+	return osf_iio_sensor_supported(entry->sensor_type,
+					entry->channel_count) &&
+	       entry->sample_format == OSF_SAMPLE_FORMAT_S32 &&
+	       !(entry->flags & ~OSF_CAPABILITY_FLAGS_MASK);
+}
+
+static const struct osf_capability_entry *
+osf_core_find_capability(const struct osf_capability_cache *cache,
+			 u16 sensor_type, u16 sensor_index)
+{
+	for (u16 i = 0; i < cache->capability_count; i++) {
+		const struct osf_capability_entry *entry = &cache->entries[i];
+
+		if (entry->sensor_type == sensor_type &&
+		    entry->sensor_index == sensor_index)
+			return entry;
+	}
+
+	return NULL;
+}
+
+static bool
+osf_core_capabilities_equal(const struct osf_capability_cache *old,
+			    const struct osf_capability_cache *new)
+{
+	if (old->capability_count != new->capability_count)
+		return false;
+
+	for (u16 i = 0; i < old->capability_count; i++) {
+		const struct osf_capability_entry *entry = &old->entries[i];
+		const struct osf_capability_entry *other;
+
+		other = osf_core_find_capability(new, entry->sensor_type,
+						 entry->sensor_index);
+		/* Type and the supported major version also fix the unit. */
+		if (!other || other->channel_count != entry->channel_count ||
+		    other->sample_format != entry->sample_format ||
+		    other->scale_nano != entry->scale_nano)
+			return false;
+	}
+
+	return true;
+}
+
+static bool osf_core_capability_is_duplicate(const struct osf_capability_cache *cache,
+					     u16 index)
+{
+	const struct osf_capability_entry *entry = &cache->entries[index];
+
+	for (u16 i = 0; i < index; i++) {
+		if (cache->entries[i].sensor_type == entry->sensor_type &&
+		    cache->entries[i].sensor_index == entry->sensor_index)
+			return true;
+	}
+
+	return false;
+}
+
+static int osf_core_register_capabilities(struct osf_device *osf,
+					  const struct osf_capability_cache *cache)
+{
+	struct iio_dev *indio_dev;
+	int ret;
+
+	for (u16 i = 0; i < cache->capability_count; i++) {
+		ret = osf_iio_register_sensor(osf->dev, &cache->entries[i],
+					      osf, &indio_dev);
+		if (ret)
+			goto err_unregister;
+
+		osf->iio_devs[osf->iio_dev_count++] = (struct osf_iio_binding) {
+			.sensor_type = cache->entries[i].sensor_type,
+			.sensor_index = cache->entries[i].sensor_index,
+			.indio_dev = indio_dev,
+		};
+	}
+
+	return 0;
+
+err_unregister:
+	osf_core_unregister_iio(osf);
+
+	return ret;
+}
+
+void osf_core_start(struct osf_device *osf)
+{
+	int ret;
+
+	if (osf->iio_ready)
+		return;
+
+	osf->iio_ready = true;
+	if (!osf->capability_cache.valid ||
+	    !osf->capability_cache.capability_count)
+		return;
+
+	ret = osf_core_register_capabilities(osf, &osf->capability_cache);
+	if (ret) {
+		/* As with RX discovery failure, a later report may retry. */
+		osf->capability_cache.valid = false;
+		dev_err_ratelimited(osf->dev,
+				    "failed to register pending capabilities: %d\n",
+				    ret);
+	}
+}
+
+static int osf_core_handle_sensor_sample(struct osf_device *osf,
+					 const struct osf_frame *frame)
+{
+	const struct osf_capability_entry *entry;
+	struct osf_latest_sample *latest;
+	struct osf_sensor_sample sample;
+	struct iio_dev *indio_dev;
+	s32 values[OSF_MAX_SAMPLE_CHANNELS] = { };
+	int ret;
+
+	ret = osf_protocol_decode_sensor_sample(frame, &sample);
+	if (ret) {
+		dev_warn_ratelimited(osf->dev,
+				     "rejecting malformed sensor sample: %d\n",
+				     ret);
+		return ret;
+	}
+
+	indio_dev = osf_core_find_iio_dev(osf, sample.sensor_type,
+					  sample.sensor_index);
+	if (!indio_dev) {
+		dev_dbg_ratelimited(osf->dev,
+				    "ignoring sample for unregistered sensor %#x:%u\n",
+				    sample.sensor_type, sample.sensor_index);
+		return OSF_STREAM_FRAME_IGNORED;
+	}
+
+	entry = osf_core_find_capability(&osf->capability_cache,
+					 sample.sensor_type, sample.sensor_index);
+	if (!entry || sample.channel_count != entry->channel_count ||
+	    sample.sample_format != entry->sample_format ||
+	    sample.scale_nano != entry->scale_nano)
+		return -EPROTO;
+
+	if (sample.channel_count > OSF_MAX_SAMPLE_CHANNELS) {
+		dev_warn_ratelimited(osf->dev,
+				     "rejecting sensor sample with %u channels\n",
+				     sample.channel_count);
+		return -E2BIG;
+	}
+
+	for (u16 i = 0; i < sample.channel_count; i++) {
+		ret = osf_protocol_sensor_sample_value(&sample, i, &values[i]);
+		if (ret) {
+			dev_warn_ratelimited(osf->dev,
+					     "rejecting malformed sample value: %d\n",
+					     ret);
+			return ret;
+		}
+	}
+
+	/* Serialize publication and cache admission with the session fault. */
+	guard(mutex)(&osf->latest_lock);
+	if (osf->session_fault)
+		return -EPROTO;
+
+	ret = osf_iio_push_sample(indio_dev, values, sample.channel_count);
+	if (ret) {
+		dev_err_ratelimited(osf->dev,
+				    "failed to push sensor %#x:%u sample: %d\n",
+				    sample.sensor_type, sample.sensor_index, ret);
+		return ret;
+	}
+
+	latest = osf_core_find_latest_sample(osf, sample.sensor_type,
+					     sample.sensor_index);
+	if (!latest) {
+		dev_err_ratelimited(osf->dev,
+				    "latest sample cache full for sensor %#x:%u\n",
+				    sample.sensor_type,
+				    sample.sensor_index);
+		return -ENOSPC;
+	}
+
+	memcpy(latest->values, values, sizeof(values));
+	latest->sensor_type = sample.sensor_type;
+	latest->sensor_index = sample.sensor_index;
+	latest->channel_count = sample.channel_count;
+	latest->sample_format = sample.sample_format;
+	latest->scale_nano = sample.scale_nano;
+	latest->sequence = frame->sequence;
+	latest->timestamp_us = frame->timestamp_us;
+	latest->valid = true;
+	osf->last_sequence = frame->sequence;
+
+	return OSF_STREAM_FRAME_HANDLED;
 }
 
 static int osf_core_handle_device_status(struct osf_device *osf,
@@ -45,6 +289,91 @@ static int osf_core_handle_device_status(struct osf_device *osf,
 	return OSF_STREAM_FRAME_HANDLED;
 }
 
+static int osf_core_handle_capability_report(struct osf_device *osf,
+					     const struct osf_frame *frame)
+{
+	struct osf_capability_cache cache = { };
+	struct osf_capability_report report;
+	int frame_result;
+	int ret;
+
+	ret = osf_protocol_decode_capability_report(frame, &report);
+	if (ret) {
+		dev_warn_ratelimited(osf->dev,
+				     "rejecting malformed capability report: %d\n",
+				     ret);
+		return ret;
+	}
+
+	for (u16 i = 0; i < report.capability_count; i++) {
+		struct osf_capability_entry entry;
+
+		ret = osf_protocol_decode_capability_entry(&report, i, &entry);
+		if (ret) {
+			dev_warn_ratelimited(osf->dev,
+					     "rejecting malformed capability entry: %d\n",
+					     ret);
+			return ret;
+		}
+
+		if (!osf_core_capability_supported(&entry))
+			continue;
+
+		if (!entry.scale_nano)
+			return -EPROTO;
+
+		if (cache.capability_count >= OSF_MAX_CAPABILITIES) {
+			dev_warn_ratelimited(osf->dev,
+					     "too many supported capabilities\n");
+			return -E2BIG;
+		}
+
+		cache.entries[cache.capability_count] = entry;
+		if (osf_core_capability_is_duplicate(&cache, cache.capability_count)) {
+			dev_warn_ratelimited(osf->dev,
+					     "rejecting duplicate capability\n");
+			return -EEXIST;
+		}
+		cache.capability_count++;
+	}
+
+	cache.sequence = frame->sequence;
+	cache.valid = cache.capability_count != 0;
+
+	if (osf->iio_dev_count) {
+		if (!osf_core_capabilities_equal(&osf->capability_cache, &cache)) {
+			guard(mutex)(&osf->latest_lock);
+			osf->session_fault = true;
+			return -EPROTO;
+		}
+
+		osf->last_sequence = frame->sequence;
+		return OSF_STREAM_FRAME_IGNORED;
+	}
+
+	frame_result = OSF_STREAM_FRAME_IGNORED;
+	if (cache.capability_count) {
+		if (osf->iio_ready) {
+			ret = osf_core_register_capabilities(osf, &cache);
+			if (ret) {
+				dev_err_ratelimited(osf->dev,
+						    "failed to register capabilities: %d\n",
+						    ret);
+				return ret;
+			}
+		}
+		frame_result = OSF_STREAM_FRAME_HANDLED;
+	} else {
+		dev_dbg_ratelimited(osf->dev,
+				    "ignoring report without supported capabilities\n");
+	}
+
+	osf->capability_cache = cache;
+	osf->last_sequence = frame->sequence;
+
+	return frame_result;
+}
+
 int osf_core_receive_frame(struct osf_device *osf, const u8 *buf, size_t len)
 {
 	struct osf_frame frame;
@@ -70,9 +399,15 @@ int osf_core_receive_frame(struct osf_device *osf, const u8 *buf, size_t len)
 	 * trusted frame boundary and let the stream consume the complete frame.
 	 */
 	switch (frame.message_type) {
+	case OSF_MSG_SENSOR_SAMPLE:
+		ret = osf_core_handle_sensor_sample(osf, &frame);
+		return ret < 0 ? OSF_STREAM_FRAME_REJECTED : ret;
 	case OSF_MSG_DEVICE_STATUS:
 		ret = osf_core_handle_device_status(osf, &frame);
 		return ret < 0 ? OSF_STREAM_FRAME_REJECTED : ret;
+	case OSF_MSG_CAPABILITY_REPORT:
+		ret = osf_core_handle_capability_report(osf, &frame);
+		return ret < 0 ? OSF_STREAM_FRAME_REJECTED : ret;
 	default:
 		if (frame.message_type >= OSF_RESERVED_MSG_FIRST &&
 		    frame.message_type <= OSF_RESERVED_MSG_LAST) {
@@ -94,3 +429,32 @@ int osf_core_receive_frame(struct osf_device *osf, const u8 *buf, size_t len)
 		return OSF_STREAM_FRAME_IGNORED;
 	}
 }
+
+int osf_core_read_latest_sample(struct osf_device *osf, u16 sensor_type,
+				u16 sensor_index, u16 channel,
+				s32 *value)
+{
+	const struct osf_latest_sample *latest;
+
+	if (!osf || !value)
+		return -EINVAL;
+
+	guard(mutex)(&osf->latest_lock);
+	if (osf->session_fault)
+		return -EPROTO;
+
+	for (unsigned int i = 0; i < osf->latest_sample_count; i++) {
+		latest = &osf->latest_samples[i];
+		if (latest->sensor_type != sensor_type ||
+		    latest->sensor_index != sensor_index)
+			continue;
+
+		if (!latest->valid || channel >= latest->channel_count)
+			break;
+
+		*value = latest->values[channel];
+		return 0;
+	}
+
+	return -ENODATA;
+}
diff --git a/drivers/iio/sensorhubs/osf/osf_core.h b/drivers/iio/sensorhubs/osf/osf_core.h
index 7095e3f967fcde473f3f64b05061c3e9ab0996e4..1fd8e5db14434e8e3c67fa15186a842815ac3ab2 100644
--- a/drivers/iio/sensorhubs/osf/osf_core.h
+++ b/drivers/iio/sensorhubs/osf/osf_core.h
@@ -2,11 +2,35 @@
 #ifndef _OSF_CORE_H
 #define _OSF_CORE_H
 
+#include <linux/mutex.h>
 #include <linux/types.h>
 
 #include "osf_protocol.h"
 
+#define OSF_MAX_SAMPLE_CHANNELS	3
+#define OSF_MAX_CAPABILITIES	16
+
 struct device;
+struct iio_dev;
+
+struct osf_latest_sample {
+	u16 sensor_type;
+	u16 sensor_index;
+	u16 channel_count;
+	u16 sample_format;
+	u32 scale_nano;
+	s32 values[OSF_MAX_SAMPLE_CHANNELS];
+	u64 sequence;
+	u64 timestamp_us;
+	bool valid;
+};
+
+struct osf_capability_cache {
+	u16 capability_count;
+	struct osf_capability_entry entries[OSF_MAX_CAPABILITIES];
+	u64 sequence;
+	bool valid;
+};
 
 struct osf_status_cache {
 	u32 uptime_s;
@@ -17,13 +41,35 @@ struct osf_status_cache {
 	bool valid;
 };
 
+struct osf_iio_binding {
+	u16 sensor_type;
+	u16 sensor_index;
+	struct iio_dev *indio_dev;
+};
+
 struct osf_device {
 	struct device *dev;
+	bool iio_ready;
+	/* Protects session_fault and latest samples; nests outside buffer_lock. */
+	struct mutex latest_lock;
+	bool session_fault;
+	struct osf_latest_sample latest_samples[OSF_MAX_CAPABILITIES];
+	unsigned int latest_sample_count;
+	struct osf_capability_cache capability_cache;
 	struct osf_status_cache status_cache;
+	struct osf_iio_binding iio_devs[OSF_MAX_CAPABILITIES];
+	unsigned int iio_dev_count;
 	u64 last_sequence;
 };
 
 void osf_core_init(struct osf_device *osf, struct device *dev);
+/* Serialize start with receive_frame; pending entries own their data. */
+void osf_core_start(struct osf_device *osf);
+/* Stop the receive producer before teardown; init starts a fresh session. */
+void osf_core_unregister_iio(struct osf_device *osf);
 int osf_core_receive_frame(struct osf_device *osf, const u8 *buf, size_t len);
+int osf_core_read_latest_sample(struct osf_device *osf, u16 sensor_type,
+				u16 sensor_index, u16 channel,
+				s32 *value);
 
 #endif
diff --git a/drivers/iio/sensorhubs/osf/osf_iio.c b/drivers/iio/sensorhubs/osf/osf_iio.c
new file mode 100644
index 0000000000000000000000000000000000000000..24a5393ef4176aa4742833b01391dc88d03a5011
--- /dev/null
+++ b/drivers/iio/sensorhubs/osf/osf_iio.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/array_size.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include "osf_core.h"
+#include "osf_iio.h"
+
+struct osf_iio_sensor_spec {
+	u16 sensor_type;
+	u16 channel_count;
+	const char *name;
+	const struct iio_chan_spec *channels;
+	unsigned int num_channels;
+};
+
+struct osf_iio_state {
+	const struct osf_iio_sensor_spec *spec;
+	struct iio_buffer *buffer;
+	u32 scale_nano;
+	u16 sensor_index;
+	struct osf_device *osf;
+};
+
+#define OSF_CHAN(_type, _mod, _idx)				\
+	{								\
+		.type = (_type),					\
+		.modified = ((_mod) != IIO_NO_MOD),		\
+		.channel2 = (_mod),					\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+		.scan_index = (_idx),					\
+		.scan_type = {						\
+			.sign = 's',					\
+			.realbits = 32,					\
+			.storagebits = 32,				\
+			.endianness = IIO_CPU,				\
+		},							\
+	}
+
+static const struct iio_chan_spec osf_accel_channels[] = {
+	OSF_CHAN(IIO_ACCEL, IIO_MOD_X, 0),
+	OSF_CHAN(IIO_ACCEL, IIO_MOD_Y, 1),
+	OSF_CHAN(IIO_ACCEL, IIO_MOD_Z, 2),
+	IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec osf_gyro_channels[] = {
+	OSF_CHAN(IIO_ANGL_VEL, IIO_MOD_X, 0),
+	OSF_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, 1),
+	OSF_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, 2),
+	IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec osf_mag_channels[] = {
+	OSF_CHAN(IIO_MAGN, IIO_MOD_X, 0),
+	OSF_CHAN(IIO_MAGN, IIO_MOD_Y, 1),
+	OSF_CHAN(IIO_MAGN, IIO_MOD_Z, 2),
+	IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec osf_temp_channels[] = {
+	OSF_CHAN(IIO_TEMP, IIO_NO_MOD, 0),
+	IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct osf_iio_sensor_spec osf_iio_sensor_specs[] = {
+	{
+		.sensor_type = OSF_SENSOR_ACCELEROMETER,
+		.channel_count = 3,
+		.name = "osf-accel",
+		.channels = osf_accel_channels,
+		.num_channels = ARRAY_SIZE(osf_accel_channels),
+	},
+	{
+		.sensor_type = OSF_SENSOR_GYROSCOPE,
+		.channel_count = 3,
+		.name = "osf-gyro",
+		.channels = osf_gyro_channels,
+		.num_channels = ARRAY_SIZE(osf_gyro_channels),
+	},
+	{
+		.sensor_type = OSF_SENSOR_MAGNETOMETER,
+		.channel_count = 3,
+		.name = "osf-magn",
+		.channels = osf_mag_channels,
+		.num_channels = ARRAY_SIZE(osf_mag_channels),
+	},
+	{
+		.sensor_type = OSF_SENSOR_TEMPERATURE,
+		.channel_count = 1,
+		.name = "osf-temp",
+		.channels = osf_temp_channels,
+		.num_channels = ARRAY_SIZE(osf_temp_channels),
+	},
+};
+
+static const struct osf_iio_sensor_spec *
+osf_iio_find_sensor_spec(u16 sensor_type, u16 channel_count)
+{
+	for (unsigned int i = 0; i < ARRAY_SIZE(osf_iio_sensor_specs); i++) {
+		if (osf_iio_sensor_specs[i].sensor_type == sensor_type &&
+		    osf_iio_sensor_specs[i].channel_count == channel_count)
+			return &osf_iio_sensor_specs[i];
+	}
+
+	return NULL;
+}
+
+bool osf_iio_sensor_supported(u16 sensor_type, u16 channel_count)
+{
+	return osf_iio_find_sensor_spec(sensor_type, channel_count) != NULL;
+}
+
+const char *osf_iio_sensor_name(u16 sensor_type)
+{
+	for (unsigned int i = 0; i < ARRAY_SIZE(osf_iio_sensor_specs); i++) {
+		if (osf_iio_sensor_specs[i].sensor_type == sensor_type)
+			return osf_iio_sensor_specs[i].name;
+	}
+
+	return NULL;
+}
+
+static int osf_iio_read_raw(struct iio_dev *indio_dev,
+			    const struct iio_chan_spec *chan, int *val,
+			    int *val2, long mask)
+{
+	struct osf_iio_state *state = iio_priv(indio_dev);
+	s32 raw;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = osf_core_read_latest_sample(state->osf,
+						  state->spec->sensor_type,
+						  state->sensor_index,
+						  chan->scan_index, &raw);
+		if (ret)
+			return ret;
+
+		*val = raw;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = state->scale_nano / NANO;
+		*val2 = state->scale_nano % NANO;
+		return IIO_VAL_INT_PLUS_NANO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info osf_iio_info = {
+	.read_raw = osf_iio_read_raw,
+};
+
+int osf_iio_register_sensor(struct device *dev,
+			    const struct osf_capability_entry *entry,
+			    struct osf_device *osf, struct iio_dev **indio_dev)
+{
+	const struct osf_iio_sensor_spec *spec;
+	struct osf_iio_state *state;
+	struct iio_dev *iio_dev;
+	int ret;
+
+	spec = osf_iio_find_sensor_spec(entry->sensor_type,
+					entry->channel_count);
+	if (!spec)
+		return -EOPNOTSUPP;
+
+	if (entry->sample_format != OSF_SAMPLE_FORMAT_S32 ||
+	    (entry->flags & ~OSF_CAPABILITY_FLAGS_MASK))
+		return -EOPNOTSUPP;
+
+	if (!entry->scale_nano)
+		return -EINVAL;
+
+	iio_dev = iio_device_alloc(dev, sizeof(*state));
+	if (!iio_dev)
+		return -ENOMEM;
+
+	state = iio_priv(iio_dev);
+	state->spec = spec;
+	state->scale_nano = entry->scale_nano;
+	state->sensor_index = entry->sensor_index;
+	state->osf = osf;
+
+	iio_dev->name = spec->name;
+	iio_dev->info = &osf_iio_info;
+	iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+	iio_dev->channels = spec->channels;
+	iio_dev->num_channels = spec->num_channels;
+
+	state->buffer = iio_kfifo_allocate();
+	if (!state->buffer) {
+		ret = -ENOMEM;
+		goto err_free_iio;
+	}
+
+	ret = iio_device_attach_buffer(iio_dev, state->buffer);
+	if (ret)
+		goto err_free_buffer;
+
+	ret = iio_device_register(iio_dev);
+	if (ret)
+		goto err_free_buffer;
+
+	*indio_dev = iio_dev;
+
+	return 0;
+
+err_free_buffer:
+	iio_kfifo_free(state->buffer);
+err_free_iio:
+	iio_device_free(iio_dev);
+
+	return ret;
+}
+
+void osf_iio_unregister_sensor(struct iio_dev *indio_dev)
+{
+	struct osf_iio_state *state = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	iio_kfifo_free(state->buffer);
+	iio_device_free(indio_dev);
+}
+
+int osf_iio_push_sample(struct iio_dev *indio_dev, const s32 *values,
+			u16 channel_count)
+{
+	struct osf_iio_state *state = iio_priv(indio_dev);
+	s64 timestamp;
+	int ret;
+
+	if (channel_count != state->spec->channel_count)
+		return -EPROTO;
+
+	if (!iio_device_try_claim_buffer_mode(indio_dev))
+		return 0;
+
+	timestamp = iio_get_time_ns(indio_dev);
+
+	switch (channel_count) {
+	case 1: {
+		struct {
+			s32 value;
+			aligned_s64 timestamp;
+		} scan = { };
+
+		scan.value = values[0];
+
+		ret = iio_push_to_buffers_with_ts(indio_dev, &scan,
+						  sizeof(scan), timestamp);
+		break;
+	}
+	case 3: {
+		struct {
+			s32 values[3];
+			aligned_s64 timestamp;
+		} scan = { };
+		unsigned int index = 0;
+		unsigned int channel;
+
+		/* Pack the active channels; unused storage remains initialized. */
+		for_each_set_bit(channel, indio_dev->active_scan_mask, channel_count)
+			scan.values[index++] = values[channel];
+
+		ret = iio_push_to_buffers_with_ts(indio_dev, &scan,
+						  sizeof(scan), timestamp);
+		break;
+	}
+	default:
+		ret = -EPROTO;
+		break;
+	}
+
+	iio_device_release_buffer_mode(indio_dev);
+
+	return ret;
+}
diff --git a/drivers/iio/sensorhubs/osf/osf_iio.h b/drivers/iio/sensorhubs/osf/osf_iio.h
new file mode 100644
index 0000000000000000000000000000000000000000..d0745167f8e05666d7667555a027eeaad11fb0ec
--- /dev/null
+++ b/drivers/iio/sensorhubs/osf/osf_iio.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _OSF_IIO_H
+#define _OSF_IIO_H
+
+#include <linux/types.h>
+
+#include "osf_protocol.h"
+
+struct device;
+struct iio_dev;
+struct osf_device;
+
+int osf_iio_register_sensor(struct device *dev,
+			    const struct osf_capability_entry *entry,
+			    struct osf_device *osf, struct iio_dev **indio_dev);
+void osf_iio_unregister_sensor(struct iio_dev *indio_dev);
+int osf_iio_push_sample(struct iio_dev *indio_dev, const s32 *values,
+			u16 channel_count);
+bool osf_iio_sensor_supported(u16 sensor_type, u16 channel_count);
+const char *osf_iio_sensor_name(u16 sensor_type);
+
+#endif
diff --git a/drivers/iio/sensorhubs/osf/osf_serdev.c b/drivers/iio/sensorhubs/osf/osf_serdev.c
index 791bef93e4b8495fdfdf673faddc2059696e8872..dceac281443206588ee42b6abf08bcdb7ae2dc25 100644
--- a/drivers/iio/sensorhubs/osf/osf_serdev.c
+++ b/drivers/iio/sensorhubs/osf/osf_serdev.c
@@ -68,9 +68,10 @@ static void osf_serdev_release(void *data)
 {
 	struct osf_serdev *osf_uart = data;
 
-	/* The TTY controller drains RX work on close; hold no RX lock. */
+	/* The TTY controller drains RX work on close; hold no RX or IIO lock. */
 	serdev_device_close(osf_uart->serdev);
 	osf_stream_reset(&osf_uart->stream);
+	osf_core_unregister_iio(&osf_uart->osf);
 	if (osf_uart->vcc_enabled)
 		regulator_disable(osf_uart->vcc);
 }
@@ -124,6 +125,10 @@ static int osf_serdev_probe(struct serdev_device *serdev)
 		return dev_err_probe(dev, ret, "failed to enable vcc regulator\n");
 	osf_uart->vcc_enabled = true;
 
+	/* No fallible probe steps remain when IIO children become visible. */
+	scoped_guard(mutex, &osf_uart->lock)
+		osf_core_start(&osf_uart->osf);
+
 	return 0;
 }
 

  parent reply	other threads:[~2026-10-11 23:15 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-11 23:13 [PATCH v11 0/8] iio: add Open Sensor Fusion UART support Jinseob Kim
2026-10-11 23:13 ` [PATCH v11 1/8] dt-bindings: iio: add Open Sensor Fusion device Jinseob Kim
2026-10-11 23:13 ` [PATCH v11 2/8] Documentation: iio: add Open Sensor Fusion driver overview Jinseob Kim
2026-10-11 23:13 ` [PATCH v11 3/8] iio: osf: add protocol decoding Jinseob Kim
2026-10-11 23:13 ` [PATCH v11 4/8] iio: osf: add validated stream parser Jinseob Kim
2026-10-11 23:13 ` [PATCH v11 5/8] iio: osf: add UART transport and core receive path Jinseob Kim
2026-10-11 23:13 ` Jinseob Kim [this message]
2026-10-11 23:13 ` [PATCH v11 7/8] iio: osf: add core KUnit tests Jinseob Kim
2026-10-11 23:13 ` [PATCH v11 8/8] iio: osf: add IIO " Jinseob Kim

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261011231340.42885-7-kimjinseob88@gmail.com \
    --to=kimjinseob88@gmail.com \
    --cc=andersson@kernel.org \
    --cc=andy@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=corbet@lwn.net \
    --cc=devicetree@vger.kernel.org \
    --cc=dlechner@baylibre.com \
    --cc=jic23@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=neil.armstrong@linaro.org \
    --cc=nuno.sa@analog.com \
    --cc=prabhakar.mahadev-lad.rj@bp.renesas.com \
    --cc=rdunlap@infradead.org \
    --cc=robh@kernel.org \
    --cc=skhan@linuxfoundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®