Linux Power Management development
 help / color / mirror / Atom feed
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


  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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox