// SPDX-License-Identifier: GPL-2.0 // SPDX-FileCopyrightText: Copyright 2025 Collabora ltd. //! This module provides types like [`Registration`] and //! [`ThreadedRegistration`], which allow users to register handlers for a given //! IRQ line. use core::marker::{ PhantomData, PhantomPinned, // }; use crate::{ device::{ Bound, Device, // }, error::to_result, irq::flags::Flags, prelude::*, str::CStr, }; /// The value that can be returned from a [`Handler`] or a [`ThreadedHandler`]. #[repr(u32)] pub enum IrqReturn { /// The interrupt was not from this device or was not handled. None = bindings::irqreturn_IRQ_NONE, /// The interrupt was handled by this device. Handled = bindings::irqreturn_IRQ_HANDLED, } /// Callbacks for an IRQ handler. pub trait Handler: Sync { /// The hard IRQ handler. /// /// This is executed in interrupt context, hence all corresponding /// limitations do apply. /// /// All work that does not necessarily need to be executed from /// interrupt context, should be deferred to a threaded handler. /// See also [`ThreadedRegistration`]. fn handle(&self) -> IrqReturn; } /// A request for an IRQ line for a given device. /// /// # Invariants /// /// - `ìrq` is the number of an interrupt source of `dev`. /// - `irq` has not been registered yet; this is consumed by [`Registration::new()`]. pub struct IrqRequest<'a> { irq: u32, /// Proves the device is bound at registration time and ties `'a` to the device's bound /// lifetime, ensuring the [`Registration`] cannot outlive it. _dev: PhantomData<&'a Device>, } impl<'a> IrqRequest<'a> { /// Creates a new IRQ request for the given device and IRQ number. /// /// # Safety /// /// - `irq` should be a valid IRQ number for `dev`. pub(crate) unsafe fn new(_dev: &'a Device, irq: u32) -> Self { // INVARIANT: `irq` is a valid IRQ number for `dev`. IrqRequest { irq, _dev: PhantomData, } } /// Returns the IRQ number of an [`IrqRequest`]. #[inline] pub fn irq(&self) -> u32 { self.irq } } /// A registration of an IRQ handler for a given IRQ line. /// /// # Examples /// /// The following is an example of using `Registration`. It uses a /// [`Completion`] to coordinate between the IRQ /// handler and process context. [`Completion`] uses interior mutability, so the /// handler can signal with [`Completion::complete_all()`] and the process /// context can wait with [`Completion::wait_for_completion()`] even though /// there is no way to get a mutable reference to the any of the fields in /// `Data`. /// /// [`Completion`]: kernel::sync::Completion /// [`Completion::complete_all()`]: kernel::sync::Completion::complete_all /// [`Completion::wait_for_completion()`]: kernel::sync::Completion::wait_for_completion /// /// ``` /// use core::pin::Pin; /// use kernel::{ /// irq::{ /// self, /// Flags, /// IrqRequest, /// IrqReturn, /// Registration, /// }, /// prelude::*, /// sync::Completion, /// }; /// /// // Data shared between process and IRQ context. /// #[pin_data] /// struct Data { /// #[pin] /// completion: Completion, /// } /// /// impl irq::Handler for Data { /// // Executed in IRQ context. /// fn handle(&self) -> IrqReturn { /// self.completion.complete_all(); /// IrqReturn::Handled /// } /// } /// /// // Registers an IRQ handler for the given IrqRequest. /// // /// // This runs in process context and assumes `request` was previously acquired from a device. /// fn register_irq( /// request: IrqRequest<'_>, /// ) -> Result>>> { /// // SAFETY: The returned Registration is not leaked. /// let registration = unsafe { /// Registration::new( /// request, /// Flags::SHARED, /// c"my_device", /// try_pin_init!(Data { /// completion <- Completion::new(), /// }? Error), /// ) /// }; /// /// let registration = KBox::pin_init(registration, GFP_KERNEL)?; /// /// registration.handler().completion.wait_for_completion(); /// /// Ok(registration) /// } /// # Ok::<(), Error>(()) /// ``` /// /// # Invariants /// /// * We own an irq handler registered via `request_irq` whose cookie is a pointer to `Self`. #[pin_data(PinnedDrop)] pub struct Registration<'a, T: Handler> { request: IrqRequest<'a>, #[pin] handler: T, /// Pinned because we need address stability so that we can pass a pointer /// to the callback. #[pin] _pin: PhantomPinned, } impl<'a, T: Handler> Registration<'a, T> { /// Registers the IRQ handler with the system for the given IRQ number. /// /// # Safety /// /// Callers must not `mem::forget()` the returned [`Registration`] or otherwise prevent its /// [`Drop`] implementation from running. pub unsafe fn new( request: IrqRequest<'a>, flags: Flags, name: &'static CStr, handler: impl PinInit + 'a, ) -> impl PinInit + 'a where T: 'a, { // INVARIANT: If initialization completes successfully, we own an IRQ handler registered // via `request_irq` whose cookie is a pointer to `Self`. try_pin_init!(&this in Self { handler <- handler, request, _pin: PhantomPinned, _: { // SAFETY: // - The callbacks are valid for use with request_irq. // - If this succeeds, the slot is guaranteed to be valid until the destructor of // Self runs, which will deregister the callbacks before the memory location // becomes invalid. // - All fields are already initialized, so it's safe for the callback to be // called immediately. to_result(unsafe { bindings::request_irq( request.irq, Some(handle_irq_callback::), flags.into_inner(), name.as_char_ptr(), this.as_ptr().cast::(), ) })?; }, }) } /// Returns a reference to the handler that was registered with the system. pub fn handler(&self) -> &T { &self.handler } /// Wait for pending IRQ handlers on other CPUs. #[inline] pub fn synchronize(&self) { // SAFETY: `self.request.irq` is a valid registered IRQ number (type invariant). unsafe { bindings::synchronize_irq(self.request.irq) }; } } #[pinned_drop] impl PinnedDrop for Registration<'_, T> { fn drop(self: Pin<&mut Self>) { // SAFETY: The cookie was set to a pointer to `Self` in `Registration::new()`. This blocks // until all in-flight handlers complete, so no references to `self` remain after this // returns. unsafe { bindings::free_irq( self.request.irq, core::ptr::from_mut::(self.get_unchecked_mut()).cast::(), ) }; } } /// # Safety /// /// This function should be only used as the callback in `request_irq`. unsafe extern "C" fn handle_irq_callback(_irq: i32, ptr: *mut c_void) -> c_uint { let ptr = ptr.cast_const().cast::>(); // SAFETY: `ptr` is a pointer to `Registration<'_, T>` set in `Registration::new()`. let registration = unsafe { &*ptr }; T::handle(®istration.handler) as c_uint } /// The value that can be returned from [`ThreadedHandler::handle`]. #[repr(u32)] pub enum ThreadedIrqReturn { /// The interrupt was not from this device or was not handled. None = bindings::irqreturn_IRQ_NONE, /// The interrupt was handled by this device. Handled = bindings::irqreturn_IRQ_HANDLED, /// The handler wants the handler thread to wake up. WakeThread = bindings::irqreturn_IRQ_WAKE_THREAD, } /// Callbacks for a threaded IRQ handler. pub trait ThreadedHandler: Sync { /// The hard IRQ handler. /// /// This is executed in interrupt context, hence all corresponding /// limitations do apply. All work that does not necessarily need to be /// executed from interrupt context, should be deferred to the threaded /// handler, i.e. [`ThreadedHandler::handle_threaded`]. /// /// The default implementation returns [`ThreadedIrqReturn::WakeThread`]. fn handle(&self) -> ThreadedIrqReturn { ThreadedIrqReturn::WakeThread } /// The threaded IRQ handler. /// /// This is executed in process context. The kernel creates a dedicated /// `kthread` for this purpose. fn handle_threaded(&self) -> IrqReturn; } /// A registration of a threaded IRQ handler for a given IRQ line. /// /// Two callbacks are required: one to handle the IRQ, and one to handle any /// other work in a separate thread. /// /// The thread handler is only called if the IRQ handler returns /// [`ThreadedIrqReturn::WakeThread`]. /// /// # Examples /// /// The following is an example of using [`ThreadedRegistration`]. It uses a /// [`Mutex`](kernel::sync::Mutex) to provide interior mutability. /// /// ``` /// use core::pin::Pin; /// use kernel::{ /// irq::{ /// self, /// Flags, /// IrqRequest, /// IrqReturn, /// ThreadedHandler, /// ThreadedIrqReturn, /// ThreadedRegistration, /// }, /// prelude::*, /// sync::Mutex, /// }; /// /// // Declare a struct that will be passed in when the interrupt fires. The u32 /// // merely serves as an example of some internal data. /// // /// // [`irq::ThreadedHandler::handle`] takes `&self`. This example /// // illustrates how interior mutability can be used when sharing the data /// // between process context and IRQ context. /// #[pin_data] /// struct Data { /// #[pin] /// value: Mutex, /// } /// /// impl ThreadedHandler for Data { /// // This will run (in a separate kthread) if and only if /// // [`ThreadedHandler::handle`] returns [`WakeThread`], which it does by /// // default. /// fn handle_threaded(&self) -> IrqReturn { /// let mut data = self.value.lock(); /// *data += 1; /// IrqReturn::Handled /// } /// } /// /// // Registers a threaded IRQ handler for the given [`IrqRequest`]. /// // /// // This is executing in process context and assumes that `request` was /// // previously acquired from a device. /// fn register_threaded_irq( /// request: IrqRequest<'_>, /// ) -> Result>>> { /// // SAFETY: The returned Registration is not leaked. /// let registration = unsafe { /// ThreadedRegistration::new( /// request, /// Flags::SHARED, /// c"my_device", /// try_pin_init!(Data { /// value <- kernel::new_mutex!(0), /// }? Error), /// ) /// }; /// /// let registration = KBox::pin_init(registration, GFP_KERNEL)?; /// /// { /// // The data can be accessed from process context too. /// let mut data = registration.handler().value.lock(); /// *data += 1; /// } /// /// Ok(registration) /// } /// # Ok::<(), Error>(()) /// ``` /// /// # Invariants /// /// * We own an irq handler registered via `request_threaded_irq` whose cookie is a pointer to /// `Self`. #[pin_data(PinnedDrop)] pub struct ThreadedRegistration<'a, T: ThreadedHandler> { request: IrqRequest<'a>, #[pin] handler: T, /// Pinned because we need address stability so that we can pass a pointer /// to the callback. #[pin] _pin: PhantomPinned, } impl<'a, T: ThreadedHandler> ThreadedRegistration<'a, T> { /// Registers the IRQ handler with the system for the given IRQ number. /// /// # Safety /// /// Callers must not `mem::forget()` the returned [`ThreadedRegistration`] or otherwise prevent /// its [`Drop`] implementation from running. pub unsafe fn new( request: IrqRequest<'a>, flags: Flags, name: &'static CStr, handler: impl PinInit + 'a, ) -> impl PinInit + 'a where T: 'a, { // INVARIANT: If initialization completes successfully, we own an IRQ handler registered // via `request_threaded_irq` whose cookie is a pointer to `Self`. try_pin_init!(&this in Self { handler <- handler, request, _pin: PhantomPinned, _: { // SAFETY: // - The callbacks are valid for use with request_threaded_irq. // - If this succeeds, the slot is guaranteed to be valid until the destructor of // Self runs, which will deregister the callbacks before the memory location // becomes invalid. // - All fields are already initialized, so it's safe for the callbacks to be // called immediately. to_result(unsafe { bindings::request_threaded_irq( request.irq, Some(handle_threaded_irq_callback::), Some(thread_fn_callback::), flags.into_inner(), name.as_char_ptr(), this.as_ptr().cast::(), ) })?; }, }) } /// Returns a reference to the handler that was registered with the system. pub fn handler(&self) -> &T { &self.handler } /// Wait for pending IRQ handlers on other CPUs. #[inline] pub fn synchronize(&self) { // SAFETY: `self.request.irq` is a valid registered IRQ number (type invariant). unsafe { bindings::synchronize_irq(self.request.irq) }; } } #[pinned_drop] impl PinnedDrop for ThreadedRegistration<'_, T> { fn drop(self: Pin<&mut Self>) { // SAFETY: The cookie was set to a pointer to `Self` in `ThreadedRegistration::new()`. This // blocks until all in-flight handlers complete, so no references to `self` remain after // this returns. unsafe { bindings::free_irq( self.request.irq, core::ptr::from_mut::(self.get_unchecked_mut()).cast::(), ) }; } } /// # Safety /// /// This function should be only used as the callback in `request_threaded_irq`. unsafe extern "C" fn handle_threaded_irq_callback( _irq: i32, ptr: *mut c_void, ) -> c_uint { let ptr = ptr.cast_const().cast::>(); // SAFETY: `ptr` is a pointer to `ThreadedRegistration<'_, T>` set in // `ThreadedRegistration::new()`. let registration = unsafe { &*ptr }; T::handle(®istration.handler) as c_uint } /// # Safety /// /// This function should be only used as the callback in `request_threaded_irq`. unsafe extern "C" fn thread_fn_callback(_irq: i32, ptr: *mut c_void) -> c_uint { let ptr = ptr.cast_const().cast::>(); // SAFETY: `ptr` is a pointer to `ThreadedRegistration<'_, T>` set in // `ThreadedRegistration::new()`. let registration = unsafe { &*ptr }; T::handle_threaded(®istration.handler) as c_uint }