// 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, // 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, #[pin] counter: File, #[pin] inner: File>, } #[derive(Debug)] struct Inner { x: u32, y: u32, } impl FromStr for Inner { type Err = Error; fn from_str(s: &str) -> Result { let mut parts = s.split_whitespace(); let x = parts .next() .ok_or(EINVAL)? .parse::() .map_err(|_| EINVAL)?; let y = parts .next() .ok_or(EINVAL)? .parse::() .map_err(|_| EINVAL)?; if parts.next().is_some() { return Err(EINVAL); } Ok(Inner { x, y }) } } kernel::acpi_device_table!( ACPI_TABLE, MODULE_ACPI_TABLE, ::IdInfo, [(acpi::DeviceId::new(c_str!("LNUXBEEF")), ())] ); impl platform::Driver for RustDebugFs { type IdInfo = (); const OF_ID_TABLE: Option> = None; const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); fn probe( pdev: &platform::Device, _info: Option<&Self::IdInfo>, ) -> Result>> { 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> + '_ { dir.read_write_file(c_str!("counter"), AtomicUsize::new(0)) } fn build_inner(dir: &Dir) -> impl PinInit>> + '_ { dir.read_write_file(c_str!("pair"), new_mutex!(Inner { x: 3, y: 10 })) } fn new(pdev: &platform::Device) -> impl PinInit + '_ { 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::(c_str!("compatible")) .required_by(dev)?, ), counter <- Self::build_counter(&debugfs), inner <- Self::build_inner(&debugfs), _debugfs: debugfs, pdev: pdev.into(), } } } }