use super::{
Element, Error, Transfer,
channel::{self, Channel, Configuration},
};
use core::{
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
pub unsafe trait Source<E: Element> {
fn source_signal(&self) -> u32;
fn source_address(&self) -> *const E;
fn enable_source(&mut self);
fn disable_source(&mut self);
}
pub unsafe trait Destination<E: Element> {
fn destination_signal(&self) -> u32;
fn destination_address(&self) -> *const E;
fn enable_destination(&mut self);
fn disable_destination(&mut self);
}
pub struct Read<'a, S, E>
where
S: Source<E>,
E: Element,
{
channel: &'a Channel,
source: &'a mut S,
transfer: Transfer<'a>,
_elem: PhantomData<&'a mut E>,
}
impl<S, E> Future for Read<'_, S, E>
where
S: Source<E>,
E: Element,
{
type Output = Result<(), Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unsafe { self.map_unchecked_mut(|this| &mut this.transfer) }.poll(cx)
}
}
impl<S, E> Drop for Read<'_, S, E>
where
S: Source<E>,
E: Element,
{
fn drop(&mut self) {
self.source.disable_source();
while self.channel.is_hardware_signaling() {}
}
}
fn prepare_read<S, E>(channel: &mut Channel, source: &mut S, buffer: &mut [E])
where
S: Source<E>,
E: Element,
{
channel.disable();
channel.set_disable_on_completion(true);
channel.set_channel_configuration(Configuration::enable(source.source_signal()));
unsafe {
channel::set_source_hardware(channel, source.source_address());
channel::set_destination_linear_buffer(channel, buffer);
channel.set_minor_loop_bytes(core::mem::size_of::<E>() as u32);
channel.set_transfer_iterations(buffer.len() as u16);
}
source.enable_source();
}
pub fn read<'a, S, E>(
channel: &'a mut Channel,
source: &'a mut S,
buffer: &'a mut [E],
) -> Read<'a, S, E>
where
S: Source<E>,
E: Element,
{
prepare_read(channel, source, buffer);
Read {
channel,
transfer: unsafe { Transfer::new(channel) },
source,
_elem: PhantomData,
}
}
pub struct Write<'a, D, E>
where
D: Destination<E>,
E: Element,
{
channel: &'a Channel,
destination: &'a mut D,
transfer: Transfer<'a>,
_elem: PhantomData<&'a E>,
}
impl<D, E> Future for Write<'_, D, E>
where
D: Destination<E>,
E: Element,
{
type Output = Result<(), Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unsafe { self.map_unchecked_mut(|this| &mut this.transfer) }.poll(cx)
}
}
impl<D, E> Drop for Write<'_, D, E>
where
D: Destination<E>,
E: Element,
{
fn drop(&mut self) {
self.destination.disable_destination();
while self.channel.is_hardware_signaling() {}
}
}
fn prepare_write<D, E>(channel: &mut Channel, buffer: &[E], destination: &mut D)
where
D: Destination<E>,
E: Element,
{
channel.disable();
channel.set_disable_on_completion(true);
channel.set_channel_configuration(Configuration::enable(destination.destination_signal()));
unsafe {
channel::set_source_linear_buffer(channel, buffer);
channel::set_destination_hardware(channel, destination.destination_address());
channel.set_minor_loop_bytes(core::mem::size_of::<E>() as u32);
channel.set_transfer_iterations(buffer.len() as u16);
}
destination.enable_destination();
}
pub fn write<'a, D, E>(
channel: &'a mut Channel,
buffer: &'a [E],
destination: &'a mut D,
) -> Write<'a, D, E>
where
D: Destination<E>,
E: Element,
{
prepare_write(channel, buffer, destination);
Write {
channel,
destination,
transfer: unsafe { Transfer::new(channel) },
_elem: PhantomData,
}
}
pub unsafe trait Bidirectional<E: Element>: Source<E> + Destination<E> {}
pub struct FullDuplex<'a, P, E>
where
P: Bidirectional<E>,
E: Element,
{
rx_channel: &'a Channel,
rx_transfer: Transfer<'a>,
rx_done: bool,
tx_channel: &'a Channel,
tx_transfer: Transfer<'a>,
tx_done: bool,
peripheral: &'a mut P,
_elem: PhantomData<E>,
}
pub fn full_duplex<'a, P, E>(
rx_channel: &'a mut Channel,
tx_channel: &'a mut Channel,
peripheral: &'a mut P,
buffer: &'a mut [E],
) -> FullDuplex<'a, P, E>
where
P: Bidirectional<E>,
E: Element,
{
prepare_write(tx_channel, buffer, peripheral);
prepare_read(rx_channel, peripheral, buffer);
FullDuplex {
rx_channel,
rx_transfer: unsafe { Transfer::new(rx_channel) },
rx_done: false,
tx_channel,
tx_transfer: unsafe { Transfer::new(tx_channel) },
tx_done: false,
peripheral,
_elem: PhantomData,
}
}
impl<P, E> Future for FullDuplex<'_, P, E>
where
P: Bidirectional<E>,
E: Element,
{
type Output = Result<(), Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if !self.rx_done {
let poll = unsafe {
self.as_mut()
.map_unchecked_mut(|this| &mut this.rx_transfer)
}
.poll(cx)?;
*unsafe { &mut self.as_mut().get_unchecked_mut().rx_done } = poll.is_ready();
}
if !self.tx_done {
let poll = unsafe {
self.as_mut()
.map_unchecked_mut(|this| &mut this.tx_transfer)
}
.poll(cx)?;
*unsafe { &mut self.as_mut().get_unchecked_mut().tx_done } = poll.is_ready();
}
if self.tx_done && self.rx_done {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
}
impl<P, E> Drop for FullDuplex<'_, P, E>
where
P: Bidirectional<E>,
E: Element,
{
fn drop(&mut self) {
self.peripheral.disable_destination();
self.peripheral.disable_source();
while self.tx_channel.is_hardware_signaling() {}
while self.rx_channel.is_hardware_signaling() {}
}
}