Skip to content

Commit f2e2afb

Browse files
committed
rust: add irq abstractions
Based on irq.rs from 575b1c0
1 parent 3dcbf56 commit f2e2afb

File tree

3 files changed

+219
-0
lines changed

3 files changed

+219
-0
lines changed

rust/bindings/bindings_helper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
#include <linux/device.h>
1111
#include <linux/errname.h>
1212
#include <linux/ethtool.h>
13+
#include <linux/interrupt.h>
1314
#include <linux/io.h>
15+
#include <linux/irq.h>
1416
#include <linux/mdio.h>
1517
#include <linux/pci.h>
1618
#include <linux/phy.h>

rust/kernel/irq.rs

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
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+
}

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub mod error;
4242
pub mod init;
4343
pub mod io_mem;
4444
pub mod ioctl;
45+
pub mod irq;
4546
#[cfg(CONFIG_KUNIT)]
4647
pub mod kunit;
4748
#[cfg(CONFIG_NET)]

0 commit comments

Comments
 (0)