|
| 1 | +use crate::{ |
| 2 | + error::to_result, |
| 3 | + error::Result, |
| 4 | + str::CString, |
| 5 | + types::{ForeignOwnable, ScopeGuard}, |
| 6 | +}; |
| 7 | +use core::fmt; |
| 8 | +use core::marker::PhantomData; |
| 9 | + |
| 10 | +struct InternalRegistration<T: ForeignOwnable> { |
| 11 | + irq: u32, |
| 12 | + data: *mut core::ffi::c_void, |
| 13 | + name: CString, |
| 14 | + _p: PhantomData<T>, |
| 15 | +} |
| 16 | + |
| 17 | +impl<T: ForeignOwnable> InternalRegistration<T> { |
| 18 | + /// Registers a new irq handler. |
| 19 | + /// |
| 20 | + /// # Safety |
| 21 | + /// |
| 22 | + /// Callers must ensure that `handler` and `thread_fn` are compatible with the registration, |
| 23 | + /// that is, that they only use their second argument while the call is happening and that they |
| 24 | + /// only call [`T::borrow`] on it (e.g., they shouldn't call [`T::from_foreign`] and consume |
| 25 | + /// it). |
| 26 | + unsafe fn try_new( |
| 27 | + irq: core::ffi::c_uint, |
| 28 | + handler: bindings::irq_handler_t, |
| 29 | + thread_fn: bindings::irq_handler_t, |
| 30 | + flags: usize, |
| 31 | + data: T, |
| 32 | + name: fmt::Arguments<'_>, |
| 33 | + ) -> Result<Self> { |
| 34 | + let ptr = data.into_foreign() as *mut _; |
| 35 | + let name = CString::try_from_fmt(name)?; |
| 36 | + let guard = ScopeGuard::new(|| { |
| 37 | + // SAFETY: `ptr` came from a previous call to `into_foreign`. |
| 38 | + unsafe { T::from_foreign(ptr) }; |
| 39 | + }); |
| 40 | + // SAFETY: `name` and `ptr` remain valid as long as the registration is alive. |
| 41 | + to_result(unsafe { |
| 42 | + bindings::request_threaded_irq( |
| 43 | + irq, |
| 44 | + handler, |
| 45 | + thread_fn, |
| 46 | + flags as _, |
| 47 | + name.as_char_ptr(), |
| 48 | + ptr, |
| 49 | + ) |
| 50 | + })?; |
| 51 | + guard.dismiss(); |
| 52 | + Ok(Self { |
| 53 | + irq, |
| 54 | + name, |
| 55 | + data: ptr, |
| 56 | + _p: PhantomData, |
| 57 | + }) |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +impl<T: ForeignOwnable> Drop for InternalRegistration<T> { |
| 62 | + fn drop(&mut self) { |
| 63 | + // Unregister irq handler. |
| 64 | + // |
| 65 | + // SAFETY: When `try_new` succeeds, the irq was successfully requested, so it is ok to free |
| 66 | + // it here. |
| 67 | + unsafe { bindings::free_irq(self.irq, self.data) }; |
| 68 | + |
| 69 | + // Free context data. |
| 70 | + // |
| 71 | + // SAFETY: This matches the call to `into_foreign` from `try_new` in the success case. |
| 72 | + unsafe { T::from_foreign(self.data) }; |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +/// An irq handler. |
| 77 | +pub trait Handler { |
| 78 | + /// The context data associated with and made available to the handler. |
| 79 | + type Data: ForeignOwnable; |
| 80 | + |
| 81 | + /// Called from interrupt context when the irq happens. |
| 82 | + fn handle_irq(data: <Self::Data as ForeignOwnable>::Borrowed<'_>) -> Return; |
| 83 | +} |
| 84 | + |
| 85 | +/// The registration of an interrupt handler. |
| 86 | +/// |
| 87 | +/// # Examples |
| 88 | +/// |
| 89 | +/// The following is an example of a regular handler with a boxed `u32` as data. |
| 90 | +/// |
| 91 | +/// ``` |
| 92 | +/// # use kernel::prelude::*; |
| 93 | +/// use kernel::irq; |
| 94 | +/// |
| 95 | +/// struct Example; |
| 96 | +/// |
| 97 | +/// impl irq::Handler for Example { |
| 98 | +/// type Data = Box<u32>; |
| 99 | +/// |
| 100 | +/// fn handle_irq(_data: &u32) -> irq::Return { |
| 101 | +/// irq::Return::None |
| 102 | +/// } |
| 103 | +/// } |
| 104 | +/// |
| 105 | +/// fn request_irq(irq: u32, data: Box<u32>) -> Result<irq::Registration<Example>> { |
| 106 | +/// irq::Registration::try_new(irq, data, irq::flags::SHARED, fmt!("example_{irq}")) |
| 107 | +/// } |
| 108 | +/// ``` |
| 109 | +pub struct Registration<H: Handler>(InternalRegistration<H::Data>); |
| 110 | + |
| 111 | +impl<H: Handler> Registration<H> { |
| 112 | + /// Registers a new irq handler. |
| 113 | + /// |
| 114 | + /// The valid values of `flags` come from the [`flags`] module. |
| 115 | + pub fn try_new( |
| 116 | + irq: u32, |
| 117 | + data: H::Data, |
| 118 | + flags: usize, |
| 119 | + name: fmt::Arguments<'_>, |
| 120 | + ) -> Result<Self> { |
| 121 | + // SAFETY: `handler` only calls `H::Data::borrow` on `raw_data`. |
| 122 | + Ok(Self(unsafe { |
| 123 | + InternalRegistration::try_new(irq, Some(Self::handler), None, flags, data, name)? |
| 124 | + })) |
| 125 | + } |
| 126 | + |
| 127 | + unsafe extern "C" fn handler( |
| 128 | + _irq: core::ffi::c_int, |
| 129 | + raw_data: *mut core::ffi::c_void, |
| 130 | + ) -> bindings::irqreturn_t { |
| 131 | + // SAFETY: On registration, `into_foreign` was called, so it is safe to borrow from it here |
| 132 | + // because `from_foreign` is called only after the irq is unregistered. |
| 133 | + let data = unsafe { H::Data::borrow(raw_data) }; |
| 134 | + H::handle_irq(data) as _ |
| 135 | + } |
| 136 | +} |
| 137 | + |
| 138 | +/// The return value from interrupt handlers. |
| 139 | +pub enum Return { |
| 140 | + /// The interrupt was not from this device or was not handled. |
| 141 | + None = bindings::irqreturn_IRQ_NONE as _, |
| 142 | + |
| 143 | + /// The interrupt was handled by this device. |
| 144 | + Handled = bindings::irqreturn_IRQ_HANDLED as _, |
| 145 | + |
| 146 | + /// The handler wants the handler thread to wake up. |
| 147 | + WakeThread = bindings::irqreturn_IRQ_WAKE_THREAD as _, |
| 148 | +} |
| 149 | + |
| 150 | +/// Container for interrupt flags. |
| 151 | +pub mod flags { |
| 152 | + use crate::bindings; |
| 153 | + |
| 154 | + /// Use the interrupt line as already configured. |
| 155 | + pub const TRIGGER_NONE: usize = bindings::IRQF_TRIGGER_NONE as _; |
| 156 | + |
| 157 | + /// The interrupt is triggered when the signal goes from low to high. |
| 158 | + pub const TRIGGER_RISING: usize = bindings::IRQF_TRIGGER_RISING as _; |
| 159 | + |
| 160 | + /// The interrupt is triggered when the signal goes from high to low. |
| 161 | + pub const TRIGGER_FALLING: usize = bindings::IRQF_TRIGGER_FALLING as _; |
| 162 | + |
| 163 | + /// The interrupt is triggered while the signal is held high. |
| 164 | + pub const TRIGGER_HIGH: usize = bindings::IRQF_TRIGGER_HIGH as _; |
| 165 | + |
| 166 | + /// The interrupt is triggered while the signal is held low. |
| 167 | + pub const TRIGGER_LOW: usize = bindings::IRQF_TRIGGER_LOW as _; |
| 168 | + |
| 169 | + /// Allow sharing the irq among several devices. |
| 170 | + pub const SHARED: usize = bindings::IRQF_SHARED as _; |
| 171 | + |
| 172 | + /// Set by callers when they expect sharing mismatches to occur. |
| 173 | + pub const PROBE_SHARED: usize = bindings::IRQF_PROBE_SHARED as _; |
| 174 | + |
| 175 | + /// Flag to mark this interrupt as timer interrupt. |
| 176 | + pub const TIMER: usize = bindings::IRQF_TIMER as _; |
| 177 | + |
| 178 | + /// Interrupt is per cpu. |
| 179 | + pub const PERCPU: usize = bindings::IRQF_PERCPU as _; |
| 180 | + |
| 181 | + /// Flag to exclude this interrupt from irq balancing. |
| 182 | + pub const NOBALANCING: usize = bindings::IRQF_NOBALANCING as _; |
| 183 | + |
| 184 | + /// Interrupt is used for polling (only the interrupt that is registered first in a shared |
| 185 | + /// interrupt is considered for performance reasons). |
| 186 | + pub const IRQPOLL: usize = bindings::IRQF_IRQPOLL as _; |
| 187 | + |
| 188 | + /// Interrupt is not reenabled after the hardirq handler finished. Used by threaded interrupts |
| 189 | + /// which need to keep the irq line disabled until the threaded handler has been run. |
| 190 | + pub const ONESHOT: usize = bindings::IRQF_ONESHOT as _; |
| 191 | + |
| 192 | + /// Do not disable this IRQ during suspend. Does not guarantee that this interrupt will wake |
| 193 | + /// the system from a suspended state. |
| 194 | + pub const NO_SUSPEND: usize = bindings::IRQF_NO_SUSPEND as _; |
| 195 | + |
| 196 | + /// Force enable it on resume even if [`NO_SUSPEND`] is set. |
| 197 | + pub const FORCE_RESUME: usize = bindings::IRQF_FORCE_RESUME as _; |
| 198 | + |
| 199 | + /// Interrupt cannot be threaded. |
| 200 | + pub const NO_THREAD: usize = bindings::IRQF_NO_THREAD as _; |
| 201 | + |
| 202 | + /// Resume IRQ early during syscore instead of at device resume time. |
| 203 | + pub const EARLY_RESUME: usize = bindings::IRQF_EARLY_RESUME as _; |
| 204 | + |
| 205 | + /// If the IRQ is shared with a NO_SUSPEND user, execute this interrupt handler after |
| 206 | + /// suspending interrupts. For system wakeup devices users need to implement wakeup detection |
| 207 | + /// in their interrupt handlers. |
| 208 | + pub const COND_SUSPEND: usize = bindings::IRQF_COND_SUSPEND as _; |
| 209 | + |
| 210 | + /// Don't enable IRQ or NMI automatically when users request it. Users will enable it |
| 211 | + /// explicitly by `enable_irq` or `enable_nmi` later. |
| 212 | + pub const NO_AUTOEN: usize = bindings::IRQF_NO_AUTOEN as _; |
| 213 | + |
| 214 | + /// Exclude from runnaway detection for IPI and similar handlers, depends on `PERCPU`. |
| 215 | + pub const NO_DEBUG: usize = bindings::IRQF_NO_DEBUG as _; |
| 216 | +} |
0 commit comments