1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* interrupt.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! Support for interrupt service routines/interrupt isolation for Oxide.

// Imports ===================================================================
use core::arch::asm;
use avr_oxide::hal::generic::cpu::Cpu;
use avr_oxide::cpu;
use avr_oxide::concurrency::Isolated;

// Declarations ==============================================================
pub mod token {
  /// Token obtained by either the isolated() or isr() methods that
  /// can be used to indicate we are isolated from interrupts
  #[derive(Clone, Copy)]
  pub struct Isolated {
    // This ensures only we can create it, nobody else
    pub(super) _private: ()
  }
}

// Code ======================================================================
/**
 * Execute the given closure without interruptions
 */
pub fn isolated<F,R>(f: F) -> R
where
  F: FnOnce(Isolated) -> R
{
  if cpu!().interrupts_enabled() && !cpu!().in_isr() {
    unsafe {
      disable_interrupts();
      let result = f(Isolated{_private:()});
      enable_interrupts();
      result
    }
  } else {
    f(Isolated{_private:()})
  }
}

/// Execute the given code within the context of an Interrupt Service Routine.
/// Actually, we let the `[oxide_macros::interrupt]` attribute macro generate
/// the preamble/postamble for us, so this closure is just responsible for
/// setting the "I'm in an interrupt" flag and passing an isotoken.
///
/// # Parameters
/// `reschedule`: If true, a thread reschedule will be triggered before returning
#[cfg(target_arch="avr")]
pub(crate) fn isr<F>(reschedule: bool, f: F) -> ()
where
  F: FnOnce(Isolated) -> ()
{
  let isotoken = Isolated{_private:()};

  unsafe {
    // Set the context flag that indicates we're inside an ISR
    core::arch::asm!(
      "sbi {context_flags_reg},{flag_inisr}",
      context_flags_reg = const(avr_oxide::hardware::cpu::cpuregs::IOADR_CONTEXT_FLAGS),
      flag_inisr = const(avr_oxide::hal::generic::cpu::CONTEXT_FLAG_INISR)
    );

    // Call our closure
    f(isotoken);

    // Check that the closure didn't crash the stack
    #[cfg(feature="runtime_checks")]
    avr_oxide::concurrency::stack::kernel::halt_if_stack_invalid();

    // Reschedule if requested
    if reschedule {
      preemptive_reschedule(isotoken);
    }

    // Clear the 'in an interrupt' context flag
    core::arch::asm!(
      "cbi {context_flags_reg},{flag_inisr}",
      context_flags_reg = const(avr_oxide::hardware::cpu::cpuregs::IOADR_CONTEXT_FLAGS),
      flag_inisr = const(avr_oxide::hal::generic::cpu::CONTEXT_FLAG_INISR)
    );
  }
}

#[cfg(target_arch="avr")]
pub(crate) unsafe fn preemptive_reschedule(isotoken: Isolated) {
  avr_oxide::concurrency::scheduler::schedule_next_thread(isotoken);

  // Set the context flag that indicates thread-preemption is happening
  core::arch::asm!(
  "sbi {context_flags_reg},{flag_preemption}",
  context_flags_reg = const(avr_oxide::hardware::cpu::cpuregs::IOADR_CONTEXT_FLAGS),
  flag_preemption = const(avr_oxide::hal::generic::cpu::CONTEXT_FLAG_PREEMPTION)
  );
}

#[cfg(not(target_arch="avr"))]
pub(crate) unsafe fn preemptive_reschedule(isotoken: Isolated) {
  unimplemented!()
}

#[cfg(not(target_arch="avr"))]
pub fn isr<F>(_reschedule: bool, f: F) -> ()
  where
    F: FnOnce(Isolated) -> (),
{
  f(Isolated{_private:()});
}

#[cfg(not(target_arch="avr"))]
pub(crate) unsafe fn enable_interrupts() {
}
#[cfg(not(target_arch="avr"))]
pub(crate) unsafe fn disable_interrupts() {
}


#[cfg(target_arch="avr")]
#[inline(always)]
pub(crate) unsafe fn enable_interrupts(){
  asm!("sei")
}

#[cfg(target_arch="avr")]
#[inline(always)]
pub(crate) unsafe fn disable_interrupts() {
  asm!("cli")
}

#[cfg(not(target_arch="avr"))]
pub fn wait() {
  std::thread::sleep(std::time::Duration::from_millis(100));
}

#[cfg(target_arch="avr")]
#[inline(always)]
pub fn wait() {
  unsafe {
    asm!("sleep")
  }
}
// Tests =====================================================================