* [PATCH RFC v3 1/6] dt-bindings: iio: add OSF GREEN sensor aggregation device
2026-05-29 12:09 [PATCH RFC v3 0/6] iio: add Open Sensor Fusion OSF0 UART driver Jinseob Kim
@ 2026-05-29 12:10 ` Jinseob Kim
2026-05-29 12:19 ` sashiko-bot
2026-05-29 16:31 ` Conor Dooley
2026-05-29 12:10 ` [PATCH RFC v3 2/6] Documentation: iio: add Open Sensor Fusion protocol v0 reference Jinseob Kim
` (4 subsequent siblings)
5 siblings, 2 replies; 14+ messages in thread
From: Jinseob Kim @ 2026-05-29 12:10 UTC (permalink / raw)
To: Jonathan Cameron, linux-iio
Cc: David Lechner, Nuno Sa, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel
Describe OSF GREEN as the first board target.
Add vendor prefix and MAINTAINERS binding entry.
Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
---
.../iio/imu/opensensorfusion,osf-green.yaml | 43 +++++++++++++++++++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 5 +++
3 files changed, 50 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
diff --git a/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml b/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
new file mode 100644
index 000000000..626b41fb0
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/imu/opensensorfusion,osf-green.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: OSF GREEN sensor aggregation board
+
+maintainers:
+ - Jinseob Kim <kimjinseob88@gmail.com>
+
+description: |
+ OSF GREEN is an STM32F405-based sensor aggregation board from the Open
+ Sensor Fusion open hardware project. It sends OSF0 capability, status, and
+ sample frames to a host over a UART link.
+
+ Open Sensor Fusion is not a generic industry standard. Public project and
+ hardware documentation is available at:
+
+ https://github.com/opensensorfusion
+ https://github.com/opensensorfusion/opensensorfusion-hardware
+
+allOf:
+ - $ref: /schemas/serial/serial-peripheral-props.yaml#
+
+properties:
+ compatible:
+ const: opensensorfusion,osf-green
+
+required:
+ - compatible
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ serial {
+ sensor {
+ compatible = "opensensorfusion,osf-green";
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 28784d66a..f1b1f2ad3 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1237,6 +1237,8 @@ patternProperties:
description: OpenPandora GmbH
"^openrisc,.*":
description: OpenRISC.io
+ "^opensensorfusion,.*":
+ description: Open Sensor Fusion project
"^openwrt,.*":
description: OpenWrt
"^option,.*":
diff --git a/MAINTAINERS b/MAINTAINERS
index c2c6d7927..26bbdf8d3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19977,6 +19977,11 @@ F: Documentation/networking/oa-tc6-framework.rst
F: drivers/net/ethernet/oa_tc6.c
F: include/linux/oa_tc6.h
+OPEN SENSOR FUSION IIO DRIVER
+M: Jinseob Kim <kimjinseob88@gmail.com>
+S: Maintained
+F: Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
+
OPEN FIRMWARE AND FLATTENED DEVICE TREE
M: Rob Herring <robh@kernel.org>
M: Saravana Kannan <saravanak@kernel.org>
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH RFC v3 1/6] dt-bindings: iio: add OSF GREEN sensor aggregation device
2026-05-29 12:10 ` [PATCH RFC v3 1/6] dt-bindings: iio: add OSF GREEN sensor aggregation device Jinseob Kim
@ 2026-05-29 12:19 ` sashiko-bot
2026-05-29 16:31 ` Conor Dooley
1 sibling, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-05-29 12:19 UTC (permalink / raw)
To: Jinseob Kim; +Cc: robh, devicetree, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The new entry in the MAINTAINERS file is not sorted alphabetically.
--
commit 869d40e7e24db8cc9824d50010506b2a425a4d29
Author: Jinseob Kim <kimjinseob88@gmail.com>
dt-bindings: iio: add OSF GREEN sensor aggregation device
This commit adds the device tree binding for the OSF GREEN sensor
aggregation board. It also introduces the vendor prefix and adds the
corresponding entry to the MAINTAINERS file.
[ ... ]
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 6aa3fe2ee1bbf..5931d46fcabc2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -19977,6 +19977,11 @@ F: Documentation/networking/oa-tc6-framework.rst
> F: drivers/net/ethernet/oa_tc6.c
> F: include/linux/oa_tc6.h
>
> +OPEN SENSOR FUSION IIO DRIVER
> +M: Jinseob Kim <kimjinseob88@gmail.com>
> +S: Maintained
> +F: Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
> +
> OPEN FIRMWARE AND FLATTENED DEVICE TREE
[Severity: Low]
This isn't a bug, but should this new entry be placed after the Open
Firmware section? MAINTAINERS entries are typically sorted alphabetically by
their section titles to prevent merge conflicts. Because S comes after F
alphabetically, Open Sensor Fusion should probably appear after Open Firmware
and Flattened Device Tree.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260529121005.1470-1-kimjinseob88@gmail.com?part=1
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH RFC v3 1/6] dt-bindings: iio: add OSF GREEN sensor aggregation device
2026-05-29 12:10 ` [PATCH RFC v3 1/6] dt-bindings: iio: add OSF GREEN sensor aggregation device Jinseob Kim
2026-05-29 12:19 ` sashiko-bot
@ 2026-05-29 16:31 ` Conor Dooley
2026-05-29 17:14 ` Jonathan Cameron
1 sibling, 1 reply; 14+ messages in thread
From: Conor Dooley @ 2026-05-29 16:31 UTC (permalink / raw)
To: Jinseob Kim
Cc: Jonathan Cameron, linux-iio, David Lechner, Nuno Sa,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
devicetree, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 4686 bytes --]
On Fri, May 29, 2026 at 09:10:00PM +0900, Jinseob Kim wrote:
> Describe OSF GREEN as the first board target.
>
> Add vendor prefix and MAINTAINERS binding entry.
>
> Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
> ---
> .../iio/imu/opensensorfusion,osf-green.yaml | 43 +++++++++++++++++++
> .../devicetree/bindings/vendor-prefixes.yaml | 2 +
> MAINTAINERS | 5 +++
> 3 files changed, 50 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
>
> diff --git a/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml b/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
> new file mode 100644
> index 000000000..626b41fb0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
This is still not an IMU.
pw-bot: changes-requested
> @@ -0,0 +1,43 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/imu/opensensorfusion,osf-green.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: OSF GREEN sensor aggregation board
> +
> +maintainers:
> + - Jinseob Kim <kimjinseob88@gmail.com>
> +
> +description: |
> + OSF GREEN is an STM32F405-based sensor aggregation board from the Open
> + Sensor Fusion open hardware project. It sends OSF0 capability, status, and
> + sample frames to a host over a UART link.
> +
> + Open Sensor Fusion is not a generic industry standard. Public project and
> + hardware documentation is available at:
> +
> + https://github.com/opensensorfusion
> + https://github.com/opensensorfusion/opensensorfusion-hardware
> +
> +allOf:
> + - $ref: /schemas/serial/serial-peripheral-props.yaml#
> +
> +properties:
> + compatible:
> + const: opensensorfusion,osf-green
I'm still not convinced by the compatible here, or at least I am not
convinced by it without clear answers to my questions on v1 about
discoverability and compatibility between protocol versions. If the
software on the "osf-green" is updatable (it is, right?) the compatible
doesn't actually represent the hardware, it represents the programming
model of what's exposed on the serial port to the host. That means the
compatible you use has to identify the exact protocol version
implemented, or provide enough information that the version can be
figured out by software.
Given you talk about OSF0 communicating capability etc, it seems to me
like OSF0 is a discoverable bus? In that case, compatibles for boards
doesn't really matter, all software should need to know is that there is
an OSF0 "bus" and query it for what sensors are there.
The questions I asked on v1 were:
- What does "v0" mean here? Is the data format not complete yet?
- Are versions of the protocol likely to be backwards compatible?
- Will the device identify what version of the protocol it implements?
Remember, there's no rush here, and you're better off slowing down and
taking your time responding to reviews before sending new versions, so
that the same conversations don't take place multiple times.
Cheers,
Conor.
> +
> +required:
> + - compatible
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + serial {
> + sensor {
> + compatible = "opensensorfusion,osf-green";
> + };
> + };
> +
> +...
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> index 28784d66a..f1b1f2ad3 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> @@ -1237,6 +1237,8 @@ patternProperties:
> description: OpenPandora GmbH
> "^openrisc,.*":
> description: OpenRISC.io
> + "^opensensorfusion,.*":
> + description: Open Sensor Fusion project
> "^openwrt,.*":
> description: OpenWrt
> "^option,.*":
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c2c6d7927..26bbdf8d3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -19977,6 +19977,11 @@ F: Documentation/networking/oa-tc6-framework.rst
> F: drivers/net/ethernet/oa_tc6.c
> F: include/linux/oa_tc6.h
>
> +OPEN SENSOR FUSION IIO DRIVER
> +M: Jinseob Kim <kimjinseob88@gmail.com>
> +S: Maintained
> +F: Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
> +
> OPEN FIRMWARE AND FLATTENED DEVICE TREE
> M: Rob Herring <robh@kernel.org>
> M: Saravana Kannan <saravanak@kernel.org>
> --
> 2.43.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH RFC v3 1/6] dt-bindings: iio: add OSF GREEN sensor aggregation device
2026-05-29 16:31 ` Conor Dooley
@ 2026-05-29 17:14 ` Jonathan Cameron
0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Cameron @ 2026-05-29 17:14 UTC (permalink / raw)
To: Conor Dooley
Cc: Jinseob Kim, linux-iio, David Lechner, Nuno Sa, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, devicetree,
linux-kernel
On Fri, 29 May 2026 17:31:42 +0100
Conor Dooley <conor@kernel.org> wrote:
> On Fri, May 29, 2026 at 09:10:00PM +0900, Jinseob Kim wrote:
> > Describe OSF GREEN as the first board target.
> >
> > Add vendor prefix and MAINTAINERS binding entry.
> >
> > Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
> > ---
> > .../iio/imu/opensensorfusion,osf-green.yaml | 43 +++++++++++++++++++
> > .../devicetree/bindings/vendor-prefixes.yaml | 2 +
> > MAINTAINERS | 5 +++
> > 3 files changed, 50 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml b/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
> > new file mode 100644
> > index 000000000..626b41fb0
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
>
> This is still not an IMU.
Might include one but agreed, it is more. So probably move it up a a directory
to bindings/iio as it's more of a sensorhub.
> pw-bot: changes-requested
>
> > @@ -0,0 +1,43 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/iio/imu/opensensorfusion,osf-green.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: OSF GREEN sensor aggregation board
> > +
> > +maintainers:
> > + - Jinseob Kim <kimjinseob88@gmail.com>
> > +
> > +description: |
> > + OSF GREEN is an STM32F405-based sensor aggregation board from the Open
> > + Sensor Fusion open hardware project. It sends OSF0 capability, status, and
> > + sample frames to a host over a UART link.
> > +
> > + Open Sensor Fusion is not a generic industry standard. Public project and
> > + hardware documentation is available at:
> > +
> > + https://github.com/opensensorfusion
> > + https://github.com/opensensorfusion/opensensorfusion-hardware
> > +
> > +allOf:
> > + - $ref: /schemas/serial/serial-peripheral-props.yaml#
> > +
> > +properties:
> > + compatible:
> > + const: opensensorfusion,osf-green
>
> I'm still not convinced by the compatible here, or at least I am not
> convinced by it without clear answers to my questions on v1 about
> discoverability and compatibility between protocol versions. If the
> software on the "osf-green" is updatable (it is, right?) the compatible
> doesn't actually represent the hardware, it represents the programming
> model of what's exposed on the serial port to the host. That means the
> compatible you use has to identify the exact protocol version
> implemented, or provide enough information that the version can be
> figured out by software.
>
> Given you talk about OSF0 communicating capability etc, it seems to me
> like OSF0 is a discoverable bus? In that case, compatibles for boards
> doesn't really matter, all software should need to know is that there is
> an OSF0 "bus" and query it for what sensors are there.
Agreed - should be very generic and rely on protocol discovery. Only
need to break that if some some silly reason the way protocol version is
discovered changes.
>
> The questions I asked on v1 were:
> - What does "v0" mean here? Is the data format not complete yet?
> - Are versions of the protocol likely to be backwards compatible?
> - Will the device identify what version of the protocol it implements?
>
> Remember, there's no rush here, and you're better off slowing down and
> taking your time responding to reviews before sending new versions, so
> that the same conversations don't take place multiple times.
>
> Cheers,
> Conor.
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH RFC v3 2/6] Documentation: iio: add Open Sensor Fusion protocol v0 reference
2026-05-29 12:09 [PATCH RFC v3 0/6] iio: add Open Sensor Fusion OSF0 UART driver Jinseob Kim
2026-05-29 12:10 ` [PATCH RFC v3 1/6] dt-bindings: iio: add OSF GREEN sensor aggregation device Jinseob Kim
@ 2026-05-29 12:10 ` Jinseob Kim
2026-05-29 12:23 ` sashiko-bot
2026-05-29 12:10 ` [PATCH RFC v3 3/6] iio: osf: add protocol v0 decoding Jinseob Kim
` (3 subsequent siblings)
5 siblings, 1 reply; 14+ messages in thread
From: Jinseob Kim @ 2026-05-29 12:10 UTC (permalink / raw)
To: Jonathan Cameron, linux-iio
Cc: David Lechner, Nuno Sa, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel
Document OSF0 UART frames and the supported RFC driver subset.
Extend MAINTAINERS to cover the protocol reference.
Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
---
.../iio/open-sensor-fusion-protocol-v0.rst | 308 ++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 309 insertions(+)
create mode 100644 Documentation/iio/open-sensor-fusion-protocol-v0.rst
diff --git a/Documentation/iio/open-sensor-fusion-protocol-v0.rst b/Documentation/iio/open-sensor-fusion-protocol-v0.rst
new file mode 100644
index 000000000..80852f4cf
--- /dev/null
+++ b/Documentation/iio/open-sensor-fusion-protocol-v0.rst
@@ -0,0 +1,308 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Open Sensor Fusion protocol v0
+==============================
+
+This document describes the OSF0 UART wire format used by the Open Sensor
+Fusion Linux IIO driver. It is a wire format reference for the host driver. It
+is not a firmware programming interface.
+
+Background
+----------
+
+Open Sensor Fusion is an open hardware project for sensor aggregation devices
+and Linux IIO host support. It is not a generic sensor protocol standard. The
+first concrete hardware target is OSF GREEN, an STM32F405-based board that
+streams OSF0 frames to a Linux host.
+
+Public project documentation is available at:
+
+- https://www.opensensorfusion.org/
+- https://github.com/opensensorfusion
+- https://github.com/opensensorfusion/opensensorfusion-hardware
+- https://github.com/opensensorfusion/opensensorfusion-linux
+
+Wire format and driver subset
+-----------------------------
+
+OSF0 defines a small device-to-host UART frame format. The current RFC driver
+supports only the subset needed to expose OSF GREEN raw sensor data through
+IIO:
+
+- ``SENSOR_SAMPLE`` frames for accelerometer, gyroscope, magnetometer, and
+ temperature samples.
+- ``CAPABILITY_REPORT`` frames used to create the supported IIO devices.
+- ``DEVICE_STATUS`` frames cached for diagnostics.
+
+The driver ignores vendor private message types and does not implement command
+transport, calibration controls, USB transport, fusion output, or runtime
+capability removal.
+
+Device model
+------------
+
+An OSF0 device is a sensor aggregation device. It sends binary frames from the
+device to the host. The host driver decodes the frames and maps supported
+sensors to IIO devices.
+
+The hardware used for smoke testing is an OSF GREEN prototype with an
+STM32F405RGT6 MCU, an ICM42688P-class IMU, and an MMC5983MA magnetometer. That
+hardware is the first supported target for the RFC driver.
+
+Transport
+---------
+
+The transport is UART at 115200 baud, 8 data bits, no parity, and 1 stop bit.
+The Linux transport is serdev. The v0 upstream driver covers device-to-host
+frames. Flow control is not used by the tested stream.
+
+Byte order
+----------
+
+All multi-byte integer fields are little-endian. Samples use signed 32-bit
+little-endian integers when ``sample_format`` is ``s32``.
+
+Frame format
+------------
+
+Each frame has a fixed 38-byte header, a payload, and a 4-byte CRC.
+
+.. list-table::
+ :header-rows: 1
+
+ * - Offset
+ - Size
+ - Field
+ - Description
+ * - 0
+ - 4
+ - magic
+ - ASCII ``OSF0``
+ * - 4
+ - 1
+ - protocol_major
+ - Must be ``0``
+ * - 5
+ - 1
+ - protocol_minor
+ - Minor version
+ * - 6
+ - 2
+ - header_len
+ - Must be ``38``
+ * - 8
+ - 2
+ - message_type
+ - Message type
+ * - 10
+ - 4
+ - payload_len
+ - Payload length in bytes
+ * - 14
+ - 8
+ - sequence
+ - Monotonic device sequence
+ * - 22
+ - 8
+ - timestamp_us
+ - Device timestamp in microseconds
+ * - 30
+ - 4
+ - flags
+ - Message flags
+ * - 34
+ - 4
+ - reserved
+ - Must be zero for v0
+ * - 38
+ - payload_len
+ - payload
+ - Message payload
+ * - 38 + payload_len
+ - 4
+ - crc32
+ - CRC32 over header and payload
+
+The frame CRC is IEEE CRC32 as implemented by ``crc32_le()`` with initial
+value ``0xffffffff`` and final XOR value ``0xffffffff``. The CRC field is not
+included in the CRC input.
+
+Message types
+-------------
+
+.. list-table::
+ :header-rows: 1
+
+ * - Value
+ - Name
+ - Direction
+ * - ``0x0001``
+ - ``SENSOR_SAMPLE``
+ - device to host
+ * - ``0x0002``
+ - ``DEVICE_STATUS``
+ - device to host
+ * - ``0x0003``
+ - ``CAPABILITY_REPORT``
+ - device to host
+
+Message types ``0x7f00`` through ``0x7fff`` are reserved. Values at or above
+``0x8000`` are vendor private and are ignored by the current RFC driver.
+
+``SENSOR_SAMPLE`` payload
+-------------------------
+
+The payload is a 16-byte payload header followed by ``4 * channel_count`` bytes
+of sample data.
+
+.. list-table::
+ :header-rows: 1
+
+ * - Offset
+ - Size
+ - Field
+ - Description
+ * - 0
+ - 2
+ - sensor_type
+ - Sensor type ID
+ * - 2
+ - 2
+ - sensor_index
+ - Instance index
+ * - 4
+ - 2
+ - channel_count
+ - Number of ``s32`` channels
+ * - 6
+ - 2
+ - sample_format
+ - Must be ``1`` (``s32``)
+ * - 8
+ - 4
+ - scale_nano
+ - Scale factor in nano-units
+ * - 12
+ - 4
+ - reserved
+ - Must be zero for v0
+ * - 16
+ - 4 * channel_count
+ - samples
+ - Signed 32-bit channel samples
+
+The current RFC driver accepts only ``sample_format = s32`` and only the fixed
+channel counts used by its supported IIO devices.
+
+``DEVICE_STATUS`` payload
+-------------------------
+
+The payload size is 20 bytes. Fields are ``uptime_s``, ``status_flags``,
+``error_flags``, ``dropped_frames``, and a reserved field. Each field is
+32 bits. The reserved field must be zero for v0.
+
+``CAPABILITY_REPORT`` payload
+-----------------------------
+
+The base payload size is 4 bytes. It contains ``capability_count`` and a
+reserved field. The reserved field must be zero for v0. Each capability entry
+is 20 bytes:
+
+.. list-table::
+ :header-rows: 1
+
+ * - Offset
+ - Size
+ - Field
+ - Description
+ * - 0
+ - 2
+ - sensor_type
+ - Sensor type ID
+ * - 2
+ - 2
+ - sensor_index
+ - Instance index
+ * - 4
+ - 2
+ - channel_count
+ - Number of channels
+ * - 6
+ - 2
+ - sample_format
+ - Must be ``1`` (``s32``)
+ * - 8
+ - 4
+ - scale_nano
+ - Scale factor in nano-units
+ * - 12
+ - 4
+ - flags
+ - Capability flags
+ * - 16
+ - 4
+ - reserved
+ - Must be zero for v0
+
+Capability flag bit 0 means enabled by default. Bit 1 means calibrated data can
+be provided by the device. Other bits are invalid for v0.
+
+Sensor type IDs
+---------------
+
+.. list-table::
+ :header-rows: 1
+
+ * - Value
+ - Sensor
+ - Current RFC driver mapping
+ * - ``0x0001``
+ - accelerometer
+ - ``IIO_ACCEL``, X/Y/Z
+ * - ``0x0002``
+ - gyroscope
+ - ``IIO_ANGL_VEL``, X/Y/Z
+ * - ``0x0003``
+ - magnetometer
+ - ``IIO_MAGN``, X/Y/Z
+ * - ``0x0004``
+ - barometer
+ - not mapped
+ * - ``0x0005``
+ - temperature
+ - ``IIO_TEMP``
+ * - ``0x0006``
+ - humidity
+ - not mapped
+ * - ``0x0007``
+ - ambient light
+ - not mapped
+ * - ``0x0008``
+ - proximity
+ - not mapped
+
+Scaling
+-------
+
+``scale_nano`` is the per-channel scale value in nano-units. The Linux driver
+maps it to ``IIO_CHAN_INFO_SCALE`` as integer plus nano. The exact physical
+unit depends on the IIO channel type.
+
+Timestamps
+----------
+
+The frame header carries ``timestamp_us``, a device-side timestamp in
+microseconds. UART buffering and host scheduling can add delay before a frame
+is processed by the host.
+
+The current RFC driver does not claim production-grade host/device timestamp
+correlation. Buffered IIO timestamps are taken from IIO timestamp clock handling
+when samples are pushed to IIO buffers.
+
+Non-goals for v0 upstream
+-------------------------
+
+The v0 upstream driver does not include USB transport, fusion output, Attitude
+and Heading Reference System (AHRS) output, Kalman output, calibration command
+ABI, custom sysfs control surface, production timestamp correlation, or runtime
+capability removal.
diff --git a/MAINTAINERS b/MAINTAINERS
index 26bbdf8d3..6ccaaa738 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19981,6 +19981,7 @@ OPEN SENSOR FUSION IIO DRIVER
M: Jinseob Kim <kimjinseob88@gmail.com>
S: Maintained
F: Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
+F: Documentation/iio/open-sensor-fusion-protocol-v0.rst
OPEN FIRMWARE AND FLATTENED DEVICE TREE
M: Rob Herring <robh@kernel.org>
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH RFC v3 2/6] Documentation: iio: add Open Sensor Fusion protocol v0 reference
2026-05-29 12:10 ` [PATCH RFC v3 2/6] Documentation: iio: add Open Sensor Fusion protocol v0 reference Jinseob Kim
@ 2026-05-29 12:23 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-05-29 12:23 UTC (permalink / raw)
To: Jinseob Kim; +Cc: robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The newly added documentation file `open-sensor-fusion-protocol-v0.rst` is not included in any `toctree`.
--
commit 6378b8f2ea4ed104155c2c56a270addafda38309
Author: Jinseob Kim <kimjinseob88@gmail.com>
Documentation: iio: add Open Sensor Fusion protocol v0 reference
This commit documents the OSF0 UART frames and the supported RFC driver
subset. It also extends the MAINTAINERS file to cover the new protocol
reference documentation.
> diff --git a/Documentation/iio/open-sensor-fusion-protocol-v0.rst b/Documentation/iio/open-sensor-fusion-protocol-v0.rst
> new file mode 100644
> index 0000000000000..80852f4cf4f1f
> --- /dev/null
> +++ b/Documentation/iio/open-sensor-fusion-protocol-v0.rst
> @@ -0,0 +1,308 @@
> +.. SPDX-License-Identifier: GPL-2.0-only
> +
> +Open Sensor Fusion protocol v0
> +==============================
[Severity: Low]
This new documentation file does not appear to be included in any toctree,
such as the one in Documentation/iio/index.rst. Should it be added to an
index to ensure it is discoverable and to prevent a Sphinx build warning
about the document not being included in any toctree?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260529121005.1470-1-kimjinseob88@gmail.com?part=2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH RFC v3 3/6] iio: osf: add protocol v0 decoding
2026-05-29 12:09 [PATCH RFC v3 0/6] iio: add Open Sensor Fusion OSF0 UART driver Jinseob Kim
2026-05-29 12:10 ` [PATCH RFC v3 1/6] dt-bindings: iio: add OSF GREEN sensor aggregation device Jinseob Kim
2026-05-29 12:10 ` [PATCH RFC v3 2/6] Documentation: iio: add Open Sensor Fusion protocol v0 reference Jinseob Kim
@ 2026-05-29 12:10 ` Jinseob Kim
2026-05-29 12:10 ` [PATCH RFC v3 4/6] iio: osf: add stream parser Jinseob Kim
` (2 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Jinseob Kim @ 2026-05-29 12:10 UTC (permalink / raw)
To: Jonathan Cameron, linux-iio
Cc: David Lechner, Nuno Sa, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel
Add OSF0 frame validation and payload decoders.
Extend MAINTAINERS to cover the protocol decoder.
Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
---
MAINTAINERS | 1 +
drivers/iio/opensensorfusion/osf_protocol.c | 247 ++++++++++++++++++++
drivers/iio/opensensorfusion/osf_protocol.h | 95 ++++++++
3 files changed, 343 insertions(+)
create mode 100644 drivers/iio/opensensorfusion/osf_protocol.c
create mode 100644 drivers/iio/opensensorfusion/osf_protocol.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 6ccaaa738..9fab72689 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19982,6 +19982,7 @@ M: Jinseob Kim <kimjinseob88@gmail.com>
S: Maintained
F: Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
F: Documentation/iio/open-sensor-fusion-protocol-v0.rst
+F: drivers/iio/opensensorfusion/osf_protocol.*
OPEN FIRMWARE AND FLATTENED DEVICE TREE
M: Rob Herring <robh@kernel.org>
diff --git a/drivers/iio/opensensorfusion/osf_protocol.c b/drivers/iio/opensensorfusion/osf_protocol.c
new file mode 100644
index 000000000..ed91d3dd5
--- /dev/null
+++ b/drivers/iio/opensensorfusion/osf_protocol.c
@@ -0,0 +1,247 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bits.h>
+#include <linux/crc32.h>
+#include <linux/errno.h>
+#include <linux/limits.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include "osf_protocol.h"
+
+#define OSF_CRC32_INIT GENMASK(31, 0)
+#define OSF_CRC32_XOROUT GENMASK(31, 0)
+
+static bool osf_sensor_type_valid(u16 sensor_type)
+{
+ return sensor_type >= OSF_SENSOR_ACCELEROMETER &&
+ sensor_type <= OSF_SENSOR_PROXIMITY;
+}
+
+static u32 osf_crc32_ieee(const u8 *buf, size_t len)
+{
+ return crc32_le(OSF_CRC32_INIT, buf, len) ^ OSF_CRC32_XOROUT;
+}
+
+int osf_protocol_decode_frame(const u8 *buf, size_t len,
+ struct osf_frame *frame, size_t *frame_len)
+{
+ u32 expected_crc;
+ u32 actual_crc;
+ u32 payload_len;
+ size_t total_len;
+ u8 major;
+
+ if (!buf || !frame || !frame_len)
+ return -EINVAL;
+
+ if (len < OSF_FRAME_MIN_LEN)
+ return -EMSGSIZE;
+
+ if (buf[0] != 'O' || buf[1] != 'S' || buf[2] != 'F' || buf[3] != '0')
+ return -EPROTO;
+
+ major = buf[4];
+ if (major != OSF_PROTOCOL_MAJOR)
+ return -EPROTO;
+
+ if (get_unaligned_le16(buf + 6) != OSF_FRAME_HEADER_LEN)
+ return -EPROTO;
+
+ payload_len = get_unaligned_le32(buf + 10);
+ if (payload_len > len - OSF_FRAME_MIN_LEN)
+ return -EMSGSIZE;
+
+ if (get_unaligned_le32(buf + 34))
+ return -EPROTO;
+
+ total_len = OSF_FRAME_HEADER_LEN + payload_len + OSF_FRAME_CRC_LEN;
+ expected_crc = osf_crc32_ieee(buf, OSF_FRAME_HEADER_LEN + payload_len);
+ actual_crc = get_unaligned_le32(buf + OSF_FRAME_HEADER_LEN + payload_len);
+
+ if (actual_crc != expected_crc)
+ return -EBADMSG;
+
+ frame->protocol_minor = buf[5];
+ frame->message_type = get_unaligned_le16(buf + 8);
+ frame->payload_len = payload_len;
+ frame->sequence = get_unaligned_le64(buf + 14);
+ frame->timestamp_us = get_unaligned_le64(buf + 22);
+ frame->flags = get_unaligned_le32(buf + 30);
+ frame->payload = buf + OSF_FRAME_HEADER_LEN;
+ frame->crc = actual_crc;
+ *frame_len = total_len;
+
+ return 0;
+}
+
+int osf_protocol_decode_sensor_sample(const struct osf_frame *frame,
+ struct osf_sensor_sample *sample)
+{
+ u16 channel_count;
+ u16 sample_format;
+ u16 sensor_type;
+ size_t expected_len;
+ const u8 *payload;
+
+ if (!frame || !sample || !frame->payload)
+ return -EINVAL;
+
+ if (frame->message_type != OSF_MSG_SENSOR_SAMPLE)
+ return -EPROTO;
+
+ if (frame->payload_len < OSF_SENSOR_SAMPLE_BASE_LEN)
+ return -EMSGSIZE;
+
+ payload = frame->payload;
+ sensor_type = get_unaligned_le16(payload);
+ channel_count = get_unaligned_le16(payload + 4);
+ sample_format = get_unaligned_le16(payload + 6);
+
+ if (!osf_sensor_type_valid(sensor_type))
+ return -EPROTO;
+
+ if (!channel_count)
+ return -EPROTO;
+
+ if (sample_format != OSF_SAMPLE_FORMAT_S32)
+ return -EPROTO;
+
+ if (get_unaligned_le32(payload + 12))
+ return -EPROTO;
+
+ if (channel_count > (SIZE_MAX - OSF_SENSOR_SAMPLE_BASE_LEN) / sizeof(s32))
+ return -EOVERFLOW;
+
+ expected_len = OSF_SENSOR_SAMPLE_BASE_LEN + channel_count * sizeof(s32);
+ if (frame->payload_len != expected_len)
+ return -EMSGSIZE;
+
+ sample->sensor_type = sensor_type;
+ sample->sensor_index = get_unaligned_le16(payload + 2);
+ sample->channel_count = channel_count;
+ sample->sample_format = sample_format;
+ sample->scale_nano = get_unaligned_le32(payload + 8);
+ sample->samples = payload + OSF_SENSOR_SAMPLE_BASE_LEN;
+
+ return 0;
+}
+
+int osf_protocol_sensor_sample_value(const struct osf_sensor_sample *sample,
+ unsigned int index, s32 *value)
+{
+ if (!sample || !sample->samples || !value)
+ return -EINVAL;
+
+ if (index >= sample->channel_count)
+ return -ERANGE;
+
+ *value = (s32)get_unaligned_le32(sample->samples + index * sizeof(s32));
+
+ return 0;
+}
+
+int osf_protocol_decode_device_status(const struct osf_frame *frame,
+ struct osf_device_status *status)
+{
+ const u8 *payload;
+
+ if (!frame || !status || !frame->payload)
+ return -EINVAL;
+
+ if (frame->message_type != OSF_MSG_DEVICE_STATUS)
+ return -EPROTO;
+
+ if (frame->payload_len != OSF_DEVICE_STATUS_LEN)
+ return -EMSGSIZE;
+
+ payload = frame->payload;
+ if (get_unaligned_le32(payload + 16))
+ return -EPROTO;
+
+ status->uptime_s = get_unaligned_le32(payload);
+ status->status_flags = get_unaligned_le32(payload + 4);
+ status->error_flags = get_unaligned_le32(payload + 8);
+ status->dropped_frames = get_unaligned_le32(payload + 12);
+
+ return 0;
+}
+
+int osf_protocol_decode_capability_report(const struct osf_frame *frame,
+ struct osf_capability_report *report)
+{
+ u16 capability_count;
+ size_t expected_len;
+ const u8 *payload;
+
+ if (!frame || !report || !frame->payload)
+ return -EINVAL;
+
+ if (frame->message_type != OSF_MSG_CAPABILITY_REPORT)
+ return -EPROTO;
+
+ if (frame->payload_len < OSF_CAP_REPORT_BASE_LEN)
+ return -EMSGSIZE;
+
+ payload = frame->payload;
+ capability_count = get_unaligned_le16(payload);
+
+ if (get_unaligned_le16(payload + 2))
+ return -EPROTO;
+
+ if (capability_count > (SIZE_MAX - OSF_CAP_REPORT_BASE_LEN) /
+ OSF_CAP_SENSOR_ENTRY_LEN)
+ return -EOVERFLOW;
+
+ expected_len = OSF_CAP_REPORT_BASE_LEN +
+ capability_count * OSF_CAP_SENSOR_ENTRY_LEN;
+ if (frame->payload_len != expected_len)
+ return -EMSGSIZE;
+
+ report->capability_count = capability_count;
+ report->entries = payload + OSF_CAP_REPORT_BASE_LEN;
+
+ return 0;
+}
+
+int osf_protocol_decode_capability_entry(const struct osf_capability_report *report,
+ unsigned int index,
+ struct osf_capability_entry *entry)
+{
+ u16 sample_format;
+ u16 sensor_type;
+ u32 flags;
+ const u8 *payload;
+
+ if (!report || !report->entries || !entry)
+ return -EINVAL;
+
+ if (index >= report->capability_count)
+ return -ERANGE;
+
+ payload = report->entries + index * OSF_CAP_SENSOR_ENTRY_LEN;
+ sensor_type = get_unaligned_le16(payload);
+ sample_format = get_unaligned_le16(payload + 6);
+ flags = get_unaligned_le32(payload + 12);
+
+ if (!osf_sensor_type_valid(sensor_type))
+ return -EPROTO;
+
+ if (sample_format != OSF_SAMPLE_FORMAT_S32)
+ return -EPROTO;
+
+ if (flags & ~OSF_CAPABILITY_FLAGS_MASK)
+ return -EPROTO;
+
+ if (get_unaligned_le32(payload + 16))
+ return -EPROTO;
+
+ entry->sensor_type = sensor_type;
+ entry->sensor_index = get_unaligned_le16(payload + 2);
+ entry->channel_count = get_unaligned_le16(payload + 4);
+ entry->sample_format = sample_format;
+ entry->scale_nano = get_unaligned_le32(payload + 8);
+ entry->flags = flags;
+
+ return 0;
+}
diff --git a/drivers/iio/opensensorfusion/osf_protocol.h b/drivers/iio/opensensorfusion/osf_protocol.h
new file mode 100644
index 000000000..4b6fb131a
--- /dev/null
+++ b/drivers/iio/opensensorfusion/osf_protocol.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _OSF_PROTOCOL_H
+#define _OSF_PROTOCOL_H
+
+#include <linux/types.h>
+
+#define OSF_PROTOCOL_MAJOR 0
+#define OSF_PROTOCOL_MINOR 0
+#define OSF_FRAME_HEADER_LEN 38
+#define OSF_FRAME_CRC_LEN 4
+#define OSF_FRAME_MIN_LEN (OSF_FRAME_HEADER_LEN + OSF_FRAME_CRC_LEN)
+
+#define OSF_SENSOR_SAMPLE_BASE_LEN 16
+#define OSF_DEVICE_STATUS_LEN 20
+#define OSF_CAP_REPORT_BASE_LEN 4
+#define OSF_CAP_SENSOR_ENTRY_LEN 20
+#define OSF_CAPABILITY_FLAGS_MASK 0x00000003U
+
+enum osf_message_type {
+ OSF_MSG_SENSOR_SAMPLE = 0x0001,
+ OSF_MSG_DEVICE_STATUS = 0x0002,
+ OSF_MSG_CAPABILITY_REPORT = 0x0003,
+};
+
+enum osf_sensor_type {
+ OSF_SENSOR_ACCELEROMETER = 0x0001,
+ OSF_SENSOR_GYROSCOPE = 0x0002,
+ OSF_SENSOR_MAGNETOMETER = 0x0003,
+ OSF_SENSOR_BAROMETER = 0x0004,
+ OSF_SENSOR_TEMPERATURE = 0x0005,
+ OSF_SENSOR_HUMIDITY = 0x0006,
+ OSF_SENSOR_AMBIENT_LIGHT = 0x0007,
+ OSF_SENSOR_PROXIMITY = 0x0008,
+};
+
+enum osf_sample_format {
+ OSF_SAMPLE_FORMAT_S32 = 0x0001,
+};
+
+struct osf_frame {
+ u8 protocol_minor;
+ u16 message_type;
+ u32 payload_len;
+ u64 sequence;
+ u64 timestamp_us;
+ u32 flags;
+ const u8 *payload;
+ u32 crc;
+};
+
+struct osf_sensor_sample {
+ u16 sensor_type;
+ u16 sensor_index;
+ u16 channel_count;
+ u16 sample_format;
+ u32 scale_nano;
+ const u8 *samples;
+};
+
+struct osf_device_status {
+ u32 uptime_s;
+ u32 status_flags;
+ u32 error_flags;
+ u32 dropped_frames;
+};
+
+struct osf_capability_report {
+ u16 capability_count;
+ const u8 *entries;
+};
+
+struct osf_capability_entry {
+ u16 sensor_type;
+ u16 sensor_index;
+ u16 channel_count;
+ u16 sample_format;
+ u32 scale_nano;
+ u32 flags;
+};
+
+int osf_protocol_decode_frame(const u8 *buf, size_t len,
+ struct osf_frame *frame, size_t *frame_len);
+int osf_protocol_decode_sensor_sample(const struct osf_frame *frame,
+ struct osf_sensor_sample *sample);
+int osf_protocol_decode_device_status(const struct osf_frame *frame,
+ struct osf_device_status *status);
+int osf_protocol_decode_capability_report(const struct osf_frame *frame,
+ struct osf_capability_report *report);
+int osf_protocol_decode_capability_entry(const struct osf_capability_report *report,
+ unsigned int index,
+ struct osf_capability_entry *entry);
+int osf_protocol_sensor_sample_value(const struct osf_sensor_sample *sample,
+ unsigned int index, s32 *value);
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH RFC v3 4/6] iio: osf: add stream parser
2026-05-29 12:09 [PATCH RFC v3 0/6] iio: add Open Sensor Fusion OSF0 UART driver Jinseob Kim
` (2 preceding siblings ...)
2026-05-29 12:10 ` [PATCH RFC v3 3/6] iio: osf: add protocol v0 decoding Jinseob Kim
@ 2026-05-29 12:10 ` Jinseob Kim
2026-05-29 13:08 ` sashiko-bot
2026-05-29 12:10 ` [PATCH RFC v3 5/6] iio: osf: add UART serdev transport Jinseob Kim
2026-05-29 12:10 ` [PATCH RFC v3 6/6] iio: osf: register IIO devices from capabilities Jinseob Kim
5 siblings, 1 reply; 14+ messages in thread
From: Jinseob Kim @ 2026-05-29 12:10 UTC (permalink / raw)
To: Jonathan Cameron, linux-iio
Cc: David Lechner, Nuno Sa, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel
Add the byte stream assembler and resync handling for OSF0 UART frames.
Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
---
MAINTAINERS | 1 +
drivers/iio/opensensorfusion/osf_stream.c | 207 ++++++++++++++++++++++
drivers/iio/opensensorfusion/osf_stream.h | 31 ++++
3 files changed, 239 insertions(+)
create mode 100644 drivers/iio/opensensorfusion/osf_stream.c
create mode 100644 drivers/iio/opensensorfusion/osf_stream.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 9fab72689..91c342d88 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19983,6 +19983,7 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
F: Documentation/iio/open-sensor-fusion-protocol-v0.rst
F: drivers/iio/opensensorfusion/osf_protocol.*
+F: drivers/iio/opensensorfusion/osf_stream.*
OPEN FIRMWARE AND FLATTENED DEVICE TREE
M: Rob Herring <robh@kernel.org>
diff --git a/drivers/iio/opensensorfusion/osf_stream.c b/drivers/iio/opensensorfusion/osf_stream.c
new file mode 100644
index 000000000..a2739c987
--- /dev/null
+++ b/drivers/iio/opensensorfusion/osf_stream.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include "osf_core.h"
+#include "osf_protocol.h"
+#include "osf_stream.h"
+
+#define OSF_STREAM_MAGIC_LEN 4
+#define OSF_STREAM_MAX_PAYLOAD_LEN \
+ (OSF_STREAM_MAX_FRAME_LEN - OSF_FRAME_HEADER_LEN - OSF_FRAME_CRC_LEN)
+
+static const u8 osf_stream_magic[OSF_STREAM_MAGIC_LEN] = {
+ 'O', 'S', 'F', '0',
+};
+
+static void osf_stream_discard(struct osf_stream *stream, size_t count)
+{
+ if (count >= stream->len) {
+ stream->len = 0;
+ return;
+ }
+
+ memmove(stream->buf, stream->buf + count, stream->len - count);
+ stream->len -= count;
+}
+
+static void osf_stream_drop_invalid_head(struct osf_stream *stream)
+{
+ osf_stream_discard(stream, 1);
+}
+
+static bool osf_stream_magic_match(const u8 *buf, size_t len)
+{
+ return !memcmp(buf, osf_stream_magic, len);
+}
+
+static size_t osf_stream_discard_to_magic(struct osf_stream *stream)
+{
+ size_t old_len = stream->len;
+ size_t match_len;
+ size_t i;
+
+ for (i = 0; i < stream->len; i++) {
+ match_len = stream->len - i;
+ if (match_len > OSF_STREAM_MAGIC_LEN)
+ match_len = OSF_STREAM_MAGIC_LEN;
+
+ if (osf_stream_magic_match(stream->buf + i, match_len)) {
+ if (i)
+ osf_stream_discard(stream, i);
+ return i;
+ }
+ }
+
+ stream->len = 0;
+ return old_len;
+}
+
+static int osf_stream_process(struct osf_stream *stream)
+{
+ struct osf_frame frame;
+ size_t decoded_len;
+ size_t discarded;
+ size_t frame_len;
+ u32 payload_len;
+ int first_err = 0;
+ int ret;
+
+ while (stream->len) {
+ discarded = osf_stream_discard_to_magic(stream);
+ if (discarded) {
+ stream->stats.bad_magic_resyncs++;
+ stream->stats.dropped_bytes += discarded;
+ if (!first_err)
+ first_err = -EPROTO;
+ }
+
+ if (!stream->len)
+ break;
+
+ if (stream->len < OSF_FRAME_HEADER_LEN) {
+ stream->stats.partial_frames++;
+ break;
+ }
+
+ if (get_unaligned_le16(stream->buf + 6) !=
+ OSF_FRAME_HEADER_LEN) {
+ stream->stats.dropped_bytes++;
+ osf_stream_drop_invalid_head(stream);
+ if (!first_err)
+ first_err = -EPROTO;
+ continue;
+ }
+
+ payload_len = get_unaligned_le32(stream->buf + 10);
+ if (payload_len > OSF_STREAM_MAX_PAYLOAD_LEN) {
+ stream->stats.dropped_bytes++;
+ osf_stream_drop_invalid_head(stream);
+ if (!first_err)
+ first_err = -EMSGSIZE;
+ continue;
+ }
+
+ frame_len = OSF_FRAME_HEADER_LEN + payload_len + OSF_FRAME_CRC_LEN;
+ if (stream->len < frame_len) {
+ stream->stats.partial_frames++;
+ break;
+ }
+
+ ret = osf_protocol_decode_frame(stream->buf, frame_len, &frame,
+ &decoded_len);
+ if (ret) {
+ if (ret == -EBADMSG)
+ stream->stats.bad_crc_frames++;
+ stream->stats.dropped_bytes++;
+ osf_stream_drop_invalid_head(stream);
+ if (!first_err)
+ first_err = ret;
+ continue;
+ }
+
+ if (decoded_len != frame_len) {
+ stream->stats.dropped_bytes++;
+ osf_stream_drop_invalid_head(stream);
+ if (!first_err)
+ first_err = -EMSGSIZE;
+ continue;
+ }
+
+ ret = osf_core_receive_frame(stream->osf, stream->buf, frame_len);
+ if (ret) {
+ osf_stream_discard(stream, frame_len);
+ if (!first_err)
+ first_err = ret;
+ continue;
+ }
+
+ stream->stats.valid_frames++;
+ osf_stream_discard(stream, frame_len);
+ }
+
+ return first_err;
+}
+
+void osf_stream_init(struct osf_stream *stream, struct osf_device *osf)
+{
+ if (!stream)
+ return;
+
+ stream->osf = osf;
+ stream->len = 0;
+ memset(&stream->stats, 0, sizeof(stream->stats));
+}
+
+void osf_stream_reset(struct osf_stream *stream)
+{
+ if (stream) {
+ stream->len = 0;
+ memset(&stream->stats, 0, sizeof(stream->stats));
+ }
+}
+
+int osf_stream_receive_bytes(struct osf_stream *stream, const u8 *buf,
+ size_t len)
+{
+ size_t copy_len;
+ size_t space;
+ int first_err = 0;
+ int ret;
+
+ if (!stream || !stream->osf || (!buf && len))
+ return -EINVAL;
+
+ if (!len) {
+ ret = osf_stream_process(stream);
+ if (ret && !first_err)
+ first_err = ret;
+ return first_err;
+ }
+
+ while (len) {
+ space = OSF_STREAM_MAX_FRAME_LEN - stream->len;
+ if (!space) {
+ stream->stats.dropped_bytes++;
+ osf_stream_discard(stream, 1);
+ if (!first_err)
+ first_err = -EMSGSIZE;
+ continue;
+ }
+
+ copy_len = len < space ? len : space;
+ memcpy(stream->buf + stream->len, buf, copy_len);
+ stream->len += copy_len;
+ buf += copy_len;
+ len -= copy_len;
+
+ ret = osf_stream_process(stream);
+ if (ret && !first_err)
+ first_err = ret;
+ }
+
+ return first_err;
+}
diff --git a/drivers/iio/opensensorfusion/osf_stream.h b/drivers/iio/opensensorfusion/osf_stream.h
new file mode 100644
index 000000000..f7f9477fe
--- /dev/null
+++ b/drivers/iio/opensensorfusion/osf_stream.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _OSF_STREAM_H
+#define _OSF_STREAM_H
+
+#include <linux/types.h>
+
+#define OSF_STREAM_MAX_FRAME_LEN 4096
+
+struct osf_device;
+
+struct osf_stream_stats {
+ u64 valid_frames;
+ u64 bad_magic_resyncs;
+ u64 bad_crc_frames;
+ u64 partial_frames;
+ u64 dropped_bytes;
+};
+
+struct osf_stream {
+ struct osf_device *osf;
+ u8 buf[OSF_STREAM_MAX_FRAME_LEN];
+ size_t len;
+ struct osf_stream_stats stats;
+};
+
+void osf_stream_init(struct osf_stream *stream, struct osf_device *osf);
+void osf_stream_reset(struct osf_stream *stream);
+int osf_stream_receive_bytes(struct osf_stream *stream, const u8 *buf,
+ size_t len);
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH RFC v3 4/6] iio: osf: add stream parser
2026-05-29 12:10 ` [PATCH RFC v3 4/6] iio: osf: add stream parser Jinseob Kim
@ 2026-05-29 13:08 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-05-29 13:08 UTC (permalink / raw)
To: Jinseob Kim; +Cc: robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The `partial_frames` statistic incorrectly increments multiple times for a single valid frame during normal stream reception.
- [Medium] Frame is fully decoded into a local structure that is subsequently discarded without being used, causing duplicate CRC and parsing overhead.
--
commit 8eb00bef4725c351a5a3267078dfac541b5f3a1b
Author: Jinseob Kim <kimjinseob88@gmail.com>
iio: osf: add stream parser
Add the byte stream assembler and resync handling for OSF0 UART frames.
> diff --git a/drivers/iio/opensensorfusion/osf_stream.c b/drivers/iio/opensensorfusion/osf_stream.c
> new file mode 100644
> index 0000000000000..a2739c987da3f
> --- /dev/null
> +++ b/drivers/iio/opensensorfusion/osf_stream.c
[ ... ]
> +static int osf_stream_process(struct osf_stream *stream)
> +{
> + struct osf_frame frame;
> + size_t decoded_len;
> + size_t discarded;
> + size_t frame_len;
> + u32 payload_len;
> + int first_err = 0;
> + int ret;
> +
> + while (stream->len) {
> + discarded = osf_stream_discard_to_magic(stream);
> + if (discarded) {
> + stream->stats.bad_magic_resyncs++;
> + stream->stats.dropped_bytes += discarded;
> + if (!first_err)
> + first_err = -EPROTO;
> + }
> +
> + if (!stream->len)
> + break;
> +
> + if (stream->len < OSF_FRAME_HEADER_LEN) {
> + stream->stats.partial_frames++;
[Severity: Medium]
Since UART receives data in small chunks, will osf_stream_receive_bytes()
repeatedly hit this break condition while accumulating a single valid frame?
It looks like this might inflate partial_frames into a count of receive wait
states rather than actual truncated frames.
> + break;
> + }
> +
> + if (get_unaligned_le16(stream->buf + 6) !=
> + OSF_FRAME_HEADER_LEN) {
> + stream->stats.dropped_bytes++;
> + osf_stream_drop_invalid_head(stream);
> + if (!first_err)
> + first_err = -EPROTO;
> + continue;
> + }
> +
> + payload_len = get_unaligned_le32(stream->buf + 10);
> + if (payload_len > OSF_STREAM_MAX_PAYLOAD_LEN) {
> + stream->stats.dropped_bytes++;
> + osf_stream_drop_invalid_head(stream);
> + if (!first_err)
> + first_err = -EMSGSIZE;
> + continue;
> + }
> +
> + frame_len = OSF_FRAME_HEADER_LEN + payload_len + OSF_FRAME_CRC_LEN;
> + if (stream->len < frame_len) {
> + stream->stats.partial_frames++;
[Severity: Medium]
Similar to the check above, does this increment partial_frames incorrectly
when simply waiting for the rest of the payload and CRC to arrive over UART?
> + break;
> + }
> +
> + ret = osf_protocol_decode_frame(stream->buf, frame_len, &frame,
> + &decoded_len);
> + if (ret) {
> + if (ret == -EBADMSG)
> + stream->stats.bad_crc_frames++;
> + stream->stats.dropped_bytes++;
> + osf_stream_drop_invalid_head(stream);
> + if (!first_err)
> + first_err = ret;
> + continue;
> + }
> +
> + if (decoded_len != frame_len) {
> + stream->stats.dropped_bytes++;
> + osf_stream_drop_invalid_head(stream);
> + if (!first_err)
> + first_err = -EMSGSIZE;
> + continue;
> + }
> +
> + ret = osf_core_receive_frame(stream->osf, stream->buf, frame_len);
[Severity: Medium]
The frame structure is fully decoded and its CRC calculated by
osf_protocol_decode_frame() above, but then the local frame struct is
discarded.
Does passing stream->buf directly to osf_core_receive_frame() cause the
frame to be decoded and the CRC to be calculated a second time?
> + if (ret) {
> + osf_stream_discard(stream, frame_len);
> + if (!first_err)
> + first_err = ret;
> + continue;
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260529121005.1470-1-kimjinseob88@gmail.com?part=4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH RFC v3 5/6] iio: osf: add UART serdev transport
2026-05-29 12:09 [PATCH RFC v3 0/6] iio: add Open Sensor Fusion OSF0 UART driver Jinseob Kim
` (3 preceding siblings ...)
2026-05-29 12:10 ` [PATCH RFC v3 4/6] iio: osf: add stream parser Jinseob Kim
@ 2026-05-29 12:10 ` Jinseob Kim
2026-05-29 13:40 ` sashiko-bot
2026-05-29 12:10 ` [PATCH RFC v3 6/6] iio: osf: register IIO devices from capabilities Jinseob Kim
5 siblings, 1 reply; 14+ messages in thread
From: Jinseob Kim @ 2026-05-29 12:10 UTC (permalink / raw)
To: Jonathan Cameron, linux-iio
Cc: David Lechner, Nuno Sa, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel
Register the OSF serdev driver.
Pass received bytes into the OSF0 stream parser.
Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
---
MAINTAINERS | 4 +
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/opensensorfusion/Kconfig | 15 +++
drivers/iio/opensensorfusion/Makefile | 5 +
drivers/iio/opensensorfusion/osf_core.c | 107 +++++++++++++++++++++
drivers/iio/opensensorfusion/osf_core.h | 18 ++++
drivers/iio/opensensorfusion/osf_serdev.c | 111 ++++++++++++++++++++++
8 files changed, 262 insertions(+)
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 91c342d88..ebbd82bf0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19982,7 +19982,11 @@ M: Jinseob Kim <kimjinseob88@gmail.com>
S: Maintained
F: Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.yaml
F: Documentation/iio/open-sensor-fusion-protocol-v0.rst
+F: drivers/iio/opensensorfusion/Kconfig
+F: drivers/iio/opensensorfusion/Makefile
+F: drivers/iio/opensensorfusion/osf_core.*
F: drivers/iio/opensensorfusion/osf_protocol.*
+F: drivers/iio/opensensorfusion/osf_serdev.c
F: drivers/iio/opensensorfusion/osf_stream.*
OPEN FIRMWARE AND FLATTENED DEVICE TREE
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 661127aed..939f6c546 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -101,6 +101,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 cb80ef837..d864fe17b 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -37,6 +37,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 000000000..360f25b4f
--- /dev/null
+++ b/drivers/iio/opensensorfusion/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config OPEN_SENSOR_FUSION
+ tristate "Open Sensor Fusion UART IIO driver"
+ depends on IIO
+ depends on SERIAL_DEV_BUS
+ select CRC32
+ help
+ Build the Open Sensor Fusion UART receive path.
+
+ The driver receives OSF0 frames over a serdev UART.
+ Frames are decoded and validated before being passed to the
+ driver core.
+ This patch only adds the transport path.
+ IIO device registration is added separately.
diff --git a/drivers/iio/opensensorfusion/Makefile b/drivers/iio/opensensorfusion/Makefile
new file mode 100644
index 000000000..940c82edd
--- /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 000000000..c867b3158
--- /dev/null
+++ b/drivers/iio/opensensorfusion/osf_core.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "osf_core.h"
+#include "osf_protocol.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)
+{
+ memset(osf, 0, sizeof(*osf));
+ osf->dev = dev;
+}
+
+void osf_core_unregister_iio(struct osf_device *osf)
+{
+}
+
+static int osf_core_validate_sensor_sample(const struct osf_frame *frame)
+{
+ struct osf_sensor_sample sample;
+
+ return osf_protocol_decode_sensor_sample(frame, &sample);
+}
+
+static int osf_core_validate_device_status(const struct osf_frame *frame)
+{
+ struct osf_device_status status;
+ int ret;
+
+ ret = osf_protocol_decode_device_status(frame, &status);
+ if (ret)
+ return ret;
+
+ if (status.reserved)
+ return -EPROTO;
+
+ return 0;
+}
+
+static int osf_core_validate_capability_report(const struct osf_frame *frame)
+{
+ struct osf_capability_entry entry;
+ struct osf_capability_report report;
+ unsigned int i;
+ int ret;
+
+ ret = osf_protocol_decode_capability_report(frame, &report);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < report.capability_count; i++) {
+ ret = osf_protocol_decode_capability_entry(&report, i, &entry);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+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;
+
+ if (!osf || !buf)
+ return -EINVAL;
+
+ ret = osf_protocol_decode_frame(buf, len, &frame, &frame_len);
+ if (ret)
+ return ret;
+
+ if (frame_len != len)
+ return -EMSGSIZE;
+
+ switch (frame.message_type) {
+ case OSF_MSG_SENSOR_SAMPLE:
+ ret = osf_core_validate_sensor_sample(&frame);
+ break;
+ case OSF_MSG_DEVICE_STATUS:
+ ret = osf_core_validate_device_status(&frame);
+ break;
+ case OSF_MSG_CAPABILITY_REPORT:
+ ret = osf_core_validate_capability_report(&frame);
+ break;
+ default:
+ if (frame.message_type >= OSF_RESERVED_MSG_FIRST &&
+ frame.message_type <= OSF_RESERVED_MSG_LAST)
+ ret = 0;
+ else if (frame.message_type >= OSF_VENDOR_PRIVATE_FIRST)
+ ret = 0;
+ else
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ if (!ret)
+ osf->last_sequence = frame.sequence;
+
+ return ret;
+}
diff --git a/drivers/iio/opensensorfusion/osf_core.h b/drivers/iio/opensensorfusion/osf_core.h
new file mode 100644
index 000000000..3680c8c9b
--- /dev/null
+++ b/drivers/iio/opensensorfusion/osf_core.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _OSF_CORE_H
+#define _OSF_CORE_H
+
+#include <linux/types.h>
+
+struct device;
+
+struct osf_device {
+ struct device *dev;
+ u64 last_sequence;
+};
+
+void osf_core_init(struct osf_device *osf, struct device *dev);
+void osf_core_unregister_iio(struct osf_device *osf);
+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 000000000..1ac93548d
--- /dev/null
+++ b/drivers/iio/opensensorfusion/osf_serdev.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.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 osf_device osf;
+ struct osf_stream stream;
+};
+
+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 valid_before;
+ int ret;
+
+ valid_before = osf_uart->stream.stats.valid_frames;
+ ret = osf_stream_receive_bytes(&osf_uart->stream, buf, count);
+ stats = &osf_uart->stream.stats;
+
+ if (ret || stats->valid_frames != valid_before)
+ dev_dbg_ratelimited(&serdev->dev,
+ "rx count=%zu valid=%llu bad_magic=%llu bad_crc=%llu partial=%llu dropped=%llu ret=%d\n",
+ count,
+ (unsigned long long)stats->valid_frames,
+ (unsigned long long)stats->bad_magic_resyncs,
+ (unsigned long long)stats->bad_crc_frames,
+ (unsigned long long)stats->partial_frames,
+ (unsigned long long)stats->dropped_bytes,
+ ret);
+
+ return count;
+}
+
+static const struct serdev_device_ops osf_serdev_ops = {
+ .receive_buf = osf_serdev_receive_buf,
+};
+
+static int osf_serdev_probe(struct serdev_device *serdev)
+{
+ struct osf_serdev *osf_uart;
+ unsigned int baudrate;
+ int ret;
+
+ osf_uart = devm_kzalloc(&serdev->dev, sizeof(*osf_uart), GFP_KERNEL);
+ if (!osf_uart)
+ return -ENOMEM;
+
+ osf_uart->serdev = serdev;
+ osf_core_init(&osf_uart->osf, &serdev->dev);
+ osf_stream_init(&osf_uart->stream, &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;
+
+ baudrate = serdev_device_set_baudrate(serdev, OSF_SERDEV_BAUD);
+ if (baudrate != OSF_SERDEV_BAUD)
+ dev_warn(&serdev->dev, "requested %u baud, controller set %u\n",
+ OSF_SERDEV_BAUD, baudrate);
+
+ serdev_device_set_flow_control(serdev, false);
+
+ return 0;
+}
+
+static void osf_serdev_remove(struct serdev_device *serdev)
+{
+ struct osf_serdev *osf_uart = serdev_device_get_drvdata(serdev);
+
+ serdev_device_close(serdev);
+ osf_stream_reset(&osf_uart->stream);
+ osf_core_unregister_iio(&osf_uart->osf);
+}
+
+static const struct of_device_id osf_serdev_of_match[] = {
+ { .compatible = "opensensorfusion,osf-green" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, osf_serdev_of_match);
+
+static struct serdev_device_driver osf_serdev_driver = {
+ .probe = osf_serdev_probe,
+ .remove = osf_serdev_remove,
+ .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 IIO driver");
+MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH RFC v3 5/6] iio: osf: add UART serdev transport
2026-05-29 12:10 ` [PATCH RFC v3 5/6] iio: osf: add UART serdev transport Jinseob Kim
@ 2026-05-29 13:40 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-05-29 13:40 UTC (permalink / raw)
To: Jinseob Kim; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Build failure due to access of non-existent `reserved` field in `struct osf_device_status`.
--
commit 63140b0b3643f374150f5ac89882047f831e521f
Author: Jinseob Kim <kimjinseob88@gmail.com>
iio: osf: add UART serdev transport
Register the OSF serdev driver and pass received bytes into the OSF0 stream
parser.
> diff --git a/drivers/iio/opensensorfusion/osf_core.c b/drivers/iio/opensensorfusion/osf_core.c
> new file mode 100644
> index 0000000000000..c867b315865d2
> --- /dev/null
> +++ b/drivers/iio/opensensorfusion/osf_core.c
[ ... ]
> +static int osf_core_validate_device_status(const struct osf_frame *frame)
> +{
> + struct osf_device_status status;
> + int ret;
> +
> + ret = osf_protocol_decode_device_status(frame, &status);
> + if (ret)
> + return ret;
> +
> + if (status.reserved)
[Severity: Low]
Does struct osf_device_status contain a reserved field?
Looking at osf_protocol_decode_device_status(), the struct appears to only
contain uptime_s, status_flags, error_flags, and dropped_frames.
This causes a compilation error (error: 'struct osf_device_status' has no
member named 'reserved') when building this patch.
While I noticed this function is replaced and the invalid check is removed
later in the series by the patch "iio: osf: register IIO devices from
capabilities", each patch in a series should ideally compile independently to
preserve git bisect functionality.
> + return -EPROTO;
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260529121005.1470-1-kimjinseob88@gmail.com?part=5
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH RFC v3 6/6] iio: osf: register IIO devices from capabilities
2026-05-29 12:09 [PATCH RFC v3 0/6] iio: add Open Sensor Fusion OSF0 UART driver Jinseob Kim
` (4 preceding siblings ...)
2026-05-29 12:10 ` [PATCH RFC v3 5/6] iio: osf: add UART serdev transport Jinseob Kim
@ 2026-05-29 12:10 ` Jinseob Kim
2026-05-29 14:36 ` sashiko-bot
5 siblings, 1 reply; 14+ messages in thread
From: Jinseob Kim @ 2026-05-29 12:10 UTC (permalink / raw)
To: Jonathan Cameron, linux-iio
Cc: David Lechner, Nuno Sa, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel
Use the first capability report to create supported IIO devices.
Cache latest samples and push enabled buffers directly.
Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
---
MAINTAINERS | 1 +
drivers/iio/opensensorfusion/Kconfig | 11 +-
drivers/iio/opensensorfusion/Makefile | 3 +-
drivers/iio/opensensorfusion/osf_core.c | 244 ++++++++++++++++++--
drivers/iio/opensensorfusion/osf_core.h | 52 +++++
drivers/iio/opensensorfusion/osf_iio.c | 285 ++++++++++++++++++++++++
drivers/iio/opensensorfusion/osf_iio.h | 22 ++
7 files changed, 588 insertions(+), 30 deletions(-)
create mode 100644 drivers/iio/opensensorfusion/osf_iio.c
create mode 100644 drivers/iio/opensensorfusion/osf_iio.h
diff --git a/MAINTAINERS b/MAINTAINERS
index ebbd82bf0..56181470d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19985,6 +19985,7 @@ F: Documentation/iio/open-sensor-fusion-protocol-v0.rst
F: drivers/iio/opensensorfusion/Kconfig
F: drivers/iio/opensensorfusion/Makefile
F: drivers/iio/opensensorfusion/osf_core.*
+F: drivers/iio/opensensorfusion/osf_iio.*
F: drivers/iio/opensensorfusion/osf_protocol.*
F: drivers/iio/opensensorfusion/osf_serdev.c
F: drivers/iio/opensensorfusion/osf_stream.*
diff --git a/drivers/iio/opensensorfusion/Kconfig b/drivers/iio/opensensorfusion/Kconfig
index 360f25b4f..957caed2b 100644
--- a/drivers/iio/opensensorfusion/Kconfig
+++ b/drivers/iio/opensensorfusion/Kconfig
@@ -5,11 +5,10 @@ config OPEN_SENSOR_FUSION
depends on IIO
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 OSF0 frames over a serdev UART.
- Frames are decoded and validated before being passed to the
- driver core.
- This patch only adds the transport path.
- IIO device registration is added separately.
+ The driver receives OSF0 frames over a serdev UART and registers
+ IIO devices for supported capability entries.
diff --git a/drivers/iio/opensensorfusion/Makefile b/drivers/iio/opensensorfusion/Makefile
index 940c82edd..b4e03b80c 100644
--- a/drivers/iio/opensensorfusion/Makefile
+++ b/drivers/iio/opensensorfusion/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/opensensorfusion/osf_core.c b/drivers/iio/opensensorfusion/osf_core.c
index c867b3158..e0a12de01 100644
--- a/drivers/iio/opensensorfusion/osf_core.c
+++ b/drivers/iio/opensensorfusion/osf_core.c
@@ -5,7 +5,7 @@
#include <linux/types.h>
#include "osf_core.h"
-#include "osf_protocol.h"
+#include "osf_iio.h"
#define OSF_RESERVED_MSG_FIRST 0x7f00
#define OSF_RESERVED_MSG_LAST 0x7fff
@@ -13,23 +13,175 @@
void osf_core_init(struct osf_device *osf, struct device *dev)
{
- memset(osf, 0, sizeof(*osf));
+ mutex_init(&osf->latest_lock);
osf->dev = dev;
}
void osf_core_unregister_iio(struct osf_device *osf)
{
+ unsigned int i;
+
+ for (i = 0; i < osf->iio_dev_count; i++)
+ osf_iio_unregister_sensor(osf->iio_devs[i].indio_dev);
+
+ osf->iio_dev_count = 0;
}
-static int osf_core_validate_sensor_sample(const struct osf_frame *frame)
+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;
+ unsigned int i;
+
+ for (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;
+ unsigned int i;
+
+ for (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_is_duplicate(const struct osf_capability_cache *cache,
+ unsigned int index)
+{
+ const struct osf_capability_entry *entry = &cache->entries[index];
+ unsigned int i;
+
+ for (i = 0; i < index; i++) {
+ if (!osf_iio_sensor_supported(cache->entries[i].sensor_type,
+ cache->entries[i].channel_count))
+ continue;
+
+ 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;
+ unsigned int i;
+ int ret;
+
+ if (osf->capability_cache.valid)
+ return 0;
+
+ for (i = 0; i < cache->capability_count; i++) {
+ if (!osf_iio_sensor_supported(cache->entries[i].sensor_type,
+ cache->entries[i].channel_count))
+ continue;
+
+ if (osf_core_capability_is_duplicate(cache, i))
+ return -EEXIST;
+ }
+
+ for (i = 0; i < cache->capability_count; i++) {
+ if (!osf_iio_sensor_supported(cache->entries[i].sensor_type,
+ cache->entries[i].channel_count))
+ continue;
+
+ 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].sensor_type =
+ cache->entries[i].sensor_type;
+ osf->iio_devs[osf->iio_dev_count].sensor_index =
+ cache->entries[i].sensor_index;
+ osf->iio_devs[osf->iio_dev_count].indio_dev = indio_dev;
+ osf->iio_dev_count++;
+ }
+
+ return 0;
+
+err_unregister:
+ osf_core_unregister_iio(osf);
+
+ return ret;
+}
+
+static int osf_core_handle_sensor_sample(struct osf_device *osf,
+ const struct osf_frame *frame)
+{
+ struct osf_latest_sample *latest;
struct osf_sensor_sample sample;
+ struct iio_dev *indio_dev;
+ s32 values[OSF_MAX_SAMPLE_CHANNELS] = { };
+ unsigned int i;
+ int ret;
+
+ ret = osf_protocol_decode_sensor_sample(frame, &sample);
+ if (ret)
+ return ret;
+
+ if (sample.channel_count > OSF_MAX_SAMPLE_CHANNELS)
+ return -E2BIG;
- return osf_protocol_decode_sensor_sample(frame, &sample);
+ for (i = 0; i < sample.channel_count; i++) {
+ ret = osf_protocol_sensor_sample_value(&sample, i, &values[i]);
+ if (ret)
+ return ret;
+ }
+
+ mutex_lock(&osf->latest_lock);
+ latest = osf_core_find_latest_sample(osf, sample.sensor_type,
+ sample.sensor_index);
+ if (!latest) {
+ mutex_unlock(&osf->latest_lock);
+ return -E2BIG;
+ }
+
+ 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;
+ mutex_unlock(&osf->latest_lock);
+
+ indio_dev = osf_core_find_iio_dev(osf, sample.sensor_type,
+ sample.sensor_index);
+ if (!indio_dev)
+ return 0;
+
+ return osf_iio_push_sample(indio_dev, values, sample.channel_count);
}
-static int osf_core_validate_device_status(const struct osf_frame *frame)
+static int osf_core_handle_device_status(struct osf_device *osf,
+ const struct osf_frame *frame)
{
+ struct osf_status_cache cache = { };
struct osf_device_status status;
int ret;
@@ -37,15 +189,22 @@ static int osf_core_validate_device_status(const struct osf_frame *frame)
if (ret)
return ret;
- if (status.reserved)
- return -EPROTO;
+ cache.uptime_s = status.uptime_s;
+ cache.status_flags = status.status_flags;
+ cache.error_flags = status.error_flags;
+ cache.dropped_frames = status.dropped_frames;
+ cache.sequence = frame->sequence;
+ cache.valid = true;
+ osf->status_cache = cache;
+ osf->last_sequence = frame->sequence;
return 0;
}
-static int osf_core_validate_capability_report(const struct osf_frame *frame)
+static int osf_core_handle_capability_report(struct osf_device *osf,
+ const struct osf_frame *frame)
{
- struct osf_capability_entry entry;
+ struct osf_capability_cache cache = { };
struct osf_capability_report report;
unsigned int i;
int ret;
@@ -54,12 +213,32 @@ static int osf_core_validate_capability_report(const struct osf_frame *frame)
if (ret)
return ret;
+ if (report.capability_count > OSF_MAX_CAPABILITIES)
+ return -E2BIG;
+
+ if (osf->capability_cache.valid) {
+ osf->last_sequence = frame->sequence;
+ return 0;
+ }
+
for (i = 0; i < report.capability_count; i++) {
- ret = osf_protocol_decode_capability_entry(&report, i, &entry);
+ ret = osf_protocol_decode_capability_entry(&report, i,
+ &cache.entries[i]);
if (ret)
return ret;
}
+ cache.capability_count = report.capability_count;
+ cache.sequence = frame->sequence;
+ cache.valid = true;
+
+ ret = osf_core_register_capabilities(osf, &cache);
+ if (ret)
+ return ret;
+
+ osf->capability_cache = cache;
+ osf->last_sequence = frame->sequence;
+
return 0;
}
@@ -81,27 +260,46 @@ int osf_core_receive_frame(struct osf_device *osf, const u8 *buf, size_t len)
switch (frame.message_type) {
case OSF_MSG_SENSOR_SAMPLE:
- ret = osf_core_validate_sensor_sample(&frame);
- break;
+ return osf_core_handle_sensor_sample(osf, &frame);
case OSF_MSG_DEVICE_STATUS:
- ret = osf_core_validate_device_status(&frame);
- break;
+ return osf_core_handle_device_status(osf, &frame);
case OSF_MSG_CAPABILITY_REPORT:
- ret = osf_core_validate_capability_report(&frame);
- break;
+ return osf_core_handle_capability_report(osf, &frame);
default:
if (frame.message_type >= OSF_RESERVED_MSG_FIRST &&
frame.message_type <= OSF_RESERVED_MSG_LAST)
+ return 0;
+ if (frame.message_type >= OSF_VENDOR_PRIVATE_FIRST)
+ return 0;
+ return -EOPNOTSUPP;
+ }
+}
+
+int osf_core_read_latest_sample(struct osf_device *osf, u16 sensor_type,
+ u16 sensor_index, unsigned int channel,
+ s32 *value)
+{
+ const struct osf_latest_sample *latest;
+ unsigned int i;
+ int ret = -ENODATA;
+
+ if (!osf || !value)
+ return -EINVAL;
+
+ mutex_lock(&osf->latest_lock);
+ for (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) {
+ *value = latest->values[channel];
ret = 0;
- else if (frame.message_type >= OSF_VENDOR_PRIVATE_FIRST)
- ret = 0;
- else
- ret = -EOPNOTSUPP;
+ }
break;
}
-
- if (!ret)
- osf->last_sequence = frame.sequence;
+ mutex_unlock(&osf->latest_lock);
return ret;
}
diff --git a/drivers/iio/opensensorfusion/osf_core.h b/drivers/iio/opensensorfusion/osf_core.h
index 3680c8c9b..04dd2a367 100644
--- a/drivers/iio/opensensorfusion/osf_core.h
+++ b/drivers/iio/opensensorfusion/osf_core.h
@@ -2,17 +2,69 @@
#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;
+ u32 status_flags;
+ u32 error_flags;
+ u32 dropped_frames;
+ u64 sequence;
+ bool valid;
+};
+
+struct osf_iio_binding {
+ u16 sensor_type;
+ u16 sensor_index;
+ struct iio_dev *indio_dev;
+};
struct osf_device {
struct device *dev;
+ /* Protects latest_samples and latest_sample_count. */
+ struct mutex latest_lock;
+ 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);
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, unsigned int channel,
+ s32 *value);
#endif
diff --git a/drivers/iio/opensensorfusion/osf_iio.c b/drivers/iio/opensensorfusion/osf_iio.c
new file mode 100644
index 000000000..5e5099878
--- /dev/null
+++ b/drivers/iio/opensensorfusion/osf_iio.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/array_size.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/string.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;
+ const unsigned long *available_scan_masks;
+};
+
+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_SCAN_TYPE_S32 \
+ { \
+ .sign = 's', \
+ .realbits = 32, \
+ .storagebits = 32, \
+ .endianness = IIO_CPU, \
+ }
+
+#define OSF_MOD_CHAN(_type, _mod, _idx) \
+ { \
+ .type = (_type), \
+ .modified = 1, \
+ .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 = OSF_SCAN_TYPE_S32, \
+ }
+
+#define OSF_CHAN(_type, _idx) \
+ { \
+ .type = (_type), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = (_idx), \
+ .scan_type = OSF_SCAN_TYPE_S32, \
+ }
+
+static const struct iio_chan_spec osf_accel_channels[] = {
+ OSF_MOD_CHAN(IIO_ACCEL, IIO_MOD_X, 0),
+ OSF_MOD_CHAN(IIO_ACCEL, IIO_MOD_Y, 1),
+ OSF_MOD_CHAN(IIO_ACCEL, IIO_MOD_Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec osf_gyro_channels[] = {
+ OSF_MOD_CHAN(IIO_ANGL_VEL, IIO_MOD_X, 0),
+ OSF_MOD_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, 1),
+ OSF_MOD_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec osf_mag_channels[] = {
+ OSF_MOD_CHAN(IIO_MAGN, IIO_MOD_X, 0),
+ OSF_MOD_CHAN(IIO_MAGN, IIO_MOD_Y, 1),
+ OSF_MOD_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, 0),
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const unsigned long osf_3axis_available_scan_masks[] = {
+ GENMASK(2, 0),
+ 0
+};
+
+static const unsigned long osf_temp_available_scan_masks[] = {
+ BIT(0),
+ 0
+};
+
+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),
+ .available_scan_masks = osf_3axis_available_scan_masks,
+ },
+ {
+ .sensor_type = OSF_SENSOR_GYROSCOPE,
+ .channel_count = 3,
+ .name = "osf-gyro",
+ .channels = osf_gyro_channels,
+ .num_channels = ARRAY_SIZE(osf_gyro_channels),
+ .available_scan_masks = osf_3axis_available_scan_masks,
+ },
+ {
+ .sensor_type = OSF_SENSOR_MAGNETOMETER,
+ .channel_count = 3,
+ .name = "osf-magn",
+ .channels = osf_mag_channels,
+ .num_channels = ARRAY_SIZE(osf_mag_channels),
+ .available_scan_masks = osf_3axis_available_scan_masks,
+ },
+ {
+ .sensor_type = OSF_SENSOR_TEMPERATURE,
+ .channel_count = 1,
+ .name = "osf-temp",
+ .channels = osf_temp_channels,
+ .num_channels = ARRAY_SIZE(osf_temp_channels),
+ .available_scan_masks = osf_temp_available_scan_masks,
+ },
+};
+
+static const struct osf_iio_sensor_spec *
+osf_iio_find_sensor_spec(u16 sensor_type, u16 channel_count)
+{
+ unsigned int i;
+
+ for (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);
+}
+
+const char *osf_iio_sensor_name(u16 sensor_type)
+{
+ unsigned int i;
+
+ for (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)
+ return -EOPNOTSUPP;
+
+ 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;
+ iio_dev->available_scan_masks = spec->available_scan_masks;
+
+ 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,
+ unsigned int channel_count)
+{
+ struct osf_iio_state *state = iio_priv(indio_dev);
+ s32 scan[OSF_MAX_SAMPLE_CHANNELS] = { };
+ s64 timestamp;
+
+ if (channel_count != state->spec->channel_count)
+ return -EPROTO;
+
+ memcpy(scan, values, channel_count * sizeof(*values));
+
+ /* Buffer state can change here; IIO rechecks it during the push path. */
+ if (!iio_buffer_enabled(indio_dev))
+ return 0;
+
+ timestamp = iio_get_time_ns(indio_dev);
+
+ return iio_push_to_buffers_with_ts_unaligned(indio_dev, scan,
+ channel_count * sizeof(*scan),
+ timestamp);
+}
diff --git a/drivers/iio/opensensorfusion/osf_iio.h b/drivers/iio/opensensorfusion/osf_iio.h
new file mode 100644
index 000000000..d90c58fc4
--- /dev/null
+++ b/drivers/iio/opensensorfusion/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,
+ unsigned int channel_count);
+bool osf_iio_sensor_supported(u16 sensor_type, u16 channel_count);
+const char *osf_iio_sensor_name(u16 sensor_type);
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH RFC v3 6/6] iio: osf: register IIO devices from capabilities
2026-05-29 12:10 ` [PATCH RFC v3 6/6] iio: osf: register IIO devices from capabilities Jinseob Kim
@ 2026-05-29 14:36 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-05-29 14:36 UTC (permalink / raw)
To: Jinseob Kim; +Cc: robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Uninitialized kernel heap memory is leaked to userspace through IIO buffer padding.
--
commit 9319c55b6b77ef9ce1f7e526e5ce9463c217bc27
Author: Jinseob Kim <kimjinseob88@gmail.com>
iio: osf: register IIO devices from capabilities
This commit updates the Open Sensor Fusion (OSF) driver to parse the first
capability report and create corresponding IIO devices. It also adds logic
to cache the latest samples and push enabled buffers directly to userspace.
> diff --git a/drivers/iio/opensensorfusion/osf_iio.c b/drivers/iio/opensensorfusion/osf_iio.c
> new file mode 100644
> index 0000000000000..5e509987876d7
> --- /dev/null
> +++ b/drivers/iio/opensensorfusion/osf_iio.c
[ ... ]
> +int osf_iio_push_sample(struct iio_dev *indio_dev, const s32 *values,
> + unsigned int channel_count)
> +{
> + struct osf_iio_state *state = iio_priv(indio_dev);
> + s32 scan[OSF_MAX_SAMPLE_CHANNELS] = { };
> + s64 timestamp;
> +
> + if (channel_count != state->spec->channel_count)
> + return -EPROTO;
> +
> + memcpy(scan, values, channel_count * sizeof(*values));
> +
> + /* Buffer state can change here; IIO rechecks it during the push path. */
> + if (!iio_buffer_enabled(indio_dev))
> + return 0;
> +
> + timestamp = iio_get_time_ns(indio_dev);
> +
> + return iio_push_to_buffers_with_ts_unaligned(indio_dev, scan,
> + channel_count * sizeof(*scan),
> + timestamp);
[Severity: High]
Does this leak uninitialized kernel heap memory to userspace?
The driver passes channel_count * sizeof(*scan) as the data_sz, which
is 12 bytes for a 3-axis sensor or 4 bytes for temperature.
However, the IIO core calculates the buffer's scan_bytes to include padding
to 8-byte align the 64-bit timestamp. This results in a scan size of 24
bytes for a 3-axis sensor, or 16 bytes for temperature.
Looking at iio_push_to_buffers_with_ts_unaligned(), the bounce buffer is
allocated without zeroing the memory:
drivers/iio/industrialio-buffer.c:iio_push_to_buffers_with_ts_unaligned() {
...
if (iio_dev_opaque->bounce_buffer_size != indio_dev->scan_bytes) {
void *bb;
bb = devm_krealloc(&indio_dev->dev,
iio_dev_opaque->bounce_buffer,
indio_dev->scan_bytes, GFP_KERNEL);
...
}
memcpy(iio_dev_opaque->bounce_buffer, data, data_sz);
return iio_push_to_buffers_with_timestamp(indio_dev, ...);
}
It only copies data_sz bytes into the bounce buffer and then appends the
8-byte timestamp, leaving the intermediate padding bytes uninitialized.
When this buffer is pushed to the kfifo, the uninitialized kernel memory is
exposed to userspace.
Could we pass a correctly padded and zero-initialized structure, or provide
the exact size including padding up to the timestamp, to avoid leaking
these bytes?
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260529121005.1470-1-kimjinseob88@gmail.com?part=6
^ permalink raw reply [flat|nested] 14+ messages in thread