From nobody Sat Sep 26 01:04:55 2026 Received: from mail-pf1-f169.google.com (mail-pf1-f169.google.com [209.85.210.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 9B81442255C for ; Sun, 6 Sep 2026 08:46:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788684383; cv=none; b=Ts5xPBtW2F+OevsK0BbMf+lf/Fd+/YoF4hjb+ccLI0g7nLp7UhbmGlWsgKR4sdxqAwKYdFttT6vaC59wNryFENmcxboRsoaoOGSIS/sakddQW5VXRSUxZihrmPKp7g1QAHRwWIv+obFPHw18c3F0gbX2X7YS4HJAFqSvV8LYU54= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788684383; c=relaxed/simple; bh=UFA5fI8C+4EiuKIzvPGp1iegNLyohGUY8/0+ZYqGEXA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=PgtiPbRkKZ+/JNrOBFfAXtdoklNM95Q4U1J8RnUeFdvT1ZVbC4I0hLEmePJ1rSFWL2JlH+BRR2qcva7upm1pXvdEgcXEa67e5mjwCBEDGvh71QKhJ29bQgElZex8w3+C1f8U/a4nnYiVnFodyLBAFSfbouCv8fpaGF5UzwUNNtc= 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=j0j/fD/L; arc=none smtp.client-ip=209.85.210.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="j0j/fD/L" Received: by mail-pf1-f169.google.com with SMTP id d2e1a72fcca58-84e04df8c46so2840628b3a.2 for ; Sun, 06 Sep 2026 01:46:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788684381; x=1789289181; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=/gkzIc2mrM4Qdq/WyMfgp/R6p5u/2x3xKFYvN0yjh84=; b=j0j/fD/LrMDYgCiVOcsLBVWp7yp4OKf/wP9yCmjM3LwC1xlKJxSyos6fMKVNGC09DV v7DniNZbWTKezAljB9rkcENQwJcw0jDXDDAkOlIbVYjw87LzDMuxJOdo0tr1XpbscNjJ AB27Ya6EqEv3bYT0ROSD/ZChbHh7/w1aoHi+WMYVITzk+u84gMMmmB8EW5S/hHIv7rwh o8uiHmTjMvAuMnAPtPvodmE4VSTvGjQAV8/hCVStt3ZrO0s39Ls/GWbGQIwUcI+5nzp6 2Qfc60dn5PHCPujww5uF9QFUT5a+noyshVBpMKHqgHE82JNKPxQmWeGwg76ysuwe9Ccv nBeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788684381; x=1789289181; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=/gkzIc2mrM4Qdq/WyMfgp/R6p5u/2x3xKFYvN0yjh84=; b=D+wKFTyzmeHtsIL4b/IO3iZBHTtX2E2QLLkWW3Gp+3N53z0a+Qtn0mFhI0n5JjoMna 9CpMWiwTyJxH+4DNUcUmjli2WS36kDAxxTMyNUl9HUduCN09c1DPCm+ABOReLb6MNUQA Hsnl6YrBLJCi/m60tLFxn2mDQVS5vCW44KVP3C4TE/mIeAwEh7Fc0V8cjqtQtJCEDBHL S3ymMVXS7+Kz1as45UsKefwJFrddbw9Sba0D8IrZoL6dBCq7L42kLA4IHsbWMXL7UjHL iqE9akicKyOUS9IFNcPBdz7p0ThhsyxSpImqi7g+GnpPKynKZSkvKVUKYxr9KvNi9aHT ICsg== X-Gm-Message-State: AFuF++luUY5dVd/uGZx8speu9YHeTPpcEmIQvXpFEt6ILZnjsOQrg+vQ bvbRZ8MC8a6YetxaCqBVKdjF4dVH9+jzOWjZT0FFFSAUjqTvSen8exeU X-Gm-Gg: AYBFou1GpZ9DmW/AUY4AbJfef+ykjgGnIAhi8Gnokinslny1WFNDM77R4Ht8GCTqiIb OwscEpswQ/W1e7uXU+XSsq4v06ypMelkUqDLl+P5/h1xuszUkjETUlLdoT8KwLkRe5d5tYaoptz sY+JwjtGJzLRtnvNuaDZsUzCPRu69T9LDUrVKYeAh3IwurS3TSAwndlVCi9rI5hIKc4rLmWIWJE 7XibZgjIOIPxOYo3tzXCd8i16uFgquGikKlCWiZ/bLSTN8ArR6k1CfAcaR2oNg6QTpbkB1O/UXk N+hYHtpDYmAnG9HpIrkp6/JrJx1Py/8lLpRkClAQTjyFdufRackN7gKlmPcsxSO5C+3glJmPHW0 GTkHN+RlyqvxOZsLrMtIvpo+I+fz32FhKmox/Ar9AKPtwD4p5JeWNQIVZ7ZJTQYyWycXE6SbOlM s0EK3XZlYvfU+3CxtNe7JZ+AbNAj7BQhqltWMo8Y+07XcfkNJNMCu+pnt230BdNwEJ3kYhEE07O HzGmdQ6 X-Received: by 2002:aa7:88c3:0:b0:848:4424:2b8e with SMTP id d2e1a72fcca58-861696788b3mr22513081b3a.3.1788684380945; Sun, 06 Sep 2026 01:46:20 -0700 (PDT) Received: from localhost (madb688426.ap.nuro.jp. [219.104.132.38]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-cc4554770f6sm2697918a12.24.2026.09.06.01.46.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Sep 2026 01:46:20 -0700 (PDT) From: Kohei Ito Date: Sun, 06 Sep 2026 17:45:49 +0900 Subject: [PATCH 1/3] rust: gpio: add GPIO module with common definitions 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 Message-Id: <20260906-add-rust-gpio-consumer-v1-1-24d192f93760@gmail.com> References: <20260906-add-rust-gpio-consumer-v1-0-24d192f93760@gmail.com> In-Reply-To: <20260906-add-rust-gpio-consumer-v1-0-24d192f93760@gmail.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-gpio@vger.kernel.org, Kohei Ito X-Mailer: b4 0.15.2 Add the top-level GPIO module with minimal common definitions. This module is the basis for future Rust GPIO extensions. Signed-off-by: Kohei Ito --- rust/bindings/bindings_helper.h | 1 + rust/kernel/gpio.rs | 152 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/lib.rs | 2 + 3 files changed, 155 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 1124785e210b..98b048b36771 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/gpio.rs b/rust/kernel/gpio.rs new file mode 100644 index 000000000000..819efc8a0c05 --- /dev/null +++ b/rust/kernel/gpio.rs @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! GPIO abstractions. + +use crate::{ + error::{ + Error, + Result, // + }, + fmt, + prelude::*, // +}; + +/// Describes GPIO direction. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum LineDirection { + /// Represent the output direction. + Out =3D bindings::GPIO_LINE_DIRECTION_OUT, + + /// Represent the input direction. + In =3D bindings::GPIO_LINE_DIRECTION_IN, +} + +impl core::ops::Not for LineDirection { + type Output =3D Self; + fn not(self) -> Self::Output { + match self { + Self::Out =3D> Self::In, + Self::In =3D> Self::Out, + } + } +} + +impl TryFrom for LineDirection { + type Error =3D Error; + fn try_from(value: c_int) -> Result { + match value { + v if v =3D=3D Self::Out as c_int =3D> Ok(Self::Out), + v if v =3D=3D Self::In as c_int =3D> Ok(Self::In), + _ =3D> Err(EINVAL), + } + } +} + +impl fmt::Display for LineDirection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::In =3D> f.pad("In"), + Self::Out =3D> f.pad("Out"), + } + } +} + +/// Describes the logical GPIO level, i.e. taking the ACTIVE_LOW status in= to account. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum LogicalLineLevel { + /// Represent the logical inactive level. + Inactive, + + /// Represent the logical active level. + Active, +} + +impl core::ops::Not for LogicalLineLevel { + type Output =3D Self; + fn not(self) -> Self::Output { + match self { + Self::Inactive =3D> Self::Active, + Self::Active =3D> Self::Inactive, + } + } +} + +impl LogicalLineLevel { + fn as_c_int(&self) -> c_int { + match self { + Self::Inactive =3D> 0, + Self::Active =3D> 1, + } + } +} + +impl TryFrom for LogicalLineLevel { + type Error =3D Error; + fn try_from(value: c_int) -> Result { + match value { + 0 =3D> Ok(Self::Inactive), + 1 =3D> Ok(Self::Active), + _ =3D> Err(EINVAL), + } + } +} + +impl fmt::Display for LogicalLineLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Inactive =3D> f.pad("Inactive"), + Self::Active =3D> f.pad("Active"), + } + } +} + +/// Describes the raw GPIO level, i.e. the value of its physical line with= out regard for its +/// ACTIVE_LOW status. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum PhysicalLineLevel { + /// Represent the physical LOW level. + Low, + + /// Represent the physical HIGH level. + High, +} + +impl core::ops::Not for PhysicalLineLevel { + type Output =3D Self; + fn not(self) -> Self::Output { + match self { + Self::Low =3D> Self::High, + Self::High =3D> Self::Low, + } + } +} + +impl PhysicalLineLevel { + fn as_c_int(&self) -> c_int { + match self { + Self::Low =3D> 0, + Self::High =3D> 1, + } + } +} + +impl TryFrom for PhysicalLineLevel { + type Error =3D Error; + fn try_from(value: c_int) -> Result { + match value { + 0 =3D> Ok(Self::Low), + 1 =3D> Ok(Self::High), + _ =3D> Err(EINVAL), + } + } +} + +impl fmt::Display for PhysicalLineLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Low =3D> f.pad("Low"), + Self::High =3D> f.pad("High"), + } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 9512af7156df..6c96c1269249 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -73,6 +73,8 @@ pub mod firmware; pub mod fmt; pub mod fs; +#[cfg(CONFIG_GPIOLIB)] +pub mod gpio; #[cfg(CONFIG_GPU_BUDDY =3D "y")] pub mod gpu; #[cfg(CONFIG_I2C =3D "y")] --=20 2.50.1 From nobody Sat Sep 26 01:04:55 2026 Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.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 0DA1742122A for ; Sun, 6 Sep 2026 08:46:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788684387; cv=none; b=nTCprOBtBXHaWt/BkkpeG987A//22ZYXnzXHOdahIdzFNX/3QwFMBYEep8SvLziJ2YuK8CLQIExr54sgt/k3BpiTMFR7OEsocM7khj81JXWYd5h37Q/6D5CGMFS/KqW+rXdHXSaKADzDXTNdj7bXxteJx118iMrU9VMFgf9ygAU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788684387; c=relaxed/simple; bh=nPnNq8PNL3EdxldTZ4UGGXWdBEEcU1yX193DQQnMoAQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Rx1OGAvvnvfbnuQkeZHPXqf27nc+k9AWusiPPzuyDwH8FSOJ+vxn41wt8zv2OTm8ZWJUPRNdd4u6MIKUaSqIlPoS7T9P9xo3P98NQKORzBEhVRo0ZirN38ekA3fcFs9XNBVfDychpi/bjeX9daGQaXJLB/fKfiTat7zYhC+5BHo= 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=MucM33P6; arc=none smtp.client-ip=209.85.214.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="MucM33P6" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-2d91ded8174so18887795ad.1 for ; Sun, 06 Sep 2026 01:46:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788684383; x=1789289183; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=oZaFfOhlZYqUNpBBIQ32ijip3K7hsLhyzDi6pF6GBpc=; b=MucM33P6GbFRkFi1Tp0b5WZhb5XBl7ZvArKZeHO7Su02W335nbieNSuVUMQcfrSf21 WFetc5mamiFh8SFO5NLnwkI/HIGvW6BcD/2P0l3TpfSv9CYrIuRAPkrFo0/93KYusscX cNj4CI9Lf+E9dxGkESfE9gpapn+25sJPdWkatxF/cBBIDjNRNRQChk6HOTk6nm6Bottz r9P20McBsW077crge/0D2qX2VXKZ2qRbIKTJAAU5fPgfy4Q3tg71+On5jWLAgKYVO38q 6DeOqCY6cfDIIqCAhzeeKb0WBUscMQbOS3J0cPjlFYVPM4SZc8kKiU+1nPigeuFqAIcp C6mw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788684383; x=1789289183; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=oZaFfOhlZYqUNpBBIQ32ijip3K7hsLhyzDi6pF6GBpc=; b=igTjBIHq53UM+5wAqZB7VoGFXKtvvNbUF/D+KRF9X+ZcF30NLBUKOQfWiCMufoLH4O UkcOcDCnwT0vw+qp4N+H48OwkAiqQxDSUwVOEk78ZDfVfaUUUqJPgYkFYyO9vIX/1vW3 RWj1UUyDIqes3AnAI0IzGOVr5gtK7+Any0RbijQdgWymuXUSeuAk7NFh73woDsBe5S6A lbeBlN2p7uHpqXwLHz0CvH+oxrpWldnvCQRZKHRgFjkC0aNzvywkmSoqgqHSmrMQLPuL OnB63ZEyH2BZchxxMwAMjmrYwOU/kNl+c+/EQCn2nzf1wRV6bkiRCYsDkQsUL//5i0Ar u2pA== X-Gm-Message-State: AFuF++lWUOKJuvEXS6X0c/AFP+1zbQhcIyl05VePpuiU5Ol7GCwmJmng yOMR4aPMZbReFI20GcEUOR3b8nl0O/IkTdU0XOt1D2Z62CXTrip0G2w/ X-Gm-Gg: AYBFou0hoXt1SOMZQynrbdfrvU42v+ZymPfJMBccq/vkqct51RrwNQiaYMp6bLT6QuT 2zcey9OSn8DoHr17NvPS4L9vj818rK0kjZRykj7SvIKXBu6J1PLBAak60ctMgk8F0bFQszUNP9/ pf8ceuy/kPv+EIuc1LA8yVL9m8kIXem/VQhHXIp5Swuni29kzAjwHDjVyF0MBw4Tfcy3Qw6Mduj NAgUIp6KLjJ8MgZrktoq/25YaZ4P+jPyymbMmqnVYtqA/EDMJ815bKz9AXKXGTadnuQEADOKp3Z SCZpj6pAjuH9bbESj0ZNmR5ljhuZzCEYko6iX8nIoMYMOKXgJVuEU9gA3iZMqh60gvn3dpZhzOI BpdbfcSyWhrQ0LOdp+Ds3R2Ega6nMX6EGfa4PA8pytr7RYAXbUudk1lmI+m6iczta9xHLFhozSM WaXPX4MINsCUL7OwTdgj9R6rJYlENvj9wD3azvOWe76LaYDsr9FbCnlvNZ4MDW9iCsss9jPbNDT q1+PiOS X-Received: by 2002:a17:90b:4c86:b0:395:5f43:4ec4 with SMTP id 98e67ed59e1d1-39b25f78ab8mr23868891a91.0.1788684383083; Sun, 06 Sep 2026 01:46:23 -0700 (PDT) Received: from localhost (madb688426.ap.nuro.jp. [219.104.132.38]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-39b08c397b1sm20068158a91.8.2026.09.06.01.46.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Sep 2026 01:46:22 -0700 (PDT) From: Kohei Ito Date: Sun, 06 Sep 2026 17:45:50 +0900 Subject: [PATCH 2/3] rust: gpio: Add basic consumer abstractions 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 Message-Id: <20260906-add-rust-gpio-consumer-v1-2-24d192f93760@gmail.com> References: <20260906-add-rust-gpio-consumer-v1-0-24d192f93760@gmail.com> In-Reply-To: <20260906-add-rust-gpio-consumer-v1-0-24d192f93760@gmail.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-gpio@vger.kernel.org, Kohei Ito X-Mailer: b4 0.15.2 Add basic abstractions for GPIO consumer APIs. Due to a bindgen issue that may generate the wrong type for enum types, `gpio/consumer.h` is included at the top of `bindings_helper.h` as a temporary workaround. Once the issue is resolved, it can be moved back to its proper alphabetical position. Signed-off-by: Kohei Ito --- rust/bindings/bindings_helper.h | 1 + rust/kernel/gpio.rs | 2 + rust/kernel/gpio/consumer.rs | 437 ++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 440 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 98b048b36771..30985c102c70 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -26,6 +26,7 @@ * This workaround may not be possible in some cases, depending on how the= C * headers are set up. */ +#include #include =20 #include diff --git a/rust/kernel/gpio.rs b/rust/kernel/gpio.rs index 819efc8a0c05..40b6c64e8f5e 100644 --- a/rust/kernel/gpio.rs +++ b/rust/kernel/gpio.rs @@ -11,6 +11,8 @@ prelude::*, // }; =20 +pub mod consumer; + /// Describes GPIO direction. #[derive(Clone, Copy, PartialEq, Eq)] #[repr(u32)] diff --git a/rust/kernel/gpio/consumer.rs b/rust/kernel/gpio/consumer.rs new file mode 100644 index 000000000000..f81c7381c075 --- /dev/null +++ b/rust/kernel/gpio/consumer.rs @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0 +// This file is based on rust/kernel/clk.rs. + +//! GPIO consumer abstractions. +//! +//! C header: [`include/linux/gpio/consumer.h`](srctree/include/linux/gpio= /consumer.h) +//! +//! Reference: + +use crate::{ + device::Device, + error::{ + from_err_ptr, + to_result, + Error, + Result, // + }, + gpio::{ + LineDirection, + LogicalLineLevel, + PhysicalLineLevel, // + }, + prelude::*, // +}; + +use core::{ops::Deref, ptr}; + +/// The GPIO descriptor flags to configure its direction and output value. +/// +/// Rust abstraction for the C [`enum gpiod_flags`]. +/// +/// They can be combined with the operators `|`, and `&`. +/// +/// Values can be used from the associated constants such as +/// [`Flags::GPIOD_ASIS`]. +#[derive(Clone, Copy, PartialEq)] +pub struct GpiodFlags(bindings::gpiod_flags); + +impl GpiodFlags { + /// Don't change anything. + pub const ASIS: Self =3D Self::new(bindings::gpiod_flags_GPIOD_ASIS); + + /// Set lines to input mode. + pub const IN: Self =3D Self::new(bindings::gpiod_flags_GPIOD_IN); + + /// Set lines to output and drive them low. + pub const OUT_LOW: Self =3D Self::new(bindings::gpiod_flags_GPIOD_OUT_= LOW); + + /// Set lines to output and drive them high. + pub const OUT_HIGH: Self =3D Self::new(bindings::gpiod_flags_GPIOD_OUT= _HIGH); + + /// Set lines to open-drain output and drive them low. + pub const OUT_LOW_OPEN_DRAIN: Self =3D Self::new(bindings::gpiod_flags= _GPIOD_OUT_LOW_OPEN_DRAIN); + + /// Set lines to open-drain output and drive them high. + pub const OUT_HIGH_OPEN_DRAIN: Self =3D + Self::new(bindings::gpiod_flags_GPIOD_OUT_HIGH_OPEN_DRAIN); + + fn into_inner(self) -> bindings::gpiod_flags { + self.0 + } + + // Always inline to optimize out error path of `build_assert`. + #[inline(always)] + const fn new(value: bindings::gpiod_flags) -> Self { + build_assert!(value as u64 <=3D bindings::gpiod_flags::MAX as u64); + Self(value) + } +} + +/// A reference-counted gpio descriptor. +/// +/// Rust abstraction for the C [`struct gpio_desc`]. +/// +/// # Invariants +/// +/// A [`GpioDesc`] instance holds either a pointer to a valid [`struct gpi= o_desc`] created by the C +/// portion of the kernel or a `NULL` pointer. +/// +/// Instances of this type are reference-counted. Calling [`GpioDesc::get`= ] ensures that the +/// allocation remains valid for the lifetime of the [`GpioDesc`]. +/// +/// # Examples +/// +/// The following example demonstrates how to obtain a GPIO line for a dev= ice. +/// +/// ``` +/// use crate::{ +/// device::Device, +/// error::Result, +/// gpio::{ +/// consumer::{ +/// GpioDesc, +/// GpiodFlags, // +/// }, +/// LogicalLineLevel, // +/// }, // +/// }; +/// +/// fn examine_gpio(dev: &Device) -> Result { +/// let gpiod =3D GpioDesc::get(dev, Some(c"reset"), GpiodFlags::ASIS)= ?; +/// +/// gpiod.set_value(LogicalLineLevel::Inactive)?; +/// +/// gpiod.set_value(LogicalLineLevel::Active)?; +/// +/// Ok(()) +/// } +/// ``` +/// +/// [`struct gpio_desc`]: https://docs.kernel.org/driver-api/gpio/consumer= .html +#[repr(transparent)] +pub struct GpioDesc(*mut bindings::gpio_desc); + +// SAFETY: It is safe to call `gpiod_put` on another thread than where `gp= iod_get` was called. +unsafe impl Send for GpioDesc {} + +impl GpioDesc { + /// Gets [`GpioDesc`] corresponding to a [`Device`] and a connection i= d. + /// + /// Equivalent to the kernel's [`gpiod_get`] API. + /// + /// [`gpiod_get`]: https://docs.kernel.org/driver-api/gpio/index.html#= c.gpiod_get + pub fn get(dev: &Device, name: Option<&CStr>, flags: GpiodFlags) -> Re= sult { + let con_id =3D name.map_or(ptr::null(), |n| n.as_char_ptr()); + + // SAFETY: It is safe to call [`gpiod_get`] for a valid device poi= nter. + // + // INVARIANT: The reference-count is decremented when [`GpioDesc`]= goes out of scope. + Ok(Self(from_err_ptr(unsafe { + bindings::gpiod_get(dev.as_raw(), con_id, flags.into_inner()) + })?)) + } + + /// Obtain the raw [`struct gpio_desc`] pointer. + #[inline] + fn as_raw(&self) -> *mut bindings::gpio_desc { + self.0 + } + + /// Get the direction. + /// + /// Equivalent to the kernel's [`gpiod_get_direction`] API. + /// + /// [`gpiod_get_direction`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_get_dir= ection + #[inline] + pub fn get_direction(&self) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_get_direction`]. + let ret =3D unsafe { bindings::gpiod_get_direction(self.as_raw()) = }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + LineDirection::try_from(ret) + } + } + + /// Set the GPIO direction to input. + /// + /// Equivalent to the kernel's [`gpiod_direction_input`] API. + /// + /// [`gpiod_direction_input`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_directi= on_input + #[inline] + pub fn direction_input(&self) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_direction_input`]. + to_result(unsafe { bindings::gpiod_direction_input(self.as_raw()) = }) + } + + /// Set the GPIO direction to output and assign the logical value. + /// + /// Equivalent to the kernel's [`gpiod_direction_output`] API. + /// + /// [`gpiod_direction_output`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_directi= on_output + #[inline] + pub fn direction_output(&self, value: LogicalLineLevel) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_direction_output`]. + to_result(unsafe { bindings::gpiod_direction_output(self.as_raw(),= value.as_c_int()) }) + } + + /// Set the GPIO direction to output and assign the physical value. + /// + /// Equivalent to the kernel's [`gpiod_direction_output_raw`] API. + /// + /// [`gpiod_direction_output_raw`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_directi= on_output_raw + #[inline] + pub fn direction_output_raw(&self, value: PhysicalLineLevel) -> Result= { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_direction_output_raw`]. + to_result(unsafe { bindings::gpiod_direction_output_raw(self.as_ra= w(), value.as_c_int()) }) + } + + /// Get the logical GPIO value. + /// + /// Equivalent to the kernel's [`gpiod_get_value`] API. + /// + /// [`gpiod_get_value`]: https://docs.kernel.org/driver-api/gpio/index= .html#c.gpiod_get_value + #[inline] + pub fn get_value(&self) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_get_value`]. + let ret =3D unsafe { bindings::gpiod_get_value(self.as_raw()) }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + LogicalLineLevel::try_from(ret) + } + } + + /// Assign the logical value. + /// + /// Equivalent to the kernel's [`gpiod_set_value`] API. + /// + /// [`gpiod_set_value`]: https://docs.kernel.org/driver-api/gpio/index= .html#c.gpiod_set_value + #[inline] + pub fn set_value(&self, value: LogicalLineLevel) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_set_value`]. + to_result(unsafe { bindings::gpiod_set_value(self.as_raw(), value.= as_c_int()) }) + } + + /// Get the physical GPIO value. + /// + /// Equivalent to the kernel's [`gpiod_get_raw_value`] API. + /// + /// [`gpiod_get_raw_value`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_get_raw= _value + #[inline] + pub fn get_raw_value(&self) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_get_raw_value`]. + let ret =3D unsafe { bindings::gpiod_get_raw_value(self.as_raw()) = }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + PhysicalLineLevel::try_from(ret) + } + } + + /// Assign the physical value. + /// + /// Equivalent to the kernel's [`gpiod_set_raw_value`] API. + /// + /// [`gpiod_set_raw_value`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_set_raw= _value + #[inline] + pub fn set_raw_value(&self, value: PhysicalLineLevel) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_set_raw_value`]. + to_result(unsafe { bindings::gpiod_set_raw_value(self.as_raw(), va= lue.as_c_int()) }) + } + + /// Get the logical GPIO value. + /// + /// Equivalent to the kernel's [`gpiod_get_value_cansleep`] API. + /// + /// [`gpiod_get_value_cansleep`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_get_val= ue_cansleep + #[inline] + pub fn get_value_cansleep(&self) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_get_value_cansleep`]. + let ret =3D unsafe { bindings::gpiod_get_value_cansleep(self.as_ra= w()) }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + LogicalLineLevel::try_from(ret) + } + } + + /// Assign the logical value. + /// + /// Equivalent to the kernel's [`gpiod_set_value_cansleep`] API. + /// + /// [`gpiod_set_value_cansleep`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_set_val= ue_cansleep + #[inline] + pub fn set_value_cansleep(&self, value: LogicalLineLevel) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_set_value_cansleep`]. + to_result(unsafe { bindings::gpiod_set_value_cansleep(self.as_raw(= ), value.as_c_int()) }) + } + + /// Get the physical GPIO value. + /// + /// Equivalent to the kernel's [`gpiod_get_raw_value_cansleep`] API. + /// + /// [`gpiod_get_raw_value_cansleep`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_get_raw= _value_cansleep + #[inline] + pub fn get_raw_value_cansleep(&self) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_get_raw_value_cansleep`]. + let ret =3D unsafe { bindings::gpiod_get_raw_value_cansleep(self.a= s_raw()) }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + PhysicalLineLevel::try_from(ret) + } + } + + /// Assign the physical value. + /// + /// Equivalent to the kernel's [`gpiod_set_raw_value_cansleep`] API. + /// + /// [`gpiod_set_raw_value_cansleep`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_set_raw= _value_cansleep + #[inline] + pub fn set_raw_value_cansleep(&self, value: PhysicalLineLevel) -> Resu= lt { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_set_raw_value_cansleep`]. + to_result(unsafe { + bindings::gpiod_set_raw_value_cansleep(self.as_raw(), value.as= _c_int()) + }) + } + + /// Test whether the GPIO is active-low or not. + /// + /// Equivalent to the kernel's [`gpiod_is_active_low`] API. + /// + /// [`gpiod_is_active_low`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_is_acti= ve_low + #[inline] + pub fn is_active_low(&self) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_is_active_low`]. + match unsafe { bindings::gpiod_is_active_low(self.as_raw()) } { + 0 =3D> Ok(false), + 1 =3D> Ok(true), + err =3D> Err(Error::from_errno(err)), + } + } + + /// Report whether gpio value access may sleep or not. + /// + /// Equivalent to the kernel's [`gpiod_cansleep`] API. + /// + /// [`gpiod_cansleep`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_cansleep + #[inline] + pub fn cansleep(&self) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`gpiod_cansleep`]. + match unsafe { bindings::gpiod_cansleep(self.as_raw()) } { + 0 =3D> Ok(false), + 1 =3D> Ok(true), + err =3D> Err(Error::from_errno(err)), + } + } +} + +impl Drop for GpioDesc { + fn drop(&mut self) { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for [`gpiod_put`]. + unsafe { bindings::gpiod_put(self.as_raw()) }; + } +} + +/// A reference-counted optional gpio descriptor. +/// +/// A lightweight wrapper around an optional [`GpioDesc`]. An [`OptionalGp= ioDesc`] represents +/// a [`GpioDesc`] that a driver can function without but may improve perf= ormance or enable +/// additional features when available. +/// +/// # Invariants +/// +/// An [`OptionalGpioDesc`] instance encapsulates a [`GpioDesc`] with eith= er a valid +/// [`struct gpio_desc`] or `NULL` pointer. +/// +/// Instances of this type are reference-counted. Calling [`OptionalGpioDe= sc::get`] ensures that +/// the allocation remains valid for the lifetime of the [`OptionalGpioDes= c`]. +/// +/// # Examples +/// +/// The following example demonstrates how to obtain and configure an opti= onal GPIO for a +/// device. The code functions correctly whether or not the GPIO is availa= ble. +/// +/// ``` +/// use crate::{ +/// device::Device, +/// error::Result, +/// gpio::{ +/// consumer::{ +/// OptionalGpioDesc, +/// GpiodFlags, // +/// }, +/// LogicalLineLevel, // +/// }, // +/// }; +/// +/// fn examine_gpio(dev: &Device) -> Result { +/// let gpiod =3D OptionalGpioDesc::get(dev, Some(c"reset"), GpiodFlag= s::ASIS)?; +/// +/// gpiod.set_value(LogicalLineLevel::Inactive)?; +/// +/// gpiod.set_value(LogicalLineLevel::Active)?; +/// +/// Ok(()) +/// } +/// ``` +/// +/// [`struct gpio_desc`]: https://docs.kernel.org/driver-api/gpio/consumer= .html +pub struct OptionalGpioDesc(GpioDesc); + +impl OptionalGpioDesc { + /// Gets [`OptionalGpioDesc`] corresponding to a [`Device`] and a conn= ection id. + /// + /// Equivalent to the kernel's [`gpiod_get_optional`] API. + /// + /// [`gpiod_get_optional`]: + /// https://docs.kernel.org/driver-api/gpio/index.html#c.gpiod_get_opt= ional + pub fn get(dev: &Device, name: Option<&CStr>, flags: GpiodFlags) -> Re= sult { + let con_id =3D name.map_or(ptr::null(), |n| n.as_char_ptr()); + + // SAFETY: It is safe to call [`gpiod_get_optional`] for a valid d= evice pointer. + // + // INVARIANT: The reference-count is decremented when [`OptionalGp= ioDesc`] goes out of + // scope. + Ok(Self(GpioDesc(from_err_ptr(unsafe { + bindings::gpiod_get_optional(dev.as_raw(), con_id, flags.into_= inner()) + })?))) + } +} + +// Make [`OptionalGpioDesc`] behave like [`GpioDesc`]. +impl Deref for OptionalGpioDesc { + type Target =3D GpioDesc; + + fn deref(&self) -> &GpioDesc { + &self.0 + } +} --=20 2.50.1 From nobody Sat Sep 26 01:04:55 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 14AAB370AF6 for ; Sun, 6 Sep 2026 08:46:25 +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=1788684388; cv=none; b=W66O23n7ktjd/a8GkRkHy0wh2C2eeZoKOYt0cB5cNFEfdUdwv34mQb8RUmJnDNpprk1DPwGhyFwA+wakbBJ/xxiU93oysD1E/NF9gl3vSEl6xwxSks6M/Dgh7+q5CrlqCB1r9fRgJ/a6aGlwPy2zWsyU6y0WapLa87xf7Bm4DBw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788684388; c=relaxed/simple; bh=EAwiGhsNfNUoTWCfH5MKHO2VRQhaq1dnt2ypoyVNiWg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WaZPIrxqX1vvz2BiBAitRmXrP8tDsU6gbUFIhUqMPXWH8cY0AyrboZCJNI/9hMUbKBYtnkH6eZ6tvD+lhk7TkvtLI93SGYiDUIAtwmvveXYHHRLnWBxzxP49l8XBxwO0HyeOcMorSh3FsT1ti0PCJJxFm70P7EwvldVfcrKbvm8= 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=HXriYoCS; 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="HXriYoCS" Received: by mail-pg1-f182.google.com with SMTP id 41be03b00d2f7-cc1a4c62804so1980050a12.3 for ; Sun, 06 Sep 2026 01:46:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788684385; x=1789289185; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Y5IIaAx3QztQSmArELd1kmaa4AxdD+imzocVdEF8EdM=; b=HXriYoCSKM21a5H9MmP+Id9gCuGSBw2ZptUfmvCIPaYNnovQurrm6udYtDT/jotBk2 fKUCZXmJHiomt1mBsyJ4P/E6CIvG8JLn3K+rov0P2/GbySOZLipzqJrn1o747xKdNLTG fa/qN5ttDexkHTbZ68D1MYMYc/a0PNUnJvN5CW/k1eGDLQ2NHDGcbZ1n/K5ce1jU1JIl axYIxexXxqvJTR+dxsJrOmXBEXRrNNv+VlQ9HWTdUShpzdCz8YAUTKp3ESl8QCG98EbR hF3JgmG6kIUsWy1fpWMX5Mu//TUcUrcv9/+JDfhUKrOt/hAQx4iv41K6guer2Lug+nWZ SiRA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788684385; x=1789289185; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=Y5IIaAx3QztQSmArELd1kmaa4AxdD+imzocVdEF8EdM=; b=Z2gci4VnfBgY9aRd8VMJ8VUzjnTafzPlKYjK923enEeTTq89BVd0jUyagTvWTbEueu CYeHLZX2gTIoz/izO6OxomkhOm3JIuDKdwoTNMlmMSIW/xz7XFeOzWtEomccDYAzsqTT Y0EdHs9QGz4CWYZaJMZkHsOoD+wg7S4t5Sa554Q30O/5FNTB5OMRGVIqONIshUPA3C/c ig0SmcMpKkEUBJBMehsjjVR8TMBUCXGpTXthLesbE/I4eTNdNApPsfWNaGH2iYkEsRuZ 5tyZmPNcaWUGPrjnUbo7CQZch/u7wSoeJuvmbmep9oZIHXQm52rUGO+UO67+epjCNtsP OxkQ== X-Gm-Message-State: AFuF++nAUcrUd38mMf5rqU0IDIHrIvi7lpoUeXEJQamJOBcx1EkIxbCR Ob/Qs3mn2WjGgyMXZxsqSRusylcPalD1rMjhhTWABJgKqDlQFIkncWkh X-Gm-Gg: AYBFou1wjxgHkQCxnRACN6dpGUy22X8L37lcjFCWIyoHEc4fu/696frbeozjJ1hzGhe /L+HOJN6kXNyQtzjsJNBftHkek6kqjb326K8/DaNTF8K2L9BIRrhTXdKA1PWMaw6aBPwXSJF8Pd F8EEnm4auwfmkbOcmWZkl6LED6v7KCThUWrt5POCkJob3HHWAZaS3tdYo5yz8ulQOPy12iOLGhj 5hmPOkXAlzbhmOGrqbFTiXKrwiYGybRXbw6FFGYdTxSwQlbtHIpdrc2P8kCMyCfocqPKnmRADDx 16kgRPqBoajvRMlyJTEN4U3wdoNHk/eqPSsHteFA9ek3p+1YlFEM+yXM9xsdflp1LTtSHEYRf/u kn0vxWS0TQXB2LoQkmbqLtadERXBAoInEHFBSdu9JEj0akozbAl55Yv4uk71Agb2lsQVfH/uzFa CqMayNMtINWampkWz1oJzNSP3rF138elkbR9NyteImnoDtISJBlqo4qm1WMMZwNMnP9dbBuZuAV LWG37Ml X-Received: by 2002:a05:6a21:8202:b0:3c3:7a0a:18a6 with SMTP id adf61e73a8af0-3da3a11eee0mr23307025637.18.1788684385308; Sun, 06 Sep 2026 01:46:25 -0700 (PDT) Received: from localhost (madb688426.ap.nuro.jp. [219.104.132.38]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-cc46da5acabsm1204179a12.8.2026.09.06.01.46.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Sep 2026 01:46:24 -0700 (PDT) From: Kohei Ito Date: Sun, 06 Sep 2026 17:45:51 +0900 Subject: [PATCH 3/3] sample: rust: Add GPIO consumer sample driver 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 Message-Id: <20260906-add-rust-gpio-consumer-v1-3-24d192f93760@gmail.com> References: <20260906-add-rust-gpio-consumer-v1-0-24d192f93760@gmail.com> In-Reply-To: <20260906-add-rust-gpio-consumer-v1-0-24d192f93760@gmail.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-gpio@vger.kernel.org, Kohei Ito X-Mailer: b4 0.15.2 Add a sample driver to demonstrate the use of the Rust GPIO APIs. Signed-off-by: Kohei Ito --- samples/rust/Kconfig | 11 +++ samples/rust/Makefile | 1 + samples/rust/rust_gpio_consumer.rs | 156 +++++++++++++++++++++++++++++++++= ++++ 3 files changed, 168 insertions(+) diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index c49ab9106345..1085d90c87f3 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -161,6 +161,17 @@ config SAMPLE_RUST_DRIVER_AUXILIARY =20 If unsure, say N. =20 +config SAMPLE_RUST_GPIO_CONSUMER + tristate "GPIO Consumer Driver" + depends on GPIOLIB + help + This option builds the Rust GPIO consumer sample. + + To compile this as a module, choose M here: + the module will be called rust_gpio_consumer. + + If unsure, say N. + config SAMPLE_RUST_SOC tristate "SoC Driver" select SOC_BUS diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 6c0aaa58cccc..1cf181dea6d3 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB) +=3D rust_driver_us= b.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) +=3D rust_driver_faux.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY) +=3D rust_driver_auxiliary.o obj-$(CONFIG_SAMPLE_RUST_CONFIGFS) +=3D rust_configfs.o +obj-$(CONFIG_SAMPLE_RUST_GPIO_CONSUMER) +=3D rust_gpio_consumer.o obj-$(CONFIG_SAMPLE_RUST_SOC) +=3D rust_soc.o =20 rust_print-y :=3D rust_print_main.o rust_print_events.o diff --git a/samples/rust/rust_gpio_consumer.rs b/samples/rust/rust_gpio_co= nsumer.rs new file mode 100644 index 000000000000..b70b305126fc --- /dev/null +++ b/samples/rust/rust_gpio_consumer.rs @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust GPIO consumer driver sample. + +use kernel::{ + device::{ + self, + Core, // + }, + gpio::{ + self, + consumer::{ + GpioDesc, + GpiodFlags, // + }, // + }, + of, + platform, + prelude::*, + time::{ + delay::fsleep, + Delta, // + }, // +}; + +struct SampleDriver; + +impl platform::Driver for SampleDriver { + type IdInfo =3D (); + type Data<'bound> =3D Self; + const OF_ID_TABLE: Option> =3D Some(&OF_TABL= E); + + fn probe<'bound>( + pdev: &'bound platform::Device>, + _info: Option<&'bound Self::IdInfo>, + ) -> impl PinInit + 'bound { + let dev =3D pdev.as_ref(); + + dev_dbg!(dev, "Probe Rust GPIO consumer driver sample.\n"); + + Self::examine_gpio(dev)?; + + Ok(Self) + } +} + +type GetValue =3D fn(&GpioDesc) -> Result; +type GetRawValue =3D fn(&GpioDesc) -> Result; +type SetValue =3D fn(&GpioDesc, gpio::LogicalLineLevel) -> Result<()>; +type SetRawValue =3D fn(&GpioDesc, gpio::PhysicalLineLevel) -> Result<()>; + +impl SampleDriver { + fn examine_gpio(dev: &device::Device) -> Result { + let gpiod =3D GpioDesc::get(dev, None, GpiodFlags::ASIS)?; + + let cansleep =3D gpiod.cansleep()?; + let (get_value, get_raw_value, set_value, set_raw_value): ( + GetValue, + GetRawValue, + SetValue, + SetRawValue, + ) =3D if cansleep { + ( + |gpiod| gpiod.get_value_cansleep(), + |gpiod| gpiod.get_raw_value_cansleep(), + |gpiod, value| gpiod.set_value_cansleep(value), + |gpiod, value| gpiod.set_raw_value_cansleep(value), + ) + } else { + ( + |gpiod| gpiod.get_value(), + |gpiod| gpiod.get_raw_value(), + |gpiod, value| gpiod.set_value(value), + |gpiod, value| gpiod.set_raw_value(value), + ) + }; + + let pr_status =3D || -> Result<()> { + dev_info!( + dev, + "direction: {}, value: {}, raw value: {}\n", + gpiod.get_direction()?, + get_value(&gpiod)?, + get_raw_value(&gpiod)? + ); + Ok(()) + }; + + let active_low =3D gpiod.is_active_low()?; + + dev_info!( + dev, + "got the GPIO ({}{})\n", + if active_low { + "active low" + } else { + "active high" + }, + if cansleep { ", sleepy" } else { "" } + ); + pr_status()?; + + gpiod.direction_output(gpio::LogicalLineLevel::Inactive)?; + dev_info!(dev, "line is inactivated\n"); + pr_status()?; + + fsleep(Delta::from_millis(1)); + + set_value(&gpiod, gpio::LogicalLineLevel::Active)?; + dev_info!(dev, "line is activated\n"); + pr_status()?; + + fsleep(Delta::from_millis(1)); + + set_value(&gpiod, gpio::LogicalLineLevel::Inactive)?; + dev_info!(dev, "GPIO: line is inactivated\n"); + pr_status()?; + + fsleep(Delta::from_millis(1)); + + let level =3D get_raw_value(&gpiod)?; + + gpiod.direction_input()?; + dev_info!(dev, "line is input mode\n"); + pr_status()?; + + fsleep(Delta::from_millis(1)); + + gpiod.direction_output_raw(!level)?; + dev_info!(dev, "line is toggled\n"); + pr_status()?; + + fsleep(Delta::from_millis(1)); + + set_raw_value(&gpiod, !!level)?; + dev_info!(dev, "line is toggled\n"); + pr_status()?; + + Ok(()) + } +} + +kernel::of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + ::IdInfo, + [(of::DeviceId::new(c"test,rust-gpio-consumer"), ())] +); + +kernel::module_platform_driver! { + type: SampleDriver, + name: "rust_gpio_consumer", + authors: ["Kohei Ito"], + description: "Rust GPIO consumer driver", + license: "GPL v2", +} --=20 2.50.1