xref: /linux/rust/kernel/configfs.rs (revision ec7714e4947909190ffb3041a03311a975350fe0)
1446cafc2SAndreas Hindborg // SPDX-License-Identifier: GPL-2.0
2446cafc2SAndreas Hindborg 
3446cafc2SAndreas Hindborg //! configfs interface: Userspace-driven Kernel Object Configuration
4446cafc2SAndreas Hindborg //!
5446cafc2SAndreas Hindborg //! configfs is an in-memory pseudo file system for configuration of kernel
6446cafc2SAndreas Hindborg //! modules. Please see the [C documentation] for details and intended use of
7446cafc2SAndreas Hindborg //! configfs.
8446cafc2SAndreas Hindborg //!
9446cafc2SAndreas Hindborg //! This module does not support the following configfs features:
10446cafc2SAndreas Hindborg //!
11446cafc2SAndreas Hindborg //! - Items. All group children are groups.
12446cafc2SAndreas Hindborg //! - Symlink support.
13446cafc2SAndreas Hindborg //! - `disconnect_notify` hook.
14446cafc2SAndreas Hindborg //! - Default groups.
15446cafc2SAndreas Hindborg //!
16446cafc2SAndreas Hindborg //! See the [`rust_configfs.rs`] sample for a full example use of this module.
17446cafc2SAndreas Hindborg //!
18446cafc2SAndreas Hindborg //! C header: [`include/linux/configfs.h`](srctree/include/linux/configfs.h)
19446cafc2SAndreas Hindborg //!
20446cafc2SAndreas Hindborg //! # Example
21446cafc2SAndreas Hindborg //!
22446cafc2SAndreas Hindborg //! ```ignore
23446cafc2SAndreas Hindborg //! use kernel::alloc::flags;
24446cafc2SAndreas Hindborg //! use kernel::c_str;
25446cafc2SAndreas Hindborg //! use kernel::configfs_attrs;
26446cafc2SAndreas Hindborg //! use kernel::configfs;
27446cafc2SAndreas Hindborg //! use kernel::new_mutex;
28446cafc2SAndreas Hindborg //! use kernel::page::PAGE_SIZE;
29446cafc2SAndreas Hindborg //! use kernel::sync::Mutex;
30446cafc2SAndreas Hindborg //! use kernel::ThisModule;
31446cafc2SAndreas Hindborg //!
32446cafc2SAndreas Hindborg //! #[pin_data]
33446cafc2SAndreas Hindborg //! struct RustConfigfs {
34446cafc2SAndreas Hindborg //!     #[pin]
35446cafc2SAndreas Hindborg //!     config: configfs::Subsystem<Configuration>,
36446cafc2SAndreas Hindborg //! }
37446cafc2SAndreas Hindborg //!
38446cafc2SAndreas Hindborg //! impl kernel::InPlaceModule for RustConfigfs {
39446cafc2SAndreas Hindborg //!     fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
40446cafc2SAndreas Hindborg //!         pr_info!("Rust configfs sample (init)\n");
41446cafc2SAndreas Hindborg //!
42446cafc2SAndreas Hindborg //!         let item_type = configfs_attrs! {
43446cafc2SAndreas Hindborg //!             container: configfs::Subsystem<Configuration>,
44446cafc2SAndreas Hindborg //!             data: Configuration,
45446cafc2SAndreas Hindborg //!             attributes: [
46446cafc2SAndreas Hindborg //!                 message: 0,
47446cafc2SAndreas Hindborg //!                 bar: 1,
48446cafc2SAndreas Hindborg //!             ],
49446cafc2SAndreas Hindborg //!         };
50446cafc2SAndreas Hindborg //!
51446cafc2SAndreas Hindborg //!         try_pin_init!(Self {
52446cafc2SAndreas Hindborg //!             config <- configfs::Subsystem::new(
53446cafc2SAndreas Hindborg //!                 c_str!("rust_configfs"), item_type, Configuration::new()
54446cafc2SAndreas Hindborg //!             ),
55446cafc2SAndreas Hindborg //!         })
56446cafc2SAndreas Hindborg //!     }
57446cafc2SAndreas Hindborg //! }
58446cafc2SAndreas Hindborg //!
59446cafc2SAndreas Hindborg //! #[pin_data]
60446cafc2SAndreas Hindborg //! struct Configuration {
61446cafc2SAndreas Hindborg //!     message: &'static CStr,
62446cafc2SAndreas Hindborg //!     #[pin]
63446cafc2SAndreas Hindborg //!     bar: Mutex<(KBox<[u8; PAGE_SIZE]>, usize)>,
64446cafc2SAndreas Hindborg //! }
65446cafc2SAndreas Hindborg //!
66446cafc2SAndreas Hindborg //! impl Configuration {
67446cafc2SAndreas Hindborg //!     fn new() -> impl PinInit<Self, Error> {
68446cafc2SAndreas Hindborg //!         try_pin_init!(Self {
69446cafc2SAndreas Hindborg //!             message: c_str!("Hello World\n"),
70446cafc2SAndreas Hindborg //!             bar <- new_mutex!((KBox::new([0; PAGE_SIZE], flags::GFP_KERNEL)?, 0)),
71446cafc2SAndreas Hindborg //!         })
72446cafc2SAndreas Hindborg //!     }
73446cafc2SAndreas Hindborg //! }
74446cafc2SAndreas Hindborg //!
75446cafc2SAndreas Hindborg //! #[vtable]
76446cafc2SAndreas Hindborg //! impl configfs::AttributeOperations<0> for Configuration {
77446cafc2SAndreas Hindborg //!     type Data = Configuration;
78446cafc2SAndreas Hindborg //!
79446cafc2SAndreas Hindborg //!     fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
80446cafc2SAndreas Hindborg //!         pr_info!("Show message\n");
81446cafc2SAndreas Hindborg //!         let data = container.message;
82446cafc2SAndreas Hindborg //!         page[0..data.len()].copy_from_slice(data);
83446cafc2SAndreas Hindborg //!         Ok(data.len())
84446cafc2SAndreas Hindborg //!     }
85446cafc2SAndreas Hindborg //! }
86446cafc2SAndreas Hindborg //!
87446cafc2SAndreas Hindborg //! #[vtable]
88446cafc2SAndreas Hindborg //! impl configfs::AttributeOperations<1> for Configuration {
89446cafc2SAndreas Hindborg //!     type Data = Configuration;
90446cafc2SAndreas Hindborg //!
91446cafc2SAndreas Hindborg //!     fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
92446cafc2SAndreas Hindborg //!         pr_info!("Show bar\n");
93446cafc2SAndreas Hindborg //!         let guard = container.bar.lock();
94446cafc2SAndreas Hindborg //!         let data = guard.0.as_slice();
95446cafc2SAndreas Hindborg //!         let len = guard.1;
96446cafc2SAndreas Hindborg //!         page[0..len].copy_from_slice(&data[0..len]);
97446cafc2SAndreas Hindborg //!         Ok(len)
98446cafc2SAndreas Hindborg //!     }
99446cafc2SAndreas Hindborg //!
100446cafc2SAndreas Hindborg //!     fn store(container: &Configuration, page: &[u8]) -> Result {
101446cafc2SAndreas Hindborg //!         pr_info!("Store bar\n");
102446cafc2SAndreas Hindborg //!         let mut guard = container.bar.lock();
103446cafc2SAndreas Hindborg //!         guard.0[0..page.len()].copy_from_slice(page);
104446cafc2SAndreas Hindborg //!         guard.1 = page.len();
105446cafc2SAndreas Hindborg //!         Ok(())
106446cafc2SAndreas Hindborg //!     }
107446cafc2SAndreas Hindborg //! }
108446cafc2SAndreas Hindborg //! ```
109446cafc2SAndreas Hindborg //!
110446cafc2SAndreas Hindborg //! [C documentation]: srctree/Documentation/filesystems/configfs.rst
111446cafc2SAndreas Hindborg //! [`rust_configfs.rs`]: srctree/samples/rust/rust_configfs.rs
112446cafc2SAndreas Hindborg 
113446cafc2SAndreas Hindborg use crate::alloc::flags;
114446cafc2SAndreas Hindborg use crate::container_of;
115446cafc2SAndreas Hindborg use crate::page::PAGE_SIZE;
116446cafc2SAndreas Hindborg use crate::prelude::*;
117446cafc2SAndreas Hindborg use crate::str::CString;
118446cafc2SAndreas Hindborg use crate::sync::Arc;
119446cafc2SAndreas Hindborg use crate::sync::ArcBorrow;
120446cafc2SAndreas Hindborg use crate::types::Opaque;
121446cafc2SAndreas Hindborg use core::cell::UnsafeCell;
122446cafc2SAndreas Hindborg use core::marker::PhantomData;
123446cafc2SAndreas Hindborg 
124446cafc2SAndreas Hindborg /// A configfs subsystem.
125446cafc2SAndreas Hindborg ///
126446cafc2SAndreas Hindborg /// This is the top level entrypoint for a configfs hierarchy. To register
127446cafc2SAndreas Hindborg /// with configfs, embed a field of this type into your kernel module struct.
128446cafc2SAndreas Hindborg #[pin_data(PinnedDrop)]
129446cafc2SAndreas Hindborg pub struct Subsystem<Data> {
130446cafc2SAndreas Hindborg     #[pin]
131446cafc2SAndreas Hindborg     subsystem: Opaque<bindings::configfs_subsystem>,
132446cafc2SAndreas Hindborg     #[pin]
133446cafc2SAndreas Hindborg     data: Data,
134446cafc2SAndreas Hindborg }
135446cafc2SAndreas Hindborg 
136446cafc2SAndreas Hindborg // SAFETY: We do not provide any operations on `Subsystem`.
137446cafc2SAndreas Hindborg unsafe impl<Data> Sync for Subsystem<Data> {}
138446cafc2SAndreas Hindborg 
139446cafc2SAndreas Hindborg // SAFETY: Ownership of `Subsystem` can safely be transferred to other threads.
140446cafc2SAndreas Hindborg unsafe impl<Data> Send for Subsystem<Data> {}
141446cafc2SAndreas Hindborg 
142446cafc2SAndreas Hindborg impl<Data> Subsystem<Data> {
143446cafc2SAndreas Hindborg     /// Create an initializer for a [`Subsystem`].
144446cafc2SAndreas Hindborg     ///
145446cafc2SAndreas Hindborg     /// The subsystem will appear in configfs as a directory name given by
146446cafc2SAndreas Hindborg     /// `name`. The attributes available in directory are specified by
147446cafc2SAndreas Hindborg     /// `item_type`.
new( name: &'static CStr, item_type: &'static ItemType<Subsystem<Data>, Data>, data: impl PinInit<Data, Error>, ) -> impl PinInit<Self, Error>148446cafc2SAndreas Hindborg     pub fn new(
149446cafc2SAndreas Hindborg         name: &'static CStr,
150446cafc2SAndreas Hindborg         item_type: &'static ItemType<Subsystem<Data>, Data>,
151446cafc2SAndreas Hindborg         data: impl PinInit<Data, Error>,
152446cafc2SAndreas Hindborg     ) -> impl PinInit<Self, Error> {
153446cafc2SAndreas Hindborg         try_pin_init!(Self {
154446cafc2SAndreas Hindborg             subsystem <- pin_init::zeroed().chain(
155446cafc2SAndreas Hindborg                 |place: &mut Opaque<bindings::configfs_subsystem>| {
156446cafc2SAndreas Hindborg                     // SAFETY: We initialized the required fields of `place.group` above.
157446cafc2SAndreas Hindborg                     unsafe {
158446cafc2SAndreas Hindborg                         bindings::config_group_init_type_name(
159446cafc2SAndreas Hindborg                             &mut (*place.get()).su_group,
160446cafc2SAndreas Hindborg                             name.as_ptr(),
161446cafc2SAndreas Hindborg                             item_type.as_ptr(),
162446cafc2SAndreas Hindborg                         )
163446cafc2SAndreas Hindborg                     };
164446cafc2SAndreas Hindborg 
165446cafc2SAndreas Hindborg                     // SAFETY: `place.su_mutex` is valid for use as a mutex.
166446cafc2SAndreas Hindborg                     unsafe {
167446cafc2SAndreas Hindborg                         bindings::__mutex_init(
168446cafc2SAndreas Hindborg                             &mut (*place.get()).su_mutex,
169446cafc2SAndreas Hindborg                             kernel::optional_name!().as_char_ptr(),
170446cafc2SAndreas Hindborg                             kernel::static_lock_class!().as_ptr(),
171446cafc2SAndreas Hindborg                         )
172446cafc2SAndreas Hindborg                     }
173446cafc2SAndreas Hindborg                     Ok(())
174446cafc2SAndreas Hindborg                 }
175446cafc2SAndreas Hindborg             ),
176446cafc2SAndreas Hindborg             data <- data,
177446cafc2SAndreas Hindborg         })
178446cafc2SAndreas Hindborg         .pin_chain(|this| {
179446cafc2SAndreas Hindborg             crate::error::to_result(
180446cafc2SAndreas Hindborg                 // SAFETY: We initialized `this.subsystem` according to C API contract above.
181446cafc2SAndreas Hindborg                 unsafe { bindings::configfs_register_subsystem(this.subsystem.get()) },
182446cafc2SAndreas Hindborg             )
183446cafc2SAndreas Hindborg         })
184446cafc2SAndreas Hindborg     }
185446cafc2SAndreas Hindborg }
186446cafc2SAndreas Hindborg 
187446cafc2SAndreas Hindborg #[pinned_drop]
188446cafc2SAndreas Hindborg impl<Data> PinnedDrop for Subsystem<Data> {
drop(self: Pin<&mut Self>)189446cafc2SAndreas Hindborg     fn drop(self: Pin<&mut Self>) {
190446cafc2SAndreas Hindborg         // SAFETY: We registered `self.subsystem` in the initializer returned by `Self::new`.
191446cafc2SAndreas Hindborg         unsafe { bindings::configfs_unregister_subsystem(self.subsystem.get()) };
192446cafc2SAndreas Hindborg         // SAFETY: We initialized the mutex in `Subsystem::new`.
193446cafc2SAndreas Hindborg         unsafe { bindings::mutex_destroy(&raw mut (*self.subsystem.get()).su_mutex) };
194446cafc2SAndreas Hindborg     }
195446cafc2SAndreas Hindborg }
196446cafc2SAndreas Hindborg 
197446cafc2SAndreas Hindborg /// Trait that allows offset calculations for structs that embed a
198446cafc2SAndreas Hindborg /// `bindings::config_group`.
199446cafc2SAndreas Hindborg ///
200446cafc2SAndreas Hindborg /// Users of the configfs API should not need to implement this trait.
201446cafc2SAndreas Hindborg ///
202446cafc2SAndreas Hindborg /// # Safety
203446cafc2SAndreas Hindborg ///
204446cafc2SAndreas Hindborg /// - Implementers of this trait must embed a `bindings::config_group`.
205446cafc2SAndreas Hindborg /// - Methods must be implemented according to method documentation.
206446cafc2SAndreas Hindborg pub unsafe trait HasGroup<Data> {
207446cafc2SAndreas Hindborg     /// Return the address of the `bindings::config_group` embedded in [`Self`].
208446cafc2SAndreas Hindborg     ///
209446cafc2SAndreas Hindborg     /// # Safety
210446cafc2SAndreas Hindborg     ///
211446cafc2SAndreas Hindborg     /// - `this` must be a valid allocation of at least the size of [`Self`].
group(this: *const Self) -> *const bindings::config_group212446cafc2SAndreas Hindborg     unsafe fn group(this: *const Self) -> *const bindings::config_group;
213446cafc2SAndreas Hindborg 
214446cafc2SAndreas Hindborg     /// Return the address of the [`Self`] that `group` is embedded in.
215446cafc2SAndreas Hindborg     ///
216446cafc2SAndreas Hindborg     /// # Safety
217446cafc2SAndreas Hindborg     ///
218446cafc2SAndreas Hindborg     /// - `group` must point to the `bindings::config_group` that is embedded in
219446cafc2SAndreas Hindborg     ///   [`Self`].
container_of(group: *const bindings::config_group) -> *const Self220446cafc2SAndreas Hindborg     unsafe fn container_of(group: *const bindings::config_group) -> *const Self;
221446cafc2SAndreas Hindborg }
222446cafc2SAndreas Hindborg 
223446cafc2SAndreas Hindborg // SAFETY: `Subsystem<Data>` embeds a field of type `bindings::config_group`
224446cafc2SAndreas Hindborg // within the `subsystem` field.
225446cafc2SAndreas Hindborg unsafe impl<Data> HasGroup<Data> for Subsystem<Data> {
group(this: *const Self) -> *const bindings::config_group226446cafc2SAndreas Hindborg     unsafe fn group(this: *const Self) -> *const bindings::config_group {
227446cafc2SAndreas Hindborg         // SAFETY: By impl and function safety requirement this projection is in bounds.
228446cafc2SAndreas Hindborg         unsafe { &raw const (*(*this).subsystem.get()).su_group }
229446cafc2SAndreas Hindborg     }
230446cafc2SAndreas Hindborg 
container_of(group: *const bindings::config_group) -> *const Self231446cafc2SAndreas Hindborg     unsafe fn container_of(group: *const bindings::config_group) -> *const Self {
232446cafc2SAndreas Hindborg         // SAFETY: By impl and function safety requirement this projection is in bounds.
233446cafc2SAndreas Hindborg         let c_subsys_ptr = unsafe { container_of!(group, bindings::configfs_subsystem, su_group) };
234446cafc2SAndreas Hindborg         let opaque_ptr = c_subsys_ptr.cast::<Opaque<bindings::configfs_subsystem>>();
235446cafc2SAndreas Hindborg         // SAFETY: By impl and function safety requirement, `opaque_ptr` and the
236446cafc2SAndreas Hindborg         // pointer it returns, are within the same allocation.
237446cafc2SAndreas Hindborg         unsafe { container_of!(opaque_ptr, Subsystem<Data>, subsystem) }
238446cafc2SAndreas Hindborg     }
239446cafc2SAndreas Hindborg }
240446cafc2SAndreas Hindborg 
241446cafc2SAndreas Hindborg /// A configfs group.
242446cafc2SAndreas Hindborg ///
243446cafc2SAndreas Hindborg /// To add a subgroup to configfs, pass this type as `ctype` to
244446cafc2SAndreas Hindborg /// [`crate::configfs_attrs`] when creating a group in [`GroupOperations::make_group`].
245446cafc2SAndreas Hindborg #[pin_data]
246446cafc2SAndreas Hindborg pub struct Group<Data> {
247446cafc2SAndreas Hindborg     #[pin]
248446cafc2SAndreas Hindborg     group: Opaque<bindings::config_group>,
249446cafc2SAndreas Hindborg     #[pin]
250446cafc2SAndreas Hindborg     data: Data,
251446cafc2SAndreas Hindborg }
252446cafc2SAndreas Hindborg 
253446cafc2SAndreas Hindborg impl<Data> Group<Data> {
254446cafc2SAndreas Hindborg     /// Create an initializer for a new group.
255446cafc2SAndreas Hindborg     ///
256446cafc2SAndreas Hindborg     /// When instantiated, the group will appear as a directory with the name
257446cafc2SAndreas Hindborg     /// given by `name` and it will contain attributes specified by `item_type`.
new( name: CString, item_type: &'static ItemType<Group<Data>, Data>, data: impl PinInit<Data, Error>, ) -> impl PinInit<Self, Error>258446cafc2SAndreas Hindborg     pub fn new(
259446cafc2SAndreas Hindborg         name: CString,
260446cafc2SAndreas Hindborg         item_type: &'static ItemType<Group<Data>, Data>,
261446cafc2SAndreas Hindborg         data: impl PinInit<Data, Error>,
262446cafc2SAndreas Hindborg     ) -> impl PinInit<Self, Error> {
263446cafc2SAndreas Hindborg         try_pin_init!(Self {
264446cafc2SAndreas Hindborg             group <- pin_init::zeroed().chain(|v: &mut Opaque<bindings::config_group>| {
265446cafc2SAndreas Hindborg                 let place = v.get();
266446cafc2SAndreas Hindborg                 let name = name.as_bytes_with_nul().as_ptr();
267446cafc2SAndreas Hindborg                 // SAFETY: It is safe to initialize a group once it has been zeroed.
268446cafc2SAndreas Hindborg                 unsafe {
269446cafc2SAndreas Hindborg                     bindings::config_group_init_type_name(place, name.cast(), item_type.as_ptr())
270446cafc2SAndreas Hindborg                 };
271446cafc2SAndreas Hindborg                 Ok(())
272446cafc2SAndreas Hindborg             }),
273446cafc2SAndreas Hindborg             data <- data,
274446cafc2SAndreas Hindborg         })
275446cafc2SAndreas Hindborg     }
276446cafc2SAndreas Hindborg }
277446cafc2SAndreas Hindborg 
278446cafc2SAndreas Hindborg // SAFETY: `Group<Data>` embeds a field of type `bindings::config_group`
279446cafc2SAndreas Hindborg // within the `group` field.
280446cafc2SAndreas Hindborg unsafe impl<Data> HasGroup<Data> for Group<Data> {
group(this: *const Self) -> *const bindings::config_group281446cafc2SAndreas Hindborg     unsafe fn group(this: *const Self) -> *const bindings::config_group {
282446cafc2SAndreas Hindborg         Opaque::raw_get(
283446cafc2SAndreas Hindborg             // SAFETY: By impl and function safety requirements this field
284446cafc2SAndreas Hindborg             // projection is within bounds of the allocation.
285446cafc2SAndreas Hindborg             unsafe { &raw const (*this).group },
286446cafc2SAndreas Hindborg         )
287446cafc2SAndreas Hindborg     }
288446cafc2SAndreas Hindborg 
container_of(group: *const bindings::config_group) -> *const Self289446cafc2SAndreas Hindborg     unsafe fn container_of(group: *const bindings::config_group) -> *const Self {
290446cafc2SAndreas Hindborg         let opaque_ptr = group.cast::<Opaque<bindings::config_group>>();
291446cafc2SAndreas Hindborg         // SAFETY: By impl and function safety requirement, `opaque_ptr` and
292446cafc2SAndreas Hindborg         // pointer it returns will be in the same allocation.
293446cafc2SAndreas Hindborg         unsafe { container_of!(opaque_ptr, Self, group) }
294446cafc2SAndreas Hindborg     }
295446cafc2SAndreas Hindborg }
296446cafc2SAndreas Hindborg 
297446cafc2SAndreas Hindborg /// # Safety
298446cafc2SAndreas Hindborg ///
299446cafc2SAndreas Hindborg /// `this` must be a valid pointer.
300446cafc2SAndreas Hindborg ///
301446cafc2SAndreas Hindborg /// If `this` does not represent the root group of a configfs subsystem,
302446cafc2SAndreas Hindborg /// `this` must be a pointer to a `bindings::config_group` embedded in a
303446cafc2SAndreas Hindborg /// `Group<Parent>`.
304446cafc2SAndreas Hindborg ///
305446cafc2SAndreas Hindborg /// Otherwise, `this` must be a pointer to a `bindings::config_group` that
306446cafc2SAndreas Hindborg /// is embedded in a `bindings::configfs_subsystem` that is embedded in a
307446cafc2SAndreas Hindborg /// `Subsystem<Parent>`.
get_group_data<'a, Parent>(this: *mut bindings::config_group) -> &'a Parent308446cafc2SAndreas Hindborg unsafe fn get_group_data<'a, Parent>(this: *mut bindings::config_group) -> &'a Parent {
309446cafc2SAndreas Hindborg     // SAFETY: `this` is a valid pointer.
310446cafc2SAndreas Hindborg     let is_root = unsafe { (*this).cg_subsys.is_null() };
311446cafc2SAndreas Hindborg 
312446cafc2SAndreas Hindborg     if !is_root {
313446cafc2SAndreas Hindborg         // SAFETY: By C API contact,`this` was returned from a call to
314446cafc2SAndreas Hindborg         // `make_group`. The pointer is known to be embedded within a
315446cafc2SAndreas Hindborg         // `Group<Parent>`.
316446cafc2SAndreas Hindborg         unsafe { &(*Group::<Parent>::container_of(this)).data }
317446cafc2SAndreas Hindborg     } else {
318446cafc2SAndreas Hindborg         // SAFETY: By C API contract, `this` is a pointer to the
319446cafc2SAndreas Hindborg         // `bindings::config_group` field within a `Subsystem<Parent>`.
320446cafc2SAndreas Hindborg         unsafe { &(*Subsystem::container_of(this)).data }
321446cafc2SAndreas Hindborg     }
322446cafc2SAndreas Hindborg }
323446cafc2SAndreas Hindborg 
324446cafc2SAndreas Hindborg struct GroupOperationsVTable<Parent, Child>(PhantomData<(Parent, Child)>);
325446cafc2SAndreas Hindborg 
326446cafc2SAndreas Hindborg impl<Parent, Child> GroupOperationsVTable<Parent, Child>
327446cafc2SAndreas Hindborg where
328446cafc2SAndreas Hindborg     Parent: GroupOperations<Child = Child>,
329446cafc2SAndreas Hindborg     Child: 'static,
330446cafc2SAndreas Hindborg {
331446cafc2SAndreas Hindborg     /// # Safety
332446cafc2SAndreas Hindborg     ///
333446cafc2SAndreas Hindborg     /// `this` must be a valid pointer.
334446cafc2SAndreas Hindborg     ///
335446cafc2SAndreas Hindborg     /// If `this` does not represent the root group of a configfs subsystem,
336446cafc2SAndreas Hindborg     /// `this` must be a pointer to a `bindings::config_group` embedded in a
337446cafc2SAndreas Hindborg     /// `Group<Parent>`.
338446cafc2SAndreas Hindborg     ///
339446cafc2SAndreas Hindborg     /// Otherwise, `this` must be a pointer to a `bindings::config_group` that
340446cafc2SAndreas Hindborg     /// is embedded in a `bindings::configfs_subsystem` that is embedded in a
341446cafc2SAndreas Hindborg     /// `Subsystem<Parent>`.
342446cafc2SAndreas Hindborg     ///
343446cafc2SAndreas Hindborg     /// `name` must point to a null terminated string.
make_group( this: *mut bindings::config_group, name: *const kernel::ffi::c_char, ) -> *mut bindings::config_group344446cafc2SAndreas Hindborg     unsafe extern "C" fn make_group(
345446cafc2SAndreas Hindborg         this: *mut bindings::config_group,
346446cafc2SAndreas Hindborg         name: *const kernel::ffi::c_char,
347446cafc2SAndreas Hindborg     ) -> *mut bindings::config_group {
348446cafc2SAndreas Hindborg         // SAFETY: By function safety requirements of this function, this call
349446cafc2SAndreas Hindborg         // is safe.
350446cafc2SAndreas Hindborg         let parent_data = unsafe { get_group_data(this) };
351446cafc2SAndreas Hindborg 
352446cafc2SAndreas Hindborg         let group_init = match Parent::make_group(
353446cafc2SAndreas Hindborg             parent_data,
354446cafc2SAndreas Hindborg             // SAFETY: By function safety requirements, name points to a null
355446cafc2SAndreas Hindborg             // terminated string.
356446cafc2SAndreas Hindborg             unsafe { CStr::from_char_ptr(name) },
357446cafc2SAndreas Hindborg         ) {
358446cafc2SAndreas Hindborg             Ok(init) => init,
359446cafc2SAndreas Hindborg             Err(e) => return e.to_ptr(),
360446cafc2SAndreas Hindborg         };
361446cafc2SAndreas Hindborg 
362446cafc2SAndreas Hindborg         let child_group = <Arc<Group<Child>> as InPlaceInit<Group<Child>>>::try_pin_init(
363446cafc2SAndreas Hindborg             group_init,
364446cafc2SAndreas Hindborg             flags::GFP_KERNEL,
365446cafc2SAndreas Hindborg         );
366446cafc2SAndreas Hindborg 
367446cafc2SAndreas Hindborg         match child_group {
368446cafc2SAndreas Hindborg             Ok(child_group) => {
369446cafc2SAndreas Hindborg                 let child_group_ptr = child_group.into_raw();
370446cafc2SAndreas Hindborg                 // SAFETY: We allocated the pointee of `child_ptr` above as a
371446cafc2SAndreas Hindborg                 // `Group<Child>`.
372446cafc2SAndreas Hindborg                 unsafe { Group::<Child>::group(child_group_ptr) }.cast_mut()
373446cafc2SAndreas Hindborg             }
374446cafc2SAndreas Hindborg             Err(e) => e.to_ptr(),
375446cafc2SAndreas Hindborg         }
376446cafc2SAndreas Hindborg     }
377446cafc2SAndreas Hindborg 
378446cafc2SAndreas Hindborg     /// # Safety
379446cafc2SAndreas Hindborg     ///
380446cafc2SAndreas Hindborg     /// If `this` does not represent the root group of a configfs subsystem,
381446cafc2SAndreas Hindborg     /// `this` must be a pointer to a `bindings::config_group` embedded in a
382446cafc2SAndreas Hindborg     /// `Group<Parent>`.
383446cafc2SAndreas Hindborg     ///
384446cafc2SAndreas Hindborg     /// Otherwise, `this` must be a pointer to a `bindings::config_group` that
385446cafc2SAndreas Hindborg     /// is embedded in a `bindings::configfs_subsystem` that is embedded in a
386446cafc2SAndreas Hindborg     /// `Subsystem<Parent>`.
387446cafc2SAndreas Hindborg     ///
388446cafc2SAndreas Hindborg     /// `item` must point to a `bindings::config_item` within a
389446cafc2SAndreas Hindborg     /// `bindings::config_group` within a `Group<Child>`.
drop_item( this: *mut bindings::config_group, item: *mut bindings::config_item, )390446cafc2SAndreas Hindborg     unsafe extern "C" fn drop_item(
391446cafc2SAndreas Hindborg         this: *mut bindings::config_group,
392446cafc2SAndreas Hindborg         item: *mut bindings::config_item,
393446cafc2SAndreas Hindborg     ) {
394446cafc2SAndreas Hindborg         // SAFETY: By function safety requirements of this function, this call
395446cafc2SAndreas Hindborg         // is safe.
396446cafc2SAndreas Hindborg         let parent_data = unsafe { get_group_data(this) };
397446cafc2SAndreas Hindborg 
398446cafc2SAndreas Hindborg         // SAFETY: By function safety requirements, `item` is embedded in a
399446cafc2SAndreas Hindborg         // `config_group`.
400446cafc2SAndreas Hindborg         let c_child_group_ptr = unsafe { container_of!(item, bindings::config_group, cg_item) };
401446cafc2SAndreas Hindborg         // SAFETY: By function safety requirements, `c_child_group_ptr` is
402446cafc2SAndreas Hindborg         // embedded within a `Group<Child>`.
403446cafc2SAndreas Hindborg         let r_child_group_ptr = unsafe { Group::<Child>::container_of(c_child_group_ptr) };
404446cafc2SAndreas Hindborg 
405446cafc2SAndreas Hindborg         if Parent::HAS_DROP_ITEM {
406446cafc2SAndreas Hindborg             // SAFETY: We called `into_raw` to produce `r_child_group_ptr` in
407446cafc2SAndreas Hindborg             // `make_group`.
408446cafc2SAndreas Hindborg             let arc: Arc<Group<Child>> = unsafe { Arc::from_raw(r_child_group_ptr.cast_mut()) };
409446cafc2SAndreas Hindborg 
410446cafc2SAndreas Hindborg             Parent::drop_item(parent_data, arc.as_arc_borrow());
411446cafc2SAndreas Hindborg             arc.into_raw();
412446cafc2SAndreas Hindborg         }
413446cafc2SAndreas Hindborg 
414446cafc2SAndreas Hindborg         // SAFETY: By C API contract, we are required to drop a refcount on
415446cafc2SAndreas Hindborg         // `item`.
416446cafc2SAndreas Hindborg         unsafe { bindings::config_item_put(item) };
417446cafc2SAndreas Hindborg     }
418446cafc2SAndreas Hindborg 
419446cafc2SAndreas Hindborg     const VTABLE: bindings::configfs_group_operations = bindings::configfs_group_operations {
420446cafc2SAndreas Hindborg         make_item: None,
421446cafc2SAndreas Hindborg         make_group: Some(Self::make_group),
422446cafc2SAndreas Hindborg         disconnect_notify: None,
423446cafc2SAndreas Hindborg         drop_item: Some(Self::drop_item),
424446cafc2SAndreas Hindborg         is_visible: None,
425446cafc2SAndreas Hindborg         is_bin_visible: None,
426446cafc2SAndreas Hindborg     };
427446cafc2SAndreas Hindborg 
vtable_ptr() -> *const bindings::configfs_group_operations428446cafc2SAndreas Hindborg     const fn vtable_ptr() -> *const bindings::configfs_group_operations {
429446cafc2SAndreas Hindborg         &Self::VTABLE as *const bindings::configfs_group_operations
430446cafc2SAndreas Hindborg     }
431446cafc2SAndreas Hindborg }
432446cafc2SAndreas Hindborg 
433446cafc2SAndreas Hindborg struct ItemOperationsVTable<Container, Data>(PhantomData<(Container, Data)>);
434446cafc2SAndreas Hindborg 
435446cafc2SAndreas Hindborg impl<Data> ItemOperationsVTable<Group<Data>, Data>
436446cafc2SAndreas Hindborg where
437446cafc2SAndreas Hindborg     Data: 'static,
438446cafc2SAndreas Hindborg {
439446cafc2SAndreas Hindborg     /// # Safety
440446cafc2SAndreas Hindborg     ///
441446cafc2SAndreas Hindborg     /// `this` must be a pointer to a `bindings::config_group` embedded in a
442446cafc2SAndreas Hindborg     /// `Group<Parent>`.
443446cafc2SAndreas Hindborg     ///
444446cafc2SAndreas Hindborg     /// This function will destroy the pointee of `this`. The pointee of `this`
445446cafc2SAndreas Hindborg     /// must not be accessed after the function returns.
release(this: *mut bindings::config_item)446446cafc2SAndreas Hindborg     unsafe extern "C" fn release(this: *mut bindings::config_item) {
447446cafc2SAndreas Hindborg         // SAFETY: By function safety requirements, `this` is embedded in a
448446cafc2SAndreas Hindborg         // `config_group`.
449446cafc2SAndreas Hindborg         let c_group_ptr = unsafe { kernel::container_of!(this, bindings::config_group, cg_item) };
450446cafc2SAndreas Hindborg         // SAFETY: By function safety requirements, `c_group_ptr` is
451446cafc2SAndreas Hindborg         // embedded within a `Group<Data>`.
452446cafc2SAndreas Hindborg         let r_group_ptr = unsafe { Group::<Data>::container_of(c_group_ptr) };
453446cafc2SAndreas Hindborg 
454446cafc2SAndreas Hindborg         // SAFETY: We called `into_raw` on `r_group_ptr` in
455446cafc2SAndreas Hindborg         // `make_group`.
456446cafc2SAndreas Hindborg         let pin_self: Arc<Group<Data>> = unsafe { Arc::from_raw(r_group_ptr.cast_mut()) };
457446cafc2SAndreas Hindborg         drop(pin_self);
458446cafc2SAndreas Hindborg     }
459446cafc2SAndreas Hindborg 
460446cafc2SAndreas Hindborg     const VTABLE: bindings::configfs_item_operations = bindings::configfs_item_operations {
461446cafc2SAndreas Hindborg         release: Some(Self::release),
462446cafc2SAndreas Hindborg         allow_link: None,
463446cafc2SAndreas Hindborg         drop_link: None,
464446cafc2SAndreas Hindborg     };
465446cafc2SAndreas Hindborg 
vtable_ptr() -> *const bindings::configfs_item_operations466446cafc2SAndreas Hindborg     const fn vtable_ptr() -> *const bindings::configfs_item_operations {
467446cafc2SAndreas Hindborg         &Self::VTABLE as *const bindings::configfs_item_operations
468446cafc2SAndreas Hindborg     }
469446cafc2SAndreas Hindborg }
470446cafc2SAndreas Hindborg 
471446cafc2SAndreas Hindborg impl<Data> ItemOperationsVTable<Subsystem<Data>, Data> {
472446cafc2SAndreas Hindborg     const VTABLE: bindings::configfs_item_operations = bindings::configfs_item_operations {
473446cafc2SAndreas Hindborg         release: None,
474446cafc2SAndreas Hindborg         allow_link: None,
475446cafc2SAndreas Hindborg         drop_link: None,
476446cafc2SAndreas Hindborg     };
477446cafc2SAndreas Hindborg 
vtable_ptr() -> *const bindings::configfs_item_operations478446cafc2SAndreas Hindborg     const fn vtable_ptr() -> *const bindings::configfs_item_operations {
479446cafc2SAndreas Hindborg         &Self::VTABLE as *const bindings::configfs_item_operations
480446cafc2SAndreas Hindborg     }
481446cafc2SAndreas Hindborg }
482446cafc2SAndreas Hindborg 
483446cafc2SAndreas Hindborg /// Operations implemented by configfs groups that can create subgroups.
484446cafc2SAndreas Hindborg ///
485446cafc2SAndreas Hindborg /// Implement this trait on structs that embed a [`Subsystem`] or a [`Group`].
486446cafc2SAndreas Hindborg #[vtable]
487446cafc2SAndreas Hindborg pub trait GroupOperations {
488446cafc2SAndreas Hindborg     /// The child data object type.
489446cafc2SAndreas Hindborg     ///
490446cafc2SAndreas Hindborg     /// This group will create subgroups (subdirectories) backed by this kind of
491446cafc2SAndreas Hindborg     /// object.
492446cafc2SAndreas Hindborg     type Child: 'static;
493446cafc2SAndreas Hindborg 
494446cafc2SAndreas Hindborg     /// Creates a new subgroup.
495446cafc2SAndreas Hindborg     ///
496446cafc2SAndreas Hindborg     /// The kernel will call this method in response to `mkdir(2)` in the
497446cafc2SAndreas Hindborg     /// directory representing `this`.
498446cafc2SAndreas Hindborg     ///
499446cafc2SAndreas Hindborg     /// To accept the request to create a group, implementations should
500446cafc2SAndreas Hindborg     /// return an initializer of a `Group<Self::Child>`. To prevent creation,
501446cafc2SAndreas Hindborg     /// return a suitable error.
make_group(&self, name: &CStr) -> Result<impl PinInit<Group<Self::Child>, Error>>502446cafc2SAndreas Hindborg     fn make_group(&self, name: &CStr) -> Result<impl PinInit<Group<Self::Child>, Error>>;
503446cafc2SAndreas Hindborg 
504446cafc2SAndreas Hindborg     /// Prepares the group for removal from configfs.
505446cafc2SAndreas Hindborg     ///
506446cafc2SAndreas Hindborg     /// The kernel will call this method before the directory representing `_child` is removed from
507446cafc2SAndreas Hindborg     /// configfs.
508446cafc2SAndreas Hindborg     ///
509446cafc2SAndreas Hindborg     /// Implementations can use this method to do house keeping before configfs drops its
510446cafc2SAndreas Hindborg     /// reference to `Child`.
511446cafc2SAndreas Hindborg     ///
512446cafc2SAndreas Hindborg     /// NOTE: "drop" in the name of this function is not related to the Rust drop term. Rather, the
513446cafc2SAndreas Hindborg     /// name is inherited from the callback name in the underlying C code.
drop_item(&self, _child: ArcBorrow<'_, Group<Self::Child>>)514446cafc2SAndreas Hindborg     fn drop_item(&self, _child: ArcBorrow<'_, Group<Self::Child>>) {
515446cafc2SAndreas Hindborg         kernel::build_error!(kernel::error::VTABLE_DEFAULT_ERROR)
516446cafc2SAndreas Hindborg     }
517446cafc2SAndreas Hindborg }
518446cafc2SAndreas Hindborg 
519446cafc2SAndreas Hindborg /// A configfs attribute.
520446cafc2SAndreas Hindborg ///
521446cafc2SAndreas Hindborg /// An attribute appears as a file in configfs, inside a folder that represent
522446cafc2SAndreas Hindborg /// the group that the attribute belongs to.
523446cafc2SAndreas Hindborg #[repr(transparent)]
524446cafc2SAndreas Hindborg pub struct Attribute<const ID: u64, O, Data> {
525446cafc2SAndreas Hindborg     attribute: Opaque<bindings::configfs_attribute>,
526446cafc2SAndreas Hindborg     _p: PhantomData<(O, Data)>,
527446cafc2SAndreas Hindborg }
528446cafc2SAndreas Hindborg 
529446cafc2SAndreas Hindborg // SAFETY: We do not provide any operations on `Attribute`.
530446cafc2SAndreas Hindborg unsafe impl<const ID: u64, O, Data> Sync for Attribute<ID, O, Data> {}
531446cafc2SAndreas Hindborg 
532446cafc2SAndreas Hindborg // SAFETY: Ownership of `Attribute` can safely be transferred to other threads.
533446cafc2SAndreas Hindborg unsafe impl<const ID: u64, O, Data> Send for Attribute<ID, O, Data> {}
534446cafc2SAndreas Hindborg 
535446cafc2SAndreas Hindborg impl<const ID: u64, O, Data> Attribute<ID, O, Data>
536446cafc2SAndreas Hindborg where
537446cafc2SAndreas Hindborg     O: AttributeOperations<ID, Data = Data>,
538446cafc2SAndreas Hindborg {
539446cafc2SAndreas Hindborg     /// # Safety
540446cafc2SAndreas Hindborg     ///
541446cafc2SAndreas Hindborg     /// `item` must be embedded in a `bindings::config_group`.
542446cafc2SAndreas Hindborg     ///
543446cafc2SAndreas Hindborg     /// If `item` does not represent the root group of a configfs subsystem,
544446cafc2SAndreas Hindborg     /// the group must be embedded in a `Group<Data>`.
545446cafc2SAndreas Hindborg     ///
546446cafc2SAndreas Hindborg     /// Otherwise, the group must be a embedded in a
547446cafc2SAndreas Hindborg     /// `bindings::configfs_subsystem` that is embedded in a `Subsystem<Data>`.
548446cafc2SAndreas Hindborg     ///
549446cafc2SAndreas Hindborg     /// `page` must point to a writable buffer of size at least [`PAGE_SIZE`].
show( item: *mut bindings::config_item, page: *mut kernel::ffi::c_char, ) -> isize550446cafc2SAndreas Hindborg     unsafe extern "C" fn show(
551446cafc2SAndreas Hindborg         item: *mut bindings::config_item,
552446cafc2SAndreas Hindborg         page: *mut kernel::ffi::c_char,
553446cafc2SAndreas Hindborg     ) -> isize {
554446cafc2SAndreas Hindborg         let c_group: *mut bindings::config_group =
555446cafc2SAndreas Hindborg             // SAFETY: By function safety requirements, `item` is embedded in a
556446cafc2SAndreas Hindborg             // `config_group`.
557*ec7714e4SLinus Torvalds             unsafe { container_of!(item, bindings::config_group, cg_item) };
558446cafc2SAndreas Hindborg 
559446cafc2SAndreas Hindborg         // SAFETY: The function safety requirements for this function satisfy
560446cafc2SAndreas Hindborg         // the conditions for this call.
561446cafc2SAndreas Hindborg         let data: &Data = unsafe { get_group_data(c_group) };
562446cafc2SAndreas Hindborg 
563446cafc2SAndreas Hindborg         // SAFETY: By function safety requirements, `page` is writable for `PAGE_SIZE`.
564446cafc2SAndreas Hindborg         let ret = O::show(data, unsafe { &mut *(page as *mut [u8; PAGE_SIZE]) });
565446cafc2SAndreas Hindborg 
566446cafc2SAndreas Hindborg         match ret {
567446cafc2SAndreas Hindborg             Ok(size) => size as isize,
568446cafc2SAndreas Hindborg             Err(err) => err.to_errno() as isize,
569446cafc2SAndreas Hindborg         }
570446cafc2SAndreas Hindborg     }
571446cafc2SAndreas Hindborg 
572446cafc2SAndreas Hindborg     /// # Safety
573446cafc2SAndreas Hindborg     ///
574446cafc2SAndreas Hindborg     /// `item` must be embedded in a `bindings::config_group`.
575446cafc2SAndreas Hindborg     ///
576446cafc2SAndreas Hindborg     /// If `item` does not represent the root group of a configfs subsystem,
577446cafc2SAndreas Hindborg     /// the group must be embedded in a `Group<Data>`.
578446cafc2SAndreas Hindborg     ///
579446cafc2SAndreas Hindborg     /// Otherwise, the group must be a embedded in a
580446cafc2SAndreas Hindborg     /// `bindings::configfs_subsystem` that is embedded in a `Subsystem<Data>`.
581446cafc2SAndreas Hindborg     ///
582446cafc2SAndreas Hindborg     /// `page` must point to a readable buffer of size at least `size`.
store( item: *mut bindings::config_item, page: *const kernel::ffi::c_char, size: usize, ) -> isize583446cafc2SAndreas Hindborg     unsafe extern "C" fn store(
584446cafc2SAndreas Hindborg         item: *mut bindings::config_item,
585446cafc2SAndreas Hindborg         page: *const kernel::ffi::c_char,
586446cafc2SAndreas Hindborg         size: usize,
587446cafc2SAndreas Hindborg     ) -> isize {
588446cafc2SAndreas Hindborg         let c_group: *mut bindings::config_group =
589446cafc2SAndreas Hindborg         // SAFETY: By function safety requirements, `item` is embedded in a
590446cafc2SAndreas Hindborg         // `config_group`.
591*ec7714e4SLinus Torvalds             unsafe { container_of!(item, bindings::config_group, cg_item) };
592446cafc2SAndreas Hindborg 
593446cafc2SAndreas Hindborg         // SAFETY: The function safety requirements for this function satisfy
594446cafc2SAndreas Hindborg         // the conditions for this call.
595446cafc2SAndreas Hindborg         let data: &Data = unsafe { get_group_data(c_group) };
596446cafc2SAndreas Hindborg 
597446cafc2SAndreas Hindborg         let ret = O::store(
598446cafc2SAndreas Hindborg             data,
599446cafc2SAndreas Hindborg             // SAFETY: By function safety requirements, `page` is readable
600446cafc2SAndreas Hindborg             // for at least `size`.
601446cafc2SAndreas Hindborg             unsafe { core::slice::from_raw_parts(page.cast(), size) },
602446cafc2SAndreas Hindborg         );
603446cafc2SAndreas Hindborg 
604446cafc2SAndreas Hindborg         match ret {
605446cafc2SAndreas Hindborg             Ok(()) => size as isize,
606446cafc2SAndreas Hindborg             Err(err) => err.to_errno() as isize,
607446cafc2SAndreas Hindborg         }
608446cafc2SAndreas Hindborg     }
609446cafc2SAndreas Hindborg 
610446cafc2SAndreas Hindborg     /// Create a new attribute.
611446cafc2SAndreas Hindborg     ///
612446cafc2SAndreas Hindborg     /// The attribute will appear as a file with name given by `name`.
new(name: &'static CStr) -> Self613446cafc2SAndreas Hindborg     pub const fn new(name: &'static CStr) -> Self {
614446cafc2SAndreas Hindborg         Self {
615446cafc2SAndreas Hindborg             attribute: Opaque::new(bindings::configfs_attribute {
616446cafc2SAndreas Hindborg                 ca_name: name.as_char_ptr(),
617446cafc2SAndreas Hindborg                 ca_owner: core::ptr::null_mut(),
618446cafc2SAndreas Hindborg                 ca_mode: 0o660,
619446cafc2SAndreas Hindborg                 show: Some(Self::show),
620446cafc2SAndreas Hindborg                 store: if O::HAS_STORE {
621446cafc2SAndreas Hindborg                     Some(Self::store)
622446cafc2SAndreas Hindborg                 } else {
623446cafc2SAndreas Hindborg                     None
624446cafc2SAndreas Hindborg                 },
625446cafc2SAndreas Hindborg             }),
626446cafc2SAndreas Hindborg             _p: PhantomData,
627446cafc2SAndreas Hindborg         }
628446cafc2SAndreas Hindborg     }
629446cafc2SAndreas Hindborg }
630446cafc2SAndreas Hindborg 
631446cafc2SAndreas Hindborg /// Operations supported by an attribute.
632446cafc2SAndreas Hindborg ///
633446cafc2SAndreas Hindborg /// Implement this trait on type and pass that type as generic parameter when
634446cafc2SAndreas Hindborg /// creating an [`Attribute`]. The type carrying the implementation serve no
635446cafc2SAndreas Hindborg /// purpose other than specifying the attribute operations.
636446cafc2SAndreas Hindborg ///
637446cafc2SAndreas Hindborg /// This trait must be implemented on the `Data` type of for types that
638446cafc2SAndreas Hindborg /// implement `HasGroup<Data>`. The trait must be implemented once for each
639446cafc2SAndreas Hindborg /// attribute of the group. The constant type parameter `ID` maps the
640446cafc2SAndreas Hindborg /// implementation to a specific `Attribute`. `ID` must be passed when declaring
641446cafc2SAndreas Hindborg /// attributes via the [`kernel::configfs_attrs`] macro, to tie
642446cafc2SAndreas Hindborg /// `AttributeOperations` implementations to concrete named attributes.
643446cafc2SAndreas Hindborg #[vtable]
644446cafc2SAndreas Hindborg pub trait AttributeOperations<const ID: u64 = 0> {
645446cafc2SAndreas Hindborg     /// The type of the object that contains the field that is backing the
646446cafc2SAndreas Hindborg     /// attribute for this operation.
647446cafc2SAndreas Hindborg     type Data;
648446cafc2SAndreas Hindborg 
649446cafc2SAndreas Hindborg     /// Renders the value of an attribute.
650446cafc2SAndreas Hindborg     ///
651446cafc2SAndreas Hindborg     /// This function is called by the kernel to read the value of an attribute.
652446cafc2SAndreas Hindborg     ///
653446cafc2SAndreas Hindborg     /// Implementations should write the rendering of the attribute to `page`
654446cafc2SAndreas Hindborg     /// and return the number of bytes written.
show(data: &Self::Data, page: &mut [u8; PAGE_SIZE]) -> Result<usize>655446cafc2SAndreas Hindborg     fn show(data: &Self::Data, page: &mut [u8; PAGE_SIZE]) -> Result<usize>;
656446cafc2SAndreas Hindborg 
657446cafc2SAndreas Hindborg     /// Stores the value of an attribute.
658446cafc2SAndreas Hindborg     ///
659446cafc2SAndreas Hindborg     /// This function is called by the kernel to update the value of an attribute.
660446cafc2SAndreas Hindborg     ///
661446cafc2SAndreas Hindborg     /// Implementations should parse the value from `page` and update internal
662446cafc2SAndreas Hindborg     /// state to reflect the parsed value.
store(_data: &Self::Data, _page: &[u8]) -> Result663446cafc2SAndreas Hindborg     fn store(_data: &Self::Data, _page: &[u8]) -> Result {
664446cafc2SAndreas Hindborg         kernel::build_error!(kernel::error::VTABLE_DEFAULT_ERROR)
665446cafc2SAndreas Hindborg     }
666446cafc2SAndreas Hindborg }
667446cafc2SAndreas Hindborg 
668446cafc2SAndreas Hindborg /// A list of attributes.
669446cafc2SAndreas Hindborg ///
670446cafc2SAndreas Hindborg /// This type is used to construct a new [`ItemType`]. It represents a list of
671446cafc2SAndreas Hindborg /// [`Attribute`] that will appear in the directory representing a [`Group`].
672446cafc2SAndreas Hindborg /// Users should not directly instantiate this type, rather they should use the
673446cafc2SAndreas Hindborg /// [`kernel::configfs_attrs`] macro to declare a static set of attributes for a
674446cafc2SAndreas Hindborg /// group.
675446cafc2SAndreas Hindborg ///
676446cafc2SAndreas Hindborg /// # Note
677446cafc2SAndreas Hindborg ///
678446cafc2SAndreas Hindborg /// Instances of this type are constructed statically at compile by the
679446cafc2SAndreas Hindborg /// [`kernel::configfs_attrs`] macro.
680446cafc2SAndreas Hindborg #[repr(transparent)]
681446cafc2SAndreas Hindborg pub struct AttributeList<const N: usize, Data>(
682446cafc2SAndreas Hindborg     /// Null terminated Array of pointers to [`Attribute`]. The type is [`c_void`]
683446cafc2SAndreas Hindborg     /// to conform to the C API.
684446cafc2SAndreas Hindborg     UnsafeCell<[*mut kernel::ffi::c_void; N]>,
685446cafc2SAndreas Hindborg     PhantomData<Data>,
686446cafc2SAndreas Hindborg );
687446cafc2SAndreas Hindborg 
688446cafc2SAndreas Hindborg // SAFETY: Ownership of `AttributeList` can safely be transferred to other threads.
689446cafc2SAndreas Hindborg unsafe impl<const N: usize, Data> Send for AttributeList<N, Data> {}
690446cafc2SAndreas Hindborg 
691446cafc2SAndreas Hindborg // SAFETY: We do not provide any operations on `AttributeList` that need synchronization.
692446cafc2SAndreas Hindborg unsafe impl<const N: usize, Data> Sync for AttributeList<N, Data> {}
693446cafc2SAndreas Hindborg 
694446cafc2SAndreas Hindborg impl<const N: usize, Data> AttributeList<N, Data> {
695446cafc2SAndreas Hindborg     /// # Safety
696446cafc2SAndreas Hindborg     ///
697446cafc2SAndreas Hindborg     /// This function must only be called by the [`kernel::configfs_attrs`]
698446cafc2SAndreas Hindborg     /// macro.
699446cafc2SAndreas Hindborg     #[doc(hidden)]
new() -> Self700446cafc2SAndreas Hindborg     pub const unsafe fn new() -> Self {
701446cafc2SAndreas Hindborg         Self(UnsafeCell::new([core::ptr::null_mut(); N]), PhantomData)
702446cafc2SAndreas Hindborg     }
703446cafc2SAndreas Hindborg 
704446cafc2SAndreas Hindborg     /// # Safety
705446cafc2SAndreas Hindborg     ///
706446cafc2SAndreas Hindborg     /// The caller must ensure that there are no other concurrent accesses to
707446cafc2SAndreas Hindborg     /// `self`. That is, the caller has exclusive access to `self.`
708446cafc2SAndreas Hindborg     #[doc(hidden)]
add<const I: usize, const ID: u64, O>( &'static self, attribute: &'static Attribute<ID, O, Data>, ) where O: AttributeOperations<ID, Data = Data>,709446cafc2SAndreas Hindborg     pub const unsafe fn add<const I: usize, const ID: u64, O>(
710446cafc2SAndreas Hindborg         &'static self,
711446cafc2SAndreas Hindborg         attribute: &'static Attribute<ID, O, Data>,
712446cafc2SAndreas Hindborg     ) where
713446cafc2SAndreas Hindborg         O: AttributeOperations<ID, Data = Data>,
714446cafc2SAndreas Hindborg     {
715446cafc2SAndreas Hindborg         // We need a space at the end of our list for a null terminator.
716446cafc2SAndreas Hindborg         const { assert!(I < N - 1, "Invalid attribute index") };
717446cafc2SAndreas Hindborg 
718446cafc2SAndreas Hindborg         // SAFETY: By function safety requirements, we have exclusive access to
719446cafc2SAndreas Hindborg         // `self` and the reference created below will be exclusive.
720446cafc2SAndreas Hindborg         unsafe {
721446cafc2SAndreas Hindborg             (&mut *self.0.get())[I] = (attribute as *const Attribute<ID, O, Data>)
722446cafc2SAndreas Hindborg                 .cast_mut()
723446cafc2SAndreas Hindborg                 .cast()
724446cafc2SAndreas Hindborg         };
725446cafc2SAndreas Hindborg     }
726446cafc2SAndreas Hindborg }
727446cafc2SAndreas Hindborg 
728446cafc2SAndreas Hindborg /// A representation of the attributes that will appear in a [`Group`] or
729446cafc2SAndreas Hindborg /// [`Subsystem`].
730446cafc2SAndreas Hindborg ///
731446cafc2SAndreas Hindborg /// Users should not directly instantiate objects of this type. Rather, they
732446cafc2SAndreas Hindborg /// should use the [`kernel::configfs_attrs`] macro to statically declare the
733446cafc2SAndreas Hindborg /// shape of a [`Group`] or [`Subsystem`].
734446cafc2SAndreas Hindborg #[pin_data]
735446cafc2SAndreas Hindborg pub struct ItemType<Container, Data> {
736446cafc2SAndreas Hindborg     #[pin]
737446cafc2SAndreas Hindborg     item_type: Opaque<bindings::config_item_type>,
738446cafc2SAndreas Hindborg     _p: PhantomData<(Container, Data)>,
739446cafc2SAndreas Hindborg }
740446cafc2SAndreas Hindborg 
741446cafc2SAndreas Hindborg // SAFETY: We do not provide any operations on `ItemType` that need synchronization.
742446cafc2SAndreas Hindborg unsafe impl<Container, Data> Sync for ItemType<Container, Data> {}
743446cafc2SAndreas Hindborg 
744446cafc2SAndreas Hindborg // SAFETY: Ownership of `ItemType` can safely be transferred to other threads.
745446cafc2SAndreas Hindborg unsafe impl<Container, Data> Send for ItemType<Container, Data> {}
746446cafc2SAndreas Hindborg 
747446cafc2SAndreas Hindborg macro_rules! impl_item_type {
748446cafc2SAndreas Hindborg     ($tpe:ty) => {
749446cafc2SAndreas Hindborg         impl<Data> ItemType<$tpe, Data> {
750446cafc2SAndreas Hindborg             #[doc(hidden)]
751446cafc2SAndreas Hindborg             pub const fn new_with_child_ctor<const N: usize, Child>(
752446cafc2SAndreas Hindborg                 owner: &'static ThisModule,
753446cafc2SAndreas Hindborg                 attributes: &'static AttributeList<N, Data>,
754446cafc2SAndreas Hindborg             ) -> Self
755446cafc2SAndreas Hindborg             where
756446cafc2SAndreas Hindborg                 Data: GroupOperations<Child = Child>,
757446cafc2SAndreas Hindborg                 Child: 'static,
758446cafc2SAndreas Hindborg             {
759446cafc2SAndreas Hindborg                 Self {
760446cafc2SAndreas Hindborg                     item_type: Opaque::new(bindings::config_item_type {
761446cafc2SAndreas Hindborg                         ct_owner: owner.as_ptr(),
762446cafc2SAndreas Hindborg                         ct_group_ops: GroupOperationsVTable::<Data, Child>::vtable_ptr().cast_mut(),
763446cafc2SAndreas Hindborg                         ct_item_ops: ItemOperationsVTable::<$tpe, Data>::vtable_ptr().cast_mut(),
764446cafc2SAndreas Hindborg                         ct_attrs: (attributes as *const AttributeList<N, Data>)
765446cafc2SAndreas Hindborg                             .cast_mut()
766446cafc2SAndreas Hindborg                             .cast(),
767446cafc2SAndreas Hindborg                         ct_bin_attrs: core::ptr::null_mut(),
768446cafc2SAndreas Hindborg                     }),
769446cafc2SAndreas Hindborg                     _p: PhantomData,
770446cafc2SAndreas Hindborg                 }
771446cafc2SAndreas Hindborg             }
772446cafc2SAndreas Hindborg 
773446cafc2SAndreas Hindborg             #[doc(hidden)]
774446cafc2SAndreas Hindborg             pub const fn new<const N: usize>(
775446cafc2SAndreas Hindborg                 owner: &'static ThisModule,
776446cafc2SAndreas Hindborg                 attributes: &'static AttributeList<N, Data>,
777446cafc2SAndreas Hindborg             ) -> Self {
778446cafc2SAndreas Hindborg                 Self {
779446cafc2SAndreas Hindborg                     item_type: Opaque::new(bindings::config_item_type {
780446cafc2SAndreas Hindborg                         ct_owner: owner.as_ptr(),
781446cafc2SAndreas Hindborg                         ct_group_ops: core::ptr::null_mut(),
782446cafc2SAndreas Hindborg                         ct_item_ops: ItemOperationsVTable::<$tpe, Data>::vtable_ptr().cast_mut(),
783446cafc2SAndreas Hindborg                         ct_attrs: (attributes as *const AttributeList<N, Data>)
784446cafc2SAndreas Hindborg                             .cast_mut()
785446cafc2SAndreas Hindborg                             .cast(),
786446cafc2SAndreas Hindborg                         ct_bin_attrs: core::ptr::null_mut(),
787446cafc2SAndreas Hindborg                     }),
788446cafc2SAndreas Hindborg                     _p: PhantomData,
789446cafc2SAndreas Hindborg                 }
790446cafc2SAndreas Hindborg             }
791446cafc2SAndreas Hindborg         }
792446cafc2SAndreas Hindborg     };
793446cafc2SAndreas Hindborg }
794446cafc2SAndreas Hindborg 
795446cafc2SAndreas Hindborg impl_item_type!(Subsystem<Data>);
796446cafc2SAndreas Hindborg impl_item_type!(Group<Data>);
797446cafc2SAndreas Hindborg 
798446cafc2SAndreas Hindborg impl<Container, Data> ItemType<Container, Data> {
as_ptr(&self) -> *const bindings::config_item_type799446cafc2SAndreas Hindborg     fn as_ptr(&self) -> *const bindings::config_item_type {
800446cafc2SAndreas Hindborg         self.item_type.get()
801446cafc2SAndreas Hindborg     }
802446cafc2SAndreas Hindborg }
803446cafc2SAndreas Hindborg 
804446cafc2SAndreas Hindborg /// Define a list of configfs attributes statically.
805446cafc2SAndreas Hindborg ///
806446cafc2SAndreas Hindborg /// Invoking the macro in the following manner:
807446cafc2SAndreas Hindborg ///
808446cafc2SAndreas Hindborg /// ```ignore
809446cafc2SAndreas Hindborg /// let item_type = configfs_attrs! {
810446cafc2SAndreas Hindborg ///     container: configfs::Subsystem<Configuration>,
811446cafc2SAndreas Hindborg ///     data: Configuration,
812446cafc2SAndreas Hindborg ///     child: Child,
813446cafc2SAndreas Hindborg ///     attributes: [
814446cafc2SAndreas Hindborg ///         message: 0,
815446cafc2SAndreas Hindborg ///         bar: 1,
816446cafc2SAndreas Hindborg ///     ],
817446cafc2SAndreas Hindborg /// };
818446cafc2SAndreas Hindborg /// ```
819446cafc2SAndreas Hindborg ///
820446cafc2SAndreas Hindborg /// Expands the following output:
821446cafc2SAndreas Hindborg ///
822446cafc2SAndreas Hindborg /// ```ignore
823446cafc2SAndreas Hindborg /// let item_type = {
824446cafc2SAndreas Hindborg ///     static CONFIGURATION_MESSAGE_ATTR: kernel::configfs::Attribute<
825446cafc2SAndreas Hindborg ///         0,
826446cafc2SAndreas Hindborg ///         Configuration,
827446cafc2SAndreas Hindborg ///         Configuration,
828446cafc2SAndreas Hindborg ///     > = unsafe {
829446cafc2SAndreas Hindborg ///         kernel::configfs::Attribute::new({
830446cafc2SAndreas Hindborg ///             const S: &str = "message\u{0}";
831446cafc2SAndreas Hindborg ///             const C: &kernel::str::CStr = match kernel::str::CStr::from_bytes_with_nul(
832446cafc2SAndreas Hindborg ///                 S.as_bytes()
833446cafc2SAndreas Hindborg ///             ) {
834446cafc2SAndreas Hindborg ///                 Ok(v) => v,
835446cafc2SAndreas Hindborg ///                 Err(_) => {
836446cafc2SAndreas Hindborg ///                     core::panicking::panic_fmt(core::const_format_args!(
837446cafc2SAndreas Hindborg ///                         "string contains interior NUL"
838446cafc2SAndreas Hindborg ///                     ));
839446cafc2SAndreas Hindborg ///                 }
840446cafc2SAndreas Hindborg ///             };
841446cafc2SAndreas Hindborg ///             C
842446cafc2SAndreas Hindborg ///         })
843446cafc2SAndreas Hindborg ///     };
844446cafc2SAndreas Hindborg ///
845446cafc2SAndreas Hindborg ///     static CONFIGURATION_BAR_ATTR: kernel::configfs::Attribute<
846446cafc2SAndreas Hindborg ///             1,
847446cafc2SAndreas Hindborg ///             Configuration,
848446cafc2SAndreas Hindborg ///             Configuration
849446cafc2SAndreas Hindborg ///     > = unsafe {
850446cafc2SAndreas Hindborg ///         kernel::configfs::Attribute::new({
851446cafc2SAndreas Hindborg ///             const S: &str = "bar\u{0}";
852446cafc2SAndreas Hindborg ///             const C: &kernel::str::CStr = match kernel::str::CStr::from_bytes_with_nul(
853446cafc2SAndreas Hindborg ///                 S.as_bytes()
854446cafc2SAndreas Hindborg ///             ) {
855446cafc2SAndreas Hindborg ///                 Ok(v) => v,
856446cafc2SAndreas Hindborg ///                 Err(_) => {
857446cafc2SAndreas Hindborg ///                     core::panicking::panic_fmt(core::const_format_args!(
858446cafc2SAndreas Hindborg ///                         "string contains interior NUL"
859446cafc2SAndreas Hindborg ///                     ));
860446cafc2SAndreas Hindborg ///                 }
861446cafc2SAndreas Hindborg ///             };
862446cafc2SAndreas Hindborg ///             C
863446cafc2SAndreas Hindborg ///         })
864446cafc2SAndreas Hindborg ///     };
865446cafc2SAndreas Hindborg ///
866446cafc2SAndreas Hindborg ///     const N: usize = (1usize + (1usize + 0usize)) + 1usize;
867446cafc2SAndreas Hindborg ///
868446cafc2SAndreas Hindborg ///     static CONFIGURATION_ATTRS: kernel::configfs::AttributeList<N, Configuration> =
869446cafc2SAndreas Hindborg ///         unsafe { kernel::configfs::AttributeList::new() };
870446cafc2SAndreas Hindborg ///
871446cafc2SAndreas Hindborg ///     {
872446cafc2SAndreas Hindborg ///         const N: usize = 0usize;
873446cafc2SAndreas Hindborg ///         unsafe { CONFIGURATION_ATTRS.add::<N, 0, _>(&CONFIGURATION_MESSAGE_ATTR) };
874446cafc2SAndreas Hindborg ///     }
875446cafc2SAndreas Hindborg ///
876446cafc2SAndreas Hindborg ///     {
877446cafc2SAndreas Hindborg ///         const N: usize = (1usize + 0usize);
878446cafc2SAndreas Hindborg ///         unsafe { CONFIGURATION_ATTRS.add::<N, 1, _>(&CONFIGURATION_BAR_ATTR) };
879446cafc2SAndreas Hindborg ///     }
880446cafc2SAndreas Hindborg ///
881446cafc2SAndreas Hindborg ///     static CONFIGURATION_TPE:
882446cafc2SAndreas Hindborg ///       kernel::configfs::ItemType<configfs::Subsystem<Configuration> ,Configuration>
883446cafc2SAndreas Hindborg ///         = kernel::configfs::ItemType::<
884446cafc2SAndreas Hindborg ///                 configfs::Subsystem<Configuration>,
885446cafc2SAndreas Hindborg ///                 Configuration
886446cafc2SAndreas Hindborg ///                 >::new_with_child_ctor::<N,Child>(
887446cafc2SAndreas Hindborg ///             &THIS_MODULE,
888446cafc2SAndreas Hindborg ///             &CONFIGURATION_ATTRS
889446cafc2SAndreas Hindborg ///         );
890446cafc2SAndreas Hindborg ///
891446cafc2SAndreas Hindborg ///     &CONFIGURATION_TPE
892446cafc2SAndreas Hindborg /// }
893446cafc2SAndreas Hindborg /// ```
894446cafc2SAndreas Hindborg #[macro_export]
895446cafc2SAndreas Hindborg macro_rules! configfs_attrs {
896446cafc2SAndreas Hindborg     (
897446cafc2SAndreas Hindborg         container: $container:ty,
898446cafc2SAndreas Hindborg         data: $data:ty,
899446cafc2SAndreas Hindborg         attributes: [
900446cafc2SAndreas Hindborg             $($name:ident: $attr:literal),* $(,)?
901446cafc2SAndreas Hindborg         ] $(,)?
902446cafc2SAndreas Hindborg     ) => {
903446cafc2SAndreas Hindborg         $crate::configfs_attrs!(
904446cafc2SAndreas Hindborg             count:
905446cafc2SAndreas Hindborg             @container($container),
906446cafc2SAndreas Hindborg             @data($data),
907446cafc2SAndreas Hindborg             @child(),
908446cafc2SAndreas Hindborg             @no_child(x),
909446cafc2SAndreas Hindborg             @attrs($($name $attr)*),
910446cafc2SAndreas Hindborg             @eat($($name $attr,)*),
911446cafc2SAndreas Hindborg             @assign(),
912446cafc2SAndreas Hindborg             @cnt(0usize),
913446cafc2SAndreas Hindborg         )
914446cafc2SAndreas Hindborg     };
915446cafc2SAndreas Hindborg     (
916446cafc2SAndreas Hindborg         container: $container:ty,
917446cafc2SAndreas Hindborg         data: $data:ty,
918446cafc2SAndreas Hindborg         child: $child:ty,
919446cafc2SAndreas Hindborg         attributes: [
920446cafc2SAndreas Hindborg             $($name:ident: $attr:literal),* $(,)?
921446cafc2SAndreas Hindborg         ] $(,)?
922446cafc2SAndreas Hindborg     ) => {
923446cafc2SAndreas Hindborg         $crate::configfs_attrs!(
924446cafc2SAndreas Hindborg             count:
925446cafc2SAndreas Hindborg             @container($container),
926446cafc2SAndreas Hindborg             @data($data),
927446cafc2SAndreas Hindborg             @child($child),
928446cafc2SAndreas Hindborg             @no_child(),
929446cafc2SAndreas Hindborg             @attrs($($name $attr)*),
930446cafc2SAndreas Hindborg             @eat($($name $attr,)*),
931446cafc2SAndreas Hindborg             @assign(),
932446cafc2SAndreas Hindborg             @cnt(0usize),
933446cafc2SAndreas Hindborg         )
934446cafc2SAndreas Hindborg     };
935446cafc2SAndreas Hindborg     (count:
936446cafc2SAndreas Hindborg      @container($container:ty),
937446cafc2SAndreas Hindborg      @data($data:ty),
938446cafc2SAndreas Hindborg      @child($($child:ty)?),
939446cafc2SAndreas Hindborg      @no_child($($no_child:ident)?),
940446cafc2SAndreas Hindborg      @attrs($($aname:ident $aattr:literal)*),
941446cafc2SAndreas Hindborg      @eat($name:ident $attr:literal, $($rname:ident $rattr:literal,)*),
942446cafc2SAndreas Hindborg      @assign($($assign:block)*),
943446cafc2SAndreas Hindborg      @cnt($cnt:expr),
944446cafc2SAndreas Hindborg     ) => {
945446cafc2SAndreas Hindborg         $crate::configfs_attrs!(
946446cafc2SAndreas Hindborg             count:
947446cafc2SAndreas Hindborg             @container($container),
948446cafc2SAndreas Hindborg             @data($data),
949446cafc2SAndreas Hindborg             @child($($child)?),
950446cafc2SAndreas Hindborg             @no_child($($no_child)?),
951446cafc2SAndreas Hindborg             @attrs($($aname $aattr)*),
952446cafc2SAndreas Hindborg             @eat($($rname $rattr,)*),
953446cafc2SAndreas Hindborg             @assign($($assign)* {
954446cafc2SAndreas Hindborg                 const N: usize = $cnt;
955446cafc2SAndreas Hindborg                 // The following macro text expands to a call to `Attribute::add`.
956446cafc2SAndreas Hindborg 
957446cafc2SAndreas Hindborg                 // SAFETY: By design of this macro, the name of the variable we
958446cafc2SAndreas Hindborg                 // invoke the `add` method on below, is not visible outside of
959446cafc2SAndreas Hindborg                 // the macro expansion. The macro does not operate concurrently
960446cafc2SAndreas Hindborg                 // on this variable, and thus we have exclusive access to the
961446cafc2SAndreas Hindborg                 // variable.
962446cafc2SAndreas Hindborg                 unsafe {
963446cafc2SAndreas Hindborg                     $crate::macros::paste!(
964446cafc2SAndreas Hindborg                         [< $data:upper _ATTRS >]
965446cafc2SAndreas Hindborg                             .add::<N, $attr, _>(&[< $data:upper _ $name:upper _ATTR >])
966446cafc2SAndreas Hindborg                     )
967446cafc2SAndreas Hindborg                 };
968446cafc2SAndreas Hindborg             }),
969446cafc2SAndreas Hindborg             @cnt(1usize + $cnt),
970446cafc2SAndreas Hindborg         )
971446cafc2SAndreas Hindborg     };
972446cafc2SAndreas Hindborg     (count:
973446cafc2SAndreas Hindborg      @container($container:ty),
974446cafc2SAndreas Hindborg      @data($data:ty),
975446cafc2SAndreas Hindborg      @child($($child:ty)?),
976446cafc2SAndreas Hindborg      @no_child($($no_child:ident)?),
977446cafc2SAndreas Hindborg      @attrs($($aname:ident $aattr:literal)*),
978446cafc2SAndreas Hindborg      @eat(),
979446cafc2SAndreas Hindborg      @assign($($assign:block)*),
980446cafc2SAndreas Hindborg      @cnt($cnt:expr),
981446cafc2SAndreas Hindborg     ) =>
982446cafc2SAndreas Hindborg     {
983446cafc2SAndreas Hindborg         $crate::configfs_attrs!(
984446cafc2SAndreas Hindborg             final:
985446cafc2SAndreas Hindborg             @container($container),
986446cafc2SAndreas Hindborg             @data($data),
987446cafc2SAndreas Hindborg             @child($($child)?),
988446cafc2SAndreas Hindborg             @no_child($($no_child)?),
989446cafc2SAndreas Hindborg             @attrs($($aname $aattr)*),
990446cafc2SAndreas Hindborg             @assign($($assign)*),
991446cafc2SAndreas Hindborg             @cnt($cnt),
992446cafc2SAndreas Hindborg         )
993446cafc2SAndreas Hindborg     };
994446cafc2SAndreas Hindborg     (final:
995446cafc2SAndreas Hindborg      @container($container:ty),
996446cafc2SAndreas Hindborg      @data($data:ty),
997446cafc2SAndreas Hindborg      @child($($child:ty)?),
998446cafc2SAndreas Hindborg      @no_child($($no_child:ident)?),
999446cafc2SAndreas Hindborg      @attrs($($name:ident $attr:literal)*),
1000446cafc2SAndreas Hindborg      @assign($($assign:block)*),
1001446cafc2SAndreas Hindborg      @cnt($cnt:expr),
1002446cafc2SAndreas Hindborg     ) =>
1003446cafc2SAndreas Hindborg     {
1004446cafc2SAndreas Hindborg         $crate::macros::paste!{
1005446cafc2SAndreas Hindborg             {
1006446cafc2SAndreas Hindborg                 $(
1007446cafc2SAndreas Hindborg                     // SAFETY: We are expanding `configfs_attrs`.
1008446cafc2SAndreas Hindborg                     static [< $data:upper _ $name:upper _ATTR >]:
1009446cafc2SAndreas Hindborg                         $crate::configfs::Attribute<$attr, $data, $data> =
1010446cafc2SAndreas Hindborg                             unsafe {
1011446cafc2SAndreas Hindborg                                 $crate::configfs::Attribute::new(c_str!(::core::stringify!($name)))
1012446cafc2SAndreas Hindborg                             };
1013446cafc2SAndreas Hindborg                 )*
1014446cafc2SAndreas Hindborg 
1015446cafc2SAndreas Hindborg 
1016446cafc2SAndreas Hindborg                 // We need space for a null terminator.
1017446cafc2SAndreas Hindborg                 const N: usize = $cnt + 1usize;
1018446cafc2SAndreas Hindborg 
1019446cafc2SAndreas Hindborg                 // SAFETY: We are expanding `configfs_attrs`.
1020446cafc2SAndreas Hindborg                 static [< $data:upper _ATTRS >]:
1021446cafc2SAndreas Hindborg                 $crate::configfs::AttributeList<N, $data> =
1022446cafc2SAndreas Hindborg                     unsafe { $crate::configfs::AttributeList::new() };
1023446cafc2SAndreas Hindborg 
1024446cafc2SAndreas Hindborg                 $($assign)*
1025446cafc2SAndreas Hindborg 
1026446cafc2SAndreas Hindborg                 $(
1027446cafc2SAndreas Hindborg                     const [<$no_child:upper>]: bool = true;
1028446cafc2SAndreas Hindborg 
1029446cafc2SAndreas Hindborg                     static [< $data:upper _TPE >] : $crate::configfs::ItemType<$container, $data>  =
1030446cafc2SAndreas Hindborg                         $crate::configfs::ItemType::<$container, $data>::new::<N>(
1031446cafc2SAndreas Hindborg                             &THIS_MODULE, &[<$ data:upper _ATTRS >]
1032446cafc2SAndreas Hindborg                         );
1033446cafc2SAndreas Hindborg                 )?
1034446cafc2SAndreas Hindborg 
1035446cafc2SAndreas Hindborg                 $(
1036446cafc2SAndreas Hindborg                     static [< $data:upper _TPE >]:
1037446cafc2SAndreas Hindborg                         $crate::configfs::ItemType<$container, $data>  =
1038446cafc2SAndreas Hindborg                             $crate::configfs::ItemType::<$container, $data>::
1039446cafc2SAndreas Hindborg                             new_with_child_ctor::<N, $child>(
1040446cafc2SAndreas Hindborg                                 &THIS_MODULE, &[<$ data:upper _ATTRS >]
1041446cafc2SAndreas Hindborg                             );
1042446cafc2SAndreas Hindborg                 )?
1043446cafc2SAndreas Hindborg 
1044446cafc2SAndreas Hindborg                 & [< $data:upper _TPE >]
1045446cafc2SAndreas Hindborg             }
1046446cafc2SAndreas Hindborg         }
1047446cafc2SAndreas Hindborg     };
1048446cafc2SAndreas Hindborg 
1049446cafc2SAndreas Hindborg }
1050