Skip to main content

kernel/irq/
request.rs

1// SPDX-License-Identifier: GPL-2.0
2// SPDX-FileCopyrightText: Copyright 2025 Collabora ltd.
3
4//! This module provides types like [`Registration`] and
5//! [`ThreadedRegistration`], which allow users to register handlers for a given
6//! IRQ line.
7
8use core::marker::{
9    PhantomData,
10    PhantomPinned, //
11};
12
13use crate::{
14    device::{
15        Bound,
16        Device, //
17    },
18    error::to_result,
19    irq::flags::Flags,
20    prelude::*,
21    str::CStr,
22};
23
24/// The value that can be returned from a [`Handler`] or a [`ThreadedHandler`].
25#[repr(u32)]
26pub enum IrqReturn {
27    /// The interrupt was not from this device or was not handled.
28    None = bindings::irqreturn::IRQ_NONE.0,
29
30    /// The interrupt was handled by this device.
31    Handled = bindings::irqreturn::IRQ_HANDLED.0,
32}
33
34/// Callbacks for an IRQ handler.
35pub trait Handler: Sync {
36    /// The hard IRQ handler.
37    ///
38    /// This is executed in interrupt context, hence all corresponding
39    /// limitations do apply.
40    ///
41    /// All work that does not necessarily need to be executed from
42    /// interrupt context, should be deferred to a threaded handler.
43    /// See also [`ThreadedRegistration`].
44    fn handle(&self) -> IrqReturn;
45}
46
47/// A request for an IRQ line for a given device.
48///
49/// # Invariants
50///
51/// - `ìrq` is the number of an interrupt source of `dev`.
52/// - `irq` has not been registered yet; this is consumed by [`Registration::new()`].
53pub struct IrqRequest<'a> {
54    irq: u32,
55    /// Proves the device is bound at registration time and ties `'a` to the device's bound
56    /// lifetime, ensuring the [`Registration`] cannot outlive it.
57    _dev: PhantomData<&'a Device<Bound>>,
58}
59
60impl<'a> IrqRequest<'a> {
61    /// Creates a new IRQ request for the given device and IRQ number.
62    ///
63    /// # Safety
64    ///
65    /// - `irq` should be a valid IRQ number for `dev`.
66    pub(crate) unsafe fn new(_dev: &'a Device<Bound>, irq: u32) -> Self {
67        // INVARIANT: `irq` is a valid IRQ number for `dev`.
68        IrqRequest {
69            irq,
70            _dev: PhantomData,
71        }
72    }
73
74    /// Returns the IRQ number of an [`IrqRequest`].
75    #[inline]
76    pub fn irq(&self) -> u32 {
77        self.irq
78    }
79}
80
81/// A registration of an IRQ handler for a given IRQ line.
82///
83/// # Examples
84///
85/// The following is an example of using `Registration`. It uses a
86/// [`Completion`] to coordinate between the IRQ
87/// handler and process context. [`Completion`] uses interior mutability, so the
88/// handler can signal with [`Completion::complete_all()`] and the process
89/// context can wait with [`Completion::wait_for_completion()`] even though
90/// there is no way to get a mutable reference to the any of the fields in
91/// `Data`.
92///
93/// [`Completion`]: kernel::sync::Completion
94/// [`Completion::complete_all()`]: kernel::sync::Completion::complete_all
95/// [`Completion::wait_for_completion()`]: kernel::sync::Completion::wait_for_completion
96///
97/// ```
98/// use core::pin::Pin;
99/// use kernel::{
100///     irq::{
101///         self,
102///         Flags,
103///         IrqRequest,
104///         IrqReturn,
105///         Registration,
106///     },
107///     prelude::*,
108///     sync::Completion,
109/// };
110///
111/// // Data shared between process and IRQ context.
112/// #[pin_data]
113/// struct Data {
114///     #[pin]
115///     completion: Completion,
116/// }
117///
118/// impl irq::Handler for Data {
119///     // Executed in IRQ context.
120///     fn handle(&self) -> IrqReturn {
121///         self.completion.complete_all();
122///         IrqReturn::Handled
123///     }
124/// }
125///
126/// // Registers an IRQ handler for the given IrqRequest.
127/// //
128/// // This runs in process context and assumes `request` was previously acquired from a device.
129/// fn register_irq(
130///     request: IrqRequest<'_>,
131/// ) -> Result<Pin<KBox<Registration<'_, Data>>>> {
132///     // SAFETY: The returned Registration is not leaked.
133///     let registration = unsafe {
134///         Registration::new(
135///             request,
136///             Flags::SHARED,
137///             c"my_device",
138///             try_pin_init!(Data {
139///                 completion <- Completion::new(),
140///             }? Error),
141///         )
142///     };
143///
144///     let registration = KBox::pin_init(registration, GFP_KERNEL)?;
145///
146///     registration.handler().completion.wait_for_completion();
147///
148///     Ok(registration)
149/// }
150/// # Ok::<(), Error>(())
151/// ```
152///
153/// # Invariants
154///
155/// * We own an irq handler registered via `request_irq` whose cookie is a pointer to `Self`.
156#[pin_data(PinnedDrop)]
157pub struct Registration<'a, T: Handler> {
158    request: IrqRequest<'a>,
159
160    #[pin]
161    handler: T,
162
163    /// Pinned because we need address stability so that we can pass a pointer
164    /// to the callback.
165    #[pin]
166    _pin: PhantomPinned,
167}
168
169impl<'a, T: Handler> Registration<'a, T> {
170    /// Registers the IRQ handler with the system for the given IRQ number.
171    ///
172    /// # Safety
173    ///
174    /// Callers must not `mem::forget()` the returned [`Registration`] or otherwise prevent its
175    /// [`Drop`] implementation from running.
176    pub unsafe fn new(
177        request: IrqRequest<'a>,
178        flags: Flags,
179        name: &'static CStr,
180        handler: impl PinInit<T, Error> + 'a,
181    ) -> impl PinInit<Self, Error> + 'a
182    where
183        T: 'a,
184    {
185        // INVARIANT: If initialization completes successfully, we own an IRQ handler registered
186        // via `request_irq` whose cookie is a pointer to `Self`.
187        try_pin_init!(&this in Self {
188            handler <- handler,
189            request,
190            _pin: PhantomPinned,
191            _: {
192                // SAFETY:
193                // - The callbacks are valid for use with request_irq.
194                // - If this succeeds, the slot is guaranteed to be valid until the destructor of
195                //   Self runs, which will deregister the callbacks before the memory location
196                //   becomes invalid.
197                // - All fields are already initialized, so it's safe for the callback to be
198                //   called immediately.
199                to_result(unsafe {
200                    bindings::request_irq(
201                        request.irq,
202                        Some(handle_irq_callback::<T>),
203                        flags.into_inner(),
204                        name.as_char_ptr(),
205                        this.as_ptr().cast::<c_void>(),
206                    )
207                })?;
208            },
209        })
210    }
211
212    /// Returns a reference to the handler that was registered with the system.
213    pub fn handler(&self) -> &T {
214        &self.handler
215    }
216
217    /// Wait for pending IRQ handlers on other CPUs.
218    #[inline]
219    pub fn synchronize(&self) {
220        // SAFETY: `self.request.irq` is a valid registered IRQ number (type invariant).
221        unsafe { bindings::synchronize_irq(self.request.irq) };
222    }
223}
224
225#[pinned_drop]
226impl<T: Handler> PinnedDrop for Registration<'_, T> {
227    fn drop(self: Pin<&mut Self>) {
228        // SAFETY: The cookie was set to a pointer to `Self` in `Registration::new()`. This blocks
229        // until all in-flight handlers complete, so no references to `self` remain after this
230        // returns.
231        unsafe {
232            bindings::free_irq(
233                self.request.irq,
234                core::ptr::from_mut::<Self>(self.get_unchecked_mut()).cast::<c_void>(),
235            )
236        };
237    }
238}
239
240/// # Safety
241///
242/// This function should be only used as the callback in `request_irq`.
243unsafe extern "C" fn handle_irq_callback<T: Handler>(
244    _irq: i32,
245    ptr: *mut c_void,
246) -> bindings::irqreturn_t {
247    let ptr = ptr.cast_const().cast::<Registration<'_, T>>();
248    // SAFETY: `ptr` is a pointer to `Registration<'_, T>` set in `Registration::new()`.
249    let registration = unsafe { &*ptr };
250
251    bindings::irqreturn_t(T::handle(&registration.handler) as c_uint)
252}
253
254/// The value that can be returned from [`ThreadedHandler::handle`].
255#[repr(u32)]
256pub enum ThreadedIrqReturn {
257    /// The interrupt was not from this device or was not handled.
258    None = bindings::irqreturn::IRQ_NONE.0,
259
260    /// The interrupt was handled by this device.
261    Handled = bindings::irqreturn::IRQ_HANDLED.0,
262
263    /// The handler wants the handler thread to wake up.
264    WakeThread = bindings::irqreturn::IRQ_WAKE_THREAD.0,
265}
266
267/// Callbacks for a threaded IRQ handler.
268pub trait ThreadedHandler: Sync {
269    /// The hard IRQ handler.
270    ///
271    /// This is executed in interrupt context, hence all corresponding
272    /// limitations do apply. All work that does not necessarily need to be
273    /// executed from interrupt context, should be deferred to the threaded
274    /// handler, i.e. [`ThreadedHandler::handle_threaded`].
275    ///
276    /// The default implementation returns [`ThreadedIrqReturn::WakeThread`].
277    fn handle(&self) -> ThreadedIrqReturn {
278        ThreadedIrqReturn::WakeThread
279    }
280
281    /// The threaded IRQ handler.
282    ///
283    /// This is executed in process context. The kernel creates a dedicated
284    /// `kthread` for this purpose.
285    fn handle_threaded(&self) -> IrqReturn;
286}
287
288/// A registration of a threaded IRQ handler for a given IRQ line.
289///
290/// Two callbacks are required: one to handle the IRQ, and one to handle any
291/// other work in a separate thread.
292///
293/// The thread handler is only called if the IRQ handler returns
294/// [`ThreadedIrqReturn::WakeThread`].
295///
296/// # Examples
297///
298/// The following is an example of using [`ThreadedRegistration`]. It uses a
299/// [`Mutex`](kernel::sync::Mutex) to provide interior mutability.
300///
301/// ```
302/// use core::pin::Pin;
303/// use kernel::{
304///     irq::{
305///         self,
306///         Flags,
307///         IrqRequest,
308///         IrqReturn,
309///         ThreadedHandler,
310///         ThreadedIrqReturn,
311///         ThreadedRegistration,
312///     },
313///     prelude::*,
314///     sync::Mutex,
315/// };
316///
317/// // Declare a struct that will be passed in when the interrupt fires. The u32
318/// // merely serves as an example of some internal data.
319/// //
320/// // [`irq::ThreadedHandler::handle`] takes `&self`. This example
321/// // illustrates how interior mutability can be used when sharing the data
322/// // between process context and IRQ context.
323/// #[pin_data]
324/// struct Data {
325///     #[pin]
326///     value: Mutex<u32>,
327/// }
328///
329/// impl ThreadedHandler for Data {
330///     // This will run (in a separate kthread) if and only if
331///     // [`ThreadedHandler::handle`] returns [`WakeThread`], which it does by
332///     // default.
333///     fn handle_threaded(&self) -> IrqReturn {
334///         let mut data = self.value.lock();
335///         *data += 1;
336///         IrqReturn::Handled
337///     }
338/// }
339///
340/// // Registers a threaded IRQ handler for the given [`IrqRequest`].
341/// //
342/// // This is executing in process context and assumes that `request` was
343/// // previously acquired from a device.
344/// fn register_threaded_irq(
345///     request: IrqRequest<'_>,
346/// ) -> Result<Pin<KBox<ThreadedRegistration<'_, Data>>>> {
347///     // SAFETY: The returned Registration is not leaked.
348///     let registration = unsafe {
349///         ThreadedRegistration::new(
350///             request,
351///             Flags::SHARED,
352///             c"my_device",
353///             try_pin_init!(Data {
354///                 value <- kernel::new_mutex!(0),
355///             }? Error),
356///         )
357///     };
358///
359///     let registration = KBox::pin_init(registration, GFP_KERNEL)?;
360///
361///     {
362///         // The data can be accessed from process context too.
363///         let mut data = registration.handler().value.lock();
364///         *data += 1;
365///     }
366///
367///     Ok(registration)
368/// }
369/// # Ok::<(), Error>(())
370/// ```
371///
372/// # Invariants
373///
374/// * We own an irq handler registered via `request_threaded_irq` whose cookie is a pointer to
375///   `Self`.
376#[pin_data(PinnedDrop)]
377pub struct ThreadedRegistration<'a, T: ThreadedHandler> {
378    request: IrqRequest<'a>,
379
380    #[pin]
381    handler: T,
382
383    /// Pinned because we need address stability so that we can pass a pointer
384    /// to the callback.
385    #[pin]
386    _pin: PhantomPinned,
387}
388
389impl<'a, T: ThreadedHandler> ThreadedRegistration<'a, T> {
390    /// Registers the IRQ handler with the system for the given IRQ number.
391    ///
392    /// # Safety
393    ///
394    /// Callers must not `mem::forget()` the returned [`ThreadedRegistration`] or otherwise prevent
395    /// its [`Drop`] implementation from running.
396    pub unsafe fn new(
397        request: IrqRequest<'a>,
398        flags: Flags,
399        name: &'static CStr,
400        handler: impl PinInit<T, Error> + 'a,
401    ) -> impl PinInit<Self, Error> + 'a
402    where
403        T: 'a,
404    {
405        // INVARIANT: If initialization completes successfully, we own an IRQ handler registered
406        // via `request_threaded_irq` whose cookie is a pointer to `Self`.
407        try_pin_init!(&this in Self {
408            handler <- handler,
409            request,
410            _pin: PhantomPinned,
411            _: {
412                // SAFETY:
413                // - The callbacks are valid for use with request_threaded_irq.
414                // - If this succeeds, the slot is guaranteed to be valid until the destructor of
415                //   Self runs, which will deregister the callbacks before the memory location
416                //   becomes invalid.
417                // - All fields are already initialized, so it's safe for the callbacks to be
418                //   called immediately.
419                to_result(unsafe {
420                    bindings::request_threaded_irq(
421                        request.irq,
422                        Some(handle_threaded_irq_callback::<T>),
423                        Some(thread_fn_callback::<T>),
424                        flags.into_inner(),
425                        name.as_char_ptr(),
426                        this.as_ptr().cast::<c_void>(),
427                    )
428                })?;
429            },
430        })
431    }
432
433    /// Returns a reference to the handler that was registered with the system.
434    pub fn handler(&self) -> &T {
435        &self.handler
436    }
437
438    /// Wait for pending IRQ handlers on other CPUs.
439    #[inline]
440    pub fn synchronize(&self) {
441        // SAFETY: `self.request.irq` is a valid registered IRQ number (type invariant).
442        unsafe { bindings::synchronize_irq(self.request.irq) };
443    }
444}
445
446#[pinned_drop]
447impl<T: ThreadedHandler> PinnedDrop for ThreadedRegistration<'_, T> {
448    fn drop(self: Pin<&mut Self>) {
449        // SAFETY: The cookie was set to a pointer to `Self` in `ThreadedRegistration::new()`. This
450        // blocks until all in-flight handlers complete, so no references to `self` remain after
451        // this returns.
452        unsafe {
453            bindings::free_irq(
454                self.request.irq,
455                core::ptr::from_mut::<Self>(self.get_unchecked_mut()).cast::<c_void>(),
456            )
457        };
458    }
459}
460
461/// # Safety
462///
463/// This function should be only used as the callback in `request_threaded_irq`.
464unsafe extern "C" fn handle_threaded_irq_callback<T: ThreadedHandler>(
465    _irq: i32,
466    ptr: *mut c_void,
467) -> bindings::irqreturn_t {
468    let ptr = ptr.cast_const().cast::<ThreadedRegistration<'_, T>>();
469    // SAFETY: `ptr` is a pointer to `ThreadedRegistration<'_, T>` set in
470    // `ThreadedRegistration::new()`.
471    let registration = unsafe { &*ptr };
472
473    bindings::irqreturn_t(T::handle(&registration.handler) as c_uint)
474}
475
476/// # Safety
477///
478/// This function should be only used as the callback in `request_threaded_irq`.
479unsafe extern "C" fn thread_fn_callback<T: ThreadedHandler>(
480    _irq: i32,
481    ptr: *mut c_void,
482) -> bindings::irqreturn_t {
483    let ptr = ptr.cast_const().cast::<ThreadedRegistration<'_, T>>();
484    // SAFETY: `ptr` is a pointer to `ThreadedRegistration<'_, T>` set in
485    // `ThreadedRegistration::new()`.
486    let registration = unsafe { &*ptr };
487
488    bindings::irqreturn_t(T::handle_threaded(&registration.handler) as c_uint)
489}