xref: /DragonOS/kernel/src/exception/irqdata.rs (revision c3dc6f2ff9169c309d1cbf47dcb9e4528d509b2f)
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     pub struct IrqLineStatus: u32 {
335         /// 默认,未指明类型
336         const IRQ_TYPE_NONE     = 0x00000000;
337         /// 上升沿触发
338         const IRQ_TYPE_EDGE_RISING  = 0x00000001;
339         /// 下降沿触发
340         const IRQ_TYPE_EDGE_FALLING = 0x00000002;
341         /// 上升沿和下降沿触发
342         const IRQ_TYPE_EDGE_BOTH    = Self::IRQ_TYPE_EDGE_RISING.bits | Self::IRQ_TYPE_EDGE_FALLING.bits;
343         /// 高电平触发
344         const IRQ_TYPE_LEVEL_HIGH   = 0x00000004;
345         /// 低电平触发
346         const IRQ_TYPE_LEVEL_LOW    = 0x00000008;
347         /// 过滤掉电平位的掩码
348         const IRQ_TYPE_LEVEL_MASK   = Self::IRQ_TYPE_LEVEL_LOW.bits | Self::IRQ_TYPE_LEVEL_HIGH.bits;
349         /// 上述位掩码的掩码
350         const IRQ_TYPE_SENSE_MASK   = 0x0000000f;
351         /// 某些PICs使用此类型要求 `IrqChip::irq_set_type()` 设置硬件到一个合理的默认值
352         /// (由irqdomain的map()回调使用,以便为新分配的描述符同步硬件状态和软件标志位)。
353         const IRQ_TYPE_DEFAULT      = Self::IRQ_TYPE_SENSE_MASK.bits;
354 
355         /// 特定于探测的过程中的特殊标志
356         const IRQ_TYPE_PROBE        = 0x00000010;
357 
358         /// 中断是电平类型。当上述触发位通过`IrqChip::irq_set_type()` 修改时,也会在代码中更新
359         const IRQ_LEVEL     = 1 << 8;
360         /// 标记一个PER_CPU的中断。将保护其免受亲和性设置的影响
361         const IRQ_PER_CPU       = 1 << 9;
362         /// 中断不能被自动探测
363         const IRQ_NOPROBE       = 1 << 10;
364         /// 中断不能通过request_irq()请求
365         const IRQ_NOREQUEST     = 1 << 11;
366         /// 中断在request/setup_irq()中不会自动启用
367         const IRQ_NOAUTOEN      = 1 << 12;
368         /// 中断不能被平衡(亲和性设置)
369         const IRQ_NO_BALANCING      = 1 << 13;
370         /// 中断可以从进程上下文中迁移
371         const IRQ_MOVE_PCNTXT       = 1 << 14;
372         /// 中断嵌套在另一个线程中
373         const IRQ_NESTED_THREAD = 1 << 15;
374         /// 中断不能被线程化
375         const IRQ_NOTHREAD      = 1 << 16;
376         /// Dev_id是一个per-CPU变量
377         const IRQ_PER_CPU_DEVID = 1 << 17;
378         /// 总是由另一个中断轮询。将其从错误的中断检测机制和核心侧轮询中排除
379         const IRQ_IS_POLLED     = 1 << 18;
380         /// 禁用延迟的中断禁用 (Disable lazy irq disable)
381         const IRQ_DISABLE_UNLAZY    = 1 << 19;
382         /// 在/proc/interrupts中不显示
383         const IRQ_HIDDEN        = 1 << 20;
384         /// 从note_interrupt()调试中排除
385         const IRQ_NO_DEBUG      = 1 << 21;
386     }
387 
388 
389 
390 }
391 
392 impl IrqLineStatus {
393     pub const fn trigger_bits(&self) -> u32 {
394         self.bits & Self::IRQ_TYPE_SENSE_MASK.bits
395     }
396 
397     pub fn trigger_type(&self) -> Self {
398         *self & Self::IRQ_TYPE_SENSE_MASK
399     }
400 
401     pub fn is_level_type(&self) -> bool {
402         self.contains(Self::IRQ_LEVEL)
403     }
404 
405     /// 是否为高电平触发
406     ///
407     /// ## 返回
408     ///
409     /// - 如果不是电平触发类型,则返回None
410     /// - 如果是电平触发类型,则返回Some(bool),当为true时表示高电平触发
411     pub fn is_level_high(&self) -> Option<bool> {
412         if !self.is_level_type() {
413             return None;
414         }
415         return Some(self.contains(Self::IRQ_TYPE_LEVEL_HIGH));
416     }
417 
418     #[allow(dead_code)]
419     pub fn is_per_cpu_devid(&self) -> bool {
420         self.contains(Self::IRQ_PER_CPU_DEVID)
421     }
422 }
423 bitflags! {
424     /// 中断状态(存储在IrqCommonData)
425     ///
426     /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h#227
427     pub struct IrqStatus: u32 {
428         const IRQD_TRIGGER_NONE = IrqLineStatus::IRQ_TYPE_NONE.bits();
429         const IRQD_TRIGGER_RISING = IrqLineStatus::IRQ_TYPE_EDGE_RISING.bits();
430         const IRQD_TRIGGER_FALLING = IrqLineStatus::IRQ_TYPE_EDGE_FALLING.bits();
431         const IRQD_TRIGGER_HIGH = IrqLineStatus::IRQ_TYPE_LEVEL_HIGH.bits();
432         const IRQD_TRIGGER_LOW = IrqLineStatus::IRQ_TYPE_LEVEL_LOW.bits();
433 
434         /// 触发类型位的掩码
435         const IRQD_TRIGGER_MASK = 0xf;
436         /// 亲和性设置待处理
437         const IRQD_SETAFFINITY_PENDING = 1 << 8;
438         /// 中断已激活
439         const IRQD_ACTIVATED = 1 << 9;
440         /// 对此IRQ禁用平衡
441         const IRQD_NO_BALANCING = 1 << 10;
442         /// 中断是每个CPU特定的
443         const IRQD_PER_CPU = 1 << 11;
444         /// 中断亲和性已设置
445         const IRQD_AFFINITY_SET = 1 << 12;
446         /// 中断是电平触发
447         const IRQD_LEVEL = 1 << 13;
448         /// 中断配置为从挂起状态唤醒
449         const IRQD_WAKEUP_STATE = 1 << 14;
450         /// 中断可以在进程上下文中移动
451         const IRQD_MOVE_PCNTXT = 1 << 15;
452         /// 中断被禁用
453         const IRQD_IRQ_DISABLED = 1 << 16;
454         /// 中断被屏蔽
455         const IRQD_IRQ_MASKED = 1 << 17;
456         /// 中断正在处理中
457         const IRQD_IRQ_INPROGRESS = 1 << 18;
458         /// 唤醒模式已准备就绪
459         const IRQD_WAKEUP_ARMED = 1 << 19;
460         /// 中断被转发到一个虚拟CPU
461         const IRQD_FORWARDED_TO_VCPU = 1 << 20;
462         /// 亲和性由内核自动管理
463         const IRQD_AFFINITY_MANAGED = 1 << 21;
464         /// 中断已启动
465         const IRQD_IRQ_STARTED = 1 << 22;
466         /// 由于空亲和性掩码而关闭的中断。仅适用于亲和性管理的中断。
467         const IRQD_MANAGED_SHUTDOWN = 1 << 23;
468         /// IRQ只允许单个亲和性目标
469         const IRQD_SINGLE_TARGET = 1 << 24;
470         /// 默认的触发器已设置
471         const IRQD_DEFAULT_TRIGGER_SET = 1 << 25;
472         /// 可以使用保留模式
473         const IRQD_CAN_RESERVE = 1 << 26;
474         /// Non-maskable MSI quirk for affinity change required
475         const IRQD_MSI_NOMASK_QUIRK = 1 << 27;
476         /// 强制要求`handle_irq_()`只能在真实的中断上下文中调用
477         const IRQD_HANDLE_ENFORCE_IRQCTX = 1 << 28;
478         /// 激活时设置亲和性。在禁用时不要调用irq_chip::irq_set_affinity()。
479         const IRQD_AFFINITY_ON_ACTIVATE = 1 << 29;
480         /// 如果irqpm具有标志 IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND,则在挂起时中断被启用。
481         const IRQD_IRQ_ENABLED_ON_SUSPEND = 1 << 30;
482     }
483 }
484 
485 #[allow(dead_code)]
486 impl IrqStatus {
487     pub const fn is_set_affinity_pending(&self) -> bool {
488         self.contains(Self::IRQD_SETAFFINITY_PENDING)
489     }
490 
491     pub const fn is_per_cpu(&self) -> bool {
492         self.contains(Self::IRQD_PER_CPU)
493     }
494 
495     pub const fn can_balance(&self) -> bool {
496         !((self.bits & (Self::IRQD_PER_CPU.bits | Self::IRQD_NO_BALANCING.bits)) != 0)
497     }
498 
499     pub const fn affinity_was_set(&self) -> bool {
500         self.contains(Self::IRQD_AFFINITY_SET)
501     }
502 
503     pub fn masked(&self) -> bool {
504         self.contains(Self::IRQD_IRQ_MASKED)
505     }
506 
507     pub fn disabled(&self) -> bool {
508         self.contains(Self::IRQD_IRQ_DISABLED)
509     }
510 
511     pub fn mark_affinity_set(&mut self) {
512         self.insert(Self::IRQD_AFFINITY_SET);
513     }
514 
515     pub const fn trigger_type_was_set(&self) -> bool {
516         self.contains(Self::IRQD_DEFAULT_TRIGGER_SET)
517     }
518 
519     pub fn mark_trigger_type_set(&mut self) {
520         self.insert(Self::IRQD_DEFAULT_TRIGGER_SET);
521     }
522 
523     pub const fn trigger_type(&self) -> IrqLineStatus {
524         IrqLineStatus::from_bits_truncate(self.bits & Self::IRQD_TRIGGER_MASK.bits)
525     }
526 
527     /// Must only be called inside irq_chip.irq_set_type() functions or
528     /// from the DT/ACPI setup code.
529     pub const fn set_trigger_type(&mut self, trigger: IrqLineStatus) {
530         self.bits &= !Self::IRQD_TRIGGER_MASK.bits;
531         self.bits |= trigger.bits & Self::IRQD_TRIGGER_MASK.bits;
532 
533         self.bits |= Self::IRQD_DEFAULT_TRIGGER_SET.bits;
534     }
535 
536     pub const fn is_level_type(&self) -> bool {
537         self.contains(Self::IRQD_LEVEL)
538     }
539 
540     /// Must only be called of irqchip.irq_set_affinity() or low level
541     /// hierarchy domain allocation functions.
542     pub fn set_single_target(&mut self) {
543         self.insert(Self::IRQD_SINGLE_TARGET);
544     }
545 
546     pub const fn is_single_target(&self) -> bool {
547         self.contains(Self::IRQD_SINGLE_TARGET)
548     }
549 
550     pub fn set_handle_enforce_irqctx(&mut self) {
551         self.insert(Self::IRQD_HANDLE_ENFORCE_IRQCTX);
552     }
553 
554     pub const fn is_handle_enforce_irqctx(&self) -> bool {
555         self.contains(Self::IRQD_HANDLE_ENFORCE_IRQCTX)
556     }
557 
558     pub const fn is_enabled_on_suspend(&self) -> bool {
559         self.contains(Self::IRQD_IRQ_ENABLED_ON_SUSPEND)
560     }
561 
562     pub const fn is_wakeup_set(&self) -> bool {
563         self.contains(Self::IRQD_WAKEUP_STATE)
564     }
565 
566     pub const fn can_move_in_process_context(&self) -> bool {
567         self.contains(Self::IRQD_MOVE_PCNTXT)
568     }
569 
570     pub const fn is_irq_in_progress(&self) -> bool {
571         self.contains(Self::IRQD_IRQ_INPROGRESS)
572     }
573 
574     pub const fn is_wakeup_armed(&self) -> bool {
575         self.contains(Self::IRQD_WAKEUP_ARMED)
576     }
577 
578     pub const fn is_forwarded_to_vcpu(&self) -> bool {
579         self.contains(Self::IRQD_FORWARDED_TO_VCPU)
580     }
581 
582     pub fn set_forwarded_to_vcpu(&mut self) {
583         self.insert(Self::IRQD_FORWARDED_TO_VCPU);
584     }
585 
586     pub const fn affinity_managed(&self) -> bool {
587         self.contains(Self::IRQD_AFFINITY_MANAGED)
588     }
589 
590     pub const fn is_activated(&self) -> bool {
591         self.contains(Self::IRQD_ACTIVATED)
592     }
593 
594     pub fn set_activated(&mut self) {
595         self.insert(Self::IRQD_ACTIVATED);
596     }
597 
598     pub fn clear_activated(&mut self) {
599         self.remove(Self::IRQD_ACTIVATED);
600     }
601 
602     pub const fn is_started(&self) -> bool {
603         self.contains(Self::IRQD_IRQ_STARTED)
604     }
605 
606     pub const fn is_managed_and_shutdown(&self) -> bool {
607         self.contains(Self::IRQD_MANAGED_SHUTDOWN)
608     }
609 
610     pub fn set_can_reserve(&mut self) {
611         self.insert(Self::IRQD_CAN_RESERVE);
612     }
613 
614     pub const fn can_reserve(&self) -> bool {
615         self.contains(Self::IRQD_CAN_RESERVE)
616     }
617 
618     pub fn clear_can_reserve(&mut self) {
619         self.remove(Self::IRQD_CAN_RESERVE);
620     }
621 
622     pub fn set_msi_nomask_quirk(&mut self) {
623         self.insert(Self::IRQD_MSI_NOMASK_QUIRK);
624     }
625 
626     pub fn clear_msi_nomask_quirk(&mut self) {
627         self.remove(Self::IRQD_MSI_NOMASK_QUIRK);
628     }
629 
630     pub const fn is_msi_nomask_quirk(&self) -> bool {
631         self.contains(Self::IRQD_MSI_NOMASK_QUIRK)
632     }
633 
634     pub fn set_affinity_on_activate(&mut self) {
635         self.insert(Self::IRQD_AFFINITY_ON_ACTIVATE);
636     }
637 
638     pub const fn is_affinity_on_activate(&self) -> bool {
639         self.contains(Self::IRQD_AFFINITY_ON_ACTIVATE)
640     }
641 
642     pub const fn started(&self) -> bool {
643         self.contains(Self::IRQD_IRQ_STARTED)
644     }
645 }
646