libboard_zynq: implement eth hot-plugging

This commit is contained in:
Astro 2020-11-20 17:12:22 +01:00
parent 379b6b973a
commit ddff295ae1
5 changed files with 48 additions and 13 deletions

1
Cargo.lock generated
View File

@ -89,6 +89,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"embedded-hal", "embedded-hal",
"libasync",
"libcortex_a9", "libcortex_a9",
"libregister", "libregister",
"log", "log",

View File

@ -7,13 +7,19 @@ use smoltcp::{
iface::EthernetInterface, iface::EthernetInterface,
phy::Device, phy::Device,
socket::SocketSet, socket::SocketSet,
time::Instant, time::{Duration, Instant},
}; };
use crate::task; use crate::task;
mod tcp_stream; mod tcp_stream;
pub use tcp_stream::TcpStream; pub use tcp_stream::TcpStream;
pub trait LinkCheck {
type Link;
fn is_idle(&self) -> bool;
fn check_link_change(&mut self) -> Option<Self::Link>;
}
static mut SOCKETS: Option<Sockets> = None; static mut SOCKETS: Option<Sockets> = None;
pub struct Sockets { pub struct Sockets {
@ -41,14 +47,24 @@ impl Sockets {
/// Block and run executor indefinitely while polling the smoltcp /// Block and run executor indefinitely while polling the smoltcp
/// iface /// 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>, iface: &mut EthernetInterface<'b, 'c, 'e, D>,
mut get_time: impl FnMut() -> Instant, mut get_time: impl FnMut() -> Instant,
) -> ! { ) -> ! {
task::block_on(async { task::block_on(async {
let mut last_link_check = Instant::from_millis(0);
const LINK_CHECK_INTERVAL: u64 = 500;
loop { loop {
let instant = get_time(); let instant = get_time();
Self::instance().poll(iface, instant); 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; task::r#yield().await;
} }
}) })

View File

@ -21,6 +21,7 @@ void = { version = "1", default-features = false }
log = "0.4" log = "0.4"
libregister = { path = "../libregister" } libregister = { path = "../libregister" }
libcortex_a9 = { path = "../libcortex_a9" } libcortex_a9 = { path = "../libcortex_a9" }
libasync = { path = "../libasync" }
[dependencies.smoltcp] [dependencies.smoltcp]
version = "0.6" version = "0.6"

View File

@ -145,6 +145,8 @@ pub struct Eth<GEM: Gem, RX, TX> {
tx: TX, tx: TX,
inner: EthInner<GEM>, inner: EthInner<GEM>,
phy: Phy, phy: Phy,
/// keep track of RX path occupation to avoid needless `check_link_change()`
idle: bool,
} }
impl Eth<Gem0, (), ()> { impl Eth<Gem0, (), ()> {
@ -314,6 +316,7 @@ impl<GEM: Gem> Eth<GEM, (), ()> {
tx: (), tx: (),
inner, inner,
phy, phy,
idle: true,
} }
} }
} }
@ -325,6 +328,7 @@ impl<GEM: Gem, RX, TX> Eth<GEM, RX, TX> {
tx: self.tx, tx: self.tx,
inner: self.inner, inner: self.inner,
phy: self.phy, phy: self.phy,
idle: self.idle,
}; };
let list_addr = new_self.rx.list_addr(); let list_addr = new_self.rx.list_addr();
assert!(list_addr & 0b11 == 0); assert!(list_addr & 0b11 == 0);
@ -344,6 +348,7 @@ impl<GEM: Gem, RX, TX> Eth<GEM, RX, TX> {
tx: tx::DescList::new(tx_size), tx: tx::DescList::new(tx_size),
inner: self.inner, inner: self.inner,
phy: self.phy, phy: self.phy,
idle: self.idle,
}; };
let list_addr = &new_self.tx.list_addr(); let list_addr = &new_self.tx.list_addr();
assert!(list_addr & 0b11 == 0); assert!(list_addr & 0b11 == 0);
@ -395,17 +400,31 @@ impl<GEM: Gem, TX> Eth<GEM, rx::DescList, TX> {
regs::RxStatus::zeroed() regs::RxStatus::zeroed()
.frame_recd(true) .frame_recd(true)
); );
self.idle = true;
} }
_ => {} _ =>
self.idle = false,
} }
result result
} else { } else {
self.inner.check_link_change(&self.phy); self.idle = true;
Ok(None) Ok(None)
} }
} }
} }
impl<GEM: Gem, TX> libasync::smoltcp::LinkCheck for &mut Eth<GEM, rx::DescList, TX> {
type Link = Option<phy::Link>;
fn check_link_change(&mut self) -> Option<Self::Link> {
self.inner.check_link_change(&self.phy)
}
fn is_idle(&self) -> bool {
self.idle
}
}
impl<GEM: Gem, RX> Eth<GEM, RX, tx::DescList> { impl<GEM: Gem, RX> Eth<GEM, RX, tx::DescList> {
pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option<tx::PktRef<'p>> { pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option<tx::PktRef<'p>> {
self.tx.send(GEM::regs(), length) self.tx.send(GEM::regs(), length)
@ -439,10 +458,11 @@ impl<'a, GEM: Gem> smoltcp::phy::Device<'a> for &mut Eth<GEM, rx::DescList, tx::
regs: GEM::regs(), regs: GEM::regs(),
desc_list: &mut self.tx, desc_list: &mut self.tx,
}; };
self.idle = false;
Some((pktref, tx_token)) Some((pktref, tx_token))
} }
Ok(None) => { Ok(None) => {
self.inner.check_link_change(&self.phy); self.idle = true;
None None
} }
Err(e) => { Err(e) => {
@ -602,13 +622,7 @@ impl<GEM: Gem> EthInner<GEM> {
} }
fn check_link_change(&mut self, phy: &Phy) { fn check_link_change(&mut self, phy: &Phy) -> Option<Option<phy::Link>> {
// 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
}
let link = phy.get_link(self); let link = phy.get_link(self);
// Check link state transition // Check link state transition
@ -640,6 +654,9 @@ impl<GEM: Gem> EthInner<GEM> {
} }
self.link = link; self.link = link;
Some(link)
} else {
None
} }
} }
} }

View File

@ -7,7 +7,7 @@ pub use control::Control;
mod pssr; mod pssr;
pub use pssr::PSSR; pub use pssr::PSSR;
#[derive(Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub struct Link { pub struct Link {
pub speed: LinkSpeed, pub speed: LinkSpeed,
pub duplex: LinkDuplex, pub duplex: LinkDuplex,