From dab73c3fec2ccbd563eb8b67bb397f4fb68eb517 Mon Sep 17 00:00:00 2001 From: topquark12 Date: Tue, 1 Nov 2022 11:16:55 +0800 Subject: [PATCH] working ethernet --- Cargo.lock | 64 ++++++++-- Cargo.toml | 5 +- src/device/boot.rs | 18 ++- src/device/gpio.rs | 17 ++- src/main.rs | 32 ++++- src/network/mod.rs | 1 + src/network/network.rs | 280 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 393 insertions(+), 24 deletions(-) create mode 100644 src/network/mod.rs create mode 100644 src/network/network.rs diff --git a/Cargo.lock b/Cargo.lock index 0b9a12a..96ca44c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,6 +224,35 @@ dependencies = [ "riscv", ] +[[package]] +name = "defmt" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a0ae7494d9bff013d7b89471f4c424356a71e9752e0c78abe7e6c608a16bb3" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8500cbe4cca056412efce4215a63d0bc20492942aeee695f23b624a53e0a6854" +dependencies = [ + "defmt-parser", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "defmt-parser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0db23d29972d99baa3de2ee2ae3f104c10564a6d05a346eb3f4c4f2c0525a06e" + [[package]] name = "embedded-dma" version = "0.2.0" @@ -342,6 +371,15 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "ieee802_3_miim" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa6fe9a9feafccff3129a8ada9271d7f710a960be2b7c124583f7a3a5de3443e" +dependencies = [ + "bitflags", +] + [[package]] name = "indexmap" version = "1.9.1" @@ -363,6 +401,7 @@ dependencies = [ "cortex-m-rtic", "cortex-m-semihosting 0.5.0", "fugit", + "ieee802_3_miim", "log", "nb 1.0.0", "num-traits", @@ -384,9 +423,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.135" +version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "libm" @@ -666,11 +705,13 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stm32-eth" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f6e20fd0f89dcf5637782090e654de814eecfb83c66ba41c2d64ce7f211b31" +source = "git+https://github.com/stm32-rs/stm32-eth#34243c92efbea19414fab27f34e282a2a4121190" dependencies = [ "aligned 0.4.1", "cortex-m 0.7.6", + "defmt", + "ieee802_3_miim", + "smoltcp", "stm32f4xx-hal", "volatile-register", ] @@ -714,9 +755,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ "proc-macro2", "quote", @@ -747,14 +788,21 @@ dependencies = [ [[package]] name = "time" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" +checksum = "0fab5c8b9980850e06d92ddbe3ab839c062c801f3927c0fb8abd6fc8e918fbca" dependencies = [ "libc", "num_threads", + "time-core", ] +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + [[package]] name = "typenum" version = "1.15.0" diff --git a/Cargo.toml b/Cargo.toml index f5afaab..6613f93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,8 +23,9 @@ bare-metal = "1" nb = "1" cortex-m-log = { version = "0.7.0", features = ["log-integration", "semihosting"] } stm32f4xx-hal = { version = "0.13.2", features = ["rt", "stm32f407", "usb_fs", "rtic-monotonic"] } -stm32-eth = { version = "0.3.0", features = ["stm32f407"] } -smoltcp = { version = "0.8.0", default-features = false, features = ["proto-ipv4", "socket-tcp", "log", "medium-ethernet"] } +stm32-eth = { git = "https://github.com/stm32-rs/stm32-eth", features = ["stm32f407", "smoltcp-phy", "smoltcp"] } +smoltcp = { version = "0.8.1", default-features = false, features = ["proto-ipv4", "socket-tcp", "log", "medium-ethernet"] } +ieee802_3_miim = "0.7.2" num-traits = { version = "0.2.15", default-features = false, features = ["libm"] } usb-device = "0.2.9" usbd-serial = "0.1.1" diff --git a/src/device/boot.rs b/src/device/boot.rs index 1e688f6..7daf35d 100644 --- a/src/device/boot.rs +++ b/src/device/boot.rs @@ -1,5 +1,7 @@ use super::{gpio, usb, delay}; use crate::laser_diode::current_sources::*; +use smoltcp::iface::{SocketHandle, Interface}; +use stm32_eth::stm32::ETHERNET_DMA; use systick_monotonic::Systick; use fugit::ExtU32; use log::info; @@ -9,13 +11,15 @@ use stm32f4xx_hal::{ time::MegaHertz, watchdog::IndependentWatchdog, }; +use crate::network::network; +use crate::network::network::EthernetPeripherals; #[cfg(not(feature = "semihosting"))] const WATCHDOG_PERIOD: u32 = 1000; #[cfg(feature = "semihosting")] const WATCHDOG_PERIOD: u32 = 30000; -pub fn bootup(mut core_perif: CorePeripherals, perif: Peripherals) -> (IndependentWatchdog, Systick<1000_u32>) { +pub fn bootup(mut core_perif: CorePeripherals, perif: Peripherals, server_storage: &'static mut network::ServerStorage) -> (IndependentWatchdog, Systick<1000_u32>, network::ServerHandle) { core_perif.SCB.enable_icache(); core_perif.SCB.enable_dcache(&mut core_perif.CPUID); @@ -33,7 +37,7 @@ pub fn bootup(mut core_perif: CorePeripherals, perif: Peripherals) -> (Independe let systick = Systick::new(core_perif.SYST, clocks.hclk().to_Hz()); let delay = delay::AsmDelay::new(clocks.sysclk().to_Hz()); - let (_eth_pins, usb, current_source_phy) = gpio::setup( + let (eth_pins, usb, current_source_phy, mdio, mdc) = gpio::setup( clocks, perif.GPIOA, perif.GPIOB, @@ -46,6 +50,14 @@ pub fn bootup(mut core_perif: CorePeripherals, perif: Peripherals) -> (Independe perif.OTG_FS_PWRCLK, ); + let ethernet = EthernetPeripherals { + dma: perif.ETHERNET_DMA, + mac: perif.ETHERNET_MAC, + mmc: perif.ETHERNET_MMC, + }; + let server = network::ServerHandle::new(eth_pins, ethernet, clocks, server_storage, mdio, mdc); + // let (serverhandle, iface) = network::setup(eth_pins, ethernet, clocks); + usb::State::setup(usb); let mut laser = CurrentSource { @@ -64,5 +76,5 @@ pub fn bootup(mut core_perif: CorePeripherals, perif: Peripherals) -> (Independe info!("Kirdy setup complete"); - (wd, systick) + (wd, systick, server) } diff --git a/src/device/gpio.rs b/src/device/gpio.rs index 4fb560f..c8e856f 100644 --- a/src/device/gpio.rs +++ b/src/device/gpio.rs @@ -2,7 +2,7 @@ use crate::laser_diode::current_sources::*; use fugit::RateExtU32; use stm32_eth::EthPins; use stm32f4xx_hal::{ - gpio::{gpioa::*, gpiob::*, gpioc::*, gpiog::*, GpioExt, Input}, + gpio::{GpioExt, AF11, Speed, Alternate}, otg_fs::USB, pac::{GPIOA, GPIOB, GPIOC, GPIOD, GPIOG, OTG_FS_DEVICE, OTG_FS_GLOBAL, OTG_FS_PWRCLK, SPI2}, rcc::Clocks, @@ -10,8 +10,7 @@ use stm32f4xx_hal::{ spi::{NoMiso, Spi}, }; -pub type EthernetPins = - EthPins, PA7, PB11, PG13, PB13, PC4, PC5>; +use crate::network::network::EthernetPins; pub fn setup( clocks: Clocks, @@ -28,6 +27,8 @@ pub fn setup( EthernetPins, USB, CurrentSourcePhyConstruct, + stm32f4xx_hal::gpio::PA2>, + stm32f4xx_hal::gpio::PC1> // photo_diode_phy, // thermostat_phy ) { @@ -50,12 +51,18 @@ pub fn setup( ref_clk: gpioa.pa1, crs: gpioa.pa7, tx_en: gpiob.pb11, - tx_d0: gpiog.pg13, + tx_d0: gpiob.pb12, tx_d1: gpiob.pb13, rx_d0: gpioc.pc4, rx_d1: gpioc.pc5, }; + let mut mdio = gpioa.pa2.into_alternate::<11>(); + let mut mdc = gpioc.pc1.into_alternate::<11>(); + mdio.set_speed(Speed::VeryHigh); + mdc.set_speed(Speed::VeryHigh); + + let current_source_phy = CurrentSourcePhyConstruct { max5719_spi: Spi::new( spi2, @@ -77,5 +84,5 @@ pub fn setup( current_source_short: gpioa.pa4.into_push_pull_output(), }; - (eth_pins, usb, current_source_phy) + (eth_pins, usb, current_source_phy, mdio, mdc) } diff --git a/src/main.rs b/src/main.rs index 67dd3c0..79db30f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ use rtic::app; mod device; mod laser_diode; +mod network; // If RTT is used, print panic info through RTT #[cfg(feature = "RTT")] @@ -22,14 +23,22 @@ fn panic(info: &PanicInfo) -> ! { use panic_halt as _; // #[entry] -#[app(device = stm32f4xx_hal::pac, peripherals = true, dispatchers = [TIM8_CC])] +#[app(device = stm32f4xx_hal::pac, peripherals = true, dispatchers = [TIM8_CC, TIM8_BRK_TIM12])] mod app { use log::info; + use stm32_eth::stm32::ETHERNET_DMA; use systick_monotonic::Systick; use fugit::ExtU32; use stm32f4xx_hal::watchdog::IndependentWatchdog; use crate::device::{boot::bootup, log_setup}; + use smoltcp::iface::{SocketHandle, Interface}; + use crate::network::network; + + fn now_fn() -> smoltcp::time::Instant { + let time = monotonics::now().duration_since_epoch().to_millis(); + smoltcp::time::Instant::from_millis(time as i64) + } #[monotonic(binds = SysTick, default = true)] type SystickTimer = Systick<1000>; @@ -42,31 +51,42 @@ mod app { #[local] struct Local { wd: IndependentWatchdog, + // server_storage: network::network::ServerStorage::new(), + server_handle: network::ServerHandle } - #[init] + #[init( local = [server_storage: network::ServerStorage = network::ServerStorage::new()] )] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { log_setup::init_log(); info!("Kirdy init"); let core_perif = cx.core; let perif = cx.device; - let (wd, systick) = bootup(core_perif, perif); + let server_storage = cx.local.server_storage; + let (wd, systick, server_handle) = bootup(core_perif, perif, server_storage); wd_feed::spawn().unwrap(); - - (Shared {}, Local {wd}, init::Monotonics(systick)) + // server_poll::spawn().unwrap(); + (Shared {}, Local {wd, server_handle}, init::Monotonics(systick)) } #[task(priority = 5, local = [wd])] fn wd_feed (cx: wd_feed::Context) { let start = monotonics::now(); let wd = cx.local.wd; - info!("feed wd"); + // info!("feed wd"); wd.feed(); wd_feed::spawn_at(start + 10_u32.millis()).unwrap(); } + #[task(binds = ETH, priority = 2, local = [server_handle, data: [u8; 512] = [0u8; 512]])] + fn server_poll (cx: server_poll::Context) { + let data = cx.local.data; + let server_handle = cx.local.server_handle; + server_handle.poll(data); + // server_poll::spawn_at(start + 10_u32.millis()).unwrap(); + } + #[idle] fn idle(_: idle::Context) -> ! { loop { diff --git a/src/network/mod.rs b/src/network/mod.rs new file mode 100644 index 0000000..8ce92ac --- /dev/null +++ b/src/network/mod.rs @@ -0,0 +1 @@ +pub mod network; \ No newline at end of file diff --git a/src/network/network.rs b/src/network/network.rs new file mode 100644 index 0000000..99d4ccc --- /dev/null +++ b/src/network/network.rs @@ -0,0 +1,280 @@ +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, 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()); + 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::(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()); + } + } + } +} \ No newline at end of file