use avr_oxide::hal::generic::serial::{SerialRxTx, ReadHandlerResult, SerialError, SerialPortMode, SerialPortIdentity, SerialWriteEventCallback, SerialReadEventCallback, SerialErrorEventCallback};
use avr_oxide::event::{EventSink, EventSource, OxideEvent, OxideEventEnvelope};
use core::marker::PhantomData;
use core::cell::RefCell;
use core::ops::DerefMut;
use ufmt::derive::uDebug;
use avr_oxide::devices::internal::StaticShareable;
use oxide_macros::Persist;
use avr_oxide::alloc::boxed::Box;
use avr_oxide::devices::Handle;
use avr_oxide::hal::generic::port::{InterruptMode, Pin, PinMode};
use avr_oxide::halt_if_none;
use avr_oxide::sync::Mutex;
use avr_oxide::util::datatypes::{BitField, BitFieldAccess, BitIndex};
pub trait SerialCallback = FnMut(SerialPortIdentity, SerialState) -> ();
#[derive(PartialEq,Eq,Clone,Copy,uDebug,Persist)]
pub enum SerialState {
ReadAvailable,
SerialCommsError,
BreakDetected,
ReadBufferOverflow
}
pub type SerialOptions = BitField;
pub struct SerialPort<'sp,S>
where
S: EventSink
{
port: &'static mut dyn SerialRxTx,
tx: &'static dyn Pin,
_rx: &'static dyn Pin,
phantom: PhantomData<S>,
serial_options: Mutex<SerialOptions>,
rx_lock: Mutex<()>,
tx_lock: Mutex<()>,
on_event: RefCell<Option<Box<dyn SerialCallback + 'sp>>>
}
impl<'sp,S> StaticShareable for SerialPort<'sp,S>
where
S: EventSink
{}
const TX_FLUSH_ON_EOL: BitIndex = BitIndex::bit_c(0);
const TX_CR_TO_CRLF: BitIndex = BitIndex::bit_c(1);
const TX_LF_TO_CRLF: BitIndex = BitIndex::bit_c(2);
const INTERACTIVE: BitIndex = BitIndex::bit_c(3);
const ECHO: BitIndex = BitIndex::bit_c(4);
const RX_BLOCKING: BitIndex = BitIndex::bit_c(5);
const RX_TO_EOL: BitIndex = BitIndex::bit_c(6);
const RX_TO_EOT: BitIndex = BitIndex::bit_c(7);
impl SerialOptions {
pub fn set_flush_on_eol(&mut self, flag: bool) {
self.set_or_clr(TX_FLUSH_ON_EOL, flag);
}
pub fn flush_on_eol(self) -> bool {
self.is_set(TX_FLUSH_ON_EOL)
}
pub fn set_cr_to_crlf(&mut self, flag: bool) {
self.set_or_clr(TX_CR_TO_CRLF, flag);
}
pub fn cr_to_crlf(&self) -> bool {
self.is_set(TX_CR_TO_CRLF)
}
pub fn set_lf_to_crlf(&mut self, flag: bool) {
self.set_or_clr(TX_LF_TO_CRLF, flag);
}
pub fn lf_to_crlf(&self) -> bool {
self.is_set(TX_LF_TO_CRLF)
}
pub fn set_interactive(&mut self, flag: bool) {
self.set_or_clr(INTERACTIVE, flag);
}
pub fn interactive(&self) -> bool {
self.is_set(INTERACTIVE)
}
pub fn set_echo(&mut self, flag: bool) {
self.set_or_clr(ECHO, flag)
}
pub fn echo(&self) -> bool {
self.is_set(ECHO)
}
pub fn set_blocking_read(&mut self, flag: bool) {
self.set_or_clr(RX_BLOCKING, flag)
}
pub fn blocking_read(&self) -> bool {
self.is_set(RX_BLOCKING)
}
pub fn set_read_to_eol(&mut self, flag: bool) {
self.set_or_clr(RX_TO_EOL, flag)
}
pub fn read_to_eol(&self) -> bool {
self.is_set(RX_TO_EOL)
}
pub fn set_read_to_eot(&mut self, flag: bool) {
self.set_or_clr(RX_TO_EOT, flag)
}
pub fn read_to_eot(&self) -> bool {
self.is_set(RX_TO_EOT)
}
pub fn disable_translations(&mut self) {
self.set_cr_to_crlf(false);
self.set_lf_to_crlf(false);
}
}
impl Default for SerialOptions {
fn default() -> Self {
BitField::with_bits_set(&[TX_FLUSH_ON_EOL])
}
}
impl<S> SerialPort<'_,S>
where
S: EventSink
{
pub fn using_port_and_pins(port: &'static mut dyn SerialRxTx, tx: &'static dyn Pin, rx: &'static dyn Pin) -> Self {
tx.set_mode(PinMode::InputFloating);
rx.set_mode(PinMode::InputFloating);
rx.set_interrupt_mode(InterruptMode::Disabled);
tx.set_interrupt_mode(InterruptMode::Disabled);
port.set_enable_rxtx(false);
SerialPort {
port, tx,
_rx: rx,
phantom: PhantomData::default(),
serial_options: Mutex::new(SerialOptions::default()),
rx_lock: Mutex::new(()),
tx_lock: Mutex::new(()),
on_event: RefCell::new(None)
}
}
pub fn mode(self, mode: SerialPortMode) -> Self {
self.port.set_mode(mode);
self.port.set_enable_rxtx(true);
self.tx.set_mode(PinMode::Output);
self
}
pub fn get_options(&self) -> SerialOptions {
let locked = self.serial_options.lock();
(*locked).clone()
}
pub fn set_options(&mut self, options: SerialOptions) {
let mut locked = self.serial_options.lock();
*locked = options;
}
}
impl<S> EventSource for SerialPort<'_,S>
where
S: EventSink
{
fn listen(&'static self) {
self.port.set_write_complete_callback(SerialWriteEventCallback::Nop(()));
self.port.set_read_callback(SerialReadEventCallback::WithData(|isotoken, src,byte, udata|{
S::event(isotoken, OxideEventEnvelope::to(unsafe {&*(halt_if_none!(udata, avr_oxide::oserror::OsError::InternalError) as *const SerialPort<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(|isotoken, src,error, udata|{
S::event(isotoken, OxideEventEnvelope::to(unsafe {&*(halt_if_none!(udata, avr_oxide::oserror::OsError::InternalError) as *const SerialPort<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<S> avr_oxide::io::Read for SerialPort<'_,S>
where
S: EventSink
{
fn read(&mut self, buf: &mut [u8]) -> avr_oxide::io::Result<usize> {
let options = self.get_options();
let rx_lock = self.rx_lock.lock();
let mut cnt = 0usize;
if options.interactive() {
let tx_lock = self.tx_lock.lock();
self.port.flush();
drop(tx_lock);
}
while cnt < buf.len() {
let character = {
if options.blocking_read() {
Some(self.port.read_u8())
} else {
self.port.try_read_u8()
}
};
match character {
Some(c) => {
buf[cnt] = c;
cnt += 1;
if options.echo() {
let tx_lock = self.tx_lock.lock();
self.port.write_u8(c);
self.port.flush();
drop(tx_lock);
}
if match c {
b'\n' | b'\r' => {
options.read_to_eol()
},
0x04u8 => {
options.read_to_eot()
},
b'x' => {
true
}
_ => false
} {
return Ok(cnt);
}
},
None => {
return Ok(cnt);
}
}
}
drop(rx_lock);
Ok(cnt)
}
}
impl<S> avr_oxide::io::Write for SerialPort<'_,S>
where
S: EventSink
{
fn write(&mut self, buf: &[u8]) -> avr_oxide::io::Result<usize> {
let options = self.get_options();
let tx_lock = self.tx_lock.lock();
let cnt : usize = 0;
for byte in buf {
match byte {
b'\n' => {
if options.lf_to_crlf() {
self.port.write_u8(b'\r');
}
},
_ => {}
}
self.port.write_u8(*byte);
match byte {
b'\r' => {
if options.cr_to_crlf() {
self.port.write_u8(b'\n');
}
if options.flush_on_eol() {
self.port.flush();
}
},
b'\n' => {
if options.flush_on_eol() {
self.port.flush();
}
},
_ => {}
}
}
drop(tx_lock);
Ok(cnt)
}
fn flush(&mut self) -> avr_oxide::io::Result<()> {
self.port.flush();
Ok(())
}
}
impl<S> ufmt_write::uWrite for SerialPort<'_,S>
where
S: EventSink
{
type Error = core::convert::Infallible;
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
let _ignore = avr_oxide::io::Write::write(self, s.as_bytes());
Ok(())
}
}
unsafe impl <S> Send for SerialPort<'_,S>
where
S: EventSink
{}
unsafe impl <S> Sync for SerialPort<'_,S>
where
S: EventSink
{}
impl<S> avr_oxide::io::Write for Handle<SerialPort<'_,S>>
where
S: EventSink
{
fn write(&mut self, buf: &[u8]) -> avr_oxide::io::Result<usize> {
Handle::deref_mut(self).write(buf)
}
fn flush(&mut self) -> avr_oxide::io::Result<()> {
Handle::deref_mut(self).flush()
}
}
impl<S> avr_oxide::io::Read for Handle<SerialPort<'_,S>>
where
S: EventSink
{
fn read(&mut self, buf: &mut [u8]) -> avr_oxide::io::Result<usize> {
Handle::deref_mut(self).read(buf)
}
}
impl<S> ufmt_write::uWrite for Handle<SerialPort<'_,S>>
where
S: EventSink
{
type Error = core::convert::Infallible;
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
Handle::deref_mut(self).write_str(s)
}
}