#![deny(clippy::pedantic)] #![allow(async_fn_in_trait)] #![cfg_attr(not(any(test, feature = "std")), no_std)] #![allow(clippy::module_name_repetitions)] #![allow(clippy::missing_errors_doc)] #![allow(clippy::missing_panics_doc)] #![doc = include_str!("../README.md")] // must go first! mod fmt; mod crc32; mod crc8; mod mdio; mod phy; mod regs; use ch::driver::LinkState; pub use crc32::ETH_FCS; use crc8::crc8; use embassy_futures::select::{select, Either}; use embassy_net_driver_channel as ch; use embassy_time::Timer; use embedded_hal_1::digital::OutputPin; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::{Error, Operation, SpiDevice}; use heapless::Vec; pub use mdio::MdioBus; pub use phy::{Phy10BaseT1x, RegsC22, RegsC45}; pub use regs::{Config0, Config2, SpiRegisters as sr, Status0, Status1}; use crate::fmt::Bytes; use crate::regs::{LedCntrl, LedFunc, LedPol, LedPolarity, SpiHeader}; /// ADIN1110 intern PHY ID pub const PHYID: u32 = 0x0283_BC91; /// Error values ADIN1110 #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[allow(non_camel_case_types)] pub enum AdinError { /// SPI-BUS Error Spi(E), /// Ethernet FCS error FCS, /// SPI Header CRC error SPI_CRC, /// Received or sended ethernet packet is too big PACKET_TOO_BIG, /// Received or sended ethernet packet is too small PACKET_TOO_SMALL, /// MDIO transaction timeout MDIO_ACC_TIMEOUT, } /// Type alias `Result` type with `AdinError` as error type. pub type AEResult = core::result::Result>; /// Internet PHY address pub const MDIO_PHY_ADDR: u8 = 0x01; /// Maximum Transmission Unit pub const MTU: usize = 1514; /// Max SPI/Frame buffer size pub const MAX_BUFF: usize = 2048; const DONT_CARE_BYTE: u8 = 0x00; const TURN_AROUND_BYTE: u8 = 0x00; /// Packet minimal frame/packet length const ETH_MIN_LEN: usize = 64; /// Ethernet `Frame Check Sequence` length const FCS_LEN: usize = 4; /// Packet minimal frame/packet length without `Frame Check Sequence` length const ETH_MIN_WITHOUT_FCS_LEN: usize = ETH_MIN_LEN - FCS_LEN; /// SPI Header, contains SPI action and register id. const SPI_HEADER_LEN: usize = 2; /// SPI Header CRC length const SPI_HEADER_CRC_LEN: usize = 1; /// SPI Header Turn Around length const SPI_HEADER_TA_LEN: usize = 1; /// Frame Header length const FRAME_HEADER_LEN: usize = 2; /// Space for last bytes to create multipule 4 bytes on the end of a FIFO read/write. const SPI_SPACE_MULTIPULE: usize = 3; /// P1 = 0x00, P2 = 0x01 const PORT_ID_BYTE: u8 = 0x00; /// Type alias for the embassy-net driver for ADIN1110 pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; /// Internal state for the embassy-net integration. pub struct State { ch_state: ch::State, } impl State { /// Create a new `State`. #[must_use] pub const fn new() -> Self { Self { ch_state: ch::State::new(), } } } /// ADIN1110 embassy-net driver #[derive(Debug)] pub struct ADIN1110 { /// SPI bus spi: SPI, /// Enable CRC on SPI transfer. /// This must match with the hardware pin `SPI_CFG0` were low = CRC enable, high = CRC disabled. spi_crc: bool, /// Append FCS by the application of transmit packet, false = FCS is appended by the MAC, true = FCS appended by the application. append_fcs_on_tx: bool, } impl ADIN1110 { /// Create a new ADIN1110 instance. pub fn new(spi: SPI, spi_crc: bool, append_fcs_on_tx: bool) -> Self { Self { spi, spi_crc, append_fcs_on_tx, } } /// Read a SPI register pub async fn read_reg(&mut self, reg: sr) -> AEResult { let mut tx_buf = Vec::::new(); let mut spi_hdr = SpiHeader(0); spi_hdr.set_control(true); spi_hdr.set_addr(reg); let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice()); if self.spi_crc { // Add CRC for header data let _ = tx_buf.push(crc8(&tx_buf)); } // Turn around byte, give the chip the time to access/setup the answer data. let _ = tx_buf.push(TURN_AROUND_BYTE); let mut rx_buf = [0; 5]; let spi_read_len = if self.spi_crc { rx_buf.len() } else { rx_buf.len() - 1 }; let mut spi_op = [Operation::Write(&tx_buf), Operation::Read(&mut rx_buf[0..spi_read_len])]; self.spi.transaction(&mut spi_op).await.map_err(AdinError::Spi)?; if self.spi_crc { let crc = crc8(&rx_buf[0..4]); if crc != rx_buf[4] { return Err(AdinError::SPI_CRC); } } let value = u32::from_be_bytes(rx_buf[0..4].try_into().unwrap()); trace!("REG Read {} = {:08x} SPI {}", reg, value, Bytes(&tx_buf)); Ok(value) } /// Write a SPI register pub async fn write_reg(&mut self, reg: sr, value: u32) -> AEResult<(), SPI::Error> { let mut tx_buf = Vec::::new(); let mut spi_hdr = SpiHeader(0); spi_hdr.set_control(true); spi_hdr.set_write(true); spi_hdr.set_addr(reg); let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice()); if self.spi_crc { // Add CRC for header data let _ = tx_buf.push(crc8(&tx_buf)); } let val = value.to_be_bytes(); let _ = tx_buf.extend_from_slice(val.as_slice()); if self.spi_crc { // Add CRC for header data let _ = tx_buf.push(crc8(val.as_slice())); } trace!("REG Write {} = {:08x} SPI {}", reg, value, Bytes(&tx_buf)); self.spi.write(&tx_buf).await.map_err(AdinError::Spi) } /// helper function for write to `MDIO_ACC` register and wait for ready! async fn write_mdio_acc_reg(&mut self, mdio_acc_val: u32) -> AEResult { self.write_reg(sr::MDIO_ACC, mdio_acc_val).await?; // TODO: Add proper timeout! for _ in 0..100_000 { let val = self.read_reg(sr::MDIO_ACC).await?; if val & 0x8000_0000 != 0 { return Ok(val); } } Err(AdinError::MDIO_ACC_TIMEOUT) } /// Read out fifo ethernet packet memory received via the wire. pub async fn read_fifo(&mut self, frame: &mut [u8]) -> AEResult { const HEAD_LEN: usize = SPI_HEADER_LEN + SPI_HEADER_CRC_LEN + SPI_HEADER_TA_LEN; const TAIL_LEN: usize = FCS_LEN + SPI_SPACE_MULTIPULE; let mut tx_buf = Vec::::new(); // Size of the frame, also includes the `frame header` and `FCS`. let fifo_frame_size = self.read_reg(sr::RX_FSIZE).await? as usize; if fifo_frame_size < ETH_MIN_LEN + FRAME_HEADER_LEN { return Err(AdinError::PACKET_TOO_SMALL); } let packet_size = fifo_frame_size - FRAME_HEADER_LEN - FCS_LEN; if packet_size > frame.len() { trace!("MAX: {} WANT: {}", frame.len(), packet_size); return Err(AdinError::PACKET_TOO_BIG); } let mut spi_hdr = SpiHeader(0); spi_hdr.set_control(true); spi_hdr.set_addr(sr::RX); let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice()); if self.spi_crc { // Add CRC for header data let _ = tx_buf.push(crc8(&tx_buf)); } // Turn around byte, TODO: Unknown that this is. let _ = tx_buf.push(TURN_AROUND_BYTE); let mut frame_header = [0, 0]; let mut fcs_and_extra = [0; TAIL_LEN]; // Packet read of write to the MAC packet buffer must be a multipul of 4! let tail_size = (fifo_frame_size & 0x03) + FCS_LEN; let mut spi_op = [ Operation::Write(&tx_buf), Operation::Read(&mut frame_header), Operation::Read(&mut frame[0..packet_size]), Operation::Read(&mut fcs_and_extra[0..tail_size]), ]; self.spi.transaction(&mut spi_op).await.map_err(AdinError::Spi)?; // According to register `CONFIG2`, bit 5 `CRC_APPEND` discription: // "Similarly, on receive, the CRC32 is forwarded with the frame to the host where the host must verify it is correct." // The application must allways check the FCS. It seems that the MAC/PHY has no option to handle this. let fcs_calc = ETH_FCS::new(&frame[0..packet_size]); if fcs_calc.hton_bytes() == fcs_and_extra[0..4] { Ok(packet_size) } else { Err(AdinError::FCS) } } /// Write to fifo ethernet packet memory send over the wire. pub async fn write_fifo(&mut self, frame: &[u8]) -> AEResult<(), SPI::Error> { const HEAD_LEN: usize = SPI_HEADER_LEN + SPI_HEADER_CRC_LEN + FRAME_HEADER_LEN; const TAIL_LEN: usize = ETH_MIN_LEN - FCS_LEN + FCS_LEN + SPI_SPACE_MULTIPULE; if frame.len() < (6 + 6 + 2) { return Err(AdinError::PACKET_TOO_SMALL); } if frame.len() > (MAX_BUFF - FRAME_HEADER_LEN) { return Err(AdinError::PACKET_TOO_BIG); } // SPI HEADER + [OPTIONAL SPI CRC] + FRAME HEADER let mut head_data = Vec::::new(); // [OPTIONAL PAD DATA] + FCS + [OPTINAL BYTES MAKE SPI FRAME EVEN] let mut tail_data = Vec::::new(); let mut spi_hdr = SpiHeader(0); spi_hdr.set_control(true); spi_hdr.set_write(true); spi_hdr.set_addr(sr::TX); head_data .extend_from_slice(spi_hdr.0.to_be_bytes().as_slice()) .map_err(|_e| AdinError::PACKET_TOO_BIG)?; if self.spi_crc { // Add CRC for header data head_data .push(crc8(&head_data[0..2])) .map_err(|_| AdinError::PACKET_TOO_BIG)?; } // Add port number, ADIN1110 its fixed to zero/P1, but for ADIN2111 has two ports. head_data .extend_from_slice(u16::from(PORT_ID_BYTE).to_be_bytes().as_slice()) .map_err(|_e| AdinError::PACKET_TOO_BIG)?; // ADIN1110 MAC and PHY don´t accept ethernet packet smaller than 64 bytes. // So padded the data minus the FCS, FCS is automatilly added to by the MAC. if frame.len() < ETH_MIN_WITHOUT_FCS_LEN { let _ = tail_data.resize(ETH_MIN_WITHOUT_FCS_LEN - frame.len(), 0x00); } // Append FCS by the application if self.append_fcs_on_tx { let mut frame_fcs = ETH_FCS::new(frame); if !tail_data.is_empty() { frame_fcs = frame_fcs.update(&tail_data); } let _ = tail_data.extend_from_slice(frame_fcs.hton_bytes().as_slice()); } // len = frame_size + optional padding + 2 bytes Frame header let send_len_orig = frame.len() + tail_data.len() + FRAME_HEADER_LEN; let send_len = u32::try_from(send_len_orig).map_err(|_| AdinError::PACKET_TOO_BIG)?; // Packet read of write to the MAC packet buffer must be a multipul of 4 bytes! let pad_len = send_len_orig & 0x03; if pad_len != 0 { let spi_pad_len = 4 - pad_len + tail_data.len(); let _ = tail_data.resize(spi_pad_len, DONT_CARE_BYTE); } self.write_reg(sr::TX_FSIZE, send_len).await?; trace!( "TX: hdr {} [{}] {}-{}-{} SIZE: {}", head_data.len(), frame.len(), Bytes(head_data.as_slice()), Bytes(frame), Bytes(tail_data.as_slice()), send_len, ); let mut transaction = [ Operation::Write(head_data.as_slice()), Operation::Write(frame), Operation::Write(tail_data.as_slice()), ]; self.spi.transaction(&mut transaction).await.map_err(AdinError::Spi) } /// Programs the mac address in the mac filters. /// Also set the boardcast address. /// The chip supports 2 priority queues but current code doesn't support this mode. pub async fn set_mac_addr(&mut self, mac: &[u8; 6]) -> AEResult<(), SPI::Error> { let mac_high_part = u16::from_be_bytes(mac[0..2].try_into().unwrap()); let mac_low_part = u32::from_be_bytes(mac[2..6].try_into().unwrap()); // program our mac address in the mac address filter self.write_reg(sr::ADDR_FILT_UPR0, (1 << 16) | (1 << 30) | u32::from(mac_high_part)) .await?; self.write_reg(sr::ADDR_FILT_LWR0, mac_low_part).await?; self.write_reg(sr::ADDR_MSK_UPR0, u32::from(mac_high_part)).await?; self.write_reg(sr::ADDR_MSK_LWR0, mac_low_part).await?; // Also program broadcast address in the mac address filter self.write_reg(sr::ADDR_FILT_UPR1, (1 << 16) | (1 << 30) | 0xFFFF) .await?; self.write_reg(sr::ADDR_FILT_LWR1, 0xFFFF_FFFF).await?; self.write_reg(sr::ADDR_MSK_UPR1, 0xFFFF).await?; self.write_reg(sr::ADDR_MSK_LWR1, 0xFFFF_FFFF).await?; Ok(()) } } impl mdio::MdioBus for ADIN1110 { type Error = AdinError; /// Read from the PHY Registers as Clause 22. async fn read_cl22(&mut self, phy_id: u8, reg: u8) -> Result { let mdio_acc_val: u32 = (0x1 << 28) | u32::from(phy_id & 0x1F) << 21 | u32::from(reg & 0x1F) << 16 | (0x3 << 26); // Result is in the lower half of the answer. #[allow(clippy::cast_possible_truncation)] self.write_mdio_acc_reg(mdio_acc_val).await.map(|val| val as u16) } /// Read from the PHY Registers as Clause 45. async fn read_cl45(&mut self, phy_id: u8, regc45: (u8, u16)) -> Result { let mdio_acc_val = u32::from(phy_id & 0x1F) << 21 | u32::from(regc45.0 & 0x1F) << 16 | u32::from(regc45.1); self.write_mdio_acc_reg(mdio_acc_val).await?; let mdio_acc_val = u32::from(phy_id & 0x1F) << 21 | u32::from(regc45.0 & 0x1F) << 16 | (0x03 << 26); // Result is in the lower half of the answer. #[allow(clippy::cast_possible_truncation)] self.write_mdio_acc_reg(mdio_acc_val).await.map(|val| val as u16) } /// Write to the PHY Registers as Clause 22. async fn write_cl22(&mut self, phy_id: u8, reg: u8, val: u16) -> Result<(), Self::Error> { let mdio_acc_val: u32 = (0x1 << 28) | u32::from(phy_id & 0x1F) << 21 | u32::from(reg & 0x1F) << 16 | (0x1 << 26) | u32::from(val); self.write_mdio_acc_reg(mdio_acc_val).await.map(|_| ()) } /// Write to the PHY Registers as Clause 45. async fn write_cl45(&mut self, phy_id: u8, regc45: (u8, u16), value: u16) -> AEResult<(), SPI::Error> { let phy_id = u32::from(phy_id & 0x1F) << 21; let dev_addr = u32::from(regc45.0 & 0x1F) << 16; let reg = u32::from(regc45.1); let mdio_acc_val: u32 = phy_id | dev_addr | reg; self.write_mdio_acc_reg(mdio_acc_val).await?; let mdio_acc_val: u32 = phy_id | dev_addr | (0x01 << 26) | u32::from(value); self.write_mdio_acc_reg(mdio_acc_val).await.map(|_| ()) } } /// Background runner for the ADIN1110. /// /// You must call `.run()` in a background task for the ADIN1110 to operate. pub struct Runner<'d, SPI, INT, RST> { mac: ADIN1110, ch: ch::Runner<'d, MTU>, int: INT, is_link_up: bool, _reset: RST, } impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { #[allow(clippy::too_many_lines)] pub async fn run(mut self) -> ! { loop { let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); loop { debug!("Waiting for interrupts"); match select(self.int.wait_for_low(), tx_chan.tx_buf()).await { Either::First(_) => { let mut status1_clr = Status1(0); let mut status1 = Status1(self.mac.read_reg(sr::STATUS1).await.unwrap()); while status1.p1_rx_rdy() { debug!("alloc RX packet buffer"); match select(rx_chan.rx_buf(), tx_chan.tx_buf()).await { // Handle frames that needs to transmit from the wire. // Note: rx_chan.rx_buf() channel don´t accept new request // when the tx_chan is full. So these will be handled // automaticly. Either::First(frame) => match self.mac.read_fifo(frame).await { Ok(n) => { rx_chan.rx_done(n); } Err(e) => match e { AdinError::PACKET_TOO_BIG => { error!("RX Packet too big, DROP"); self.mac.write_reg(sr::FIFO_CLR, 1).await.unwrap(); } AdinError::PACKET_TOO_SMALL => { error!("RX Packet too small, DROP"); self.mac.write_reg(sr::FIFO_CLR, 1).await.unwrap(); } AdinError::Spi(e) => { error!("RX Spi error {}", e.kind()); } e => { error!("RX Error {:?}", e); } }, }, Either::Second(frame) => { // Handle frames that needs to transmit to the wire. self.mac.write_fifo(frame).await.unwrap(); tx_chan.tx_done(); } } status1 = Status1(self.mac.read_reg(sr::STATUS1).await.unwrap()); } let status0 = Status0(self.mac.read_reg(sr::STATUS0).await.unwrap()); if status1.0 & !0x1b != 0 { error!("SPE CHIP STATUS 0:{:08x} 1:{:08x}", status0.0, status1.0); } if status1.tx_rdy() { status1_clr.set_tx_rdy(true); trace!("TX_DONE"); } if status1.link_change() { let link = status1.p1_link_status(); self.is_link_up = link; if link { let link_status = self .mac .read_cl45(MDIO_PHY_ADDR, RegsC45::DA7::AN_STATUS_EXTRA.into()) .await .unwrap(); let volt = if link_status & (0b11 << 5) == (0b11 << 5) { "2.4" } else { "1.0" }; let mse = self .mac .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1::MSE_VAL.into()) .await .unwrap(); info!("LINK Changed: Link Up, Volt: {} V p-p, MSE: {:0004}", volt, mse); } else { info!("LINK Changed: Link Down"); } state_chan.set_link_state(if link { LinkState::Up } else { LinkState::Down }); status1_clr.set_link_change(true); } if status1.tx_ecc_err() { error!("SPI TX_ECC_ERR error, CLEAR TX FIFO"); self.mac.write_reg(sr::FIFO_CLR, 2).await.unwrap(); status1_clr.set_tx_ecc_err(true); } if status1.rx_ecc_err() { error!("SPI RX_ECC_ERR error"); status1_clr.set_rx_ecc_err(true); } if status1.spi_err() { error!("SPI SPI_ERR CRC error"); status1_clr.set_spi_err(true); } if status0.phyint() { let crsm_irq_st = self .mac .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::CRSM_IRQ_STATUS.into()) .await .unwrap(); let phy_irq_st = self .mac .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1F::PHY_SYBSYS_IRQ_STATUS.into()) .await .unwrap(); warn!( "SPE CHIP PHY CRSM_IRQ_STATUS {:04x} PHY_SUBSYS_IRQ_STATUS {:04x}", crsm_irq_st, phy_irq_st ); } if status0.txfcse() { error!("Ethernet Frame FCS and calc FCS don't match!"); } // Clear status0 self.mac.write_reg(sr::STATUS0, 0xFFF).await.unwrap(); self.mac.write_reg(sr::STATUS1, status1_clr.0).await.unwrap(); } Either::Second(packet) => { // Handle frames that needs to transmit to the wire. self.mac.write_fifo(packet).await.unwrap(); tx_chan.tx_done(); } } } } } } /// Obtain a driver for using the ADIN1110 with [`embassy-net`](crates.io/crates/embassy-net). pub async fn new( mac_addr: [u8; 6], state: &'_ mut State, spi_dev: SPI, int: INT, mut reset: RST, spi_crc: bool, append_fcs_on_tx: bool, ) -> (Device<'_>, Runner<'_, SPI, INT, RST>) { use crate::regs::{IMask0, IMask1}; info!("INIT ADIN1110"); // Reset sequence reset.set_low().unwrap(); // Wait t1: 20-43mS Timer::after_millis(30).await; reset.set_high().unwrap(); // Wait t3: 50mS Timer::after_millis(50).await; // Create device let mut mac = ADIN1110::new(spi_dev, spi_crc, append_fcs_on_tx); // Check PHYID let id = mac.read_reg(sr::PHYID).await.unwrap(); assert_eq!(id, PHYID); debug!("SPE: CHIP MAC/ID: {:08x}", id); #[cfg(any(feature = "defmt", feature = "log"))] { let adin_phy = Phy10BaseT1x::default(); let phy_id = adin_phy.get_id(&mut mac).await.unwrap(); debug!("SPE: CHIP: PHY ID: {:08x}", phy_id); } let mi_control = mac.read_cl22(MDIO_PHY_ADDR, RegsC22::CONTROL as u8).await.unwrap(); debug!("SPE CHIP PHY MI_CONTROL {:04x}", mi_control); if mi_control & 0x0800 != 0 { let val = mi_control & !0x0800; debug!("SPE CHIP PHY MI_CONTROL Disable PowerDown"); mac.write_cl22(MDIO_PHY_ADDR, RegsC22::CONTROL as u8, val) .await .unwrap(); } // Config0 let mut config0 = Config0(0x0000_0006); config0.set_txfcsve(mac.append_fcs_on_tx); mac.write_reg(sr::CONFIG0, config0.0).await.unwrap(); // Config2 let mut config2 = Config2(0x0000_0800); // crc_append must be disable if tx_fcs_validation_enable is true! config2.set_crc_append(!mac.append_fcs_on_tx); mac.write_reg(sr::CONFIG2, config2.0).await.unwrap(); // Pin Mux Config 1 let led_val = (0b11 << 6) | (0b11 << 4); // | (0b00 << 1); mac.write_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::DIGIO_PINMUX.into(), led_val) .await .unwrap(); let mut led_pol = LedPolarity(0); led_pol.set_led1_polarity(LedPol::ActiveLow); led_pol.set_led0_polarity(LedPol::ActiveLow); // Led Polarity Regisgere Active Low mac.write_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::LED_POLARITY.into(), led_pol.0) .await .unwrap(); // Led Both On let mut led_cntr = LedCntrl(0x0); // LED1: Yellow led_cntr.set_led1_en(true); led_cntr.set_led1_function(LedFunc::TxLevel2P4); // LED0: Green led_cntr.set_led0_en(true); led_cntr.set_led0_function(LedFunc::LinkupTxRxActicity); mac.write_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::LED_CNTRL.into(), led_cntr.0) .await .unwrap(); // Set ADIN1110 Interrupts, RX_READY and LINK_CHANGE // Enable interrupts LINK_CHANGE, TX_RDY, RX_RDY(P1), SPI_ERR // Have to clear the mask the enable it. let mut imask0_val = IMask0(0x0000_1FBF); imask0_val.set_txfcsem(false); imask0_val.set_phyintm(false); imask0_val.set_txboem(false); imask0_val.set_rxboem(false); imask0_val.set_txpem(false); mac.write_reg(sr::IMASK0, imask0_val.0).await.unwrap(); // Set ADIN1110 Interrupts, RX_READY and LINK_CHANGE // Enable interrupts LINK_CHANGE, TX_RDY, RX_RDY(P1), SPI_ERR // Have to clear the mask the enable it. let mut imask1_val = IMask1(0x43FA_1F1A); imask1_val.set_link_change_mask(false); imask1_val.set_p1_rx_rdy_mask(false); imask1_val.set_spi_err_mask(false); imask1_val.set_tx_ecc_err_mask(false); imask1_val.set_rx_ecc_err_mask(false); mac.write_reg(sr::IMASK1, imask1_val.0).await.unwrap(); // Program mac address but also sets mac filters. mac.set_mac_addr(&mac_addr).await.unwrap(); let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr)); ( device, Runner { ch: runner, mac, int, is_link_up: false, _reset: reset, }, ) } #[allow(clippy::similar_names)] #[cfg(test)] mod tests { use core::convert::Infallible; use embedded_hal_1::digital::{ErrorType, OutputPin}; use embedded_hal_async::delay::DelayNs; use embedded_hal_bus::spi::ExclusiveDevice; use embedded_hal_mock::common::Generic; use embedded_hal_mock::eh1::spi::{Mock as SpiMock, Transaction as SpiTransaction}; #[derive(Debug, Default)] struct CsPinMock { pub high: u32, pub low: u32, } impl OutputPin for CsPinMock { fn set_low(&mut self) -> Result<(), Self::Error> { self.low += 1; Ok(()) } fn set_high(&mut self) -> Result<(), Self::Error> { self.high += 1; Ok(()) } } impl ErrorType for CsPinMock { type Error = Infallible; } use super::*; // TODO: This is currently a workaround unit `ExclusiveDevice` is moved to `embedded-hal-bus` // see https://github.com/rust-embedded/embedded-hal/pull/462#issuecomment-1560014426 struct MockDelay {} impl DelayNs for MockDelay { async fn delay_ns(&mut self, _ns: u32) { todo!() } async fn delay_us(&mut self, _us: u32) { todo!() } async fn delay_ms(&mut self, _ms: u32) { todo!() } } struct TestHarnass { spe: ADIN1110, CsPinMock, MockDelay>>, spi: Generic, } impl TestHarnass { pub fn new(expectations: &[SpiTransaction], spi_crc: bool, append_fcs_on_tx: bool) -> Self { let cs = CsPinMock::default(); let delay = MockDelay {}; let spi = SpiMock::new(expectations); let spi_dev: ExclusiveDevice, CsPinMock, MockDelay> = ExclusiveDevice::new(spi.clone(), cs, delay); let spe: ADIN1110< ExclusiveDevice, CsPinMock, MockDelay>, > = ADIN1110::new(spi_dev, spi_crc, append_fcs_on_tx); Self { spe, spi } } pub fn done(&mut self) { self.spi.done(); } } #[futures_test::test] async fn mac_read_registers_without_crc() { // Configure expectations let expectations = [ // 1st SpiTransaction::write_vec(vec![0x80, 0x01, TURN_AROUND_BYTE]), SpiTransaction::read_vec(vec![0x02, 0x83, 0xBC, 0x91]), SpiTransaction::flush(), // 2nd SpiTransaction::write_vec(vec![0x80, 0x02, TURN_AROUND_BYTE]), SpiTransaction::read_vec(vec![0x00, 0x00, 0x06, 0xC3]), SpiTransaction::flush(), ]; // Create TestHarnass let mut th = TestHarnass::new(&expectations, false, true); // Read PHIID let val = th.spe.read_reg(sr::PHYID).await.expect("Error"); assert_eq!(val, 0x0283_BC91); // Read CAPAVILITY let val = th.spe.read_reg(sr::CAPABILITY).await.expect("Error"); assert_eq!(val, 0x0000_06C3); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn mac_read_registers_with_crc() { // Configure expectations let expectations = [ // 1st SpiTransaction::write_vec(vec![0x80, 0x01, 177, TURN_AROUND_BYTE]), SpiTransaction::read_vec(vec![0x02, 0x83, 0xBC, 0x91, 215]), SpiTransaction::flush(), // 2nd SpiTransaction::write_vec(vec![0x80, 0x02, 184, TURN_AROUND_BYTE]), SpiTransaction::read_vec(vec![0x00, 0x00, 0x06, 0xC3, 57]), SpiTransaction::flush(), ]; // Create TestHarnass let mut th = TestHarnass::new(&expectations, true, true); assert_eq!(crc8(0x0283_BC91_u32.to_be_bytes().as_slice()), 215); assert_eq!(crc8(0x0000_06C3_u32.to_be_bytes().as_slice()), 57); // Read PHIID let val = th.spe.read_reg(sr::PHYID).await.expect("Error"); assert_eq!(val, 0x0283_BC91); // Read CAPAVILITY let val = th.spe.read_reg(sr::CAPABILITY).await.expect("Error"); assert_eq!(val, 0x0000_06C3); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn mac_write_registers_without_crc() { // Configure expectations let expectations = [ SpiTransaction::write_vec(vec![0xA0, 0x09, 0x12, 0x34, 0x56, 0x78]), SpiTransaction::flush(), ]; // Create TestHarnass let mut th = TestHarnass::new(&expectations, false, true); // Write reg: 0x1FFF assert!(th.spe.write_reg(sr::STATUS1, 0x1234_5678).await.is_ok()); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn mac_write_registers_with_crc() { // Configure expectations let expectations = [ SpiTransaction::write_vec(vec![0xA0, 0x09, 39, 0x12, 0x34, 0x56, 0x78, 28]), SpiTransaction::flush(), ]; // Create TestHarnass let mut th = TestHarnass::new(&expectations, true, true); // Write reg: 0x1FFF assert!(th.spe.write_reg(sr::STATUS1, 0x1234_5678).await.is_ok()); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn write_packet_to_fifo_minimal_with_crc() { // Configure expectations let mut expectations = vec![]; // Write TX_SIZE reg expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 0, 66, 201])); expectations.push(SpiTransaction::flush()); // Write TX reg. // SPI Header + optional CRC + Frame Header expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0])); // Packet data let packet = [0xFF_u8; 60]; expectations.push(SpiTransaction::write_vec(packet.to_vec())); let mut tail = std::vec::Vec::::with_capacity(100); // Padding if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { tail.resize(padding_len, 0x00); } // Packet FCS + optinal padding tail.extend_from_slice(&[77, 241, 140, 244, DONT_CARE_BYTE, DONT_CARE_BYTE]); expectations.push(SpiTransaction::write_vec(tail)); expectations.push(SpiTransaction::flush()); // Create TestHarnass let mut th = TestHarnass::new(&expectations, true, true); assert!(th.spe.write_fifo(&packet).await.is_ok()); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn write_packet_to_fifo_minimal_with_crc_without_fcs() { // Configure expectations let mut expectations = vec![]; // Write TX_SIZE reg expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 0, 62, 186])); expectations.push(SpiTransaction::flush()); // Write TX reg. // SPI Header + optional CRC + Frame Header expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0])); // Packet data let packet = [0xFF_u8; 60]; expectations.push(SpiTransaction::write_vec(packet.to_vec())); let mut tail = std::vec::Vec::::with_capacity(100); // Padding if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { tail.resize(padding_len, 0x00); } // Packet FCS + optinal padding tail.extend_from_slice(&[DONT_CARE_BYTE, DONT_CARE_BYTE]); expectations.push(SpiTransaction::write_vec(tail)); expectations.push(SpiTransaction::flush()); // Create TestHarnass let mut th = TestHarnass::new(&expectations, true, false); assert!(th.spe.write_fifo(&packet).await.is_ok()); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn write_packet_to_fifo_max_mtu_with_crc() { assert_eq!(MTU, 1514); // Configure expectations let mut expectations = vec![]; // Write TX_SIZE reg expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 5, 240, 159])); expectations.push(SpiTransaction::flush()); // Write TX reg. // SPI Header + optional CRC + Frame Header expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0])); // Packet data let packet = [0xAA_u8; MTU]; expectations.push(SpiTransaction::write_vec(packet.to_vec())); let mut tail = std::vec::Vec::::with_capacity(100); // Padding if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { tail.resize(padding_len, 0x00); } // Packet FCS + optinal padding tail.extend_from_slice(&[49, 196, 205, 160]); expectations.push(SpiTransaction::write_vec(tail)); expectations.push(SpiTransaction::flush()); // Create TestHarnass let mut th = TestHarnass::new(&expectations, true, true); assert!(th.spe.write_fifo(&packet).await.is_ok()); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn write_packet_to_fifo_invalid_lengths() { assert_eq!(MTU, 1514); // Configure expectations let expectations = vec![]; // Max packet size = MAX_BUFF - FRAME_HEADER_LEN let packet = [0xAA_u8; MAX_BUFF - FRAME_HEADER_LEN + 1]; // Create TestHarnass let mut th = TestHarnass::new(&expectations, true, true); // minimal assert!(matches!( th.spe.write_fifo(&packet[0..(6 + 6 + 2 - 1)]).await, Err(AdinError::PACKET_TOO_SMALL) )); // max + 1 assert!(matches!( th.spe.write_fifo(&packet).await, Err(AdinError::PACKET_TOO_BIG) )); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn write_packet_to_fifo_arp_46bytes_with_crc() { // Configure expectations let mut expectations = vec![]; // Write TX_SIZE reg expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 0, 66, 201])); expectations.push(SpiTransaction::flush()); // Write TX reg. // Header expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0])); // Packet data let packet = [ 34, 51, 68, 85, 102, 119, 18, 52, 86, 120, 154, 188, 8, 6, 0, 1, 8, 0, 6, 4, 0, 2, 18, 52, 86, 120, 154, 188, 192, 168, 16, 4, 34, 51, 68, 85, 102, 119, 192, 168, 16, 1, ]; expectations.push(SpiTransaction::write_vec(packet.to_vec())); let mut tail = std::vec::Vec::::with_capacity(100); // Padding if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { tail.resize(padding_len, 0x00); } // Packet FCS + optinal padding tail.extend_from_slice(&[147, 149, 213, 68, DONT_CARE_BYTE, DONT_CARE_BYTE]); expectations.push(SpiTransaction::write_vec(tail)); expectations.push(SpiTransaction::flush()); // Create TestHarnass let mut th = TestHarnass::new(&expectations, true, true); assert!(th.spe.write_fifo(&packet).await.is_ok()); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn write_packet_to_fifo_arp_46bytes_without_crc() { // Configure expectations let mut expectations = vec![]; // Write TX_SIZE reg expectations.push(SpiTransaction::write_vec(vec![160, 48, 0, 0, 0, 66])); expectations.push(SpiTransaction::flush()); // Write TX reg. // SPI Header + Frame Header expectations.push(SpiTransaction::write_vec(vec![160, 49, 0, 0])); // Packet data let packet = [ 34, 51, 68, 85, 102, 119, 18, 52, 86, 120, 154, 188, 8, 6, 0, 1, 8, 0, 6, 4, 0, 2, 18, 52, 86, 120, 154, 188, 192, 168, 16, 4, 34, 51, 68, 85, 102, 119, 192, 168, 16, 1, ]; expectations.push(SpiTransaction::write_vec(packet.to_vec())); let mut tail = std::vec::Vec::::with_capacity(100); // Padding if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { tail.resize(padding_len, 0x00); } // Packet FCS + optinal padding tail.extend_from_slice(&[147, 149, 213, 68, DONT_CARE_BYTE, DONT_CARE_BYTE]); expectations.push(SpiTransaction::write_vec(tail)); expectations.push(SpiTransaction::flush()); // Create TestHarnass let mut th = TestHarnass::new(&expectations, false, true); assert!(th.spe.write_fifo(&packet).await.is_ok()); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn read_packet_from_fifo_packet_too_big_for_frame_buffer() { // Configure expectations let mut expectations = vec![]; // Read RX_SIZE reg let rx_size: u32 = u32::try_from(ETH_MIN_LEN + FRAME_HEADER_LEN + FCS_LEN).unwrap(); let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); rx_size_vec.push(crc8(&rx_size_vec)); expectations.push(SpiTransaction::write_vec(vec![128, 144, 79, TURN_AROUND_BYTE])); expectations.push(SpiTransaction::read_vec(rx_size_vec)); expectations.push(SpiTransaction::flush()); let mut frame = [0; MTU]; // Create TestHarnass let mut th = TestHarnass::new(&expectations, true, true); let ret = th.spe.read_fifo(&mut frame[0..ETH_MIN_LEN - 1]).await; assert!(matches!(dbg!(ret), Err(AdinError::PACKET_TOO_BIG))); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn read_packet_from_fifo_packet_too_small() { // Configure expectations let mut expectations = vec![]; // This value is importen for this test! assert_eq!(ETH_MIN_LEN, 64); // Packet data, size = `ETH_MIN_LEN` - `FCS_LEN` - 1 let packet = [0; 64 - FCS_LEN - 1]; // Read RX_SIZE reg let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FCS_LEN).unwrap(); let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); rx_size_vec.push(crc8(&rx_size_vec)); expectations.push(SpiTransaction::write_vec(vec![128, 144, 79, TURN_AROUND_BYTE])); expectations.push(SpiTransaction::read_vec(rx_size_vec)); expectations.push(SpiTransaction::flush()); let mut frame = [0; MTU]; // Create TestHarnass let mut th = TestHarnass::new(&expectations, true, true); let ret = th.spe.read_fifo(&mut frame).await; assert!(matches!(dbg!(ret), Err(AdinError::PACKET_TOO_SMALL))); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn read_packet_from_fifo_packet_corrupted_fcs() { let mut frame = [0; MTU]; // Configure expectations let mut expectations = vec![]; let packet = [0xDE; 60]; let crc_en = true; // Read RX_SIZE reg let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FCS_LEN).unwrap(); let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); if crc_en { rx_size_vec.push(crc8(&rx_size_vec)); } // SPI Header with CRC let mut rx_fsize = vec![128, 144, 79, TURN_AROUND_BYTE]; if !crc_en { // remove the CRC on idx 2 rx_fsize.swap_remove(2); } expectations.push(SpiTransaction::write_vec(rx_fsize)); expectations.push(SpiTransaction::read_vec(rx_size_vec)); expectations.push(SpiTransaction::flush()); // Read RX reg, SPI Header with CRC let mut rx_reg = vec![128, 145, 72, TURN_AROUND_BYTE]; if !crc_en { // remove the CRC on idx 2 rx_reg.swap_remove(2); } expectations.push(SpiTransaction::write_vec(rx_reg)); // Frame Header expectations.push(SpiTransaction::read_vec(vec![0, 0])); // Packet data expectations.push(SpiTransaction::read_vec(packet.to_vec())); let packet_crc = ETH_FCS::new(&packet); let mut tail = std::vec::Vec::::with_capacity(100); tail.extend_from_slice(&packet_crc.hton_bytes()); // increase last byte with 1. if let Some(crc) = tail.last_mut() { *crc = crc.wrapping_add(1); } // Need extra bytes? let pad = (packet.len() + FCS_LEN + FRAME_HEADER_LEN) & 0x03; if pad != 0 { // Packet FCS + optinal padding tail.resize(tail.len() + pad, DONT_CARE_BYTE); } expectations.push(SpiTransaction::read_vec(tail)); expectations.push(SpiTransaction::flush()); // Create TestHarnass let mut th = TestHarnass::new(&expectations, crc_en, false); let ret = th.spe.read_fifo(&mut frame).await.expect_err("Error!"); assert!(matches!(ret, AdinError::FCS)); // Mark end of the SPI test. th.done(); } #[futures_test::test] async fn read_packet_to_fifo_check_spi_read_multipule_of_u32_valid_lengths() { let packet_buffer = [0; MTU]; let mut frame = [0; MTU]; let mut expectations = std::vec::Vec::with_capacity(16); // Packet data, size = `ETH_MIN_LEN` - `FCS_LEN` for packet_size in [60, 61, 62, 63, 64, MTU - 4, MTU - 3, MTU - 2, MTU - 1, MTU] { for crc_en in [false, true] { expectations.clear(); let packet = &packet_buffer[0..packet_size]; // Read RX_SIZE reg let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FCS_LEN).unwrap(); let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); if crc_en { rx_size_vec.push(crc8(&rx_size_vec)); } // SPI Header with CRC let mut rx_fsize = vec![128, 144, 79, TURN_AROUND_BYTE]; if !crc_en { // remove the CRC on idx 2 rx_fsize.swap_remove(2); } expectations.push(SpiTransaction::write_vec(rx_fsize)); expectations.push(SpiTransaction::read_vec(rx_size_vec)); expectations.push(SpiTransaction::flush()); // Read RX reg, SPI Header with CRC let mut rx_reg = vec![128, 145, 72, TURN_AROUND_BYTE]; if !crc_en { // remove the CRC on idx 2 rx_reg.swap_remove(2); } expectations.push(SpiTransaction::write_vec(rx_reg)); // Frame Header expectations.push(SpiTransaction::read_vec(vec![0, 0])); // Packet data expectations.push(SpiTransaction::read_vec(packet.to_vec())); let packet_crc = ETH_FCS::new(packet); let mut tail = std::vec::Vec::::with_capacity(100); tail.extend_from_slice(&packet_crc.hton_bytes()); // Need extra bytes? let pad = (packet_size + FCS_LEN + FRAME_HEADER_LEN) & 0x03; if pad != 0 { // Packet FCS + optinal padding tail.resize(tail.len() + pad, DONT_CARE_BYTE); } expectations.push(SpiTransaction::read_vec(tail)); expectations.push(SpiTransaction::flush()); // Create TestHarnass let mut th = TestHarnass::new(&expectations, crc_en, false); let ret = th.spe.read_fifo(&mut frame).await.expect("Error!"); assert_eq!(ret, packet_size); // Mark end of the SPI test. th.done(); } } } #[futures_test::test] async fn spi_crc_error() { // Configure expectations let expectations = vec![ SpiTransaction::write_vec(vec![128, 144, 79, TURN_AROUND_BYTE]), SpiTransaction::read_vec(vec![0x00, 0x00, 0x00, 0x00, 0xDD]), SpiTransaction::flush(), ]; // Create TestHarnass let mut th = TestHarnass::new(&expectations, true, false); let ret = th.spe.read_reg(sr::RX_FSIZE).await; assert!(matches!(dbg!(ret), Err(AdinError::SPI_CRC))); // Mark end of the SPI test. th.done(); } }