use crate::driver::config::{Duplex, Speed};
use crate::driver::error::Result;
use crate::hal::mdio::MdioBus;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LinkStatus {
pub speed: Speed,
pub duplex: Duplex,
}
impl LinkStatus {
pub const fn new(speed: Speed, duplex: Duplex) -> Self {
Self { speed, duplex }
}
pub const fn fast_full() -> Self {
Self::new(Speed::Mbps100, Duplex::Full)
}
pub const fn fast_half() -> Self {
Self::new(Speed::Mbps100, Duplex::Half)
}
pub const fn slow_full() -> Self {
Self::new(Speed::Mbps10, Duplex::Full)
}
pub const fn slow_half() -> Self {
Self::new(Speed::Mbps10, Duplex::Half)
}
}
#[derive(Debug, Clone, Copy, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PhyCapabilities {
pub speed_100_fd: bool,
pub speed_100_hd: bool,
pub speed_10_fd: bool,
pub speed_10_hd: bool,
pub auto_negotiation: bool,
pub pause: bool,
pub pause_asymmetric: bool,
}
impl PhyCapabilities {
pub const fn standard_10_100() -> Self {
Self {
speed_100_fd: true,
speed_100_hd: true,
speed_10_fd: true,
speed_10_hd: true,
auto_negotiation: true,
pause: true,
pause_asymmetric: false,
}
}
}
pub trait PhyDriver {
fn address(&self) -> u8;
fn init<M: MdioBus>(&mut self, mdio: &mut M) -> Result<()>;
fn soft_reset<M: MdioBus>(&mut self, mdio: &mut M) -> Result<()>;
fn is_link_up<M: MdioBus>(&self, mdio: &mut M) -> Result<bool>;
fn link_status<M: MdioBus>(&self, mdio: &mut M) -> Result<Option<LinkStatus>>;
fn poll_link<M: MdioBus>(&mut self, mdio: &mut M) -> Result<Option<LinkStatus>>;
fn enable_auto_negotiation<M: MdioBus>(&mut self, mdio: &mut M) -> Result<()>;
fn force_link<M: MdioBus>(&mut self, mdio: &mut M, status: LinkStatus) -> Result<()>;
fn capabilities<M: MdioBus>(&self, mdio: &mut M) -> Result<PhyCapabilities>;
fn phy_id<M: MdioBus>(&self, mdio: &mut M) -> Result<u32>;
fn is_auto_negotiation_complete<M: MdioBus>(&self, mdio: &mut M) -> Result<bool>;
fn link_partner_abilities<M: MdioBus>(&self, mdio: &mut M) -> Result<PhyCapabilities>;
}
pub mod ieee802_3 {
use super::*;
use crate::internal::phy_regs::standard::{anar, bmcr, bmsr, phy_reg};
pub fn is_link_up<M: MdioBus>(mdio: &mut M, phy_addr: u8) -> Result<bool> {
let bmsr_val = mdio.read(phy_addr, phy_reg::BMSR)?;
Ok((bmsr_val & bmsr::LINK_STATUS) != 0)
}
pub fn is_an_complete<M: MdioBus>(mdio: &mut M, phy_addr: u8) -> Result<bool> {
let bmsr_val = mdio.read(phy_addr, phy_reg::BMSR)?;
Ok((bmsr_val & bmsr::AN_COMPLETE) != 0)
}
pub fn soft_reset<M: MdioBus>(mdio: &mut M, phy_addr: u8, max_attempts: u32) -> Result<()> {
mdio.write(phy_addr, phy_reg::BMCR, bmcr::RESET)?;
for _ in 0..max_attempts {
let bmcr_val = mdio.read(phy_addr, phy_reg::BMCR)?;
if (bmcr_val & bmcr::RESET) == 0 {
return Ok(());
}
}
Ok(())
}
pub fn enable_auto_negotiation<M: MdioBus>(mdio: &mut M, phy_addr: u8) -> Result<()> {
let bmcr_val = mdio.read(phy_addr, phy_reg::BMCR)?;
mdio.write(
phy_addr,
phy_reg::BMCR,
(bmcr_val | bmcr::AN_ENABLE | bmcr::AN_RESTART) & !bmcr::ISOLATE,
)
}
pub fn force_link<M: MdioBus>(mdio: &mut M, phy_addr: u8, status: LinkStatus) -> Result<()> {
let mut bmcr_val = mdio.read(phy_addr, phy_reg::BMCR)?;
bmcr_val &= !bmcr::AN_ENABLE;
bmcr_val &= !bmcr::ISOLATE;
if matches!(status.speed, Speed::Mbps100) {
bmcr_val |= bmcr::SPEED_100;
} else {
bmcr_val &= !bmcr::SPEED_100;
}
if matches!(status.duplex, Duplex::Full) {
bmcr_val |= bmcr::DUPLEX_FULL;
} else {
bmcr_val &= !bmcr::DUPLEX_FULL;
}
mdio.write(phy_addr, phy_reg::BMCR, bmcr_val)
}
pub fn read_phy_id<M: MdioBus>(mdio: &mut M, phy_addr: u8) -> Result<u32> {
let id1 = mdio.read(phy_addr, phy_reg::PHYIDR1)? as u32;
let id2 = mdio.read(phy_addr, phy_reg::PHYIDR2)? as u32;
Ok((id1 << 16) | id2)
}
pub fn read_capabilities<M: MdioBus>(mdio: &mut M, phy_addr: u8) -> Result<PhyCapabilities> {
let bmsr_val = mdio.read(phy_addr, phy_reg::BMSR)?;
Ok(PhyCapabilities {
speed_100_fd: (bmsr_val & bmsr::TX_FD_CAPABLE) != 0,
speed_100_hd: (bmsr_val & bmsr::TX_HD_CAPABLE) != 0,
speed_10_fd: (bmsr_val & bmsr::T10_FD_CAPABLE) != 0,
speed_10_hd: (bmsr_val & bmsr::T10_HD_CAPABLE) != 0,
auto_negotiation: (bmsr_val & bmsr::AN_ABILITY) != 0,
pause: false, pause_asymmetric: false,
})
}
pub fn read_link_partner<M: MdioBus>(mdio: &mut M, phy_addr: u8) -> Result<PhyCapabilities> {
let anlpar_val = mdio.read(phy_addr, phy_reg::ANLPAR)?;
Ok(PhyCapabilities {
speed_100_fd: (anlpar_val & anar::TX_FD) != 0,
speed_100_hd: (anlpar_val & anar::TX_HD) != 0,
speed_10_fd: (anlpar_val & anar::T10_FD) != 0,
speed_10_hd: (anlpar_val & anar::T10_HD) != 0,
auto_negotiation: true, pause: (anlpar_val & anar::PAUSE) != 0,
pause_asymmetric: false,
})
}
pub fn link_status_from_bmcr<M: MdioBus>(mdio: &mut M, phy_addr: u8) -> Result<LinkStatus> {
let bmcr_val = mdio.read(phy_addr, phy_reg::BMCR)?;
let speed = if (bmcr_val & bmcr::SPEED_100) != 0 {
Speed::Mbps100
} else {
Speed::Mbps10
};
let duplex = if (bmcr_val & bmcr::DUPLEX_FULL) != 0 {
Duplex::Full
} else {
Duplex::Half
};
Ok(LinkStatus::new(speed, duplex))
}
}