| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- // SPDX-License-Identifier: GPL-2.0
- // Copyright (C) 2025 Google LLC.
- //! Sample DebugFS exporting platform driver that demonstrates the use of
- //! `Scope::dir` to create a variety of files without the need to separately
- //! track them all.
- use kernel::{
- debugfs::{
- Dir,
- Scope, //
- },
- new_mutex,
- prelude::*,
- sizes::*,
- str::CString,
- sync::{
- atomic::Atomic,
- Mutex, //
- },
- };
- module! {
- type: RustScopedDebugFs,
- name: "rust_debugfs_scoped",
- authors: ["Matthew Maurer"],
- description: "Rust Scoped DebugFS usage sample",
- license: "GPL",
- }
- fn remove_file_write(
- mod_data: &ModuleData,
- reader: &mut kernel::uaccess::UserSliceReader,
- ) -> Result {
- let mut buf = [0u8; 128];
- if reader.len() >= buf.len() {
- return Err(EINVAL);
- }
- let n = reader.len();
- reader.read_slice(&mut buf[..n])?;
- let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
- let nul_idx = s.len();
- buf[nul_idx] = 0;
- let to_remove = CStr::from_bytes_with_nul(&buf[..nul_idx + 1]).map_err(|_| EINVAL)?;
- mod_data
- .devices
- .lock()
- .retain(|device| device.name.to_bytes() != to_remove.to_bytes());
- Ok(())
- }
- fn create_file_write(
- mod_data: &ModuleData,
- reader: &mut kernel::uaccess::UserSliceReader,
- ) -> Result {
- let mut buf = [0u8; 128];
- if reader.len() > buf.len() {
- return Err(EINVAL);
- }
- let n = reader.len();
- reader.read_slice(&mut buf[..n])?;
- let mut nums = KVec::new();
- let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
- let mut items = s.split_whitespace();
- let name_str = items.next().ok_or(EINVAL)?;
- let name = CString::try_from_fmt(fmt!("{name_str}"))?;
- let file_name = CString::try_from_fmt(fmt!("{name_str}"))?;
- for sub in items {
- nums.push(
- Atomic::<usize>::new(sub.parse().map_err(|_| EINVAL)?),
- GFP_KERNEL,
- )?;
- }
- let blob = KBox::pin_init(new_mutex!([0x42; SZ_4K]), GFP_KERNEL)?;
- let scope = KBox::pin_init(
- mod_data.device_dir.scope(
- DeviceData { name, nums, blob },
- &file_name,
- |dev_data, dir| {
- for (idx, val) in dev_data.nums.iter().enumerate() {
- let Ok(name) = CString::try_from_fmt(fmt!("{idx}")) else {
- return;
- };
- dir.read_write_file(&name, val);
- }
- dir.read_write_binary_file(c"blob", &dev_data.blob);
- },
- ),
- GFP_KERNEL,
- )?;
- (*mod_data.devices.lock()).push(scope, GFP_KERNEL)?;
- Ok(())
- }
- struct RustScopedDebugFs {
- _data: Pin<KBox<Scope<ModuleData>>>,
- }
- #[pin_data]
- struct ModuleData {
- device_dir: Dir,
- #[pin]
- devices: Mutex<KVec<Pin<KBox<Scope<DeviceData>>>>>,
- }
- impl ModuleData {
- fn init(device_dir: Dir) -> impl PinInit<Self> {
- pin_init! {
- Self {
- device_dir: device_dir,
- devices <- new_mutex!(KVec::new())
- }
- }
- }
- }
- struct DeviceData {
- name: CString,
- nums: KVec<Atomic<usize>>,
- blob: Pin<KBox<Mutex<[u8; SZ_4K]>>>,
- }
- fn init_control(base_dir: &Dir, dyn_dirs: Dir) -> impl PinInit<Scope<ModuleData>> + '_ {
- base_dir.scope(ModuleData::init(dyn_dirs), c"control", |data, dir| {
- dir.write_only_callback_file(c"create", data, &create_file_write);
- dir.write_only_callback_file(c"remove", data, &remove_file_write);
- })
- }
- impl kernel::Module for RustScopedDebugFs {
- fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
- let base_dir = Dir::new(c"rust_scoped_debugfs");
- let dyn_dirs = base_dir.subdir(c"dynamic");
- Ok(Self {
- _data: KBox::pin_init(init_control(&base_dir, dyn_dirs), GFP_KERNEL)?,
- })
- }
- }
|