1 // SPDX-License-Identifier: GPL-2.0
2
3 // Copyright (C) 2025 Google LLC.
4
5 //! Sample DebugFS exporting platform driver that demonstrates the use of
6 //! `Scope::dir` to create a variety of files without the need to separately
7 //! track them all.
8
9 use kernel::debugfs::{Dir, Scope};
10 use kernel::prelude::*;
11 use kernel::sizes::*;
12 use kernel::sync::atomic::Atomic;
13 use kernel::sync::Mutex;
14 use kernel::{c_str, new_mutex, str::CString};
15
16 module! {
17 type: RustScopedDebugFs,
18 name: "rust_debugfs_scoped",
19 authors: ["Matthew Maurer"],
20 description: "Rust Scoped DebugFS usage sample",
21 license: "GPL",
22 }
23
remove_file_write( mod_data: &ModuleData, reader: &mut kernel::uaccess::UserSliceReader, ) -> Result24 fn remove_file_write(
25 mod_data: &ModuleData,
26 reader: &mut kernel::uaccess::UserSliceReader,
27 ) -> Result {
28 let mut buf = [0u8; 128];
29 if reader.len() >= buf.len() {
30 return Err(EINVAL);
31 }
32 let n = reader.len();
33 reader.read_slice(&mut buf[..n])?;
34
35 let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
36 let nul_idx = s.len();
37 buf[nul_idx] = 0;
38 let to_remove = CStr::from_bytes_with_nul(&buf[..nul_idx + 1]).map_err(|_| EINVAL)?;
39 mod_data
40 .devices
41 .lock()
42 .retain(|device| device.name.to_bytes() != to_remove.to_bytes());
43 Ok(())
44 }
45
create_file_write( mod_data: &ModuleData, reader: &mut kernel::uaccess::UserSliceReader, ) -> Result46 fn create_file_write(
47 mod_data: &ModuleData,
48 reader: &mut kernel::uaccess::UserSliceReader,
49 ) -> Result {
50 let mut buf = [0u8; 128];
51 if reader.len() > buf.len() {
52 return Err(EINVAL);
53 }
54 let n = reader.len();
55 reader.read_slice(&mut buf[..n])?;
56
57 let mut nums = KVec::new();
58
59 let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
60 let mut items = s.split_whitespace();
61 let name_str = items.next().ok_or(EINVAL)?;
62 let name = CString::try_from_fmt(fmt!("{name_str}"))?;
63 let file_name = CString::try_from_fmt(fmt!("{name_str}"))?;
64 for sub in items {
65 nums.push(
66 Atomic::<usize>::new(sub.parse().map_err(|_| EINVAL)?),
67 GFP_KERNEL,
68 )?;
69 }
70 let blob = KBox::pin_init(new_mutex!([0x42; SZ_4K]), GFP_KERNEL)?;
71
72 let scope = KBox::pin_init(
73 mod_data.device_dir.scope(
74 DeviceData { name, nums, blob },
75 &file_name,
76 |dev_data, dir| {
77 for (idx, val) in dev_data.nums.iter().enumerate() {
78 let Ok(name) = CString::try_from_fmt(fmt!("{idx}")) else {
79 return;
80 };
81 dir.read_write_file(&name, val);
82 }
83 dir.read_write_binary_file(c_str!("blob"), &dev_data.blob);
84 },
85 ),
86 GFP_KERNEL,
87 )?;
88 (*mod_data.devices.lock()).push(scope, GFP_KERNEL)?;
89
90 Ok(())
91 }
92
93 struct RustScopedDebugFs {
94 _data: Pin<KBox<Scope<ModuleData>>>,
95 }
96
97 #[pin_data]
98 struct ModuleData {
99 device_dir: Dir,
100 #[pin]
101 devices: Mutex<KVec<Pin<KBox<Scope<DeviceData>>>>>,
102 }
103
104 impl ModuleData {
init(device_dir: Dir) -> impl PinInit<Self>105 fn init(device_dir: Dir) -> impl PinInit<Self> {
106 pin_init! {
107 Self {
108 device_dir: device_dir,
109 devices <- new_mutex!(KVec::new())
110 }
111 }
112 }
113 }
114
115 struct DeviceData {
116 name: CString,
117 nums: KVec<Atomic<usize>>,
118 blob: Pin<KBox<Mutex<[u8; SZ_4K]>>>,
119 }
120
init_control(base_dir: &Dir, dyn_dirs: Dir) -> impl PinInit<Scope<ModuleData>> + '_121 fn init_control(base_dir: &Dir, dyn_dirs: Dir) -> impl PinInit<Scope<ModuleData>> + '_ {
122 base_dir.scope(
123 ModuleData::init(dyn_dirs),
124 c_str!("control"),
125 |data, dir| {
126 dir.write_only_callback_file(c_str!("create"), data, &create_file_write);
127 dir.write_only_callback_file(c_str!("remove"), data, &remove_file_write);
128 },
129 )
130 }
131
132 impl kernel::Module for RustScopedDebugFs {
init(_module: &'static kernel::ThisModule) -> Result<Self>133 fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
134 let base_dir = Dir::new(c_str!("rust_scoped_debugfs"));
135 let dyn_dirs = base_dir.subdir(c_str!("dynamic"));
136 Ok(Self {
137 _data: KBox::pin_init(init_control(&base_dir, dyn_dirs), GFP_KERNEL)?,
138 })
139 }
140 }
141