#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
mod hostname;
pub mod io;
mod tcp;
mod udp;
pub use hostname::{Hostname, TryFromStrError};
pub use ll::net;
use ll::{Registers, Sn, SocketCommand, SocketStatus, SOCKETS};
pub use tcp::{Tcp, TcpReader, TcpWriter};
pub use udp::{Udp, UdpHeader, UdpReader, UdpWriter};
pub use w5500_ll as ll;
use net::{Ipv4Addr, SocketAddrV4};
fn port_is_unique<T: ?Sized, E>(w5500: &mut T, socket: Sn, port: u16) -> Result<bool, E>
where
T: Registers<Error = E>,
{
const CLOSED_STATUS: [Result<SocketStatus, u8>; 3] = [
Ok(SocketStatus::Closed),
Ok(SocketStatus::CloseWait),
Ok(SocketStatus::Closing),
];
for socket in SOCKETS.iter().filter(|s| s != &&socket) {
if w5500.sn_port(*socket)? == port {
let status = w5500.sn_sr(*socket)?;
if !CLOSED_STATUS.iter().any(|x| x == &status) {
return Ok(false);
}
}
}
Ok(true)
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error<E> {
UnexpectedEof,
OutOfMemory,
WouldBlock,
Other(E),
}
impl<E> From<E> for Error<E> {
fn from(error: E) -> Error<E> {
Error::Other(error)
}
}
#[macro_export]
macro_rules! block {
($e:expr) => {
loop {
#[allow(unreachable_patterns)]
match $e {
Err($crate::Error::WouldBlock) => {}
Err(e) => break Err(e),
Ok(x) => break Ok(x),
}
}
};
}
pub trait Common: Registers {
fn local_addr(&mut self, sn: Sn) -> Result<SocketAddrV4, Self::Error> {
let ip: Ipv4Addr = self.sipr()?;
let port: u16 = self.sn_port(sn)?;
Ok(SocketAddrV4::new(ip, port))
}
fn close(&mut self, sn: Sn) -> Result<(), Self::Error> {
self.set_sn_cr(sn, SocketCommand::Close)
}
#[allow(clippy::wrong_self_convention)]
fn is_state_closed(&mut self, sn: Sn) -> Result<bool, Self::Error> {
Ok(self.sn_sr(sn)? == Ok(SocketStatus::Closed))
}
#[allow(clippy::wrong_self_convention)]
fn is_state_tcp(&mut self, sn: Sn) -> Result<bool, Self::Error> {
Ok(matches!(
self.sn_sr(sn)?,
Ok(SocketStatus::Closed)
| Ok(SocketStatus::Listen)
| Ok(SocketStatus::SynSent)
| Ok(SocketStatus::SynRecv)
| Ok(SocketStatus::Established)
| Ok(SocketStatus::FinWait)
| Ok(SocketStatus::Closing)
| Ok(SocketStatus::CloseWait)
| Ok(SocketStatus::TimeWait)
| Ok(SocketStatus::LastAck)
))
}
#[allow(clippy::wrong_self_convention)]
fn is_state_udp(&mut self, sn: Sn) -> Result<bool, Self::Error> {
Ok(self.sn_sr(sn)? == Ok(SocketStatus::Udp))
}
}
impl<T> Common for T where T: Registers {}
#[cfg(test)]
mod tests {
use core::convert::Infallible;
use super::*;
struct MockRegisters {
pub socket_ports: [u16; SOCKETS.len()],
pub socket_status: [SocketStatus; SOCKETS.len()],
}
impl Registers for MockRegisters {
type Error = Infallible;
fn read(&mut self, _address: u16, _block: u8, _data: &mut [u8]) -> Result<(), Self::Error> {
unimplemented!()
}
fn write(&mut self, _address: u16, _block: u8, _data: &[u8]) -> Result<(), Self::Error> {
unimplemented!()
}
fn sn_port(&mut self, socket: Sn) -> Result<u16, Self::Error> {
Ok(self.socket_ports[usize::from(socket)])
}
fn sn_sr(&mut self, socket: Sn) -> Result<Result<SocketStatus, u8>, Self::Error> {
Ok(Ok(self.socket_status[usize::from(socket)]))
}
}
#[test]
fn test_port_is_unique() {
let mut mock = MockRegisters {
socket_ports: [0; SOCKETS.len()],
socket_status: [SocketStatus::Closed; SOCKETS.len()],
};
assert!(port_is_unique(&mut mock, Sn::Sn0, 0).unwrap());
assert!(port_is_unique(&mut mock, Sn::Sn0, 1).unwrap());
assert!(port_is_unique(&mut mock, Sn::Sn0, u16::MAX).unwrap());
mock.socket_status[0] = SocketStatus::Init;
assert!(port_is_unique(&mut mock, Sn::Sn0, 0).unwrap());
assert!(port_is_unique(&mut mock, Sn::Sn0, 1).unwrap());
assert!(!port_is_unique(&mut mock, Sn::Sn1, 0).unwrap());
}
}