On Thu, Jun 05, 2025 at 05:27:26PM +0100, Igor Korotin wrote: > From: Igor Korotin <igor.korotin.linux@xxxxxxxxx> > > Extend the `Adapter` trait to support ACPI device identification. > > This mirrors the existing Open Firmware (OF) support (`of_id_table`) and > enables Rust drivers to match and retrieve ACPI-specific device data > when `CONFIG_ACPI` is enabled. > > To avoid breaking compilation, a stub implementation of `acpi_id_table()` > is added to the Platform adapter; the full implementation will be provided > in a subsequent patch. > --- > rust/bindings/bindings_helper.h | 1 + > rust/kernel/driver.rs | 58 ++++++++++++++++++++++++++++++--- > rust/kernel/platform.rs | 5 +++ > 3 files changed, 60 insertions(+), 4 deletions(-) > > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h > index e0bcd130b494..d974fc6c141f 100644 > --- a/rust/bindings/bindings_helper.h > +++ b/rust/bindings/bindings_helper.h > @@ -6,6 +6,7 @@ > * Sorted alphabetically. > */ > > +#include <linux/acpi.h> > #include <kunit/test.h> > #include <linux/blk-mq.h> > #include <linux/blk_types.h> > diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs > index ec9166cedfa7..d4098596188a 100644 > --- a/rust/kernel/driver.rs > +++ b/rust/kernel/driver.rs > @@ -6,7 +6,7 @@ > //! register using the [`Registration`] class. > > use crate::error::{Error, Result}; > -use crate::{device, of, str::CStr, try_pin_init, types::Opaque, ThisModule}; > +use crate::{device, of, acpi, str::CStr, try_pin_init, types::Opaque, ThisModule}; > use core::pin::Pin; > use pin_init::{pin_data, pinned_drop, PinInit}; > > @@ -141,6 +141,38 @@ pub trait Adapter { > /// The type holding driver private data about each device id supported by the driver. > type IdInfo: 'static; > > + /// The [`acpi::IdTable`] of the corresponding driver > + fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>>; > + > + /// Returns the driver's private data from the matching entry in the [`acpi::IdTable`], if any. > + /// > + /// If this returns `None`, it means there is no match with an entry in the [`acpi::IdTable`]. > + #[cfg(CONFIG_ACPI)] > + fn acpi_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> { > + let table = Self::acpi_id_table()?; > + > + // SAFETY: > + // - `table` has static lifetime, hence it's valid for read, > + // - `dev` is guaranteed to be valid while it's alive, and so is `pdev.as_ref().as_raw()`. > + let raw_id = unsafe { bindings::acpi_match_device(table.as_ptr(), dev.as_raw()) }; > + > + if raw_id.is_null() { > + None > + } else { > + // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and > + // does not add additional invariants, so it's safe to transmute. > + let id = unsafe { &*raw_id.cast::<acpi::DeviceId>() }; > + > + Some(table.info(<acpi::DeviceId as crate::device_id::RawDeviceId>::index(id))) > + } > + } > + > + #[cfg(not(CONFIG_ACPI))] > + #[allow(missing_docs)] > + fn acpi_id_info(_dev: &device::Device) -> Option<&'static Self::IdInfo> { > + None > + } > + > /// The [`of::IdTable`] of the corresponding driver. > fn of_id_table() -> Option<of::IdTable<Self::IdInfo>>; > > @@ -178,9 +210,27 @@ fn of_id_info(_dev: &device::Device) -> Option<&'static Self::IdInfo> { > /// If this returns `None`, it means that there is no match in any of the ID tables directly > /// associated with a [`device::Device`]. > fn id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> { > - let id = Self::of_id_info(dev); > - if id.is_some() { > - return id; > + // SAFETY: `id_info` is called from `Adapter::probe_callback` with a valid `dev` argument. > + let fwnode = unsafe{ (*dev.as_raw()).fwnode}; There is an abstraction for FwNode on the list [1] that I plan to merge soon. Generally, it would make sense to build on top of that. However, I don't understand why we need this and the subsequent is_acpi_device_node() and is_of_node() checks. Instead, I think we can keep the existing code and just add the following. let id = Self::acpi_id_info(dev); if id.is_some() { return id; } [1] https://lore.kernel.org/lkml/20250530192856.1177011-1-remo@xxxxxxxxxxx/ > + > + // SAFETY: `bindings::is_acpi_device_node` checks `fwnode` before accessing `fwnode->ops`, > + // and only compares it with the address of `acpi_device_fwnode_ops`. > + if unsafe { bindings::is_acpi_device_node(fwnode) } { As mentioned above, I think we don't need this check. > + let id = Self::acpi_id_info(dev); > + > + if id.is_some() { > + return id; > + } > + } > + > + // SAFETY: `bindings::is_of_node` checks `fwnode` before accessing `fwnode->ops`, > + // and only compares it with the address of `of_fwnode_ops`. > + if unsafe { bindings::is_of_node(fwnode) } { Same here. > + let id = Self::of_id_info(dev); > + > + if id.is_some() { > + return id; > + } > } > > None > diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs > index fd4a494f30e8..3cc9fe6ccfcf 100644 > --- a/rust/kernel/platform.rs > +++ b/rust/kernel/platform.rs > @@ -5,6 +5,7 @@ > //! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h) > > use crate::{ > + acpi, > bindings, device, driver, > error::{to_result, Result}, > of, > @@ -95,6 +96,10 @@ impl<T: Driver + 'static> driver::Adapter for Adapter<T> { > fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> { > T::OF_ID_TABLE > } > + > + fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> { > + None > + } > } > > /// Declares a kernel module that exposes a single platform driver. > -- > 2.43.0 >