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