From: Bruce Robertson <brucer42@gmail.com>
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 <sre@kernel.org>,
Miguel Ojeda <ojeda@kernel.org>,
Igor Korotin <igor.korotin@linux.dev>,
Gary Guo <gary@garyguo.net>, Tamir Duberstein <tamird@kernel.org>,
Alice Ryhl <aliceryhl@google.com>, Boqun Feng <boqun@kernel.org>,
Bruce Robertson <brucer42@gmail.com>
Subject: [RFC PATCH 2/3] rust: power_supply: add power supply class abstraction
Date: Wed, 8 Jul 2026 21:47:37 +0000 [thread overview]
Message-ID: <20260708214738.25008-3-brucer42@gmail.com> (raw)
In-Reply-To: <20260708214738.25008-1-brucer42@gmail.com>
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 <brucer42@gmail.com>
---
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 <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/poll.h>
+#include <linux/power_supply.h>
#include <linux/property.h>
#include <linux/pwm.h>
#include <linux/random.h>
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/<NAME>`.
+ 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<T: Driver + 'static>(
+ 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::<bindings::device>();
+ // SAFETY: that device is valid while the (devm) supply lives.
+ let dev: &Device<device::CoreInternal<'_>> = 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::<T>() };
+ // 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<bindings::power_supply_desc>,
+}
+
+// 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<T: Driver + 'static>(dev: &Device) -> Result<Registration> {
+ 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::<T>);
+
+ // 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::<kernel::ffi::c_void>(),
+ ..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
next prev parent reply other threads:[~2026-07-08 21:48 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-08 21:47 [RFC PATCH 0/3] rust: power_supply class abstraction and SMB347 charger driver Bruce Robertson
2026-07-08 21:47 ` [RFC PATCH 1/3] rust: i2c: add SMBus byte transfer helpers Bruce Robertson
2026-07-11 10:34 ` Igor Korotin
2026-07-11 12:06 ` Danilo Krummrich
2026-07-08 21:47 ` Bruce Robertson [this message]
2026-07-09 9:04 ` [RFC PATCH 2/3] rust: power_supply: add power supply class abstraction Alice Ryhl
2026-07-08 21:47 ` [RFC PATCH 3/3] power: supply: add Rust SMB347 charger driver Bruce Robertson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260708214738.25008-3-brucer42@gmail.com \
--to=brucer42@gmail.com \
--cc=aliceryhl@google.com \
--cc=boqun@kernel.org \
--cc=gary@garyguo.net \
--cc=igor.korotin@linux.dev \
--cc=linux-i2c@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=sre@kernel.org \
--cc=tamird@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.