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;
}
next prev 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®