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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
/* oserror
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! A way of communicating fatal OS errors that doesn't have all the
//! baggage that Rust loads Panic with, but still allows for useful debugging.
//!
//! You can denote a pin (typically an error LED or something similar that
//! you can hook up a meter to), which will be used to signal error
//! conditions as a series of pulses.
//!
//! When the OS triggers these conditions it is fatal; it will sit in a loop
//! flashing the given pin until the device is reset.  If you need to
//! automatically reset after an error, use the Watchdog device to arrange
//! this.
//!
//! # Setting the debug pin to use
//! The debug pin to use can be set dynamically at runtime using the
//! `set_debug_pin` method:
//!
//! ```rust,no_run
//! # fn set_debug_pin() {
//!     avr_oxide::oserror::set_debug_pin(avr_oxide::hal::atmega4809::port::porte::pin(2));
//! # }
//! ```

// Imports ===================================================================
use avr_oxide::hal::generic::port::{InterruptMode, Pin, PinMode};
use avr_oxide::util::debug;
use core::ops::{ControlFlow, Try};

// Declarations ==============================================================
static mut DEBUGPIN : Option<&'static dyn Pin> = None;

/// A type used for returning and propagating errors; this implementation
/// differs from `core::result::Result` by having an `unwrap()`
/// implementation that doesn't require Debug and importing a thousand
/// dependencies we can't afford on AVR.
#[cfg_attr(not(target_arch="avr"), derive(Debug))]
#[cfg_attr(target_arch="avr", derive(ufmt::derive::uDebug))]
pub enum OxideResult<T,E> {
  Ok(T),
  Err(E)
}

#[cfg_attr(not(target_arch="avr"), derive(Debug))]
#[cfg_attr(target_arch="avr", derive(ufmt::derive::uDebug))]
#[derive(Copy,Clone,PartialEq)]
pub enum OsError {
  /// There is not enough memory to allocate a heap.
  NotEnoughRam,

  /// Ran out of memory for dynamic allocation.
  OutOfMemory,

  /// A thread has overflowed its allocated stack.
  StackOverflow,

  /// An interrupt or other kernel routine has overflowed its allocated stack.
  KernelStackOverflow,

  /// A kernel memory guard has been corrupted
  KernelGuardCrashed,

  /// It was impossible to schedule a thread for execution, because all
  /// threads have died or are deadlocked.
  NoSchedulableThreads,

  /// Some kind of 'impossible' internal error condition has arisen...
  InternalError,

  /// An attempt to make an illegal thread state change has taken place
  BadThreadState,

  /// The maximum number of threads has been reached
  OutOfThreads,

  /// Call to free a block of memory twice
  DoubleFree,

  /// Bad parameters were passed to a system call
  BadParams,

  /// Calls to inhibit sleep/standby nested too deep
  NestedInhibitTooDeep,

  /// Arithmetic overflow/underflow
  Arithmetic,

  /// A call to the default panic!() handler was made
  Panic,

  /// A call to yield() was made that is not permitted (typically, yield
  /// while interrupts are disabled.)
  CannotYield,

  /// The event queue for Oxide events overflowed
  OxideEventOverflow,

  /// You dropped a supposedly undroppable [`StaticWrap`]
  ///
  /// [`StaticWrap`]: avr_oxide::StaticWrap
  StaticDropped,

  /// You violated the borrowing rules of a [`StaticWrap`]
  ///
  /// [`StaticWrap`]: avr_oxide::StaticWrap
  StaticBorrow,

  /// The application called `panic_if_error!` on an Error
  UnwrapError,

  /// The application called `panic_if_none!` on a None
  UnwrapNone
}

// Code ======================================================================
pub trait OxideTryFrom<T>
where
  T: Sized
{
  type Error;

  fn oxide_try_from(_: T) -> OxideResult<Self, Self::Error> where Self: Sized;
}

impl<T,E> OxideResult<T,E> {
  pub const fn is_ok(&self) -> bool {
    match self {
      OxideResult::Ok(_) => true,
      OxideResult::Err(_) => false
    }
  }
  pub const fn is_err(&self) -> bool {
    match self {
      OxideResult::Ok(_) => false,
      OxideResult::Err(_) => true
    }

  }
  pub fn unwrap(self) -> T {
    match self {
      OxideResult::Ok(t) => t,
      OxideResult::Err(_) => {
        halt(OsError::UnwrapError);
      }
    }

  }
  pub fn expect_err(self) -> E {
    match self {
      OxideResult::Ok(_) => {
        halt(OsError::UnwrapError)
      }
      OxideResult::Err(e) => e
    }

  }
  pub fn unwrap_or(self, default: T) -> T {
    match self {
      OxideResult::Ok(t) => t,
      OxideResult::Err(_) => default
    }

  }
  pub fn unwrap_or_else<F: FnOnce(E)->T>(self, op: F) -> T {
    match self {
      OxideResult::Ok(t) => t,
      OxideResult::Err(e) => { (op)(e) }
    }

  }
}

impl<T,E> Try for OxideResult<T,E> {
  type Output = T;
  type Residual = OxideResult<core::convert::Infallible, E>;

  #[inline]
  fn from_output(output: Self::Output) -> Self {
    OxideResult::Ok(output)
  }

  #[inline]
  fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
    match self {
      OxideResult::Ok(t) => ControlFlow::Continue(t),
      OxideResult::Err(e) => ControlFlow::Break(OxideResult::Err(e))
    }
  }
}

impl<T, E, F: From<E>> const core::ops::FromResidual<OxideResult<core::convert::Infallible, E>>
    for OxideResult<T, F>
{
  #[inline]
  fn from_residual(residual: OxideResult<core::convert::Infallible, E>) -> Self {
    match residual {
      OxideResult::Err(e) => return OxideResult::Err(From::from(e)),
      OxideResult::Ok(_) => { panic!() }
    }
  }
}

impl<T, E> core::ops::Residual<T> for OxideResult<core::convert::Infallible, E> {
    type TryType = OxideResult<T, E>;
}


impl OsError {
  /// Turn this error into a bit representation of the pulse train we should
  /// send to the debug pin to represent this error.
  fn pulse_train(&self) -> u32 {
    match self {
      Self::NotEnoughRam           => 0b_0000_0000_0000_0000_0000_0000_0000_0001,
      Self::OutOfMemory            => 0b_0000_0000_0000_0000_0000_0000_0000_0101,
      Self::StackOverflow          => 0b_0000_0000_0000_0000_0000_0000_0001_0101,
      Self::KernelStackOverflow    => 0b_0000_0000_0000_0000_0000_0001_0101_0111,
      Self::KernelGuardCrashed     => 0b_0000_0000_0000_0000_0000_0111_0101_0111,
      Self::OxideEventOverflow     => 0b_0000_0000_0000_0000_0001_0101_0111_0111,
      Self::NoSchedulableThreads   => 0b_0000_0000_0000_0000_0000_0000_0101_0101,
      Self::CannotYield            => 0b_0000_0000_0000_0000_0000_0101_0101_0111,
      Self::InternalError          => 0b_0000_0000_0000_0000_0000_0001_0101_0101,
      Self::BadThreadState         => 0b_0000_0000_0000_0000_0000_0101_0111_0101,
      Self::OutOfThreads           => 0b_0000_0000_0000_0000_0000_0101_0101_0101,
      Self::DoubleFree             => 0b_0000_0000_0000_0000_0001_0101_0101_0101,
      Self::BadParams              => 0b_0000_0000_0000_0000_0000_0000_0001_1101,
      Self::NestedInhibitTooDeep   => 0b_0000_0000_0000_0001_0101_0101_0101_0101,
      Self::Arithmetic             => 0b_0000_0000_0000_0000_0000_0001_1101_1101,
      Self::Panic                  => 0b_0000_0000_0000_0000_0001_1101_1101_1101,
      Self::StaticDropped          => 0b_0000_0000_0000_0000_0000_0000_0111_0101,
      Self::StaticBorrow           => 0b_0000_0000_0000_0000_0000_0001_0111_0101,
      Self::UnwrapError            => 0b_0000_0000_0000_0000_0101_1101_1101_1101,
      Self::UnwrapNone             => 0b_0000_0000_0000_0001_0101_1101_1101_1101,
    }
  }
}

/**
 * Set the pin which will be used when an OS error occurs to transmit the
 * error code.
 *
 * ### Default pin for Arduino
 * If avr_oxide is built with the `arduino` feature, the default pin
 * corresponding to the Arduino's debug LED will already be set, and it is
 * not necessary to call this function.  (It is still permissible to do so
 * to use an alternative pin if desired, however.)
 */
