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