#![cfg_attr(docsrs, doc(cfg(feature = "esp-hal")))]
#[cfg(feature = "esp32")]
#[cfg_attr(docsrs, doc(cfg(feature = "esp32")))]
pub use crate::boards::wt32_eth01::Wt32Eth01;
pub use esp_hal::delay::Delay;
pub use esp_hal::interrupt::{InterruptHandler, Priority};
pub use esp_hal::peripherals::Interrupt;
use embedded_hal::delay::DelayNs;
use crate::driver::error::{ConfigError, IoError};
use crate::hal::mdio::MdioBus;
#[cfg(feature = "esp32")]
use crate::hal::mdio::MdioController;
#[cfg(feature = "esp32")]
use crate::phy::Lan8720a;
use crate::phy::{LinkStatus, PhyDriver};
pub struct EmacBuilder<'a, const RX: usize, const TX: usize, const BUF: usize> {
emac: &'a mut crate::Emac<RX, TX, BUF>,
config: crate::EmacConfig,
}
impl<'a, const RX: usize, const TX: usize, const BUF: usize> EmacBuilder<'a, RX, TX, BUF> {
pub fn new(emac: &'a mut crate::Emac<RX, TX, BUF>) -> Self {
Self {
emac,
config: crate::EmacConfig::rmii_esp32_default(),
}
}
#[cfg(feature = "esp32")]
#[cfg_attr(docsrs, doc(cfg(feature = "esp32")))]
pub fn wt32_eth01(emac: &'a mut crate::Emac<RX, TX, BUF>) -> Self {
Self {
emac,
config: Wt32Eth01::emac_config(),
}
}
#[cfg(feature = "esp32")]
#[cfg_attr(docsrs, doc(cfg(feature = "esp32")))]
pub fn wt32_eth01_with_mac(
emac: &'a mut crate::Emac<RX, TX, BUF>,
mac_address: [u8; 6],
) -> Self {
Self {
emac,
config: Wt32Eth01::emac_config_with_mac(mac_address),
}
}
#[must_use]
pub const fn with_config(mut self, config: crate::EmacConfig) -> Self {
self.config = config;
self
}
#[must_use]
pub const fn with_mac_address(mut self, mac_address: [u8; 6]) -> Self {
self.config = self.config.with_mac_address(mac_address);
self
}
#[must_use]
pub const fn with_rmii_clock(mut self, rmii_clock: crate::RmiiClockMode) -> Self {
self.config = self.config.with_rmii_clock(rmii_clock);
self
}
#[must_use]
pub const fn with_rmii_external_clock(mut self, gpio: u8) -> Self {
self.config = self.config.with_rmii_external_clock(gpio);
self
}
#[must_use]
pub const fn with_rmii_internal_clock(mut self, gpio: u8) -> Self {
self.config = self.config.with_rmii_internal_clock(gpio);
self
}
pub fn init(self, delay: &mut Delay) -> crate::Result<&'a mut crate::Emac<RX, TX, BUF>> {
self.emac.init(self.config, delay)?;
Ok(self.emac)
}
pub fn init_and_start(
self,
delay: &mut Delay,
) -> crate::Result<&'a mut crate::Emac<RX, TX, BUF>> {
self.emac.init(self.config, delay)?;
self.emac.start()?;
Ok(self.emac)
}
}
pub struct EmacPhyBundle<'a, const RX: usize, const TX: usize, const BUF: usize, P, M> {
emac: &'a mut crate::Emac<RX, TX, BUF>,
phy: P,
mdio: M,
}
impl<'a, const RX: usize, const TX: usize, const BUF: usize, P, M>
EmacPhyBundle<'a, RX, TX, BUF, P, M>
where
P: PhyDriver,
M: MdioBus,
{
pub fn new(emac: &'a mut crate::Emac<RX, TX, BUF>, phy: P, mdio: M) -> Self {
Self { emac, phy, mdio }
}
pub fn emac_mut(&mut self) -> &mut crate::Emac<RX, TX, BUF> {
self.emac
}
pub fn phy_mut(&mut self) -> &mut P {
&mut self.phy
}
pub fn mdio_mut(&mut self) -> &mut M {
&mut self.mdio
}
pub fn init_phy(&mut self) -> crate::Result<()> {
self.phy.init(&mut self.mdio)
}
pub fn link_status(&mut self) -> crate::Result<Option<LinkStatus>> {
let status = self.phy.link_status(&mut self.mdio)?;
self.apply_link(status);
Ok(status)
}
pub fn poll_link(&mut self) -> crate::Result<Option<LinkStatus>> {
let status = self.phy.poll_link(&mut self.mdio)?;
self.apply_link(status);
Ok(status)
}
pub fn wait_link_up<D: DelayNs>(
&mut self,
delay: &mut D,
timeout_ms: u32,
poll_interval_ms: u32,
) -> crate::Result<LinkStatus> {
if poll_interval_ms == 0 {
return Err(ConfigError::InvalidConfig.into());
}
if let Some(status) = self.link_status()? {
return Ok(status);
}
let mut elapsed_ms = 0u32;
while elapsed_ms < timeout_ms {
delay.delay_ms(poll_interval_ms);
elapsed_ms = elapsed_ms.saturating_add(poll_interval_ms);
if let Some(status) = self.link_status()? {
return Ok(status);
}
}
Err(IoError::Timeout.into())
}
pub fn init_and_wait_link_up<D: DelayNs>(
&mut self,
delay: &mut D,
timeout_ms: u32,
poll_interval_ms: u32,
) -> crate::Result<LinkStatus> {
self.init_phy()?;
self.wait_link_up(delay, timeout_ms, poll_interval_ms)
}
pub fn into_parts(self) -> (&'a mut crate::Emac<RX, TX, BUF>, P, M) {
(self.emac, self.phy, self.mdio)
}
fn apply_link(&mut self, status: Option<LinkStatus>) {
if let Some(status) = status {
self.emac.set_speed(status.speed);
self.emac.set_duplex(status.duplex);
}
}
}
#[cfg(feature = "esp32")]
#[cfg_attr(docsrs, doc(cfg(feature = "esp32")))]
impl<'a, const RX: usize, const TX: usize, const BUF: usize, D>
EmacPhyBundle<'a, RX, TX, BUF, Lan8720a, MdioController<D>>
where
D: DelayNs,
{
pub fn wt32_eth01_lan8720a(emac: &'a mut crate::Emac<RX, TX, BUF>, delay: D) -> Self {
Self::new(emac, Wt32Eth01::lan8720a(), MdioController::new(delay))
}
}
pub const EMAC_INTERRUPT: Interrupt = Interrupt::ETH_MAC;
pub trait EmacExt {
fn bind_interrupt(&mut self, handler: InterruptHandler);
fn disable_interrupt(&mut self);
}
impl<const RX: usize, const TX: usize, const BUF: usize> EmacExt for crate::Emac<RX, TX, BUF> {
fn bind_interrupt(&mut self, handler: InterruptHandler) {
for core in esp_hal::system::Cpu::other() {
esp_hal::interrupt::disable(core, EMAC_INTERRUPT);
}
unsafe {
esp_hal::interrupt::bind_interrupt(EMAC_INTERRUPT, handler.handler());
}
esp_hal::interrupt::enable(EMAC_INTERRUPT, handler.priority())
.expect("Failed to enable EMAC interrupt");
}
fn disable_interrupt(&mut self) {
esp_hal::interrupt::disable(esp_hal::system::Cpu::current(), EMAC_INTERRUPT);
}
}
#[macro_export]
macro_rules! emac_isr {
($name:ident, $priority:expr, $body:block) => {
#[allow(non_upper_case_globals)]
const $name: $crate::esp_hal::InterruptHandler = {
#[esp_hal::handler(priority = $priority)]
fn __emac_isr_internal() {
$body
}
__emac_isr_internal
};
};
}
#[macro_export]
macro_rules! emac_async_isr {
($name:ident, $priority:expr, $state:expr) => {
#[allow(non_upper_case_globals)]
const $name: $crate::esp_hal::InterruptHandler = {
#[esp_hal::handler(priority = $priority)]
fn __emac_async_isr_internal() {
$crate::async_interrupt_handler($state);
}
__emac_async_isr_internal
};
};
}
#[cfg(test)]
mod tests {
}