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