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