use crate::{
ETHERCAT_ETHERTYPE, MAINDEVICE_ADDR,
ethernet::{EthernetAddress, EthernetFrame},
pdu_loop::{
frame_element::{FrameElement, FrameState},
frame_header::EthercatFrameHeader,
},
};
use atomic_waker::AtomicWaker;
use core::{
fmt::Debug,
marker::PhantomData,
ptr::{NonNull, addr_of, addr_of_mut},
sync::atomic::{AtomicU8, Ordering},
task::Waker,
};
use ethercrab_wire::EtherCrabWireSized;
use super::FIRST_PDU_EMPTY;
#[derive(Copy, Clone)]
pub struct FrameBox<'sto> {
frame: NonNull<FrameElement<0>>,
pdu_idx: &'sto AtomicU8,
max_len: usize,
_lifetime: PhantomData<&'sto mut FrameElement<0>>,
}
impl Debug for FrameBox<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let data = self.pdu_buf();
f.debug_struct("FrameBox")
.field("state", unsafe {
&(*addr_of!((*self.frame.as_ptr()).status))
})
.field("frame_index", &self.storage_slot_index())
.field("data_hex", &format_args!("{:02x?}", data))
.finish()
}
}
impl<'sto> FrameBox<'sto> {
pub fn new(
frame: NonNull<FrameElement<0>>,
pdu_idx: &'sto AtomicU8,
max_len: usize,
) -> FrameBox<'sto> {
Self {
frame,
max_len,
pdu_idx,
_lifetime: PhantomData,
}
}
pub fn init(&mut self) {
unsafe {
addr_of_mut!((*self.frame.as_ptr()).waker).write(AtomicWaker::new());
(*addr_of_mut!((*self.frame.as_ptr()).first_pdu))
.store(FIRST_PDU_EMPTY, Ordering::Relaxed);
addr_of_mut!((*self.frame.as_ptr()).pdu_payload_len).write(0);
}
let mut ethernet_frame = self.ethernet_frame_mut();
ethernet_frame.set_src_addr(MAINDEVICE_ADDR);
ethernet_frame.set_dst_addr(EthernetAddress::BROADCAST);
ethernet_frame.set_ethertype(ETHERCAT_ETHERTYPE);
ethernet_frame.payload_mut().fill(0);
}
pub fn next_pdu_idx(&self) -> u8 {
self.pdu_idx.fetch_add(1, Ordering::Relaxed)
}
pub fn replace_waker(&self, waker: &Waker) {
let ptr = unsafe { &*addr_of!((*self.frame.as_ptr()).waker) };
ptr.register(waker);
}
pub fn wake(&self) -> Result<(), ()> {
let waker = unsafe { &*addr_of!((*self.frame.as_ptr()).waker) };
if let Some(waker) = waker.take() {
waker.wake();
Ok(())
} else {
Err(())
}
}
pub fn storage_slot_index(&self) -> u8 {
unsafe { FrameElement::<0>::storage_slot_index(self.frame) }
}
pub fn ecat_frame_header_mut(&mut self) -> &mut [u8] {
let ptr = unsafe { FrameElement::<0>::ptr(self.frame) };
let ethercat_header_start = EthernetFrame::<&[u8]>::header_len();
unsafe {
core::slice::from_raw_parts_mut(
ptr.as_ptr().byte_add(ethercat_header_start),
EthercatFrameHeader::PACKED_LEN,
)
}
}
pub fn pdu_buf_mut(&mut self) -> &mut [u8] {
let ptr = unsafe { FrameElement::<0>::ethercat_payload_ptr(self.frame) };
let pdu_payload_start =
EthernetFrame::<&[u8]>::header_len() + EthercatFrameHeader::header_len();
unsafe { core::slice::from_raw_parts_mut(ptr.as_ptr(), self.max_len - pdu_payload_start) }
}
pub fn pdu_buf(&self) -> &[u8] {
let ptr = unsafe { FrameElement::<0>::ethercat_payload_ptr(self.frame) };
let pdu_payload_start =
EthernetFrame::<&[u8]>::header_len() + EthercatFrameHeader::header_len();
unsafe { core::slice::from_raw_parts(ptr.as_ptr(), self.max_len - pdu_payload_start) }
}
fn ethernet_frame_mut(&mut self) -> EthernetFrame<&mut [u8]> {
unsafe {
EthernetFrame::new_unchecked(core::slice::from_raw_parts_mut(
FrameElement::<0>::ptr(self.frame).as_ptr(),
self.max_len,
))
}
}
pub fn ethernet_frame(&self) -> EthernetFrame<&[u8]> {
unsafe {
EthernetFrame::new_unchecked(core::slice::from_raw_parts(
FrameElement::<0>::ptr(self.frame).as_ptr(),
self.max_len,
))
}
}
pub fn pdu_payload_len(&self) -> usize {
unsafe { *addr_of!((*self.frame.as_ptr()).pdu_payload_len) }
}
pub fn set_state(&self, to: FrameState) {
unsafe { FrameElement::set_state(self.frame, to) };
}
pub fn swap_state(&self, from: FrameState, to: FrameState) -> Result<(), FrameState> {
unsafe { FrameElement::swap_state(self.frame, from, to) }.map(|_| ())
}
pub fn clear_first_pdu(&self) {
unsafe {
FrameElement::<0>::clear_first_pdu(self.frame);
}
}
pub fn add_pdu(&mut self, alloc_size: usize, pdu_idx: u8) {
unsafe { *addr_of_mut!((*self.frame.as_ptr()).pdu_payload_len) += alloc_size };
unsafe { FrameElement::<0>::set_first_pdu(self.frame, pdu_idx) };
}
}