kirdy/src/network/network.rs

297 lines
9.0 KiB
Rust

// 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<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()).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::<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());
}
}
}
}