forked from renet/ENC424J600
Compare commits
4 Commits
b8094f84f3
...
e547b7405c
Author | SHA1 | Date |
---|---|---|
occheung | e547b7405c | |
occheung | 1d6b32c320 | |
occheung | 0df7680d60 | |
occheung | 2a12d856b9 |
|
@ -13,7 +13,7 @@ license = "BSD-2-Clause"
|
||||||
volatile-register = "0.2"
|
volatile-register = "0.2"
|
||||||
aligned = "0.3"
|
aligned = "0.3"
|
||||||
embedded-hal = "0.2"
|
embedded-hal = "0.2"
|
||||||
smoltcp = { version = "0.6.0", default-features = false, features = ["proto-ipv4", "proto-ipv6", "socket-icmp", "socket-udp", "socket-tcp", "log", "verbose", "ethernet"], optional = true }
|
smoltcp = { version = "0.7.0", default-features = false, features = ["proto-ipv4", "proto-ipv6", "socket-icmp", "socket-udp", "socket-tcp", "log", "verbose", "ethernet"], optional = true }
|
||||||
# Optional dependencies for building examples
|
# Optional dependencies for building examples
|
||||||
stm32f4xx-hal = { version = "0.8", optional = true }
|
stm32f4xx-hal = { version = "0.8", optional = true }
|
||||||
cortex-m = { version = "0.5", optional = true }
|
cortex-m = { version = "0.5", optional = true }
|
||||||
|
@ -21,6 +21,9 @@ cortex-m-rt = { version = "0.6", optional = true }
|
||||||
cortex-m-rtic = { version = "0.5.3", optional = true }
|
cortex-m-rtic = { version = "0.5.3", optional = true }
|
||||||
panic-itm = { version = "0.4", optional = true }
|
panic-itm = { version = "0.4", optional = true }
|
||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
|
# Optional dependencies for using NAL with somltcp and cortex_m
|
||||||
|
heapless = { version = "0.5.6", optional = true }
|
||||||
|
embedded-nal = { version = "0.1.0", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
smoltcp-phy = ["smoltcp"]
|
smoltcp-phy = ["smoltcp"]
|
||||||
|
@ -31,6 +34,7 @@ smoltcp-phy-all = [
|
||||||
# Example-based features
|
# Example-based features
|
||||||
tx_stm32f407 = ["stm32f4xx-hal/stm32f407", "cortex-m", "cortex-m-rtic", "panic-itm", "log"]
|
tx_stm32f407 = ["stm32f4xx-hal/stm32f407", "cortex-m", "cortex-m-rtic", "panic-itm", "log"]
|
||||||
tcp_stm32f407 = ["stm32f4xx-hal/stm32f407", "cortex-m", "cortex-m-rt", "cortex-m-rtic", "smoltcp-phy-all", "smoltcp/log", "panic-itm", "log"]
|
tcp_stm32f407 = ["stm32f4xx-hal/stm32f407", "cortex-m", "cortex-m-rt", "cortex-m-rtic", "smoltcp-phy-all", "smoltcp/log", "panic-itm", "log"]
|
||||||
|
nal = [ "smoltcp-phy", "heapless", "embedded-nal", "cortex-m", "cortex-m-rtic" ]
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
|
|
|
@ -104,8 +104,6 @@ static mut NET_STORE: NetStorage = NetStorage {
|
||||||
const APP: () = {
|
const APP: () = {
|
||||||
struct Resources {
|
struct Resources {
|
||||||
eth_iface: EthernetInterface<
|
eth_iface: EthernetInterface<
|
||||||
'static,
|
|
||||||
'static,
|
|
||||||
'static,
|
'static,
|
||||||
smoltcp_phy::SmoltcpDevice<BoosterSpiEth>>,
|
smoltcp_phy::SmoltcpDevice<BoosterSpiEth>>,
|
||||||
itm: ITM
|
itm: ITM
|
||||||
|
|
|
@ -15,6 +15,9 @@ pub mod tx;
|
||||||
#[cfg(feature="smoltcp")]
|
#[cfg(feature="smoltcp")]
|
||||||
pub mod smoltcp_phy;
|
pub mod smoltcp_phy;
|
||||||
|
|
||||||
|
#[cfg(all(feature="smoltcp", feature="nal"))]
|
||||||
|
pub mod nal;
|
||||||
|
|
||||||
/// Max raw frame array size
|
/// Max raw frame array size
|
||||||
pub const RAW_FRAME_LENGTH_MAX: usize = 1518;
|
pub const RAW_FRAME_LENGTH_MAX: usize = 1518;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use heapless::{consts, Vec};
|
||||||
|
use embedded_nal as nal;
|
||||||
|
use nal::nb;
|
||||||
|
use smoltcp as net;
|
||||||
|
use embedded_hal::{
|
||||||
|
blocking::spi::Transfer,
|
||||||
|
blocking::delay::DelayUs,
|
||||||
|
digital::v2::OutputPin
|
||||||
|
};
|
||||||
|
use rtic::cyccnt::Instant;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum NetworkError {
|
||||||
|
NoSocket,
|
||||||
|
ConnectionFailure,
|
||||||
|
ReadFailure,
|
||||||
|
WriteFailure,
|
||||||
|
Unsupported,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type NetworkInterface<SPI, NSS, Delay> = net::iface::EthernetInterface<
|
||||||
|
'static,
|
||||||
|
crate::smoltcp_phy::SmoltcpDevice<
|
||||||
|
crate::SpiEth<SPI, NSS, Delay>
|
||||||
|
>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
pub struct NetworkStack<'a, SPI, NSS, Delay>
|
||||||
|
where
|
||||||
|
SPI: 'static + Transfer<u8>,
|
||||||
|
NSS: 'static + OutputPin,
|
||||||
|
Delay: 'static + DelayUs<u16>
|
||||||
|
{
|
||||||
|
network_interface: RefCell<NetworkInterface<SPI, NSS, Delay>>,
|
||||||
|
sockets: RefCell<net::socket::SocketSet<'a>>,
|
||||||
|
next_port: RefCell<u16>,
|
||||||
|
unused_handles: RefCell<Vec<net::socket::SocketHandle, consts::U16>>,
|
||||||
|
time_ms: RefCell<u32>,
|
||||||
|
last_update_instant: RefCell<Option<Instant>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, SPI, NSS, Delay> NetworkStack<'a, SPI, NSS, Delay>
|
||||||
|
where
|
||||||
|
SPI: Transfer<u8>,
|
||||||
|
NSS: OutputPin,
|
||||||
|
Delay: DelayUs<u16>
|
||||||
|
{
|
||||||
|
pub fn new(interface: NetworkInterface<SPI, NSS, Delay>, sockets: net::socket::SocketSet<'a>) -> Self {
|
||||||
|
let mut unused_handles: Vec<net::socket::SocketHandle, consts::U16> = Vec::new();
|
||||||
|
for socket in sockets.iter() {
|
||||||
|
unused_handles.push(socket.handle()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkStack {
|
||||||
|
network_interface: RefCell::new(interface),
|
||||||
|
sockets: RefCell::new(sockets),
|
||||||
|
next_port: RefCell::new(49152),
|
||||||
|
unused_handles: RefCell::new(unused_handles),
|
||||||
|
time_ms: RefCell::new(0),
|
||||||
|
last_update_instant: RefCell::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include auto_time_update to allow Instant::now() to not be called
|
||||||
|
// Instant::now() is not safe to call in `init()` context
|
||||||
|
pub fn update(&self, auto_time_update: bool) -> bool {
|
||||||
|
if auto_time_update {
|
||||||
|
// Check if it is the first time the stack has updated the time itself
|
||||||
|
let now = match *self.last_update_instant.borrow() {
|
||||||
|
// If it is the first time, do not advance time
|
||||||
|
// Simply store the current instant to initiate time updating
|
||||||
|
None => Instant::now(),
|
||||||
|
|
||||||
|
// If it was updated before, advance time and update last_update_instant
|
||||||
|
Some(instant) => {
|
||||||
|
// Calculate elapsed time
|
||||||
|
let now = Instant::now();
|
||||||
|
let duration = now.duration_since(instant);
|
||||||
|
// Adjust duration into ms (note: decimal point truncated)
|
||||||
|
self.advance_time(duration.as_cycles() / 168_000);
|
||||||
|
now
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.last_update_instant.replace(Some(now));
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.network_interface.borrow_mut().poll(
|
||||||
|
&mut self.sockets.borrow_mut(),
|
||||||
|
net::time::Instant::from_millis(*self.time_ms.borrow() as i64),
|
||||||
|
) {
|
||||||
|
Ok(changed) => changed == false,
|
||||||
|
Err(_e) => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn advance_time(&self, duration: u32) {
|
||||||
|
*self.time_ms.borrow_mut() += duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_ephemeral_port(&self) -> u16 {
|
||||||
|
// Get the next ephemeral port
|
||||||
|
let current_port = self.next_port.borrow().clone();
|
||||||
|
|
||||||
|
let (next, wrap) = self.next_port.borrow().overflowing_add(1);
|
||||||
|
*self.next_port.borrow_mut() = if wrap { 49152 } else { next };
|
||||||
|
|
||||||
|
return current_port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, SPI, NSS, Delay> nal::TcpStack for NetworkStack<'a, SPI, NSS, Delay>
|
||||||
|
where
|
||||||
|
SPI: Transfer<u8>,
|
||||||
|
NSS: OutputPin,
|
||||||
|
Delay: DelayUs<u16>
|
||||||
|
{
|
||||||
|
type TcpSocket = net::socket::SocketHandle;
|
||||||
|
type Error = NetworkError;
|
||||||
|
|
||||||
|
fn open(&self, _mode: nal::Mode) -> Result<Self::TcpSocket, Self::Error> {
|
||||||
|
match self.unused_handles.borrow_mut().pop() {
|
||||||
|
Some(handle) => {
|
||||||
|
// Abort any active connections on the handle.
|
||||||
|
let mut sockets = self.sockets.borrow_mut();
|
||||||
|
let internal_socket: &mut net::socket::TcpSocket = &mut *sockets.get(handle);
|
||||||
|
internal_socket.abort();
|
||||||
|
|
||||||
|
Ok(handle)
|
||||||
|
}
|
||||||
|
None => Err(NetworkError::NoSocket),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ideally connect is only to be performed in `init()` of `main.rs`
|
||||||
|
// Calling `Instant::now()` of `rtic::cyccnt` would face correctness issue during `init()`
|
||||||
|
fn connect(
|
||||||
|
&self,
|
||||||
|
socket: Self::TcpSocket,
|
||||||
|
remote: nal::SocketAddr,
|
||||||
|
) -> Result<Self::TcpSocket, Self::Error> {
|
||||||
|
let address = {
|
||||||
|
let mut sockets = self.sockets.borrow_mut();
|
||||||
|
let internal_socket: &mut net::socket::TcpSocket = &mut *sockets.get(socket);
|
||||||
|
|
||||||
|
// If we're already in the process of connecting, ignore the request silently.
|
||||||
|
if internal_socket.is_open() {
|
||||||
|
return Ok(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
match remote.ip() {
|
||||||
|
nal::IpAddr::V4(addr) => {
|
||||||
|
let octets = addr.octets();
|
||||||
|
let address =
|
||||||
|
net::wire::Ipv4Address::new(octets[0], octets[1], octets[2], octets[3]);
|
||||||
|
internal_socket
|
||||||
|
.connect((address, remote.port()), self.get_ephemeral_port())
|
||||||
|
.map_err(|_| NetworkError::ConnectionFailure)?;
|
||||||
|
address
|
||||||
|
}
|
||||||
|
nal::IpAddr::V6(_) => {
|
||||||
|
// Match W5500 behavior: Reject the use of IPV6
|
||||||
|
return Err(NetworkError::Unsupported);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Match W5500 behavior: Poll until connected
|
||||||
|
loop {
|
||||||
|
match self.is_connected(&socket) {
|
||||||
|
Ok(true) => break,
|
||||||
|
_ => {
|
||||||
|
let mut sockets = self.sockets.borrow_mut();
|
||||||
|
let internal_socket: &mut net::socket::TcpSocket = &mut *sockets.get(socket);
|
||||||
|
// If the connect got ACK->RST, it will end up in Closed TCP state
|
||||||
|
// Perform reconnection in this case
|
||||||
|
// In all other scenario, simply wait for TCP connection to be established
|
||||||
|
if internal_socket.state() == net::socket::TcpState::Closed {
|
||||||
|
internal_socket.close();
|
||||||
|
internal_socket
|
||||||
|
.connect((address, remote.port()), self.get_ephemeral_port())
|
||||||
|
.map_err(|_| NetworkError::ConnectionFailure)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid using Instant::now() and Advance time manually
|
||||||
|
self.update(false);
|
||||||
|
|
||||||
|
// Delay for 1 ms, minimum time unit of smoltcp
|
||||||
|
// TODO: Allow clock configuration, if supported in main
|
||||||
|
cortex_m::asm::delay(168_000_000 / 1_000);
|
||||||
|
{
|
||||||
|
self.advance_time(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(socket)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_connected(&self, socket: &Self::TcpSocket) -> Result<bool, Self::Error> {
|
||||||
|
let mut sockets = self.sockets.borrow_mut();
|
||||||
|
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(*socket);
|
||||||
|
|
||||||
|
Ok(socket.may_send() && socket.may_recv())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&self, socket: &mut Self::TcpSocket, buffer: &[u8]) -> nb::Result<usize, Self::Error> {
|
||||||
|
let mut non_queued_bytes = &buffer[..];
|
||||||
|
while non_queued_bytes.len() != 0 {
|
||||||
|
let result = {
|
||||||
|
let mut sockets = self.sockets.borrow_mut();
|
||||||
|
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(*socket);
|
||||||
|
let result = socket.send_slice(non_queued_bytes);
|
||||||
|
result
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(num_bytes) => {
|
||||||
|
// In case the buffer is filled up, push bytes into ethernet driver
|
||||||
|
if num_bytes != non_queued_bytes.len() {
|
||||||
|
self.update(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the unwritten bytes again, if any
|
||||||
|
non_queued_bytes = &non_queued_bytes[num_bytes..]
|
||||||
|
}
|
||||||
|
Err(_) => return Err(nb::Error::Other(NetworkError::WriteFailure)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(buffer.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(
|
||||||
|
&self,
|
||||||
|
socket: &mut Self::TcpSocket,
|
||||||
|
buffer: &mut [u8],
|
||||||
|
) -> nb::Result<usize, Self::Error> {
|
||||||
|
// Enqueue received bytes into the TCP socket buffer
|
||||||
|
self.update(true);
|
||||||
|
|
||||||
|
let mut sockets = self.sockets.borrow_mut();
|
||||||
|
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(*socket);
|
||||||
|
|
||||||
|
let result = socket.recv_slice(buffer);
|
||||||
|
match result {
|
||||||
|
Ok(num_bytes) => Ok(num_bytes),
|
||||||
|
Err(_) => Err(nb::Error::Other(NetworkError::ReadFailure)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&self, socket: Self::TcpSocket) -> Result<(), Self::Error> {
|
||||||
|
let mut sockets = self.sockets.borrow_mut();
|
||||||
|
let internal_socket: &mut net::socket::TcpSocket = &mut *sockets.get(socket);
|
||||||
|
internal_socket.close();
|
||||||
|
|
||||||
|
self.unused_handles.borrow_mut().push(socket).unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue