From ddff295ae13fd218fb4a04a73dced1060578dbca Mon Sep 17 00:00:00 2001 From: Astro Date: Fri, 20 Nov 2020 17:12:22 +0100 Subject: [PATCH] libboard_zynq: implement eth hot-plugging --- Cargo.lock | 1 + libasync/src/smoltcp/mod.rs | 20 +++++++++++++++-- libboard_zynq/Cargo.toml | 1 + libboard_zynq/src/eth/mod.rs | 37 +++++++++++++++++++++++--------- libboard_zynq/src/eth/phy/mod.rs | 2 +- 5 files changed, 48 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2245d2d..abe3b33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,7 @@ version = "0.0.0" dependencies = [ "bit_field", "embedded-hal", + "libasync", "libcortex_a9", "libregister", "log", diff --git a/libasync/src/smoltcp/mod.rs b/libasync/src/smoltcp/mod.rs index 6390cd8..0a819c5 100644 --- a/libasync/src/smoltcp/mod.rs +++ b/libasync/src/smoltcp/mod.rs @@ -7,13 +7,19 @@ use smoltcp::{ iface::EthernetInterface, phy::Device, socket::SocketSet, - time::Instant, + time::{Duration, Instant}, }; use crate::task; mod tcp_stream; pub use tcp_stream::TcpStream; +pub trait LinkCheck { + type Link; + fn is_idle(&self) -> bool; + fn check_link_change(&mut self) -> Option; +} + static mut SOCKETS: Option = None; pub struct Sockets { @@ -41,14 +47,24 @@ impl Sockets { /// Block and run executor indefinitely while polling the smoltcp /// iface - pub fn run<'b, 'c, 'e, D: for<'d> Device<'d>>( + pub fn run<'b, 'c, 'e, D: for<'d> Device<'d> + LinkCheck>( iface: &mut EthernetInterface<'b, 'c, 'e, D>, mut get_time: impl FnMut() -> Instant, ) -> ! { task::block_on(async { + let mut last_link_check = Instant::from_millis(0); + const LINK_CHECK_INTERVAL: u64 = 500; + loop { let instant = get_time(); Self::instance().poll(iface, instant); + + let dev = iface.device_mut(); + if dev.is_idle() && instant >= last_link_check + Duration::from_millis(LINK_CHECK_INTERVAL) { + dev.check_link_change(); + last_link_check = instant; + } + task::r#yield().await; } }) diff --git a/libboard_zynq/Cargo.toml b/libboard_zynq/Cargo.toml index da2b4b7..cc738e8 100644 --- a/libboard_zynq/Cargo.toml +++ b/libboard_zynq/Cargo.toml @@ -21,6 +21,7 @@ void = { version = "1", default-features = false } log = "0.4" libregister = { path = "../libregister" } libcortex_a9 = { path = "../libcortex_a9" } +libasync = { path = "../libasync" } [dependencies.smoltcp] version = "0.6" diff --git a/libboard_zynq/src/eth/mod.rs b/libboard_zynq/src/eth/mod.rs index fc71346..e8e56e4 100644 --- a/libboard_zynq/src/eth/mod.rs +++ b/libboard_zynq/src/eth/mod.rs @@ -145,6 +145,8 @@ pub struct Eth { tx: TX, inner: EthInner, phy: Phy, + /// keep track of RX path occupation to avoid needless `check_link_change()` + idle: bool, } impl Eth { @@ -314,6 +316,7 @@ impl Eth { tx: (), inner, phy, + idle: true, } } } @@ -325,6 +328,7 @@ impl Eth { tx: self.tx, inner: self.inner, phy: self.phy, + idle: self.idle, }; let list_addr = new_self.rx.list_addr(); assert!(list_addr & 0b11 == 0); @@ -344,6 +348,7 @@ impl Eth { tx: tx::DescList::new(tx_size), inner: self.inner, phy: self.phy, + idle: self.idle, }; let list_addr = &new_self.tx.list_addr(); assert!(list_addr & 0b11 == 0); @@ -395,17 +400,31 @@ impl Eth { regs::RxStatus::zeroed() .frame_recd(true) ); + self.idle = true; } - _ => {} + _ => + self.idle = false, } result } else { - self.inner.check_link_change(&self.phy); + self.idle = true; Ok(None) } } } +impl libasync::smoltcp::LinkCheck for &mut Eth { + type Link = Option; + + fn check_link_change(&mut self) -> Option { + self.inner.check_link_change(&self.phy) + } + + fn is_idle(&self) -> bool { + self.idle + } +} + impl Eth { pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option> { self.tx.send(GEM::regs(), length) @@ -439,10 +458,11 @@ impl<'a, GEM: Gem> smoltcp::phy::Device<'a> for &mut Eth { - self.inner.check_link_change(&self.phy); + self.idle = true; None } Err(e) => { @@ -602,13 +622,7 @@ impl EthInner { } - fn check_link_change(&mut self, phy: &Phy) { - // As the PHY access takes some time, exit early if there was - // already a link. TODO: check once per second. - if self.link.is_some() { - return - } - + fn check_link_change(&mut self, phy: &Phy) -> Option> { let link = phy.get_link(self); // Check link state transition @@ -640,6 +654,9 @@ impl EthInner { } self.link = link; + Some(link) + } else { + None } } } diff --git a/libboard_zynq/src/eth/phy/mod.rs b/libboard_zynq/src/eth/phy/mod.rs index f123b51..47f9659 100644 --- a/libboard_zynq/src/eth/phy/mod.rs +++ b/libboard_zynq/src/eth/phy/mod.rs @@ -7,7 +7,7 @@ pub use control::Control; mod pssr; pub use pssr::PSSR; -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct Link { pub speed: LinkSpeed, pub duplex: LinkDuplex,