Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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}