From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) (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 7BED73F86FB for ; Wed, 8 Jul 2026 21:48:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.170 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783547320; cv=none; b=aKN9pi2Ac1vMd1w2+zGwy8nYafxt9XcySGgn8j4qmG1bUWkXyrxiICf/g8dA5FrvUG2FKlV6PFpYJvdIec45XeJ1bxOBx7J1nf8jFUp5lvBYqbWp867SDaCJ49PFGg2ddeDZlVfrRiua2od/5lEQXOnT/UTnlRhKW9ogO3h/Fjk= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783547320; c=relaxed/simple; bh=oBzYn/0he9cz2/wYEhSxbP/n2MENiOCwowkBueU0EHI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=HT694FBEXkB1S0cOgKXQ3CWen10gMVhv6EAVPNGuZwdYBbewYlW8OPFGqzx2AxmTB0KDk1DiDDM7aBGoqTZpZTGbHTzAM+ydtK9K+RvPxtK9WMtKFJ+ycPa+XXUyE7XVJU/bwVGvpaWtkCPlRaythcS0qzeAVq8FwuqoWNt9WVs= 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=WO8wvIaS; arc=none smtp.client-ip=209.85.214.170 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="WO8wvIaS" Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-2cace91f112so12470775ad.0 for ; Wed, 08 Jul 2026 14:48:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783547318; x=1784152118; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=CcHS4omCQhk6NJkHYXiTY8hUWJw218fa7Nr5nM8cK8s=; b=WO8wvIaSoJFMggpqfwmBgZe2JdJrXMskMHz5guokONGFElTeHr+s8dTYLu9ZqhO6DN 0j+57Q2RplQvi7n17P8zDJGPt+6wQ1WVGDsO0c1D9J6/XD8Loc2gYPnmTpzpjyiF6WtK aZ541SMZD6tT6y0OTJQA+Kyc36GOZ4uNUjSvBBbYj9DuaPImaCl4QKtx8Rozd/oWfnQV kZunitDST4BiPlEbRSobSWAa462DtE36Qv3Qd1r/3qFETQveEktAwopFNI/FBrlyWYAd W6i8yg1pmZqJcoQ3rh0d21ZFt5TG+TURL8BvrLdxWH/meoUrpl6FKPaXpf9O8at4Okyp mhNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783547318; x=1784152118; h=content-transfer-encoding:content-type: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 :content-type; bh=CcHS4omCQhk6NJkHYXiTY8hUWJw218fa7Nr5nM8cK8s=; b=i1nVP1cBn/t6k3KGaGAMjwBsN5KK5mKGQxKYttOReYb4hxLuYvTkdCiYYrHMiUKfZ8 7Yr2+gBqfp+M4rvUpqigDwtnoRwXGDykoaXDlqdKMrQBmXRMbgs7u/80UKcaoOruPixk A72mQcMicHE9bvXkfSGXjY6f8VnnX2qBVlZ9ftv5t8onFjDOnGPy85ZOdRMk3cH3hDCY qf1d6h4NsQMRaW+M5xnW9qHTe63imQHJEKggy3wToyxeyriOJ4kmFPNn16bzkC04yRIp il9uIVf1btJz9XYYZZIpMo9gV8pBrk62Jo9SNw60lqLGwROtKyWijTbCWfj1GPyUBamv GP8g== X-Forwarded-Encrypted: i=1; AHgh+Rr7Vjhf+H4YYXJYWQ5RUVVHYnq7I01RAduTsE9P/USpsxswr9lSPpR+EcHDrFjUC4qK6RdgaVJmpAsVKhE=@vger.kernel.org X-Gm-Message-State: AOJu0YzXWE4H8IvfIETjsdqybRVc+PqlMyYfAoa8S5fq3H2aIa9hLQM0 UZ4lY2tndc/b4f7wvWC5bv3EhXYcJcWOwn1yIl7a6MK803Z8FB9T/Brp X-Gm-Gg: AfdE7cmF66RhQPvVhNNz3vGlI7PFJ3HOB/GKgBsh9nJLaSO042rgAu/glWPHCUtGLJc mJoPnteGLbNsDyF444FUS+SwAil6rkPLh7/uwlJKv7wlxY5YjyaYkJyawVRi7Zo5YjBvPU8xBUK srfqLwxmfzwCtaFdhqfFn0nzU8zlMYn64QNWIMCRgyQGfpTonwM6SH34KR/MpSYsVv9/C5zBQk8 KV2PPLkmJFr8TdQ2woAPRj3SE3hKDszH7HoW/yOFvdGx/kXen2H53QpWZSNsJVQ0Bk8XHnh4Uhs CZT8nV9kptI+HeB7cY0GVF42mSXW8t8R+3XRAMzVnuR+L/pmApNWnkRdJT5PNtxpK0mYAcI7RVv lPxvYZhejh1UGZKgOU2hO56B6Y0g52gVXyvKvkc551cT1gOktB/NpNGJbxURURfJHGL/3Qt+JBh JgSzdiHtcPZogBxS6RIRALOZwzCEeUTYkMmyyD1JoJfxN6tn8ktFO50w2sDbXJ3Jxb48Tsd8nfu oXkayatdisdy/vyuN8hUS+zvbzrLMSqEsocPnZ5j8I0dkij4PDgKFLMc5hMzOenqchtGcKs+Qfq cl6pbrUm6rGDAB/9NCJff4fX2TyE5bERr7l2THzpxLZhBN7c7M/DdmQVzpN2cpDXYyjzqP2yQSQ cn6zpVrQ= X-Received: by 2002:a17:902:ff8e:b0:2ca:329:3da1 with SMTP id d9443c01a7336-2ccea3ca5eemr46255005ad.16.1783547317719; Wed, 08 Jul 2026 14:48:37 -0700 (PDT) Received: from ip-172-31-21-198.us-west-2.compute.internal (ec2-35-85-29-74.us-west-2.compute.amazonaws.com. [35.85.29.74]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2ccc9d3d946sm33733855ad.66.2026.07.08.14.48.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Jul 2026 14:48:37 -0700 (PDT) From: Bruce Robertson To: rust-for-linux@vger.kernel.org, linux-pm@vger.kernel.org, linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Sebastian Reichel , Miguel Ojeda , Igor Korotin , Gary Guo , Tamir Duberstein , Alice Ryhl , Boqun Feng , Bruce Robertson Subject: [RFC PATCH 2/3] rust: power_supply: add power supply class abstraction Date: Wed, 8 Jul 2026 21:47:37 +0000 Message-ID: <20260708214738.25008-3-brucer42@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260708214738.25008-1-brucer42@gmail.com> References: <20260708214738.25008-1-brucer42@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: 8bit There is no Rust abstraction for the power supply class in mainline or rust-next. Add a minimal one: - a Driver trait carrying the supply's name, type and property list, plus a get_property() method; - a generic extern "C" trampoline the power supply core calls back into, recovering the driver's private data via the parent device and dispatching to get_property(); - a Registration that owns the power_supply_desc and unregisters the supply on drop, freeing the descriptor in the correct order. Property, type and status constants are re-exported so drivers need not reach into the generated bindings directly. Signed-off-by: Bruce Robertson --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/kernel/lib.rs | 1 + rust/kernel/power_supply.rs | 133 ++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 rust/kernel/power_supply.rs diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..8e41f7917825 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21546,6 +21546,7 @@ F: Documentation/devicetree/bindings/power/supply/ F: drivers/power/supply/ F: include/linux/power/ F: include/linux/power_supply.h +F: rust/kernel/power_supply.rs F: tools/testing/selftests/power_supply/ POWERNV OPERATOR PANEL LCD DISPLAY DRIVER diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 1124785e210b..0bdaf7316140 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -78,6 +78,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 9512af7156df..5013bdd30361 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -106,6 +106,7 @@ pub mod pci; pub mod pid_namespace; pub mod platform; +pub mod power_supply; pub mod prelude; pub mod print; pub mod processor; diff --git a/rust/kernel/power_supply.rs b/rust/kernel/power_supply.rs new file mode 100644 index 000000000000..f466a89e7b3a --- /dev/null +++ b/rust/kernel/power_supply.rs @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Power supply class abstraction. +//! +//! C header: [`include/linux/power_supply.h`](srctree/include/linux/power_supply.h) + +use crate::{ + bindings, + device::{self, Device}, + error::*, + prelude::*, +}; + +/// A power-supply property id. +pub use bindings::power_supply_property as Property; +/// The value returned for a property query. +pub use bindings::power_supply_propval as PropertyValue; +/// A power-supply type. +pub use bindings::power_supply_type as Type; + +/// `POWER_SUPPLY_PROP_STATUS`. +pub const PROP_STATUS: Property = bindings::power_supply_property_POWER_SUPPLY_PROP_STATUS; +/// `POWER_SUPPLY_PROP_ONLINE`. +pub const PROP_ONLINE: Property = bindings::power_supply_property_POWER_SUPPLY_PROP_ONLINE; +/// `POWER_SUPPLY_TYPE_MAINS`. +pub const TYPE_MAINS: Type = bindings::power_supply_type_POWER_SUPPLY_TYPE_MAINS; +/// `POWER_SUPPLY_STATUS_CHARGING`. +pub const STATUS_CHARGING: i32 = bindings::POWER_SUPPLY_STATUS_CHARGING as i32; +/// `POWER_SUPPLY_STATUS_NOT_CHARGING`. +pub const STATUS_NOT_CHARGING: i32 = bindings::POWER_SUPPLY_STATUS_NOT_CHARGING as i32; +/// `POWER_SUPPLY_PROP_CHARGE_TYPE`. +pub const PROP_CHARGE_TYPE: Property = + bindings::power_supply_property_POWER_SUPPLY_PROP_CHARGE_TYPE; +/// `POWER_SUPPLY_CHARGE_TYPE_NONE`. +pub const CHARGE_TYPE_NONE: i32 = + bindings::power_supply_charge_type_POWER_SUPPLY_CHARGE_TYPE_NONE as i32; +/// `POWER_SUPPLY_CHARGE_TYPE_TRICKLE`. +pub const CHARGE_TYPE_TRICKLE: i32 = + bindings::power_supply_charge_type_POWER_SUPPLY_CHARGE_TYPE_TRICKLE as i32; +/// `POWER_SUPPLY_CHARGE_TYPE_FAST`. +pub const CHARGE_TYPE_FAST: i32 = + bindings::power_supply_charge_type_POWER_SUPPLY_CHARGE_TYPE_FAST as i32; + +/// A driver backing a power supply. +pub trait Driver { + /// sysfs name: appears at `/sys/class/power_supply/`. + const NAME: &'static CStr; + + /// The power-supply type (mains, USB, battery, …). + const TYPE: Type; + + /// The properties this supply can answer. + const PROPERTIES: &'static [Property]; + + /// Answer a single property query. + fn get_property(self: Pin<&Self>, psp: Property, val: &mut PropertyValue) -> Result; +} + +/// `get_property` trampoline: the C power supply core calls this and it +/// dispatches to [`Driver::get_property`]. +/// +/// # Safety +/// +/// May only be installed as the `get_property` field of a `power_supply_desc` +/// registered with `drv_data` set to the parent `*mut device`. The core +/// guarantees `psy` and `val` are valid for the duration of the call. +unsafe extern "C" fn get_property_trampoline( + psy: *mut bindings::power_supply, + psp: Property, + val: *mut PropertyValue, +) -> kernel::ffi::c_int { + // SAFETY: core passes a valid `psy`; we stored the parent device ptr as its drv_data. + let dev_ptr = unsafe { bindings::power_supply_get_drvdata(psy) }.cast::(); + // SAFETY: that device is valid while the (devm) supply lives. + let dev: &Device> = unsafe { Device::from_raw(dev_ptr) }; + // SAFETY: callbacks only fire after probe returned, so set_drvdata stored a `T`. + let data = unsafe { dev.drvdata_borrow::() }; + // SAFETY: core passes a valid `val` for the call. + let val = unsafe { &mut *val }; + from_result(|| { + data.get_property(psp, val)?; + Ok(0) + }) +} + +/// A registered power supply. Unregisters and frees its descriptor on drop. +pub struct Registration { + psy: *mut bindings::power_supply, + // Kept alive so the descriptor the core holds stays valid until unregister. + _desc: KBox, +} + +// SAFETY: `Registration` owns the `power_supply` and its descriptor; the raw +// pointer is only used to unregister on drop. Safe to send/share across threads. +unsafe impl Send for Registration {} +// SAFETY: as above — psy and descriptor are only accessed on drop, so sharing +// a `&Registration` across threads is sound. +unsafe impl Sync for Registration {} + +impl Drop for Registration { + fn drop(&mut self) { + // SAFETY: `psy` came from a successful `power_supply_register` and has not been + // unregistered. `power_supply_unregister` blocks until no callback is running; + // `_desc` (dropped immediately after) is then no longer referenced by the core. + unsafe { bindings::power_supply_unregister(self.psy) }; + } +} + +/// Register a power supply backed by driver `T` against `dev`. +pub fn register(dev: &Device) -> Result { + let mut desc = KBox::new(bindings::power_supply_desc::default(), GFP_KERNEL)?; + desc.name = T::NAME.as_char_ptr(); + desc.type_ = T::TYPE; + desc.properties = T::PROPERTIES.as_ptr(); + desc.num_properties = T::PROPERTIES.len(); + desc.get_property = Some(get_property_trampoline::); + + // Stable heap address; moving the KBox into `Registration` keeps this valid + // (moving a KBox moves the pointer, not the heap allocation). + let desc_ptr: *const bindings::power_supply_desc = &*desc; + + let cfg = bindings::power_supply_config { + drv_data: dev.as_raw().cast::(), + ..Default::default() + }; + + // SAFETY: `dev` valid; `desc_ptr` points into `desc`, kept alive in the returned + // `Registration` and freed only after `power_supply_unregister`; `cfg` valid for the call. + let psy = unsafe { bindings::power_supply_register(dev.as_raw(), desc_ptr, &cfg) }; + let psy = from_err_ptr(psy)?; + + Ok(Registration { psy, _desc: desc }) +} -- 2.43.0