| // 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<Bound>>, |
| } |
| |
| 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<Bound>, 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<Pin<KBox<Registration<'_, Data>>>> { |
| /// // 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<T, Error> + 'a, |
| ) -> impl PinInit<Self, Error> + '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::<T>), |
| flags.into_inner(), |
| name.as_char_ptr(), |
| this.as_ptr().cast::<c_void>(), |
| ) |
| })?; |
| }, |
| }) |
| } |
| |
| /// 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<T: Handler> 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>(self.get_unchecked_mut()).cast::<c_void>(), |
| ) |
| }; |
| } |
| } |
| |
| /// # Safety |
| /// |
| /// This function should be only used as the callback in `request_irq`. |
| unsafe extern "C" fn handle_irq_callback<T: Handler>(_irq: i32, ptr: *mut c_void) -> c_uint { |
| let ptr = ptr.cast_const().cast::<Registration<'_, T>>(); |
| // 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<u32>, |
| /// } |
| /// |
| /// 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<Pin<KBox<ThreadedRegistration<'_, Data>>>> { |
| /// // 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<T, Error> + 'a, |
| ) -> impl PinInit<Self, Error> + '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::<T>), |
| Some(thread_fn_callback::<T>), |
| flags.into_inner(), |
| name.as_char_ptr(), |
| this.as_ptr().cast::<c_void>(), |
| ) |
| })?; |
| }, |
| }) |
| } |
| |
| /// 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<T: ThreadedHandler> 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>(self.get_unchecked_mut()).cast::<c_void>(), |
| ) |
| }; |
| } |
| } |
| |
| /// # Safety |
| /// |
| /// This function should be only used as the callback in `request_threaded_irq`. |
| unsafe extern "C" fn handle_threaded_irq_callback<T: ThreadedHandler>( |
| _irq: i32, |
| ptr: *mut c_void, |
| ) -> c_uint { |
| let ptr = ptr.cast_const().cast::<ThreadedRegistration<'_, T>>(); |
| // 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<T: ThreadedHandler>(_irq: i32, ptr: *mut c_void) -> c_uint { |
| let ptr = ptr.cast_const().cast::<ThreadedRegistration<'_, T>>(); |
| // SAFETY: `ptr` is a pointer to `ThreadedRegistration<'_, T>` set in |
| // `ThreadedRegistration::new()`. |
| let registration = unsafe { &*ptr }; |
| |
| T::handle_threaded(®istration.handler) as c_uint |
| } |