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 kernel::c_str; 36 use kernel::debugfs::{Dir, File}; 37 use kernel::new_mutex; 38 use kernel::prelude::*; 39 use kernel::sync::atomic::{Atomic, Relaxed}; 40 use kernel::sync::Mutex; 41 use kernel::{acpi, device::Core, of, platform, str::CString, types::ARef}; 42 43 kernel::module_platform_driver! { 44 type: RustDebugFs, 45 name: "rust_debugfs", 46 authors: ["Matthew Maurer"], 47 description: "Rust DebugFS usage sample", 48 license: "GPL", 49 } 50 51 #[pin_data] 52 struct RustDebugFs { 53 pdev: ARef<platform::Device>, 54 // As we only hold these for drop effect (to remove the directory/files) we have a leading 55 // underscore to indicate to the compiler that we don't expect to use this field directly. 56 _debugfs: Dir, 57 #[pin] 58 _compatible: File<CString>, 59 #[pin] 60 counter: File<Atomic<usize>>, 61 #[pin] 62 inner: File<Mutex<Inner>>, 63 } 64 65 #[derive(Debug)] 66 struct Inner { 67 x: u32, 68 y: u32, 69 } 70 71 impl FromStr for Inner { 72 type Err = Error; 73 fn from_str(s: &str) -> Result<Self> { 74 let mut parts = s.split_whitespace(); 75 let x = parts 76 .next() 77 .ok_or(EINVAL)? 78 .parse::<u32>() 79 .map_err(|_| EINVAL)?; 80 let y = parts 81 .next() 82 .ok_or(EINVAL)? 83 .parse::<u32>() 84 .map_err(|_| EINVAL)?; 85 if parts.next().is_some() { 86 return Err(EINVAL); 87 } 88 Ok(Inner { x, y }) 89 } 90 } 91 92 kernel::acpi_device_table!( 93 ACPI_TABLE, 94 MODULE_ACPI_TABLE, 95 <RustDebugFs as platform::Driver>::IdInfo, 96 [(acpi::DeviceId::new(c_str!("LNUXBEEF")), ())] 97 ); 98 99 impl platform::Driver for RustDebugFs { 100 type IdInfo = (); 101 const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None; 102 const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE); 103 104 fn probe( 105 pdev: &platform::Device<Core>, 106 _info: Option<&Self::IdInfo>, 107 ) -> Result<Pin<KBox<Self>>> { 108 let result = KBox::try_pin_init(RustDebugFs::new(pdev), GFP_KERNEL)?; 109 // We can still mutate fields through the files which are atomic or mutexed: 110 result.counter.store(91, Relaxed); 111 { 112 let mut guard = result.inner.lock(); 113 guard.x = guard.y; 114 guard.y = 42; 115 } 116 Ok(result) 117 } 118 } 119 120 impl RustDebugFs { 121 fn build_counter(dir: &Dir) -> impl PinInit<File<Atomic<usize>>> + '_ { 122 dir.read_write_file(c_str!("counter"), Atomic::<usize>::new(0)) 123 } 124 125 fn build_inner(dir: &Dir) -> impl PinInit<File<Mutex<Inner>>> + '_ { 126 dir.read_write_file(c_str!("pair"), new_mutex!(Inner { x: 3, y: 10 })) 127 } 128 129 fn new(pdev: &platform::Device<Core>) -> impl PinInit<Self, Error> + '_ { 130 let debugfs = Dir::new(c_str!("sample_debugfs")); 131 let dev = pdev.as_ref(); 132 133 try_pin_init! { 134 Self { 135 _compatible <- debugfs.read_only_file( 136 c_str!("compatible"), 137 dev.fwnode() 138 .ok_or(ENOENT)? 139 .property_read::<CString>(c_str!("compatible")) 140 .required_by(dev)?, 141 ), 142 counter <- Self::build_counter(&debugfs), 143 inner <- Self::build_inner(&debugfs), 144 _debugfs: debugfs, 145 pdev: pdev.into(), 146 } 147 } 148 } 149 } 150