mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-03 18:20:25 +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(),
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |