diff --git a/embassy-stm32/src/eth/generic_smi.rs b/embassy-stm32/src/eth/generic_smi.rs index 96825604..90631b17 100644 --- a/embassy-stm32/src/eth/generic_smi.rs +++ b/embassy-stm32/src/eth/generic_smi.rs @@ -1,5 +1,11 @@ //! Generic SMI Ethernet PHY +#[cfg(feature = "time")] +use embassy_time::{Duration, Timer}; +use futures::task::Context; +#[cfg(feature = "time")] +use futures::FutureExt; + use super::{StationManagement, PHY}; #[allow(dead_code)] @@ -36,25 +42,48 @@ mod phy_consts { use self::phy_consts::*; /// Generic SMI Ethernet PHY -pub struct GenericSMI; +pub struct GenericSMI { + #[cfg(feature = "time")] + poll_interval: Duration, +} + +impl GenericSMI { + #[cfg(feature = "time")] + pub fn new() -> Self { + Self { + poll_interval: Duration::from_millis(500), + } + } + + #[cfg(not(feature = "time"))] + pub fn new() -> Self { + Self {} + } +} unsafe impl PHY for GenericSMI { /// Reset PHY and wait for it to come out of reset. - fn phy_reset(sm: &mut S) { + fn phy_reset(&mut self, sm: &mut S) { sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_RESET); while sm.smi_read(PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} } /// PHY initialisation. - fn phy_init(sm: &mut S) { + fn phy_init(&mut self, sm: &mut S) { // Clear WU CSR - Self::smi_write_ext(sm, PHY_REG_WUCSR, 0); + self.smi_write_ext(sm, PHY_REG_WUCSR, 0); // Enable auto-negotiation sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M); } - fn poll_link(sm: &mut S) -> bool { + fn poll_link(&mut self, sm: &mut S, cx: &mut Context) -> bool { + #[cfg(not(feature = "time"))] + cx.waker().wake_by_ref(); + + #[cfg(feature = "time")] + let _ = Timer::after(self.poll_interval).poll_unpin(cx); + let bsr = sm.smi_read(PHY_REG_BSR); // No link without autonegotiate @@ -73,8 +102,12 @@ unsafe impl PHY for GenericSMI { /// Public functions for the PHY impl GenericSMI { + pub fn set_poll_interval(&mut self, poll_interval: Duration) { + self.poll_interval = poll_interval + } + // Writes a value to an extended PHY register in MMD address space - fn smi_write_ext(sm: &mut S, reg_addr: u16, reg_data: u16) { + fn smi_write_ext(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) { sm.smi_write(PHY_REG_CTL, 0x0003); // set address sm.smi_write(PHY_REG_ADDAR, reg_addr); sm.smi_write(PHY_REG_CTL, 0x4003); // set data diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 4989e17c..1687cb31 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -81,9 +81,7 @@ impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> } fn link_state(&mut self, cx: &mut Context) -> LinkState { - // TODO: wake cx.waker on link state change - cx.waker().wake_by_ref(); - if P::poll_link(self) { + if self.phy.poll_link(&mut self.station_management, cx) { LinkState::Up } else { LinkState::Down @@ -148,11 +146,11 @@ pub unsafe trait StationManagement { /// The methods cannot move S pub unsafe trait PHY { /// Reset PHY and wait for it to come out of reset. - fn phy_reset(sm: &mut S); + fn phy_reset(&mut self, sm: &mut S); /// PHY initialisation. - fn phy_init(sm: &mut S); + fn phy_init(&mut self, sm: &mut S); /// Poll link to see if it is up and FD with 100Mbps - fn poll_link(sm: &mut S) -> bool; + fn poll_link(&mut self, sm: &mut S, cx: &mut Context) -> bool; } pub(crate) mod sealed { diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index b53c2d0f..2a6ea35f 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -3,6 +3,7 @@ mod rx_desc; mod tx_desc; +use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -48,9 +49,8 @@ pub struct Ethernet<'d, T: Instance, P: PHY> { pub(crate) rx: RDesRing<'d>, pins: [PeripheralRef<'d, AnyPin>; 9], - _phy: P, - clock_range: Cr, - phy_addr: u8, + pub(crate) phy: P, + pub(crate) station_management: EthernetStationManagement, pub(crate) mac_addr: [u8; 6], } @@ -224,9 +224,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { let mut this = Self { _peri: peri, pins, - _phy: phy, - clock_range, - phy_addr, + phy: phy, + station_management: EthernetStationManagement { + peri: PhantomData, + clock_range: clock_range, + phy_addr: phy_addr, + }, mac_addr, tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), @@ -256,8 +259,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { w.set_tie(true); }); - P::phy_reset(&mut this); - P::phy_init(&mut this); + this.phy.phy_reset(&mut this.station_management); + this.phy.phy_init(&mut this.station_management); interrupt::ETH.unpend(); unsafe { interrupt::ETH.enable() }; @@ -266,7 +269,13 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { } } -unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { +pub struct EthernetStationManagement { + peri: PhantomData, + clock_range: Cr, + phy_addr: u8, +} + +unsafe impl StationManagement for EthernetStationManagement { fn smi_read(&mut self, reg: u8) -> u16 { let mac = ETH.ethernet_mac(); diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 600e1d3b..bb681c42 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -1,5 +1,6 @@ mod descriptors; +use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -40,9 +41,8 @@ pub struct Ethernet<'d, T: Instance, P: PHY> { pub(crate) tx: TDesRing<'d>, pub(crate) rx: RDesRing<'d>, pins: [PeripheralRef<'d, AnyPin>; 9], - _phy: P, - clock_range: u8, - phy_addr: u8, + pub(crate) phy: P, + pub(crate) station_management: EthernetStationManagement, pub(crate) mac_addr: [u8; 6], } @@ -201,9 +201,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), pins, - _phy: phy, - clock_range, - phy_addr, + phy: phy, + station_management: EthernetStationManagement { + peri: PhantomData, + clock_range: clock_range, + phy_addr: phy_addr, + }, mac_addr, }; @@ -229,8 +232,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { w.set_tie(true); }); - P::phy_reset(&mut this); - P::phy_init(&mut this); + this.phy.phy_reset(&mut this.station_management); + this.phy.phy_init(&mut this.station_management); interrupt::ETH.unpend(); unsafe { interrupt::ETH.enable() }; @@ -239,7 +242,13 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { } } -unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { +pub struct EthernetStationManagement { + peri: PhantomData, + clock_range: u8, + phy_addr: u8, +} + +unsafe impl StationManagement for EthernetStationManagement { fn smi_read(&mut self, reg: u8) -> u16 { let mac = ETH.ethernet_mac(); diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs new file mode 100644 index 00000000..d0b16439 --- /dev/null +++ b/examples/stm32f4/src/bin/eth.rs @@ -0,0 +1,111 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_stm32::eth::generic_smi::GenericSMI; +use embassy_stm32::eth::{Ethernet, PacketQueue}; +use embassy_stm32::peripherals::ETH; +use embassy_stm32::rng::Rng; +use embassy_stm32::time::mhz; +use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_time::{Duration, Timer}; +use embedded_io::asynch::Write; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + ETH => eth::InterruptHandler; +}); + +type Device = Ethernet<'static, ETH, GenericSMI>; + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) -> ! { + let mut config = Config::default(); + config.rcc.sys_ck = Some(mhz(200)); + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + // Generate random seed. + let mut rng = Rng::new(p.RNG); + let mut seed = [0; 8]; + let _ = rng.async_fill_bytes(&mut seed).await; + let seed = u64::from_le_bytes(seed); + + let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; + + let device = Ethernet::new( + make_static!(PacketQueue::<16, 16>::new()), + p.ETH, + Irqs, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB13, + p.PG11, + GenericSMI::new(), + mac_addr, + 0, + ); + + let config = embassy_net::Config::dhcpv4(Default::default()); + //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + //}); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Network task initialized"); + + // Then we can use it! + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); + + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); + + let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); + info!("connecting..."); + let r = socket.connect(remote_endpoint).await; + if let Err(e) = r { + info!("connect error: {:?}", e); + continue; + } + info!("connected!"); + let buf = [0; 1024]; + loop { + let r = socket.write_all(&buf).await; + if let Err(e) = r { + info!("write error: {:?}", e); + continue; + } + Timer::after(Duration::from_secs(1)).await; + } + } +} diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index fde6a757..c6b2ba45 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -57,7 +57,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI, + GenericSMI::new(), mac_addr, 0, ); diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 78c8282a..0bff85ed 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -76,7 +76,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB15, p.PG11, - GenericSMI, + GenericSMI::new(), mac_addr, 0, ); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 12d37f7a..cfafcaed 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -58,7 +58,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI, + GenericSMI::new(), mac_addr, 0, ); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 6078fc3f..4ed73757 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -59,7 +59,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI, + GenericSMI::new(), mac_addr, 0, );