Skip to content

Commit 7475988

Browse files
committed
rust: add irq abstractions
Based on irq.rs from 575b1c0 Signed-off-by: Philipp Stanner <[email protected]>
1 parent f176b3c commit 7475988

File tree

3 files changed

+225
-0
lines changed

3 files changed

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

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)