use crate::alloc::flags;
use crate::container_of;
use crate::page::PAGE_SIZE;
use crate::prelude::*;
use crate::str::CString;
use crate::sync::Arc;
use crate::sync::ArcBorrow;
use crate::types::Opaque;
use core::cell::UnsafeCell;
use core::marker::PhantomData;
#[pin_data(PinnedDrop)]
pub struct Subsystem<Data> {
#[pin]
subsystem: Opaque<bindings::configfs_subsystem>,
#[pin]
data: Data,
}
unsafe impl<Data> Sync for Subsystem<Data> {}
unsafe impl<Data> Send for Subsystem<Data> {}
impl<Data> Subsystem<Data> {
pub fn new(
name: &'static CStr,
item_type: &'static ItemType<Subsystem<Data>, Data>,
data: impl PinInit<Data, Error>,
) -> impl PinInit<Self, Error> {
try_pin_init!(Self {
subsystem <- pin_init::init_zeroed().chain(
|place: &mut Opaque<bindings::configfs_subsystem>| {
unsafe {
bindings::config_group_init_type_name(
&mut (*place.get()).su_group,
name.as_char_ptr(),
item_type.as_ptr(),
)
};
unsafe {
bindings::__mutex_init(
&mut (*place.get()).su_mutex,
kernel::optional_name!().as_char_ptr(),
kernel::static_lock_class!().as_ptr(),
)
}
Ok(())
}
),
data <- data,
})
.pin_chain(|this| {
crate::error::to_result(
unsafe { bindings::configfs_register_subsystem(this.subsystem.get()) },
)
})
}
}
#[pinned_drop]
impl<Data> PinnedDrop for Subsystem<Data> {
fn drop(self: Pin<&mut Self>) {
unsafe { bindings::configfs_unregister_subsystem(self.subsystem.get()) };
unsafe { bindings::mutex_destroy(&raw mut (*self.subsystem.get()).su_mutex) };
}
}
pub unsafe trait HasGroup<Data> {
unsafe fn group(this: *const Self) -> *const bindings::config_group;
unsafe fn container_of(group: *const bindings::config_group) -> *const Self;
}
unsafe impl<Data> HasGroup<Data> for Subsystem<Data> {
unsafe fn group(this: *const Self) -> *const bindings::config_group {
unsafe { &raw const (*(*this).subsystem.get()).su_group }
}
unsafe fn container_of(group: *const bindings::config_group) -> *const Self {
let c_subsys_ptr = unsafe { container_of!(group, bindings::configfs_subsystem, su_group) };
let opaque_ptr = c_subsys_ptr.cast::<Opaque<bindings::configfs_subsystem>>();
unsafe { container_of!(opaque_ptr, Subsystem<Data>, subsystem) }
}
}
#[pin_data]
pub struct Group<Data> {
#[pin]
group: Opaque<bindings::config_group>,
#[pin]
data: Data,
}
impl<Data> Group<Data> {
pub fn new(
name: CString,
item_type: &'static ItemType<Group<Data>, Data>,
data: impl PinInit<Data, Error>,
) -> impl PinInit<Self, Error> {
try_pin_init!(Self {
group <- pin_init::init_zeroed().chain(|v: &mut Opaque<bindings::config_group>| {
let place = v.get();
let name = name.to_bytes_with_nul().as_ptr();
unsafe {
bindings::config_group_init_type_name(place, name.cast(), item_type.as_ptr())
};
Ok(())
}),
data <- data,
})
}
}
unsafe impl<Data> HasGroup<Data> for Group<Data> {
unsafe fn group(this: *const Self) -> *const bindings::config_group {
Opaque::cast_into(
unsafe { &raw const (*this).group },
)
}
unsafe fn container_of(group: *const bindings::config_group) -> *const Self {
let opaque_ptr = group.cast::<Opaque<bindings::config_group>>();
unsafe { container_of!(opaque_ptr, Self, group) }
}
}
unsafe fn get_group_data<'a, Parent>(this: *mut bindings::config_group) -> &'a Parent {
let is_root = unsafe { (*this).cg_subsys.is_null() };
if !is_root {
unsafe { &(*Group::<Parent>::container_of(this)).data }
} else {
unsafe { &(*Subsystem::container_of(this)).data }
}
}
struct GroupOperationsVTable<Parent, Child>(PhantomData<(Parent, Child)>);
impl<Parent, Child> GroupOperationsVTable<Parent, Child>
where
Parent: GroupOperations<Child = Child>,
Child: 'static,
{
unsafe extern "C" fn make_group(
this: *mut bindings::config_group,
name: *const kernel::ffi::c_char,
) -> *mut bindings::config_group {
let parent_data = unsafe { get_group_data(this) };
let group_init = match Parent::make_group(
parent_data,
unsafe { CStr::from_char_ptr(name) },
) {
Ok(init) => init,
Err(e) => return e.to_ptr(),
};
let child_group = <Arc<Group<Child>> as InPlaceInit<Group<Child>>>::try_pin_init(
group_init,
flags::GFP_KERNEL,
);
match child_group {
Ok(child_group) => {
let child_group_ptr = child_group.into_raw();
unsafe { Group::<Child>::group(child_group_ptr) }.cast_mut()
}
Err(e) => e.to_ptr(),
}
}
unsafe extern "C" fn drop_item(
this: *mut bindings::config_group,
item: *mut bindings::config_item,
) {
let parent_data = unsafe { get_group_data(this) };
let c_child_group_ptr = unsafe { container_of!(item, bindings::config_group, cg_item) };
let r_child_group_ptr = unsafe { Group::<Child>::container_of(c_child_group_ptr) };
if Parent::HAS_DROP_ITEM {
let arc: Arc<Group<Child>> = unsafe { Arc::from_raw(r_child_group_ptr.cast_mut()) };
Parent::drop_item(parent_data, arc.as_arc_borrow());
arc.into_raw();
}
unsafe { bindings::config_item_put(item) };
}
const VTABLE: bindings::configfs_group_operations = bindings::configfs_group_operations {
make_item: None,
make_group: Some(Self::make_group),
disconnect_notify: None,
drop_item: Some(Self::drop_item),
is_visible: None,
is_bin_visible: None,
};
const fn vtable_ptr() -> *const bindings::configfs_group_operations {
&Self::VTABLE
}
}
struct ItemOperationsVTable<Container, Data>(PhantomData<(Container, Data)>);
impl<Data> ItemOperationsVTable<Group<Data>, Data>
where
Data: 'static,
{
unsafe extern "C" fn release(this: *mut bindings::config_item) {
let c_group_ptr = unsafe { kernel::container_of!(this, bindings::config_group, cg_item) };
let r_group_ptr = unsafe { Group::<Data>::container_of(c_group_ptr) };
let pin_self: Arc<Group<Data>> = unsafe { Arc::from_raw(r_group_ptr.cast_mut()) };
drop(pin_self);
}
const VTABLE: bindings::configfs_item_operations = bindings::configfs_item_operations {
release: Some(Self::release),
allow_link: None,
drop_link: None,
};
const fn vtable_ptr() -> *const bindings::configfs_item_operations {
&Self::VTABLE
}
}
impl<Data> ItemOperationsVTable<Subsystem<Data>, Data> {
const VTABLE: bindings::configfs_item_operations = bindings::configfs_item_operations {
release: None,
allow_link: None,
drop_link: None,
};
const fn vtable_ptr() -> *const bindings::configfs_item_operations {
&Self::VTABLE
}
}
#[vtable]
pub trait GroupOperations {
type Child: 'static;
fn make_group(&self, name: &CStr) -> Result<impl PinInit<Group<Self::Child>, Error>>;
fn drop_item(&self, _child: ArcBorrow<'_, Group<Self::Child>>) {
kernel::build_error!(kernel::error::VTABLE_DEFAULT_ERROR)
}
}
#[repr(transparent)]
pub struct Attribute<const ID: u64, O, Data> {
attribute: Opaque<bindings::configfs_attribute>,
_p: PhantomData<(O, Data)>,
}
unsafe impl<const ID: u64, O, Data> Sync for Attribute<ID, O, Data> {}
unsafe impl<const ID: u64, O, Data> Send for Attribute<ID, O, Data> {}
impl<const ID: u64, O, Data> Attribute<ID, O, Data>
where
O: AttributeOperations<ID, Data = Data>,
{
unsafe extern "C" fn show(
item: *mut bindings::config_item,
page: *mut kernel::ffi::c_char,
) -> isize {
let c_group: *mut bindings::config_group =
unsafe { container_of!(item, bindings::config_group, cg_item) };
let data: &Data = unsafe { get_group_data(c_group) };
let ret = O::show(data, unsafe { &mut *(page.cast::<[u8; PAGE_SIZE]>()) });
match ret {
Ok(size) => size as isize,
Err(err) => err.to_errno() as isize,
}
}
unsafe extern "C" fn store(
item: *mut bindings::config_item,
page: *const kernel::ffi::c_char,
size: usize,
) -> isize {
let c_group: *mut bindings::config_group =
unsafe { container_of!(item, bindings::config_group, cg_item) };
let data: &Data = unsafe { get_group_data(c_group) };
let ret = O::store(
data,
unsafe { core::slice::from_raw_parts(page.cast(), size) },
);
match ret {
Ok(()) => size as isize,
Err(err) => err.to_errno() as isize,
}
}
pub const fn new(name: &'static CStr) -> Self {
Self {
attribute: Opaque::new(bindings::configfs_attribute {
ca_name: crate::str::as_char_ptr_in_const_context(name),
ca_owner: core::ptr::null_mut(),
ca_mode: 0o660,
show: Some(Self::show),
store: if O::HAS_STORE {
Some(Self::store)
} else {
None
},
}),
_p: PhantomData,
}
}
}
#[vtable]
pub trait AttributeOperations<const ID: u64 = 0> {
type Data;
fn show(data: &Self::Data, page: &mut [u8; PAGE_SIZE]) -> Result<usize>;
fn store(_data: &Self::Data, _page: &[u8]) -> Result {
kernel::build_error!(kernel::error::VTABLE_DEFAULT_ERROR)
}
}
#[repr(transparent)]
pub struct AttributeList<const N: usize, Data>(
UnsafeCell<[*mut kernel::ffi::c_void; N]>,
PhantomData<Data>,
);
unsafe impl<const N: usize, Data> Send for AttributeList<N, Data> {}
unsafe impl<const N: usize, Data> Sync for AttributeList<N, Data> {}
impl<const N: usize, Data> AttributeList<N, Data> {
#[doc(hidden)]
pub const unsafe fn new() -> Self {
Self(UnsafeCell::new([core::ptr::null_mut(); N]), PhantomData)
}
#[doc(hidden)]
pub const unsafe fn add<const I: usize, const ID: u64, O>(
&'static self,
attribute: &'static Attribute<ID, O, Data>,
) where
O: AttributeOperations<ID, Data = Data>,
{
const { assert!(I < N - 1, "Invalid attribute index") };
unsafe { (&mut *self.0.get())[I] = core::ptr::from_ref(attribute).cast_mut().cast() };
}
}
#[pin_data]
pub struct ItemType<Container, Data> {
#[pin]
item_type: Opaque<bindings::config_item_type>,
_p: PhantomData<(Container, Data)>,
}
unsafe impl<Container, Data> Sync for ItemType<Container, Data> {}
unsafe impl<Container, Data> Send for ItemType<Container, Data> {}
macro_rules! impl_item_type {
($tpe:ty) => {
impl<Data> ItemType<$tpe, Data> {
#[doc(hidden)]
pub const fn new_with_child_ctor<const N: usize, Child>(
owner: &'static ThisModule,
attributes: &'static AttributeList<N, Data>,
) -> Self
where
Data: GroupOperations<Child = Child>,
Child: 'static,
{
Self {
item_type: Opaque::new(bindings::config_item_type {
ct_owner: owner.as_ptr(),
ct_group_ops: GroupOperationsVTable::<Data, Child>::vtable_ptr().cast_mut(),
ct_item_ops: ItemOperationsVTable::<$tpe, Data>::vtable_ptr().cast_mut(),
ct_attrs: core::ptr::from_ref(attributes).cast_mut().cast(),
ct_bin_attrs: core::ptr::null_mut(),
}),
_p: PhantomData,
}
}
#[doc(hidden)]
pub const fn new<const N: usize>(
owner: &'static ThisModule,
attributes: &'static AttributeList<N, Data>,
) -> Self {
Self {
item_type: Opaque::new(bindings::config_item_type {
ct_owner: owner.as_ptr(),
ct_group_ops: core::ptr::null_mut(),
ct_item_ops: ItemOperationsVTable::<$tpe, Data>::vtable_ptr().cast_mut(),
ct_attrs: core::ptr::from_ref(attributes).cast_mut().cast(),
ct_bin_attrs: core::ptr::null_mut(),
}),
_p: PhantomData,
}
}
}
};
}
impl_item_type!(Subsystem<Data>);
impl_item_type!(Group<Data>);
impl<Container, Data> ItemType<Container, Data> {
fn as_ptr(&self) -> *const bindings::config_item_type {
self.item_type.get()
}
}
#[macro_export]
macro_rules! configfs_attrs {
(
container: $container:ty,
data: $data:ty,
attributes: [
$($name:ident: $attr:literal),* $(,)?
] $(,)?
) => {
$crate::configfs_attrs!(
count:
@container($container),
@data($data),
@child(),
@no_child(x),
@attrs($($name $attr)*),
@eat($($name $attr,)*),
@assign(),
@cnt(0usize),
)
};
(
container: $container:ty,
data: $data:ty,
child: $child:ty,
attributes: [
$($name:ident: $attr:literal),* $(,)?
] $(,)?
) => {
$crate::configfs_attrs!(
count:
@container($container),
@data($data),
@child($child),
@no_child(),
@attrs($($name $attr)*),
@eat($($name $attr,)*),
@assign(),
@cnt(0usize),
)
};
(count:
@container($container:ty),
@data($data:ty),
@child($($child:ty)?),
@no_child($($no_child:ident)?),
@attrs($($aname:ident $aattr:literal)*),
@eat($name:ident $attr:literal, $($rname:ident $rattr:literal,)*),
@assign($($assign:block)*),
@cnt($cnt:expr),
) => {
$crate::configfs_attrs!(
count:
@container($container),
@data($data),
@child($($child)?),
@no_child($($no_child)?),
@attrs($($aname $aattr)*),
@eat($($rname $rattr,)*),
@assign($($assign)* {
const N: usize = $cnt;
// The following macro text expands to a call to `Attribute::add`.
// SAFETY: By design of this macro, the name of the variable we
// invoke the `add` method on below, is not visible outside of
// the macro expansion. The macro does not operate concurrently
// on this variable, and thus we have exclusive access to the
// variable.
unsafe {
$crate::macros::paste!(
[< $data:upper _ATTRS >]
.add::<N, $attr, _>(&[< $data:upper _ $name:upper _ATTR >])
)
};
}),
@cnt(1usize + $cnt),
)
};
(count:
@container($container:ty),
@data($data:ty),
@child($($child:ty)?),
@no_child($($no_child:ident)?),
@attrs($($aname:ident $aattr:literal)*),
@eat(),
@assign($($assign:block)*),
@cnt($cnt:expr),
) =>
{
$crate::configfs_attrs!(
final:
@container($container),
@data($data),
@child($($child)?),
@no_child($($no_child)?),
@attrs($($aname $aattr)*),
@assign($($assign)*),
@cnt($cnt),
)
};
(final:
@container($container:ty),
@data($data:ty),
@child($($child:ty)?),
@no_child($($no_child:ident)?),
@attrs($($name:ident $attr:literal)*),
@assign($($assign:block)*),
@cnt($cnt:expr),
) =>
{
$crate::macros::paste!{
{
$(
static [< $data:upper _ $name:upper _ATTR >]:
$crate::configfs::Attribute<$attr, $data, $data> =
unsafe {
$crate::configfs::Attribute::new(
$crate::c_str!(::core::stringify!($name)),
)
};
)*
const N: usize = $cnt + 1usize;
static [< $data:upper _ATTRS >]:
$crate::configfs::AttributeList<N, $data> =
unsafe { $crate::configfs::AttributeList::new() };
$($assign)*
$(
const [<$no_child:upper>]: bool = true;
static [< $data:upper _TPE >] : $crate::configfs::ItemType<$container, $data> =
$crate::configfs::ItemType::<$container, $data>::new::<N>(
&THIS_MODULE, &[<$ data:upper _ATTRS >]
);
)?
$(
static [< $data:upper _TPE >]:
$crate::configfs::ItemType<$container, $data> =
$crate::configfs::ItemType::<$container, $data>::
new_with_child_ctor::<N, $child>(
&THIS_MODULE, &[<$ data:upper _ATTRS >]
);
)?
& [< $data:upper _TPE >]
}
}
};
}
pub use crate::configfs_attrs;