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