use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::channel::Channel;
use embassy_sync::signal::Signal;
use embassy_time::{Duration, Timer};
use crate::error::{Error, Result};
pub const MTU: usize = 1514;
const SPI_HEADER_MAGIC: u16 = 0x55AA;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrafficType {
AtCommand = 0,
NetworkSta = 1,
NetworkAp = 2,
Hci = 3,
OpenThread = 4,
}
#[derive(Debug, Clone, Copy)]
pub struct SpiFrameHeader {
pub magic: u16,
pub length: u16,
pub version: u8,
pub rx_stall: bool,
pub flags: u8,
pub traffic_type: TrafficType,
}
impl SpiFrameHeader {
pub fn new(traffic_type: TrafficType, payload_len: u16) -> Self {
Self {
magic: SPI_HEADER_MAGIC,
length: payload_len,
version: 0,
rx_stall: false,
flags: 0,
traffic_type,
}
}
pub fn to_bytes(&self) -> [u8; 8] {
let flags_byte =
(self.version & 0x03) | ((self.rx_stall as u8) << 2) | ((self.flags & 0x1F) << 3);
[
(self.magic & 0xFF) as u8,
(self.magic >> 8) as u8,
(self.length & 0xFF) as u8,
(self.length >> 8) as u8,
flags_byte,
self.traffic_type as u8,
0, 0, ]
}
pub fn from_bytes(bytes: &[u8; 8]) -> Option<Self> {
let magic = u16::from_le_bytes([bytes[0], bytes[1]]);
if magic != SPI_HEADER_MAGIC {
return None;
}
let length = u16::from_le_bytes([bytes[2], bytes[3]]);
let flags_byte = bytes[4];
let traffic_type_raw = bytes[5];
let traffic_type = match traffic_type_raw {
0 => TrafficType::AtCommand,
1 => TrafficType::NetworkSta,
2 => TrafficType::NetworkAp,
3 => TrafficType::Hci,
4 => TrafficType::OpenThread,
_ => return None, };
Some(Self {
magic,
length,
version: flags_byte & 0x03,
rx_stall: (flags_byte & 0x04) != 0,
flags: (flags_byte >> 3) & 0x1F,
traffic_type,
})
}
pub fn is_valid(&self) -> bool {
self.magic == SPI_HEADER_MAGIC && (self.length as usize) <= MTU
}
}
#[derive(Debug)]
pub struct PacketBuf<const N: usize> {
pub buf: [u8; N],
pub len: usize,
}
impl<const N: usize> PacketBuf<N> {
pub const fn new() -> Self {
Self {
buf: [0u8; N],
len: 0,
}
}
}
impl<const N: usize> Default for PacketBuf<N> {
fn default() -> Self {
Self::new()
}
}
pub struct State<const MTU: usize, const RX_QUEUE: usize, const TX_QUEUE: usize> {
rx_channel: Channel<NoopRawMutex, PacketBuf<MTU>, RX_QUEUE>,
tx_channel: Channel<NoopRawMutex, PacketBuf<MTU>, TX_QUEUE>,
link_state: Signal<NoopRawMutex, bool>,
}
impl<const MTU: usize, const RX_QUEUE: usize, const TX_QUEUE: usize>
State<MTU, RX_QUEUE, TX_QUEUE>
{
pub const fn new() -> Self {
Self {
rx_channel: Channel::new(),
tx_channel: Channel::new(),
link_state: Signal::new(),
}
}
}
pub struct St67w611Device<'d, const MTU: usize, const RX_QUEUE: usize, const TX_QUEUE: usize> {
state: &'d State<MTU, RX_QUEUE, TX_QUEUE>,
}
impl<'d, const MTU: usize, const RX_QUEUE: usize, const TX_QUEUE: usize>
St67w611Device<'d, MTU, RX_QUEUE, TX_QUEUE>
{
fn new(state: &'d State<MTU, RX_QUEUE, TX_QUEUE>) -> Self {
Self { state }
}
}
pub struct St67w611Runner<
'd,
SPI,
CS,
const MTU: usize,
const RX_QUEUE: usize,
const TX_QUEUE: usize,
> where
SPI: embedded_hal_async::spi::SpiBus,
CS: embedded_hal::digital::OutputPin,
{
state: &'d State<MTU, RX_QUEUE, TX_QUEUE>,
transport: St67w611Transport<SPI, CS>,
}
pub struct St67w611Transport<SPI, CS>
where
SPI: embedded_hal_async::spi::SpiBus,
CS: embedded_hal::digital::OutputPin,
{
spi: SPI,
cs: CS,
}
impl<SPI, CS> St67w611Transport<SPI, CS>
where
SPI: embedded_hal_async::spi::SpiBus,
CS: embedded_hal::digital::OutputPin,
{
pub fn new(spi: SPI, cs: CS) -> Self {
Self { spi, cs }
}
pub async fn send_frame(&mut self, traffic_type: TrafficType, data: &[u8]) -> Result<()> {
let padded_len = (data.len() + 3) & !3;
let header = SpiFrameHeader::new(traffic_type, padded_len as u16);
let header_bytes = header.to_bytes();
let mut frame = [0u8; 8 + MTU + 4]; frame[..8].copy_from_slice(&header_bytes);
frame[8..8 + data.len()].copy_from_slice(data);
for i in data.len()..padded_len {
frame[8 + i] = 0x88;
}
let total_len = 8 + padded_len;
self.cs.set_high().map_err(|_| Error::Spi)?;
Timer::after(Duration::from_micros(10)).await;
self.spi
.write(&frame[..total_len])
.await
.map_err(|_| Error::Spi)?;
Timer::after(Duration::from_micros(10)).await;
self.cs.set_low().map_err(|_| Error::Spi)?;
Ok(())
}
pub async fn receive_frame(&mut self, buffer: &mut [u8]) -> Result<(TrafficType, usize)> {
self.cs.set_high().map_err(|_| Error::Spi)?;
Timer::after(Duration::from_micros(10)).await;
let mut header_bytes = [0u8; 8];
self.spi
.read(&mut header_bytes)
.await
.map_err(|_| Error::Spi)?;
let header = SpiFrameHeader::from_bytes(&header_bytes).ok_or(Error::InvalidResponse)?;
if !header.is_valid() {
self.cs.set_low().map_err(|_| Error::Spi)?;
return Err(Error::InvalidResponse);
}
let payload_len = header.length as usize;
if payload_len == 0 {
self.cs.set_low().map_err(|_| Error::Spi)?;
return Ok((header.traffic_type, 0));
}
if payload_len > buffer.len() {
self.cs.set_low().map_err(|_| Error::Spi)?;
return Err(Error::BufferTooSmall);
}
self.spi
.read(&mut buffer[..payload_len])
.await
.map_err(|_| Error::Spi)?;
Timer::after(Duration::from_micros(10)).await;
self.cs.set_low().map_err(|_| Error::Spi)?;
Ok((header.traffic_type, payload_len))
}
}
impl<'d, SPI, CS, const MTU: usize, const RX_QUEUE: usize, const TX_QUEUE: usize>
St67w611Runner<'d, SPI, CS, MTU, RX_QUEUE, TX_QUEUE>
where
SPI: embedded_hal_async::spi::SpiBus,
CS: embedded_hal::digital::OutputPin,
{
pub async fn run(mut self) -> ! {
loop {
if let Ok(packet) = self.state.tx_channel.try_receive() {
if let Err(_e) = self
.transport
.send_frame(TrafficType::NetworkSta, &packet.buf[..packet.len])
.await
{
#[cfg(feature = "defmt")]
defmt::warn!("Failed to send frame: {:?}", _e);
}
}
let mut rx_buf = [0u8; MTU];
match self.transport.receive_frame(&mut rx_buf).await {
Ok((TrafficType::NetworkSta, len)) if len > 0 => {
let mut packet = PacketBuf::<MTU>::new();
packet.buf[..len].copy_from_slice(&rx_buf[..len]);
packet.len = len;
if self.state.rx_channel.try_send(packet).is_err() {
#[cfg(feature = "defmt")]
defmt::warn!("RX queue full, dropping frame");
}
}
Ok((TrafficType::NetworkAp, len)) if len > 0 => {
#[cfg(feature = "defmt")]
defmt::trace!("Received AP frame: {} bytes", len);
}
Ok(_) => {
}
Err(_e) => {
#[cfg(feature = "defmt")]
defmt::trace!("RX error: {:?}", _e);
}
}
Timer::after(Duration::from_micros(100)).await;
}
}
}
pub fn new_driver<'d, SPI, CS, const MTU: usize, const RX_QUEUE: usize, const TX_QUEUE: usize>(
spi: SPI,
cs: CS,
state: &'d State<MTU, RX_QUEUE, TX_QUEUE>,
) -> (
St67w611Device<'d, MTU, RX_QUEUE, TX_QUEUE>,
St67w611Runner<'d, SPI, CS, MTU, RX_QUEUE, TX_QUEUE>,
)
where
SPI: embedded_hal_async::spi::SpiBus,
CS: embedded_hal::digital::OutputPin,
{
let device = St67w611Device::new(state);
let runner = St67w611Runner {
state,
transport: St67w611Transport::new(spi, cs),
};
(device, runner)
}
#[derive(Debug, Clone, Copy)]
pub struct Capabilities {
pub max_transmission_unit: usize,
pub medium: Medium,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Medium {
Ethernet,
}
pub struct RxToken<'d, const MTU: usize, const RX_QUEUE: usize, const TX_QUEUE: usize> {
state: &'d State<MTU, RX_QUEUE, TX_QUEUE>,
packet: PacketBuf<MTU>,
}
pub struct TxToken<'d, const MTU: usize, const RX_QUEUE: usize, const TX_QUEUE: usize> {
state: &'d State<MTU, RX_QUEUE, TX_QUEUE>,
}
impl<'d, const MTU: usize, const RX_QUEUE: usize, const TX_QUEUE: usize>
St67w611Device<'d, MTU, RX_QUEUE, TX_QUEUE>
{
pub fn capabilities(&self) -> Capabilities {
Capabilities {
max_transmission_unit: MTU,
medium: Medium::Ethernet,
}
}
pub fn link_state(&self) -> bool {
self.state.link_state.signaled()
}
pub fn set_link_state(&self, up: bool) {
self.state.link_state.signal(up);
}
pub fn receive(
&self,
) -> Option<(
RxToken<'d, MTU, RX_QUEUE, TX_QUEUE>,
TxToken<'d, MTU, RX_QUEUE, TX_QUEUE>,
)> {
match self.state.rx_channel.try_receive() {
Ok(packet) => Some((
RxToken {
state: self.state,
packet,
},
TxToken { state: self.state },
)),
Err(_) => None,
}
}
pub fn transmit(&self) -> Option<TxToken<'d, MTU, RX_QUEUE, TX_QUEUE>> {
if !self.state.tx_channel.is_full() {
Some(TxToken { state: self.state })
} else {
None
}
}
pub fn hardware_address(&self) -> [u8; 6] {
[0x00, 0x80, 0xE1, 0x00, 0x00, 0x00]
}
}
impl<'d, const MTU: usize, const RX_QUEUE: usize, const TX_QUEUE: usize>
RxToken<'d, MTU, RX_QUEUE, TX_QUEUE>
{
pub fn consume<R, F: FnOnce(&mut [u8]) -> R>(self, f: F) -> R {
let mut packet = self.packet;
f(&mut packet.buf[..packet.len])
}
}
impl<'d, const MTU: usize, const RX_QUEUE: usize, const TX_QUEUE: usize>
TxToken<'d, MTU, RX_QUEUE, TX_QUEUE>
{
pub fn consume<R, F: FnOnce(&mut [u8]) -> R>(self, len: usize, f: F) -> R {
let mut packet = PacketBuf::<MTU>::new();
packet.len = len.min(MTU);
let result = f(&mut packet.buf[..packet.len]);
let _ = self.state.tx_channel.try_send(packet);
result
}
}
use embassy_net_driver::{
Capabilities as DriverCapabilities, Driver, HardwareAddress, LinkState,
RxToken as DriverRxToken, TxToken as DriverTxToken,
};
impl<'d, const MTU_SIZE: usize, const RX_QUEUE: usize, const TX_QUEUE: usize> Driver
for St67w611Device<'d, MTU_SIZE, RX_QUEUE, TX_QUEUE>
{
type RxToken<'a>
= RxToken<'a, MTU_SIZE, RX_QUEUE, TX_QUEUE>
where
Self: 'a;
type TxToken<'a>
= TxToken<'a, MTU_SIZE, RX_QUEUE, TX_QUEUE>
where
Self: 'a;
fn receive(
&mut self,
_cx: &mut core::task::Context,
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
St67w611Device::receive(self)
}
fn transmit(&mut self, _cx: &mut core::task::Context) -> Option<Self::TxToken<'_>> {
St67w611Device::transmit(self)
}
fn link_state(&mut self, _cx: &mut core::task::Context) -> LinkState {
if St67w611Device::link_state(self) {
LinkState::Up
} else {
LinkState::Down
}
}
fn capabilities(&self) -> DriverCapabilities {
let mut caps = DriverCapabilities::default();
caps.max_transmission_unit = MTU_SIZE;
caps
}
fn hardware_address(&self) -> HardwareAddress {
HardwareAddress::Ethernet(St67w611Device::hardware_address(self))
}
}
impl<'d, const MTU_SIZE: usize, const RX_QUEUE: usize, const TX_QUEUE: usize> DriverRxToken
for RxToken<'d, MTU_SIZE, RX_QUEUE, TX_QUEUE>
{
fn consume<R, F: FnOnce(&mut [u8]) -> R>(self, f: F) -> R {
RxToken::consume(self, f)
}
}
impl<'d, const MTU_SIZE: usize, const RX_QUEUE: usize, const TX_QUEUE: usize> DriverTxToken
for TxToken<'d, MTU_SIZE, RX_QUEUE, TX_QUEUE>
{
fn consume<R, F: FnOnce(&mut [u8]) -> R>(self, len: usize, f: F) -> R {
TxToken::consume(self, len, f)
}
}