xref: /linux/samples/rust/rust_debugfs.rs (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
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