From: Jinseob Kim <kimjinseob88@gmail.com>
To: jic23@kernel.org, linux-iio@vger.kernel.org
Cc: dlechner@baylibre.com, nuno.sa@analog.com,
andriy.shevchenko@intel.com, linux-kernel@vger.kernel.org,
rdunlap@infradead.org, joshua.crofts1@gmail.com,
u.kleine-koenig@baylibre.com, julianbraha@gmail.com,
robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
devicetree@vger.kernel.org, corbet@lwn.net,
skhan@linuxfoundation.org, linux-doc@vger.kernel.org
Subject: [PATCH v10 5/8] iio: osf: add UART transport and core receive path
Date: Sat, 19 Sep 2026 03:24:43 +0900 [thread overview]
Message-ID: <5eaeefa79fc2ba956bf34f691c0dc65592e5c4c8.1789753020.git.kimjinseob88@gmail.com> (raw)
In-Reply-To: <cover.1789753020.git.kimjinseob88@gmail.com>
Connect the stream parser to a serdev receiver and add core frame dispatch
with an owned device-status cache. Preserve the distinction between
unvalidated candidates and validated application rejections, so
malformed status payloads do not cause byte-wise resynchronization.
Open and configure the UART before enabling the supply. A managed release
action closes and drains the receive producer before resetting the parser
and disabling a successfully enabled supply.
This transport base processes device-status frames and ignores message
types without an application handler. It does not register IIO sensor
devices. The following patch adds capability discovery and sample
publication to IIO, including the publication gate and IIO teardown.
Assisted-by: LLM
Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
---
MAINTAINERS | 3 +-
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/opensensorfusion/Kconfig | 12 ++
drivers/iio/opensensorfusion/Makefile | 5 +
drivers/iio/opensensorfusion/osf_core.c | 101 +++++++++++++++
drivers/iio/opensensorfusion/osf_core.h | 29 +++++
drivers/iio/opensensorfusion/osf_serdev.c | 143 ++++++++++++++++++++++
8 files changed, 293 insertions(+), 2 deletions(-)
create mode 100644 drivers/iio/opensensorfusion/Kconfig
create mode 100644 drivers/iio/opensensorfusion/Makefile
create mode 100644 drivers/iio/opensensorfusion/osf_core.c
create mode 100644 drivers/iio/opensensorfusion/osf_core.h
create mode 100644 drivers/iio/opensensorfusion/osf_serdev.c
diff --git a/MAINTAINERS b/MAINTAINERS
index bad05db854cf..fbbf5f06f1a5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20516,8 +20516,7 @@ M: Jinseob Kim <kimjinseob88@gmail.com>
S: Maintained
F: Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
F: Documentation/iio/open-sensor-fusion.rst
-F: drivers/iio/opensensorfusion/osf_protocol.*
-F: drivers/iio/opensensorfusion/osf_stream.*
+F: drivers/iio/opensensorfusion/
K: opensensorfusion
OPENCOMPUTE PTP CLOCK DRIVER
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 652557a5b851..89bb5b65c761 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -102,6 +102,7 @@ source "drivers/iio/light/Kconfig"
source "drivers/iio/magnetometer/Kconfig"
source "drivers/iio/multiplexer/Kconfig"
source "drivers/iio/orientation/Kconfig"
+source "drivers/iio/opensensorfusion/Kconfig"
source "drivers/iio/test/Kconfig"
if IIO_TRIGGER
source "drivers/iio/trigger/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index f03a4100c800..a51c4446ee09 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -38,6 +38,7 @@ obj-y += light/
obj-y += magnetometer/
obj-y += multiplexer/
obj-y += orientation/
+obj-y += opensensorfusion/
obj-y += position/
obj-y += potentiometer/
obj-y += potentiostat/
diff --git a/drivers/iio/opensensorfusion/Kconfig b/drivers/iio/opensensorfusion/Kconfig
new file mode 100644
index 000000000000..fa25ad24bef6
--- /dev/null
+++ b/drivers/iio/opensensorfusion/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config OPEN_SENSOR_FUSION
+ tristate "Open Sensor Fusion UART receiver"
+ depends on SERIAL_DEV_BUS
+ select CRC32
+ help
+ Build the Open Sensor Fusion UART receive path.
+
+ 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.
diff --git a/drivers/iio/opensensorfusion/Makefile b/drivers/iio/opensensorfusion/Makefile
new file mode 100644
index 000000000000..940c82eddc2e
--- /dev/null
+++ b/drivers/iio/opensensorfusion/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+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
diff --git a/drivers/iio/opensensorfusion/osf_core.c b/drivers/iio/opensensorfusion/osf_core.c
new file mode 100644
index 000000000000..e6812cef4d8c
--- /dev/null
+++ b/drivers/iio/opensensorfusion/osf_core.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+
+#include "osf_core.h"
+#include "osf_stream.h"
+
+#define OSF_RESERVED_MSG_FIRST 0x7f00
+#define OSF_RESERVED_MSG_LAST 0x7fff
+#define OSF_VENDOR_PRIVATE_FIRST 0x8000
+
+void osf_core_init(struct osf_device *osf, struct device *dev)
+{
+ *osf = (struct osf_device) {
+ .dev = dev,
+ };
+}
+
+static int osf_core_handle_device_status(struct osf_device *osf,
+ const struct osf_frame *frame)
+{
+ struct osf_device_status status;
+ int ret;
+
+ ret = osf_protocol_decode_device_status(frame, &status);
+ if (ret) {
+ dev_warn_ratelimited(osf->dev,
+ "rejecting malformed device status: %d\n",
+ ret);
+ return ret;
+ }
+
+ osf->status_cache = (struct osf_status_cache) {
+ .uptime_s = status.uptime_s,
+ .status_flags = status.status_flags,
+ .error_flags = status.error_flags,
+ .dropped_frames = status.dropped_frames,
+ .sequence = frame->sequence,
+ .valid = true,
+ };
+ osf->last_sequence = frame->sequence;
+
+ return OSF_STREAM_FRAME_HANDLED;
+}
+
+int osf_core_receive_frame(struct osf_device *osf, const u8 *buf, size_t len)
+{
+ struct osf_frame frame;
+ size_t frame_len;
+ int ret;
+
+ ret = osf_protocol_decode_frame(buf, len, &frame, &frame_len);
+ if (ret)
+ return ret;
+
+ if (frame_len != len)
+ return -EMSGSIZE;
+
+ if (frame.protocol_major != OSF_PROTOCOL_MAJOR) {
+ dev_dbg_ratelimited(osf->dev,
+ "ignoring unsupported protocol major %u\n",
+ frame.protocol_major);
+ return OSF_STREAM_FRAME_IGNORED;
+ }
+
+ switch (frame.message_type) {
+ case OSF_MSG_DEVICE_STATUS:
+ ret = osf_core_handle_device_status(osf, &frame);
+ break;
+ default:
+ if (frame.message_type >= OSF_RESERVED_MSG_FIRST &&
+ frame.message_type <= OSF_RESERVED_MSG_LAST) {
+ dev_dbg_ratelimited(osf->dev,
+ "ignoring reserved message type %#x\n",
+ frame.message_type);
+ return OSF_STREAM_FRAME_IGNORED;
+ }
+ if (frame.message_type >= OSF_VENDOR_PRIVATE_FIRST) {
+ dev_dbg_ratelimited(osf->dev,
+ "ignoring vendor message type %#x\n",
+ frame.message_type);
+ return OSF_STREAM_FRAME_IGNORED;
+ }
+
+ dev_dbg_ratelimited(osf->dev,
+ "ignoring unsupported message type %#x\n",
+ frame.message_type);
+ return OSF_STREAM_FRAME_IGNORED;
+ }
+
+ /*
+ * Handler failures are validated application rejections. Keep the
+ * trusted frame boundary and let the stream consume the complete frame.
+ */
+ if (ret < 0)
+ return OSF_STREAM_FRAME_REJECTED;
+
+ return ret;
+}
diff --git a/drivers/iio/opensensorfusion/osf_core.h b/drivers/iio/opensensorfusion/osf_core.h
new file mode 100644
index 000000000000..7095e3f967fc
--- /dev/null
+++ b/drivers/iio/opensensorfusion/osf_core.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _OSF_CORE_H
+#define _OSF_CORE_H
+
+#include <linux/types.h>
+
+#include "osf_protocol.h"
+
+struct device;
+
+struct osf_status_cache {
+ u32 uptime_s;
+ u32 status_flags;
+ u32 error_flags;
+ u32 dropped_frames;
+ u64 sequence;
+ bool valid;
+};
+
+struct osf_device {
+ struct device *dev;
+ struct osf_status_cache status_cache;
+ u64 last_sequence;
+};
+
+void osf_core_init(struct osf_device *osf, struct device *dev);
+int osf_core_receive_frame(struct osf_device *osf, const u8 *buf, size_t len);
+
+#endif
diff --git a/drivers/iio/opensensorfusion/osf_serdev.c b/drivers/iio/opensensorfusion/osf_serdev.c
new file mode 100644
index 000000000000..8a747c01ff9d
--- /dev/null
+++ b/drivers/iio/opensensorfusion/osf_serdev.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/cleanup.h>
+#include <linux/device-id/of.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/serdev.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "osf_core.h"
+#include "osf_stream.h"
+
+#define OSF_SERDEV_BAUD 115200
+
+struct osf_serdev {
+ struct serdev_device *serdev;
+ struct regulator *vcc;
+ bool vcc_enabled;
+ struct osf_device osf;
+ struct osf_stream stream;
+ /* Protects the parser and all RX counters. */
+ struct mutex rx_lock;
+};
+
+static int osf_serdev_receive_frame(void *context, const u8 *buf, size_t len)
+{
+ struct osf_device *osf = context;
+
+ return osf_core_receive_frame(osf, buf, len);
+}
+
+static size_t osf_serdev_receive_buf(struct serdev_device *serdev,
+ const u8 *buf, size_t count)
+{
+ struct osf_serdev *osf_uart = serdev_device_get_drvdata(serdev);
+ const struct osf_stream_stats *stats;
+ u64 validated_before;
+ int ret;
+
+ guard(mutex)(&osf_uart->rx_lock);
+ validated_before = osf_uart->stream.stats.validated_frames;
+ ret = osf_stream_receive_bytes(&osf_uart->stream, buf, count);
+ stats = &osf_uart->stream.stats;
+
+ if (ret || stats->validated_frames != validated_before)
+ dev_dbg_ratelimited(&serdev->dev,
+ "rx count=%zu validated=%llu handled=%llu ignored=%llu rejected=%llu bad_magic=%llu bad_crc=%llu dropped=%llu ret=%d\n",
+ count, stats->validated_frames,
+ stats->handled_frames, stats->ignored_frames,
+ stats->rejected_frames,
+ stats->bad_magic_resyncs,
+ stats->bad_crc_frames,
+ stats->dropped_bytes, ret);
+
+ return count;
+}
+
+static const struct serdev_device_ops osf_serdev_ops = {
+ .receive_buf = osf_serdev_receive_buf,
+};
+
+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. */
+ serdev_device_close(osf_uart->serdev);
+ osf_stream_reset(&osf_uart->stream);
+ if (osf_uart->vcc_enabled)
+ regulator_disable(osf_uart->vcc);
+}
+
+static int osf_serdev_probe(struct serdev_device *serdev)
+{
+ struct device *dev = &serdev->dev;
+ struct osf_serdev *osf_uart;
+ unsigned int baudrate;
+ int ret;
+
+ osf_uart = devm_kzalloc(dev, sizeof(*osf_uart), GFP_KERNEL);
+ if (!osf_uart)
+ return -ENOMEM;
+
+ osf_uart->vcc = devm_regulator_get(dev, "vcc");
+ if (IS_ERR(osf_uart->vcc))
+ return dev_err_probe(dev, PTR_ERR(osf_uart->vcc),
+ "failed to get vcc regulator\n");
+
+ mutex_init(&osf_uart->rx_lock);
+ osf_uart->serdev = serdev;
+ osf_core_init(&osf_uart->osf, dev);
+ osf_stream_init(&osf_uart->stream, osf_serdev_receive_frame,
+ &osf_uart->osf);
+
+ serdev_device_set_drvdata(serdev, osf_uart);
+ serdev_device_set_client_ops(serdev, &osf_serdev_ops);
+
+ ret = serdev_device_open(serdev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, osf_serdev_release, osf_uart);
+ if (ret)
+ return ret;
+
+ baudrate = serdev_device_set_baudrate(serdev, OSF_SERDEV_BAUD);
+ if (baudrate != OSF_SERDEV_BAUD)
+ /* Keep accepting controller rounding after reporting the mismatch. */
+ dev_warn_probe(dev, -EINVAL, "requested %u baud, controller set %u\n",
+ OSF_SERDEV_BAUD, baudrate);
+
+ serdev_device_set_flow_control(serdev, false);
+
+ ret = regulator_enable(osf_uart->vcc);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable vcc regulator\n");
+ osf_uart->vcc_enabled = true;
+
+ return 0;
+}
+
+static const struct of_device_id osf_serdev_of_match[] = {
+ { .compatible = "opensensorfusion,osf" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, osf_serdev_of_match);
+
+static struct serdev_device_driver osf_serdev_driver = {
+ .probe = osf_serdev_probe,
+ .driver = {
+ .name = "open-sensor-fusion-uart",
+ .of_match_table = osf_serdev_of_match,
+ },
+};
+module_serdev_device_driver(osf_serdev_driver);
+
+MODULE_DESCRIPTION("Open Sensor Fusion UART receiver");
+MODULE_LICENSE("GPL");
--
2.43.0
next prev parent reply other threads:[~2026-09-18 18:25 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-18 18:24 [PATCH v10 0/8] iio: add Open Sensor Fusion UART support Jinseob Kim
2026-09-18 18:24 ` [PATCH v10 1/8] dt-bindings: iio: add Open Sensor Fusion device Jinseob Kim
2026-09-18 18:24 ` [PATCH v10 2/8] Documentation: iio: add Open Sensor Fusion driver overview Jinseob Kim
2026-09-18 18:24 ` [PATCH v10 3/8] iio: osf: add protocol decoding Jinseob Kim
2026-09-18 18:24 ` [PATCH v10 4/8] iio: osf: add validated stream parser Jinseob Kim
2026-09-18 18:24 ` Jinseob Kim [this message]
2026-09-18 18:24 ` [PATCH v10 6/8] iio: osf: add IIO devices from capability reports Jinseob Kim
2026-09-18 18:24 ` [PATCH v10 7/8] iio: osf: add core KUnit tests Jinseob Kim
2026-09-18 18:24 ` [PATCH v10 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=5eaeefa79fc2ba956bf34f691c0dc65592e5c4c8.1789753020.git.kimjinseob88@gmail.com \
--to=kimjinseob88@gmail.com \
--cc=andriy.shevchenko@intel.com \
--cc=conor+dt@kernel.org \
--cc=corbet@lwn.net \
--cc=devicetree@vger.kernel.org \
--cc=dlechner@baylibre.com \
--cc=jic23@kernel.org \
--cc=joshua.crofts1@gmail.com \
--cc=julianbraha@gmail.com \
--cc=krzk+dt@kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nuno.sa@analog.com \
--cc=rdunlap@infradead.org \
--cc=robh@kernel.org \
--cc=skhan@linuxfoundation.org \
--cc=u.kleine-koenig@baylibre.com \
/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®