avr-oxide 0.0.5

An extremely simple Rusty operating system for AVR microcontrollers
/* serialport.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! Abstractions for serial I/O devices, implementing the Read/Write interfaces
//! provided by std::io

// Imports ===================================================================
use crate::hal::generic::serial::{SerialRxTx, ReadHandlerResult, SerialError, SerialPortMode};
use crate::event::{EventSink, OxideEvent};
use core::marker::PhantomData;
use crate::hal::generic::port::{Pin, PinMode};

// Declarations ===============================================================
/**
 * State of the serial port sent with Oxide events.
 */
#[derive(PartialEq,Eq,Clone,Copy)]
pub enum SerialState {
  /// There is data waiting in the buffer for someone to read it
  ReadAvailable,

  /// A serial error has been detected (framing, parity etc.)
  SerialCommsError,

  /// A *break* condition on the receive line has been detected
  BreakDetected,

  /// The receive buffer has overflowed (data was not consumed quickly
  /// enough...)
  ReadBufferOverflow
}

/**
 * Encapsulation of a serial port exposing the standard io::Read/io::Write
 * traits.  Also generates Oxide event notifications.
 */
pub struct SerialPort<P,T,R,S>
where
  P: 'static + SerialRxTx,
  T: 'static + Pin,
  R: 'static + Pin,
  S: EventSink
{
  port: &'static mut P,
  tx: &'static mut T,
  rx: &'static mut R,
  phantom: PhantomData<S>
}

// Code ======================================================================
impl<P,T,R,S> SerialPort<P,T,R,S>
where
  P: 'static + SerialRxTx,
  T: 'static + Pin,
  R: 'static + Pin,
  S: EventSink
{
  /**
   * Create an instance of SerialPort that will communicate over the given
   * port and pins.
   */
  pub fn using_port_and_pins(port: &'static mut P, tx: &'static mut T, rx: &'static mut R) -> Self {
    rx.listen(None);

    // We won't send any events on write.
    port.set_write_complete_callback(None);
    // When the serial port receives a byte, send a ReadAvailable message
    port.set_read_callback(Some(|byte|{
      S::event(OxideEvent::SerialEvent(SerialState::ReadAvailable));
      ReadHandlerResult::Buffer(byte)
    }));
    // When the serial port generates an error, send a suitable message
    port.set_error_callback(Some(|error|{
      match error {
        SerialError::BufferOverflow => {
          S::event(OxideEvent::SerialEvent(SerialState::ReadBufferOverflow));
        },
        SerialError::Break => {
          S::event(OxideEvent::SerialEvent(SerialState::BreakDetected));
        },
        _ => {
          S::event(OxideEvent::SerialEvent(SerialState::SerialCommsError));
        }
      }
    }));

    port.enable_rxtx(true);
    tx.set_mode(PinMode::Output);
    rx.set_mode(PinMode::InputFloating);

    SerialPort {
      port, tx, rx,
      phantom: PhantomData::default()
    }
  }

  /**
   * Set the serial port comms parameters.
   */
  pub fn mode(self, mode: SerialPortMode) -> Self {
    // Disconnect our pins while we reconfigure mode to avoid splurging junk
    // out onto the line
    self.tx.set_mode(PinMode::InputFloating);
    self.rx.set_mode(PinMode::InputFloating);

    self.port.mode(mode);
    self.port.enable_rxtx(true);
    // Now we can enable the transmit pin again
    self.tx.set_mode(PinMode::Output);

    self
  }
}


impl<P,T,R,S> crate::io::Read for SerialPort<P,T,R,S>
where
  P: 'static + SerialRxTx,
  T: 'static + Pin,
  R: 'static + Pin,
  S: EventSink
{
  fn read(&mut self, buf: &mut [u8]) -> crate::io::Result<usize> {
    todo!()
  }
}

impl<P,T,R,S> crate::io::Write for SerialPort<P,T,R,S>
where
  P: 'static + SerialRxTx,
  T: 'static + Pin,
  R: 'static + Pin,
  S: EventSink
{
  fn write(&mut self, buf: &[u8]) -> crate::io::Result<usize> {
    let cnt : usize = 0;

    for byte in buf {
      self.port.write_u8(*byte);
    }
    Ok(cnt)
  }

  fn flush(&mut self) -> crate::io::Result<()> {
    self.port.flush();
    Ok(())
  }
}

impl<P,T,R,S> core::fmt::Write for SerialPort<P,T,R,S>
where
  P: 'static + SerialRxTx,
  T: 'static + Pin,
  R: 'static + Pin,
  S: EventSink
{
  fn write_str(&mut self, s: &str) -> core::fmt::Result {
    let _ignore = crate::io::Write::write(self, s.as_bytes());
    Ok(())
  }
}