mirror of
https://github.com/torvalds/linux.git
synced 2025-11-01 09:09:47 +02:00
Adds a new sample driver that demonstrates the debugfs APIs. The driver creates a directory in debugfs and populates it with a few files: - A read-only file that displays a fwnode property. - A read-write file that exposes an atomic counter. - A read-write file that exposes a custom struct. This sample serves as a basic example of how to use the `debugfs::Dir` and `debugfs::File` APIs to create and manage debugfs entries. Signed-off-by: Matthew Maurer <mmaurer@google.com> Tested-by: Dirk Behme <dirk.behme@de.bosch.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Link: https://lore.kernel.org/r/20250904-debugfs-rust-v11-5-7d12a165685a@google.com [ Change ACPI ID "LNUXDEBF" to "LNUXBEEF". - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
151 lines
4.3 KiB
Rust
151 lines
4.3 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
// Copyright (C) 2025 Google LLC.
|
|
|
|
//! Sample DebugFS exporting platform driver
|
|
//!
|
|
//! To successfully probe this driver with ACPI, use an ssdt that looks like
|
|
//!
|
|
//! ```dsl
|
|
//! 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 (_DSD, Package () { // Sample attribute
|
|
//! ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
|
|
//! Package() {
|
|
//! Package(2) {"compatible", "sample-debugfs"}
|
|
//! }
|
|
//! })
|
|
//! Name (_CRS, ResourceTemplate ()
|
|
//! {
|
|
//! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000)
|
|
//! })
|
|
//! }
|
|
//! }
|
|
//! }
|
|
//! ```
|
|
|
|
use core::str::FromStr;
|
|
use core::sync::atomic::AtomicUsize;
|
|
use core::sync::atomic::Ordering;
|
|
use kernel::c_str;
|
|
use kernel::debugfs::{Dir, File};
|
|
use kernel::new_mutex;
|
|
use kernel::prelude::*;
|
|
use kernel::sync::Mutex;
|
|
|
|
use kernel::{acpi, device::Core, of, platform, str::CString, types::ARef};
|
|
|
|
kernel::module_platform_driver! {
|
|
type: RustDebugFs,
|
|
name: "rust_debugfs",
|
|
authors: ["Matthew Maurer"],
|
|
description: "Rust DebugFS usage sample",
|
|
license: "GPL",
|
|
}
|
|
|
|
#[pin_data]
|
|
struct RustDebugFs {
|
|
pdev: ARef<platform::Device>,
|
|
// As we only hold these for drop effect (to remove the directory/files) we have a leading
|
|
// underscore to indicate to the compiler that we don't expect to use this field directly.
|
|
_debugfs: Dir,
|
|
#[pin]
|
|
_compatible: File<CString>,
|
|
#[pin]
|
|
counter: File<AtomicUsize>,
|
|
#[pin]
|
|
inner: File<Mutex<Inner>>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Inner {
|
|
x: u32,
|
|
y: u32,
|
|
}
|
|
|
|
impl FromStr for Inner {
|
|
type Err = Error;
|
|
fn from_str(s: &str) -> Result<Self> {
|
|
let mut parts = s.split_whitespace();
|
|
let x = parts
|
|
.next()
|
|
.ok_or(EINVAL)?
|
|
.parse::<u32>()
|
|
.map_err(|_| EINVAL)?;
|
|
let y = parts
|
|
.next()
|
|
.ok_or(EINVAL)?
|
|
.parse::<u32>()
|
|
.map_err(|_| EINVAL)?;
|
|
if parts.next().is_some() {
|
|
return Err(EINVAL);
|
|
}
|
|
Ok(Inner { x, y })
|
|
}
|
|
}
|
|
|
|
kernel::acpi_device_table!(
|
|
ACPI_TABLE,
|
|
MODULE_ACPI_TABLE,
|
|
<RustDebugFs as platform::Driver>::IdInfo,
|
|
[(acpi::DeviceId::new(c_str!("LNUXBEEF")), ())]
|
|
);
|
|
|
|
impl platform::Driver for RustDebugFs {
|
|
type IdInfo = ();
|
|
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
|
|
const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
|
|
|
|
fn probe(
|
|
pdev: &platform::Device<Core>,
|
|
_info: Option<&Self::IdInfo>,
|
|
) -> Result<Pin<KBox<Self>>> {
|
|
let result = KBox::try_pin_init(RustDebugFs::new(pdev), GFP_KERNEL)?;
|
|
// We can still mutate fields through the files which are atomic or mutexed:
|
|
result.counter.store(91, Ordering::Relaxed);
|
|
{
|
|
let mut guard = result.inner.lock();
|
|
guard.x = guard.y;
|
|
guard.y = 42;
|
|
}
|
|
Ok(result)
|
|
}
|
|
}
|
|
|
|
impl RustDebugFs {
|
|
fn build_counter(dir: &Dir) -> impl PinInit<File<AtomicUsize>> + '_ {
|
|
dir.read_write_file(c_str!("counter"), AtomicUsize::new(0))
|
|
}
|
|
|
|
fn build_inner(dir: &Dir) -> impl PinInit<File<Mutex<Inner>>> + '_ {
|
|
dir.read_write_file(c_str!("pair"), new_mutex!(Inner { x: 3, y: 10 }))
|
|
}
|
|
|
|
fn new(pdev: &platform::Device<Core>) -> impl PinInit<Self, Error> + '_ {
|
|
let debugfs = Dir::new(c_str!("sample_debugfs"));
|
|
let dev = pdev.as_ref();
|
|
|
|
try_pin_init! {
|
|
Self {
|
|
_compatible <- debugfs.read_only_file(
|
|
c_str!("compatible"),
|
|
dev.fwnode()
|
|
.ok_or(ENOENT)?
|
|
.property_read::<CString>(c_str!("compatible"))
|
|
.required_by(dev)?,
|
|
),
|
|
counter <- Self::build_counter(&debugfs),
|
|
inner <- Self::build_inner(&debugfs),
|
|
_debugfs: debugfs,
|
|
pdev: pdev.into(),
|
|
}
|
|
}
|
|
}
|
|
}
|