forked from M-Labs/zynq-rs
libboard_zynq: implement eth hot-plugging
This commit is contained in:
parent
379b6b973a
commit
ddff295ae1
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -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",
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user