|  | // 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(), | 
|  | } | 
|  | } | 
|  | } | 
|  | } |