xref: /DragonOS/kernel/src/exception/irqdata.rs (revision b5b571e02693d91eb6918d3b7561e088c3e7ee81)
1 use core::{any::Any, fmt::Debug};
2 
3 use alloc::sync::{Arc, Weak};
4 use intertrait::CastFromSync;
5 
6 use crate::libs::{
7     cpumask::CpuMask,
8     rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard},
9     spinlock::{SpinLock, SpinLockGuard},
10 };
11 
12 use super::{
13     irqchip::{IrqChip, IrqChipData},
14     irqdomain::IrqDomain,
15     msi::MsiDesc,
16     HardwareIrqNumber, IrqNumber,
17 };
18 
19 /// per irq chip data passed down to chip functions
20 ///
21 /// 该结构体用于表示每个Irq的私有数据,且与具体的中断芯片绑定
22 ///
23 /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h#179
24 #[allow(dead_code)]
25 #[derive(Debug)]
26 pub struct IrqData {
27     /// 中断号, 用于表示软件逻辑视角的中断号,全局唯一
28     irq: IrqNumber,
29     inner: SpinLock<InnerIrqData>,
30 
31     chip_info: RwLock<InnerIrqChipInfo>,
32 }
33 
34 impl IrqData {
35     pub fn new(
36         irq: IrqNumber,
37         hwirq: HardwareIrqNumber,
38         common_data: Arc<IrqCommonData>,
39         chip: Arc<dyn IrqChip>,
40     ) -> Self {
41         return IrqData {
42             irq,
43             inner: SpinLock::new(InnerIrqData {
44                 hwirq,
45                 common_data,
46 
47                 domain: None,
48                 parent_data: None,
49             }),
50             chip_info: RwLock::new(InnerIrqChipInfo {
51                 chip: Some(chip),
52                 chip_data: None,
53             }),
54         };
55     }
56 
57     pub fn irqd_set(&self, status: IrqStatus) {
58         // clone是为了释放inner锁
59         let common_data = self.inner.lock_irqsave().common_data.clone();
60         common_data.insert_status(status);
61     }
62 
63     #[allow(dead_code)]
64     pub fn irqd_clear(&self, status: IrqStatus) {
65         // clone是为了释放inner锁
66         let common_data = self.inner.lock_irqsave().common_data.clone();
67         common_data.clear_status(status);
68     }
69 
70     pub fn irq(&self) -> IrqNumber {
71         self.irq
72     }
73 
74     pub fn hardware_irq(&self) -> HardwareIrqNumber {
75         self.inner.lock_irqsave().hwirq
76     }
77 
78     /// 是否为电平触发
79     pub fn is_level_type(&self) -> bool {
80         self.inner
81             .lock_irqsave()
82             .common_data
83             .inner
84             .lock_irqsave()
85             .state
86             .is_level_type()
87     }
88 
89     pub fn is_wakeup_set(&self) -> bool {
90         self.inner
91             .lock_irqsave()
92             .common_data
93             .inner
94             .lock_irqsave()
95             .state
96             .is_wakeup_set()
97     }
98 
99     pub fn common_data(&self) -> Arc<IrqCommonData> {
100         self.inner.lock_irqsave().common_data.clone()
101     }
102 
103     pub fn domain(&self) -> Option<Arc<IrqDomain>> {
104         self.inner.lock_irqsave().domain.clone()
105     }
106 
107     pub fn inner(&self) -> SpinLockGuard<InnerIrqData> {
108         self.inner.lock_irqsave()
109     }
110 
111     pub fn chip_info_read(&self) -> RwLockReadGuard<InnerIrqChipInfo> {
112         self.chip_info.read()
113     }
114 
115     pub fn chip_info_read_irqsave(&self) -> RwLockReadGuard<InnerIrqChipInfo> {
116         self.chip_info.read_irqsave()
117     }
118 
119     pub fn chip_info_write_irqsave(&self) -> RwLockWriteGuard<InnerIrqChipInfo> {
120         self.chip_info.write_irqsave()
121     }
122 
123     pub fn parent_data(&self) -> Option<Weak<IrqData>> {
124         self.inner.lock_irqsave().parent_data.clone()
125     }
126 }
127 
128 #[allow(dead_code)]
129 #[derive(Debug)]
130 pub struct InnerIrqData {
131     /// 硬件中断号, 用于表示在某个IrqDomain中的中断号
132     hwirq: HardwareIrqNumber,
133     /// 涉及的所有irqchip之间共享的数据
134     common_data: Arc<IrqCommonData>,
135 
136     /// 中断域
137     domain: Option<Arc<IrqDomain>>,
138     /// 中断的父中断(如果具有中断域继承的话)
139     parent_data: Option<Weak<IrqData>>,
140 }
141 
142 impl InnerIrqData {
143     pub fn set_hwirq(&mut self, hwirq: HardwareIrqNumber) {
144         self.hwirq = hwirq;
145     }
146 
147     #[allow(dead_code)]
148     pub fn domain(&self) -> Option<Arc<IrqDomain>> {
149         self.domain.clone()
150     }
151 
152     pub fn set_domain(&mut self, domain: Option<Arc<IrqDomain>>) {
153         self.domain = domain;
154     }
155 }
156 
157 #[derive(Debug)]
158 pub struct InnerIrqChipInfo {
159     /// 绑定到的中断芯片
160     chip: Option<Arc<dyn IrqChip>>,
161     /// 中断芯片的私有数据(与当前irq相关)
162     chip_data: Option<Arc<dyn IrqChipData>>,
163 }
164 
165 impl InnerIrqChipInfo {
166     pub fn set_chip(&mut self, chip: Option<Arc<dyn IrqChip>>) {
167         self.chip = chip;
168     }
169 
170     pub fn set_chip_data(&mut self, chip_data: Option<Arc<dyn IrqChipData>>) {
171         self.chip_data = chip_data;
172     }
173 
174     pub fn chip(&self) -> Arc<dyn IrqChip> {
175         self.chip.clone().unwrap()
176     }
177 
178     pub fn chip_data(&self) -> Option<Arc<dyn IrqChipData>> {
179         self.chip_data.clone()
180     }
181 }
182 
183 /// per irq data shared by all irqchips
184 ///
185 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h#147
186 #[derive(Debug)]
187 pub struct IrqCommonData {
188     inner: SpinLock<InnerIrqCommonData>,
189 }
190 
191 impl IrqCommonData {
192     pub fn new() -> Self {
193         let inner = InnerIrqCommonData {
194             state: IrqStatus::empty(),
195             handler_data: None,
196             msi_desc: None,
197             affinity: CpuMask::new(),
198         };
199         return IrqCommonData {
200             inner: SpinLock::new(inner),
201         };
202     }
203 
204     pub fn insert_status(&self, status: IrqStatus) {
205         self.inner.lock_irqsave().irqd_insert(status);
206     }
207 
208     pub fn clear_status(&self, status: IrqStatus) {
209         self.inner.lock_irqsave().irqd_clear(status);
210     }
211 
212     pub fn clear_managed_shutdown(&self) {
213         self.inner
214             .lock_irqsave()
215             .state
216             .remove(IrqStatus::IRQD_MANAGED_SHUTDOWN);
217     }
218 
219     #[allow(dead_code)]
220     pub fn masked(&self) -> bool {
221         self.inner.lock_irqsave().state.masked()
222     }
223 
224     pub fn set_masked(&self) {
225         self.inner
226             .lock_irqsave()
227             .state
228             .insert(IrqStatus::IRQD_IRQ_MASKED);
229     }
230 
231     pub fn clear_masked(&self) {
232         self.clear_status(IrqStatus::IRQD_IRQ_MASKED);
233     }
234 
235     pub fn set_inprogress(&self) {
236         self.inner
237             .lock_irqsave()
238             .state
239             .insert(IrqStatus::IRQD_IRQ_INPROGRESS);
240     }
241 
242     pub fn clear_inprogress(&self) {
243         self.inner
244             .lock_irqsave()
245             .state
246             .remove(IrqStatus::IRQD_IRQ_INPROGRESS);
247     }
248 
249     pub fn disabled(&self) -> bool {
250         self.inner.lock_irqsave().state.disabled()
251     }
252 
253     #[allow(dead_code)]
254     pub fn set_disabled(&self) {
255         self.inner
256             .lock_irqsave()
257             .state
258             .insert(IrqStatus::IRQD_IRQ_DISABLED);
259     }
260 
261     pub fn clear_disabled(&self) {
262         self.clear_status(IrqStatus::IRQD_IRQ_DISABLED);
263     }
264 
265     pub fn status(&self) -> IrqStatus {
266         self.inner.lock_irqsave().state
267     }
268 
269     pub fn trigger_type(&self) -> IrqLineStatus {
270         self.inner.lock_irqsave().state.trigger_type()
271     }
272 
273     pub fn set_trigger_type(&self, trigger: IrqLineStatus) {
274         self.inner.lock_irqsave().state.set_trigger_type(trigger);
275     }
276 
277     pub fn set_started(&self) {
278         self.inner
279             .lock_irqsave()
280             .state
281             .insert(IrqStatus::IRQD_IRQ_STARTED);
282     }
283 
284     pub fn affinity(&self) -> CpuMask {
285         self.inner.lock_irqsave().affinity.clone()
286     }
287 
288     pub fn set_affinity(&self, affinity: CpuMask) {
289         self.inner.lock_irqsave().affinity = affinity;
290     }
291 
292     pub fn inner(&self) -> SpinLockGuard<InnerIrqCommonData> {
293         self.inner.lock_irqsave()
294     }
295 }
296 
297 #[allow(dead_code)]
298 #[derive(Debug)]
299 pub struct InnerIrqCommonData {
300     /// status information for irq chip functions.
301     state: IrqStatus,
302     /// per-IRQ data for the irq_chip methods
303     handler_data: Option<Arc<dyn IrqHandlerData>>,
304     msi_desc: Option<Arc<MsiDesc>>,
305     affinity: CpuMask,
306 }
307 
308 impl InnerIrqCommonData {
309     pub fn irqd_insert(&mut self, status: IrqStatus) {
310         self.state.insert(status);
311     }
312 
313     pub fn irqd_clear(&mut self, status: IrqStatus) {
314         self.state.remove(status);
315     }
316 
317     #[allow(dead_code)]
318     pub fn set_handler_data(&mut self, handler_data: Option<Arc<dyn IrqHandlerData>>) {
319         self.handler_data = handler_data;
320     }
321 
322     #[allow(dead_code)]
323     pub fn handler_data(&self) -> Option<Arc<dyn IrqHandlerData>> {
324         self.handler_data.clone()
325     }
326 }
327 
328 /// 中断处理函数传入的数据
329 pub trait IrqHandlerData: Send + Sync + Any + Debug + CastFromSync {}
330 
331 bitflags! {
332     /// 中断线状态
333     /// https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h?fi=IRQ_TYPE_PROBE#77
334     #[allow(clippy::bad_bit_mask)]
335     pub struct IrqLineStatus: u32 {
336         /// 默认,未指明类型
337         const IRQ_TYPE_NONE     = 0x00000000;
338         /// 上升沿触发
339         const IRQ_TYPE_EDGE_RISING  = 0x00000001;
340         /// 下降沿触发
341         const IRQ_TYPE_EDGE_FALLING = 0x00000002;
342         /// 上升沿和下降沿触发
343         const IRQ_TYPE_EDGE_BOTH    = Self::IRQ_TYPE_EDGE_RISING.bits | Self::IRQ_TYPE_EDGE_FALLING.bits;
344         /// 高电平触发
345         const IRQ_TYPE_LEVEL_HIGH   = 0x00000004;
346         /// 低电平触发
347         const IRQ_TYPE_LEVEL_LOW    = 0x00000008;
348         /// 过滤掉电平位的掩码
349         const IRQ_TYPE_LEVEL_MASK   = Self::IRQ_TYPE_LEVEL_LOW.bits | Self::IRQ_TYPE_LEVEL_HIGH.bits;
350         /// 上述位掩码的掩码
351         const IRQ_TYPE_SENSE_MASK   = 0x0000000f;
352         /// 某些PICs使用此类型要求 `IrqChip::irq_set_type()` 设置硬件到一个合理的默认值
353         /// (由irqdomain的map()回调使用,以便为新分配的描述符同步硬件状态和软件标志位)。
354         const IRQ_TYPE_DEFAULT      = Self::IRQ_TYPE_SENSE_MASK.bits;
355 
356         /// 特定于探测的过程中的特殊标志
357         const IRQ_TYPE_PROBE        = 0x00000010;
358 
359         /// 中断是电平类型。当上述触发位通过`IrqChip::irq_set_type()` 修改时,也会在代码中更新
360         const IRQ_LEVEL     = 1 << 8;
361         /// 标记一个PER_CPU的中断。将保护其免受亲和性设置的影响
362         const IRQ_PER_CPU       = 1 << 9;
363         /// 中断不能被自动探测
364         const IRQ_NOPROBE       = 1 << 10;
365         /// 中断不能通过request_irq()请求
366         const IRQ_NOREQUEST     = 1 << 11;
367         /// 中断在request/setup_irq()中不会自动启用
368         const IRQ_NOAUTOEN      = 1 << 12;
369         /// 中断不能被平衡(亲和性设置)
370         const IRQ_NO_BALANCING      = 1 << 13;
371         /// 中断可以从进程上下文中迁移
372         const IRQ_MOVE_PCNTXT       = 1 << 14;
373         /// 中断嵌套在另一个线程中
374         const IRQ_NESTED_THREAD = 1 << 15;
375         /// 中断不能被线程化
376         const IRQ_NOTHREAD      = 1 << 16;
377         /// Dev_id是一个per-CPU变量
378         const IRQ_PER_CPU_DEVID = 1 << 17;
379         /// 总是由另一个中断轮询。将其从错误的中断检测机制和核心侧轮询中排除
380         const IRQ_IS_POLLED     = 1 << 18;
381         /// 禁用延迟的中断禁用 (Disable lazy irq disable)
382         const IRQ_DISABLE_UNLAZY    = 1 << 19;
383         /// 在/proc/interrupts中不显示
384         const IRQ_HIDDEN        = 1 << 20;
385         /// 从note_interrupt()调试中排除
386         const IRQ_NO_DEBUG      = 1 << 21;
387     }
388 
389 
390 
391 }
392 
393 impl IrqLineStatus {
394     pub const fn trigger_bits(&self) -> u32 {
395         self.bits & Self::IRQ_TYPE_SENSE_MASK.bits
396     }
397 
398     pub fn trigger_type(&self) -> Self {
399         *self & Self::IRQ_TYPE_SENSE_MASK
400     }
401 
402     pub fn is_level_type(&self) -> bool {
403         self.contains(Self::IRQ_LEVEL)
404     }
405 
406     /// 是否为高电平触发
407     ///
408     /// ## 返回
409     ///
410     /// - 如果不是电平触发类型,则返回None
411     /// - 如果是电平触发类型,则返回Some(bool),当为true时表示高电平触发
412     pub fn is_level_high(&self) -> Option<bool> {
413         if !self.is_level_type() {
414             return None;
415         }
416         return Some(self.contains(Self::IRQ_TYPE_LEVEL_HIGH));
417     }
418 
419     #[allow(dead_code)]
420     pub fn is_per_cpu_devid(&self) -> bool {
421         self.contains(Self::IRQ_PER_CPU_DEVID)
422     }
423 }
424 
425 bitflags! {
426     /// 中断状态(存储在IrqCommonData)
427     ///
428     /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h#227
429     #[allow(clippy::bad_bit_mask)]
430     pub struct IrqStatus: u32 {
431         const IRQD_TRIGGER_NONE = IrqLineStatus::IRQ_TYPE_NONE.bits();
432         const IRQD_TRIGGER_RISING = IrqLineStatus::IRQ_TYPE_EDGE_RISING.bits();
433         const IRQD_TRIGGER_FALLING = IrqLineStatus::IRQ_TYPE_EDGE_FALLING.bits();
434         const IRQD_TRIGGER_HIGH = IrqLineStatus::IRQ_TYPE_LEVEL_HIGH.bits();
435         const IRQD_TRIGGER_LOW = IrqLineStatus::IRQ_TYPE_LEVEL_LOW.bits();
436 
437         /// 触发类型位的掩码
438         const IRQD_TRIGGER_MASK = 0xf;
439         /// 亲和性设置待处理
440         const IRQD_SETAFFINITY_PENDING = 1 << 8;
441         /// 中断已激活
442         const IRQD_ACTIVATED = 1 << 9;
443         /// 对此IRQ禁用平衡
444         const IRQD_NO_BALANCING = 1 << 10;
445         /// 中断是每个CPU特定的
446         const IRQD_PER_CPU = 1 << 11;
447         /// 中断亲和性已设置
448         const IRQD_AFFINITY_SET = 1 << 12;
449         /// 中断是电平触发
450         const IRQD_LEVEL = 1 << 13;
451         /// 中断配置为从挂起状态唤醒
452         const IRQD_WAKEUP_STATE = 1 << 14;
453         /// 中断可以在进程上下文中移动
454         const IRQD_MOVE_PCNTXT = 1 << 15;
455         /// 中断被禁用
456         const IRQD_IRQ_DISABLED = 1 << 16;
457         /// 中断被屏蔽
458         const IRQD_IRQ_MASKED = 1 << 17;
459         /// 中断正在处理中
460         const IRQD_IRQ_INPROGRESS = 1 << 18;
461         /// 唤醒模式已准备就绪
462         const IRQD_WAKEUP_ARMED = 1 << 19;
463         /// 中断被转发到一个虚拟CPU
464         const IRQD_FORWARDED_TO_VCPU = 1 << 20;
465         /// 亲和性由内核自动管理
466         const IRQD_AFFINITY_MANAGED = 1 << 21;
467         /// 中断已启动
468         const IRQD_IRQ_STARTED = 1 << 22;
469         /// 由于空亲和性掩码而关闭的中断。仅适用于亲和性管理的中断。
470         const IRQD_MANAGED_SHUTDOWN = 1 << 23;
471         /// IRQ只允许单个亲和性目标
472         const IRQD_SINGLE_TARGET = 1 << 24;
473         /// 默认的触发器已设置
474         const IRQD_DEFAULT_TRIGGER_SET = 1 << 25;
475         /// 可以使用保留模式
476         const IRQD_CAN_RESERVE = 1 << 26;
477         /// Non-maskable MSI quirk for affinity change required
478         const IRQD_MSI_NOMASK_QUIRK = 1 << 27;
479         /// 强制要求`handle_irq_()`只能在真实的中断上下文中调用
480         const IRQD_HANDLE_ENFORCE_IRQCTX = 1 << 28;
481         /// 激活时设置亲和性。在禁用时不要调用irq_chip::irq_set_affinity()。
482         const IRQD_AFFINITY_ON_ACTIVATE = 1 << 29;
483         /// 如果irqpm具有标志 IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND,则在挂起时中断被启用。
484         const IRQD_IRQ_ENABLED_ON_SUSPEND = 1 << 30;
485     }
486 }
487 
488 #[allow(dead_code)]
489 impl IrqStatus {
490     pub const fn is_set_affinity_pending(&self) -> bool {
491         self.contains(Self::IRQD_SETAFFINITY_PENDING)
492     }
493 
494     pub const fn is_per_cpu(&self) -> bool {
495         self.contains(Self::IRQD_PER_CPU)
496     }
497 
498     pub const fn can_balance(&self) -> bool {
499         (self.bits & (Self::IRQD_PER_CPU.bits | Self::IRQD_NO_BALANCING.bits)) == 0
500     }
501 
502     pub const fn affinity_was_set(&self) -> bool {
503         self.contains(Self::IRQD_AFFINITY_SET)
504     }
505 
506     pub fn masked(&self) -> bool {
507         self.contains(Self::IRQD_IRQ_MASKED)
508     }
509 
510     pub fn disabled(&self) -> bool {
511         self.contains(Self::IRQD_IRQ_DISABLED)
512     }
513 
514     pub fn mark_affinity_set(&mut self) {
515         self.insert(Self::IRQD_AFFINITY_SET);
516     }
517 
518     pub const fn trigger_type_was_set(&self) -> bool {
519         self.contains(Self::IRQD_DEFAULT_TRIGGER_SET)
520     }
521 
522     pub fn mark_trigger_type_set(&mut self) {
523         self.insert(Self::IRQD_DEFAULT_TRIGGER_SET);
524     }
525 
526     pub const fn trigger_type(&self) -> IrqLineStatus {
527         IrqLineStatus::from_bits_truncate(self.bits & Self::IRQD_TRIGGER_MASK.bits)
528     }
529 
530     /// Must only be called inside irq_chip.irq_set_type() functions or
531     /// from the DT/ACPI setup code.
532     pub const fn set_trigger_type(&mut self, trigger: IrqLineStatus) {
533         self.bits &= !Self::IRQD_TRIGGER_MASK.bits;
534         self.bits |= trigger.bits & Self::IRQD_TRIGGER_MASK.bits;
535 
536         self.bits |= Self::IRQD_DEFAULT_TRIGGER_SET.bits;
537     }
538 
539     pub const fn is_level_type(&self) -> bool {
540         self.contains(Self::IRQD_LEVEL)
541     }
542 
543     /// Must only be called of irqchip.irq_set_affinity() or low level
544     /// hierarchy domain allocation functions.
545     pub fn set_single_target(&mut self) {
546         self.insert(Self::IRQD_SINGLE_TARGET);
547     }
548 
549     pub const fn is_single_target(&self) -> bool {
550         self.contains(Self::IRQD_SINGLE_TARGET)
551     }
552 
553     pub fn set_handle_enforce_irqctx(&mut self) {
554         self.insert(Self::IRQD_HANDLE_ENFORCE_IRQCTX);
555     }
556 
557     pub const fn is_handle_enforce_irqctx(&self) -> bool {
558         self.contains(Self::IRQD_HANDLE_ENFORCE_IRQCTX)
559     }
560 
561     pub const fn is_enabled_on_suspend(&self) -> bool {
562         self.contains(Self::IRQD_IRQ_ENABLED_ON_SUSPEND)
563     }
564 
565     pub const fn is_wakeup_set(&self) -> bool {
566         self.contains(Self::IRQD_WAKEUP_STATE)
567     }
568 
569     pub const fn can_move_in_process_context(&self) -> bool {
570         self.contains(Self::IRQD_MOVE_PCNTXT)
571     }
572 
573     pub const fn is_irq_in_progress(&self) -> bool {
574         self.contains(Self::IRQD_IRQ_INPROGRESS)
575     }
576 
577     pub const fn is_wakeup_armed(&self) -> bool {
578         self.contains(Self::IRQD_WAKEUP_ARMED)
579     }
580 
581     pub const fn is_forwarded_to_vcpu(&self) -> bool {
582         self.contains(Self::IRQD_FORWARDED_TO_VCPU)
583     }
584 
585     pub fn set_forwarded_to_vcpu(&mut self) {
586         self.insert(Self::IRQD_FORWARDED_TO_VCPU);
587     }
588 
589     pub const fn affinity_managed(&self) -> bool {
590         self.contains(Self::IRQD_AFFINITY_MANAGED)
591     }
592 
593     pub const fn is_activated(&self) -> bool {
594         self.contains(Self::IRQD_ACTIVATED)
595     }
596 
597     pub fn set_activated(&mut self) {
598         self.insert(Self::IRQD_ACTIVATED);
599     }
600 
601     pub fn clear_activated(&mut self) {
602         self.remove(Self::IRQD_ACTIVATED);
603     }
604 
605     pub const fn is_started(&self) -> bool {
606         self.contains(Self::IRQD_IRQ_STARTED)
607     }
608 
609     pub const fn is_managed_and_shutdown(&self) -> bool {
610         self.contains(Self::IRQD_MANAGED_SHUTDOWN)
611     }
612 
613     pub fn set_can_reserve(&mut self) {
614         self.insert(Self::IRQD_CAN_RESERVE);
615     }
616 
617     pub const fn can_reserve(&self) -> bool {
618         self.contains(Self::IRQD_CAN_RESERVE)
619     }
620 
621     pub fn clear_can_reserve(&mut self) {
622         self.remove(Self::IRQD_CAN_RESERVE);
623     }
624 
625     pub fn set_msi_nomask_quirk(&mut self) {
626         self.insert(Self::IRQD_MSI_NOMASK_QUIRK);
627     }
628 
629     pub fn clear_msi_nomask_quirk(&mut self) {
630         self.remove(Self::IRQD_MSI_NOMASK_QUIRK);
631     }
632 
633     pub const fn is_msi_nomask_quirk(&self) -> bool {
634         self.contains(Self::IRQD_MSI_NOMASK_QUIRK)
635     }
636 
637     pub fn set_affinity_on_activate(&mut self) {
638         self.insert(Self::IRQD_AFFINITY_ON_ACTIVATE);
639     }
640 
641     pub const fn is_affinity_on_activate(&self) -> bool {
642         self.contains(Self::IRQD_AFFINITY_ON_ACTIVATE)
643     }
644 
645     pub const fn started(&self) -> bool {
646         self.contains(Self::IRQD_IRQ_STARTED)
647     }
648 }
649