use crate::{
alloc::Flags,
bindings,
device::{
Bound,
Device, },
error::to_result,
prelude::*,
revocable::{
Revocable,
RevocableGuard, },
sync::{
aref::ARef,
rcu,
Arc, },
types::{
ForeignOwnable,
Opaque, },
};
#[repr(C)]
#[pin_data]
struct Inner<T> {
#[pin]
node: Opaque<bindings::devres_node>,
#[pin]
data: Revocable<T>,
}
pub struct Devres<T: Send> {
dev: ARef<Device>,
inner: Arc<Inner<T>>,
}
mod base {
use kernel::{
bindings,
prelude::*, };
#[inline(never)]
#[allow(clippy::missing_safety_doc)]
pub(super) unsafe fn devres_node_init(
node: *mut bindings::devres_node,
release: bindings::dr_node_release_t,
free: bindings::dr_node_free_t,
) {
unsafe { bindings::devres_node_init(node, release, free) }
}
#[inline(never)]
#[allow(clippy::missing_safety_doc)]
pub(super) unsafe fn devres_set_node_dbginfo(
node: *mut bindings::devres_node,
name: *const c_char,
size: usize,
) {
unsafe { bindings::devres_set_node_dbginfo(node, name, size) }
}
#[inline(never)]
#[allow(clippy::missing_safety_doc)]
pub(super) unsafe fn devres_node_add(
dev: *mut bindings::device,
node: *mut bindings::devres_node,
) {
unsafe { bindings::devres_node_add(dev, node) }
}
#[must_use]
#[inline(never)]
#[allow(clippy::missing_safety_doc)]
pub(super) unsafe fn devres_node_remove(
dev: *mut bindings::device,
node: *mut bindings::devres_node,
) -> bool {
unsafe { bindings::devres_node_remove(dev, node) }
}
}
impl<T: Send> Devres<T> {
pub fn new<E>(dev: &Device<Bound>, data: impl PinInit<T, E>) -> Result<Self>
where
Error: From<E>,
{
let inner = Arc::pin_init::<Error>(
try_pin_init!(Inner {
node <- Opaque::ffi_init(|node: *mut bindings::devres_node| {
unsafe {
base::devres_node_init(
node,
Some(Self::devres_node_release),
Some(Self::devres_node_free_node),
)
};
unsafe {
base::devres_set_node_dbginfo(
node,
c"Devres<T>".as_char_ptr(),
core::mem::size_of::<Revocable<T>>(),
)
};
}),
data <- Revocable::new(data),
}),
GFP_KERNEL,
)?;
unsafe { base::devres_node_add(dev.as_raw(), inner.node.get()) };
core::mem::forget(inner.clone());
Ok(Self {
dev: dev.into(),
inner,
})
}
fn data(&self) -> &Revocable<T> {
&self.inner.data
}
#[allow(clippy::missing_safety_doc)]
unsafe extern "C" fn devres_node_release(
_dev: *mut bindings::device,
node: *mut bindings::devres_node,
) {
let node = Opaque::cast_from(node);
let inner = unsafe { kernel::container_of!(node, Inner<T>, node) };
let inner = unsafe { &*inner };
inner.data.revoke();
}
#[allow(clippy::missing_safety_doc)]
unsafe extern "C" fn devres_node_free_node(node: *mut bindings::devres_node) {
let node = Opaque::cast_from(node);
let inner = unsafe { kernel::container_of!(node, Inner<T>, node) };
drop(unsafe { Arc::from_raw(inner) });
}
fn remove_node(&self) -> bool {
unsafe { base::devres_node_remove(self.device().as_raw(), self.inner.node.get()) }
}
pub fn device(&self) -> &Device {
&self.dev
}
pub fn access<'a>(&'a self, dev: &'a Device<Bound>) -> Result<&'a T> {
if self.dev.as_raw() != dev.as_raw() {
return Err(EINVAL);
}
Ok(unsafe { self.data().access() })
}
pub fn try_access(&self) -> Option<RevocableGuard<'_, T>> {
self.data().try_access()
}
pub fn try_access_with<R, F: FnOnce(&T) -> R>(&self, f: F) -> Option<R> {
self.data().try_access_with(f)
}
pub fn try_access_with_guard<'a>(&'a self, guard: &'a rcu::Guard) -> Option<&'a T> {
self.data().try_access_with_guard(guard)
}
}
unsafe impl<T: Send> Send for Devres<T> {}
unsafe impl<T: Send + Sync> Sync for Devres<T> {}
impl<T: Send> Drop for Devres<T> {
fn drop(&mut self) {
if unsafe { self.data().revoke_nosync() } {
if self.remove_node() {
drop(unsafe { Arc::from_raw(Arc::as_ptr(&self.inner)) });
}
}
}
}
fn register_foreign<P>(dev: &Device<Bound>, data: P) -> Result
where
P: ForeignOwnable + Send + 'static,
{
let ptr = data.into_foreign();
#[allow(clippy::missing_safety_doc)]
unsafe extern "C" fn callback<P: ForeignOwnable>(ptr: *mut kernel::ffi::c_void) {
drop(unsafe { P::from_foreign(ptr.cast()) });
}
to_result(unsafe {
bindings::devm_add_action_or_reset(dev.as_raw(), Some(callback::<P>), ptr.cast())
})
}
pub fn register<T, E>(dev: &Device<Bound>, data: impl PinInit<T, E>, flags: Flags) -> Result
where
T: Send + 'static,
Error: From<E>,
{
let data = KBox::pin_init(data, flags)?;
register_foreign(dev, data)
}