// Modified by Wong Tat Hang (aw@m-labs.hk), with original source from // https://github.com/stm32-rs/stm32-eth/blob/master/examples/rtic-echo.rs // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use log::{info, warn}; use smoltcp::{ iface::{ Interface, InterfaceBuilder, Neighbor, NeighborCache, Route, SocketHandle, SocketStorage, }, socket::{TcpSocket, TcpSocketBuffer, TcpState}, wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}, }; use stm32_eth::stm32::{ETHERNET_DMA, ETHERNET_MAC, ETHERNET_MMC}; use stm32_eth::*; use stm32f4xx_hal::{ gpio::{gpioa::*, gpiob::*, gpioc::*, Alternate, Input}, rcc::Clocks, }; use crate::app::monotonics; pub type EthernetPins = EthPins, PA7, PB11, PB12, PB13, PC4, PC5>; 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>, } 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>, mdc: stm32f4xx_hal::gpio::PC1>, ) -> 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::(server.socket_handle); socket.listen(ADDRESS).ok(); server.iface.poll(now_fn()).ok(); 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]) { self.iface.poll(now_fn()).ok(); let socket = self.iface.get_socket::(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 { /// LAN8720A LAN8720A(LAN8720A), /// LAN8742A LAN8742A(LAN8742A), /// KSZ8081R KSZ8081R(KSZ8081R), } impl Phy for EthernetPhy { 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 EthernetPhy { /// 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 { 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()); } } } }