use crate::hal::generic::serial::{SerialRxTx, ReadHandlerResult, SerialError, SerialPortMode, SerialPortIdentity, SerialWriteEventCallback, SerialReadEventCallback, SerialErrorEventCallback};
use crate::event::{EventSink, EventSource, OxideEvent, OxideEventEnvelope};
use core::marker::PhantomData;
use core::cell::RefCell;
use core::ops::DerefMut;
use crate::alloc::boxed::Box;
use crate::hal::generic::callback::IsrCallback;
use crate::hal::generic::port::{Pin, PinMode};
use crate::panic_if_none;
pub trait SerialCallback = FnMut(SerialPortIdentity, SerialState) -> ();
#[derive(PartialEq,Eq,Clone,Copy)]
pub enum SerialState {
ReadAvailable,
SerialCommsError,
BreakDetected,
ReadBufferOverflow
}
#[derive(Copy,Clone)]
pub struct TransmitOptions(u8);
pub struct SerialPort<'sp,P,T,R,S>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{
port: &'static mut P,
tx: &'static T,
rx: &'static R,
phantom: PhantomData<S>,
tx_options: TransmitOptions,
on_event: RefCell<Option<Box<dyn SerialCallback + 'sp>>>
}
impl TransmitOptions {
pub fn set_flush_on_eol(&mut self, flag: bool) {
self.0 = match flag {
true => self.0 & 0b11111110,
false => self.0 | 0b00000001
}
}
pub fn flush_on_eol(self) -> bool {
self.0 & 0b00000001 > 0
}
pub fn set_cr_to_crlf(&mut self, flag: bool) {
self.0 = match flag {
true => self.0 & 0b11111101,
false => self.0 | 0b00000010
}
}
pub fn cr_to_crlf(&self) -> bool {
self.0 & 0b00000010 > 0
}
pub fn set_lf_to_crlf(&mut self, flag: bool) {
self.0 = match flag {
true => self.0 & 0b11111011,
false => self.0 | 0b00000100
}
}
pub fn lf_to_crlf(&self) -> bool {
self.0 & 0b00000100 > 0
}
}
impl Default for TransmitOptions {
fn default() -> Self {
TransmitOptions(0b00000001)
}
}
impl<P,T,R,S> SerialPort<'_,P,T,R,S>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{
pub fn using_port_and_pins(port: &'static mut P, tx: &'static mut T, rx: &'static mut R) -> Self {
rx.listen(IsrCallback::Nop(()));
port.enable_rxtx(true);
tx.set_mode(PinMode::Output);
rx.set_mode(PinMode::InputFloating);
SerialPort {
port, tx, rx,
phantom: PhantomData::default(),
tx_options: TransmitOptions::default(),
on_event: RefCell::new(None)
}
}
pub fn mode(self, mode: SerialPortMode) -> Self {
self.tx.set_mode(PinMode::InputFloating);
self.rx.set_mode(PinMode::InputFloating);
self.port.mode(mode);
self.port.enable_rxtx(true);
self.tx.set_mode(PinMode::Output);
self
}
pub fn get_transmit_options(&self) -> TransmitOptions {
self.tx_options
}
pub fn set_transmit_options(&mut self, options: TransmitOptions) {
self.tx_options = options;
}
}
impl<P,T,R,S> EventSource for SerialPort<'_,P,T,R,S>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{
fn listen(&'static self) {
self.port.set_write_complete_callback(SerialWriteEventCallback::Nop(()));
self.port.set_read_callback(SerialReadEventCallback::WithData(|src,byte, udata|{
S::event(OxideEventEnvelope::to(unsafe {&*(panic_if_none!(udata) as *const SerialPort<P,T,R,S> as *const dyn EventSource)},
OxideEvent::SerialEvent(src, SerialState::ReadAvailable)));
ReadHandlerResult::Buffer(byte)
},self as *const dyn core::any::Any));
self.port.set_error_callback(SerialErrorEventCallback::WithData(|src,error, udata|{
S::event(OxideEventEnvelope::to(unsafe {&*(panic_if_none!(udata) as *const SerialPort<P,T,R,S> as *const dyn EventSource)},
OxideEvent::SerialEvent(src, match error {
SerialError::BufferOverflow => SerialState::ReadBufferOverflow,
SerialError::Break => SerialState::BreakDetected,
_ => SerialState::SerialCommsError
})));
}, self as *const dyn core::any::Any));
}
fn process_event(&self, evt: OxideEvent) {
match (self.on_event.borrow_mut().deref_mut(), evt) {
(Some(f), OxideEvent::SerialEvent(source, state)) => {
(*f)(source,state)
},
_ => {}
}
}
}
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 {
match byte {
b'\n' => {
if self.tx_options.lf_to_crlf() {
self.port.write_u8(b'\r');
}
},
_ => {}
}
self.port.write_u8(*byte);
match byte {
b'\r' => {
if self.tx_options.cr_to_crlf() {
self.port.write_u8(b'\n');
}
if self.tx_options.flush_on_eol() {
self.port.flush();
}
},
b'\n' => {
if self.tx_options.flush_on_eol() {
self.port.flush();
}
},
_ => {}
}
}
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(())
}
}