Skip to main content

irq_framework/
types.rs

1use alloc::boxed::Box;
2use core::ptr::NonNull;
3
4/// A platform IRQ number.
5#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
6pub struct IrqNumber(pub usize);
7
8/// A logical CPU id.
9#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
10pub struct CpuId(pub usize);
11
12/// A compact CPU mask for low-level IRQ affinity.
13#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
14pub struct CpuMask {
15    bits: u128,
16}
17
18impl CpuMask {
19    /// Creates an empty CPU mask.
20    pub const fn empty() -> Self {
21        Self { bits: 0 }
22    }
23
24    /// Creates a CPU mask containing a single CPU.
25    pub fn from_cpu(cpu: CpuId) -> Self {
26        let mut mask = Self::empty();
27        mask.insert(cpu);
28        mask
29    }
30
31    /// Creates a CPU mask containing CPUs in the range `0..cpu_count`.
32    pub fn first_n(cpu_count: usize) -> Self {
33        let mut mask = Self::empty();
34        let end = cpu_count.min(u128::BITS as usize);
35        for cpu in 0..end {
36            mask.insert(CpuId(cpu));
37        }
38        mask
39    }
40
41    /// Adds a CPU to this mask.
42    pub fn insert(&mut self, cpu: CpuId) {
43        if cpu.0 < u128::BITS as usize {
44            self.bits |= 1u128 << cpu.0;
45        }
46    }
47
48    /// Removes a CPU from this mask.
49    pub fn remove(&mut self, cpu: CpuId) {
50        if cpu.0 < u128::BITS as usize {
51            self.bits &= !(1u128 << cpu.0);
52        }
53    }
54
55    /// Returns whether the CPU is present in this mask.
56    pub const fn contains(self, cpu: CpuId) -> bool {
57        cpu.0 < u128::BITS as usize && (self.bits & (1u128 << cpu.0)) != 0
58    }
59
60    /// Returns whether no CPU is present in this mask.
61    pub const fn is_empty(self) -> bool {
62        self.bits == 0
63    }
64
65    /// Iterates over the CPUs in this mask.
66    pub fn iter(self) -> CpuMaskIter {
67        CpuMaskIter { bits: self.bits }
68    }
69}
70
71/// Iterator over [`CpuMask`].
72pub struct CpuMaskIter {
73    bits: u128,
74}
75
76impl Iterator for CpuMaskIter {
77    type Item = CpuId;
78
79    fn next(&mut self) -> Option<Self::Item> {
80        if self.bits == 0 {
81            return None;
82        }
83        let cpu = self.bits.trailing_zeros() as usize;
84        self.bits &= !(1u128 << cpu);
85        Some(CpuId(cpu))
86    }
87}
88
89/// IRQ registration scope.
90#[derive(Clone, Copy, Debug, Eq, PartialEq)]
91pub enum IrqScope {
92    /// The action is visible on every CPU.
93    Global,
94    /// The action is CPU-local and only visible to matching CPUs.
95    PerCpu {
96        /// Target CPUs.
97        cpus: CpuMask,
98    },
99}
100
101/// Hardware routing preference for an IRQ line.
102#[derive(Clone, Copy, Debug, Eq, PartialEq)]
103pub enum IrqAffinity {
104    /// The platform may route the line to any CPU.
105    Any,
106    /// Route the line to one fixed logical CPU.
107    Fixed(CpuId),
108}
109
110/// Execution contract for an IRQ action.
111#[derive(Clone, Copy, Debug, Eq, PartialEq)]
112pub enum IrqExecution {
113    /// The handler may run concurrently if the controller delivers it that way.
114    Concurrent,
115    /// The framework prevents nested/concurrent calls to this action.
116    NonReentrant,
117}
118
119/// Whether an IRQ line is exclusive or shared.
120#[derive(Clone, Copy, Debug, Eq, PartialEq)]
121pub enum ShareMode {
122    /// No other action can share the IRQ.
123    Exclusive,
124    /// Multiple actions can share the IRQ.
125    Shared,
126}
127
128/// Whether an IRQ action should be enabled after registration.
129#[derive(Clone, Copy, Debug, Eq, PartialEq)]
130pub enum AutoEnable {
131    /// Register the action but leave it disabled.
132    No,
133    /// Enable the action after registration.
134    Yes,
135}
136
137/// Return value from a raw IRQ handler.
138#[derive(Clone, Copy, Debug, Eq, PartialEq)]
139pub enum IrqReturn {
140    /// This action did not handle the IRQ.
141    Unhandled,
142    /// This action handled the IRQ.
143    Handled,
144    /// This action handled the IRQ and asks the OS adapter to wake deferred work.
145    Wake,
146}
147
148/// Aggregated dispatch result.
149#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
150pub struct IrqOutcome {
151    /// At least one action handled the IRQ.
152    pub handled: bool,
153    /// At least one action requested a wakeup.
154    pub wake: bool,
155    /// Number of handlers called by this dispatch.
156    pub called: usize,
157}
158
159/// IRQ status snapshot.
160#[derive(Clone, Copy, Debug, Eq, PartialEq)]
161pub struct IrqStatus {
162    /// Whether this action is enabled in the framework.
163    pub action_enabled: bool,
164    /// Whether the platform line is enabled.
165    pub line_enabled: bool,
166    /// Whether the platform reports the IRQ pending.
167    pub pending: bool,
168    /// Whether the platform reports the IRQ in service.
169    pub in_service: bool,
170    /// Number of in-flight dispatches for this descriptor.
171    pub in_flight: usize,
172    /// Whether this action is currently running.
173    pub action_running: bool,
174}
175
176/// IRQ framework errors.
177#[derive(Clone, Copy, Debug, Eq, PartialEq)]
178pub enum IrqError {
179    /// Invalid IRQ number.
180    InvalidIrq,
181    /// Invalid CPU id.
182    InvalidCpu,
183    /// The target CPU is offline.
184    CpuOffline,
185    /// IRQ line/action sharing rules reject the operation.
186    Busy,
187    /// Allocation failed.
188    NoMemory,
189    /// The requested descriptor or action does not exist.
190    NotFound,
191    /// This operation is not legal from IRQ context.
192    InIrqContext,
193    /// The platform adapter does not support this operation.
194    Unsupported,
195    /// The platform controller reported an error.
196    Controller,
197}
198
199/// Context passed to IRQ handlers.
200#[derive(Clone, Copy, Debug, Eq, PartialEq)]
201pub struct IrqContext {
202    /// IRQ number being dispatched.
203    pub irq: IrqNumber,
204    /// CPU handling the IRQ.
205    pub cpu: CpuId,
206}
207
208/// Raw IRQ handler ABI.
209pub type RawIrqHandler = unsafe fn(ctx: IrqContext, data: NonNull<()>) -> IrqReturn;
210
211/// Boxed IRQ handler ABI.
212pub type BoxedIrqHandler = Box<dyn FnMut(IrqContext) -> IrqReturn + Send + 'static>;
213
214pub(crate) enum IrqHandler {
215    Raw {
216        handler: RawIrqHandler,
217        data: NonNull<()>,
218    },
219    Boxed(BoxedIrqHandler),
220}
221
222/// External capabilities supplied by the OS/platform adapter.
223pub trait IrqOps {
224    /// Saved local IRQ state.
225    type LocalIrqState: Copy;
226
227    /// Returns the current CPU.
228    fn current_cpu(&self) -> CpuId;
229
230    /// Returns whether the CPU is online.
231    fn cpu_online(&self, cpu: CpuId) -> bool;
232
233    /// Returns whether the current execution context is an IRQ context.
234    fn in_irq_context(&self) -> bool;
235
236    /// Saves and disables local IRQs for metadata lock acquisition.
237    fn local_irq_save(&self) -> Self::LocalIrqState;
238
239    /// Restores local IRQ state saved by [`IrqOps::local_irq_save`].
240    fn local_irq_restore(&self, state: Self::LocalIrqState);
241
242    /// Runs a thunk synchronously on the target CPU.
243    fn run_on_cpu_sync(
244        &self,
245        cpu: CpuId,
246        f: unsafe fn(*mut ()),
247        arg: *mut (),
248    ) -> Result<(), IrqError>;
249
250    /// Routes a global IRQ line to the requested CPU affinity.
251    fn set_affinity(&self, _irq: IrqNumber, _affinity: IrqAffinity) -> Result<(), IrqError> {
252        Err(IrqError::Unsupported)
253    }
254
255    /// Enables or disables an IRQ line.
256    fn set_enabled(
257        &self,
258        irq: IrqNumber,
259        cpu: Option<CpuId>,
260        enabled: bool,
261    ) -> Result<(), IrqError>;
262
263    /// Returns whether the IRQ line is enabled.
264    fn is_enabled(&self, irq: IrqNumber, cpu: Option<CpuId>) -> Result<bool, IrqError>;
265
266    /// Returns whether the IRQ line is pending.
267    fn is_pending(&self, irq: IrqNumber, cpu: Option<CpuId>) -> Result<bool, IrqError>;
268
269    /// Returns whether the IRQ line is in service.
270    fn is_in_service(&self, irq: IrqNumber, cpu: Option<CpuId>) -> Result<bool, IrqError>;
271
272    /// Relaxes a spin wait.
273    fn relax(&self);
274}
275
276/// Request parameters for an IRQ action.
277pub struct IrqRequest {
278    pub(crate) handler: Option<IrqHandler>,
279    pub(crate) scope: IrqScope,
280    pub(crate) affinity: IrqAffinity,
281    pub(crate) execution: IrqExecution,
282    pub(crate) share_mode: ShareMode,
283    pub(crate) auto_enable: AutoEnable,
284}
285
286impl IrqRequest {
287    /// Creates a new exclusive, global, auto-enabled IRQ request.
288    pub fn new(handler: RawIrqHandler, data: NonNull<()>) -> Self {
289        Self {
290            handler: Some(IrqHandler::Raw { handler, data }),
291            scope: IrqScope::Global,
292            affinity: IrqAffinity::Any,
293            execution: IrqExecution::Concurrent,
294            share_mode: ShareMode::Exclusive,
295            auto_enable: AutoEnable::Yes,
296        }
297    }
298
299    /// Creates a new exclusive, global, auto-enabled boxed IRQ request.
300    pub fn new_boxed(handler: BoxedIrqHandler) -> Self {
301        Self {
302            handler: Some(IrqHandler::Boxed(handler)),
303            scope: IrqScope::Global,
304            affinity: IrqAffinity::Any,
305            execution: IrqExecution::NonReentrant,
306            share_mode: ShareMode::Exclusive,
307            auto_enable: AutoEnable::Yes,
308        }
309    }
310
311    pub(crate) fn is_boxed(&self) -> bool {
312        matches!(self.handler.as_ref(), Some(IrqHandler::Boxed(_)))
313    }
314
315    /// Sets the IRQ scope.
316    pub fn scope(mut self, scope: IrqScope) -> Self {
317        self.scope = scope;
318        self
319    }
320
321    /// Sets the IRQ affinity.
322    pub fn affinity(mut self, affinity: IrqAffinity) -> Self {
323        self.affinity = affinity;
324        self
325    }
326
327    /// Sets the action execution contract.
328    pub fn execution(mut self, execution: IrqExecution) -> Self {
329        self.execution = execution;
330        self
331    }
332
333    /// Sets the sharing mode.
334    pub fn share_mode(mut self, share_mode: ShareMode) -> Self {
335        self.share_mode = share_mode;
336        self
337    }
338
339    /// Sets whether the action should be enabled after request.
340    pub fn auto_enable(mut self, auto_enable: AutoEnable) -> Self {
341        self.auto_enable = auto_enable;
342        self
343    }
344
345    /// Returns whether the action should be enabled after request.
346    pub const fn auto_enable_mode(&self) -> AutoEnable {
347        self.auto_enable
348    }
349}
350
351/// Token returned from request and used for later lifecycle operations.
352#[derive(Clone, Copy, Debug, Eq, PartialEq)]
353pub struct IrqHandle {
354    pub(crate) irq: IrqNumber,
355    pub(crate) id: u64,
356}
357
358impl IrqHandle {
359    /// Returns the IRQ number associated with this handle.
360    pub const fn irq(self) -> IrqNumber {
361        self.irq
362    }
363
364    /// Returns the framework-local action id.
365    pub const fn id(self) -> u64 {
366        self.id
367    }
368}