Skip to content

Commit f471bf5

Browse files
committed
rust: irq: add support for handling interrupts
This is basic funcionality used in many drivers, for example, the NVMe one. Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
1 parent f7b260a commit f471bf5

File tree

1 file changed

+303
-2
lines changed

1 file changed

+303
-2
lines changed

rust/kernel/irq.rs

Lines changed: 303 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@
99
1010
#![allow(dead_code)]
1111

12-
use crate::{bindings, error::from_kernel_result, types::PointerWrapper, Error, Result};
13-
use core::ops::Deref;
12+
use crate::{
13+
bindings,
14+
error::{from_kernel_result, to_result},
15+
str::CString,
16+
types::PointerWrapper,
17+
Error, Result, ScopeGuard,
18+
};
19+
use core::{fmt, marker::PhantomData, ops::Deref};
1420

1521
/// The type of irq hardware numbers.
1622
pub type HwNumber = bindings::irq_hw_number_t;
@@ -329,6 +335,301 @@ impl Descriptor {
329335
}
330336
}
331337

338+
struct InternalRegistration<T: PointerWrapper> {
339+
irq: u32,
340+
data: *mut core::ffi::c_void,
341+
name: CString,
342+
_p: PhantomData<T>,
343+
}
344+
345+
impl<T: PointerWrapper> InternalRegistration<T> {
346+
/// Registers a new irq handler.
347+
///
348+
/// # Safety
349+
///
350+
/// Callers must ensure that `handler` and `thread_fn` are compatible with the registration,
351+
/// that is, that they only use their second argument while the call is happening and that they
352+
/// only call [`T::borrow`] on it (e.g., they shouldn't call [`T::from_pointer`] and consume
353+
/// it).
354+
unsafe fn try_new(
355+
irq: core::ffi::c_uint,
356+
handler: bindings::irq_handler_t,
357+
thread_fn: bindings::irq_handler_t,
358+
flags: usize,
359+
data: T,
360+
name: fmt::Arguments<'_>,
361+
) -> Result<Self> {
362+
let ptr = data.into_pointer() as *mut _;
363+
let name = CString::try_from_fmt(name)?;
364+
let guard = ScopeGuard::new(|| {
365+
// SAFETY: `ptr` came from a previous call to `into_pointer`.
366+
unsafe { T::from_pointer(ptr) };
367+
});
368+
// SAFETY: `name` and `ptr` remain valid as long as the registration is alive.
369+
to_result(|| unsafe {
370+
bindings::request_threaded_irq(
371+
irq,
372+
handler,
373+
thread_fn,
374+
flags as _,
375+
name.as_char_ptr(),
376+
ptr,
377+
)
378+
})?;
379+
guard.dismiss();
380+
Ok(Self {
381+
irq,
382+
name,
383+
data: ptr,
384+
_p: PhantomData,
385+
})
386+
}
387+
}
388+
389+
impl<T: PointerWrapper> Drop for InternalRegistration<T> {
390+
fn drop(&mut self) {
391+
// Unregister irq handler.
392+
//
393+
// SAFETY: When `try_new` succeeds, the irq was successfully requested, so it is ok to free
394+
// it here.
395+
unsafe { bindings::free_irq(self.irq, self.data) };
396+
397+
// Free context data.
398+
//
399+
// SAFETY: This matches the call to `into_pointer` from `try_new` in the success case.
400+
unsafe { T::from_pointer(self.data) };
401+
}
402+
}
403+
404+
/// An irq handler.
405+
pub trait Handler {
406+
/// The context data associated with and made available to the handler.
407+
type Data: PointerWrapper;
408+
409+
/// Called from interrupt context when the irq happens.
410+
fn handle_irq(data: <Self::Data as PointerWrapper>::Borrowed<'_>) -> Return;
411+
}
412+
413+
/// The registration of an interrupt handler.
414+
///
415+
/// # Examples
416+
///
417+
/// The following is an example of a regular handler with a boxed `u32` as data.
418+
///
419+
/// ```
420+
/// # use kernel::prelude::*;
421+
/// use kernel::irq;
422+
///
423+
/// struct Example;
424+
///
425+
/// impl irq::Handler for Example {
426+
/// type Data = Box<u32>;
427+
///
428+
/// fn handle_irq(_data: &u32) -> irq::Return {
429+
/// irq::Return::None
430+
/// }
431+
/// }
432+
///
433+
/// fn request_irq(irq: u32, data: Box<u32>) -> Result<irq::Registration<Example>> {
434+
/// irq::Registration::try_new(
435+
/// irq, data, irq::flags::SHARED, fmt!("example_{irq}"))
436+
/// }
437+
/// ```
438+
pub struct Registration<H: Handler>(InternalRegistration<H::Data>);
439+
440+
impl<H: Handler> Registration<H> {
441+
/// Registers a new irq handler.
442+
///
443+
/// The valid values of `flags` come from the [`flags`] module.
444+
pub fn try_new(
445+
irq: u32,
446+
data: H::Data,
447+
flags: usize,
448+
name: fmt::Arguments<'_>,
449+
) -> Result<Self> {
450+
// SAFETY: `handler` only calls `H::Data::borrow` on `raw_data`.
451+
Ok(Self(unsafe {
452+
InternalRegistration::try_new(irq, Some(Self::handler), None, flags, data, name)?
453+
}))
454+
}
455+
456+
unsafe extern "C" fn handler(
457+
_irq: core::ffi::c_int,
458+
raw_data: *mut core::ffi::c_void,
459+
) -> bindings::irqreturn_t {
460+
// SAFETY: On registration, `into_pointer` was called, so it is safe to borrow from it here
461+
// because `from_pointer` is called only after the irq is unregistered.
462+
let data = unsafe { H::Data::borrow(raw_data) };
463+
H::handle_irq(data) as _
464+
}
465+
}
466+
467+
/// A threaded irq handler.
468+
pub trait ThreadedHandler {
469+
/// The context data associated with and made available to the handlers.
470+
type Data: PointerWrapper;
471+
472+
/// Called from interrupt context when the irq first happens.
473+
fn handle_primary_irq(_data: <Self::Data as PointerWrapper>::Borrowed<'_>) -> Return {
474+
Return::WakeThread
475+
}
476+
477+
/// Called from the handler thread.
478+
fn handle_threaded_irq(data: <Self::Data as PointerWrapper>::Borrowed<'_>) -> Return;
479+
}
480+
481+
/// The registration of a threaded interrupt handler.
482+
///
483+
/// # Examples
484+
///
485+
/// The following is an example of a threaded handler with a ref-counted u32 as data:
486+
///
487+
/// ```
488+
/// # use kernel::prelude::*;
489+
/// use kernel::{irq, sync::{Ref, RefBorrow}};
490+
///
491+
/// struct Example;
492+
///
493+
/// impl irq::ThreadedHandler for Example {
494+
/// type Data = Ref<u32>;
495+
///
496+
/// fn handle_threaded_irq(_data: RefBorrow<'_, u32>) -> irq::Return {
497+
/// irq::Return::None
498+
/// }
499+
/// }
500+
///
501+
/// fn request_irq(irq: u32, data: Ref<u32>) -> Result<irq::ThreadedRegistration<Example>> {
502+
/// irq::ThreadedRegistration::try_new(
503+
/// irq, data, irq::flags::SHARED, fmt!("example_{irq}"))
504+
/// }
505+
/// ```
506+
pub struct ThreadedRegistration<H: ThreadedHandler>(InternalRegistration<H::Data>);
507+
508+
impl<H: ThreadedHandler> ThreadedRegistration<H> {
509+
/// Registers a new threaded irq handler.
510+
///
511+
/// The valid values of `flags` come from the [`flags`] module.
512+
pub fn try_new(
513+
irq: u32,
514+
data: H::Data,
515+
flags: usize,
516+
name: fmt::Arguments<'_>,
517+
) -> Result<Self> {
518+
// SAFETY: both `primary_handler` and `threaded_handler` only call `H::Data::borrow` on
519+
// `raw_data`.
520+
Ok(Self(unsafe {
521+
InternalRegistration::try_new(
522+
irq,
523+
Some(Self::primary_handler),
524+
Some(Self::threaded_handler),
525+
flags,
526+
data,
527+
name,
528+
)?
529+
}))
530+
}
531+
532+
unsafe extern "C" fn primary_handler(
533+
_irq: core::ffi::c_int,
534+
raw_data: *mut core::ffi::c_void,
535+
) -> bindings::irqreturn_t {
536+
// SAFETY: On registration, `into_pointer` was called, so it is safe to borrow from it here
537+
// because `from_pointer` is called only after the irq is unregistered.
538+
let data = unsafe { H::Data::borrow(raw_data) };
539+
H::handle_primary_irq(data) as _
540+
}
541+
542+
unsafe extern "C" fn threaded_handler(
543+
_irq: core::ffi::c_int,
544+
raw_data: *mut core::ffi::c_void,
545+
) -> bindings::irqreturn_t {
546+
// SAFETY: On registration, `into_pointer` was called, so it is safe to borrow from it here
547+
// because `from_pointer` is called only after the irq is unregistered.
548+
let data = unsafe { H::Data::borrow(raw_data) };
549+
H::handle_threaded_irq(data) as _
550+
}
551+
}
552+
553+
/// The return value from interrupt handlers.
554+
pub enum Return {
555+
/// The interrupt was not from this device or was not handled.
556+
None = bindings::irqreturn_IRQ_NONE as _,
557+
558+
/// The interrupt was handled by this device.
559+
Handled = bindings::irqreturn_IRQ_HANDLED as _,
560+
561+
/// The handler wants the handler thread to wake up.
562+
WakeThread = bindings::irqreturn_IRQ_WAKE_THREAD as _,
563+
}
564+
565+
/// Container for interrupt flags.
566+
pub mod flags {
567+
use crate::bindings;
568+
569+
/// Use the interrupt line as already configured.
570+
pub const TRIGGER_NONE: usize = bindings::IRQF_TRIGGER_NONE as _;
571+
572+
/// The interrupt is triggered when the signal goes from low to high.
573+
pub const TRIGGER_RISING: usize = bindings::IRQF_TRIGGER_RISING as _;
574+
575+
/// The interrupt is triggered when the signal goes from high to low.
576+
pub const TRIGGER_FALLING: usize = bindings::IRQF_TRIGGER_FALLING as _;
577+
578+
/// The interrupt is triggered while the signal is held high.
579+
pub const TRIGGER_HIGH: usize = bindings::IRQF_TRIGGER_HIGH as _;
580+
581+
/// The interrupt is triggered while the signal is held low.
582+
pub const TRIGGER_LOW: usize = bindings::IRQF_TRIGGER_LOW as _;
583+
584+
/// Allow sharing the irq among several devices.
585+
pub const SHARED: usize = bindings::IRQF_SHARED as _;
586+
587+
/// Set by callers when they expect sharing mismatches to occur.
588+
pub const PROBE_SHARED: usize = bindings::IRQF_PROBE_SHARED as _;
589+
590+
/// Flag to mark this interrupt as timer interrupt.
591+
pub const TIMER: usize = bindings::IRQF_TIMER as _;
592+
593+
/// Interrupt is per cpu.
594+
pub const PERCPU: usize = bindings::IRQF_PERCPU as _;
595+
596+
/// Flag to exclude this interrupt from irq balancing.
597+
pub const NOBALANCING: usize = bindings::IRQF_NOBALANCING as _;
598+
599+
/// Interrupt is used for polling (only the interrupt that is registered first in a shared
600+
/// interrupt is considered for performance reasons).
601+
pub const IRQPOLL: usize = bindings::IRQF_IRQPOLL as _;
602+
603+
/// Interrupt is not reenabled after the hardirq handler finished. Used by threaded interrupts
604+
/// which need to keep the irq line disabled until the threaded handler has been run.
605+
pub const ONESHOT: usize = bindings::IRQF_ONESHOT as _;
606+
607+
/// Do not disable this IRQ during suspend. Does not guarantee that this interrupt will wake
608+
/// the system from a suspended state.
609+
pub const NO_SUSPEND: usize = bindings::IRQF_NO_SUSPEND as _;
610+
611+
/// Force enable it on resume even if [`NO_SUSPEND`] is set.
612+
pub const FORCE_RESUME: usize = bindings::IRQF_FORCE_RESUME as _;
613+
614+
/// Interrupt cannot be threaded.
615+
pub const NO_THREAD: usize = bindings::IRQF_NO_THREAD as _;
616+
617+
/// Resume IRQ early during syscore instead of at device resume time.
618+
pub const EARLY_RESUME: usize = bindings::IRQF_EARLY_RESUME as _;
619+
620+
/// If the IRQ is shared with a NO_SUSPEND user, execute this interrupt handler after
621+
/// suspending interrupts. For system wakeup devices users need to implement wakeup detection
622+
/// in their interrupt handlers.
623+
pub const COND_SUSPEND: usize = bindings::IRQF_COND_SUSPEND as _;
624+
625+
/// Don't enable IRQ or NMI automatically when users request it. Users will enable it
626+
/// explicitly by `enable_irq` or `enable_nmi` later.
627+
pub const NO_AUTOEN: usize = bindings::IRQF_NO_AUTOEN as _;
628+
629+
/// Exclude from runnaway detection for IPI and similar handlers, depends on `PERCPU`.
630+
pub const NO_DEBUG: usize = bindings::IRQF_NO_DEBUG as _;
631+
}
632+
332633
/// A guard to call `chained_irq_exit` after `chained_irq_enter` was called.
333634
///
334635
/// It is also used as evidence that a previous `chained_irq_enter` was called. So there are no

0 commit comments

Comments
 (0)