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