1use xtensa_lx::interrupt;
4#[cfg(esp32)]
5pub(crate) use xtensa_lx::interrupt::free;
6use xtensa_lx_rt::exception::Context;
7
8pub use self::vectored::*;
9use super::InterruptStatus;
10use crate::{pac, peripherals::Interrupt, system::Cpu};
11
12#[derive(Copy, Clone, Debug, PartialEq, Eq)]
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15pub enum Error {
16 InvalidInterrupt,
18 CpuInterruptReserved,
20}
21
22#[derive(Debug, Copy, Clone)]
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28#[repr(u32)]
29pub enum CpuInterrupt {
30 Interrupt0LevelPriority1 = 0,
32 Interrupt1LevelPriority1,
34 Interrupt2LevelPriority1,
36 Interrupt3LevelPriority1,
38 Interrupt4LevelPriority1,
40 Interrupt5LevelPriority1,
42 Interrupt6Timer0Priority1,
44 Interrupt7SoftwarePriority1,
46 Interrupt8LevelPriority1,
48 Interrupt9LevelPriority1,
50 Interrupt10EdgePriority1,
52 Interrupt11ProfilingPriority3,
54 Interrupt12LevelPriority1,
56 Interrupt13LevelPriority1,
58 Interrupt14NmiPriority7,
60 Interrupt15Timer1Priority3,
62 Interrupt16Timer2Priority5,
64 Interrupt17LevelPriority1,
66 Interrupt18LevelPriority1,
68 Interrupt19LevelPriority2,
70 Interrupt20LevelPriority2,
72 Interrupt21LevelPriority2,
74 Interrupt22EdgePriority3,
76 Interrupt23LevelPriority3,
78 Interrupt24LevelPriority4,
80 Interrupt25LevelPriority4,
82 Interrupt26LevelPriority5,
84 Interrupt27LevelPriority3,
86 Interrupt28EdgePriority4,
88 Interrupt29SoftwarePriority3,
90 Interrupt30EdgePriority4,
92 Interrupt31EdgePriority5,
94}
95
96impl CpuInterrupt {
97 fn from_u32(n: u32) -> Option<Self> {
98 if n < 32 {
99 Some(unsafe { core::mem::transmute::<u32, Self>(n) })
100 } else {
101 None
102 }
103 }
104
105 fn is_internal(self) -> bool {
106 matches!(
107 self,
108 Self::Interrupt6Timer0Priority1
109 | Self::Interrupt7SoftwarePriority1
110 | Self::Interrupt11ProfilingPriority3
111 | Self::Interrupt15Timer1Priority3
112 | Self::Interrupt16Timer2Priority5
113 | Self::Interrupt29SoftwarePriority3
114 )
115 }
116
117 fn is_peripheral(self) -> bool {
118 !self.is_internal()
119 }
120}
121
122#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
124pub static RESERVED_INTERRUPTS: &[usize] = &[
125 CpuInterrupt::Interrupt1LevelPriority1 as _,
126 CpuInterrupt::Interrupt19LevelPriority2 as _,
127 CpuInterrupt::Interrupt23LevelPriority3 as _,
128 CpuInterrupt::Interrupt10EdgePriority1 as _,
129 CpuInterrupt::Interrupt22EdgePriority3 as _,
130];
131
132pub(crate) fn setup_interrupts() {
133 for peripheral_interrupt in 0..255 {
137 Interrupt::try_from(peripheral_interrupt)
138 .map(|intr| {
139 #[cfg(multi_core)]
140 disable(Cpu::AppCpu, intr);
141 disable(Cpu::ProCpu, intr);
142 })
143 .ok();
144 }
145}
146
147pub fn enable_direct(interrupt: Interrupt, cpu_interrupt: CpuInterrupt) -> Result<(), Error> {
154 if RESERVED_INTERRUPTS.contains(&(cpu_interrupt as _)) {
155 return Err(Error::CpuInterruptReserved);
156 }
157 unsafe {
158 map(Cpu::current(), interrupt, cpu_interrupt);
159
160 xtensa_lx::interrupt::enable_mask(
161 xtensa_lx::interrupt::get_mask() | (1 << cpu_interrupt as u32),
162 );
163 }
164 Ok(())
165}
166
167pub unsafe fn map(core: Cpu, interrupt: Interrupt, which: CpuInterrupt) {
176 let interrupt_number = interrupt as isize;
177 let cpu_interrupt_number = which as isize;
178 unsafe {
179 let intr_map_base = match core {
180 Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr(),
181 #[cfg(multi_core)]
182 Cpu::AppCpu => (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr(),
183 };
184 intr_map_base
185 .offset(interrupt_number)
186 .write_volatile(cpu_interrupt_number as u32);
187 }
188}
189
190pub(crate) fn bound_cpu_interrupt_for(cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
192 let interrupt_number = interrupt as isize;
193
194 let intr_map_base = match cpu {
195 Cpu::ProCpu => unsafe { (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr() },
196 #[cfg(multi_core)]
197 Cpu::AppCpu => unsafe { (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr() },
198 };
199 let cpu_intr = unsafe { intr_map_base.offset(interrupt_number).read_volatile() };
200 let cpu_intr = CpuInterrupt::from_u32(cpu_intr)?;
201
202 if cpu_intr.is_peripheral() {
203 Some(cpu_intr)
204 } else {
205 None
206 }
207}
208
209pub fn disable(core: Cpu, interrupt: Interrupt) {
211 unsafe {
212 let interrupt_number = interrupt as isize;
213 let intr_map_base = match core {
214 Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr(),
215 #[cfg(multi_core)]
216 Cpu::AppCpu => (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr(),
217 };
218 intr_map_base
220 .offset(interrupt_number)
221 .write_volatile(CpuInterrupt::Interrupt16Timer2Priority5 as _);
222 }
223}
224
225pub fn clear(_core: Cpu, which: CpuInterrupt) {
227 unsafe {
228 xtensa_lx::interrupt::clear(1 << which as u32);
229 }
230}
231
232#[cfg(large_intr_status)]
234pub fn status(core: Cpu) -> InterruptStatus {
235 unsafe {
236 match core {
237 Cpu::ProCpu => InterruptStatus::from(
238 (*core0_interrupt_peripheral())
239 .pro_intr_status_0()
240 .read()
241 .bits(),
242 (*core0_interrupt_peripheral())
243 .pro_intr_status_1()
244 .read()
245 .bits(),
246 (*core0_interrupt_peripheral())
247 .pro_intr_status_2()
248 .read()
249 .bits(),
250 ),
251 #[cfg(multi_core)]
252 Cpu::AppCpu => InterruptStatus::from(
253 (*core1_interrupt_peripheral())
254 .app_intr_status_0()
255 .read()
256 .bits(),
257 (*core1_interrupt_peripheral())
258 .app_intr_status_1()
259 .read()
260 .bits(),
261 (*core1_interrupt_peripheral())
262 .app_intr_status_2()
263 .read()
264 .bits(),
265 ),
266 }
267 }
268}
269
270#[cfg(very_large_intr_status)]
272pub fn status(core: Cpu) -> InterruptStatus {
273 unsafe {
274 match core {
275 Cpu::ProCpu => InterruptStatus::from(
276 (*core0_interrupt_peripheral())
277 .pro_intr_status_0()
278 .read()
279 .bits(),
280 (*core0_interrupt_peripheral())
281 .pro_intr_status_1()
282 .read()
283 .bits(),
284 (*core0_interrupt_peripheral())
285 .pro_intr_status_2()
286 .read()
287 .bits(),
288 (*core0_interrupt_peripheral())
289 .pro_intr_status_3()
290 .read()
291 .bits(),
292 ),
293 #[cfg(multi_core)]
294 Cpu::AppCpu => InterruptStatus::from(
295 (*core1_interrupt_peripheral())
296 .app_intr_status_0()
297 .read()
298 .bits(),
299 (*core1_interrupt_peripheral())
300 .app_intr_status_1()
301 .read()
302 .bits(),
303 (*core1_interrupt_peripheral())
304 .app_intr_status_2()
305 .read()
306 .bits(),
307 (*core1_interrupt_peripheral())
308 .app_intr_status_3()
309 .read()
310 .bits(),
311 ),
312 }
313 }
314}
315
316#[cfg(esp32)]
317unsafe fn core0_interrupt_peripheral() -> *const crate::pac::dport::RegisterBlock {
318 pac::DPORT::PTR
319}
320
321#[cfg(esp32)]
322unsafe fn core1_interrupt_peripheral() -> *const crate::pac::dport::RegisterBlock {
323 pac::DPORT::PTR
324}
325
326#[cfg(any(esp32s2, esp32s3))]
327unsafe fn core0_interrupt_peripheral() -> *const crate::pac::interrupt_core0::RegisterBlock {
328 pac::INTERRUPT_CORE0::PTR
329}
330
331#[cfg(esp32s3)]
332unsafe fn core1_interrupt_peripheral() -> *const crate::pac::interrupt_core1::RegisterBlock {
333 pac::INTERRUPT_CORE1::PTR
334}
335
336pub fn current_runlevel() -> Priority {
338 let ps: u32;
339 unsafe { core::arch::asm!("rsr.ps {0}", out(reg) ps) };
340
341 let prev_interrupt_priority = ps as u8 & 0x0F;
342
343 unwrap!(Priority::try_from(prev_interrupt_priority))
344}
345
346pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
355 let token: u32;
356 unsafe {
357 match level {
358 Priority::None => core::arch::asm!("rsil {0}, 0", out(reg) token),
359 Priority::Priority1 => core::arch::asm!("rsil {0}, 1", out(reg) token),
360 Priority::Priority2 => core::arch::asm!("rsil {0}, 2", out(reg) token),
361 Priority::Priority3 => core::arch::asm!("rsil {0}, 3", out(reg) token),
362 };
363 }
364
365 let prev_interrupt_priority = token as u8 & 0x0F;
366
367 unwrap!(Priority::try_from(prev_interrupt_priority))
368}
369
370mod vectored {
371 use procmacros::ram;
372
373 use super::*;
374
375 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
377 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
378 #[repr(u8)]
379 pub enum Priority {
380 None = 0,
382 Priority1,
384 Priority2,
386 Priority3,
388 }
389
390 impl Priority {
391 pub const fn max() -> Priority {
393 Priority::Priority3
394 }
395
396 pub const fn min() -> Priority {
398 Priority::Priority1
399 }
400 }
401
402 impl TryFrom<u32> for Priority {
403 type Error = Error;
404
405 fn try_from(value: u32) -> Result<Self, Self::Error> {
406 match value {
407 0 => Ok(Priority::None),
408 1 => Ok(Priority::Priority1),
409 2 => Ok(Priority::Priority2),
410 3 => Ok(Priority::Priority3),
411 _ => Err(Error::InvalidInterrupt),
412 }
413 }
414 }
415
416 impl TryFrom<u8> for Priority {
417 type Error = Error;
418
419 fn try_from(value: u8) -> Result<Self, Self::Error> {
420 Self::try_from(value as u32)
421 }
422 }
423
424 impl CpuInterrupt {
425 #[inline]
426 fn level(&self) -> Priority {
427 match self {
428 CpuInterrupt::Interrupt0LevelPriority1
429 | CpuInterrupt::Interrupt1LevelPriority1
430 | CpuInterrupt::Interrupt2LevelPriority1
431 | CpuInterrupt::Interrupt3LevelPriority1
432 | CpuInterrupt::Interrupt4LevelPriority1
433 | CpuInterrupt::Interrupt5LevelPriority1
434 | CpuInterrupt::Interrupt6Timer0Priority1
435 | CpuInterrupt::Interrupt7SoftwarePriority1
436 | CpuInterrupt::Interrupt8LevelPriority1
437 | CpuInterrupt::Interrupt9LevelPriority1
438 | CpuInterrupt::Interrupt10EdgePriority1
439 | CpuInterrupt::Interrupt12LevelPriority1
440 | CpuInterrupt::Interrupt13LevelPriority1
441 | CpuInterrupt::Interrupt17LevelPriority1
442 | CpuInterrupt::Interrupt18LevelPriority1 => Priority::Priority1,
443
444 CpuInterrupt::Interrupt19LevelPriority2
445 | CpuInterrupt::Interrupt20LevelPriority2
446 | CpuInterrupt::Interrupt21LevelPriority2 => Priority::Priority2,
447
448 CpuInterrupt::Interrupt11ProfilingPriority3
449 | CpuInterrupt::Interrupt15Timer1Priority3
450 | CpuInterrupt::Interrupt22EdgePriority3
451 | CpuInterrupt::Interrupt27LevelPriority3
452 | CpuInterrupt::Interrupt29SoftwarePriority3
453 | CpuInterrupt::Interrupt23LevelPriority3 => Priority::Priority3,
454
455 CpuInterrupt::Interrupt24LevelPriority4
458 | CpuInterrupt::Interrupt25LevelPriority4
459 | CpuInterrupt::Interrupt28EdgePriority4
460 | CpuInterrupt::Interrupt30EdgePriority4
461 | CpuInterrupt::Interrupt31EdgePriority5
462 | CpuInterrupt::Interrupt16Timer2Priority5
463 | CpuInterrupt::Interrupt26LevelPriority5
464 | CpuInterrupt::Interrupt14NmiPriority7 => Priority::None,
465 }
466 }
467 }
468
469 #[inline(always)]
471 fn configured_interrupts(core: Cpu, status: InterruptStatus, level: u32) -> InterruptStatus {
472 unsafe {
473 let intr_map_base = match core {
474 Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr(),
475 #[cfg(multi_core)]
476 Cpu::AppCpu => (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr(),
477 };
478
479 let mut res = InterruptStatus::empty();
480
481 for interrupt_nr in status.iterator() {
482 let i = interrupt_nr as isize;
483 let cpu_interrupt = intr_map_base.offset(i).read_volatile();
484 let cpu_interrupt: CpuInterrupt =
486 core::mem::transmute::<u32, CpuInterrupt>(cpu_interrupt);
487 let int_level = cpu_interrupt.level() as u8 as u32;
488
489 if int_level == level {
490 res.set(interrupt_nr);
491 }
492 }
493
494 res
495 }
496 }
497
498 pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
500 enable_on_cpu(Cpu::current(), interrupt, level)
501 }
502
503 pub(crate) fn enable_on_cpu(
504 cpu: Cpu,
505 interrupt: Interrupt,
506 level: Priority,
507 ) -> Result<(), Error> {
508 let cpu_interrupt =
509 interrupt_level_to_cpu_interrupt(level, chip_specific::interrupt_is_edge(interrupt))?;
510
511 unsafe {
512 map(cpu, interrupt, cpu_interrupt);
513
514 xtensa_lx::interrupt::enable_mask(
515 xtensa_lx::interrupt::get_mask() | (1 << cpu_interrupt as u32),
516 );
517 }
518 Ok(())
519 }
520
521 pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn()) {
527 let ptr = unsafe {
528 &pac::__INTERRUPTS[interrupt as usize]._handler as *const _
529 as *mut unsafe extern "C" fn()
530 };
531 unsafe {
532 ptr.write_volatile(handler);
533 }
534 }
535
536 pub fn bound_handler(interrupt: Interrupt) -> Option<unsafe extern "C" fn()> {
538 unsafe {
539 let addr = pac::__INTERRUPTS[interrupt as usize]._handler;
540 if addr as usize == 0 {
541 return None;
542 }
543 Some(addr)
544 }
545 }
546
547 fn interrupt_level_to_cpu_interrupt(
548 level: Priority,
549 is_edge: bool,
550 ) -> Result<CpuInterrupt, Error> {
551 Ok(if is_edge {
552 match level {
553 Priority::None => return Err(Error::InvalidInterrupt),
554 Priority::Priority1 => CpuInterrupt::Interrupt10EdgePriority1,
555 Priority::Priority2 => return Err(Error::InvalidInterrupt),
556 Priority::Priority3 => CpuInterrupt::Interrupt22EdgePriority3,
557 }
558 } else {
559 match level {
560 Priority::None => return Err(Error::InvalidInterrupt),
561 Priority::Priority1 => CpuInterrupt::Interrupt1LevelPriority1,
562 Priority::Priority2 => CpuInterrupt::Interrupt19LevelPriority2,
563 Priority::Priority3 => CpuInterrupt::Interrupt23LevelPriority3,
564 }
565 })
566 }
567
568 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
570 static CPU_INTERRUPT_LEVELS: [u32; 8] = [
571 0b_0000_0000_0000_0000_0000_0000_0000_0000, 0b_0000_0000_0000_0110_0011_0111_1111_1111, 0b_0000_0000_0011_1000_0000_0000_0000_0000, 0b_0010_1000_1100_0000_1000_1000_0000_0000, 0b_0101_0011_0000_0000_0000_0000_0000_0000, 0b_1000_0100_0000_0001_0000_0000_0000_0000, 0b_0000_0000_0000_0000_0000_0000_0000_0000, 0b_0000_0000_0000_0000_0100_0000_0000_0000, ];
580 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
581 static CPU_INTERRUPT_INTERNAL: u32 = 0b_0010_0000_0000_0001_1000_1000_1100_0000;
582 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
583 static CPU_INTERRUPT_EDGE: u32 = 0b_0111_0000_0100_0000_0000_1100_1000_0000;
584
585 #[inline]
586 fn cpu_interrupt_nr_to_cpu_interrupt_handler(
587 number: u32,
588 ) -> Option<unsafe extern "C" fn(save_frame: &mut Context)> {
589 use xtensa_lx_rt::*;
590 Some(match number {
592 6 => Timer0,
593 7 => Software0,
594 11 => Profiling,
595 14 => NMI,
596 15 => Timer1,
597 16 => Timer2,
598 29 => Software1,
599 _ => return None,
600 })
601 }
602
603 #[unsafe(no_mangle)]
604 #[ram]
605 unsafe fn __level_1_interrupt(save_frame: &mut Context) {
606 unsafe {
607 handle_interrupts::<1>(save_frame);
608 }
609 }
610
611 #[unsafe(no_mangle)]
612 #[ram]
613 unsafe fn __level_2_interrupt(save_frame: &mut Context) {
614 unsafe {
615 handle_interrupts::<2>(save_frame);
616 }
617 }
618
619 #[unsafe(no_mangle)]
620 #[ram]
621 unsafe fn __level_3_interrupt(save_frame: &mut Context) {
622 unsafe {
623 handle_interrupts::<3>(save_frame);
624 }
625 }
626
627 #[inline(always)]
628 unsafe fn handle_interrupts<const LEVEL: u32>(save_frame: &mut Context) {
629 let core = Cpu::current();
630
631 let cpu_interrupt_mask =
632 interrupt::get() & interrupt::get_mask() & CPU_INTERRUPT_LEVELS[LEVEL as usize];
633
634 if cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL != 0 {
635 let cpu_interrupt_mask = cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL;
640
641 let cpu_interrupt_nr = cpu_interrupt_mask.trailing_zeros();
643
644 if ((1 << cpu_interrupt_nr) & CPU_INTERRUPT_EDGE) != 0 {
647 unsafe {
648 interrupt::clear(1 << cpu_interrupt_nr);
649 }
650 }
651
652 if let Some(handler) = cpu_interrupt_nr_to_cpu_interrupt_handler(cpu_interrupt_nr) {
653 unsafe { handler(save_frame) };
654 }
655 } else {
656 let status = if !cfg!(esp32s3) && (cpu_interrupt_mask & CPU_INTERRUPT_EDGE) != 0 {
657 unsafe { interrupt::clear(cpu_interrupt_mask & CPU_INTERRUPT_EDGE) };
663
664 chip_specific::INTERRUPT_EDGE
667 } else {
668 status(core)
671 };
672
673 let configured_interrupts = configured_interrupts(core, status, LEVEL);
674 for interrupt_nr in configured_interrupts.iterator() {
675 let interrupt: Interrupt = unsafe { core::mem::transmute(interrupt_nr as u16) };
677
678 unsafe extern "C" {
679 fn EspDefaultHandler(interrupt: Interrupt);
681 }
682
683 let handler = unsafe { pac::__INTERRUPTS[interrupt as usize]._handler };
684 if core::ptr::eq(
685 handler as *const _,
686 EspDefaultHandler as *const unsafe extern "C" fn(),
687 ) {
688 unsafe { EspDefaultHandler(interrupt) };
689 } else {
690 let handler: fn(&mut Context) = unsafe {
691 core::mem::transmute::<unsafe extern "C" fn(), fn(&mut Context)>(handler)
692 };
693 handler(save_frame);
694 }
695 }
696 }
697 }
698
699 #[cfg(esp32)]
700 mod chip_specific {
701 use super::*;
702 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
703 pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
704 0b0000_0000_0000_0000_0000_0000_0000_0000,
705 0b1111_1100_0000_0000_0000_0000_0000_0000,
706 0b0000_0000_0000_0000_0000_0000_0000_0011,
707 );
708 #[inline]
709 pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
710 [
711 Interrupt::TG0_T0_EDGE,
712 Interrupt::TG0_T1_EDGE,
713 Interrupt::TG0_WDT_EDGE,
714 Interrupt::TG0_LACT_EDGE,
715 Interrupt::TG1_T0_EDGE,
716 Interrupt::TG1_T1_EDGE,
717 Interrupt::TG1_WDT_EDGE,
718 Interrupt::TG1_LACT_EDGE,
719 ]
720 .contains(&interrupt)
721 }
722 }
723
724 #[cfg(esp32s2)]
725 mod chip_specific {
726 use super::*;
727 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
728 pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
729 0b0000_0000_0000_0000_0000_0000_0000_0000,
730 0b1100_0000_0000_0000_0000_0000_0000_0000,
731 0b0000_0000_0000_0000_0000_0011_1011_1111,
732 );
733 #[inline]
734 pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
735 [
736 Interrupt::TG0_T0_EDGE,
737 Interrupt::TG0_T1_EDGE,
738 Interrupt::TG0_WDT_EDGE,
739 Interrupt::TG0_LACT_EDGE,
740 Interrupt::TG1_T0_EDGE,
741 Interrupt::TG1_T1_EDGE,
742 Interrupt::TG1_WDT_EDGE,
743 Interrupt::TG1_LACT_EDGE,
744 Interrupt::SYSTIMER_TARGET0,
745 Interrupt::SYSTIMER_TARGET1,
746 Interrupt::SYSTIMER_TARGET2,
747 ]
748 .contains(&interrupt)
749 }
750 }
751
752 #[cfg(esp32s3)]
753 mod chip_specific {
754 use super::*;
755 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
756 pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::empty();
757 #[inline]
758 pub fn interrupt_is_edge(_interrupt: Interrupt) -> bool {
759 false
760 }
761 }
762}
763
764mod raw {
765 use super::*;
766
767 unsafe extern "C" {
768 fn level4_interrupt(save_frame: &mut Context);
769 fn level5_interrupt(save_frame: &mut Context);
770 fn level6_interrupt(save_frame: &mut Context);
771 fn level7_interrupt(save_frame: &mut Context);
772 }
773
774 #[unsafe(no_mangle)]
775 #[unsafe(link_section = ".rwtext")]
776 unsafe fn __level_4_interrupt(save_frame: &mut Context) {
777 unsafe { level4_interrupt(save_frame) }
778 }
779
780 #[unsafe(no_mangle)]
781 #[unsafe(link_section = ".rwtext")]
782 unsafe fn __level_5_interrupt(save_frame: &mut Context) {
783 unsafe { level5_interrupt(save_frame) }
784 }
785
786 #[unsafe(no_mangle)]
787 #[unsafe(link_section = ".rwtext")]
788 unsafe fn __level_6_interrupt(save_frame: &mut Context) {
789 unsafe { level6_interrupt(save_frame) }
790 }
791
792 #[unsafe(no_mangle)]
793 #[unsafe(link_section = ".rwtext")]
794 unsafe fn __level_7_interrupt(save_frame: &mut Context) {
795 unsafe { level7_interrupt(save_frame) }
796 }
797}