pub fn set_debug_pin(pin: &'static dyn Pin) {
  avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
    unsafe {
      core::ptr::replace(&mut DEBUGPIN, Some(pin));
    }
  })
}

/**
 * Halt execution with the given OS error.
 *
 * The pulse code for the given error will be continuously sent on the debugging
 * pin previously set using the `set_debug_pin` method.
 *
 * ### Hardware Debuggers
 * A `break` instruction is included between pulse trains, meaning a device
 * with a hardware debugger/ICE attached should drop to the debugger.
 */
#[cfg(target_arch="avr")]
pub fn halt(error: OsError) -> ! {
  unsafe {
    avr_oxide::concurrency::interrupt::disable_interrupts();

    #[cfg(feature="panicout")]
    {
      debug::print("\r\n\nHALT: ");
      debug::print_u32(error.pulse_train());
      debug::print("\r\n");
      debug::print_thread_state();
    }

    // We want the flash-rate to be *roughly* invariant to the clock-speed and
    // powersaving mode.  But only roughly :-).
    const DELAYLOOP : u32 = (avr_oxide::deviceconsts::clock::MASTER_CLOCK_HZ / 100) / avr_oxide::deviceconsts::clock::MASTER_CLOCK_PRESCALER as u32;

    match DEBUGPIN {
      Some(pin) => {
        let pulse_code = error.pulse_train();

        // Set up the pin as an output
        pin.set_mode(PinMode::Output);
        pin.set_interrupt_mode(InterruptMode::Disabled);
        pin.set_low();

        loop {
          let mut mask : u32 = 1u32;

          for _i in 0..32 {
            if pulse_code & mask > 0 {
              pin.set_high();
            } else {
              pin.set_low();
            }

            mask <<= 1;

            for _d in 0..DELAYLOOP {
              core::arch::asm!("nop");
            }
          }
          // If there is a debugger attached, it'd be nice to break
          core::arch::asm!("break");
        }
      },
      None => {
        // No pin to toggle; just go into a busy loop (sleeping as much
        // as possible for want of battery life.  If there is a debugger
        // attached, we'll fall into it on the Break.
        core::arch::asm!(
        "break",
        "0: ", "sleep",
        "jmp 0b",
        options(noreturn)
        );
      }
    }
  }
}

#[cfg(not(target_arch="avr"))]
pub fn halt(error: OsError) -> ! {
  panic!("OS Error: {:?}", error);
}


/**
 * A macro you can use as a substitute for .unwrap() on Options that will simply
 * panic if the result is None.  You will want this to avoid hauling in all of
 * the garbage associated with the Debug and Formatter types that comes
 * along with the 'real' unwrap() implementation.  With small EEPROMs/Flash
 * we don't have the room for that luxury.
 */
#[macro_export]
macro_rules! panic_if_none {
  ($result:expr) => {
    match $result {
      Some(value) => value,
      None => { avr_oxide::oserror::halt(avr_oxide::oserror::OsError::UnwrapNone); }
    }
  };
  ($result:expr,$paniccode:expr) => {
    match $result {
      Some(value) => value,
      None => { avr_oxide::oserror::halt($paniccode); }
    }
  };

}

// Tests =====================================================================