use super::{StackError, WincClient, Xfer};
use embedded_nal::nb;
const ETHERNET_RX_TIMEOUT_MSEC: u32 = 1000;
impl<X: Xfer> WincClient<'_, X> {
pub fn read_ethernet_packet(
&mut self,
buffer: Option<&mut [u8]>,
timeout: Option<u32>,
) -> nb::Result<usize, StackError> {
match &mut self.callbacks.eth_rx_info {
None => {
self.callbacks.eth_rx_info = Some(None);
let timeout_ms = timeout.unwrap_or(ETHERNET_RX_TIMEOUT_MSEC);
self.operation_countdown = (timeout_ms * 1000) / self.poll_loop_delay_us;
}
Some(info) => {
if let Some(info) = info {
let recv_buffer = match buffer {
None => {
self.callbacks.recv_buffer.fill(0);
self.callbacks.recv_buffer.as_mut_slice()
}
Some(buffer) => buffer,
};
let len_to_read = recv_buffer.len().min(info.packet_size as usize);
let rx_done = len_to_read >= info.packet_size as usize;
self.manager.recv_ethernet_packet(
info.hif_address + info.data_offset as u32,
&mut recv_buffer[..len_to_read],
rx_done,
)?;
if rx_done {
self.callbacks.eth_rx_info = None;
} else {
info.data_offset += len_to_read as u16;
info.packet_size -= len_to_read as u16;
}
return Ok(len_to_read);
} else {
self.delay_us(self.poll_loop_delay_us);
if self.operation_countdown == 0 {
self.callbacks.eth_rx_info = None;
return Err(nb::Error::Other(StackError::GeneralTimeout));
}
self.operation_countdown -= 1;
}
}
}
self.dispatch_events()?;
Err(nb::Error::WouldBlock)
}
pub fn send_ethernet_packet(&mut self, net_pkt: &[u8]) -> Result<(), StackError> {
Ok(self.manager.send_ethernet_packet(net_pkt)?)
}
}
#[cfg(feature = "smoltcp")]
mod smoltcp_impl {
use super::{WincClient, Xfer};
use crate::error;
use crate::manager::{Manager, SOCKET_BUFFER_MAX_LENGTH};
use embedded_nal::nb;
use smoltcp::{
phy::{self, DeviceCapabilities, Medium},
time::Instant,
};
const ETH_RECV_TIMEOUT_MSEC: u32 = 100;
pub struct WincTxToken<'a, X: Xfer> {
client: Option<&'a mut Manager<X>>,
}
pub struct WincRxToken<'a> {
buffer: &'a mut [u8],
read_length: usize,
}
impl<X: Xfer> phy::Device for WincClient<'_, X> {
type RxToken<'a>
= WincRxToken<'a>
where
Self: 'a;
type TxToken<'a>
= WincTxToken<'a, X>
where
Self: 'a;
fn receive(
&mut self,
_timestamp: Instant,
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let result = nb::block!(self.read_ethernet_packet(None, Some(ETH_RECV_TIMEOUT_MSEC)));
let Ok(read_length) = result else {
return None;
};
let rx_token = WincRxToken {
buffer: &mut self.callbacks.recv_buffer,
read_length,
};
let tx_token = WincTxToken {
client: Some(&mut self.manager),
};
Some((rx_token, tx_token))
}
fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
let tx_token = WincTxToken {
client: Some(&mut self.manager),
};
Some(tx_token)
}
fn capabilities(&self) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = SOCKET_BUFFER_MAX_LENGTH;
caps.max_burst_size = Some(1);
caps.medium = Medium::Ethernet;
caps
}
}
impl<'a> phy::RxToken for WincRxToken<'a> {
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&[u8]) -> R,
{
let length = self.read_length;
f(&self.buffer[..length])
}
}
impl<'a, X: Xfer> phy::TxToken for WincTxToken<'a, X> {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
let mut tx_buffer = [0u8; SOCKET_BUFFER_MAX_LENGTH];
let result = f(&mut tx_buffer[..len]);
if let Some(manager) = self.client {
if let Err(e) = manager.send_ethernet_packet(&tx_buffer[..len]) {
error!("Failed to send ethernet packet: {:?}", e);
}
} else {
error!("No client available to send the packet.");
}
result
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::client::{test_shared::*, SocketCallbacks};
use crate::manager::{EventListener, MAX_TX_ETHERNET_PACKET_SIZE};
use crate::CommError;
#[test]
fn test_send_ethernet_packet_success() {
let mut client = make_test_client();
let packet = [0xffu8; 10];
let result = client.send_ethernet_packet(&packet);
assert!(result.is_ok());
}
#[test]
fn test_send_ethernet_packet_failed() {
let mut client = make_test_client();
let result = client.send_ethernet_packet(&[]);
assert_eq!(
result,
Err(StackError::WincWifiFail(CommError::BufferError))
);
}
#[test]
fn test_read_ethernet_packet_success() {
let mut client = make_test_client();
let rx_info = (100 as u16, 111 as u16, 0xAABBCCDD as u32);
let mut rx_buffer = [0u8; 200];
let mut my_debug = |callbacks: &mut SocketCallbacks| {
callbacks.on_eth(rx_info.0, rx_info.1, rx_info.2);
};
client.debug_callback = Some(&mut my_debug);
let result = nb::block!(client.read_ethernet_packet(Some(&mut rx_buffer), None));
assert!(result.is_ok());
}
#[test]
fn test_read_ethernet_packet_timeout() {
let mut client = make_test_client();
let mut rx_buffer = [0u8; 200];
let timeout = 1000 as u32;
let result = nb::block!(client.read_ethernet_packet(Some(&mut rx_buffer), Some(timeout)));
assert_eq!(result, Err(StackError::GeneralTimeout));
}
#[test]
fn test_read_ethernet_packet_internal_buffer() {
let mut client = make_test_client();
client.callbacks.recv_buffer.fill(0xff);
let rx_info = (1600 as u16, 111 as u16, 0xAABBCCDD as u32);
let timeout = 1000 as u32;
let mut my_debug = |callbacks: &mut SocketCallbacks| {
callbacks.on_eth(rx_info.0, rx_info.1, rx_info.2);
};
client.debug_callback = Some(&mut my_debug);
let result = nb::block!(client.read_ethernet_packet(None, Some(timeout)));
assert!(result.is_ok());
assert!(client.callbacks.recv_buffer.iter().all(|&b| b == 0));
}
#[test]
fn test_read_ethernet_over_range() {
let mut client = make_test_client();
let packet = [0u8; MAX_TX_ETHERNET_PACKET_SIZE + 1];
let result = client.send_ethernet_packet(&packet);
assert_eq!(
result,
Err(StackError::WincWifiFail(CommError::BufferError))
);
}
}