mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-16 21:15:10 -05:00
The driver model defines the lifetime of the private data stored in (and owned by) a bus device to be valid from when the driver is bound to a device (i.e. from successful probe()) until the driver is unbound from the device. This is already taken care of by the Rust implementation of the driver model. However, we still ask drivers to return a Result<Pin<KBox<Self>>> from probe(). Unlike in C, where we do not have the concept of initializers, but rather deal with uninitialized memory, drivers can just return an impl PinInit<Self, Error> instead. This contributes to more clarity to the fact that a driver returns it's device private data in probe() and the Rust driver model owns the data, manages the lifetime and - considering the lifetime - provides (safe) accessors for the driver. Hence, let probe() functions return an impl PinInit<Self, Error> instead of Result<Pin<KBox<Self>>>. Reviewed-by: Alice Ryhl <aliceryhl@google.com> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
192 lines
5.5 KiB
Rust
192 lines
5.5 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! Rust Platform driver sample.
|
|
|
|
//! ACPI match table test
|
|
//!
|
|
//! This demonstrates how to test an ACPI-based Rust platform driver using QEMU
|
|
//! with a custom SSDT.
|
|
//!
|
|
//! Steps:
|
|
//!
|
|
//! 1. **Create an SSDT source file** (`ssdt.dsl`) with the following content:
|
|
//!
|
|
//! ```asl
|
|
//! DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001)
|
|
//! {
|
|
//! Scope (\_SB)
|
|
//! {
|
|
//! Device (T432)
|
|
//! {
|
|
//! Name (_HID, "LNUXBEEF") // ACPI hardware ID to match
|
|
//! Name (_UID, 1)
|
|
//! Name (_STA, 0x0F) // Device present, enabled
|
|
//! Name (_CRS, ResourceTemplate ()
|
|
//! {
|
|
//! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000)
|
|
//! })
|
|
//! }
|
|
//! }
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! 2. **Compile the table**:
|
|
//!
|
|
//! ```sh
|
|
//! iasl -tc ssdt.dsl
|
|
//! ```
|
|
//!
|
|
//! This generates `ssdt.aml`
|
|
//!
|
|
//! 3. **Run QEMU** with the compiled AML file:
|
|
//!
|
|
//! ```sh
|
|
//! qemu-system-x86_64 -m 512M \
|
|
//! -enable-kvm \
|
|
//! -kernel path/to/bzImage \
|
|
//! -append "root=/dev/sda console=ttyS0" \
|
|
//! -hda rootfs.img \
|
|
//! -serial stdio \
|
|
//! -acpitable file=ssdt.aml
|
|
//! ```
|
|
//!
|
|
//! Requirements:
|
|
//! - The `rust_driver_platform` must be present either:
|
|
//! - built directly into the kernel (`bzImage`), or
|
|
//! - available as a `.ko` file and loadable from `rootfs.img`
|
|
//!
|
|
//! 4. **Verify it worked** by checking `dmesg`:
|
|
//!
|
|
//! ```
|
|
//! rust_driver_platform LNUXBEEF:00: Probed with info: '0'.
|
|
//! ```
|
|
//!
|
|
|
|
use kernel::{
|
|
acpi, c_str,
|
|
device::{
|
|
self,
|
|
property::{FwNodeReferenceArgs, NArgs},
|
|
Core,
|
|
},
|
|
of, platform,
|
|
prelude::*,
|
|
str::CString,
|
|
sync::aref::ARef,
|
|
};
|
|
|
|
struct SampleDriver {
|
|
pdev: ARef<platform::Device>,
|
|
}
|
|
|
|
struct Info(u32);
|
|
|
|
kernel::of_device_table!(
|
|
OF_TABLE,
|
|
MODULE_OF_TABLE,
|
|
<SampleDriver as platform::Driver>::IdInfo,
|
|
[(of::DeviceId::new(c_str!("test,rust-device")), Info(42))]
|
|
);
|
|
|
|
kernel::acpi_device_table!(
|
|
ACPI_TABLE,
|
|
MODULE_ACPI_TABLE,
|
|
<SampleDriver as platform::Driver>::IdInfo,
|
|
[(acpi::DeviceId::new(c_str!("LNUXBEEF")), Info(0))]
|
|
);
|
|
|
|
impl platform::Driver for SampleDriver {
|
|
type IdInfo = Info;
|
|
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
|
|
const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
|
|
|
|
fn probe(
|
|
pdev: &platform::Device<Core>,
|
|
info: Option<&Self::IdInfo>,
|
|
) -> impl PinInit<Self, Error> {
|
|
let dev = pdev.as_ref();
|
|
|
|
dev_dbg!(dev, "Probe Rust Platform driver sample.\n");
|
|
|
|
if let Some(info) = info {
|
|
dev_info!(dev, "Probed with info: '{}'.\n", info.0);
|
|
}
|
|
|
|
if dev.fwnode().is_some_and(|node| node.is_of_node()) {
|
|
Self::properties_parse(dev)?;
|
|
}
|
|
|
|
Ok(Self { pdev: pdev.into() })
|
|
}
|
|
}
|
|
|
|
impl SampleDriver {
|
|
fn properties_parse(dev: &device::Device) -> Result {
|
|
let fwnode = dev.fwnode().ok_or(ENOENT)?;
|
|
|
|
if let Ok(idx) =
|
|
fwnode.property_match_string(c_str!("compatible"), c_str!("test,rust-device"))
|
|
{
|
|
dev_info!(dev, "matched compatible string idx = {}\n", idx);
|
|
}
|
|
|
|
let name = c_str!("compatible");
|
|
let prop = fwnode.property_read::<CString>(name).required_by(dev)?;
|
|
dev_info!(dev, "'{name}'='{prop:?}'\n");
|
|
|
|
let name = c_str!("test,bool-prop");
|
|
let prop = fwnode.property_read_bool(c_str!("test,bool-prop"));
|
|
dev_info!(dev, "'{name}'='{prop}'\n");
|
|
|
|
if fwnode.property_present(c_str!("test,u32-prop")) {
|
|
dev_info!(dev, "'test,u32-prop' is present\n");
|
|
}
|
|
|
|
let name = c_str!("test,u32-optional-prop");
|
|
let prop = fwnode.property_read::<u32>(name).or(0x12);
|
|
dev_info!(dev, "'{name}'='{prop:#x}' (default = 0x12)\n",);
|
|
|
|
// A missing required property will print an error. Discard the error to
|
|
// prevent properties_parse from failing in that case.
|
|
let name = c_str!("test,u32-required-prop");
|
|
let _ = fwnode.property_read::<u32>(name).required_by(dev);
|
|
|
|
let name = c_str!("test,u32-prop");
|
|
let prop: u32 = fwnode.property_read(name).required_by(dev)?;
|
|
dev_info!(dev, "'{name}'='{prop:#x}'\n");
|
|
|
|
let name = c_str!("test,i16-array");
|
|
let prop: [i16; 4] = fwnode.property_read(name).required_by(dev)?;
|
|
dev_info!(dev, "'{name}'='{prop:?}'\n");
|
|
let len = fwnode.property_count_elem::<u16>(name)?;
|
|
dev_info!(dev, "'{name}' length is {len}\n",);
|
|
|
|
let name = c_str!("test,i16-array");
|
|
let prop: KVec<i16> = fwnode.property_read_array_vec(name, 4)?.required_by(dev)?;
|
|
dev_info!(dev, "'{name}'='{prop:?}' (KVec)\n");
|
|
|
|
for child in fwnode.children() {
|
|
let name = c_str!("test,ref-arg");
|
|
let nargs = NArgs::N(2);
|
|
let prop: FwNodeReferenceArgs = child.property_get_reference_args(name, nargs, 0)?;
|
|
dev_info!(dev, "'{name}'='{prop:?}'\n");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Drop for SampleDriver {
|
|
fn drop(&mut self) {
|
|
dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n");
|
|
}
|
|
}
|
|
|
|
kernel::module_platform_driver! {
|
|
type: SampleDriver,
|
|
name: "rust_driver_platform",
|
|
authors: ["Danilo Krummrich"],
|
|
description: "Rust Platform driver",
|
|
license: "GPL v2",
|
|
}
|