From nobody Sun May 24 17:49:06 2026 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) (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 4CFE125B085 for ; Sun, 24 May 2026 13:28:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779629322; cv=none; b=IdHQnZ85cO1cE6z1DZwBaKmrUumBo68XtCnHujbWtIpT1RvYPhLJ4K6L1maKLSl5Kl+3nU28fgUXD3mp61D3sMmqAf3JvEhBvkzw8LM7qoD39QJvBvHUux/3jwsJjDZrPLldkv3qOvnej8g4UsY7XKzylcQQp0f2EMHTWzbMmj4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779629322; c=relaxed/simple; bh=HFe/kb/1zdkSbFNfPyXVpPzV9cgYhHCB1iyaej9AD00=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dcu5xNNk5QMRySOuGKk/Y7k/mme+1UVds2v7omDoxtXXTF7Dt07ToE/GUcJ73pgFLzLKgOXClS2qqqkjHNa3FJR9yG8vWX7PqkBIzIMg8cKs+hHNKFnD5fNYxEGHEgLET8nfM5XcLJBlUn5dFGlwdvzyOhbRwaBqsjy4+hQjUjs= 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=DKWdwrFp; arc=none smtp.client-ip=209.85.214.178 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="DKWdwrFp" Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-2ba17c8cfacso92371485ad.2 for ; Sun, 24 May 2026 06:28:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779629321; x=1780234121; 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=KH21OWAYiQJp2mcLx53CxgVbtFXKc66MkLprzzmSzGs=; b=DKWdwrFpCHxids78RYvhuW2OTf+Aojlw03qChije8pMlCVfJya2QjoncrTRmJCK8lY GE55qpCPw4knmEUIyLGBfBHLI+sTCwMaPHHx3kWNI1AVb9Scx5OESjO1K7HrXzdGOA06 8gJwTbQBdph2AWsqxxjklOfErw4EveFlijhFm7pib/2AGjTTVT8Kc6k+3EOEHlHiQo7U fiOg1Wyodlz7byrzAT1Ci/quf7j4+B42wdaXwE8U9SsODJXNGd0yfLkgTfWv/mOUjZcA F9Txlicano7VkZwVWFpgODLRpsufxwMceERPlCixiBF4kJUxqiy2tKQVGbZ7s04aHGpV 5w7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779629321; x=1780234121; 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=KH21OWAYiQJp2mcLx53CxgVbtFXKc66MkLprzzmSzGs=; b=T15Jz4LQI0QlosQOn1+BMmQOIKr84FQY5YS/yPWGcFGDwwevAfR+292iH2gpanN9Nx 1ErNAdJrxP8a+v6EDmlUZv5N4cC0veNcA4Cq5Bq4mTqZ9t97N5P6OXoSIQz4t3SLQYou Y/tmzmjoKtd9lxJ6zMe4L/WdRzJqARfYDuCPmTo+Oaq/W6xKhUEd6o8tPeZbmJmSvqjh 4Ue08LCYvAZAuN8h8lkMy13fmjnOKg7CdHmPBlLKggR1s7PWqO1BDhR1EZfbdJ461Lyu EGuctSsi5ycXwcRwvgp3Xx6ebzEnYU95IS/5zEn7fZZUJ0FdsexCrp2aJ9VYwnp36xiQ 117w== X-Forwarded-Encrypted: i=1; AFNElJ+o1XCw050B+n8CdR30EDGNpmaGi4jZcCDLzHRjSyHUQWwpRQzIXe6xlfzq4i0Ig5IN7kFm+huIKRqbxRw=@vger.kernel.org X-Gm-Message-State: AOJu0YwWvCMHErBaAAsClfe2u0XCWM3yN3UE7rCxgPB4zXimE9by+D1J sTDLA/yE6nT0S/8fEPG/P6hteTl9KD8sW0c3VnmefRrCqnvZLamx813W X-Gm-Gg: Acq92OHx7G6tjWp5Vc1gwRJjnUQ/VDNkmoUUXBBVlKwftzGC2Bbi0JbU/V8B02df+Qk DvmcFavdy0iqoCtlLyKMwmnJ4UiRHMJ35p6TYOG/z7y5CnfqPPgDMe5qz1m8ktLcOCE7SOcmbtC YBfz/EJrSdeJDIhmWYjkWhrMPmRnvVBKB4Vb0/0h8VWFVcH7B6P24XS2CGZKQu7NpJKLfQ+l1EI vpJH+rY7Pb1CrW4tiQ1WlfGDsnWLxhPIQQLIR5LL+PgiDF4R7vTP6C5JOcvUSPpqDIUiKLSrAhC ThHXy1v7OHRYUb5il8A60f9tpN+Xt2W5dDbLDQ8tzGgI73AT0dkd+GwAtzwZsXZ3VStqlhhunrS CDSifkevhbZGzfix/S66Cgdh38D0Qh25HPgciZfdLB02WA3IYe27nn2XazVrBJHx/bxZhiN9ZCl ugfE52agfwmkfQ+CiRHjrMorDBOERcFxP2t1Qx7mO3xHRbRASUw+uPKiU/YOyAHw== X-Received: by 2002:a17:903:41c9:b0:2bd:5ab:af95 with SMTP id d9443c01a7336-2beb023584amr113265995ad.0.1779629320699; Sun, 24 May 2026 06:28:40 -0700 (PDT) Received: from 192.168.1.3 ([2001:448a:2003:4286:c553:4edf:bb9:bbd2]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2beb56f4343sm91197175ad.36.2026.05.24.06.28.38 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 24 May 2026 06:28:40 -0700 (PDT) From: Muchamad Coirul Anwar To: Jonathan Cameron Cc: linux-iio@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Miguel Ojeda , Igor Korotin , Brandon Saint-John , Muchamad Coirul Anwar Subject: [RFC PATCH v3 1/4] i2c: rust: implement kernel::io::Io trait for I2cClient Date: Sun, 24 May 2026 20:28:20 +0700 Message-ID: <20260524132824.54918-2-muchamadcoirulanwar@gmail.com> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20260524132824.54918-1-muchamadcoirulanwar@gmail.com> References: <20260524132824.54918-1-muchamadcoirulanwar@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" Implement the Io trait for I2cClient per the agreed-upon direction for I2C register access abstractions. This provides try_read8() and try_read16() with automatic offset validation via io_addr(). I2cClient now implements IoCapable and IoCapable with maxsize=3D256 (SMBus command byte range 0x00-0xFF). Link: https://lore.kernel.org/rust-for-linux/20260131-i2c-adapter-v1-4-5a43= 6e34cd1a@gmail.com/ Signed-off-by: Muchamad Coirul Anwar --- rust/kernel/i2c.rs | 76 +++++++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 6eaea1158fda..cdbef6cfa344 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -14,6 +14,7 @@ devres::Devres, driver, error::*, + io::{Io, IoCapable}, of, prelude::*, sync::aref::{ @@ -477,30 +478,6 @@ impl I2cClient { fn as_raw(&self) -> *mut bindings::i2c_client { self.0.get() } - - /// Reads a single byte from a register via SMBus. - pub fn smbus_read_byte_data(&self, reg: u8) -> Result { - // SAFETY: `self.as_raw()` is a valid pointer to a `struct i2c_cli= ent` - // by the type invariant of `I2cClient`. - let ret =3D unsafe { bindings::i2c_smbus_read_byte_data(self.as_ra= w(), reg) }; - if ret < 0 { - Err(Error::from_errno(ret)) - } else { - Ok(ret as u8) - } - } - - /// Reads a 16-bit word from a register via SMBus. - pub fn smbus_read_word_data(&self, reg: u8) -> Result { - // SAFETY: `self.as_raw()` is a valid pointer to a `struct i2c_cli= ent` - // by the type invariant of `I2cClient`. - let ret =3D unsafe { bindings::i2c_smbus_read_word_data(self.as_ra= w(), reg) }; - if ret < 0 { - Err(Error::from_errno(ret)) - } else { - Ok(ret as u16) - } - } } =20 // SAFETY: `I2cClient` is a transparent wrapper of `struct i2c_client`. @@ -614,5 +591,54 @@ fn drop(&mut self) { unsafe impl Send for Registration {} =20 // SAFETY: `Registration` offers no interior mutability (no mutation throu= gh &self -// and no mutable access is exposed) +// and no mutable access is exposed). unsafe impl Sync for Registration {} + +impl IoCapable for I2cClient {} +impl IoCapable for I2cClient {} + +impl Io for I2cClient { + #[inline] + fn addr(&self) -> usize { + 0 + } + + #[inline] + fn maxsize(&self) -> usize { + 256 + } + + #[inline] + fn try_read8(&self, offset: usize) -> Result + where + Self: IoCapable, + { + let reg =3D self.io_addr::(offset)? as u8; + // SAFETY: `self.as_raw()` returns a valid pointer to a `struct i2= c_client` + // as guaranteed by the type invariant of `I2cClient`. `reg` is bo= unds-checked + // by `io_addr()` above (offset + 1 <=3D 256). + let ret =3D unsafe { bindings::i2c_smbus_read_byte_data(self.as_ra= w(), reg) }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + Ok(ret as u8) + } + } + + #[inline] + fn try_read16(&self, offset: usize) -> Result + where + Self: IoCapable, + { + let reg =3D self.io_addr::(offset)? as u8; + // SAFETY: `self.as_raw()` returns a valid pointer to a `struct i2= c_client` + // as guaranteed by the type invariant of `I2cClient`. `reg` is bo= unds-checked + // by `io_addr()` above (offset + 2 <=3D 256). + let ret =3D unsafe { bindings::i2c_smbus_read_word_data(self.as_ra= w(), reg) }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + Ok(ret as u16) + } + } +} --=20 2.50.0 From nobody Sun May 24 17:49:06 2026 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (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 CDFF7393DCA for ; Sun, 24 May 2026 13:28:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779629327; cv=none; b=HBeNWSelGp9jDajtpCVgnhS2+VVqv4dmvTnAOoPURBe5ElTq0Tl+LKdkhhvsLW4zSQ68pk2zRLoVW6WzVzL2wGvkAek02jPOn4JZ41yy0rIpHT0i4VW5/VMuepqMGexsfWFTSCuqWP4RJHDk6fWIx7JJpChG9AJnou5ehIH/9n8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779629327; c=relaxed/simple; bh=rxRCYggfaZEdwDkyjmmeZlht5i9m1vYJe2tzRUy9TFo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=S+oMO+J/wiEUNpdNII8BmuM5EkwjH7thzbYbbVHQXIJ+ccJW+PuE39uJ8MvIGYH8pE+SPUY0NMTx4Q4VW6l+couuDUpdekjpdKgk4wP9ijFuLewJXyLMyAKWxfgkxZ26dAkU0FUWkDSssjwPt4AA/T7QcAV23VpNna35a2TjMZE= 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=DGDCA/g0; arc=none smtp.client-ip=209.85.214.176 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="DGDCA/g0" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2bea7176c72so25976485ad.0 for ; Sun, 24 May 2026 06:28:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779629325; x=1780234125; 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=TWMN4P/wUAQ6ZB9Etc8PQC9btGokL5vUI0KZKUAzrPg=; b=DGDCA/g0Y0R0Dx7181vZWZHrZFesnsuhk6CuHiJL88zIIkrxb0LmeAAU1i6mzfVA0+ a5rsHPGnFD/YAxEEUl5ph73Xe9Ymgkj/xF7fyJwFBbctAWZd9ao15iGA2Td2MepUVc/u 9aAdfo4lqQhNuxRuJwoqra9TM1nXeTlKipb7QUz78383RSnvIFZKPPeOaizqLbH7lFhU qofrzDDpscqJ5ZW0Fbs7Ux7kDCIsaKKHci/QcoV/4JVJ2DM/sOrtP/kAXBn8/z4mDhLx PryBOckqhzmFEWcQBPjGhS0TeTUtrOnj+mEHxbbL0E8EFZq0z74Ic8hgTRl1mP9Aft+H G0kg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779629325; x=1780234125; 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=TWMN4P/wUAQ6ZB9Etc8PQC9btGokL5vUI0KZKUAzrPg=; b=e5dUA+rsrT4QKHyhmBsbMJnm6q06AZKUvIEZ8LMT1qww1Vo2TTIls3ADjzY1JLrN/j qCiHjMu93+GT12LlYLFS9qdGkpWdeny0FEDOpA1MMz1IfM4FIZzHkgjFWn9t4klkt4eU P07WCsjMa10JDQUhR3pm0uSf4HrRbWkMhIhZXuaFJGHEn/QkIB52AhJk6CzV9f3/HIlC xLooJZA5fWcs+uo51lVIIG32YLRt2VOsEcd+yLql9Z95/6Dop8j4eML81pNrauixunZt BxoO8JClzrvfUwXPCPu+wltgDUKTki9FkFbwOkGzy7fcp2E/wUC4sScNhkqmXnBbRS5A Zlrg== X-Forwarded-Encrypted: i=1; AFNElJ/7fdv1cMrORjXFc71xdh0OzxizK9qMksVJbEq3ksQTMY4AY/LzGrcJvGKKByk0dOBMCXbrS481DR+99Ec=@vger.kernel.org X-Gm-Message-State: AOJu0YwH1b5Ym/oKl+UbDLaJGmyH0sTpW/o5W/sN6tN858OhOQvo6ZHG /+UIKXnDQHtBObjeIgVXmcBFuq7mtvYByt1XvU6Ul+Y5J0fqZ4hjWEqV X-Gm-Gg: Acq92OGv6WZnaamTW7Dwzn6Vua/u8N2JzUjykE2GcQ7NHpyZLjquMdvF0vSvrYGJRkM 3Q0Zb0qpL1ItApP9blAgQUjlV2Sr3Uqujd6diXuarfJ19y/Erj5/zuB0HRKwR5XiTSxyolCbeyU DtgnfPHSYZIuABlnesZYQwMi9EHl/FY82wR2UENZy3L85TcR+yUiRpuVJbz8s8HdsQ6IKXMAMtH 1r/pC5tU+ZQG0dRCBkrN38rD6HdTsZviN8aXsndNhj37A1XRND90qcurf4q9sT8HTaF6IiltImu 5Ni3CDJxaIAwKl5VsUi0K2GbVKO59kYDCBv28kiLQClRjs2AVzgUyQPLPvqO/FrnDNKUV4DjICR KlT6ctwxRk9h2/lRXSUtdhiS3h7UXfpS7WKpsDG/mjZ4tgFRcPmbLNdrk0F/tWVwmy2K3eo4rXd NTAjmBwjeB/DhmDgxemtPnJ9BomfDy2FjnQpjXRzc1SDCWz6tsTO8AMuM4qk1beA== X-Received: by 2002:a17:902:ebc2:b0:2bd:83e1:5613 with SMTP id d9443c01a7336-2beb067468dmr111156065ad.20.1779629325058; Sun, 24 May 2026 06:28:45 -0700 (PDT) Received: from 192.168.1.3 ([2001:448a:2003:4286:c553:4edf:bb9:bbd2]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2beb56f4343sm91197175ad.36.2026.05.24.06.28.42 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 24 May 2026 06:28:44 -0700 (PDT) From: Muchamad Coirul Anwar To: Jonathan Cameron Cc: linux-iio@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Miguel Ojeda , Igor Korotin , Brandon Saint-John , Muchamad Coirul Anwar Subject: [RFC PATCH v3 2/4] rust: add minimal IIO subsystem abstractions Date: Sun, 24 May 2026 20:28:21 +0700 Message-ID: <20260524132824.54918-3-muchamadcoirulanwar@gmail.com> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20260524132824.54918-1-muchamadcoirulanwar@gmail.com> References: <20260524132824.54918-1-muchamadcoirulanwar@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add safe Rust wrappers for the Linux IIO subsystem. Provides: - Device with typestate pattern (Unregistered/Registered) - IioDriver trait with read_raw callback - DirectModeGuard RAII for iio_device_claim_direct - IioVal enum with NonZeroI32 for division-by-zero prevention - PinnedDrop cleanup: unregister -> drop_in_place -> iio_device_free Signed-off-by: Muchamad Coirul Anwar --- rust/helpers/helpers.c | 1 + rust/helpers/iio.c | 24 +++ rust/kernel/iio.rs | 341 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 4 files changed, 368 insertions(+) create mode 100644 rust/helpers/iio.c create mode 100644 rust/kernel/iio.rs diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index a3c42e51f00a..c69a9a93367d 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -33,6 +33,7 @@ #include "irq.c" #include "fs.c" #include "io.c" +#include "iio.c" #include "jump_label.c" #include "kunit.c" #include "maple_tree.c" diff --git a/rust/helpers/iio.c b/rust/helpers/iio.c new file mode 100644 index 000000000000..a5402440583c --- /dev/null +++ b/rust/helpers/iio.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +/* + * iio_device_claim_direct() is a static inline in iio.h. + * This helper exports it as a callable symbol for Rust. + */ +__rust_helper bool +rust_helper_iio_device_claim_direct(struct iio_dev *indio_dev) +{ + return iio_device_claim_direct(indio_dev); +} + +/* + * iio_device_release_direct() is a macro expanding to __iio_dev_mode_unlo= ck(). + * This helper exports it as a callable symbol for Rust. + */ +__rust_helper void +rust_helper_iio_device_release_direct(struct iio_dev *indio_dev) +{ + iio_device_release_direct(indio_dev); +} diff --git a/rust/kernel/iio.rs b/rust/kernel/iio.rs new file mode 100644 index 000000000000..bbd34f1c819a --- /dev/null +++ b/rust/kernel/iio.rs @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2026 Muchamad Coirul Anwar +//! IIO subsystem abstractions. +//! +//! Minimal safe Rust wrappers for the Linux IIO (Industrial I/O) subsyste= m. +//! Provides [`Device`] for allocating and registering an IIO device, and = the +//! [`IioDriver`] trait for implementing `read_raw` callbacks in safe Rust. + +use crate::{ + bindings::{ + __iio_device_register, + iio_chan_spec, + iio_dev, + iio_device_alloc, + iio_device_free, + iio_device_unregister, + iio_info, + INDIO_DIRECT_MODE, // + }, + device, + error::{ + code::*, + to_result, + Result, // + }, + prelude::*, + ThisModule, // +}; +use core::{ + ffi::c_int, + marker::PhantomData, + mem::{ + forget, + size_of, + zeroed, // + }, + num::NonZeroI32, + pin::Pin, + ptr::drop_in_place, // +}; + +use pin_init::{pin_data, pinned_drop}; + +/// IIO value type: single integer (`IIO_VAL_INT`). +pub const IIO_VAL_INT: c_int =3D crate::bindings::IIO_VAL_INT as c_int; +/// IIO value type: integer plus micro part (`IIO_VAL_INT_PLUS_MICRO`). +pub const IIO_VAL_INT_PLUS_MICRO: c_int =3D crate::bindings::IIO_VAL_INT_P= LUS_MICRO as c_int; +/// IIO value type: integer plus nano part (`IIO_VAL_INT_PLUS_NANO`). +pub const IIO_VAL_INT_PLUS_NANO: c_int =3D crate::bindings::IIO_VAL_INT_PL= US_NANO as c_int; +/// IIO value type: fractional (`IIO_VAL_FRACTIONAL`). +pub const IIO_VAL_FRACTIONAL: c_int =3D crate::bindings::IIO_VAL_FRACTIONA= L as c_int; + +/// Represents the return value of a `read_raw` operation. +/// +/// Each variant corresponds to an `IIO_VAL_*` constant and tells the +/// IIO core how to format `val` and `val2` for userspace. +pub enum IioVal { + /// A single integer value. + Int(i32), + /// A fractional value represented as `val / val2`. + /// The denominator is `NonZeroI32` to prevent division-by-zero in + /// `iio_format_value()`. + Fractional(i32, NonZeroI32), + /// An integer plus a micro (10=E2=81=BB=E2=81=B6) fractional part: `v= al.val2`. + IntPlusMicro(i32, i32), + /// An integer plus a nano (10=E2=81=BB=E2=81=B9) fractional part: `va= l.val2`. + IntPlusNano(i32, i32), +} + +/// Trait to be implemented by IIO driver private data. +/// +/// Implementors supply the `read_raw` callback invoked by the IIO core +/// when userspace reads a channel attribute (e.g. `in_angl_raw`). +/// +/// The `Send + Sync` bounds ensure the compiler rejects driver types with +/// thread-unsafe interior mutability (e.g. `Cell`), since the IIO core may +/// invoke `read_raw` concurrently from multiple sysfs readers. +pub trait IioDriver: Send + Sync { + /// Called by the IIO core when userspace reads a channel attribute. + /// + /// `chan` is the channel being read; `mask` selects the attribute + /// (e.g. `IIO_CHAN_INFO_RAW`, `IIO_CHAN_INFO_SCALE`). + fn read_raw(&self, chan: *const iio_chan_spec, mask: isize) -> Result<= IioVal>; + + /// Returns the channel specifications for this driver. + /// + /// The default implementation returns an empty slice. + fn channels(&self) -> &[iio_chan_spec] { + &[] + } +} + +// -----------------------------------------------------------------------= ---- +// DirectModeGuard =E2=80=94 RAII claim on IIO direct mode +// -----------------------------------------------------------------------= ---- + +/// RAII guard that claims IIO direct mode on construction and releases it= on drop. +/// +/// This prevents concurrent access conflicts between sysfs reads and +/// buffer/trigger operations. +struct DirectModeGuard(*mut iio_dev); + +impl DirectModeGuard { + fn new(indio_dev: *mut iio_dev) -> Result { + // SAFETY: `indio_dev` is a valid pointer to a fully initialized `= iio_dev` + // allocated by `iio_device_alloc`. `iio_device_claim_direct` retu= rns `true` + // if the device is in direct mode (success), `false` if buffer mo= de is active. + let claimed =3D unsafe { crate::bindings::iio_device_claim_direct(= indio_dev) }; + if claimed { + Ok(Self(indio_dev)) + } else { + Err(EBUSY) + } + } +} + +impl Drop for DirectModeGuard { + fn drop(&mut self) { + // SAFETY: `self.0` was successfully claimed in `new()`. Releasing= it + // unlocks the IIO mode lock acquired during claim. + unsafe { + crate::bindings::iio_device_release_direct(self.0); + } + } +} + +// -----------------------------------------------------------------------= ---- +// read_raw_callback =E2=80=94 C-to-Rust FFI trampoline +// -----------------------------------------------------------------------= ---- + +/// C-compatible trampoline for the `iio_info.read_raw` callback. +/// +/// # Safety +/// +/// This function is only called by the IIO core with valid pointers: +/// - `indio_dev` is a valid `iio_dev` allocated by `iio_device_alloc`. +/// - `chan` points to a valid channel spec from the device's channel arra= y. +/// - `val` and `val2` are valid pointers for writing the result. +unsafe extern "C" fn read_raw_callback( + indio_dev: *mut iio_dev, + chan: *const iio_chan_spec, + val: *mut c_int, + val2: *mut c_int, + mask: isize, +) -> c_int { + // SAFETY: `indio_dev` is valid and was allocated with space for `T` i= n its + // private data area. The `priv_` field was initialized in `Device::bu= ild_device()`. + let priv_ptr =3D unsafe { (*indio_dev).priv_ as *mut T }; + // SAFETY: `priv_ptr` points to a valid, initialized instance of `T` t= hat + // lives as long as the `iio_dev` allocation. + let driver =3D unsafe { &*priv_ptr }; + + // Claim direct mode via RAII guard. If the device is in buffer mode, + // return -EBUSY to userspace immediately. + let _guard =3D match DirectModeGuard::new(indio_dev) { + Ok(g) =3D> g, + Err(e) =3D> return e.to_errno(), + }; + + match driver.read_raw(chan, mask) { + Ok(IioVal::Int(v)) =3D> { + // SAFETY: `val` is a valid pointer provided by the IIO core. + unsafe { + *val =3D v; + } + IIO_VAL_INT + } + Ok(IioVal::Fractional(v, v2)) =3D> { + // SAFETY: `val` and `val2` are valid pointers provided by the= IIO core. + unsafe { + *val =3D v; + *val2 =3D v2.get(); + } + IIO_VAL_FRACTIONAL + } + Ok(IioVal::IntPlusMicro(v, v2)) =3D> { + // SAFETY: `val` and `val2` are valid pointers provided by the= IIO core. + unsafe { + *val =3D v; + *val2 =3D v2; + } + IIO_VAL_INT_PLUS_MICRO + } + Ok(IioVal::IntPlusNano(v, v2)) =3D> { + // SAFETY: `val` and `val2` are valid pointers provided by the= IIO core. + unsafe { + *val =3D v; + *val2 =3D v2; + } + IIO_VAL_INT_PLUS_NANO + } + Err(e) =3D> e.to_errno(), + } +} + +// -----------------------------------------------------------------------= ---- +// Device =E2=80=94 IIO device wrapper with typestate +// -----------------------------------------------------------------------= ---- + +/// Marker type for an unregistered IIO device. +pub struct Unregistered; +/// Marker type for a registered IIO device. +pub struct Registered; + +/// A wrapped IIO device managing its C `struct iio_dev` lifetime. +/// +/// Uses `iio_device_alloc` for allocation (no devres involvement) and +/// manual cleanup via `Drop`: `iio_device_unregister` =E2=86=92 `drop_in_= place` +/// for `T` =E2=86=92 `iio_device_free`. +#[pin_data(PinnedDrop)] +pub struct Device { + indio_dev: *mut iio_dev, + registered: bool, + _p: PhantomData<(T, State)>, +} + +// SAFETY: `Device` only contains a raw pointer to a kernel-managed `iio_d= ev`. +// The IIO core serializes access to the device, and `T` is required to be= `Send`. +unsafe impl Send for Device {} +// SAFETY: All `&self` access to the `iio_dev` is read-only or goes throug= h the +// IIO core which provides its own synchronization. `T` is required to be = `Sync`. +unsafe impl Sync for Device {} + +#[pinned_drop] +impl PinnedDrop for Device { + fn drop(self: Pin<&mut Self>) { + if self.registered { + // SAFETY: The device was successfully registered via + // `__iio_device_register`. Unregistering drains all pending + // callbacks, ensuring no `read_raw` is in flight after this. + unsafe { iio_device_unregister(self.indio_dev) }; + } + + // SAFETY: `priv_` was fully initialized in `build_device` via + // `init.__pinned_init(priv_ptr)`. `drop_in_place` runs `T`'s dest= ructor + // (including any pinned fields like Mutex). After that, `iio_devi= ce_free` + // calls `put_device` which decrements the kref =E2=80=94 the unde= rlying `iio_dev` + // memory is only freed when kref reaches 0. + unsafe { + let priv_ptr =3D (*self.indio_dev).priv_ as *mut T; + drop_in_place(priv_ptr); + iio_device_free(self.indio_dev); + } + } +} + +impl Device { + // SAFETY: The remaining fields of `iio_info` are pointers and function + // pointers. Zeroed values are NULL, and the IIO core checks for NULL + // before invoking callbacks or dereferencing attribute group pointers. + const VTABLE: iio_info =3D iio_info { + read_raw: Some(read_raw_callback::), + ..unsafe { zeroed() } + }; + + /// Allocates a new IIO device with the given driver data. + /// + /// Uses `iio_device_alloc` (not `devm_*`) so that the Rust `Drop` + /// implementation has full control over the cleanup sequence. + /// The device is not yet registered; call [`register`](Self::register) + /// to make it visible to userspace. + pub fn build_device( + dev: &device::Device, + name: &'static CStr, + init: impl PinInit, + ) -> Result + where + Error: From, + { + let priv_size =3D size_of::(); + + // SAFETY: `dev.as_raw()` returns a valid `struct device` pointer. + // `iio_device_alloc` allocates an `iio_dev` with `sizeof(T)` byte= s of + // private data. Returns NULL on failure. + let indio_dev =3D unsafe { iio_device_alloc(dev.as_raw(), priv_siz= e as i32) }; + if indio_dev.is_null() { + return Err(ENOMEM); + } + + // SAFETY: `indio_dev` is valid and freshly allocated. `priv_` poi= nts + // to uninitialized memory of `sizeof(T)` bytes. `PinInit::__pinne= d_init` + // initializes `priv_` in place without reading the previous + // (uninitialized) contents. + let priv_ptr =3D unsafe { (*indio_dev).priv_ as *mut T }; + let init_result =3D unsafe { init.__pinned_init(priv_ptr) }; + if let Err(e) =3D init_result { + // SAFETY: `pin_init` guarantees partial-init rollback interna= lly. + // `priv_` memory was not fully initialized, so we only free t= he + // container without running `T`'s destructor. + unsafe { iio_device_free(indio_dev) }; + return Err(Error::from(e)); + } + + // SAFETY: `priv_ptr` is now fully initialized. We set up the IIO + // device fields: + // - `name` is a `'static` C string that outlives the device. + // - `VTABLE` is a `'static` const and outlives the device. + // - `channels()` returns a reference to data owned by `T` in `pri= v_`, + // which remains at a fixed address because `priv_` is heap-allo= cated + // inside `iio_dev`. + unsafe { + (*indio_dev).name =3D name.as_char_ptr(); + (*indio_dev).info =3D &Self::VTABLE; + + let chans =3D (*priv_ptr).channels(); + (*indio_dev).channels =3D chans.as_ptr(); + (*indio_dev).num_channels =3D chans.len() as _; + (*indio_dev).modes =3D INDIO_DIRECT_MODE as i32; + } + + Ok(Self { + indio_dev, + registered: false, + _p: PhantomData, + }) + } + + /// Registers the IIO device, making it visible to userspace via sysfs. + /// + /// On success, channel attributes like `in_angl_raw` become readable. + /// On failure the device stays unregistered and will be freed when + /// this [`Device`] is dropped. + #[inline] + pub fn register(self, module: &'static ThisModule) -> Result> { + // SAFETY: `self.indio_dev` is a valid, fully initialized `iio_dev= `. + // `module.as_ptr()` provides the module owner for proper refcount= ing. + let ret =3D unsafe { __iio_device_register(self.indio_dev, module.= as_ptr()) }; + to_result(ret)?; + + let registered_dev =3D Device { + indio_dev: self.indio_dev, + registered: true, + _p: PhantomData, + }; + + // Prevent `self`'s Drop from running =E2=80=94 ownership of `indi= o_dev` + // has been transferred to `registered_dev`. + forget(self); + Ok(registered_dev) + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 138d846f798d..ec6eb4dbdb6a 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -99,6 +99,8 @@ #[cfg(CONFIG_I2C =3D "y")] pub mod i2c; pub mod id_pool; +#[cfg(CONFIG_IIO)] +pub mod iio; #[doc(hidden)] pub mod impl_flags; pub mod init; --=20 2.50.0 From nobody Sun May 24 17:49:06 2026 Received: from mail-pl1-f173.google.com (mail-pl1-f173.google.com [209.85.214.173]) (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 3691033D6E6 for ; Sun, 24 May 2026 13:28:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779629335; cv=none; b=pS+b3+BB9TQMMzz6pQiN7+2EQ8ynjjgfLQSaNlxbsHJ++MJN0nmRKGxpq7+BuYK8mZ6kFf6Pw1OZFJazp4oVTUYGbiJkMwHsdTJEZpa2TjiY6oCGP0z5Lrm16vJ6stWsVTHUnIVML9o9JcJ7pfzkjOFY++FPNq02m+jmDpXcZiA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779629335; c=relaxed/simple; bh=sCPRa+Pg6iqOf8JJkDf2nkLCIFCgH9NKdJKw4TM4vwY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=M1Win122ZwVderkMxNxKAQ++kWsdxqUp1Er3oGDjB0rlMOBQRwBk5e6fGuUsafCk7re2kWitFT0t2ogIg2fiAynTjzADsMf9GfJPls3LSxgScavOGEDUXsP52zd4SyMFNT/W47wCkaMLketB8z/DhucRiPbdRaOHP3FyWDrjybU= 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=BI5x5GtG; arc=none smtp.client-ip=209.85.214.173 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="BI5x5GtG" Received: by mail-pl1-f173.google.com with SMTP id d9443c01a7336-2ba17c8cfacso92372595ad.2 for ; Sun, 24 May 2026 06:28:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779629333; x=1780234133; 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=uVuOrsHgj0yEZq2/rleCfYS+yYI4maV483S6Tokx11g=; b=BI5x5GtGots6fwUKcpewsEcPHsIHPyQgMWyro8awez8COSreITBfdqaMB0hp2VPt8S yeIZPFyP9Vk918NudFhE7ZpSYF/gDhhkFn8wpIX6OYTL+bOVbQKxo4toRRv+SRzJhPBB 1QdYX5KJdX8bWHm7IcEFYjg2mT8zqH8VJwi/uAC8aKhZBWfGvdjWskkMXwmlTSVsFpAO sgriWZaAPVc1aemvcHyQRMVWOgILCvNCK+/9eeKJeAyCnekjSaSjdMAIQFMjF90qlXZX r5saWV5hmqGOyeu30KAmPRTP4tFMnVCpFLdwer/lQY6IGHqndBke4yuD0Q0maePFAb4N b4qg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779629333; x=1780234133; 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=uVuOrsHgj0yEZq2/rleCfYS+yYI4maV483S6Tokx11g=; b=FvibpuGUgUbhs0iAZW1OahvksJjxpQGgZfteLSr4q31Xik8ONzQcsFkTtBSdOvD5Ib HUPshuiHm5PO272Pr2j6pMRChjo8tK/dn/8SZ3hcAjc9VpA1g/rRiWUYQpnaBXO3YUV/ 7xXKt9qz0nEOL1S67wqIZPQbpzaWTg2InPlA7knFVkWS3YoHg92zwH11umQLxGKd9rHe 7wwKUcEBUFUfAzs9k9ajhENRVTtkKAlUZvsDpFYEtzTTfcw2VnugfmIUbSTQUvRK8HG/ jt0aadKxAmI4ssQV87JD0tbQXCqCY2zBE3ajJswuZO/J1rfy91bigpttm/pI+GC1RqVp i4mA== X-Forwarded-Encrypted: i=1; AFNElJ8AfQ240uUwErO0RtzHzG2lfOmWeUiSKSK7Xz8CJB0ZSkeH030nTTRoGEsJezSgl7gKHZwKODucbBWKP8w=@vger.kernel.org X-Gm-Message-State: AOJu0Yyv1s/cAAoSSB91OcWjeU+iOQ2Mp36oWpcVaJeDbnb3GzJiUiVm 8sDaOTHj9jVsngVbAwiPQdra/v6kHTRL9tFKT8xHCkI8u8nHsg2NoNnt X-Gm-Gg: Acq92OE6VZ+ZIKIU+v48hk/S+dcPqeozxJ0bljU13vQp7vstwWAFip+esCSCrLoiwV4 3siourSrUjHWDpZX9SR20opQIAFelzOdBE253dg36xCL7ZP6J1U2lv8Gs04+eo9ZPzhqnEP4CdO ksDY26+CUvMwQ76keQwjRx83lqWfouEjvwpCLwmKX+nxZYVVL34trut/TX2DokLuuexg+QGFNfS s5hJj69GaWukSLGBQbOFpoWCk9B/e9DQ63fpjgc6nEVZGF/bASthFzQHwxcLQ7ncZcpEMBc2Anx S7y9LZzldn4Dgj5ayfBSxJtzkq/HUI9ohrE8V4zApsAjkvmXnMyDu1rzo6bXrxUVTX4t91xWQS5 F7SxcjsIw/PfOvvOO68F3Fa2QlcDBwDUPfg2vOqn7AwEnO2CwiMTDQcl2NU0IHxQmxyi8oIYQf5 GjEM7cV+oeazWeaJcQJQZ4Mq2pHDOVYuykNdIzsY7MNS6L0AIxdBkKquKggz1rbg== X-Received: by 2002:a17:903:2b0e:b0:2bc:7c62:187 with SMTP id d9443c01a7336-2beb06133f5mr125142545ad.29.1779629333392; Sun, 24 May 2026 06:28:53 -0700 (PDT) Received: from 192.168.1.3 ([2001:448a:2003:4286:c553:4edf:bb9:bbd2]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2beb56f4343sm91197175ad.36.2026.05.24.06.28.50 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 24 May 2026 06:28:52 -0700 (PDT) From: Muchamad Coirul Anwar To: Jonathan Cameron Cc: linux-iio@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Miguel Ojeda , Igor Korotin , Brandon Saint-John , Muchamad Coirul Anwar Subject: [RFC PATCH v3 3/4] iio: position: add Rust driver for ams AS5600 Date: Sun, 24 May 2026 20:28:22 +0700 Message-ID: <20260524132824.54918-4-muchamadcoirulanwar@gmail.com> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20260524132824.54918-1-muchamadcoirulanwar@gmail.com> References: <20260524132824.54918-1-muchamadcoirulanwar@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add a Rust driver for the ams AS5600 12-bit magnetic rotary position sensor. The driver exposes in_angl_raw and in_angl_scale via the IIO sysfs interface. Features: - Circuit breaker pattern for I/O error resilience - Mutex-serialized multi-byte angle read sequence - Automatic recovery from bus failures (Poisoned -> Normal) - No magnet validation at probe time (deferred to read_raw) Tested on BeagleBone Black (AM335x) with AS5600 on i2c-2 (0x36). Signed-off-by: Muchamad Coirul Anwar --- drivers/iio/position/as5600.rs | 289 +++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 drivers/iio/position/as5600.rs diff --git a/drivers/iio/position/as5600.rs b/drivers/iio/position/as5600.rs new file mode 100644 index 000000000000..f87df650a91d --- /dev/null +++ b/drivers/iio/position/as5600.rs @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2026 Muchamad Coirul Anwar +//! Driver for ams AS5600 12-bit magnetic rotary position sensor. +//! +//! Datasheet: https://ams.com/documents/20143/36005/AS5600_DS000365_5-00.= pdf + +use kernel::{ + alloc::KBox, + bindings::{ + i2c_client, + iio_chan_info_enum_IIO_CHAN_INFO_RAW, + iio_chan_info_enum_IIO_CHAN_INFO_SCALE, + iio_chan_spec, + iio_chan_type_IIO_ANGL, + ENODATA, // + }, + bits::bit_u8, + device::Core, + error::{code::EIO, Error}, + i2c::{ + DeviceId, + Driver, + I2cClient, + IdTable, // + }, + i2c_device_table, + iio::{ + Device, + IioDriver, + IioVal, + Registered, // + }, + io::{Io, IoCapable}, + module_i2c_driver, of, of_device_table, + prelude::*, + sync::{ + new_mutex, + Mutex, // + }, +}; + +const AS5600_REG_STATUS: u8 =3D 0x0B; +const AS5600_REG_RAW_ANGLE_H: u8 =3D 0x0C; +const AS5600_REG_RAW_ANGLE_L: u8 =3D 0x0D; + +const AS5600_STATUS_MD: u8 =3D bit_u8(5); + +/// Returns kernel error `ENODATA`. +/// +/// Helper needed because `Error::from_errno` is not `const fn` and `ENODA= TA` +/// is only available as a raw `u32` binding, not as a wrapped `kernel::er= ror::code`. +#[inline(always)] +fn err_enodata() -> Error { + Error::from_errno(-(ENODATA as i32)) +} + +module_i2c_driver! { + type: As5600, + name: "as5600", + authors: ["Muchamad Coirul Anwar"], + description: "I2C Driver for ams OSRAM AS5600 Magnetic Rotary Position= Sensor", + license: "GPL", +} + +i2c_device_table!( + I2C_TABLE, + MODULE_I2C_TABLE, + ::IdInfo, + [(DeviceId::new(c"as5600"), ())] +); + +of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + ::IdInfo, + [(of::DeviceId::new(c"ams,as5600"), ())] +); + +#[derive(Clone, Copy)] +struct As5600Io(*mut i2c_client); + +/// Tracks the health state of the hardware bus to prevent I/O storms. +#[derive(Clone, Copy, PartialEq, Eq)] +enum DeviceState { + Normal, + Poisoned, +} + +// SAFETY: `As5600Io` wraps a raw pointer to `i2c_client`. This is `Send` +// and `Sync` because: +// - The I2C subsystem guarantees the `i2c_client` (parent) outlives the +// IIO device (child) via the Linux Device Model unbind ordering. +// - All hardware access is serialized through `Mutex>` +// (`io_lock`), and individual SMBus transactions are serialized by the = I2C +// adapter lock. +unsafe impl Send for As5600Io {} +unsafe impl Sync for As5600Io {} + +impl IoCapable for As5600Io {} +impl IoCapable for As5600Io {} + +impl Io for As5600Io { + #[inline] + fn addr(&self) -> usize { + 0 + } + + #[inline] + fn maxsize(&self) -> usize { + 256 + } + + #[inline] + fn try_read8(&self, offset: usize) -> Result + where + Self: IoCapable, + { + // SAFETY: `self.0` points to a valid `i2c_client` guaranteed by t= he + // Device Model lifetime hierarchy (parent outlives child). The ca= st is + // valid because `I2cClient` is `#[repr(transparent)]` over `i2c_c= lient`. + let client =3D unsafe { &*(self.0 as *const I2cClient) }; + client.try_read8(offset) + } + + #[inline] + fn try_read16(&self, offset: usize) -> Result + where + Self: IoCapable, + { + // SAFETY: `self.0` points to a valid `i2c_client` guaranteed by t= he + // Device Model lifetime hierarchy (parent outlives child). The ca= st is + // valid because `I2cClient` is `#[repr(transparent)]` over `i2c_c= lient`. + let client =3D unsafe { &*(self.0 as *const I2cClient) }; + client.try_read16(offset) + } +} + +#[pin_data] +struct As5600Priv { + #[pin] + io_lock: Mutex>, + channels: KBox<[iio_chan_spec; 1]>, +} + +/// Encapsulates the I/O interface and its runtime health state. +/// +/// This prevents operations on a known-dead bus (Circuit Breaker pattern). +struct As5600HwState { + io: T, + state: DeviceState, +} + +impl> As5600HwState { + /// Performs a dummy read to probe bus health after an I/O failure. + /// + /// Returns `EIO` in all cases =E2=80=94 the caller should always prop= agate the error. + /// The side effect determines recovery behavior: + /// - If the dummy read **succeeds**: state is reset to `Normal`, mean= ing the + /// next `read_raw` call will attempt normal operation directly. + /// - If the dummy read **fails**: state is set to `Poisoned`, meaning= the + /// next `read_raw` call will attempt recovery before normal operati= on. + fn handle_io_error(&mut self) -> Error { + match self.io.try_read8(AS5600_REG_STATUS as usize) { + Ok(_) =3D> { + self.state =3D DeviceState::Normal; + EIO + } + Err(_) =3D> { + self.state =3D DeviceState::Poisoned; + EIO + } + } + } +} + +// SAFETY: `As5600Priv` is `Send` and `Sync` because: +// - `T: IoCapable` is a marker trait with no interior mutability. +// The underlying `As5600Io` wrapper's Send/Sync is guaranteed by its +// manual impls (serialized via Mutex + I2C adapter lock). +// - `channels` is a heap-allocated array (`KBox`) with no interior mutabi= lity. +// - `io_lock: Mutex>` provides synchronized interior mut= ability. +// - `DeviceState` is a plain enum without interior mutability (Send + Sync +// implicitly). +// All concurrent access to hardware goes through the `Mutex` guard. +// The `Unpin` bound is strictly required because `kernel::sync::lock::Gua= rd` +// only implements `DerefMut` for `T: Unpin`. Without it, state mutation f= ails. +unsafe impl + Unpin> Send for As5600Priv {} +unsafe impl + Unpin> Sync for As5600Priv {} + +impl + Unpin> IioDriver for As5600Priv { + fn read_raw(&self, _chan: *const iio_chan_spec, mask: isize) -> Result= { + match mask { + // IIO_CHAN_INFO_RAW =E2=80=94 read the 12-bit raw angle value. + m if m =3D=3D iio_chan_info_enum_IIO_CHAN_INFO_RAW as isize = =3D> { + let mut hw_guard =3D self.io_lock.lock(); + + // If the bus was previously poisoned, attempt a single re= covery + // read before proceeding with normal operation. + let status =3D if hw_guard.state =3D=3D DeviceState::Poiso= ned { + match hw_guard.io.try_read8(AS5600_REG_STATUS as usize= ) { + Ok(s) =3D> { + hw_guard.state =3D DeviceState::Normal; + s + } + Err(_) =3D> return Err(EIO), + } + } else { + match hw_guard.io.try_read8(AS5600_REG_STATUS as usize= ) { + Ok(s) =3D> s, + Err(_) =3D> return Err(hw_guard.handle_io_error()), + } + }; + + // Check magnet presence (MD bit). Without a magnet the an= gle + // register contains stale/invalid data. + if (status & AS5600_STATUS_MD) =3D=3D 0 { + return Err(err_enodata()); + } + + // Read the 12-bit angle as two bytes. The AS5600 hardware + // freezes the internal angle value on reading the high by= te + // until the low byte is read =E2=80=94 the Mutex ensures = this + // sequence is not interleaved by concurrent readers. + let angle_h =3D match hw_guard.io.try_read8(AS5600_REG_RAW= _ANGLE_H as usize) { + Ok(v) =3D> v as u16, + Err(_) =3D> return Err(hw_guard.handle_io_error()), + }; + let angle_l =3D match hw_guard.io.try_read8(AS5600_REG_RAW= _ANGLE_L as usize) { + Ok(v) =3D> v as u16, + Err(_) =3D> return Err(hw_guard.handle_io_error()), + }; + + let angle =3D (angle_h << 8 | angle_l) & 0x0FFF; + Ok(IioVal::Int(angle as i32)) + } + // IIO_CHAN_INFO_SCALE =E2=80=94 radians per LSB: 2=CF=80 / 40= 96 =E2=89=88 0.001533981. + m if m =3D=3D iio_chan_info_enum_IIO_CHAN_INFO_SCALE as isize = =3D> { + Ok(IioVal::IntPlusNano(0, 1533981)) + } + _ =3D> Err(kernel::error::code::EINVAL), + } + } + + fn channels(&self) -> &[iio_chan_spec] { + &self.channels[..] + } +} + +struct As5600 { + _iio_dev: Device, Registered>, +} + +impl Driver for As5600 { + type IdInfo =3D (); + const I2C_ID_TABLE: Option> =3D Some(&I2C_TABLE); + const OF_ID_TABLE: Option> =3D Some(&OF_TABL= E); + + #[allow(refining_impl_trait)] + fn probe(dev: &I2cClient, _id_info: Option<&Self::IdInfo>) -> Re= sult { + // SAFETY: `iio_chan_spec` is a C struct whose fields are all inte= gers + // and pointers. Zero is a valid initialization for all of them. + let mut channels_alloc =3D kernel::alloc::KBox::new( + [unsafe { core::mem::zeroed::() }], + kernel::alloc::flags::GFP_KERNEL, + )?; + + channels_alloc[0].info_mask_separate =3D (1 << iio_chan_info_enum_= IIO_CHAN_INFO_RAW) + | (1 << iio_chan_info_enum_IIO_CHAN_INFO_SCALE); + channels_alloc[0].type_ =3D iio_chan_type_IIO_ANGL; + + let client_ptr =3D dev as *const _ as *mut i2c_client; + + let priv_init =3D pin_init!(As5600Priv { + io_lock <- new_mutex!(As5600HwState { + io: As5600Io(client_ptr), + state: DeviceState::Normal, + }), + channels: channels_alloc, + }); + + let iio_dev =3D Device::build_device(dev.as_ref(), c"as5600", priv= _init)?; + let iio_dev_registered =3D iio_dev.register(&crate::THIS_MODULE)?; + + dev_info!(dev.as_ref(), "AS5600 magnetic position sensor ready\n"); + Ok(As5600 { + _iio_dev: iio_dev_registered, + }) + } +} --=20 2.50.0 From nobody Sun May 24 17:49:06 2026 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (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 3A74E11CA9 for ; Sun, 24 May 2026 13:28:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779629339; cv=none; b=lfOlkwpUp6mbwv/OGByrB0qGWO3cPZ5kA4YV5AY8fel1V3H2IWrW3GiA8L0voXR41wnmgSqu52FYaOIZcUaQ0mO0rnpvT1VDzxEpInpo/w3YQbMpgoN+K2IKH3ulds0isJ/YEKWXGxFKUMdXdeTXAs977u3xJvodLE6o3A7/hVs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779629339; c=relaxed/simple; bh=K5joXiGigHY4AX80TpszLLsv1e0MQCIb5XR5UlRJug8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hTcLaUH5NUY9heQvWnzCaIUgtPmhhL0Yt8pLUwlJxdtkYyZaRt1bWd2TJ5+q4QSNf0RskhH+3fzIx6FA+62Sr0A9ZftF0RpJzz56frTOg729RDcYWpr9/Cctr+yodwwS/4j+8jZZwkesQHyRrJxYSdPJjVTaQw5XJKjaZFcg/hY= 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=eE9mjigx; arc=none smtp.client-ip=209.85.214.176 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="eE9mjigx" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2b45cb89f7eso59188185ad.0 for ; Sun, 24 May 2026 06:28:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779629337; x=1780234137; 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=ewEUZMFUPji4t9hkw5T9WDC0gsus4wg45o/DJDgsA2k=; b=eE9mjigx21k+xriwULpq2uqzlrez/gYAlO3QPrzJXSAh1f4D27zIddoi6pPQum8mbN TTl7ToNWnUl/KP2DVak2a7wi/UaZL+5pwoF74GqXGkx5PWfmyeUcqGHhMuwkIuJ6jmfM fC3JvVe9s8BpSJJukiy1hEwaSHSvAi7C9EObjKU1S9t/vl1sIVJG+9mtaJPuou4Dd/jP dcNQ1QvXyUd/kIsPweyrdk2LpCTt2FxhKqcU6JOI51IkkDHVQXN0S4zNxfwma8ZY7nBi xf+T9BxhhCl9O0i6hR1owvcqjRy6Emy0R3wAfYUkztOuLsCbuOzDtLu0MoblUt/apOhY ompQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779629337; x=1780234137; 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=ewEUZMFUPji4t9hkw5T9WDC0gsus4wg45o/DJDgsA2k=; b=QNFg21OVppXmFfCeVIWDzbKgSMV8/czXVBM9ojyrp9SL+ThbGSrQg6MD2nGa5pW81w f6+J3/50nmk5WO31wlnzmzbNQ2xT47lGXplNmlE9p71lILsCH0o7X9023qbvxMvw5J77 nrKl6tFW4D+0T8BnCKhtmZRBwwkP2oXQ5SG5CFQ6eC241LAW7vvZY6KvKE+e1iUvSdFx ike8rUgRKQhReomKnQQYTZGabcZQK45sJEltVH5EHdClwJW4PFV3i+XdlTcvsgA+sA/W 0ir5JFbKKSCeC2LdeJEca242pxbE65L/FuT+eqNgUQzIkGMJVB6M1GpMIHYCrrVTvrXL D00A== X-Forwarded-Encrypted: i=1; AFNElJ+r7+anqpPZ+hbnpomF/l35ZYDurAxfkVJsLg9hd7D1PKlvTnlht8nZ0TauqfuCJiVcM+oZnPuXaCDtFus=@vger.kernel.org X-Gm-Message-State: AOJu0Yxazz9oxiSr+IfEyub5e0hmhESBIygOw2HkVF6Pk23Z17o6Gep1 8vVnv1v0hYXsLRJD9rLRj7+9uO6AJ5RYnR5wnKaMnloDJpoa30k5wX03 X-Gm-Gg: Acq92OH4t2ZHgDuvEbbNSKrPp/rO4vXRM+oxRVKN5uATzopdwB3f1Ko5pHEoNZBi+KK YF+PnxzNRcwoYyHtOQCXDiVIQyaTCYN3PEludUKhPj3WBxB8giyKDBbB/+Rtn+J+lHYUtblWKAp aHMCYiMB2A2fHzINYjXb7GHvFJGrUQRuEcPSqYPgnslbF/kHa3BQ3S1Ueh4cG8I3R6j+GBJT/U6 auYenEyVH9bOqWdb+5D5Xfw6c5pN59Tf4kPwjC7SGlCiy06LCosTTVPtm7sGbruyO2njy3H9e6r LZJsKaTW0LFIMEvlOSC268jehg8mBX04uVVecBkmddBOL54n9PnM0ErDl1aY0xr2U9Y/4RXoQdx 34P9U25Ejn91kg8MzocWGTwE/4qURyip7wqgBP6jSGAl5MA4gxwiX4g4i7IxdzNbs0NQ+Fnfk9Z tUwEFeSedG3P/GVnGkb2vJtYJ3R9wK7TSeRVYb7e1OwLS/hdnfBqWzVFxJuan22Q== X-Received: by 2002:a17:903:11c4:b0:2be:260a:592e with SMTP id d9443c01a7336-2beb031b1e9mr110031255ad.1.1779629337390; Sun, 24 May 2026 06:28:57 -0700 (PDT) Received: from 192.168.1.3 ([2001:448a:2003:4286:c553:4edf:bb9:bbd2]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2beb56f4343sm91197175ad.36.2026.05.24.06.28.54 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 24 May 2026 06:28:56 -0700 (PDT) From: Muchamad Coirul Anwar To: Jonathan Cameron Cc: linux-iio@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Miguel Ojeda , Igor Korotin , Brandon Saint-John , Muchamad Coirul Anwar Subject: [RFC PATCH v3 4/4] iio: position: as5600: add Kconfig and Makefile entries Date: Sun, 24 May 2026 20:28:23 +0700 Message-ID: <20260524132824.54918-5-muchamadcoirulanwar@gmail.com> X-Mailer: git-send-email 2.50.0 In-Reply-To: <20260524132824.54918-1-muchamadcoirulanwar@gmail.com> References: <20260524132824.54918-1-muchamadcoirulanwar@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 build system integration for the AS5600 Rust IIO driver. CONFIG_AS5600 depends on I2C, IIO, and RUST. Signed-off-by: Muchamad Coirul Anwar --- drivers/iio/position/Kconfig | 14 ++++++++++++++ drivers/iio/position/Makefile | 1 + 2 files changed, 15 insertions(+) diff --git a/drivers/iio/position/Kconfig b/drivers/iio/position/Kconfig index 1576a6380b53..dab9310e8079 100644 --- a/drivers/iio/position/Kconfig +++ b/drivers/iio/position/Kconfig @@ -6,6 +6,20 @@ =20 menu "Linear and angular position sensors" =20 +config AS5600 + tristate "ams AS5600 magnetic rotary position sensor" + depends on I2C && RUST + help + Say Y here to build support for the ams AS5600 12-bit + magnetic rotary position sensor with IIO channel support + (in_angl_raw and in_angl_scale). + + This is a Rust driver that exposes the 12-bit raw angle + and radian scale via the IIO subsystem. + + To compile this driver as a module, choose M here: the + module will be called as5600. + config IQS624_POS tristate "Azoteq IQS624/625 angular position sensors" depends on MFD_IQS62X || COMPILE_TEST diff --git a/drivers/iio/position/Makefile b/drivers/iio/position/Makefile index d70902f2979d..2d26f6d6ace3 100644 --- a/drivers/iio/position/Makefile +++ b/drivers/iio/position/Makefile @@ -4,5 +4,6 @@ =20 # When adding new entries keep the list in alphabetical order =20 +obj-$(CONFIG_AS5600) +=3D as5600.o obj-$(CONFIG_HID_SENSOR_CUSTOM_INTEL_HINGE) +=3D hid-sensor-custom-intel-h= inge.o obj-$(CONFIG_IQS624_POS) +=3D iqs624-pos.o --=20 2.50.0