From nobody Mon May 25 01:14:39 2026 Received: from mail-pg1-f172.google.com (mail-pg1-f172.google.com [209.85.215.172]) (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 58D42364E9A for ; Wed, 20 May 2026 07:29:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779262150; cv=none; b=E5hgR3QqL3JwM/gUVMMSz+nx+ic2PZgFGrGpxgUg66GaKgT6FZUNxcnhZN0iY7yEMDVOlh0K7BGS1mCro759uneVZseCQm3U9m10gQoL6+Jh4AuQR6e+z1nClsgg5C8RsalyUrheSkBhCbyU6q5tgFbzKapKw4DyUnTexckZjXI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779262150; c=relaxed/simple; bh=pfyc341zXwBMZUZMj3FlAUbHl7aoZPTE0DnxlgS3F+0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LLIkN4VcMZUC0T7brcWSjg8/GPZypEZvcGFlsSsFs+vVedU8DWtk43ZlFyms7lvtU7za8S59WKJSsSkd0oLSKC7WiCQ2R7Ddj6qBSQoMd7dzorjutECxv0Su8VsjpOtO5EobS4bigEWBUwwHt2akKu565qP78lDV/rKKRUXlBnc= 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=O9yGWpdI; arc=none smtp.client-ip=209.85.215.172 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="O9yGWpdI" Received: by mail-pg1-f172.google.com with SMTP id 41be03b00d2f7-c80203b9d7bso1957426a12.0 for ; Wed, 20 May 2026 00:29:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779262149; x=1779866949; 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=n/uTr0N6/dohQUIDQm2+TsDj3+wcu3fpVpKrvpLtD7k=; b=O9yGWpdIYqbAYXLRxZ0LVq345gExNXJQisM76xIlVWQHo5KApOFzydmcicoc5Tj5ep qPru9Qu5SHIiBgU5tSEHSB4Zawg43JtwZcoq/fQ59AGTjPUGQRzyGT50gcaQsoELtwH2 e9EAwsn8nhOlqwi6u/1sbIDfTSpwRuDiKdWgcCIg3QIjDCdB6forxv3S+1Ciy26hT8SI 3h+ZD8jT68l7jRoTqFpJcJLGNMOfub3TbvpjPb4NW95GvrxCHL07bd44KQqfhhW/p09J 8x8osgtzAj0CXFDGLUmRaUPWWDuuwUHiVmy9fLIOwFViFN9qQyf2fV9fDZSB1k2zwbkA PFNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779262149; x=1779866949; 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=n/uTr0N6/dohQUIDQm2+TsDj3+wcu3fpVpKrvpLtD7k=; b=Y99Ss59CzOD3Erej5T584ACRKRuuaCeMPVd27SqPxNejbsRD8wx8vMnR0cXw0YchlD BIPSWn+jq4vm+rdkOv2RT3V2ati9poTElRYPkhV4i+Jut6sGfFRbBwo4OGdgEpbU0clL Q8N6uR3wF9gSrC9xU+vaBM1YaguSow92WHi4Ka9Pe/s1PumAPh/44oTqUX5szPk4Bksw bvF1CxtVy9t6U5gzEe7/05AeIaoCp8pekWjTNBOkXyzh6kx36nlrLX6UgUefhHo7heFD GQOJAvXErSVVTBg7sPrZTcbrnbjAFPKb84y+m8gtJ2INdDEbXjHAnjxwx4Qb1njz4GGb aaYg== X-Forwarded-Encrypted: i=1; AFNElJ/d9aHnGS3HsWCXCjSZXWcwtV65T4v7Bvr/MIrNeAbKIrXeFO6daDsXquW+vxra9eYsOtyyS/JA077YGyw=@vger.kernel.org X-Gm-Message-State: AOJu0YwmrV8usQRj+epx6YmTkzl1aEZQ49ahcW7czTI0JotzhsqYy8uo drY8m/HmU3no3HroiyLrIo+HXRry/2h23iy0SRXkmLBPX4ci8ysEkXVS X-Gm-Gg: Acq92OEi/eUdjiYqFALSF2wEE8I0yErY5w9bL94/Uv9MDSQHDaQhpEIdpcbnEXECg3W PAEYsdUrQclrmziYwcV+iO455pDhBdJHbB0UUn7af0vYKdV4UzsH7Y4dkBov4u92xA2Q5Bkngo8 VlOEm1NdJs78dA6uFuAltdpVWFzmtG+62FxjkviEqmukH92YrYrQUlduWS04q8e4NEUwgSm94WY dLEKLiguB8ZLUtIgxFRsqeXFcEgprUJgsSw4cbP/46ciOTnrHmAVmLEuSs2/aTr9+3lNvxQhqT2 sh+lEhIsZCNLbsfzEiKmtlVzrUqfZnuBaDmRpAdfotlXfGKpqV6i+EfPkstJHOo8MopgV6h/IhF 40/kzkQspeSrlCNTXdCUUZhEVrDXQxM3fHyjqo+WmA/BiZ0L3SJiYrcjuNK2SNpLiFO2ZluXaeI cpjRM8qZY7ZwIoFas5pCSDdRhANMR9gn06GYz03UUK5hJ2JHFsjkHl0ZkMy1WcfM8qN1iwvrMD5 xz4jUZGDJdfJrGOZs/hsdnwIng51h+P+xOivFZ2GRJ6vrZs X-Received: by 2002:a17:902:da82:b0:2b2:5491:e32f with SMTP id d9443c01a7336-2bd7e821ad6mr244788625ad.16.1779262148716; Wed, 20 May 2026 00:29:08 -0700 (PDT) Received: from DESKTOP-G3E0OSP.localdomain ([112.172.255.242]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2bd5c05f27csm206258595ad.25.2026.05.20.00.29.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 May 2026 00:29:08 -0700 (PDT) From: Jinseob Kim To: Jonathan Cameron , linux-iio@vger.kernel.org Cc: David Lechner , =?UTF-8?q?Nuno=20S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH RFC 1/3] dt-bindings: iio: imu: add Open Sensor Fusion UART binding Date: Wed, 20 May 2026 16:28:41 +0900 Message-ID: <20260520072843.3593-2-kimjinseob88@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260520072843.3593-1-kimjinseob88@gmail.com> References: <20260520072843.3593-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 a binding for a UART-attached Open Sensor Fusion device. The device is exposed as a serdev child node using the opensensorfusion,osf-uart compatible string. Keep Raspberry Pi overlay and test notes outside the binding. The binding has been checked with dt_binding_check. Signed-off-by: Jinseob Kim --- .../iio/imu/opensensorfusion,osf-uart.yaml | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/imu/opensensorfus= ion,osf-uart.yaml diff --git a/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf= -uart.yaml b/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf= -uart.yaml new file mode 100644 index 000000000..6f329c326 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-uart.y= aml @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/imu/opensensorfusion,osf-uart.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Open Sensor Fusion UART Sensor Aggregation Device + +maintainers: + - Jinseob Kim + +description: | + Open Sensor Fusion is a UART-attached sensor aggregation device. The dev= ice + exposes an OSF protocol v0 data stream over its host UART interface and = may + report capabilities and samples for multiple sensor classes. + +properties: + compatible: + const: opensensorfusion,osf-uart + +required: + - compatible + +additionalProperties: false + +examples: + - | + serial { + sensor { + compatible =3D "opensensorfusion,osf-uart"; + }; + }; +... --=20 2.43.0 From nobody Mon May 25 01:14:39 2026 Received: from mail-pl1-f169.google.com (mail-pl1-f169.google.com [209.85.214.169]) (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 3C5EE36EA8A for ; Wed, 20 May 2026 07:29:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779262159; cv=none; b=lp+DN5y7JjoSDchDAje7VR3rpHmV7fDVx/QuvuAqkSRzQkaBfxaUUFxxcRB3ckzVn3wEhHrsMjVB1OlsG0taLl+8XfyAxM4LM4O7STRS1drTujYdvhOl5gHm8nO+uBcUblFI82tYwajlu0HLwOPCKYb203k8M3qBoPRsmnufr4A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779262159; c=relaxed/simple; bh=Gu27Dom3lyD2xAWq/J9qYhU3INYVUnIv5dHBpDjb4SY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bycnQWdvBnvq7ejrmFplRRadcwqNHDcfgdHfZ9mu+JHsPi8LfFGwoIh9j1UbgCO4GkrdhgoyU23kGejhempoqRAotgMVLNqPFDRrTq9jhriUC0zBfOlYj6Rbd7aFn9BJjIAW5gu9RjBuxZL9MkL1M9LqGQk29GpaHlb7bLoU+eA= 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=eDw6LSx9; arc=none smtp.client-ip=209.85.214.169 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="eDw6LSx9" Received: by mail-pl1-f169.google.com with SMTP id d9443c01a7336-2bd9c3b550aso30350075ad.2 for ; Wed, 20 May 2026 00:29:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779262155; x=1779866955; 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=Fz5bVeel0FFil37S+isgnhG4r6yAXdYjxLmUxtppSew=; b=eDw6LSx9kjpM7qAj3NTft19BTN9s5dVb+Dw++oQY3YGw8p9rN7meHbD4JbIYqLxRVB IhxYMcWtysy1DuZ2o977YfFhScSK3bXA1MotBJ7sL3GfdXJ1pLEtHlHPgEwS6luU90F3 82g7klu2mhF+N1J9VCQfrhOQYFTsojF0za9UrFRbJ4aiKWWfEUWYgWOFxhsEfx77DLBL 8TV0qlJNIgkFAxEoY0j84BlDVklCJhsXNEGXbcLUXvscO0mcqKcDuczy+3kuQgzslpMG zoxRupPrPVOvwcgVQkJl8XwJ+UqKZFzzEXIAsGx9QnoMtqdMNF+b7a7cC3xTuTvt0MJR hauA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779262155; x=1779866955; 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=Fz5bVeel0FFil37S+isgnhG4r6yAXdYjxLmUxtppSew=; b=Y0sOwLxMmQTc5PMyex0yzeEGQ4S8XETp9lchKZJ9T4obbq+FHpMTuKHHVCVcYf5UR9 Ue6SMSQHpF5eULd5ADxkV/tzkWLxlsYvhHyo3ArfKtWXQbBT7FCvEI+FiMer0J+10fkd 00pIL+UkJVwrPqdTN3n+X2Zyv9+FA+MQPoevcuZX5xhP/rJnvi4pnJ8FBOZbj1cvMfSQ hfTv5QfGTmihaK36f0ztcZQCkj/id/g/Ffl32F8S1sIwqHjRtWyJYL/cSmm5Doabsm5r v7HHyBgiydxhhQV96FPtAfJ2FOcCQUtxDCjvJBRSHUFdzgf0Nfz10wAzvsYjJ07H/mhk gw7g== X-Forwarded-Encrypted: i=1; AFNElJ9aUwIYZcY+s7WLZQJKiqH51bJaHD9Vt2ZtQCddAzs6t+BEIAHaZ7/Oa4m9Ep/7D+M2biR5x3cdu/foX54=@vger.kernel.org X-Gm-Message-State: AOJu0YzIbT864OV/ewQqrPDkyTA+kr5PW6g3yUdaOwcUdOnodWFHOe6I /aCT8kZSXrLuck6caHy+ng+5pm+7XFY0vI5PLP9MBFkdYzfgY2j0ImY7 X-Gm-Gg: Acq92OGK9B13E3BfLjm5CKkJ87TJW6xKJJzoBFVnm37mt0w/bwBvmp+DN6Su7e4DkDI Zbxk93LjpVATqeA0J6eBKE8ylw0Qv3iZIgyIIjWvM5h8qPKe22ocwoMVvtmcypTSA3DAM1aPSiL vce2d7B9huybdaMuliUjeftZ5wzk09Ag1AneyaevDCEFE9SN+RNC62BZAGcqP9eIP8V9lbLlEc7 B56zbgmSO+gtg+akK13fi1w47KuPBXEJflEnECHnJJAIEQknS8EYN3mYPaXDxNT0F78hlYzG2Dy usw9Sv6W/18iAZlcvO7SuLbmJZmiB+C8WHpqdKdDiMP6F/8LUCDJqRMxOaRss8SPx6tTTlsQzOD buGS9Z/IQQYppkKFQxylCNk6b2zzXNMFhzZfnaNB8dt2pbuvuZCrpqkmg2woP9MDTZ5oXnJ0sEK s7SgX3RS+YKi0mebpcpBAI8UrZmutKwVRIvC2NmcbUFbT97NUA55I9Jj+cL1JA5+NT40Hp0arfs m2+Jbgf76WMvWrRfQVheE200WryCRLbppN7HkK0EEUc4qvoSVMDF89qbnI= X-Received: by 2002:a17:902:f144:b0:2bc:b141:8551 with SMTP id d9443c01a7336-2bd7e87ed77mr188200855ad.19.1779262154965; Wed, 20 May 2026 00:29:14 -0700 (PDT) Received: from DESKTOP-G3E0OSP.localdomain ([112.172.255.242]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2bd5c05f27csm206258595ad.25.2026.05.20.00.29.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 May 2026 00:29:14 -0700 (PDT) From: Jinseob Kim To: Jonathan Cameron , linux-iio@vger.kernel.org Cc: David Lechner , =?UTF-8?q?Nuno=20S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH RFC 2/3] iio: osf: add Open Sensor Fusion UART IIO driver Date: Wed, 20 May 2026 16:28:42 +0900 Message-ID: <20260520072843.3593-3-kimjinseob88@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260520072843.3593-1-kimjinseob88@gmail.com> References: <20260520072843.3593-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 initial Open Sensor Fusion UART IIO driver. This includes the protocol v0 parser, OSF0 stream assembler, core capability/sample cache, serdev UART transport, IIO read_raw path, and software kfifo buffer support. The first RFC keeps the driver code in one patch to avoid intermediate non-buildable states. The internal files still separate protocol, stream, core, transport, and IIO code. The tested path is STM32F405 test firmware OSF UART stream to Raspberry Pi serdev, then IIO read_raw and buffered userspace reads. Not included here: real sensor reads, USB transport, fusion/AHRS/Kalman, runtime capability removal, or production timestamp correlation. Signed-off-by: Jinseob Kim --- drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/opensensorfusion/Kconfig | 15 + drivers/iio/opensensorfusion/Makefile | 6 + drivers/iio/opensensorfusion/osf_core.c | 334 ++++++++++++++++++ drivers/iio/opensensorfusion/osf_core.h | 81 +++++ drivers/iio/opensensorfusion/osf_iio.c | 268 +++++++++++++++ drivers/iio/opensensorfusion/osf_iio.h | 21 ++ drivers/iio/opensensorfusion/osf_protocol.c | 234 +++++++++++++ drivers/iio/opensensorfusion/osf_protocol.h | 100 ++++++ drivers/iio/opensensorfusion/osf_serdev.c | 354 ++++++++++++++++++++ drivers/iio/opensensorfusion/osf_serdev.h | 8 + drivers/iio/opensensorfusion/osf_stream.c | 212 ++++++++++++ drivers/iio/opensensorfusion/osf_stream.h | 31 ++ 14 files changed, 1666 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_iio.c create mode 100644 drivers/iio/opensensorfusion/osf_iio.h create mode 100644 drivers/iio/opensensorfusion/osf_protocol.c create mode 100644 drivers/iio/opensensorfusion/osf_protocol.h create mode 100644 drivers/iio/opensensorfusion/osf_serdev.c create mode 100644 drivers/iio/opensensorfusion/osf_serdev.h create mode 100644 drivers/iio/opensensorfusion/osf_stream.c create mode 100644 drivers/iio/opensensorfusion/osf_stream.h 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..1db1dbe56 --- /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 + select IIO_BUFFER + select IIO_KFIFO_BUF + help + Build the Open Sensor Fusion UART IIO driver. + + This includes the serdev UART receive path for OSF0 protocol + bring-up. It does not include real sensor reads, USB transport, + or fusion/AHRS/Kalman output. diff --git a/drivers/iio/opensensorfusion/Makefile b/drivers/iio/opensensor= fusion/Makefile new file mode 100644 index 000000000..b4e03b80c --- /dev/null +++ b/drivers/iio/opensensorfusion/Makefile @@ -0,0 +1,6 @@ +# 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_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 new file mode 100644 index 000000000..fd2eccefc --- /dev/null +++ b/drivers/iio/opensensorfusion/osf_core.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include + +#include "osf_core.h" +#include "osf_serdev.h" + +#define OSF_RESERVED_MSG_FIRST 0x7f00 +#define OSF_RESERVED_MSG_LAST 0x7fff +#define OSF_VENDOR_PRIVATE_FIRST 0x8000 + +static struct osf_latest_sample * +osf_core_find_latest_sample(struct osf_device *osf, u16 sensor_type, + u16 sensor_index, bool allocate) +{ + struct osf_latest_sample *free_slot =3D NULL; + struct osf_latest_sample *latest; + unsigned int i; + + for (i =3D 0; i < OSF_MAX_LATEST_SAMPLES; i++) { + latest =3D &osf->latest_samples[i]; + if (!latest->valid) { + if (!free_slot) + free_slot =3D latest; + continue; + } + + if (latest->sensor_type =3D=3D sensor_type && + latest->sensor_index =3D=3D sensor_index) + return latest; + } + + return allocate ? free_slot : NULL; +} + +static void osf_core_store_latest_sample(struct osf_latest_sample *latest, + const struct osf_sensor_sample *sample, + const s32 *values, + const struct osf_frame *frame) +{ + unsigned int i; + + for (i =3D 0; i < sample->channel_count; i++) + latest->values[i] =3D values[i]; + + for (; i < OSF_MAX_SAMPLE_CHANNELS; i++) + latest->values[i] =3D 0; + + 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; +} + +static int osf_core_handle_sensor_sample(struct osf_device *osf, + const struct osf_frame *frame) +{ + osf_sample_callback_t callback; + void *callback_context; + struct osf_latest_sample *latest; + struct osf_sample_event event =3D { }; + struct osf_sensor_sample sample; + s32 values[OSF_MAX_SAMPLE_CHANNELS] =3D { }; + unsigned long flags; + 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; + + for (i =3D 0; i < sample.channel_count; i++) { + ret =3D osf_protocol_sensor_sample_value(&sample, i, &values[i]); + if (ret) + return ret; + } + + event.sensor_type =3D sample.sensor_type; + event.sensor_index =3D sample.sensor_index; + event.channel_count =3D sample.channel_count; + event.sample_format =3D sample.sample_format; + event.scale_nano =3D sample.scale_nano; + event.sequence =3D frame->sequence; + event.timestamp_us =3D frame->timestamp_us; + event.host_timestamp_ns =3D ktime_get_ns(); + for (i =3D 0; i < sample.channel_count; i++) + event.values[i] =3D values[i]; + + spin_lock_irqsave(&osf->lock, flags); + latest =3D osf_core_find_latest_sample(osf, sample.sensor_type, + sample.sensor_index, true); + if (!latest) { + spin_unlock_irqrestore(&osf->lock, flags); + return -ENOSPC; + } + + osf_core_store_latest_sample(latest, &sample, values, frame); + osf_core_store_latest_sample(&osf->latest_sample, &sample, values, + frame); + osf->last_sequence =3D frame->sequence; + callback =3D osf->sample_callback; + callback_context =3D osf->sample_callback_context; + spin_unlock_irqrestore(&osf->lock, flags); + + if (callback) + callback(callback_context, &event); + + return 0; +} + +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; + unsigned long flags; + int ret; + + ret =3D osf_protocol_decode_device_status(frame, &status); + if (ret) + return ret; + + 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; + spin_lock_irqsave(&osf->lock, flags); + osf->status_cache =3D cache; + osf->last_sequence =3D frame->sequence; + spin_unlock_irqrestore(&osf->lock, flags); + + return 0; +} + +static int osf_core_handle_capability_report(struct osf_device *osf, + const struct osf_frame *frame) +{ + struct osf_capability_cache cache =3D { }; + struct osf_capability_report report; + unsigned long flags; + unsigned int i; + int ret; + + ret =3D osf_protocol_decode_capability_report(frame, &report); + if (ret) + return ret; + + if (report.capability_count > OSF_MAX_CAPABILITIES) + return -E2BIG; + + for (i =3D 0; i < report.capability_count; i++) { + ret =3D osf_protocol_decode_capability_entry(&report, i, + &cache.entries[i]); + if (ret) + return ret; + } + + cache.capability_count =3D report.capability_count; + cache.sequence =3D frame->sequence; + cache.valid =3D true; + spin_lock_irqsave(&osf->lock, flags); + osf->capability_cache =3D cache; + osf->last_sequence =3D frame->sequence; + spin_unlock_irqrestore(&osf->lock, flags); + + 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: + return osf_core_handle_sensor_sample(osf, &frame); + case OSF_MSG_DEVICE_STATUS: + return osf_core_handle_device_status(osf, &frame); + case OSF_MSG_CAPABILITY_REPORT: + 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 long flags; + int ret =3D 0; + + if (!osf || !value) + return -EINVAL; + + spin_lock_irqsave(&osf->lock, flags); + latest =3D osf_core_find_latest_sample(osf, sensor_type, + sensor_index, false); + if (!latest) + latest =3D &osf->latest_sample; + + if (!latest->valid) { + ret =3D -ENODATA; + goto out_unlock; + } + + if (latest->sensor_type !=3D sensor_type || + latest->sensor_index !=3D sensor_index) { + ret =3D -ENODATA; + goto out_unlock; + } + + if (channel >=3D latest->channel_count) { + ret =3D -ENODATA; + goto out_unlock; + } + + *value =3D latest->values[channel]; + +out_unlock: + spin_unlock_irqrestore(&osf->lock, flags); + + return ret; +} + +void osf_core_init_device(struct osf_device *osf) +{ + if (!osf) + return; + + spin_lock_init(&osf->lock); + osf->sample_callback =3D NULL; + osf->sample_callback_context =3D NULL; +} + +bool osf_core_copy_capability_cache(struct osf_device *osf, + struct osf_capability_cache *cache) +{ + unsigned long flags; + bool valid; + + if (!osf || !cache) + return false; + + spin_lock_irqsave(&osf->lock, flags); + valid =3D osf->capability_cache.valid; + if (valid) + *cache =3D osf->capability_cache; + spin_unlock_irqrestore(&osf->lock, flags); + + return valid; +} + +bool osf_core_capability_sequence(struct osf_device *osf, u64 *sequence) +{ + unsigned long flags; + bool valid; + + if (!osf || !sequence) + return false; + + spin_lock_irqsave(&osf->lock, flags); + valid =3D osf->capability_cache.valid; + if (valid) + *sequence =3D osf->capability_cache.sequence; + spin_unlock_irqrestore(&osf->lock, flags); + + return valid; +} + +void osf_core_set_sample_callback(struct osf_device *osf, + osf_sample_callback_t callback, + void *context) +{ + unsigned long flags; + + if (!osf) + return; + + spin_lock_irqsave(&osf->lock, flags); + osf->sample_callback =3D callback; + osf->sample_callback_context =3D context; + spin_unlock_irqrestore(&osf->lock, flags); +} + +static int __init osf_core_init(void) +{ + return osf_serdev_register_driver(); +} + +static void __exit osf_core_exit(void) +{ + osf_serdev_unregister_driver(); +} + +module_init(osf_core_init); +module_exit(osf_core_exit); + +MODULE_DESCRIPTION("Open Sensor Fusion IIO skeleton"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/opensensorfusion/osf_core.h b/drivers/iio/opensens= orfusion/osf_core.h new file mode 100644 index 000000000..882683721 --- /dev/null +++ b/drivers/iio/opensensorfusion/osf_core.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _OSF_CORE_H +#define _OSF_CORE_H + +#include +#include + +#include "osf_protocol.h" + +#define OSF_MAX_SAMPLE_CHANNELS 3 +#define OSF_MAX_CAPABILITIES 16 +#define OSF_MAX_LATEST_SAMPLES OSF_MAX_CAPABILITIES + +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_sample_event { + 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; + s64 host_timestamp_ns; +}; + +typedef void (*osf_sample_callback_t)(void *context, + const struct osf_sample_event *event); + +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_device { + /* Protects latest sample, capability, status, and callback state. */ + spinlock_t lock; + struct osf_latest_sample latest_sample; + struct osf_latest_sample latest_samples[OSF_MAX_LATEST_SAMPLES]; + struct osf_capability_cache capability_cache; + struct osf_status_cache status_cache; + u64 last_sequence; + osf_sample_callback_t sample_callback; + void *sample_callback_context; +}; + +void osf_core_init_device(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); +bool osf_core_copy_capability_cache(struct osf_device *osf, + struct osf_capability_cache *cache); +bool osf_core_capability_sequence(struct osf_device *osf, u64 *sequence); +void osf_core_set_sample_callback(struct osf_device *osf, + osf_sample_callback_t callback, + void *context); + +#endif diff --git a/drivers/iio/opensensorfusion/osf_iio.c b/drivers/iio/opensenso= rfusion/osf_iio.c new file mode 100644 index 000000000..3d2674e43 --- /dev/null +++ b/drivers/iio/opensensorfusion/osf_iio.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "osf_core.h" +#include "osf_iio.h" + +#define OSF_SCALE_NANO 1000000000U +#define OSF_IIO_SCAN_BYTES \ + (ALIGN(OSF_MAX_SAMPLE_CHANNELS * sizeof(s32), sizeof(s64)) + \ + sizeof(s64)) + +struct osf_iio_sensor_spec { + u16 sensor_type; + u16 channel_count; + const char *name; + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct osf_iio_state { + const struct osf_iio_sensor_spec *spec; + 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_LE, \ + } + +#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 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), + }, + { + .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), + }, + { + .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), + }, + { + .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), + }, +}; + +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: + if (!state->osf) + return -ENODATA; + + if (chan->scan_index < 0) + return -EINVAL; + + 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 / OSF_SCALE_NANO; + *val2 =3D state->scale_nano % OSF_SCALE_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_push_sample(struct iio_dev *indio_dev, + const struct osf_sample_event *event) +{ + struct osf_iio_state *state; + u8 scan[OSF_IIO_SCAN_BYTES] __aligned(8) =3D { }; + s32 *scan_values =3D (s32 *)scan; + unsigned int i; + + if (!indio_dev || !event) + return -EINVAL; + + if (!iio_buffer_enabled(indio_dev)) + return 0; + + state =3D iio_priv(indio_dev); + if (event->sensor_type !=3D state->spec->sensor_type || + event->sensor_index !=3D state->sensor_index) + return -EINVAL; + + if (event->sample_format !=3D OSF_SAMPLE_FORMAT_S32 || + event->channel_count !=3D state->spec->channel_count || + event->channel_count > OSF_MAX_SAMPLE_CHANNELS) + return -EINVAL; + + for (i =3D 0; i < event->channel_count; i++) + scan_values[i] =3D event->values[i]; + + return iio_push_to_buffers_with_timestamp(indio_dev, scan, + event->host_timestamp_ns); +} + +int osf_iio_register_sensor(struct device *dev, + const struct osf_capability_entry *entry, + void *driver_data, struct iio_dev **indio_dev) +{ + const struct osf_iio_sensor_spec *spec; + struct osf_iio_state *state; + struct iio_dev *iio_dev; + int ret; + + if (!dev || !entry) + return -EINVAL; + + 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 devm_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 driver_data; + + 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; + + ret =3D devm_iio_kfifo_buffer_setup(dev, iio_dev, NULL); + if (ret) + return ret; + + ret =3D devm_iio_device_register(dev, iio_dev); + if (ret) + return ret; + + if (indio_dev) + *indio_dev =3D iio_dev; + + return 0; +} diff --git a/drivers/iio/opensensorfusion/osf_iio.h b/drivers/iio/opensenso= rfusion/osf_iio.h new file mode 100644 index 000000000..1e8d7a39b --- /dev/null +++ b/drivers/iio/opensensorfusion/osf_iio.h @@ -0,0 +1,21 @@ +/* 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_sample_event; + +int osf_iio_register_sensor(struct device *dev, + const struct osf_capability_entry *entry, + void *driver_data, struct iio_dev **indio_dev); +int osf_iio_push_sample(struct iio_dev *indio_dev, + const struct osf_sample_event *event); +bool osf_iio_sensor_supported(u16 sensor_type, u16 channel_count); +const char *osf_iio_sensor_name(u16 sensor_type); + +#endif diff --git a/drivers/iio/opensensorfusion/osf_protocol.c b/drivers/iio/open= sensorfusion/osf_protocol.c new file mode 100644 index 000000000..8235d3af5 --- /dev/null +++ b/drivers/iio/opensensorfusion/osf_protocol.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include + +#include "osf_protocol.h" + +#define OSF_CRC32_INIT 0xffffffffU +#define OSF_CRC32_XOROUT 0xffffffffU + +static u16 osf_get_le16(const u8 *buf) +{ + return buf[0] | buf[1] << 8; +} + +static u32 osf_get_le32(const u8 *buf) +{ + return (u32)buf[0] | (u32)buf[1] << 8 | + (u32)buf[2] << 16 | (u32)buf[3] << 24; +} + +static u64 osf_get_le64(const u8 *buf) +{ + return (u64)osf_get_le32(buf) | (u64)osf_get_le32(buf + 4) << 32; +} + +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 (osf_get_le16(buf + 6) !=3D OSF_FRAME_HEADER_LEN) + return -EPROTO; + + payload_len =3D osf_get_le32(buf + 10); + if (payload_len > len - OSF_FRAME_MIN_LEN) + return -EMSGSIZE; + + 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 osf_get_le32(buf + OSF_FRAME_HEADER_LEN + payload_len); + + if (actual_crc !=3D expected_crc) + return -EBADMSG; + + frame->protocol_minor =3D buf[5]; + frame->message_type =3D osf_get_le16(buf + 8); + frame->payload_len =3D payload_len; + frame->sequence =3D osf_get_le64(buf + 14); + frame->timestamp_us =3D osf_get_le64(buf + 22); + frame->flags =3D osf_get_le32(buf + 30); + frame->reserved =3D osf_get_le32(buf + 34); + 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; + u32 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; + channel_count =3D osf_get_le16(payload + 4); + sample_format =3D osf_get_le16(payload + 6); + + if (sample_format !=3D OSF_SAMPLE_FORMAT_S32) + return -EPROTO; + + 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 osf_get_le16(payload); + sample->sensor_index =3D osf_get_le16(payload + 2); + sample->channel_count =3D channel_count; + sample->sample_format =3D sample_format; + sample->scale_nano =3D osf_get_le32(payload + 8); + sample->reserved =3D osf_get_le32(payload + 12); + 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)osf_get_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; + status->uptime_s =3D osf_get_le32(payload); + status->status_flags =3D osf_get_le32(payload + 4); + status->error_flags =3D osf_get_le32(payload + 8); + status->dropped_frames =3D osf_get_le32(payload + 12); + status->reserved =3D osf_get_le32(payload + 16); + + return 0; +} + +int osf_protocol_decode_capability_report(const struct osf_frame *frame, + struct osf_capability_report *report) +{ + u16 capability_count; + u32 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 osf_get_le16(payload); + 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->reserved =3D osf_get_le16(payload + 2); + if (report->reserved) + return -EPROTO; + + 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) +{ + 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; + entry->sensor_type =3D osf_get_le16(payload); + entry->sensor_index =3D osf_get_le16(payload + 2); + entry->channel_count =3D osf_get_le16(payload + 4); + entry->sample_format =3D osf_get_le16(payload + 6); + entry->scale_nano =3D osf_get_le32(payload + 8); + entry->flags =3D osf_get_le32(payload + 12); + entry->reserved =3D osf_get_le32(payload + 16); + + if (!osf_sensor_type_valid(entry->sensor_type)) + return -EPROTO; + + if (entry->sample_format !=3D OSF_SAMPLE_FORMAT_S32) + return -EPROTO; + + if (entry->flags & ~OSF_CAPABILITY_FLAGS_MASK) + return -EPROTO; + + if (entry->reserved) + return -EPROTO; + + 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..fd6e9581f --- /dev/null +++ b/drivers/iio/opensensorfusion/osf_protocol.h @@ -0,0 +1,100 @@ +/* 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; + u32 reserved; + const u8 *payload; + u32 crc; +}; + +struct osf_sensor_sample { + u16 sensor_type; + u16 sensor_index; + u16 channel_count; + u16 sample_format; + u32 scale_nano; + u32 reserved; + const u8 *samples; +}; + +struct osf_device_status { + u32 uptime_s; + u32 status_flags; + u32 error_flags; + u32 dropped_frames; + u32 reserved; +}; + +struct osf_capability_report { + u16 capability_count; + u16 reserved; + const u8 *entries; +}; + +struct osf_capability_entry { + u16 sensor_type; + u16 sensor_index; + u16 channel_count; + u16 sample_format; + u32 scale_nano; + u32 flags; + u32 reserved; +}; + +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 diff --git a/drivers/iio/opensensorfusion/osf_serdev.c b/drivers/iio/opense= nsorfusion/osf_serdev.c new file mode 100644 index 000000000..1827760c2 --- /dev/null +++ b/drivers/iio/opensensorfusion/osf_serdev.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "osf_core.h" +#include "osf_iio.h" +#include "osf_serdev.h" +#include "osf_stream.h" + +#define OSF_SERDEV_BAUD 115200 +#define OSF_SERDEV_BUFFER_QUEUE_LEN 32 + +struct osf_serdev_iio { + u16 sensor_type; + u16 sensor_index; + struct iio_dev *indio_dev; +}; + +struct osf_serdev { + struct serdev_device *serdev; + struct osf_device osf; + struct osf_stream stream; + struct work_struct iio_register_work; + struct work_struct buffer_push_work; + /* Serializes IIO device table access and registration work. */ + struct mutex iio_lock; + /* Protects sample events queued from the receive path. */ + spinlock_t sample_lock; + struct osf_serdev_iio iio[OSF_MAX_CAPABILITIES]; + unsigned int iio_count; + struct osf_sample_event sample_queue[OSF_SERDEV_BUFFER_QUEUE_LEN]; + unsigned int sample_queue_head; + unsigned int sample_queue_tail; + unsigned int sample_queue_count; + u64 sample_queue_drops; + atomic64_t capability_sequence_scheduled; +}; + +static struct iio_dev *osf_serdev_find_iio(struct osf_serdev *osf_uart, + const struct osf_sample_event *event) +{ + unsigned int i; + + for (i =3D 0; i < osf_uart->iio_count; i++) { + if (osf_uart->iio[i].sensor_type =3D=3D event->sensor_type && + osf_uart->iio[i].sensor_index =3D=3D event->sensor_index) + return osf_uart->iio[i].indio_dev; + } + + return NULL; +} + +static bool osf_serdev_iio_registered(struct osf_serdev *osf_uart, + const struct osf_capability_entry *entry) +{ + unsigned int i; + + for (i =3D 0; i < osf_uart->iio_count; i++) { + if (osf_uart->iio[i].sensor_type =3D=3D entry->sensor_type && + osf_uart->iio[i].sensor_index =3D=3D entry->sensor_index) + return true; + } + + return false; +} + +static int osf_serdev_register_iio(struct osf_serdev *osf_uart, + const struct osf_capability_entry *entry) +{ + struct device *dev =3D &osf_uart->serdev->dev; + struct iio_dev *indio_dev; + int ret; + + if (osf_serdev_iio_registered(osf_uart, entry)) + return 0; + + if (!osf_iio_sensor_supported(entry->sensor_type, entry->channel_count) || + entry->sample_format !=3D OSF_SAMPLE_FORMAT_S32) { + dev_dbg(dev, + "ignoring unsupported capability sensor=3D%u index=3D%u channels=3D%u f= ormat=3D%u\n", + entry->sensor_type, entry->sensor_index, + entry->channel_count, entry->sample_format); + return 0; + } + + if (osf_uart->iio_count >=3D ARRAY_SIZE(osf_uart->iio)) { + dev_warn(dev, "IIO registration table full, ignoring sensor=3D%u index= =3D%u\n", + entry->sensor_type, entry->sensor_index); + return 0; + } + + ret =3D osf_iio_register_sensor(dev, entry, &osf_uart->osf, &indio_dev); + if (ret) + return ret; + + osf_uart->iio[osf_uart->iio_count].sensor_type =3D entry->sensor_type; + osf_uart->iio[osf_uart->iio_count].sensor_index =3D entry->sensor_index; + osf_uart->iio[osf_uart->iio_count].indio_dev =3D indio_dev; + osf_uart->iio_count++; + + return 1; +} + +static void osf_serdev_iio_register_work(struct work_struct *work) +{ + struct osf_serdev *osf_uart =3D + container_of(work, struct osf_serdev, iio_register_work); + struct device *dev =3D &osf_uart->serdev->dev; + struct osf_capability_cache cache; + unsigned int registered =3D 0; + unsigned int i; + int ret; + + if (!osf_core_copy_capability_cache(&osf_uart->osf, &cache)) + return; + + mutex_lock(&osf_uart->iio_lock); + for (i =3D 0; i < cache.capability_count; i++) { + ret =3D osf_serdev_register_iio(osf_uart, &cache.entries[i]); + if (ret) { + if (ret > 0) { + registered++; + continue; + } + + dev_err(dev, + "failed to register IIO sensor=3D%u index=3D%u: %d\n", + cache.entries[i].sensor_type, + cache.entries[i].sensor_index, ret); + } + } + mutex_unlock(&osf_uart->iio_lock); + + if (registered) + dev_info(dev, + "registered %u Open Sensor Fusion IIO devices from capability report s= eq=3D%llu\n", + registered, (unsigned long long)cache.sequence); +} + +static bool osf_serdev_dequeue_sample(struct osf_serdev *osf_uart, + struct osf_sample_event *event) +{ + unsigned long flags; + bool valid =3D false; + + spin_lock_irqsave(&osf_uart->sample_lock, flags); + if (osf_uart->sample_queue_count) { + *event =3D osf_uart->sample_queue[osf_uart->sample_queue_tail]; + osf_uart->sample_queue_tail =3D + (osf_uart->sample_queue_tail + 1) % + ARRAY_SIZE(osf_uart->sample_queue); + osf_uart->sample_queue_count--; + valid =3D true; + } + spin_unlock_irqrestore(&osf_uart->sample_lock, flags); + + return valid; +} + +static void osf_serdev_buffer_push_work(struct work_struct *work) +{ + struct osf_serdev *osf_uart =3D + container_of(work, struct osf_serdev, buffer_push_work); + struct device *dev =3D &osf_uart->serdev->dev; + struct osf_sample_event event; + struct iio_dev *indio_dev; + int ret; + + while (osf_serdev_dequeue_sample(osf_uart, &event)) { + mutex_lock(&osf_uart->iio_lock); + indio_dev =3D osf_serdev_find_iio(osf_uart, &event); + if (indio_dev) { + ret =3D osf_iio_push_sample(indio_dev, &event); + if (ret) + dev_dbg_ratelimited(dev, + "failed to push IIO buffer sample sensor=3D%u index=3D%u ret=3D%= d\n", + event.sensor_type, + event.sensor_index, ret); + } + mutex_unlock(&osf_uart->iio_lock); + } +} + +static void osf_serdev_sample_ready(void *context, + const struct osf_sample_event *event) +{ + struct osf_serdev *osf_uart =3D context; + unsigned long flags; + u64 drops =3D 0; + bool queued =3D false; + + if (!osf_uart || !event) + return; + + spin_lock_irqsave(&osf_uart->sample_lock, flags); + if (osf_uart->sample_queue_count < ARRAY_SIZE(osf_uart->sample_queue)) { + osf_uart->sample_queue[osf_uart->sample_queue_head] =3D *event; + osf_uart->sample_queue_head =3D + (osf_uart->sample_queue_head + 1) % + ARRAY_SIZE(osf_uart->sample_queue); + osf_uart->sample_queue_count++; + queued =3D true; + } else { + osf_uart->sample_queue_drops++; + drops =3D osf_uart->sample_queue_drops; + } + spin_unlock_irqrestore(&osf_uart->sample_lock, flags); + + if (queued) { + schedule_work(&osf_uart->buffer_push_work); + return; + } + + dev_dbg_ratelimited(&osf_uart->serdev->dev, + "dropping IIO buffer sample sensor=3D%u index=3D%u drops=3D%llu\n", + event->sensor_type, event->sensor_index, + (unsigned long long)drops); +} + +static void osf_serdev_schedule_iio_registration(struct osf_serdev *osf_ua= rt) +{ + s64 scheduled_seq; + u64 sequence; + + if (!osf_core_capability_sequence(&osf_uart->osf, &sequence)) + return; + + scheduled_seq =3D atomic64_read(&osf_uart->capability_sequence_scheduled); + if (scheduled_seq =3D=3D (s64)sequence) + return; + + atomic64_set(&osf_uart->capability_sequence_scheduled, (s64)sequence); + schedule_work(&osf_uart->iio_register_work); +} + +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; + osf_serdev_schedule_iio_registration(osf_uart); + + 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_device(&osf_uart->osf); + osf_stream_init(&osf_uart->stream, &osf_uart->osf); + INIT_WORK(&osf_uart->iio_register_work, osf_serdev_iio_register_work); + INIT_WORK(&osf_uart->buffer_push_work, osf_serdev_buffer_push_work); + mutex_init(&osf_uart->iio_lock); + spin_lock_init(&osf_uart->sample_lock); + atomic64_set(&osf_uart->capability_sequence_scheduled, -1); + osf_core_set_sample_callback(&osf_uart->osf, osf_serdev_sample_ready, + osf_uart); + + 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); + + dev_info(&serdev->dev, "Open Sensor Fusion UART opened at %u baud\n", + OSF_SERDEV_BAUD); + + return 0; +} + +static void osf_serdev_remove(struct serdev_device *serdev) +{ + struct osf_serdev *osf_uart =3D serdev_device_get_drvdata(serdev); + + osf_core_set_sample_callback(&osf_uart->osf, NULL, NULL); + serdev_device_close(serdev); + cancel_work_sync(&osf_uart->iio_register_work); + cancel_work_sync(&osf_uart->buffer_push_work); + osf_stream_reset(&osf_uart->stream); +} + +static const struct of_device_id osf_serdev_of_match[] =3D { + { .compatible =3D "opensensorfusion,osf-uart" }, + { } +}; +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, + }, +}; + +int osf_serdev_register_driver(void) +{ + return serdev_device_driver_register(&osf_serdev_driver); +} + +void osf_serdev_unregister_driver(void) +{ + serdev_device_driver_unregister(&osf_serdev_driver); +} diff --git a/drivers/iio/opensensorfusion/osf_serdev.h b/drivers/iio/opense= nsorfusion/osf_serdev.h new file mode 100644 index 000000000..278b088df --- /dev/null +++ b/drivers/iio/opensensorfusion/osf_serdev.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _OSF_SERDEV_H +#define _OSF_SERDEV_H + +int osf_serdev_register_driver(void); +void osf_serdev_unregister_driver(void); + +#endif diff --git a/drivers/iio/opensensorfusion/osf_stream.c b/drivers/iio/opense= nsorfusion/osf_stream.c new file mode 100644 index 000000000..e9addf70e --- /dev/null +++ b/drivers/iio/opensensorfusion/osf_stream.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#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 u16 osf_stream_get_le16(const u8 *buf) +{ + return buf[0] | buf[1] << 8; +} + +static u32 osf_stream_get_le32(const u8 *buf) +{ + return (u32)buf[0] | (u32)buf[1] << 8 | + (u32)buf[2] << 16 | (u32)buf[3] << 24; +} + +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 bool osf_stream_magic_match(const u8 *buf, size_t len) +{ + return !memcmp(buf, osf_stream_magic, len); +} + +static size_t osf_stream_resync(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_resync(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 (osf_stream_get_le16(stream->buf + 6) !=3D + OSF_FRAME_HEADER_LEN) { + stream->stats.dropped_bytes++; + osf_stream_discard(stream, 1); + if (!first_err) + first_err =3D -EPROTO; + continue; + } + + payload_len =3D osf_stream_get_le32(stream->buf + 10); + if (payload_len > OSF_STREAM_MAX_PAYLOAD_LEN) { + stream->stats.dropped_bytes++; + osf_stream_discard(stream, 1); + 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_discard(stream, 1); + if (!first_err) + first_err =3D ret; + continue; + } + + if (decoded_len !=3D frame_len) { + stream->stats.dropped_bytes++; + osf_stream_discard(stream, 1); + 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 May 25 01:14:39 2026 Received: from mail-pg1-f182.google.com (mail-pg1-f182.google.com [209.85.215.182]) (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 58D1836F901 for ; Wed, 20 May 2026 07:29:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779262163; cv=none; b=nWmc9qK+zR9Wo1JKp7W+o/kZYqxb4Z94Jckpr8gORORCDfyPxbwuOl+i9s+g7tZPRcUjaOGfiKaePwHHt4eX0UDvydx4ttHhzK45SK7JvCmTwM5kEWVp1oHMwovuOgiOX8hiAYjWW7m7rX2Sk09z4iFm34O079DgZhPqnM/2cWg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779262163; c=relaxed/simple; bh=E3m8x+5Py+DrS1yhwjO4bfslD31NqZ/mOBktiWRfdYE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=oOxK3MKkccYeWPbM1KP29vUfKm1E71WRRt2gdR+/WHnXXD/t9pWAYi5qY0gOACtA0yjT2/ZYSZbdsHvw661REw9r3UqWy3qab7+z97yfpTd9sUqbWoOakRqjbL5wicN7D+YtiOOGVQBCWqSL6kIgmtp4IU1A0H3BxJDm6gJeVcA= 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=Zwp8o4ir; arc=none smtp.client-ip=209.85.215.182 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="Zwp8o4ir" Received: by mail-pg1-f182.google.com with SMTP id 41be03b00d2f7-c70e27e2b74so1806583a12.0 for ; Wed, 20 May 2026 00:29:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779262160; x=1779866960; 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=AK8uyh5+5gdeHTCxNeKbZQznp1Z1On5Jje2vJ7+Ah9w=; b=Zwp8o4ir+C7flTamJlshf6rLgfr4jJrGTNQgXvItc/O1eOMAIhrrEUc6MJHLjxLa6M 6AoTM0rYX36gkaLtBjaS31vJybtKyxug+7ZnWEb5ioywQM/SzX3Jk6fmhC/VchsYEqhy 9QOo8nPDT3anAaCeZERTTkwTrJQ6L3PlRDUfx4QEnUONVgRp9Ykt9ZZa34FSsjcWnv8k t3Tv+zlUBLBWy6W0vYFhHmNYg6LLaxyEZG4KoAGjDvmGzE1t4p904OoG+qjgLCg1cBW8 ktDfynW5tSwlcX6nbFXLDq+mmqESwl8z7FaR0wy45r0jjlqkQcrkZicKrRKzsd2najst XHzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779262160; x=1779866960; 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=AK8uyh5+5gdeHTCxNeKbZQznp1Z1On5Jje2vJ7+Ah9w=; b=L9AeAPcT0YzYKjbot44RU0rtROaQaVLpdjD21EPHxMXkeVls4pMjPD/FYnW71v2UII 7WU1Z5GoFEOuoAzwyRlKD/7hYDdFY88vCU9N+n1FwDPGJ1yBA3QjI1JwQ+j8MJA/SKAH vEivB8iI7TUvoJixv6wIeyXzaMhz5wtwVhej0mdn5i/ePYFNoe+eAXLIwzBCRtEQYtY6 IePROPWIVhl5FP1vvi/7AGv3MJ0ha4ITmH/gjmSS+2I5itI9UMqC8+cpwKnzisD5XXyo fDCskbTDCTLIyvR+WR7jhnvjxI95Ryn0u0egliDWJsIp721SQatPm3HskzPZ1KnifokB ZtKg== X-Forwarded-Encrypted: i=1; AFNElJ+ZQaX70Qh0PwhTCWmlDY7kuUk0C2rWKcyS5eKWPGEUEa3CVWs3qsRUMzg90l2cNznemtnRZzfy/tzEC9o=@vger.kernel.org X-Gm-Message-State: AOJu0YypRFoGEPTO+piM0LTZqQ5AFtPOHDRxCuCvzaDunt8sgA9SnC6h 1pMEw+pi8KqQz1Vjurdk1WqXscxcnW2Hkc4QB4rJclIUwnc1t+Ce84p2 X-Gm-Gg: Acq92OGSWX18/OlS9/vaTucfz+r4USg+qIfINLn6LACYvIjKd9VcgpGQQtTLKlI3uCp T4ViQEOjvzq01YpTTBAILudJX41TjMmkHRxe9KdB16zR+jg+Tz3A06Kxx6r+OxpaQ7d84PPSbIC jKXR5ZJH+H7fsDNly0m6OfoCHU7Se0arOvupPGLdwYB19A5azc2+4Ub3sHn6t5g8yTJbmYWK2jT FYUFusgYr+4fsHC19CzlNWB82ymCIrHF5kkX6SDRxour8a3Qj70AjI9dcswAwx0Vt+k7dNN0DUc 9xVjZ5RnyX2KX0hVHx7AwFrPOfPDDzxIHxcVjO8yhZhKxCX+tFRiGxMo7RTLQHQa6g+G5DI0rTe ReUixcAAPiRj98Lwk1l49kxHtA3FPjRstIvWkkPK5fN4/UcoslbC/GiG5x/63emUKcFtoeZmGuU qTIOD+7ybWP7Wsap/hNT/sPXsPseZ5G//t7iIE+Km+O0UKadlmO5SsJcN8cpsG1n/iTkjOrO5vc kW6nRJgT8h8Ok9B7NmWIm5DptW1ciNTnA3H6+K1sD3vPWXs X-Received: by 2002:a17:902:ce85:b0:2bd:ef15:9fce with SMTP id d9443c01a7336-2bdef15a1e4mr111156975ad.20.1779262159642; Wed, 20 May 2026 00:29:19 -0700 (PDT) Received: from DESKTOP-G3E0OSP.localdomain ([112.172.255.242]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2bd5c05f27csm206258595ad.25.2026.05.20.00.29.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 May 2026 00:29:19 -0700 (PDT) From: Jinseob Kim To: Jonathan Cameron , linux-iio@vger.kernel.org Cc: David Lechner , =?UTF-8?q?Nuno=20S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH RFC 3/3] MAINTAINERS: add Open Sensor Fusion IIO driver entry Date: Wed, 20 May 2026 16:28:43 +0900 Message-ID: <20260520072843.3593-4-kimjinseob88@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260520072843.3593-1-kimjinseob88@gmail.com> References: <20260520072843.3593-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 a maintainer entry for the Open Sensor Fusion IIO driver and binding. The driver path reflects the current RFC layout. It may need to be adjusted after review if the driver moves under a different IIO directory. Signed-off-by: Jinseob Kim --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index c2c6d7927..001ba4ea6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20011,6 +20011,13 @@ F: Documentation/devicetree/ F: arch/*/boot/dts/ F: include/dt-bindings/ =20 +OPEN SENSOR FUSION IIO DRIVER +M: Jinseob Kim +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/imu/opensensorfusion,osf-uart.yaml +F: drivers/iio/opensensorfusion/ + OPENCOMPUTE PTP CLOCK DRIVER M: Vadim Fedorenko L: netdev@vger.kernel.org --=20 2.43.0