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