280 lines
8.7 KiB
Rust
280 lines
8.7 KiB
Rust
use fugit::Instant;
|
|
use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketHandle, Interface, SocketStorage};
|
|
use smoltcp::socket::{TcpSocket, TcpSocketBuffer, self, Socket};
|
|
use smoltcp::storage::RingBuffer;
|
|
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
|
|
use stm32f4xx_hal::gpio::{gpioa::*, gpiob::*, gpioc::*, gpiog::*, Input};
|
|
use stm32_eth::{EthPins, TxDescriptor, RxDescriptor, EthernetMAC};
|
|
use stm32_eth::stm32::{ETHERNET_DMA, ETHERNET_MAC, ETHERNET_MMC};
|
|
use stm32_eth::RingEntry;
|
|
use stm32_eth::TxRingEntry;
|
|
use stm32_eth::RxRingEntry;
|
|
use stm32f4xx_hal::rcc::Clocks;
|
|
use smoltcp::iface::Neighbor;
|
|
use stm32_eth::*;
|
|
use log::{info, warn, debug};
|
|
use smoltcp::iface::Route;
|
|
use smoltcp::wire::Ipv4Cidr;
|
|
use stm32f4xx_hal::gpio::Alternate;
|
|
use smoltcp::{
|
|
iface::{self},
|
|
socket::{TcpState},
|
|
};
|
|
|
|
|
|
use crate::app::monotonics::{self, now};
|
|
|
|
|
|
pub type EthernetPins =
|
|
EthPins<PA1<Input>, PA7<Input>, PB11<Input>, PB12<Input>, PB13<Input>, PC4<Input>, PC5<Input>>;
|
|
|
|
pub type EthInterface = Interface<'static, &'static mut EthernetDMA<'static, 'static>>;
|
|
|
|
const IPV4_ADDR: (u8, u8, u8, u8) = (192, 168, 1, 132);
|
|
const ADDRESS: (IpAddress, u16) = (IpAddress::Ipv4(Ipv4Address::new(IPV4_ADDR.0, IPV4_ADDR.1,IPV4_ADDR.2,IPV4_ADDR.3)), 1337);
|
|
const MAC: [u8; 6] = [0x02,0x5f,0x25,0x37,0x93,0x0e];
|
|
pub struct EthernetPeripherals {
|
|
pub dma: ETHERNET_DMA,
|
|
pub mac: ETHERNET_MAC,
|
|
pub mmc: ETHERNET_MMC,
|
|
}
|
|
|
|
pub struct ServerHandle {
|
|
// storage: &'static mut ServerStorage,
|
|
socket_handle: SocketHandle,
|
|
iface: EthInterface
|
|
}
|
|
|
|
pub struct ServerStorage {
|
|
rx_ring: [RxRingEntry; 8],
|
|
tx_ring: [TxRingEntry; 2],
|
|
storage: NetworkStorage,
|
|
dma: core::mem::MaybeUninit<EthernetDMA<'static, 'static>>,
|
|
}
|
|
|
|
impl ServerStorage {
|
|
pub const fn new() -> Self {
|
|
ServerStorage {
|
|
rx_ring: [RxRingEntry::new(),RxRingEntry::new(),RxRingEntry::new(),RxRingEntry::new(),RxRingEntry::new(),RxRingEntry::new(),RxRingEntry::new(),RxRingEntry::new()],
|
|
tx_ring: [TxRingEntry::new(),TxRingEntry::new()],
|
|
storage: NetworkStorage::new(),
|
|
dma: core::mem::MaybeUninit::uninit(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// All storage required for networking
|
|
pub struct NetworkStorage {
|
|
pub ip_addrs: [IpCidr; 1],
|
|
pub sockets: [SocketStorage<'static>; 1],
|
|
pub tcp_socket_storage: TcpSocketStorage,
|
|
pub neighbor_cache: [Option<(IpAddress, Neighbor)>; 8],
|
|
pub routes_cache: [Option<(IpCidr, Route)>; 8],
|
|
}
|
|
|
|
impl NetworkStorage {
|
|
const IP_INIT: IpCidr =
|
|
IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address::new(IPV4_ADDR.0, IPV4_ADDR.1,IPV4_ADDR.2,IPV4_ADDR.3), 24));
|
|
|
|
pub const fn new() -> Self {
|
|
NetworkStorage {
|
|
ip_addrs: [Self::IP_INIT],
|
|
neighbor_cache: [None; 8],
|
|
routes_cache: [None; 8],
|
|
sockets: [SocketStorage::EMPTY; 1],
|
|
tcp_socket_storage: TcpSocketStorage::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Storage of TCP sockets
|
|
#[derive(Copy, Clone)]
|
|
pub struct TcpSocketStorage {
|
|
rx_storage: [u8; 2048],
|
|
tx_storage: [u8; 2048],
|
|
}
|
|
|
|
impl TcpSocketStorage {
|
|
const fn new() -> Self {
|
|
Self {
|
|
rx_storage: [0; 2048],
|
|
tx_storage: [0; 2048],
|
|
}
|
|
}
|
|
}
|
|
|
|
fn now_fn() -> smoltcp::time::Instant {
|
|
let time = monotonics::now().duration_since_epoch().to_millis();
|
|
smoltcp::time::Instant::from_millis(time as i64)
|
|
}
|
|
|
|
impl ServerHandle {
|
|
|
|
pub fn new(
|
|
eth_pins: EthernetPins,
|
|
ethernet: EthernetPeripherals,
|
|
clocks: Clocks,
|
|
storage: &'static mut ServerStorage,
|
|
mdio: stm32f4xx_hal::gpio::PA2<Alternate<11>>,
|
|
mdc: stm32f4xx_hal::gpio::PC1<Alternate<11>>
|
|
) -> ServerHandle {
|
|
|
|
let (dma, mac) = stm32_eth::new_with_mii(
|
|
ethernet.mac,
|
|
ethernet.mmc,
|
|
ethernet.dma,
|
|
&mut storage.rx_ring[..],
|
|
&mut storage.tx_ring[..],
|
|
clocks,
|
|
eth_pins,
|
|
mdio,
|
|
mdc
|
|
)
|
|
.unwrap();
|
|
|
|
let mut routes = smoltcp::iface::Routes::new(&mut storage.storage.routes_cache[..]);
|
|
routes
|
|
.add_default_ipv4_route(Ipv4Address::new(192, 168, 1, 1))
|
|
.ok();
|
|
|
|
|
|
let dma = storage.dma.write(dma);
|
|
dma.enable_interrupt();
|
|
|
|
let rx_buffer = TcpSocketBuffer::new(&mut storage.storage.tcp_socket_storage.rx_storage[..]);
|
|
let tx_buffer = TcpSocketBuffer::new(&mut storage.storage.tcp_socket_storage.tx_storage[..]);
|
|
let socket = TcpSocket::new(rx_buffer, tx_buffer);
|
|
|
|
let mut iface = InterfaceBuilder::new(dma, &mut storage.storage.sockets[..])
|
|
.hardware_addr(EthernetAddress(MAC).into())
|
|
.ip_addrs(&mut storage.storage.ip_addrs[..])
|
|
.neighbor_cache(NeighborCache::new(&mut storage.storage.neighbor_cache[..]))
|
|
.routes(routes)
|
|
.finalize();
|
|
let mut server = ServerHandle {
|
|
socket_handle: iface.add_socket(socket),
|
|
iface: iface
|
|
};
|
|
let socket = server.iface.get_socket::<TcpSocket>(server.socket_handle);
|
|
socket.listen(ADDRESS).ok();
|
|
server.iface.poll(now_fn());
|
|
if let Ok(mut phy) = EthernetPhy::from_miim(mac, 0) {
|
|
info!(
|
|
"Resetting PHY as an extra step. Type: {}",
|
|
phy.ident_string()
|
|
);
|
|
|
|
phy.phy_init();
|
|
} else {
|
|
info!("Not resetting unsupported PHY.");
|
|
}
|
|
|
|
server
|
|
}
|
|
|
|
pub fn poll(&mut self, buffer: &mut [u8]) {
|
|
|
|
// info!("poll eth");
|
|
let interrupt_reason = self.iface.device_mut().interrupt_handler();
|
|
// debug!("Reason: {:?}", interrupt_reason);
|
|
|
|
self.iface.poll(now_fn()).ok();
|
|
|
|
let socket = self.iface.get_socket::<TcpSocket>(self.socket_handle);
|
|
if let Ok(recv_bytes) = socket.recv_slice(buffer) {
|
|
if recv_bytes > 0 {
|
|
socket.send_slice(&buffer[..recv_bytes]).ok();
|
|
info!("Echoed {} bytes.", recv_bytes);
|
|
}
|
|
}
|
|
|
|
if !socket.is_listening() && !socket.is_open() || socket.state() == TcpState::CloseWait {
|
|
socket.abort();
|
|
socket.listen(ADDRESS).ok();
|
|
warn!("Disconnected... Reopening listening socket.");
|
|
}
|
|
|
|
self.iface.poll(now_fn()).ok();
|
|
|
|
}
|
|
}
|
|
|
|
|
|
use ieee802_3_miim::{
|
|
phy::{
|
|
lan87xxa::{LAN8720A, LAN8742A},
|
|
BarePhy, KSZ8081R,
|
|
},
|
|
Miim, Pause, Phy,
|
|
};
|
|
|
|
/// An ethernet PHY
|
|
pub enum EthernetPhy<M: Miim> {
|
|
/// LAN8720A
|
|
LAN8720A(LAN8720A<M>),
|
|
/// LAN8742A
|
|
LAN8742A(LAN8742A<M>),
|
|
/// KSZ8081R
|
|
KSZ8081R(KSZ8081R<M>),
|
|
}
|
|
|
|
impl<M: Miim> Phy<M> for EthernetPhy<M> {
|
|
fn best_supported_advertisement(&self) -> ieee802_3_miim::AutoNegotiationAdvertisement {
|
|
unimplemented!()
|
|
}
|
|
|
|
fn get_miim(&mut self) -> &mut M {
|
|
match self {
|
|
EthernetPhy::LAN8720A(phy) => phy.get_miim(),
|
|
EthernetPhy::LAN8742A(phy) => phy.get_miim(),
|
|
EthernetPhy::KSZ8081R(phy) => phy.get_miim(),
|
|
}
|
|
}
|
|
|
|
fn get_phy_addr(&self) -> u8 {
|
|
match self {
|
|
EthernetPhy::LAN8720A(phy) => phy.get_phy_addr(),
|
|
EthernetPhy::LAN8742A(phy) => phy.get_phy_addr(),
|
|
EthernetPhy::KSZ8081R(phy) => phy.get_phy_addr(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<M: Miim> EthernetPhy<M> {
|
|
/// Attempt to create one of the known PHYs from the given
|
|
/// MIIM.
|
|
///
|
|
/// Returns an error if the PHY does not support the extended register
|
|
/// set, or if the PHY's identifier does not correspond to a known PHY.
|
|
pub fn from_miim(miim: M, phy_addr: u8) -> Result<Self, ()> {
|
|
let mut bare = BarePhy::new(miim, phy_addr, Pause::NoPause);
|
|
let phy_ident = bare.phy_ident().ok_or(())?;
|
|
let miim = bare.release();
|
|
match phy_ident & 0xFFFFFFF0 {
|
|
0x0007C0F0 => Ok(Self::LAN8720A(LAN8720A::new(miim, phy_addr))),
|
|
0x0007C130 => Ok(Self::LAN8742A(LAN8742A::new(miim, phy_addr))),
|
|
0x00221560 => Ok(Self::KSZ8081R(KSZ8081R::new(miim, phy_addr))),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
|
|
/// Get a string describing the type of PHY
|
|
pub const fn ident_string(&self) -> &'static str {
|
|
match self {
|
|
EthernetPhy::LAN8720A(_) => "LAN8720A",
|
|
EthernetPhy::LAN8742A(_) => "LAN8742A",
|
|
EthernetPhy::KSZ8081R(_) => "KSZ8081R",
|
|
}
|
|
}
|
|
|
|
/// Initialize the PHY
|
|
pub fn phy_init(&mut self) {
|
|
match self {
|
|
EthernetPhy::LAN8720A(phy) => phy.phy_init(),
|
|
EthernetPhy::LAN8742A(phy) => phy.phy_init(),
|
|
EthernetPhy::KSZ8081R(phy) => {
|
|
phy.set_autonegotiation_advertisement(phy.best_supported_advertisement());
|
|
}
|
|
}
|
|
}
|
|
} |