at v6.18 4.4 kB view raw
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 34use core::str::FromStr; 35use core::sync::atomic::AtomicUsize; 36use core::sync::atomic::Ordering; 37use kernel::c_str; 38use kernel::debugfs::{Dir, File}; 39use kernel::new_mutex; 40use kernel::prelude::*; 41use kernel::sync::Mutex; 42 43use kernel::{acpi, device::Core, of, platform, str::CString, types::ARef}; 44 45kernel::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] 54struct 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)] 68struct Inner { 69 x: u32, 70 y: u32, 71} 72 73impl 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 94kernel::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 101impl 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 122impl 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}