1 // SPDX-License-Identifier: GPL-2.0 2 3 // Copyright (C) 2025 Google LLC. 4 5 //! Sample DebugFS exporting platform driver 6 //! 7 //! To successfully probe this driver with ACPI, use an ssdt that looks like 8 //! 9 //! ```dsl 10 //! DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001) 11 //! { 12 //! Scope (\_SB) 13 //! { 14 //! Device (T432) 15 //! { 16 //! Name (_HID, "LNUXBEEF") // ACPI hardware ID to match 17 //! Name (_UID, 1) 18 //! Name (_STA, 0x0F) // Device present, enabled 19 //! Name (_DSD, Package () { // Sample attribute 20 //! ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), 21 //! Package() { 22 //! Package(2) {"compatible", "sample-debugfs"} 23 //! } 24 //! }) 25 //! Name (_CRS, ResourceTemplate () 26 //! { 27 //! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000) 28 //! }) 29 //! } 30 //! } 31 //! } 32 //! ``` 33 34 use core::str::FromStr; 35 use core::sync::atomic::AtomicUsize; 36 use core::sync::atomic::Ordering; 37 use kernel::c_str; 38 use kernel::debugfs::{Dir, File}; 39 use kernel::new_mutex; 40 use kernel::prelude::*; 41 use kernel::sync::Mutex; 42 43 use kernel::{acpi, device::Core, of, platform, str::CString, types::ARef}; 44 45 kernel::module_platform_driver! { 46 type: RustDebugFs, 47 name: "rust_debugfs", 48 authors: ["Matthew Maurer"], 49 description: "Rust DebugFS usage sample", 50 license: "GPL", 51 } 52 53 #[pin_data] 54 struct RustDebugFs { 55 pdev: ARef<platform::Device>, 56 // As we only hold these for drop effect (to remove the directory/files) we have a leading 57 // underscore to indicate to the compiler that we don't expect to use this field directly. 58 _debugfs: Dir, 59 #[pin] 60 _compatible: File<CString>, 61 #[pin] 62 counter: File<AtomicUsize>, 63 #[pin] 64 inner: File<Mutex<Inner>>, 65 } 66 67 #[derive(Debug)] 68 struct Inner { 69 x: u32, 70 y: u32, 71 } 72 73 impl FromStr for Inner { 74 type Err = Error; 75 fn from_str(s: &str) -> Result<Self> { 76 let mut parts = s.split_whitespace(); 77 let x = parts 78 .next() 79 .ok_or(EINVAL)? 80 .parse::<u32>() 81 .map_err(|_| EINVAL)?; 82 let y = parts 83 .next() 84 .ok_or(EINVAL)? 85 .parse::<u32>() 86 .map_err(|_| EINVAL)?; 87 if parts.next().is_some() { 88 return Err(EINVAL); 89 } 90 Ok(Inner { x, y }) 91 } 92 } 93 94 kernel::acpi_device_table!( 95 ACPI_TABLE, 96 MODULE_ACPI_TABLE, 97 <RustDebugFs as platform::Driver>::IdInfo, 98 [(acpi::DeviceId::new(c_str!("LNUXBEEF")), ())] 99 ); 100 101 impl platform::Driver for RustDebugFs { 102 type IdInfo = (); 103 const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None; 104 const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE); 105 106 fn probe( 107 pdev: &platform::Device<Core>, 108 _info: Option<&Self::IdInfo>, 109 ) -> Result<Pin<KBox<Self>>> { 110 let result = KBox::try_pin_init(RustDebugFs::new(pdev), GFP_KERNEL)?; 111 // We can still mutate fields through the files which are atomic or mutexed: 112 result.counter.store(91, Ordering::Relaxed); 113 { 114 let mut guard = result.inner.lock(); 115 guard.x = guard.y; 116 guard.y = 42; 117 } 118 Ok(result) 119 } 120 } 121 122 impl RustDebugFs { 123 fn build_counter(dir: &Dir) -> impl PinInit<File<AtomicUsize>> + '_ { 124 dir.read_write_file(c_str!("counter"), AtomicUsize::new(0)) 125 } 126 127 fn build_inner(dir: &Dir) -> impl PinInit<File<Mutex<Inner>>> + '_ { 128 dir.read_write_file(c_str!("pair"), new_mutex!(Inner { x: 3, y: 10 })) 129 } 130 131 fn new(pdev: &platform::Device<Core>) -> impl PinInit<Self, Error> + '_ { 132 let debugfs = Dir::new(c_str!("sample_debugfs")); 133 let dev = pdev.as_ref(); 134 135 try_pin_init! { 136 Self { 137 _compatible <- debugfs.read_only_file( 138 c_str!("compatible"), 139 dev.fwnode() 140 .ok_or(ENOENT)? 141 .property_read::<CString>(c_str!("compatible")) 142 .required_by(dev)?, 143 ), 144 counter <- Self::build_counter(&debugfs), 145 inner <- Self::build_inner(&debugfs), 146 _debugfs: debugfs, 147 pdev: pdev.into(), 148 } 149 } 150 } 151 } 152