From nobody Mon Jun 8 12:11:58 2026 Received: from mail-pj1-f52.google.com (mail-pj1-f52.google.com [209.85.216.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8293E3E0C61 for ; Fri, 29 May 2026 12:10:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780056651; cv=none; b=KeleISD80+5wYba1+iSNm8qslMCHEh5NaJeTMKvI6mi2X0FFA4AU8IdjTwT7EP6TlBakT2ZSboTc+fZDEn2wkF5vB9R4Nye8vPaUZ+Xm4jFtGBmkitxmG/e6HXNSTNueapWS31Ucrpm+987+ZiAV4mBg43TFbjQxFm0K+ml6LZg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780056651; c=relaxed/simple; bh=JnGEEFp8WTXWR0uwBO6AmcqSpeSfcYZH5tJyzNik8gw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=feWUmib22kIvdMQjXOhcn6kOJJeigszYW0ddgEZQlsai/sTJcCpW2RkNqewfNpePRdzyfURKOY89sEBRqXoYNTcfNtBOrXoUc/HpnC1dhXuP3OICkRoS6gPraBi/4WMB1DyWBBAXjiQWpRtfssi9IzkeKfK7FG7gLUhupJbC7iQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HxRbdapE; arc=none smtp.client-ip=209.85.216.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HxRbdapE" Received: by mail-pj1-f52.google.com with SMTP id 98e67ed59e1d1-36ba285e98bso1371864a91.2 for ; Fri, 29 May 2026 05:10:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780056650; x=1780661450; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=80UKwhf+3B8/I9ptiFQwWeU5LzEfb4vtDtyUw96NZBg=; b=HxRbdapEgceDRX4beW2i+VZBJyxGAOPQ7bTna9vcX6ATKFae4K66YJqhwdovn1RTbn AEhaZrMKEx33QTqnPTfeJ2EgLCp5x//xiInhsU6tmmCLEuGORpvHrueTREc+OnW6bFO7 FcB+qZS3PT00EI66+wATRt1qAwkdmgoI6CBcSNNcpIiN2vzwssqgqA2+k6y3yEk9Fgaf ptRI3/ydHPTmTyblfZoq8gG/uGLGkJVD63mXls0r48Am6TvZKL/LVEOPKSt9v432d0RH LxWGM/toI0UdZBzf0NF6vtnwnCw1usrWUS+VMZFk0YZUE2Lq4M2byStgeJh6qBzw9kSR Bhdg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780056650; x=1780661450; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=80UKwhf+3B8/I9ptiFQwWeU5LzEfb4vtDtyUw96NZBg=; b=ajcX4PxPisxosj6qpnPqW30vCM/eWXZp3XPgU/q1v0kT+JF5FV+AZPNr5rzMKwzRjF zRIhLHhDbxqA3SZ+XVm4Bv0RhKcBxfzR06InBd82WQRcz/zPtY/Gq7dEGoMmhke5ap63 6sbEeIXGkeozxLgin6odm0jJsWebXO7Xr42LvEL2QIkih+wUcE8rejUjQrf5LfAbVfwO XWmvua8mtEtYRUuKyAlH93ukEMHxsnDwngdVb6GROsH3nWlzOBvZUpV3lyBsakIOEWgc qS8pmqegNaPDS+9CRqmCDvll70tA65bgGJOqOCHdCq31vR3F1glvzBR+KPDfme7eg2Xq CyVw== X-Forwarded-Encrypted: i=1; AFNElJ9xjUwjHSyIL2oTYW3cDl9QV9VoaTDOwJnQFE1Ys1eNjMV0J+/d+NBTybfiDYLpfuT8fO62ocoSENNWRps=@vger.kernel.org X-Gm-Message-State: AOJu0YwXxXrvY9jIIyN3lQbackObuUbvNAdZk5+STaaDBB4N6foKde2y bO+rpLoxm+n8rRMBlt5hY/DbVPGoItglBbOp/7qv1XuvhoEhzByO+BIH X-Gm-Gg: Acq92OFuzw2sHr8l29b1DQ3q1hJDlTHM+QmY4cbZuJttXPj3B29fT9i9IRqzLuWLZFT I80zYbINfzJIxnWSYMVbmZjxX5jzqJR+D9KnxhTk9jcfNw9khTt0dc2mVlGK2W9TypU7jbB2XBn Ehf9JfARFlaWch6cAXT16Qs5FznHj/x0JBEHTEo8VoJ+Qd6uVb8bjRWZhysZ/gWa4r1YAOBMu0B Wd56NdyrWWCx4oqpG65x/DblD69KMhp6h5493/zEMldsYp2WYNZww8WEFgdveAfuRHdYeBeZ4Jp wEyKN+yyxCWRYST7buMmxV+grIsXx9G0DFODZr5wHiyEjzYM9ThASfhrviX7nskxeGzyqhnRdH0 A5N2wR1K0ZfU7U6BWM1bOUcNjKGHGe9c28RrSGivNfO3W861ecmUFqyRsA3x48BLZ/Rm/3BIwoj ns4J3IMXbzwyyZ9nJz8gXBYMPDaxbjImQO6/hPDJXkF9Tqy3ZV/Fr3Cj2PqQcnp7R4kL2RQPOyW rrVLttPBBsHGxjcmHELQJE0WN1GP4gl2Dzr01CmnMCMuvKe X-Received: by 2002:a17:90b:3a50:b0:369:a359:b192 with SMTP id 98e67ed59e1d1-36bbcc160damr3225238a91.10.1780056649582; Fri, 29 May 2026 05:10:49 -0700 (PDT) Received: from DESKTOP-G3E0OSP.localdomain ([112.172.255.242]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-36bbdf3afc6sm942820a91.7.2026.05.29.05.10.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 May 2026 05:10:49 -0700 (PDT) From: Jinseob Kim To: Jonathan Cameron , linux-iio@vger.kernel.org Cc: David Lechner , Nuno Sa , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH RFC v3 1/6] dt-bindings: iio: add OSF GREEN sensor aggregation device Date: Fri, 29 May 2026 21:10:00 +0900 Message-ID: <20260529121005.1470-2-kimjinseob88@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260529121005.1470-1-kimjinseob88@gmail.com> References: <20260529121005.1470-1-kimjinseob88@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Describe OSF GREEN as the first board target. Add vendor prefix and MAINTAINERS binding entry. Signed-off-by: Jinseob Kim --- .../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/opensensorfus= ion,osf-green.yaml diff --git a/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf= -green.yaml b/Documentation/devicetree/bindings/iio/imu/opensensorfusion,os= f-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 + +description: | + OSF GREEN is an STM32F405-based sensor aggregation board from the Open + Sensor Fusion open hardware project. It sends OSF0 capability, status, a= nd + 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 =3D "opensensorfusion,osf-green"; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Docum= entation/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 =20 +OPEN SENSOR FUSION IIO DRIVER +M: Jinseob Kim +S: Maintained +F: Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.ya= ml + OPEN FIRMWARE AND FLATTENED DEVICE TREE M: Rob Herring M: Saravana Kannan --=20 2.43.0 From nobody Mon Jun 8 12:11:58 2026 Received: from mail-pj1-f51.google.com (mail-pj1-f51.google.com [209.85.216.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 601063E0C7C for ; Fri, 29 May 2026 12:10:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780056657; cv=none; b=rHX/v4yyE68Om6autAiPCDs6l3lLXfQKq1NoJ26HTc7Ga4StRA5wdcwZVSSsUvQrGg/Z1UBRRP/Oe9tllTWQ6CDnCoZ/3wAcGO50daq6i9ju75oomLEb/cWhkdQ/g0Bt3z28cDtE+ycKV/sb/9/QZLyUx4yWjqAQRDldko4x4eM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780056657; c=relaxed/simple; bh=VH7Ka/cVlmbRionnl0lrx6wcs7N1rLhq7lvEXxwhNn4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ofy5mQ2QMrhW+ffdLSVPHmzolhTmfhMz5PfZ2qJVet8QvIig2PMh5zCjdAyEvehR+MWiltE8z5h0LsRQ77/t7TQ9fur15nunZ8dsQbEZVHkH/Z2XzFnWEgcd3Z3cuinUie6YKrJ9Zyaf7U6t9wjnKGtSyf1+Kl0tT4oraMVIh6w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=h1JzPhCH; arc=none smtp.client-ip=209.85.216.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="h1JzPhCH" Received: by mail-pj1-f51.google.com with SMTP id 98e67ed59e1d1-36baeec21dcso678138a91.3 for ; Fri, 29 May 2026 05:10:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780056654; x=1780661454; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=p5cZYVzlz8iPqUGTkOYAZDMrEG8HTlDhnuFROv+PHGc=; b=h1JzPhCHPiVtSVUVGO8LtJQRY2k27A7wieiqp5kKLXOrmzxU5LAcuON0e/JrY2hqQw 1PcBmK7sFBfsUJArl2qwPIZEMpkjIyeX5GpJZJBXuAX5mP2TjWMYgXhZpPjVUGYfoes9 bDBEUc6qELI2p3Tix4z+SfC79mG7td5b7OsS98X+fxNAt2YkRX5wFyQK5AufYlyHQqno dpGYu5v+tF/hMxsx71W3u4gRY5XG6MQGgVRI2YvnELeUy2T2nHyaiGK5BDl6UvhaQgvg yxE5zIx33lOxSI8oAza1tedI0B0R56nlkPBZJTjp5Q2pg6t/vXYDlJjt4XjFjQf/3Rzc xFxQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780056654; x=1780661454; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=p5cZYVzlz8iPqUGTkOYAZDMrEG8HTlDhnuFROv+PHGc=; b=jatiKhy4z4of4H0E88wdOLjlcDc14h8/a6E7CsSRugVhSrZACAD5HApoo7dRuBFl6q OuR9U/WAtI+/oMGETJeu+Z2dlnn12cxIEXL8ds5Q7bUZ/J6JQ428gNQaxcCBi+VOvwsu ZsoOwbxE2g6qOP2p4oaR1kr/agV7IWfk7jogG2nCf3/T7/Bc3avhPfNNgQpBu/tnfBUq uYW4F+nRQwk1SkbmYVZpnf7vubhi7V8aeKwjxg7ROdbtaecyLqeK2FrCWDRAu0aVS4fi GQuD1eIiZzLE8Fku0NUNUceC+STCyCZFUcHiTEOExClDyAQIAPHaCyI6tCMXWT/6KP3Y 2ogQ== X-Forwarded-Encrypted: i=1; AFNElJ/xGC3pVAsybQmzv/TKbKD1f9PYWIqtB7bTaJVdz4jpH9W8nkzak7oOAwLzGGgtcveWie8Ppkp7OIjcXHI=@vger.kernel.org X-Gm-Message-State: AOJu0Yxf5nXP4Hljn4Im6AjKt/s6IWJmO10Oka4i62sVaLMVe98UX5Qg TaIqm2FqQa0RpBH1fgrP5kOREvQ3V9JATDh50P96GBVXbjS34vvKpuZM X-Gm-Gg: Acq92OGTCCxH96Ou2HoLMD+keJtLqxBp7QX5pNLfryaoYsQzeVOWvabQSZgoto4NulK 7nL98WhThot++9tIeV7B3akknJKTHaYlfREIMNZ699NZHTWxbySGXT91Twc/VnebLNQpfQXPkRp DOU0gioXH94nLyupDJprhtwZBdhmlbYgwj9+EnxDxBhsXLrMhiQ1MTszstAhWde2YerpI4rt5WC 19DynaPKLWki6slgUB5x4ImAa1eMmnLijxbkf3G9cgjkItSoSZshG7QFtQDoLzGEoB0Uo7Oczyk HcfpZAM+kj9zJg3xIdzWCEKjoldPCC/xQBT2p6FOAn9lPqy4ktwMf/n3qvRcyXU8vNuT3r+oTtC X8oigFMi+Cwt+jHydp423cTPOhL4lROG1Ol//Fs38tAh8o8kgIR7jzxvSShWX0Bue2BUMSVOS2r gCLo89SSohIsv/pwEFT+bkb/ZGw5mt8gwoKwKVR3YHQWMpkRm6OW59+7jRzyYf0XUL0g3INvKVf 4tE4vyGjaK4RFpJuOyKyEoTmy2XzzA8JOA1BSOjH1EBstGl X-Received: by 2002:a17:90b:3f85:b0:36a:4832:54b9 with SMTP id 98e67ed59e1d1-36bbceb6f21mr3286153a91.27.1780056653512; Fri, 29 May 2026 05:10:53 -0700 (PDT) Received: from DESKTOP-G3E0OSP.localdomain ([112.172.255.242]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-36bbdf3afc6sm942820a91.7.2026.05.29.05.10.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 May 2026 05:10:53 -0700 (PDT) From: Jinseob Kim To: Jonathan Cameron , linux-iio@vger.kernel.org Cc: David Lechner , Nuno Sa , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH RFC v3 2/6] Documentation: iio: add Open Sensor Fusion protocol v0 reference Date: Fri, 29 May 2026 21:10:01 +0900 Message-ID: <20260529121005.1470-3-kimjinseob88@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260529121005.1470-1-kimjinseob88@gmail.com> References: <20260529121005.1470-1-kimjinseob88@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Document OSF0 UART frames and the supported RFC driver subset. Extend MAINTAINERS to cover the protocol reference. Signed-off-by: Jinseob Kim --- .../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/Documen= tation/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 +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D + +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 devi= ces +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 dri= ver +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 com= mand +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 b= it. +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 abo= ve +``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 =3D 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 en= try +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 dat= a 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 dri= ver +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 fr= ame +is processed by the host. + +The current RFC driver does not claim production-grade host/device timesta= mp +correlation. Buffered IIO timestamps are taken from IIO timestamp clock ha= ndling +when samples are pushed to IIO buffers. + +Non-goals for v0 upstream +------------------------- + +The v0 upstream driver does not include USB transport, fusion output, Atti= tude +and Heading Reference System (AHRS) output, Kalman output, calibration com= mand +ABI, custom sysfs control surface, production timestamp correlation, or ru= ntime +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 S: Maintained F: Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.ya= ml +F: Documentation/iio/open-sensor-fusion-protocol-v0.rst =20 OPEN FIRMWARE AND FLATTENED DEVICE TREE M: Rob Herring --=20 2.43.0 From nobody Mon Jun 8 12:11:58 2026 Received: from mail-pj1-f45.google.com (mail-pj1-f45.google.com [209.85.216.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E50BA3E120B for ; Fri, 29 May 2026 12:10:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780056660; cv=none; b=j5uoMPgPrE3PNNbR+/USyus3T+MUZEI/1KXOZoFpPBiW7USf0EJF7qbTc3SKgaaR1kf0WL02MLyyE1HbMkI7Umbm3G3TS1G/5NhcpD2euRamRObxBTFxiAwb8gkFDWSCDb/W95u7eexYxIaYaytBcuLoZEijHqN25c0JeraSaao= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780056660; c=relaxed/simple; bh=lNFULvOPvgrVMAHRIjbWwPfBe9IXIwaIEdD34oSFTD8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dIFiWyZ5tS4wnISWQ5lo4SnnyRbkEB3uUu+Ppuf8vmK4Gn4pCMrOmlzAYZrycZw71+M3ES/jbE3fw0Lx0YkeXgDIyyRj38meakyaxGHRgZ6quVKncWLcS9fyReVHFxTdTW8udkOjcSTZlRYqGsdbuSOwQY8AS782jYK1yxqZ/PQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=mVIq2xg2; arc=none smtp.client-ip=209.85.216.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="mVIq2xg2" Received: by mail-pj1-f45.google.com with SMTP id 98e67ed59e1d1-36babe2c4bdso634582a91.1 for ; Fri, 29 May 2026 05:10:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780056657; x=1780661457; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=OnVtfOUvffQvhhS/iftC/tK4tOsTF8ZY+RX/0peU1Yg=; b=mVIq2xg2mZ2CL6zgLOOTz481zX+XnEPRiZjg15ile2ePtGvS2MxI0e1DssBc3a7nSf nbPcDiIXiRyrUuZFj7edhPwmhA/1+ayR2iTb+aoyarS286Tvj/gYw5fHQjxtuFxkSYhx wXd0KHLta30X5dlVCpLB4NFaRcZmMugLz/A5QZj3nN01NaDd04Wu/WB8UqhwzSl+RW1K oBubaCXWNyi3p/V/SDleGMAU4CB5ZsPFym8vHQ8qkb5M76K8CZakqTWQHi5bgTIXpXS4 iFKfR0hXHy6h6D8hdqD64RXqJudGbuDSwI/6AX3K9pOKO5MCBNAQSGzXxFzJiTZx1/gJ +luw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780056657; x=1780661457; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=OnVtfOUvffQvhhS/iftC/tK4tOsTF8ZY+RX/0peU1Yg=; b=KEE/4bfEGwVI/JG9r7Z+eRuNZEjHkl0KoZW15huzj8cMt/lNNFcL2AJEi9oRz4hbG/ ogJrwW3QCCitKD0B+kz7kB4gOAkCofLRIpSQRBhUHBzNtaGZNk6A1lglTJcG5aw3cEFD UnqbzQowUNhHSCcWoD1S+VUMeyX0o0XDIYJqgIE5TGlpLPzDt/rBs2pa+67sIuJGjeHK TqdgmlKRvpf/GIJPRz5kGjZqK/V6IEKSVeC4nP+enjj5PXR55yfaLBemVfhlH9vLEYco bVzzFP/OavhMeS2Y/FkBR81jIbyq/jzgkPNFerLjmyScS/YrLtaNDkI1Vw2YoHtI3fJM unww== X-Forwarded-Encrypted: i=1; AFNElJ/6GfUJYDFEJ9QX9a5bKL4HhzKSagBg6zU2H/pjn9/xc6Rj/nxiPw30QdbmfRb2aVEm+rFCXvV+JvW3hsI=@vger.kernel.org X-Gm-Message-State: AOJu0Yzfs6a7nkZtN7QRR4z/xZtjtc/rLDV+YZDR/DQrZFCOLnTtQqpr u1QgEmIUBeU7qpTMVcN/OEJhj2UdOp0Lvdjl/j5Py3WDIE8a2mXrZzkY X-Gm-Gg: Acq92OF9iHkpbn/UX10rU/xkF+cTfLM/ywI2aBmvWFp0oEZw8ZC1K7CLMDK6q6uOzyx TVKa4VqbiRxuK3shUzzkzv1/s6njnun1efZ42ccY40puby+V/hbLvIABVRgW3kaK2UZUOw/FqKx RtuZ1aJsTbcEJa+09hoP+d85oC5yxwyfk3SZCy3lScDgKCQL5dfnjB0u6FYwWMBFQ0aRJWQqFGL eTIHzq41Hc7Jwnmq2iOVsq2+GLgyTq0lYPXaaQm81z9x2vZDv0A0pke8NFWBgVrO43Dcj9ETMw1 MyDgR5YAGmTYNfDe0eIRA7JAe70QYBidpq/KIbfcvgjaSwqtVYsMW+vUhh/UPsPNlaKcyf6Ck92 +3M5Db9AFHRN3yI5oiJkRLLmDvqqou2ok0knzFdXrEsxz/nsikC+Px2LoKWvAr3nG06kcs/JOVD F3yqsIoGjO02t56YPK66r8SkVWT6o6OUtXQ4Vl1Ch3qT/8CZOpyo+qJRWlfoSFrFrNv46HmbDwL d2wyqoEZtFWszVPa9AaG7aMDOVaYsPdaAU1UjOj1Y6OJ5u+3vVG25iFmNg= X-Received: by 2002:a17:90b:3d50:b0:368:1064:62f7 with SMTP id 98e67ed59e1d1-36bbcc14a21mr3334283a91.6.1780056657109; Fri, 29 May 2026 05:10:57 -0700 (PDT) Received: from DESKTOP-G3E0OSP.localdomain ([112.172.255.242]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-36bbdf3afc6sm942820a91.7.2026.05.29.05.10.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 May 2026 05:10:56 -0700 (PDT) From: Jinseob Kim To: Jonathan Cameron , linux-iio@vger.kernel.org Cc: David Lechner , Nuno Sa , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH RFC v3 3/6] iio: osf: add protocol v0 decoding Date: Fri, 29 May 2026 21:10:02 +0900 Message-ID: <20260529121005.1470-4-kimjinseob88@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260529121005.1470-1-kimjinseob88@gmail.com> References: <20260529121005.1470-1-kimjinseob88@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add OSF0 frame validation and payload decoders. Extend MAINTAINERS to cover the protocol decoder. Signed-off-by: Jinseob Kim --- 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 S: Maintained F: Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.ya= ml F: Documentation/iio/open-sensor-fusion-protocol-v0.rst +F: drivers/iio/opensensorfusion/osf_protocol.* =20 OPEN FIRMWARE AND FLATTENED DEVICE TREE M: Rob Herring diff --git a/drivers/iio/opensensorfusion/osf_protocol.c b/drivers/iio/open= sensorfusion/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 +#include +#include +#include +#include +#include + +#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 >=3D OSF_SENSOR_ACCELEROMETER && + sensor_type <=3D 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] !=3D 'O' || buf[1] !=3D 'S' || buf[2] !=3D 'F' || buf[3] !=3D = '0') + return -EPROTO; + + major =3D buf[4]; + if (major !=3D OSF_PROTOCOL_MAJOR) + return -EPROTO; + + if (get_unaligned_le16(buf + 6) !=3D OSF_FRAME_HEADER_LEN) + return -EPROTO; + + payload_len =3D 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 =3D OSF_FRAME_HEADER_LEN + payload_len + OSF_FRAME_CRC_LEN; + expected_crc =3D osf_crc32_ieee(buf, OSF_FRAME_HEADER_LEN + payload_len); + actual_crc =3D get_unaligned_le32(buf + OSF_FRAME_HEADER_LEN + payload_le= n); + + if (actual_crc !=3D expected_crc) + return -EBADMSG; + + frame->protocol_minor =3D buf[5]; + frame->message_type =3D get_unaligned_le16(buf + 8); + frame->payload_len =3D payload_len; + frame->sequence =3D get_unaligned_le64(buf + 14); + frame->timestamp_us =3D get_unaligned_le64(buf + 22); + frame->flags =3D get_unaligned_le32(buf + 30); + frame->payload =3D buf + OSF_FRAME_HEADER_LEN; + frame->crc =3D actual_crc; + *frame_len =3D 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 !=3D OSF_MSG_SENSOR_SAMPLE) + return -EPROTO; + + if (frame->payload_len < OSF_SENSOR_SAMPLE_BASE_LEN) + return -EMSGSIZE; + + payload =3D frame->payload; + sensor_type =3D get_unaligned_le16(payload); + channel_count =3D get_unaligned_le16(payload + 4); + sample_format =3D get_unaligned_le16(payload + 6); + + if (!osf_sensor_type_valid(sensor_type)) + return -EPROTO; + + if (!channel_count) + return -EPROTO; + + if (sample_format !=3D 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 =3D OSF_SENSOR_SAMPLE_BASE_LEN + channel_count * sizeof(s32); + if (frame->payload_len !=3D expected_len) + return -EMSGSIZE; + + sample->sensor_type =3D sensor_type; + sample->sensor_index =3D get_unaligned_le16(payload + 2); + sample->channel_count =3D channel_count; + sample->sample_format =3D sample_format; + sample->scale_nano =3D get_unaligned_le32(payload + 8); + sample->samples =3D payload + OSF_SENSOR_SAMPLE_BASE_LEN; + + return 0; +} + +int osf_protocol_sensor_sample_value(const struct osf_sensor_sample *sampl= e, + unsigned int index, s32 *value) +{ + if (!sample || !sample->samples || !value) + return -EINVAL; + + if (index >=3D sample->channel_count) + return -ERANGE; + + *value =3D (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 !=3D OSF_MSG_DEVICE_STATUS) + return -EPROTO; + + if (frame->payload_len !=3D OSF_DEVICE_STATUS_LEN) + return -EMSGSIZE; + + payload =3D frame->payload; + if (get_unaligned_le32(payload + 16)) + return -EPROTO; + + status->uptime_s =3D get_unaligned_le32(payload); + status->status_flags =3D get_unaligned_le32(payload + 4); + status->error_flags =3D get_unaligned_le32(payload + 8); + status->dropped_frames =3D 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 !=3D OSF_MSG_CAPABILITY_REPORT) + return -EPROTO; + + if (frame->payload_len < OSF_CAP_REPORT_BASE_LEN) + return -EMSGSIZE; + + payload =3D frame->payload; + capability_count =3D 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 =3D OSF_CAP_REPORT_BASE_LEN + + capability_count * OSF_CAP_SENSOR_ENTRY_LEN; + if (frame->payload_len !=3D expected_len) + return -EMSGSIZE; + + report->capability_count =3D capability_count; + report->entries =3D payload + OSF_CAP_REPORT_BASE_LEN; + + return 0; +} + +int osf_protocol_decode_capability_entry(const struct osf_capability_repor= t *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 >=3D report->capability_count) + return -ERANGE; + + payload =3D report->entries + index * OSF_CAP_SENSOR_ENTRY_LEN; + sensor_type =3D get_unaligned_le16(payload); + sample_format =3D get_unaligned_le16(payload + 6); + flags =3D get_unaligned_le32(payload + 12); + + if (!osf_sensor_type_valid(sensor_type)) + return -EPROTO; + + if (sample_format !=3D 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 =3D sensor_type; + entry->sensor_index =3D get_unaligned_le16(payload + 2); + entry->channel_count =3D get_unaligned_le16(payload + 4); + entry->sample_format =3D sample_format; + entry->scale_nano =3D get_unaligned_le32(payload + 8); + entry->flags =3D flags; + + return 0; +} diff --git a/drivers/iio/opensensorfusion/osf_protocol.h b/drivers/iio/open= sensorfusion/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 + +#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 =3D 0x0001, + OSF_MSG_DEVICE_STATUS =3D 0x0002, + OSF_MSG_CAPABILITY_REPORT =3D 0x0003, +}; + +enum osf_sensor_type { + OSF_SENSOR_ACCELEROMETER =3D 0x0001, + OSF_SENSOR_GYROSCOPE =3D 0x0002, + OSF_SENSOR_MAGNETOMETER =3D 0x0003, + OSF_SENSOR_BAROMETER =3D 0x0004, + OSF_SENSOR_TEMPERATURE =3D 0x0005, + OSF_SENSOR_HUMIDITY =3D 0x0006, + OSF_SENSOR_AMBIENT_LIGHT =3D 0x0007, + OSF_SENSOR_PROXIMITY =3D 0x0008, +}; + +enum osf_sample_format { + OSF_SAMPLE_FORMAT_S32 =3D 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_repor= t *report, + unsigned int index, + struct osf_capability_entry *entry); +int osf_protocol_sensor_sample_value(const struct osf_sensor_sample *sampl= e, + unsigned int index, s32 *value); + +#endif --=20 2.43.0 From nobody Mon Jun 8 12:11:58 2026 Received: from mail-pj1-f50.google.com (mail-pj1-f50.google.com [209.85.216.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4B4873E1209 for ; Fri, 29 May 2026 12:11:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780056663; cv=none; b=JXHc2RdanNRzujKUALVmCns2KniMSAK9TLefKkDofyuunWsDtwjNI7K8wBkhQRANdZB70r5wxFBWFNi4CHdmQYTvYqYOfmsvfxS0YiL3V7bCKrBZkGQv/TPUriiq6iE9PWGhxZEaBXpojDlEIvrVyztGubSkUCCg74aL4asFK0Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780056663; c=relaxed/simple; bh=5hBuvenJVW1uhvf1FlYMNBQsWWfgRCpmbHXB90VN/aQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZQvKWg9FqmjVVV2D4eF12BuNS3JR0DL0xNSvrtjHlym6PKbRjsUk9za5mtGpv6Hd8e6JwwdNGs1IgHD39JEW8wou/yw8uwRQYSXGmu4k2xeH55CNvtXT7pJYQrc0CP87nZijFXYt7A7O4Ak4aFFmZexuYOcg9KCzuoOxvkYX8KA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=f7wrRw6J; arc=none smtp.client-ip=209.85.216.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="f7wrRw6J" Received: by mail-pj1-f50.google.com with SMTP id 98e67ed59e1d1-36b95eb4bb4so770766a91.3 for ; Fri, 29 May 2026 05:11:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780056660; x=1780661460; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=emS1jQNoBv7TJ3i72JnQuDzTHH8V4k0g2GqipVr6FaE=; b=f7wrRw6JHzyNBVp6qLRzbnshBdZ/N+pBIsHmI35DA6MkzDaxexoTim6rMFIDMXFMSF f5RuHBhimEFDEKldZMlZ6zNT0Xh8X+dJk6P8ghgaidBhlKvA/JYwmq3PYuZoZe4XAptQ GooZRnc5ybSZyj8iKDHyEmtij+YjjlYdeoVoza+wBUMur2Xn0yE21ppB36hbGolPRACQ 9vh7/3cdglO/Mf9ld2jExXQJHzEbUNKmclemLJYgBYR4S2sOBuXNdvpj7+kNPImt5Jh3 AVlw5zP5VZw03tnQpmeTbA0QFKXVh70pYdGw9/7zGPzzi3aQwCOcsRAuNIm2uIe1ht6/ IIlw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780056660; x=1780661460; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=emS1jQNoBv7TJ3i72JnQuDzTHH8V4k0g2GqipVr6FaE=; b=qLLbDxColv248Sj/2gluPeK9+xkj0aHfuXP1eYaNGwlsLKIgKBUEGEmhLZBlS3yA9S cXWtKsmwEQ5RD/OCTmf+g2vrfdUaAobjwm6VFruz13J44YLNEUBe6pu9egTZ1lpyM16W bxIYYgYlVcdQr9q54BqusKTRw7UU9TxCyiBRYrWAZrB86YBD9jxy3BUF5oc1ts+qBVNp oBlKYZtHurEOcDHF7qSZQexoKL4OEyHLLgU2gYh1zQNRWkXGb24ih/4GaQqz0h9FeIAo gUtlDm3p/R9LX0IGHBG4EbdISdU8sFGK0vnMjcFaOGGQ5XUwqt4k/6ZVEj94LG2a2254 y5ow== X-Forwarded-Encrypted: i=1; AFNElJ/Wh/YVr+bpXrHLA2B1AfPu8RWos1Bl4F7BzwuN/iQSbDirptWKDRx8+R3+5CI8RvIpJQITGuu2eMr65CA=@vger.kernel.org X-Gm-Message-State: AOJu0YytrygYrDcSKZzkJaabHS8mDq09+R+EMCSIhMflm7jFy+ZWd+z8 tqaFJFDs4oilfE6sTJPdkWwrA4oNcIUGufFc3ljxNKjIFzPeDUCBw9VX X-Gm-Gg: Acq92OH5OCU/i+Id6qAfqcgzHZOavVnFGKVdlxE5IGdgEuBbcEQGTGhdTGDAQehJBcK ozBhRhMHHe49/KdJYe+0fSyDhZ0XY3mA0v512e6hPjdrQg3Dg5z0TG1faz3vvw93o1m7xTmyINc GkFLvtFs4YQNcmpTs6mYOYIg3lEopqQmzVDDUfra9Kav32xYiQfqHTnSuH0p2V5u7vVsWk4BHRq WM3HuisSsH4PgtLH/MYhMJFuxEKKqke3hsDs4ya3KkOxJtKLCMJDXRCWOwDMRlH9eFlrAi3tmMv wM3gVJcgSKjduw0p76p/lWMqm2M2jcO+MzpyLeOfkImVWgs/KmojyuEYCLdXrdjhYiZG9Lboj1g 8fjXVBklLvyCvHZ40U8nj0D8FpkKSqMa1sxLudp5brguZ01IaXS/JgQ7LduQNzU2o7EgIfIjSpf +Pr/1Jdcl/gXQDlBcQ6D80XOLrFATydvU3PrJ1AE+dQEtvlNZy8JKSzt/0KlwMAIy9s9ojBTupd zvzlHYvgKVE2FreZDJR1CHI2WRM1N4ELWDGdH09HFWtbMxF X-Received: by 2002:a17:90b:2f0f:b0:368:83e6:ca95 with SMTP id 98e67ed59e1d1-36bbc6c1b61mr3464420a91.0.1780056660426; Fri, 29 May 2026 05:11:00 -0700 (PDT) Received: from DESKTOP-G3E0OSP.localdomain ([112.172.255.242]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-36bbdf3afc6sm942820a91.7.2026.05.29.05.10.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 May 2026 05:11:00 -0700 (PDT) From: Jinseob Kim To: Jonathan Cameron , linux-iio@vger.kernel.org Cc: David Lechner , Nuno Sa , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH RFC v3 4/6] iio: osf: add stream parser Date: Fri, 29 May 2026 21:10:03 +0900 Message-ID: <20260529121005.1470-5-kimjinseob88@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260529121005.1470-1-kimjinseob88@gmail.com> References: <20260529121005.1470-1-kimjinseob88@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the byte stream assembler and resync handling for OSF0 UART frames. Signed-off-by: Jinseob Kim --- 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.ya= ml F: Documentation/iio/open-sensor-fusion-protocol-v0.rst F: drivers/iio/opensensorfusion/osf_protocol.* +F: drivers/iio/opensensorfusion/osf_stream.* =20 OPEN FIRMWARE AND FLATTENED DEVICE TREE M: Rob Herring diff --git a/drivers/iio/opensensorfusion/osf_stream.c b/drivers/iio/opense= nsorfusion/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 +#include +#include +#include + +#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] =3D { + 'O', 'S', 'F', '0', +}; + +static void osf_stream_discard(struct osf_stream *stream, size_t count) +{ + if (count >=3D stream->len) { + stream->len =3D 0; + return; + } + + memmove(stream->buf, stream->buf + count, stream->len - count); + stream->len -=3D 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 =3D stream->len; + size_t match_len; + size_t i; + + for (i =3D 0; i < stream->len; i++) { + match_len =3D stream->len - i; + if (match_len > OSF_STREAM_MAGIC_LEN) + match_len =3D 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 =3D 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 =3D 0; + int ret; + + while (stream->len) { + discarded =3D osf_stream_discard_to_magic(stream); + if (discarded) { + stream->stats.bad_magic_resyncs++; + stream->stats.dropped_bytes +=3D discarded; + if (!first_err) + first_err =3D -EPROTO; + } + + if (!stream->len) + break; + + if (stream->len < OSF_FRAME_HEADER_LEN) { + stream->stats.partial_frames++; + break; + } + + if (get_unaligned_le16(stream->buf + 6) !=3D + OSF_FRAME_HEADER_LEN) { + stream->stats.dropped_bytes++; + osf_stream_drop_invalid_head(stream); + if (!first_err) + first_err =3D -EPROTO; + continue; + } + + payload_len =3D 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 =3D -EMSGSIZE; + continue; + } + + frame_len =3D OSF_FRAME_HEADER_LEN + payload_len + OSF_FRAME_CRC_LEN; + if (stream->len < frame_len) { + stream->stats.partial_frames++; + break; + } + + ret =3D osf_protocol_decode_frame(stream->buf, frame_len, &frame, + &decoded_len); + if (ret) { + if (ret =3D=3D -EBADMSG) + stream->stats.bad_crc_frames++; + stream->stats.dropped_bytes++; + osf_stream_drop_invalid_head(stream); + if (!first_err) + first_err =3D ret; + continue; + } + + if (decoded_len !=3D frame_len) { + stream->stats.dropped_bytes++; + osf_stream_drop_invalid_head(stream); + if (!first_err) + first_err =3D -EMSGSIZE; + continue; + } + + ret =3D osf_core_receive_frame(stream->osf, stream->buf, frame_len); + if (ret) { + osf_stream_discard(stream, frame_len); + if (!first_err) + first_err =3D 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 =3D osf; + stream->len =3D 0; + memset(&stream->stats, 0, sizeof(stream->stats)); +} + +void osf_stream_reset(struct osf_stream *stream) +{ + if (stream) { + stream->len =3D 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 =3D 0; + int ret; + + if (!stream || !stream->osf || (!buf && len)) + return -EINVAL; + + if (!len) { + ret =3D osf_stream_process(stream); + if (ret && !first_err) + first_err =3D ret; + return first_err; + } + + while (len) { + space =3D OSF_STREAM_MAX_FRAME_LEN - stream->len; + if (!space) { + stream->stats.dropped_bytes++; + osf_stream_discard(stream, 1); + if (!first_err) + first_err =3D -EMSGSIZE; + continue; + } + + copy_len =3D len < space ? len : space; + memcpy(stream->buf + stream->len, buf, copy_len); + stream->len +=3D copy_len; + buf +=3D copy_len; + len -=3D copy_len; + + ret =3D osf_stream_process(stream); + if (ret && !first_err) + first_err =3D ret; + } + + return first_err; +} diff --git a/drivers/iio/opensensorfusion/osf_stream.h b/drivers/iio/opense= nsorfusion/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 + +#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 --=20 2.43.0 From nobody Mon Jun 8 12:11:58 2026 Received: from mail-pj1-f42.google.com (mail-pj1-f42.google.com [209.85.216.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6DE3E3E1681 for ; Fri, 29 May 2026 12:11:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780056666; cv=none; b=MonYsiVMn5idpwpaLhJu2X/vh1FhS1+qYH8OjJgEGDHVYRKdll1Aks+5cpUHIhgNH1TEkwqzkkHCHyl8+dflSdJ1onJRT6le+jaZUISu8iq3T2s6bETtLnQCLb4ILcs1K8y83AP3+khGcKHVPN9RaYFjXzixiHMp03T16aiQmgw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780056666; c=relaxed/simple; bh=c6/Aiu4UZ/sU0uPkdJkuopnm1atE3NwBxxm+EVVTIYE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bVvpkmbTUMUx8aShjr5iyfEuoHyW8oubnew1/fL7h61tKHp4hnvng/JuOgniEA9X4TifbMnAIYK6C4w82H+1gOCStX6npZAvF53MQyXtlMqYS2OkO6KtZ21BjeHWpPxns0KE02UeG46hD22bzLD0LKOe1nNem3mNbPgTTDZzrzU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=LfUcR+AH; arc=none smtp.client-ip=209.85.216.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="LfUcR+AH" Received: by mail-pj1-f42.google.com with SMTP id 98e67ed59e1d1-36bdb11bf8bso131815a91.0 for ; Fri, 29 May 2026 05:11:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780056664; x=1780661464; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=JQCaoukUuAifdwoQLb2AtvzNeKZEcEahL4zcCl7ipLA=; b=LfUcR+AH8z20kY8dSPn9thJvvthOcAFZu8mX0esWy5VP23TxxSMg8jUXTgKvcTkZcn MGISgvPdhTqpZ/Mb2s4GiG03lttSmj2Cr2z/kS4U3IQXIOTQm/nQqR19dzJJSG3hRWjI YiDndByAm6AVdp7tZOVGf5g1j7zQljmc5lREVkqM8gYvGMwQMuDqPKcA/qjoQMj7VRE2 ynHnsJq2+XFpqlPASO8CIHlXe9Z/T5c+w0q02VCz6NI9e12tFFm3ipesFp5/YoTxLKXu 9N+ZZRO6itO512x780DYhpQjRgzcfFkPO3XDrBcpdr9MpNDtnb4SZamZd9W5JnAZinbl KVVg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780056664; x=1780661464; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=JQCaoukUuAifdwoQLb2AtvzNeKZEcEahL4zcCl7ipLA=; b=dSJZDB83AQi+B7vU07WpOKF25XnbmqxC+uDfLDUK7XgcNzTiVRwLwQzsIb8xst3bNo SgREgvvupbWNKwvZc8c0HoHsBg6dsDV15VliUfWmCBv7qvUyRWCwd97cuvpkmzONOXJQ kcYsu67rM1WBcDaO1jyI+6gSW1cDgZG0icpMCtZl3RHXo3YO4PQjJku9PZuKu3bFtS6E 9io1+5qvRQ62I5UPyeRfeQBEJyZzPy/hFuoF3bAbw/nhea9XqcNGRtRgwto1fzwcQhOa 8VjGr39vtzASLeZcDlE177eenXHr7RjSYBmiMynabTfCFcG7gXfvWI/+og/0BwNQHPit 1SQQ== X-Forwarded-Encrypted: i=1; AFNElJ+pIRzqvRDaWNTv8lcsI86r0T24DGhDBMKaPSV88Dcw05N7EmYQokEP/JSqtwrsm6Xq2Y7YCN7TROjvwjA=@vger.kernel.org X-Gm-Message-State: AOJu0YwSjhN+QikF3NFlRPJDwKsNqeMkM4POmtHZIq33v06G9boq4yb+ e6mm10QlwCfb8TxXCg1iJeuXjq/6jRfiEIS5lWtufIHLmrvQTjAPTySgzVchFKUk6vM= X-Gm-Gg: Acq92OEldZHI49h+6C7ijZfLmCHvsnqKH5dPwqWiWejZfQvI3u8Sundmc6XHx1eVJum Gl8paZ12fxi1xqlkMBpWs97uj48+bYNdisQhU+10i0gCITWWAzBMNyKTong0Bgask7hwFevd2ri rpJPdHR17dBweLe1SpyksJmx5nsGVFOAYgwpYO3qZZYzcJ2r6fWrXxpPAABe6WIJuTfrJRIt0Qj ezCaqdlSoD6jHo5t+Hva/rTgJeG855NPAc8DvUp+CJ29Yw440Ecf+XpL8jDi4UM6h9VnpZQE4qZ FVGUYf+93GjS0yMX6BJA5cr9iFu9gHi4A3nZHgBUvYhCeYIQfrqa34fp2dIptkKwPPlXIsQIGp2 D5VqqK9f/T3LQCclDI+zEZWqeYTGk6d9/v2kFe7uiya5QJ4cO/AU/ypohA1o8nqvz2wRTVZSZvr hWYakoJsAWtjSn11AI63FFcR5YtdY/QivM15cWm+9k1iseSOH8qt2OoGhJjaf6B3kXsWuM6NWVj 2b3K6Dp1C8koSEjfApvcYJaLOt8KNf5lepkfy7hwVCaymu2kIfYN81uox4= X-Received: by 2002:a17:90a:ec88:b0:369:7421:b36f with SMTP id 98e67ed59e1d1-36bbceae629mr3232957a91.21.1780056663735; Fri, 29 May 2026 05:11:03 -0700 (PDT) Received: from DESKTOP-G3E0OSP.localdomain ([112.172.255.242]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-36bbdf3afc6sm942820a91.7.2026.05.29.05.11.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 May 2026 05:11:03 -0700 (PDT) From: Jinseob Kim To: Jonathan Cameron , linux-iio@vger.kernel.org Cc: David Lechner , Nuno Sa , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH RFC v3 5/6] iio: osf: add UART serdev transport Date: Fri, 29 May 2026 21:10:04 +0900 Message-ID: <20260529121005.1470-6-kimjinseob88@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260529121005.1470-1-kimjinseob88@gmail.com> References: <20260529121005.1470-1-kimjinseob88@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Register the OSF serdev driver. Pass received bytes into the OSF0 stream parser. Signed-off-by: Jinseob Kim --- 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 S: Maintained F: Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-green.ya= ml 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.* =20 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 +=3D light/ obj-y +=3D magnetometer/ obj-y +=3D multiplexer/ obj-y +=3D orientation/ +obj-y +=3D opensensorfusion/ obj-y +=3D position/ obj-y +=3D potentiometer/ obj-y +=3D potentiostat/ diff --git a/drivers/iio/opensensorfusion/Kconfig b/drivers/iio/opensensorf= usion/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/opensensor= fusion/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) +=3D open-sensor-fusion.o + +open-sensor-fusion-y :=3D osf_core.o osf_protocol.o osf_serdev.o osf_strea= m.o diff --git a/drivers/iio/opensensorfusion/osf_core.c b/drivers/iio/opensens= orfusion/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 +#include +#include + +#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 =3D 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 =3D 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 *fra= me) +{ + struct osf_capability_entry entry; + struct osf_capability_report report; + unsigned int i; + int ret; + + ret =3D osf_protocol_decode_capability_report(frame, &report); + if (ret) + return ret; + + for (i =3D 0; i < report.capability_count; i++) { + ret =3D 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 l= en) +{ + struct osf_frame frame; + size_t frame_len; + int ret; + + if (!osf || !buf) + return -EINVAL; + + ret =3D osf_protocol_decode_frame(buf, len, &frame, &frame_len); + if (ret) + return ret; + + if (frame_len !=3D len) + return -EMSGSIZE; + + switch (frame.message_type) { + case OSF_MSG_SENSOR_SAMPLE: + ret =3D osf_core_validate_sensor_sample(&frame); + break; + case OSF_MSG_DEVICE_STATUS: + ret =3D osf_core_validate_device_status(&frame); + break; + case OSF_MSG_CAPABILITY_REPORT: + ret =3D osf_core_validate_capability_report(&frame); + break; + default: + if (frame.message_type >=3D OSF_RESERVED_MSG_FIRST && + frame.message_type <=3D OSF_RESERVED_MSG_LAST) + ret =3D 0; + else if (frame.message_type >=3D OSF_VENDOR_PRIVATE_FIRST) + ret =3D 0; + else + ret =3D -EOPNOTSUPP; + break; + } + + if (!ret) + osf->last_sequence =3D frame.sequence; + + return ret; +} diff --git a/drivers/iio/opensensorfusion/osf_core.h b/drivers/iio/opensens= orfusion/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 + +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 l= en); + +#endif diff --git a/drivers/iio/opensensorfusion/osf_serdev.c b/drivers/iio/opense= nsorfusion/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 +#include +#include +#include +#include +#include +#include +#include + +#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 =3D serdev_device_get_drvdata(serdev); + const struct osf_stream_stats *stats; + u64 valid_before; + int ret; + + valid_before =3D osf_uart->stream.stats.valid_frames; + ret =3D osf_stream_receive_bytes(&osf_uart->stream, buf, count); + stats =3D &osf_uart->stream.stats; + + if (ret || stats->valid_frames !=3D valid_before) + dev_dbg_ratelimited(&serdev->dev, + "rx count=3D%zu valid=3D%llu bad_magic=3D%llu bad_crc=3D%llu parti= al=3D%llu dropped=3D%llu ret=3D%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 =3D { + .receive_buf =3D 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 =3D devm_kzalloc(&serdev->dev, sizeof(*osf_uart), GFP_KERNEL); + if (!osf_uart) + return -ENOMEM; + + osf_uart->serdev =3D 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 =3D serdev_device_open(serdev); + if (ret) + return ret; + + baudrate =3D serdev_device_set_baudrate(serdev, OSF_SERDEV_BAUD); + if (baudrate !=3D 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 =3D 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[] =3D { + { .compatible =3D "opensensorfusion,osf-green" }, + { } +}; +MODULE_DEVICE_TABLE(of, osf_serdev_of_match); + +static struct serdev_device_driver osf_serdev_driver =3D { + .probe =3D osf_serdev_probe, + .remove =3D osf_serdev_remove, + .driver =3D { + .name =3D "open-sensor-fusion-uart", + .of_match_table =3D osf_serdev_of_match, + }, +}; + +module_serdev_device_driver(osf_serdev_driver); + +MODULE_DESCRIPTION("Open Sensor Fusion IIO driver"); +MODULE_LICENSE("GPL"); --=20 2.43.0 From nobody Mon Jun 8 12:11:58 2026 Received: from mail-pj1-f43.google.com (mail-pj1-f43.google.com [209.85.216.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A8B683E1209 for ; Fri, 29 May 2026 12:11:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780056669; cv=none; b=irOpLsBsgS8Ox3t6Wa7kJj2+V/EGjDM0CkJ3TZ1Vv30gEq5GphzcRaNXSzB6iOjmhYNPrabOQ0l5kiv7kQAIACvogWu7RCO5o07bsYbtFtubzrKYKZESqtl0NtGpd9h4QJ9IGjcu1hyMfhGsurRdV7DqYTjizcVPWvK6Q2ZRBHU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780056669; c=relaxed/simple; bh=uh3qnP80X/x/loXtZjwzEIOnD5Ul0YdqfJrFcHQprAc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=k3WGV5wsaofCoFqcjB3TbKdnr3Ur+CUURNHIQ2g3lTYnF+pIMK8FDMrfaU6pHmf2DE/wHL0ZvS2dXF6TqdV8o+eRQKxYW51nutRzQaxSZ68/pIQpAULXiszzUdl2GPRjCmoz1OX88tbQ9cJYzQVQ8RZMKJI0+dEjbMvhWOOmyag= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ClMMxlp9; arc=none smtp.client-ip=209.85.216.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ClMMxlp9" Received: by mail-pj1-f43.google.com with SMTP id 98e67ed59e1d1-36b903567fdso1524583a91.1 for ; Fri, 29 May 2026 05:11:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780056667; x=1780661467; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ZDz79rsrbfxADkUstWv7Wa0YddM7uElqbIZ77wvpmyE=; b=ClMMxlp9SS+daTHWbgvSDe8Y8EhaUPQnXun7N3r2QR6l6edu3D+2MZq1u92pxtYnqn yAJcrMyuGOeY7RvAcLOXpHQ2VE9rZClWM29T2KMjcmKmuanZRaNmES1GbWxRZen/z/K3 On1V2ZNY7W8NL3FqfaX+Rtrl+4LF1r1/wD1QSYfteDlrBjbpTisqmVvr1jyTkwU6fGo4 uAZ0hkFP6OncA60A8V/cXVgADx9URKVa1jyum/b/LqR4VWHP1l8mcB+lSxwAjkJ/yrmB KJZ11FOy+UH8fhKS1r7kufpTx3/gT4P4VLODjhPS5Hin40BeSs7iqNPbc0ld72nUuSwy YP8Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780056667; x=1780661467; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ZDz79rsrbfxADkUstWv7Wa0YddM7uElqbIZ77wvpmyE=; b=b64SstkgirARVg5LSJUVdz6Qw75VllhZ+INtm3dRRIb8OGhU19/4rneWh7h4QxfhW2 O4wgnFCZt6wTItzmjYfbXWzcUMEQxtUSBAMq0kU8yL9k+2EHXP1gdKUc93kaVIFdyhCx gMJ1WEc4czvHEkBrJ5/qbHxF1SbW08GtvqreA04/YZCtEPjebgc7ZQ1pvEkYzNcjKIVA tvRNkW9xsYZZBaj+ltUo1b3HkSQ7Y9LNu4M3gdUPnhtaXbENCPAMJwbJ15Ayqucw1u4s 6mpa9uVAT+pDncMWg14Om6JjlN1I06fPypGI46VE224RL9qj7Q/0Ous7MgtHNTcWoibH Nsyw== X-Forwarded-Encrypted: i=1; AFNElJ/UB2K1KCasWhQseLjvcvH2l4mHApad0ebQkxhh7/WVgWTYXQ3nmzcv1YSOD8mAbvzySgondOjqehCDkdc=@vger.kernel.org X-Gm-Message-State: AOJu0YylDgQhUYq6L4e3HBwpj1aL1ae5VgvaQubmxd2gW+liKI9lIOmv zlxMKj8Kgi/a7B0hjzfITwXhv7BZHsB+bTnU4sdUKBLAtdTLRmkxBjiw X-Gm-Gg: Acq92OFyN2xlskJc2ZOvLrnBAmWylPYZ4CiC1Ikp9EpRiw8213nO+DSnu72k4zRPV3R JAVyhtEXsblsTmUGqAtTpEMrEc+FzNPSdFtmHt1eTFBSA/RMV9ZyaMb5J4obifph5ouJWj52HSC XjoquySLOiW0UV8COwNoOIZ6A8+mgE557+qssGVChFSsVaFdKt5cEW3T8JGDUjtMMj/wFUiVTLD X8cJ1tpV8TYfEFR0ntzGtd1GHjwBpBSV2VdJxf65L+16NHC8Rmp9aDwD79a6wt1QoyvlS18krkC YNP2e1fWbkEHx+RIP8a04Angy01dLPU36OzFIMB9EorK3WcuihmdQLexRq0hXaozcU+qRlgYody fWGUYT+X8aQBBSBNAlS6ZXyabewQnfthQ3+7VdZujPilg2H3fmiBR+cPPQpdAq7xGoHpu1qTgCZ hfEGaYvjQ2MP+kdTcYukMTBkfrmyD/pAoBSkggHno8zVRK5YaGkjdKJ7YMuavDHiOkAb6KoHHYD FKs+QQlIDIMLTkVA/zfV80wx2xT4HYRbyjjJ9J43HjiyLro X-Received: by 2002:a17:90b:3d0a:b0:364:8f64:49ea with SMTP id 98e67ed59e1d1-36bbd4488cfmr3323718a91.27.1780056666871; Fri, 29 May 2026 05:11:06 -0700 (PDT) Received: from DESKTOP-G3E0OSP.localdomain ([112.172.255.242]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-36bbdf3afc6sm942820a91.7.2026.05.29.05.11.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 May 2026 05:11:06 -0700 (PDT) From: Jinseob Kim To: Jonathan Cameron , linux-iio@vger.kernel.org Cc: David Lechner , Nuno Sa , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH RFC v3 6/6] iio: osf: register IIO devices from capabilities Date: Fri, 29 May 2026 21:10:05 +0900 Message-ID: <20260529121005.1470-7-kimjinseob88@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260529121005.1470-1-kimjinseob88@gmail.com> References: <20260529121005.1470-1-kimjinseob88@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Use the first capability report to create supported IIO devices. Cache latest samples and push enabled buffers directly. Signed-off-by: Jinseob Kim --- 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/opensensorf= usion/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. =20 - 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/opensensor= fusion/Makefile index 940c82edd..b4e03b80c 100644 --- a/drivers/iio/opensensorfusion/Makefile +++ b/drivers/iio/opensensorfusion/Makefile @@ -2,4 +2,5 @@ =20 obj-$(CONFIG_OPEN_SENSOR_FUSION) +=3D open-sensor-fusion.o =20 -open-sensor-fusion-y :=3D osf_core.o osf_protocol.o osf_serdev.o osf_strea= m.o +open-sensor-fusion-y :=3D 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/opensens= orfusion/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 =20 #include "osf_core.h" -#include "osf_protocol.h" +#include "osf_iio.h" =20 #define OSF_RESERVED_MSG_FIRST 0x7f00 #define OSF_RESERVED_MSG_LAST 0x7fff @@ -13,23 +13,175 @@ =20 void osf_core_init(struct osf_device *osf, struct device *dev) { - memset(osf, 0, sizeof(*osf)); + mutex_init(&osf->latest_lock); osf->dev =3D dev; } =20 void osf_core_unregister_iio(struct osf_device *osf) { + unsigned int i; + + for (i =3D 0; i < osf->iio_dev_count; i++) + osf_iio_unregister_sensor(osf->iio_devs[i].indio_dev); + + osf->iio_dev_count =3D 0; } =20 -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 =3D 0; i < osf->iio_dev_count; i++) { + binding =3D &osf->iio_devs[i]; + if (binding->sensor_type =3D=3D sensor_type && + binding->sensor_index =3D=3D 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 =3D 0; i < osf->latest_sample_count; i++) { + latest =3D &osf->latest_samples[i]; + if (latest->sensor_type =3D=3D sensor_type && + latest->sensor_index =3D=3D sensor_index) + return latest; + } + + if (osf->latest_sample_count >=3D OSF_MAX_CAPABILITIES) + return NULL; + + return &osf->latest_samples[osf->latest_sample_count++]; +} + +static bool osf_core_capability_is_duplicate(const struct osf_capability_c= ache *cache, + unsigned int index) +{ + const struct osf_capability_entry *entry =3D &cache->entries[index]; + unsigned int i; + + for (i =3D 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 =3D=3D entry->sensor_type && + cache->entries[i].sensor_index =3D=3D 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 =3D 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 =3D 0; i < cache->capability_count; i++) { + if (!osf_iio_sensor_supported(cache->entries[i].sensor_type, + cache->entries[i].channel_count)) + continue; + + ret =3D 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 =3D + cache->entries[i].sensor_type; + osf->iio_devs[osf->iio_dev_count].sensor_index =3D + cache->entries[i].sensor_index; + osf->iio_devs[osf->iio_dev_count].indio_dev =3D 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] =3D { }; + unsigned int i; + int ret; + + ret =3D osf_protocol_decode_sensor_sample(frame, &sample); + if (ret) + return ret; + + if (sample.channel_count > OSF_MAX_SAMPLE_CHANNELS) + return -E2BIG; =20 - return osf_protocol_decode_sensor_sample(frame, &sample); + for (i =3D 0; i < sample.channel_count; i++) { + ret =3D osf_protocol_sensor_sample_value(&sample, i, &values[i]); + if (ret) + return ret; + } + + mutex_lock(&osf->latest_lock); + latest =3D 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 =3D sample.sensor_type; + latest->sensor_index =3D sample.sensor_index; + latest->channel_count =3D sample.channel_count; + latest->sample_format =3D sample.sample_format; + latest->scale_nano =3D sample.scale_nano; + latest->sequence =3D frame->sequence; + latest->timestamp_us =3D frame->timestamp_us; + latest->valid =3D true; + osf->last_sequence =3D frame->sequence; + mutex_unlock(&osf->latest_lock); + + indio_dev =3D 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); } =20 -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 =3D { }; struct osf_device_status status; int ret; =20 @@ -37,15 +189,22 @@ static int osf_core_validate_device_status(const struc= t osf_frame *frame) if (ret) return ret; =20 - if (status.reserved) - return -EPROTO; + cache.uptime_s =3D status.uptime_s; + cache.status_flags =3D status.status_flags; + cache.error_flags =3D status.error_flags; + cache.dropped_frames =3D status.dropped_frames; + cache.sequence =3D frame->sequence; + cache.valid =3D true; + osf->status_cache =3D cache; + osf->last_sequence =3D frame->sequence; =20 return 0; } =20 -static int osf_core_validate_capability_report(const struct osf_frame *fra= me) +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 =3D { }; struct osf_capability_report report; unsigned int i; int ret; @@ -54,12 +213,32 @@ static int osf_core_validate_capability_report(const s= truct osf_frame *frame) if (ret) return ret; =20 + if (report.capability_count > OSF_MAX_CAPABILITIES) + return -E2BIG; + + if (osf->capability_cache.valid) { + osf->last_sequence =3D frame->sequence; + return 0; + } + for (i =3D 0; i < report.capability_count; i++) { - ret =3D osf_protocol_decode_capability_entry(&report, i, &entry); + ret =3D osf_protocol_decode_capability_entry(&report, i, + &cache.entries[i]); if (ret) return ret; } =20 + cache.capability_count =3D report.capability_count; + cache.sequence =3D frame->sequence; + cache.valid =3D true; + + ret =3D osf_core_register_capabilities(osf, &cache); + if (ret) + return ret; + + osf->capability_cache =3D cache; + osf->last_sequence =3D frame->sequence; + return 0; } =20 @@ -81,27 +260,46 @@ int osf_core_receive_frame(struct osf_device *osf, con= st u8 *buf, size_t len) =20 switch (frame.message_type) { case OSF_MSG_SENSOR_SAMPLE: - ret =3D osf_core_validate_sensor_sample(&frame); - break; + return osf_core_handle_sensor_sample(osf, &frame); case OSF_MSG_DEVICE_STATUS: - ret =3D osf_core_validate_device_status(&frame); - break; + return osf_core_handle_device_status(osf, &frame); case OSF_MSG_CAPABILITY_REPORT: - ret =3D osf_core_validate_capability_report(&frame); - break; + return osf_core_handle_capability_report(osf, &frame); default: if (frame.message_type >=3D OSF_RESERVED_MSG_FIRST && frame.message_type <=3D OSF_RESERVED_MSG_LAST) + return 0; + if (frame.message_type >=3D 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 =3D -ENODATA; + + if (!osf || !value) + return -EINVAL; + + mutex_lock(&osf->latest_lock); + for (i =3D 0; i < osf->latest_sample_count; i++) { + latest =3D &osf->latest_samples[i]; + if (latest->sensor_type !=3D sensor_type || + latest->sensor_index !=3D sensor_index) + continue; + + if (latest->valid && channel < latest->channel_count) { + *value =3D latest->values[channel]; ret =3D 0; - else if (frame.message_type >=3D OSF_VENDOR_PRIVATE_FIRST) - ret =3D 0; - else - ret =3D -EOPNOTSUPP; + } break; } - - if (!ret) - osf->last_sequence =3D frame.sequence; + mutex_unlock(&osf->latest_lock); =20 return ret; } diff --git a/drivers/iio/opensensorfusion/osf_core.h b/drivers/iio/opensens= orfusion/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 =20 +#include #include =20 +#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; +}; =20 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; }; =20 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 l= en); +int osf_core_read_latest_sample(struct osf_device *osf, u16 sensor_type, + u16 sensor_index, unsigned int channel, + s32 *value); =20 #endif diff --git a/drivers/iio/opensensorfusion/osf_iio.c b/drivers/iio/opensenso= rfusion/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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 =3D 's', \ + .realbits =3D 32, \ + .storagebits =3D 32, \ + .endianness =3D IIO_CPU, \ + } + +#define OSF_MOD_CHAN(_type, _mod, _idx) \ + { \ + .type =3D (_type), \ + .modified =3D 1, \ + .channel2 =3D (_mod), \ + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index =3D (_idx), \ + .scan_type =3D OSF_SCAN_TYPE_S32, \ + } + +#define OSF_CHAN(_type, _idx) \ + { \ + .type =3D (_type), \ + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index =3D (_idx), \ + .scan_type =3D OSF_SCAN_TYPE_S32, \ + } + +static const struct iio_chan_spec osf_accel_channels[] =3D { + 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[] =3D { + 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[] =3D { + 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[] =3D { + OSF_CHAN(IIO_TEMP, 0), + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const unsigned long osf_3axis_available_scan_masks[] =3D { + GENMASK(2, 0), + 0 +}; + +static const unsigned long osf_temp_available_scan_masks[] =3D { + BIT(0), + 0 +}; + +static const struct osf_iio_sensor_spec osf_iio_sensor_specs[] =3D { + { + .sensor_type =3D OSF_SENSOR_ACCELEROMETER, + .channel_count =3D 3, + .name =3D "osf-accel", + .channels =3D osf_accel_channels, + .num_channels =3D ARRAY_SIZE(osf_accel_channels), + .available_scan_masks =3D osf_3axis_available_scan_masks, + }, + { + .sensor_type =3D OSF_SENSOR_GYROSCOPE, + .channel_count =3D 3, + .name =3D "osf-gyro", + .channels =3D osf_gyro_channels, + .num_channels =3D ARRAY_SIZE(osf_gyro_channels), + .available_scan_masks =3D osf_3axis_available_scan_masks, + }, + { + .sensor_type =3D OSF_SENSOR_MAGNETOMETER, + .channel_count =3D 3, + .name =3D "osf-magn", + .channels =3D osf_mag_channels, + .num_channels =3D ARRAY_SIZE(osf_mag_channels), + .available_scan_masks =3D osf_3axis_available_scan_masks, + }, + { + .sensor_type =3D OSF_SENSOR_TEMPERATURE, + .channel_count =3D 1, + .name =3D "osf-temp", + .channels =3D osf_temp_channels, + .num_channels =3D ARRAY_SIZE(osf_temp_channels), + .available_scan_masks =3D 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 =3D 0; i < ARRAY_SIZE(osf_iio_sensor_specs); i++) { + if (osf_iio_sensor_specs[i].sensor_type =3D=3D sensor_type && + osf_iio_sensor_specs[i].channel_count =3D=3D 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 =3D 0; i < ARRAY_SIZE(osf_iio_sensor_specs); i++) { + if (osf_iio_sensor_specs[i].sensor_type =3D=3D 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 =3D iio_priv(indio_dev); + s32 raw; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret =3D osf_core_read_latest_sample(state->osf, + state->spec->sensor_type, + state->sensor_index, + chan->scan_index, &raw); + if (ret) + return ret; + + *val =3D raw; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val =3D state->scale_nano / NANO; + *val2 =3D state->scale_nano % NANO; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static const struct iio_info osf_iio_info =3D { + .read_raw =3D 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 =3D osf_iio_find_sensor_spec(entry->sensor_type, + entry->channel_count); + if (!spec) + return -EOPNOTSUPP; + + if (entry->sample_format !=3D OSF_SAMPLE_FORMAT_S32) + return -EOPNOTSUPP; + + iio_dev =3D iio_device_alloc(dev, sizeof(*state)); + if (!iio_dev) + return -ENOMEM; + + state =3D iio_priv(iio_dev); + state->spec =3D spec; + state->scale_nano =3D entry->scale_nano; + state->sensor_index =3D entry->sensor_index; + state->osf =3D osf; + + iio_dev->name =3D spec->name; + iio_dev->info =3D &osf_iio_info; + iio_dev->modes =3D INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + iio_dev->channels =3D spec->channels; + iio_dev->num_channels =3D spec->num_channels; + iio_dev->available_scan_masks =3D spec->available_scan_masks; + + state->buffer =3D iio_kfifo_allocate(); + if (!state->buffer) { + ret =3D -ENOMEM; + goto err_free_iio; + } + + ret =3D iio_device_attach_buffer(iio_dev, state->buffer); + if (ret) + goto err_free_buffer; + + ret =3D iio_device_register(iio_dev); + if (ret) + goto err_free_buffer; + + *indio_dev =3D 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 =3D 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 =3D iio_priv(indio_dev); + s32 scan[OSF_MAX_SAMPLE_CHANNELS] =3D { }; + s64 timestamp; + + if (channel_count !=3D 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 =3D 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/opensenso= rfusion/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 + +#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 --=20 2.43.0