w5100-rs/src/tcp.rs
Pascal Engélibert 62e56052cf
Some checks failed
Rust / Check (push) Has been cancelled
Rust / Test Suite (push) Has been cancelled
Rust / Rustfmt (push) Has been cancelled
Rust / Clippy (push) Has been cancelled
Adapt UDP to W5100
2025-11-11 14:40:25 +01:00

260 lines
8.3 KiB
Rust

use core::{
convert::TryFrom,
net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4},
};
use embedded_nal::{nb, TcpClientStack, TcpError, TcpErrorKind};
use crate::{
bus::Bus,
device::{Device, State},
register::socketn,
socket::Socket,
};
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TcpSocketError<E: core::fmt::Debug> {
NoMoreSockets,
NotConnected,
UnsupportedAddress,
Other(#[cfg_attr(feature = "defmt", defmt(Debug2Format))] E),
UnsupportedMode,
}
impl<E: core::fmt::Debug> TcpError for TcpSocketError<E> {
fn kind(&self) -> TcpErrorKind {
match self {
TcpSocketError::NotConnected => TcpErrorKind::PipeClosed,
_ => TcpErrorKind::Other,
}
}
}
impl<E: core::fmt::Debug> From<E> for TcpSocketError<E> {
fn from(e: E) -> Self {
TcpSocketError::Other(e)
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TcpSocket {
socket: Socket,
}
impl TcpSocket {
fn reopen<B: Bus>(&mut self, bus: &mut B) -> Result<(), TcpSocketError<B::Error>> {
self.socket.command(bus, socketn::Command::Close)?;
self.socket.reset_interrupt(bus, socketn::Interrupt::All)?;
self.socket.set_mode(bus, socketn::Protocol::Tcp)?;
self.socket.enable_interrupts(bus)?;
self.socket.command(bus, socketn::Command::Open)?;
Ok(())
}
fn open<B: Bus>(
&mut self,
bus: &mut B,
local_port: u16,
) -> Result<(), TcpSocketError<B::Error>> {
self.socket.command(bus, socketn::Command::Close)?;
self.socket.reset_interrupt(bus, socketn::Interrupt::All)?;
self.socket.set_source_port(bus, local_port)?;
self.socket.set_mode(bus, socketn::Protocol::Tcp)?;
self.socket.enable_interrupts(bus)?;
self.socket.command(bus, socketn::Command::Open)?;
Ok(())
}
fn socket_close<B: Bus>(&self, bus: &mut B) -> Result<(), TcpSocketError<B::Error>> {
self.socket.set_mode(bus, socketn::Protocol::Closed)?;
self.socket.command(bus, socketn::Command::Close)?;
Ok(())
}
fn socket_connect<B: Bus>(
&mut self,
bus: &mut B,
remote: SocketAddrV4,
) -> Result<(), TcpSocketError<B::Error>> {
// Ensure the socket is open and ready before we attempt to connect it.
match socketn::Status::try_from(self.socket.get_status(bus)?) {
// Happy case: Nothing to do.
Ok(socketn::Status::Init) => {}
// If the socket is in the wrong mode, we can't use it. The user needs to re-open it.
Err(_) | Ok(socketn::Status::MacRaw) | Ok(socketn::Status::Udp) => {
return Err(TcpSocketError::UnsupportedMode)
}
// All other cases are transient TCP states. For these, we need to reset the TCP
// machinery to return to the INIT state.
Ok(_) => {
self.socket_close(bus)?;
self.reopen(bus)?;
}
}
// Write the remote port and IP
self.socket.set_destination_ip(bus, *remote.ip())?;
self.socket.set_destination_port(bus, remote.port())?;
// Connect the socket.
self.socket.command(bus, socketn::Command::Connect)?;
// Wait for the socket to connect or encounter an error.
loop {
match socketn::Status::try_from(self.socket.get_status(bus)?) {
Ok(socketn::Status::Established) => return Ok(()),
// The socket is closed if a timeout (ARP or SYN-ACK) or if the TCP socket receives
// a RST packet. In this case, the client will need to re-attempt to connect.
// TODO: Due to limitations of the embedded-nal, we currently still return the
// socket (since we cannot inform the user of the connection failure). The returned
// socket will not actually be connected.
Ok(socketn::Status::Closed) => {
// For now, always return an open socket so that the user can re-connect with
// it in the future.
self.socket_close(bus)?;
return self.reopen(bus);
}
// The socket is still in some transient state. Wait for it to connect or for the
// connection to fail.
_ => {}
}
}
}
fn socket_is_connected<B: Bus>(&self, bus: &mut B) -> Result<bool, TcpSocketError<B::Error>> {
Ok(self.socket.get_status(bus)? == socketn::Status::Established as u8)
}
fn socket_send<B: Bus>(
&mut self,
bus: &mut B,
data: &[u8],
) -> Result<usize, TcpSocketError<B::Error>> {
if !self.socket_is_connected(bus)? {
return Err(TcpSocketError::NotConnected);
}
let max_size = self.socket.get_tx_free_size(bus)? as usize;
let write_data = if data.len() < max_size {
data
} else {
&data[..max_size]
};
// Append the data to the write buffer after the current write pointer.
let write_pointer = self.socket.get_tx_write_pointer(bus)?;
// Write data into the buffer and update the writer pointer.
bus.write_frame(write_pointer, write_data)?;
self.socket
.set_tx_write_pointer(bus, write_pointer.wrapping_add(write_data.len() as u16))?;
// Send the data.
self.socket.command(bus, socketn::Command::Send)?;
// Wait until the send command completes.
while !self.socket.has_interrupt(bus, socketn::Interrupt::SendOk)? {}
self.socket
.reset_interrupt(bus, socketn::Interrupt::SendOk)?;
Ok(write_data.len())
}
fn socket_receive<B: Bus>(
&mut self,
bus: &mut B,
data: &mut [u8],
) -> Result<usize, TcpSocketError<B::Error>> {
if !self.socket_is_connected(bus)? {
return Err(TcpSocketError::NotConnected);
}
// Check if we've received data.
if !self
.socket
.has_interrupt(bus, socketn::Interrupt::Receive)?
{
return Ok(0);
}
let rx_size = self.socket.get_receive_size(bus)? as usize;
let read_buffer = if rx_size > data.len() {
data
} else {
&mut data[..rx_size]
};
// Read from the RX ring buffer.
let read_pointer = self.socket.get_rx_read_pointer(bus)?;
bus.read_frame(read_pointer, read_buffer)?;
self.socket
.set_rx_read_pointer(bus, read_pointer.wrapping_add(read_buffer.len() as u16))?;
// Register the reception as complete.
self.socket.command(bus, socketn::Command::Receive)?;
self.socket
.reset_interrupt(bus, socketn::Interrupt::Receive)?;
Ok(read_buffer.len())
}
}
impl<SpiBus: Bus, StateImpl: State> TcpClientStack for Device<SpiBus, StateImpl> {
type TcpSocket = TcpSocket;
type Error = TcpSocketError<SpiBus::Error>;
fn socket(&mut self) -> Result<TcpSocket, Self::Error> {
match self.take_socket() {
Some(socket) => Ok(TcpSocket { socket }),
None => Err(TcpSocketError::NoMoreSockets),
}
}
fn connect(
&mut self,
socket: &mut Self::TcpSocket,
remote: SocketAddr,
) -> nb::Result<(), Self::Error> {
let SocketAddr::V4(remote) = remote else {
return Err(nb::Error::Other(Self::Error::UnsupportedAddress));
};
// TODO dynamically select a random port
socket.open(&mut self.bus, 49849 + u16::from(socket.socket.index))?; // chosen by fair dice roll.
socket.socket_connect(&mut self.bus, remote)?;
Ok(())
}
fn send(
&mut self,
socket: &mut Self::TcpSocket,
buffer: &[u8],
) -> nb::Result<usize, Self::Error> {
let len = socket.socket_send(&mut self.bus, buffer)?;
Ok(len)
}
fn receive(
&mut self,
socket: &mut Self::TcpSocket,
buffer: &mut [u8],
) -> nb::Result<usize, Self::Error> {
Ok(socket.socket_receive(&mut self.bus, buffer)?)
}
fn close(&mut self, socket: Self::TcpSocket) -> Result<(), Self::Error> {
socket.socket_close(&mut self.bus)?;
self.release_socket(socket.socket);
Ok(())
}
}