use core::marker::PhantomPinned;
use crate::alloc::Allocator;
use crate::device::{Bound, Device};
use crate::devres::Devres;
use crate::error::to_result;
use crate::irq::flags::Flags;
use crate::prelude::*;
use crate::str::CStr;
use crate::sync::Arc;
#[repr(u32)]
pub enum IrqReturn {
None = bindings::irqreturn_IRQ_NONE,
Handled = bindings::irqreturn_IRQ_HANDLED,
}
pub trait Handler: Sync + 'static {
fn handle(&self, device: &Device<Bound>) -> IrqReturn;
}
impl<T: ?Sized + Handler + Send> Handler for Arc<T> {
fn handle(&self, device: &Device<Bound>) -> IrqReturn {
T::handle(self, device)
}
}
impl<T: ?Sized + Handler, A: Allocator + 'static> Handler for Box<T, A> {
fn handle(&self, device: &Device<Bound>) -> IrqReturn {
T::handle(self, device)
}
}
#[pin_data(PinnedDrop)]
struct RegistrationInner {
irq: u32,
cookie: *mut c_void,
}
impl RegistrationInner {
fn synchronize(&self) {
unsafe { bindings::synchronize_irq(self.irq) };
}
}
#[pinned_drop]
impl PinnedDrop for RegistrationInner {
fn drop(self: Pin<&mut Self>) {
unsafe { bindings::free_irq(self.irq, self.cookie) };
}
}
unsafe impl Sync for RegistrationInner {}
unsafe impl Send for RegistrationInner {}
pub struct IrqRequest<'a> {
dev: &'a Device<Bound>,
irq: u32,
}
impl<'a> IrqRequest<'a> {
pub(crate) unsafe fn new(dev: &'a Device<Bound>, irq: u32) -> Self {
IrqRequest { dev, irq }
}
pub fn irq(&self) -> u32 {
self.irq
}
}
#[pin_data]
pub struct Registration<T: Handler> {
#[pin]
inner: Devres<RegistrationInner>,
#[pin]
handler: T,
#[pin]
_pin: PhantomPinned,
}
impl<T: Handler> Registration<T> {
pub fn new<'a>(
request: IrqRequest<'a>,
flags: Flags,
name: &'static CStr,
handler: impl PinInit<T, Error> + 'a,
) -> impl PinInit<Self, Error> + 'a {
try_pin_init!(&this in Self {
handler <- handler,
inner <- Devres::new(
request.dev,
try_pin_init!(RegistrationInner {
cookie: this.as_ptr().cast::<c_void>(),
irq: {
to_result(unsafe {
bindings::request_irq(
request.irq,
Some(handle_irq_callback::<T>),
flags.into_inner(),
name.as_char_ptr(),
this.as_ptr().cast::<c_void>(),
)
})?;
request.irq
}
})
),
_pin: PhantomPinned,
})
}
pub fn handler(&self) -> &T {
&self.handler
}
pub fn try_synchronize(&self) -> Result {
let inner = self.inner.try_access().ok_or(ENODEV)?;
inner.synchronize();
Ok(())
}
pub fn synchronize(&self, dev: &Device<Bound>) -> Result {
let inner = self.inner.access(dev)?;
inner.synchronize();
Ok(())
}
}
unsafe extern "C" fn handle_irq_callback<T: Handler>(_irq: i32, ptr: *mut c_void) -> c_uint {
let registration = unsafe { &*(ptr as *const Registration<T>) };
let device = unsafe { registration.inner.device().as_bound() };
T::handle(®istration.handler, device) as c_uint
}
#[repr(u32)]
pub enum ThreadedIrqReturn {
None = bindings::irqreturn_IRQ_NONE,
Handled = bindings::irqreturn_IRQ_HANDLED,
WakeThread = bindings::irqreturn_IRQ_WAKE_THREAD,
}
pub trait ThreadedHandler: Sync + 'static {
#[expect(unused_variables)]
fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn {
ThreadedIrqReturn::WakeThread
}
fn handle_threaded(&self, device: &Device<Bound>) -> IrqReturn;
}
impl<T: ?Sized + ThreadedHandler + Send> ThreadedHandler for Arc<T> {
fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn {
T::handle(self, device)
}
fn handle_threaded(&self, device: &Device<Bound>) -> IrqReturn {
T::handle_threaded(self, device)
}
}
impl<T: ?Sized + ThreadedHandler, A: Allocator + 'static> ThreadedHandler for Box<T, A> {
fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn {
T::handle(self, device)
}
fn handle_threaded(&self, device: &Device<Bound>) -> IrqReturn {
T::handle_threaded(self, device)
}
}
#[pin_data]
pub struct ThreadedRegistration<T: ThreadedHandler> {
#[pin]
inner: Devres<RegistrationInner>,
#[pin]
handler: T,
#[pin]
_pin: PhantomPinned,
}
impl<T: ThreadedHandler> ThreadedRegistration<T> {
pub fn new<'a>(
request: IrqRequest<'a>,
flags: Flags,
name: &'static CStr,
handler: impl PinInit<T, Error> + 'a,
) -> impl PinInit<Self, Error> + 'a {
try_pin_init!(&this in Self {
handler <- handler,
inner <- Devres::new(
request.dev,
try_pin_init!(RegistrationInner {
cookie: this.as_ptr().cast::<c_void>(),
irq: {
to_result(unsafe {
bindings::request_threaded_irq(
request.irq,
Some(handle_threaded_irq_callback::<T>),
Some(thread_fn_callback::<T>),
flags.into_inner(),
name.as_char_ptr(),
this.as_ptr().cast::<c_void>(),
)
})?;
request.irq
}
})
),
_pin: PhantomPinned,
})
}
pub fn handler(&self) -> &T {
&self.handler
}
pub fn try_synchronize(&self) -> Result {
let inner = self.inner.try_access().ok_or(ENODEV)?;
inner.synchronize();
Ok(())
}
pub fn synchronize(&self, dev: &Device<Bound>) -> Result {
let inner = self.inner.access(dev)?;
inner.synchronize();
Ok(())
}
}
unsafe extern "C" fn handle_threaded_irq_callback<T: ThreadedHandler>(
_irq: i32,
ptr: *mut c_void,
) -> c_uint {
let registration = unsafe { &*(ptr as *const ThreadedRegistration<T>) };
let device = unsafe { registration.inner.device().as_bound() };
T::handle(®istration.handler, device) as c_uint
}
unsafe extern "C" fn thread_fn_callback<T: ThreadedHandler>(_irq: i32, ptr: *mut c_void) -> c_uint {
let registration = unsafe { &*(ptr as *const ThreadedRegistration<T>) };
let device = unsafe { registration.inner.device().as_bound() };
T::handle_threaded(®istration.handler, device) as c_uint
}