1#[cfg(target_arch = "riscv32")]
8use core::cell::RefCell;
9#[cfg(target_arch = "riscv32")]
10use core::future::poll_fn;
11#[cfg(target_arch = "riscv32")]
12use core::task::Poll;
13
14use static_cell::ConstStaticCell;
15
16#[cfg(target_arch = "riscv32")]
17use embassy_futures::{join::join3, yield_now};
18use embassy_sync::waitqueue::AtomicWaker;
22#[cfg(target_arch = "riscv32")]
23use embassy_sync::blocking_mutex::{raw::NoopRawMutex, Mutex};
24
25use crate::ch;
26
27#[path = "../clock.rs"]
28pub mod clock;
29#[path = "../descriptors.rs"]
30pub mod descriptors;
31#[path = "../dma.rs"]
32pub mod dma;
33#[path = "../phy.rs"]
34pub mod phy;
35#[path = "../pins.rs"]
36pub mod pins;
37#[path = "../regs.rs"]
38pub mod regs;
39
40use self::{
41 clock::{configure_clock_ext_in, configure_speed_divider, disable_emac_clock_tree},
42 descriptors::{
43 DescriptorError, RDes, RDesRing, RxRingStats, StaticDmaResources, TDes, TDesRing,
44 TxRingStats, BUF_SIZE, RX_DESC_COUNT, TX_DESC_COUNT,
45 },
46 dma::{Dma, DmaInterruptStatus},
47 phy::{Ip101, LinkState, Phy, PhyError, Speed},
48 pins::{configure_mdio_pin_set, configure_rmii_pin_set},
49};
50
51const DEFAULT_AHB_CLOCK_HZ: u32 = 40_000_000;
57const MAC_STOP_TIMEOUT_POLLS: usize = 1_000;
58const MAC_DEFAULT_IFG_96_BIT_TIMES: u32 = 0;
59const MACFFILT_DEFAULT: u32 = regs::bits::macffilt::PM;
64
65const DMA_CACHE_BOUNDARY: u32 = 0x4FF8_0000;
69
70const _: () = assert!(0x4FF4_0000 + 0x1_0000 <= DMA_CACHE_BOUNDARY);
74const LINK_POLL_INTERVAL_MS: u64 = 100;
81
82const _: () = {
89 assert!(
90 LINK_POLL_INTERVAL_MS >= 50 && LINK_POLL_INTERVAL_MS <= 500,
91 "LINK_POLL_INTERVAL_MS must stay in [50, 500] ms",
92 );
93};
94
95#[repr(u8)]
97#[derive(Clone, Copy, Debug, Eq, PartialEq)]
98pub enum Duplex {
99 Half,
101 Full,
103}
104
105pub const MTU: usize = BUF_SIZE;
107pub const CHANNEL_RX_COUNT: usize = 8;
109pub const CHANNEL_TX_COUNT: usize = 8;
111
112static STATE: ConstStaticCell<ch::State<MTU, CHANNEL_RX_COUNT, CHANNEL_TX_COUNT>> =
121 ConstStaticCell::new(ch::State::new());
122static RX_WAKER: AtomicWaker = AtomicWaker::new();
123static TX_WAKER: AtomicWaker = AtomicWaker::new();
124
125pub static RX_DESC_BASE: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
128
129pub const RX_DESC_STRIDE: usize = 128;
132
133pub static RX_FRAMES: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
135pub static TX_FRAMES: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
136pub static RX_BUF_REQUESTED: core::sync::atomic::AtomicU32 =
137 core::sync::atomic::AtomicU32::new(0);
138pub static TX_BUF_REQUESTED: core::sync::atomic::AtomicU32 =
139 core::sync::atomic::AtomicU32::new(0);
140pub static RX_ARP: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
142pub static RX_IPV4: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
143pub static RX_ICMP: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
144pub static RX_LAST_ETHERTYPE: core::sync::atomic::AtomicU32 =
147 core::sync::atomic::AtomicU32::new(0);
148pub static RX_LAST_DST_MAC_HI: core::sync::atomic::AtomicU32 =
149 core::sync::atomic::AtomicU32::new(0);
150pub static RX_LAST_DHCP_FRAME: [core::sync::atomic::AtomicU32; 16] = [
154 core::sync::atomic::AtomicU32::new(0),
155 core::sync::atomic::AtomicU32::new(0),
156 core::sync::atomic::AtomicU32::new(0),
157 core::sync::atomic::AtomicU32::new(0),
158 core::sync::atomic::AtomicU32::new(0),
159 core::sync::atomic::AtomicU32::new(0),
160 core::sync::atomic::AtomicU32::new(0),
161 core::sync::atomic::AtomicU32::new(0),
162 core::sync::atomic::AtomicU32::new(0),
163 core::sync::atomic::AtomicU32::new(0),
164 core::sync::atomic::AtomicU32::new(0),
165 core::sync::atomic::AtomicU32::new(0),
166 core::sync::atomic::AtomicU32::new(0),
167 core::sync::atomic::AtomicU32::new(0),
168 core::sync::atomic::AtomicU32::new(0),
169 core::sync::atomic::AtomicU32::new(0),
170];
171pub static RX_DHCP_FRAMES: core::sync::atomic::AtomicU32 =
172 core::sync::atomic::AtomicU32::new(0);
173
174pub static RX_LAST_LARGE_FRAME: [core::sync::atomic::AtomicU32; 16] = [
177 core::sync::atomic::AtomicU32::new(0),
178 core::sync::atomic::AtomicU32::new(0),
179 core::sync::atomic::AtomicU32::new(0),
180 core::sync::atomic::AtomicU32::new(0),
181 core::sync::atomic::AtomicU32::new(0),
182 core::sync::atomic::AtomicU32::new(0),
183 core::sync::atomic::AtomicU32::new(0),
184 core::sync::atomic::AtomicU32::new(0),
185 core::sync::atomic::AtomicU32::new(0),
186 core::sync::atomic::AtomicU32::new(0),
187 core::sync::atomic::AtomicU32::new(0),
188 core::sync::atomic::AtomicU32::new(0),
189 core::sync::atomic::AtomicU32::new(0),
190 core::sync::atomic::AtomicU32::new(0),
191 core::sync::atomic::AtomicU32::new(0),
192 core::sync::atomic::AtomicU32::new(0),
193];
194pub static RX_LAST_LARGE_FRAME_LEN: core::sync::atomic::AtomicU32 =
196 core::sync::atomic::AtomicU32::new(0);
197pub static TX_LAST_LEN: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
200pub static TX_LAST_ETHERTYPE: core::sync::atomic::AtomicU32 =
201 core::sync::atomic::AtomicU32::new(0);
202pub static TX_LAST_DST_MAC_HI: core::sync::atomic::AtomicU32 =
203 core::sync::atomic::AtomicU32::new(0);
204pub static TX_LAST_SRC_MAC_HI: core::sync::atomic::AtomicU32 =
205 core::sync::atomic::AtomicU32::new(0);
206
207pub type Device<'d> = ch::Device<'d, MTU>;
209pub type Runner<'d> = ch::Runner<'d, MTU>;
211
212pub fn wake_rx_task() {
214 RX_WAKER.wake();
215}
216
217pub fn wake_tx_task() {
219 TX_WAKER.wake();
220}
221
222#[derive(Clone, Copy, Debug, Eq, PartialEq)]
224pub enum MacError {
225 Phy(PhyError),
227 ResetTimeout,
229 StopTimeout,
231 DmaBufferAboveCacheBoundary {
238 addr: u32,
240 },
241}
242
243#[inline]
247fn check_dma_buffer_address(addr: usize) -> Result<(), MacError> {
248 #[cfg(target_arch = "riscv32")]
249 {
250 if addr >= DMA_CACHE_BOUNDARY as usize {
251 return Err(MacError::DmaBufferAboveCacheBoundary { addr: addr as u32 });
252 }
253 }
254 #[cfg(not(target_arch = "riscv32"))]
255 {
256 let _ = addr;
257 }
258 Ok(())
259}
260
261pub fn new(
268 mac_addr: [u8; 6],
269 tx_descriptors: &'static mut [TDes; TX_DESC_COUNT],
270 rx_descriptors: &'static mut [RDes; RX_DESC_COUNT],
271 tx_buffers: &'static mut [[u8; BUF_SIZE]; TX_DESC_COUNT],
272 rx_buffers: &'static mut [[u8; BUF_SIZE]; RX_DESC_COUNT],
273) -> (Device<'static>, Runner<'static>, Ethernet<'static>) {
274 new_with_board(
275 mac_addr,
276 tx_descriptors,
277 rx_descriptors,
278 tx_buffers,
279 rx_buffers,
280 &crate::board::BoardConfig::WAVESHARE_P4_ETH,
281 )
282}
283
284pub fn new_with_board(
286 mac_addr: [u8; 6],
287 tx_descriptors: &'static mut [TDes; TX_DESC_COUNT],
288 rx_descriptors: &'static mut [RDes; RX_DESC_COUNT],
289 tx_buffers: &'static mut [[u8; BUF_SIZE]; TX_DESC_COUNT],
290 rx_buffers: &'static mut [[u8; BUF_SIZE]; RX_DESC_COUNT],
291 board: &crate::board::BoardConfig,
292) -> (Device<'static>, Runner<'static>, Ethernet<'static>) {
293 let state = STATE.take();
294 let (runner, device) = ch::new(state, ch::driver::HardwareAddress::Ethernet(mac_addr));
295 let eth = Ethernet::try_new_with_board(
296 mac_addr,
297 tx_descriptors,
298 rx_descriptors,
299 tx_buffers,
300 rx_buffers,
301 board,
302 )
303 .expect("EMAC bootstrap failed");
304
305 (device, runner, eth)
306}
307
308pub fn new_from_static_resources(
313 mac_addr: [u8; 6],
314 resources: &'static mut StaticDmaResources,
315) -> (Device<'static>, Runner<'static>, Ethernet<'static>) {
316 new_from_static_resources_with_board(
317 mac_addr,
318 resources,
319 &crate::board::BoardConfig::WAVESHARE_P4_ETH,
320 )
321}
322
323pub fn new_from_static_resources_with_board(
325 mac_addr: [u8; 6],
326 resources: &'static mut StaticDmaResources,
327 board: &crate::board::BoardConfig,
328) -> (Device<'static>, Runner<'static>, Ethernet<'static>) {
329 let (tx_descriptors, rx_descriptors, tx_buffers, rx_buffers) = resources.split();
330 new_with_board(
331 mac_addr,
332 tx_descriptors,
333 rx_descriptors,
334 tx_buffers,
335 rx_buffers,
336 board,
337 )
338}
339
340#[embassy_executor::task]
346#[cfg(target_arch = "riscv32")]
347pub async fn ethernet_task(runner: Runner<'static>, mut eth: Ethernet<'static>) {
348 eth.start().expect("EMAC start failed");
349 #[cfg(feature = "p4-time-driver-irq")]
354 crate::time_driver_irq::enable_emac_irq();
355 RX_DESC_BASE.store(
358 regs::read(regs::dma::RX_DESC_LIST),
359 core::sync::atomic::Ordering::Relaxed,
360 );
361
362 let (state_runner, mut rx_runner, mut tx_runner) = runner.split();
363 let eth = Mutex::<NoopRawMutex, RefCell<Ethernet<'static>>>::new(RefCell::new(eth));
364
365 state_runner.set_link_state(ch::driver::LinkState::Down);
366
367 let rx_fut = async {
368 loop {
369 let buf = rx_runner.rx_buf().await;
370 RX_BUF_REQUESTED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
371 Dma::demand_rx_poll();
376
377 poll_fn(|cx| {
378 RX_WAKER.register(cx.waker());
379
380 let ready = eth.lock(|eth| eth.borrow().rx_ring.has_packet());
381 if ready {
382 Poll::Ready(())
383 } else {
384 #[cfg(not(feature = "p4-time-driver-irq"))]
391 cx.waker().wake_by_ref();
392 Poll::Pending
393 }
394 })
395 .await;
396
397 let len = eth.lock(|eth| {
398 let mut eth = eth.borrow_mut();
399 match eth.receive() {
400 Some((len, data)) => {
401 buf[..len].copy_from_slice(&data[..len]);
402 eth.pop_rx();
403 len
404 }
405 None => 0,
406 }
407 });
408
409 if len != 0 {
410 classify_rx_frame(buf, len);
411 rx_runner.rx_done(len);
412 RX_FRAMES.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
413 wake_rx_task();
414 } else {
415 yield_now().await;
416 }
417 }
418 };
419
420 let tx_fut = async {
421 loop {
422 let buf = tx_runner.tx_buf().await;
423 TX_BUF_REQUESTED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
424
425 loop {
426 poll_fn(|cx| {
427 TX_WAKER.register(cx.waker());
428
429 let ready = eth.lock(|eth| eth.borrow().tx_ring.has_capacity());
430 if ready {
431 Poll::Ready(())
432 } else {
433 #[cfg(not(feature = "p4-time-driver-irq"))]
435 cx.waker().wake_by_ref();
436 Poll::Pending
437 }
438 })
439 .await;
440
441 record_tx_frame_metadata(buf);
442 let result = eth.lock(|eth| eth.borrow_mut().transmit(buf));
443
444 match result {
445 Ok(()) => {
446 tx_runner.tx_done();
447 TX_FRAMES.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
448 wake_tx_task();
449 break;
450 }
451 Err(DescriptorError::RingFull) => {
452 yield_now().await;
453 }
454 Err(_) => {
455 tx_runner.tx_done();
456 break;
457 }
458 }
459 }
460 }
461 };
462
463 let link_fut = async {
464 let mut link_up = false;
465
466 loop {
467 let is_up = eth.lock(|eth| eth.borrow_mut().phy_link_status());
468
469 match link_state_action(link_up, is_up) {
470 LinkAction::NoChange => {}
471 LinkAction::AttemptNegotiate => {
472 if eth.lock(|eth| eth.borrow_mut().phy_negotiate()).is_ok() {
473 state_runner.set_link_state(ch::driver::LinkState::Up);
474 link_up = true;
475 }
476 }
477 LinkAction::GoDown => {
478 state_runner.set_link_state(ch::driver::LinkState::Down);
479 link_up = false;
480 }
481 }
482
483 link_poll_delay().await;
484 }
485 };
486
487 let _ = join3(rx_fut, tx_fut, link_fut).await;
488}
489
490pub struct Ethernet<'a> {
492 tx_ring: TDesRing<'a>,
493 rx_ring: RDesRing<'a>,
494 phy: Ip101,
495 mac_addr: [u8; 6],
496}
497
498impl<'a> Ethernet<'a> {
499 pub fn new(
504 mac_addr: [u8; 6],
505 tx_descriptors: &'a mut [TDes; TX_DESC_COUNT],
506 rx_descriptors: &'a mut [RDes; RX_DESC_COUNT],
507 tx_buffers: &'a mut [[u8; BUF_SIZE]; TX_DESC_COUNT],
508 rx_buffers: &'a mut [[u8; BUF_SIZE]; RX_DESC_COUNT],
509 ) -> Self {
510 Self::try_new(
511 mac_addr,
512 tx_descriptors,
513 rx_descriptors,
514 tx_buffers,
515 rx_buffers,
516 )
517 .expect("EMAC bootstrap failed")
518 }
519
520 pub fn new_from_static_resources(
525 mac_addr: [u8; 6],
526 resources: &'a mut StaticDmaResources,
527 ) -> Self {
528 let (tx_descriptors, rx_descriptors, tx_buffers, rx_buffers) = resources.split();
529 Self::new(
530 mac_addr,
531 tx_descriptors,
532 rx_descriptors,
533 tx_buffers,
534 rx_buffers,
535 )
536 }
537
538 pub fn try_new(
542 mac_addr: [u8; 6],
543 tx_descriptors: &'a mut [TDes; TX_DESC_COUNT],
544 rx_descriptors: &'a mut [RDes; RX_DESC_COUNT],
545 tx_buffers: &'a mut [[u8; BUF_SIZE]; TX_DESC_COUNT],
546 rx_buffers: &'a mut [[u8; BUF_SIZE]; RX_DESC_COUNT],
547 ) -> Result<Self, MacError> {
548 Self::try_new_with_board(
549 mac_addr,
550 tx_descriptors,
551 rx_descriptors,
552 tx_buffers,
553 rx_buffers,
554 &crate::board::BoardConfig::WAVESHARE_P4_ETH,
555 )
556 }
557
558 pub fn try_new_with_board(
564 mac_addr: [u8; 6],
565 tx_descriptors: &'a mut [TDes; TX_DESC_COUNT],
566 rx_descriptors: &'a mut [RDes; RX_DESC_COUNT],
567 tx_buffers: &'a mut [[u8; BUF_SIZE]; TX_DESC_COUNT],
568 rx_buffers: &'a mut [[u8; BUF_SIZE]; RX_DESC_COUNT],
569 board: &crate::board::BoardConfig,
570 ) -> Result<Self, MacError> {
571 check_dma_buffer_address(tx_descriptors.as_ptr() as usize)?;
572 check_dma_buffer_address(rx_descriptors.as_ptr() as usize)?;
573 check_dma_buffer_address(tx_buffers.as_ptr() as usize)?;
574 check_dma_buffer_address(rx_buffers.as_ptr() as usize)?;
575
576 crate::phy::diag_log("[try_new] enter, init_l2_cache_mode ...");
577 Dma::init_l2_cache_mode();
586 crate::phy::diag_log("[try_new] L2 cache mode set, configure_rmii_pin_set ...");
587 configure_rmii_pin_set(board.rmii_pins);
588 crate::phy::diag_log("[try_new] rmii ok, configure_mdio_pin_set ...");
589 configure_mdio_pin_set(board.mdio_pins);
590 crate::phy::diag_log("[try_new] mdio pins ok, configure_clock_ext_in ...");
591 configure_clock_ext_in(board.ref_clock);
592 crate::phy::diag_log("[try_new] clock ext in ok, Dma::dma_reset ...");
593 Dma::dma_reset().map_err(|_| MacError::ResetTimeout)?;
602 crate::phy::diag_log("[try_new] dma_reset ok, TDesRing::new ...");
603 let tx_ring = TDesRing::new(tx_descriptors, tx_buffers);
604 crate::phy::diag_log("[try_new] tx_ring ok, RDesRing::new ...");
605 let rx_ring = RDesRing::new(rx_descriptors, rx_buffers);
606 crate::phy::diag_log("[try_new] rx_ring ok, assembling struct ...");
607 let phy = Ip101::new(board.phy_addr);
608 let mut ethernet = Self { tx_ring, rx_ring, phy, mac_addr };
609 crate::phy::diag_log("[try_new] struct built, calling mac_init ...");
610 ethernet.mac_init()?;
611 crate::phy::diag_log("[try_new] mac_init ok, set_mac_address ...");
612 ethernet.set_mac_address(mac_addr);
613 crate::phy::diag_log("[try_new] set_mac_address ok, calling phy.init ...");
614 ethernet
615 .with_phy(|phy, eth| phy.init(eth))
616 .map_err(MacError::Phy)?;
617 crate::phy::diag_log("[try_new] phy.init ok, reset_dma ...");
618 ethernet.reset_dma();
619 crate::phy::diag_log("[try_new] DONE");
620
621 Ok(ethernet)
622 }
623
624 pub fn mac_addr(&self) -> [u8; 6] {
626 self.mac_addr
627 }
628
629 pub fn set_mac_address(&mut self, mac_addr: [u8; 6]) {
631 self.mac_addr = mac_addr;
632 regs::write_mac_address(mac_addr);
633 }
634
635 pub fn set_mac_addr(&mut self, mac_addr: [u8; 6]) {
637 self.set_mac_address(mac_addr);
638 }
639
640 pub fn mac_init(&mut self) -> Result<(), MacError> {
645 crate::phy::diag_log("[mac_init] enter, Dma::dma_reset() ...");
646 Dma::dma_reset().map_err(|_| MacError::ResetTimeout)?;
647 crate::phy::diag_log("[mac_init] dma_reset ok, program_mdio_clock_range ...");
648 self.program_mdio_clock_range(DEFAULT_AHB_CLOCK_HZ);
649 crate::phy::diag_log("[mac_init] mdio clock ok, write MAC CONFIG ...");
650 regs::write(regs::mac::CONFIG, mac_default_config());
651 crate::phy::diag_log("[mac_init] MAC CONFIG ok, write FRAME_FILTER ...");
652 regs::write(regs::mac::FRAME_FILTER, MACFFILT_DEFAULT);
653 crate::phy::diag_log("[mac_init] FRAME_FILTER ok, set_mac_address ...");
654 self.set_mac_address(self.mac_addr);
655 crate::phy::diag_log("[mac_init] DONE");
656 Ok(())
657 }
658
659 pub fn reset_dma(&mut self) {
667 self.reset_descriptor_state();
668 self.flush_dma_visibility();
669 self.program_dma_descriptor_lists();
670 }
671
672 pub fn reset_descriptor_state(&mut self) {
678 crate::phy::diag_log("[reset_dma] tx_ring.reset ...");
679 self.tx_ring.reset();
680 crate::phy::diag_log("[reset_dma] rx_ring.reset ...");
681 self.rx_ring.reset();
682 }
683
684 pub fn flush_dma_visibility(&self) {
691 crate::phy::diag_log("[reset_dma] flush descriptors (per-desc L2) ...");
692 self.tx_ring.flush_all();
693 self.rx_ring.flush_all();
694 }
695
696 pub fn program_dma_descriptor_lists(&self) {
700 crate::phy::diag_log("[reset_dma] write TX_DESC_LIST ...");
701 regs::write(
702 regs::dma::TX_DESC_LIST,
703 self.tx_ring.descriptors_ptr() as usize as u32,
704 );
705 crate::phy::diag_log("[reset_dma] write RX_DESC_LIST ...");
706 regs::write(
707 regs::dma::RX_DESC_LIST,
708 self.rx_ring.descriptors_ptr() as usize as u32,
709 );
710 crate::phy::diag_log("[reset_dma] DONE");
711 }
712
713 pub fn phy_link_status(&mut self) -> bool {
715 matches!(
716 self.with_phy(|phy, eth| phy.poll_link(eth)),
717 LinkState::Up { .. }
718 )
719 }
720
721 pub fn phy_negotiate(&mut self) -> Result<(Speed, Duplex), PhyError> {
723 let (speed, duplex) = self.with_phy(|phy, eth| phy.negotiate(eth))?;
724 self.set_speed(speed);
725 self.set_duplex(duplex);
726 Ok((speed, duplex))
727 }
728
729 pub fn set_speed(&mut self, speed: Speed) {
731 let mut config = regs::read(regs::mac::CONFIG);
732 match speed {
733 Speed::Mbps100 => {
734 config |= regs::bits::maccfg::FES | regs::bits::maccfg::PS;
735 }
736 Speed::Mbps10 => {
737 config = (config & !regs::bits::maccfg::FES) | regs::bits::maccfg::PS;
738 }
739 }
740
741 regs::write(regs::mac::CONFIG, config);
742 configure_speed_divider(speed);
743 }
744
745 pub fn set_duplex(&mut self, duplex: Duplex) {
747 let mut config = regs::read(regs::mac::CONFIG);
748 match duplex {
749 Duplex::Full => config |= regs::bits::maccfg::DM,
750 Duplex::Half => config &= !regs::bits::maccfg::DM,
751 }
752
753 regs::write(regs::mac::CONFIG, config);
754 }
755
756 pub fn start(&mut self) -> Result<(), MacError> {
762 self.mac_init()?;
763 Dma::dma_init();
764 self.reset_dma();
765
766 let config =
767 regs::read(regs::mac::CONFIG) | regs::bits::maccfg::TE | regs::bits::maccfg::RE;
768 regs::write(regs::mac::CONFIG, config);
769
770 let op_mode =
771 regs::read(regs::dma::OP_MODE) | regs::bits::dmaopmode::ST | regs::bits::dmaopmode::SR;
772 regs::write(regs::dma::OP_MODE, op_mode);
773 Ok(())
774 }
775
776 pub fn stop(&mut self) -> Result<(), MacError> {
781 let op_mode = regs::read(regs::dma::OP_MODE)
782 & !(regs::bits::dmaopmode::ST | regs::bits::dmaopmode::SR);
783 regs::write(regs::dma::OP_MODE, op_mode);
784
785 let config =
786 regs::read(regs::mac::CONFIG) & !(regs::bits::maccfg::TE | regs::bits::maccfg::RE);
787 regs::write(regs::mac::CONFIG, config);
788
789 wait_for_dma_stop(|| regs::read(regs::dma::STATUS))
790 }
791
792 pub fn transmit(&mut self, frame: &[u8]) -> Result<(), DescriptorError> {
794 self.tx_ring.transmit(frame)
795 }
796
797 pub fn receive(&mut self) -> Option<(usize, &[u8])> {
799 self.rx_ring.receive()
800 }
801
802 pub fn pop_rx(&mut self) {
804 self.rx_ring.pop();
805 }
806
807 pub fn shutdown(&mut self) {
812 let _ = self.stop();
813 Dma::disable_interrupts();
814 regs::write(regs::mac::INTMASK, 0);
815 Dma::clear_interrupt_status(Dma::read_interrupt_status());
816 disable_emac_clock_tree();
817 }
818
819 pub fn tx_stats(&self) -> TxRingStats {
821 self.tx_ring.stats()
822 }
823
824 pub fn rx_stats(&self) -> RxRingStats {
826 self.rx_ring.stats()
827 }
828
829 pub fn handle_dma_interrupt(&mut self) -> DmaInterruptStatus {
834 let status = Dma::read_interrupt_status();
835 self.apply_dma_interrupt_status(status);
836 Dma::clear_interrupt_status(status);
837 status
838 }
839
840 fn program_mdio_clock_range(&mut self, ahb_clock_hz: u32) {
841 let mut miiaddr = regs::read(regs::mac::MII_ADDR);
842 miiaddr &= !regs::bits::miiaddr::CSR_CLOCK_RANGE_MASK;
843 miiaddr |= csr_clock_range_bits(ahb_clock_hz) << regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT;
844 regs::write(regs::mac::MII_ADDR, miiaddr);
845 }
846
847 fn with_phy<R>(&mut self, f: impl FnOnce(&mut Ip101, &mut Self) -> R) -> R {
848 let mut phy = self.phy;
849 let result = f(&mut phy, self);
850 self.phy = phy;
851 result
852 }
853
854 fn apply_dma_interrupt_status(&mut self, status: DmaInterruptStatus) {
855 if status.has_fatal_bus_error() {
856 Dma::dma_init();
857 self.reset_dma();
858 let op_mode = regs::read(regs::dma::OP_MODE)
859 | regs::bits::dmaopmode::ST
860 | regs::bits::dmaopmode::SR;
861 regs::write(regs::dma::OP_MODE, op_mode);
862 } else if status.has_rx_overflow()
863 || status.has_rx_buffer_unavailable()
864 || status.has_rx_process_stopped()
865 {
866 self.rx_ring.handle_overflow();
867 }
868
869 if status.has_tx_buffer_unavailable() || status.has_tx_underflow() {
870 Dma::demand_tx_poll();
871 }
872
873 if status.has_rx_interrupt() || status.has_early_receive_interrupt() {
874 Dma::demand_rx_poll();
875 }
876
877 #[cfg(target_arch = "riscv32")]
878 {
879 if status.should_kick_rx() {
880 wake_rx_task();
881 }
882
883 if status.should_kick_tx() {
884 wake_tx_task();
885 }
886 }
887 }
888}
889
890impl Drop for Ethernet<'_> {
891 fn drop(&mut self) {
892 self.shutdown();
893 }
894}
895
896fn mac_default_config() -> u32 {
897 regs::bits::maccfg::ACS
898 | regs::bits::maccfg::DM
899 | regs::bits::maccfg::PS
900 | regs::bits::maccfg::FES
901 | regs::bits::maccfg::CST
902 | (MAC_DEFAULT_IFG_96_BIT_TIMES << regs::bits::maccfg::IFG_SHIFT)
903}
904
905fn csr_clock_range_bits(ahb_clock_hz: u32) -> u32 {
906 match ahb_clock_hz {
907 20_000_000..=34_999_999 => 0b0010,
908 35_000_000..=59_999_999 => 0b0011,
909 60_000_000..=99_999_999 => 0b0000,
910 100_000_000..=149_999_999 => 0b0001,
911 150_000_000..=249_999_999 => 0b0100,
912 250_000_000..=300_000_000 => 0b0101,
913 _ => 0b0001,
914 }
915}
916
917fn wait_for_dma_stop<F>(mut read_status: F) -> Result<(), MacError>
918where
919 F: FnMut() -> u32,
920{
921 for _ in 0..MAC_STOP_TIMEOUT_POLLS {
922 let status = read_status();
923 let rx_state = (status & regs::bits::dmastatus::RS_MASK) >> regs::bits::dmastatus::RS_SHIFT;
924 let tx_state = (status & regs::bits::dmastatus::TS_MASK) >> regs::bits::dmastatus::TS_SHIFT;
925
926 if rx_state == regs::bits::dmastatus::PROCESS_STOPPED
927 && tx_state == regs::bits::dmastatus::PROCESS_STOPPED
928 {
929 return Ok(());
930 }
931 }
932
933 Err(MacError::StopTimeout)
934}
935
936#[cfg(target_arch = "riscv32")]
937async fn link_poll_delay() {
938 embassy_time::Timer::after(embassy_time::Duration::from_millis(LINK_POLL_INTERVAL_MS)).await;
939}
940
941#[cfg(test)]
942fn link_poll_interval_ms() -> u64 {
943 LINK_POLL_INTERVAL_MS
944}
945
946#[inline]
955fn read_be_u32(buf: &[u8], off: usize) -> u32 {
956 (u32::from(buf[off]) << 24)
957 | (u32::from(buf[off + 1]) << 16)
958 | (u32::from(buf[off + 2]) << 8)
959 | u32::from(buf[off + 3])
960}
961
962#[inline]
966fn hex_dump_into(slot: &[core::sync::atomic::AtomicU32; 16], buf: &[u8], len: usize) {
967 use core::sync::atomic::Ordering::Relaxed;
968 let n = (len / 4).min(16);
969 for (i, cell) in slot.iter().take(n).enumerate() {
970 cell.store(read_be_u32(buf, i * 4), Relaxed);
971 }
972}
973
974pub(crate) fn classify_rx_frame(buf: &[u8], len: usize) {
984 use core::sync::atomic::Ordering::Relaxed;
985
986 if len >= 200 {
987 RX_LAST_LARGE_FRAME_LEN.store(len as u32, Relaxed);
988 hex_dump_into(&RX_LAST_LARGE_FRAME, buf, len);
989 }
990
991 if len < 14 {
992 return;
993 }
994
995 let ethertype = (u16::from(buf[12]) << 8) | u16::from(buf[13]);
996 RX_LAST_ETHERTYPE.store(u32::from(ethertype), Relaxed);
997 RX_LAST_DST_MAC_HI.store(read_be_u32(buf, 0), Relaxed);
998
999 match ethertype {
1000 0x0806 => {
1001 RX_ARP.fetch_add(1, Relaxed);
1002 }
1003 0x0800 => {
1004 RX_IPV4.fetch_add(1, Relaxed);
1005 if len >= 24 && buf[23] == 0x01 {
1007 RX_ICMP.fetch_add(1, Relaxed);
1008 }
1009 if len >= 14 + 20 + 4 && buf[23] == 0x11 {
1012 let src_port = (u16::from(buf[14 + 20]) << 8) | u16::from(buf[14 + 20 + 1]);
1013 let dst_port = (u16::from(buf[14 + 20 + 2]) << 8) | u16::from(buf[14 + 20 + 3]);
1014 if src_port == 67 || src_port == 68 || dst_port == 67 || dst_port == 68 {
1015 RX_DHCP_FRAMES.fetch_add(1, Relaxed);
1016 hex_dump_into(&RX_LAST_DHCP_FRAME, buf, len);
1017 }
1018 }
1019 }
1020 _ => {}
1021 }
1022}
1023
1024pub(crate) fn record_tx_frame_metadata(buf: &[u8]) {
1028 use core::sync::atomic::Ordering::Relaxed;
1029
1030 TX_LAST_LEN.store(buf.len() as u32, Relaxed);
1031 if buf.len() >= 14 {
1032 let dst_hi = read_be_u32(buf, 0);
1033 let src_hi = read_be_u32(buf, 6);
1034 let etype = (u32::from(buf[12]) << 8) | u32::from(buf[13]);
1035 TX_LAST_DST_MAC_HI.store(dst_hi, Relaxed);
1036 TX_LAST_SRC_MAC_HI.store(src_hi, Relaxed);
1037 TX_LAST_ETHERTYPE.store(etype, Relaxed);
1038 }
1039}
1040
1041#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1044pub enum LinkAction {
1045 NoChange,
1047 AttemptNegotiate,
1050 GoDown,
1053}
1054
1055#[inline]
1059pub(crate) const fn link_state_action(prev_link_up: bool, is_up_now: bool) -> LinkAction {
1060 match (prev_link_up, is_up_now) {
1061 (false, true) => LinkAction::AttemptNegotiate,
1062 (true, false) => LinkAction::GoDown,
1063 _ => LinkAction::NoChange,
1064 }
1065}
1066
1067#[cfg(test)]
1068mod tests {
1069 use super::{
1070 classify_rx_frame,
1071 clock::{emac_clock_tree_enabled, emac_reset_asserted},
1072 csr_clock_range_bits,
1073 descriptors::{OwnedBy, RDES0_FL_SHIFT, RDES0_FS, RDES0_LS},
1074 link_poll_interval_ms, link_state_action, mac_default_config, record_tx_frame_metadata,
1075 wait_for_dma_stop, DmaInterruptStatus, Duplex, Ethernet, LinkAction, MacError, RX_ARP,
1076 RX_DHCP_FRAMES, RX_ICMP, RX_IPV4, RX_LAST_DHCP_FRAME, RX_LAST_ETHERTYPE,
1077 RX_LAST_LARGE_FRAME, RX_LAST_LARGE_FRAME_LEN, TX_LAST_DST_MAC_HI, TX_LAST_ETHERTYPE,
1078 TX_LAST_LEN, TX_LAST_SRC_MAC_HI,
1079 };
1080 use crate::{
1081 regs, zeroed_rx_descriptors, zeroed_tx_descriptors, Speed, StaticDmaResources, BUF_SIZE,
1082 RX_DESC_COUNT, TX_DESC_COUNT,
1083 };
1084
1085 const IRQ_FUZZ_ITERATIONS: usize = 2_048;
1086 const DOMAIN_MAC: [u8; 6] = [0x02, 0x44, 0x52, 0x49, 0x56, 0x01];
1087
1088 fn advance_lcg(state: &mut u32) -> u32 {
1089 *state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
1090 *state
1091 }
1092
1093 fn with_bootstrapped_ethernet<R>(scenario: impl FnOnce(&mut Ethernet<'_>) -> R) -> R {
1094 regs::reset_test_registers();
1095 let mut tx_desc = zeroed_tx_descriptors();
1096 let mut rx_desc = zeroed_rx_descriptors();
1097 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1098 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1099 let mut ethernet = Ethernet::new(
1100 DOMAIN_MAC,
1101 &mut tx_desc,
1102 &mut rx_desc,
1103 &mut tx_buf,
1104 &mut rx_buf,
1105 );
1106
1107 scenario(&mut ethernet)
1108 }
1109
1110 #[test]
1111 fn domain_bootstrap_declares_station_identity_and_empty_rings() {
1112 with_bootstrapped_ethernet(|ethernet| {
1113 assert_eq!(ethernet.mac_addr(), DOMAIN_MAC);
1114 assert_eq!(regs::read(regs::mac::ADDR0_LOW), 0x4952_4402);
1115 assert_eq!(
1116 regs::read(regs::mac::ADDR0_HIGH),
1117 regs::bits::macaddr::AE0 | 0x0000_0156
1118 );
1119 assert_eq!(ethernet.tx_stats(), Default::default());
1120 assert_eq!(ethernet.rx_stats(), Default::default());
1121 assert_eq!(ethernet.tx_ring.fuzz_current_index(), 0);
1122 assert_eq!(ethernet.rx_ring.fuzz_current_index(), 0);
1123 });
1124 }
1125
1126 #[test]
1127 fn domain_transmit_frame_queues_dma_work_and_advances_tx_cursor() {
1128 with_bootstrapped_ethernet(|ethernet| {
1129 let frame = [0xA5; 64];
1130
1131 ethernet.transmit(&frame).unwrap();
1132
1133 assert_eq!(ethernet.tx_stats().transmitted_frames, 1);
1134 assert_eq!(ethernet.tx_stats().ring_full_events, 0);
1135 assert_eq!(ethernet.tx_ring.fuzz_current_index(), 1);
1136 assert_eq!(regs::read(regs::dma::TX_POLL_DEMAND), 1);
1137 });
1138 }
1139
1140 #[test]
1141 fn domain_receive_frame_delivers_payload_until_driver_acknowledges_it() {
1142 with_bootstrapped_ethernet(|ethernet| {
1143 let payload = [0xC3; 96];
1144 let status = RDES0_FS | RDES0_LS | ((payload.len() as u32) << RDES0_FL_SHIFT);
1145 ethernet
1146 .rx_ring
1147 .fuzz_seed_current(status, OwnedBy::Cpu, &payload);
1148
1149 {
1150 let (len, frame) = ethernet.receive().unwrap();
1151 assert_eq!(len, payload.len());
1152 assert_eq!(frame, &payload);
1153 }
1154
1155 assert_eq!(ethernet.rx_stats().received_frames, 1);
1156 assert_eq!(ethernet.rx_ring.fuzz_current_index(), 0);
1157
1158 ethernet.pop_rx();
1159
1160 assert_eq!(ethernet.rx_ring.fuzz_current_index(), 1);
1161 });
1162 }
1163
1164 #[test]
1165 fn domain_rx_overflow_interrupt_rebuilds_receive_path() {
1166 with_bootstrapped_ethernet(|ethernet| {
1167 ethernet.apply_dma_interrupt_status(DmaInterruptStatus::from_raw(
1168 regs::bits::dmastatus::OVF | regs::bits::dmastatus::AIS,
1169 ));
1170
1171 assert_eq!(ethernet.rx_stats().overflow_resets, 1);
1172 assert_eq!(ethernet.rx_ring.fuzz_current_index(), 0);
1173 assert_eq!(regs::read(regs::dma::RX_POLL_DEMAND), 1);
1174 });
1175 }
1176
1177 #[test]
1178 fn domain_fatal_dma_error_restores_transmit_path() {
1179 with_bootstrapped_ethernet(|ethernet| {
1180 ethernet.apply_dma_interrupt_status(DmaInterruptStatus::from_raw(
1181 regs::bits::dmastatus::FBI | regs::bits::dmastatus::AIS,
1182 ));
1183
1184 assert_ne!(
1185 regs::read(regs::dma::OP_MODE) & regs::bits::dmaopmode::ST,
1186 0
1187 );
1188 assert_ne!(
1189 regs::read(regs::dma::OP_MODE) & regs::bits::dmaopmode::SR,
1190 0
1191 );
1192 assert!(ethernet.transmit(&[0x5A; 64]).is_ok());
1193 assert_eq!(ethernet.tx_stats().transmitted_frames, 1);
1194 });
1195 }
1196
1197 #[test]
1198 fn bdd_given_bootstrapped_driver_when_station_mac_changes_then_hardware_identity_changes() {
1199 with_bootstrapped_ethernet(|ethernet| {
1200 let new_mac = [0x02, 0x10, 0x20, 0x30, 0x40, 0x50];
1201
1202 ethernet.set_mac_address(new_mac);
1203
1204 assert_eq!(ethernet.mac_addr(), new_mac);
1205 assert_eq!(regs::read(regs::mac::ADDR0_LOW), 0x3020_1002);
1206 assert_eq!(
1207 regs::read(regs::mac::ADDR0_HIGH),
1208 regs::bits::macaddr::AE0 | 0x0000_5040
1209 );
1210 });
1211 }
1212
1213 #[test]
1214 fn bdd_given_tx_ring_is_full_when_stack_transmits_then_backpressure_is_reported() {
1215 with_bootstrapped_ethernet(|ethernet| {
1216 for slot in 0..TX_DESC_COUNT {
1217 assert_eq!(ethernet.tx_ring.fuzz_current_index(), slot);
1218 ethernet.transmit(&[slot as u8; 64]).unwrap();
1219 }
1220
1221 let result = ethernet.transmit(&[0xEE; 64]);
1222
1223 assert_eq!(result, Err(super::DescriptorError::RingFull));
1224 assert_eq!(ethernet.tx_stats().transmitted_frames, TX_DESC_COUNT as u32);
1225 assert_eq!(ethernet.tx_stats().ring_full_events, 1);
1226 assert_eq!(ethernet.tx_ring.fuzz_current_index(), 0);
1227 });
1228 }
1229
1230 #[test]
1231 fn bdd_given_runt_frame_when_driver_receives_then_frame_is_dropped_and_dma_rearmed() {
1232 with_bootstrapped_ethernet(|ethernet| {
1233 let payload = [0x11; 32];
1234 let status = RDES0_FS | RDES0_LS | ((payload.len() as u32) << RDES0_FL_SHIFT);
1235 ethernet
1236 .rx_ring
1237 .fuzz_seed_current(status, OwnedBy::Cpu, &payload);
1238
1239 let received = ethernet.receive();
1240
1241 assert!(received.is_none());
1242 assert_eq!(ethernet.rx_stats().received_frames, 0);
1243 assert_eq!(ethernet.rx_stats().runt_frames, 1);
1244 assert_eq!(ethernet.rx_ring.fuzz_current_index(), 1);
1245 });
1246 }
1247
1248 #[test]
1249 fn bdd_given_rx_and_tx_recovery_bits_when_interrupt_is_handled_then_dma_paths_are_polled() {
1250 with_bootstrapped_ethernet(|ethernet| {
1251 regs::write(
1252 regs::dma::STATUS,
1253 regs::bits::dmastatus::RI
1254 | regs::bits::dmastatus::ERI
1255 | regs::bits::dmastatus::TU
1256 | regs::bits::dmastatus::UNF
1257 | regs::bits::dmastatus::AIS
1258 | regs::bits::dmastatus::NIS,
1259 );
1260
1261 let status = ethernet.handle_dma_interrupt();
1262
1263 assert!(status.should_kick_rx());
1264 assert!(status.should_kick_tx());
1265 assert_eq!(regs::read(regs::dma::RX_POLL_DEMAND), 1);
1266 assert_eq!(regs::read(regs::dma::TX_POLL_DEMAND), 1);
1267 assert_eq!(regs::read(regs::dma::STATUS), status.clear_mask());
1268 });
1269 }
1270
1271 #[test]
1272 fn bdd_given_running_driver_when_shutdown_is_requested_then_peripheral_is_quiesced() {
1273 with_bootstrapped_ethernet(|ethernet| {
1274 ethernet.start().unwrap();
1275
1276 ethernet.shutdown();
1277
1278 assert_eq!(regs::read(regs::dma::INT_EN), 0);
1279 assert_eq!(regs::read(regs::mac::INTMASK), 0);
1280 assert!(!emac_clock_tree_enabled());
1281 assert!(emac_reset_asserted());
1282 });
1283 }
1284
1285 #[test]
1286 fn mac_default_config_matches_expected_defaults() {
1287 let config = mac_default_config();
1288
1289 assert_ne!(config & regs::bits::maccfg::ACS, 0);
1290 assert_ne!(config & regs::bits::maccfg::DM, 0);
1291 assert_ne!(config & regs::bits::maccfg::FES, 0);
1292 assert_ne!(config & regs::bits::maccfg::PS, 0);
1293 assert_ne!(config & regs::bits::maccfg::CST, 0);
1294 assert_eq!(
1295 (config & regs::bits::maccfg::IFG_MASK) >> regs::bits::maccfg::IFG_SHIFT,
1296 0
1297 );
1298 }
1299
1300 #[test]
1301 fn csr_clock_range_matches_expected_bucket() {
1302 assert_eq!(csr_clock_range_bits(30_000_000), 0b0010);
1303 assert_eq!(csr_clock_range_bits(50_000_000), 0b0011);
1304 assert_eq!(csr_clock_range_bits(80_000_000), 0b0000);
1305 assert_eq!(csr_clock_range_bits(120_000_000), 0b0001);
1306 assert_eq!(csr_clock_range_bits(200_000_000), 0b0100);
1307 assert_eq!(csr_clock_range_bits(300_000_000), 0b0101);
1308 assert_eq!(csr_clock_range_bits(10_000_000), 0b0001);
1309 }
1310
1311 #[test]
1317 fn link_poll_interval_stays_in_sane_range() {
1318 let ms = link_poll_interval_ms();
1319 assert!(
1320 ms >= 50,
1321 "LINK_POLL_INTERVAL_MS = {} ms is too aggressive — \
1322 see feedback_p4_yield_now_antipattern in project memory",
1323 ms,
1324 );
1325 assert!(
1326 ms <= 500,
1327 "LINK_POLL_INTERVAL_MS = {} ms is too long — \
1328 link state changes will go unnoticed for over half a second",
1329 ms,
1330 );
1331 }
1332
1333 #[test]
1334 fn wait_for_dma_stop_succeeds_when_states_reach_stopped() {
1335 let mut polls = 0;
1336 let result = wait_for_dma_stop(|| {
1337 polls += 1;
1338 if polls < 4 {
1339 (0b011 << regs::bits::dmastatus::RS_SHIFT)
1340 | (0b011 << regs::bits::dmastatus::TS_SHIFT)
1341 } else {
1342 0
1343 }
1344 });
1345
1346 assert_eq!(result, Ok(()));
1347 assert_eq!(polls, 4);
1348 }
1349
1350 #[test]
1351 fn wait_for_dma_stop_times_out_when_dma_keeps_running() {
1352 let result = wait_for_dma_stop(|| {
1353 (0b111 << regs::bits::dmastatus::RS_SHIFT) | (0b111 << regs::bits::dmastatus::TS_SHIFT)
1354 });
1355
1356 assert_eq!(result, Err(MacError::StopTimeout));
1357 }
1358
1359 #[test]
1360 fn enums_stay_stable() {
1361 assert_eq!(Speed::Mbps10 as u8, 0);
1362 assert_eq!(Speed::Mbps100 as u8, 1);
1363 assert_eq!(Duplex::Half as u8, 0);
1364 assert_eq!(Duplex::Full as u8, 1);
1365 }
1366
1367 #[test]
1368 fn ethernet_new_from_static_resources_uses_single_dma_resource_owner() {
1369 regs::reset_test_registers();
1370 let mut resources = StaticDmaResources::new();
1371
1372 let ethernet = Ethernet::new_from_static_resources(DOMAIN_MAC, &mut resources);
1373
1374 assert_eq!(ethernet.mac_addr(), DOMAIN_MAC);
1375 assert_eq!(ethernet.tx_stats(), Default::default());
1376 assert_eq!(ethernet.rx_stats(), Default::default());
1377 }
1378
1379 #[test]
1380 fn mac_init_programs_mdio_filter_config_and_station_address() {
1381 with_bootstrapped_ethernet(|ethernet| {
1382 regs::write(regs::mac::CONFIG, 0);
1383 regs::write(regs::mac::FRAME_FILTER, 0);
1384 regs::write(regs::mac::MII_ADDR, 0);
1385
1386 ethernet.mac_init().unwrap();
1387
1388 assert_eq!(regs::read(regs::mac::CONFIG), mac_default_config());
1389 assert_eq!(regs::read(regs::mac::FRAME_FILTER), super::MACFFILT_DEFAULT);
1390 assert_eq!(
1391 (regs::read(regs::mac::MII_ADDR) & regs::bits::miiaddr::CSR_CLOCK_RANGE_MASK)
1392 >> regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT,
1393 csr_clock_range_bits(super::DEFAULT_AHB_CLOCK_HZ)
1394 );
1395 assert_eq!(ethernet.mac_addr(), DOMAIN_MAC);
1396 });
1397 }
1398
1399 #[test]
1400 fn start_enables_mac_and_dma_datapaths() {
1401 with_bootstrapped_ethernet(|ethernet| {
1402 ethernet.start().unwrap();
1403
1404 let mac_config = regs::read(regs::mac::CONFIG);
1405 let op_mode = regs::read(regs::dma::OP_MODE);
1406 assert_ne!(mac_config & regs::bits::maccfg::TE, 0);
1407 assert_ne!(mac_config & regs::bits::maccfg::RE, 0);
1408 assert_ne!(op_mode & regs::bits::dmaopmode::ST, 0);
1409 assert_ne!(op_mode & regs::bits::dmaopmode::SR, 0);
1410 let interrupt_mask = regs::read(regs::dma::INT_EN);
1411 assert_ne!(interrupt_mask & regs::bits::dmainten::TIE, 0);
1412 assert_ne!(interrupt_mask & regs::bits::dmainten::RIE, 0);
1413 assert_ne!(interrupt_mask & regs::bits::dmainten::AIE, 0);
1414 assert_ne!(interrupt_mask & regs::bits::dmainten::NIE, 0);
1415 });
1416 }
1417
1418 #[test]
1419 fn stop_disables_mac_and_dma_datapaths_when_hardware_reports_stopped() {
1420 with_bootstrapped_ethernet(|ethernet| {
1421 regs::write(
1422 regs::dma::OP_MODE,
1423 regs::bits::dmaopmode::ST | regs::bits::dmaopmode::SR,
1424 );
1425 regs::write(
1426 regs::mac::CONFIG,
1427 regs::bits::maccfg::TE | regs::bits::maccfg::RE | regs::bits::maccfg::DM,
1428 );
1429 regs::write(regs::dma::STATUS, 0);
1430
1431 assert_eq!(ethernet.stop(), Ok(()));
1432
1433 assert_eq!(
1434 regs::read(regs::dma::OP_MODE)
1435 & (regs::bits::dmaopmode::ST | regs::bits::dmaopmode::SR),
1436 0
1437 );
1438 assert_eq!(
1439 regs::read(regs::mac::CONFIG) & (regs::bits::maccfg::TE | regs::bits::maccfg::RE),
1440 0
1441 );
1442 assert_ne!(regs::read(regs::mac::CONFIG) & regs::bits::maccfg::DM, 0);
1443 });
1444 }
1445
1446 #[test]
1447 fn set_speed_updates_speed_bits_and_preserves_unrelated_config() {
1448 regs::reset_test_registers();
1449 let mut tx_desc = zeroed_tx_descriptors();
1450 let mut rx_desc = zeroed_rx_descriptors();
1451 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1452 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1453 let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
1454 let mut ethernet = Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1455
1456 regs::write(
1457 regs::mac::CONFIG,
1458 regs::bits::maccfg::JD | regs::bits::maccfg::DM | regs::bits::maccfg::FES,
1459 );
1460
1461 ethernet.set_speed(Speed::Mbps10);
1462 let config_10 = regs::read(regs::mac::CONFIG);
1463 assert_eq!(config_10 & regs::bits::maccfg::FES, 0);
1464 assert_ne!(config_10 & regs::bits::maccfg::PS, 0);
1465 assert_ne!(config_10 & regs::bits::maccfg::JD, 0);
1466 assert_ne!(config_10 & regs::bits::maccfg::DM, 0);
1467
1468 ethernet.set_speed(Speed::Mbps100);
1469 let config_100 = regs::read(regs::mac::CONFIG);
1470 assert_ne!(config_100 & regs::bits::maccfg::FES, 0);
1471 assert_ne!(config_100 & regs::bits::maccfg::PS, 0);
1472 assert_ne!(config_100 & regs::bits::maccfg::JD, 0);
1473 assert_ne!(config_100 & regs::bits::maccfg::DM, 0);
1474 }
1475
1476 #[test]
1477 fn set_duplex_only_toggles_duplex_bit() {
1478 regs::reset_test_registers();
1479 let mut tx_desc = zeroed_tx_descriptors();
1480 let mut rx_desc = zeroed_rx_descriptors();
1481 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1482 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1483 let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
1484 let mut ethernet = Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1485
1486 let base = regs::bits::maccfg::JD | regs::bits::maccfg::PS | regs::bits::maccfg::FES;
1487 regs::write(regs::mac::CONFIG, base | regs::bits::maccfg::DM);
1488
1489 ethernet.set_duplex(Duplex::Half);
1490 assert_eq!(regs::read(regs::mac::CONFIG), base);
1491
1492 ethernet.set_duplex(Duplex::Full);
1493 assert_eq!(regs::read(regs::mac::CONFIG), base | regs::bits::maccfg::DM);
1494 }
1495
1496 #[test]
1497 fn ethernet_new_keeps_mac_and_resets_rings() {
1498 regs::reset_test_registers();
1499 let mut tx_desc = zeroed_tx_descriptors();
1500 let mut rx_desc = zeroed_rx_descriptors();
1501 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1502 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1503 let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
1504
1505 let ethernet = Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1506
1507 assert_eq!(ethernet.mac_addr(), mac);
1508 }
1509
1510 #[test]
1511 fn phy_link_status_is_down_on_host_stub() {
1512 regs::reset_test_registers();
1513 let mut tx_desc = zeroed_tx_descriptors();
1514 let mut rx_desc = zeroed_rx_descriptors();
1515 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1516 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1517 let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
1518
1519 let mut ethernet = Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1520
1521 assert!(!ethernet.phy_link_status());
1522 }
1523
1524 #[test]
1525 fn phy_negotiate_returns_fallback_mode_on_host_stub() {
1526 regs::reset_test_registers();
1527 let mut tx_desc = zeroed_tx_descriptors();
1528 let mut rx_desc = zeroed_rx_descriptors();
1529 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1530 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1531 let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
1532
1533 let mut ethernet = Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1534
1535 assert_eq!(ethernet.phy_negotiate(), Ok((Speed::Mbps10, Duplex::Half)));
1536 }
1537
1538 #[test]
1539 fn dma_interrupt_status_resets_rx_ring_on_overflow() {
1540 regs::reset_test_registers();
1541 let mut tx_desc = zeroed_tx_descriptors();
1542 let mut rx_desc = zeroed_rx_descriptors();
1543 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1544 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1545 let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
1546
1547 let mut ethernet = Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1548
1549 ethernet.apply_dma_interrupt_status(DmaInterruptStatus::from_raw(
1550 regs::bits::dmastatus::OVF | regs::bits::dmastatus::AIS,
1551 ));
1552
1553 assert_eq!(ethernet.rx_stats().overflow_resets, 1);
1554 assert_eq!(ethernet.receive(), None);
1555 }
1556
1557 #[test]
1558 fn dma_interrupt_status_restarts_dma_on_fatal_bus_error() {
1559 regs::reset_test_registers();
1560 let mut tx_desc = zeroed_tx_descriptors();
1561 let mut rx_desc = zeroed_rx_descriptors();
1562 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1563 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1564 let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
1565
1566 let mut ethernet = Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1567 ethernet.apply_dma_interrupt_status(DmaInterruptStatus::from_raw(
1568 regs::bits::dmastatus::FBI | regs::bits::dmastatus::AIS,
1569 ));
1570
1571 assert!(ethernet.transmit(&[0xAB; 64]).is_ok());
1572 assert_eq!(ethernet.tx_stats().transmitted_frames, 1);
1573 }
1574
1575 #[test]
1576 fn handle_dma_interrupt_reads_and_clears_status_register() {
1577 regs::reset_test_registers();
1578 let mut tx_desc = zeroed_tx_descriptors();
1579 let mut rx_desc = zeroed_rx_descriptors();
1580 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1581 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1582 let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
1583
1584 let mut ethernet = Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1585 let raw =
1586 regs::bits::dmastatus::OVF | regs::bits::dmastatus::RI | regs::bits::dmastatus::AIS;
1587 regs::write(regs::dma::STATUS, raw);
1588
1589 let status = ethernet.handle_dma_interrupt();
1590
1591 assert_eq!(status.raw(), raw);
1592 assert_eq!(regs::read(regs::dma::STATUS), status.clear_mask());
1593 assert_eq!(regs::read(regs::dma::RX_POLL_DEMAND), 1);
1594 assert_eq!(ethernet.rx_stats().overflow_resets, 1);
1595 }
1596
1597 #[test]
1598 fn handle_dma_interrupt_kicks_tx_poll_on_tx_recovery_bits() {
1599 regs::reset_test_registers();
1600 let mut tx_desc = zeroed_tx_descriptors();
1601 let mut rx_desc = zeroed_rx_descriptors();
1602 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1603 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1604 let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
1605
1606 let mut ethernet = Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1607 regs::write(
1608 regs::dma::STATUS,
1609 regs::bits::dmastatus::TU | regs::bits::dmastatus::UNF | regs::bits::dmastatus::AIS,
1610 );
1611
1612 ethernet.handle_dma_interrupt();
1613
1614 assert_eq!(regs::read(regs::dma::TX_POLL_DEMAND), 1);
1615 }
1616
1617 #[test]
1618 fn handle_dma_interrupt_property_fuzz_preserves_driver_invariants() {
1619 regs::reset_test_registers();
1620 let mut tx_desc = zeroed_tx_descriptors();
1621 let mut rx_desc = zeroed_rx_descriptors();
1622 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1623 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1624 let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
1625
1626 let mut ethernet = Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1627 let mut rng = 0xA17C_E55Du32;
1628
1629 for _ in 0..IRQ_FUZZ_ITERATIONS {
1630 let random = advance_lcg(&mut rng);
1631 let mut raw = 0u32;
1632
1633 if random & 0x01 != 0 {
1634 raw |= regs::bits::dmastatus::OVF;
1635 }
1636 if random & 0x02 != 0 {
1637 raw |= regs::bits::dmastatus::RU;
1638 }
1639 if random & 0x04 != 0 {
1640 raw |= regs::bits::dmastatus::RPS;
1641 }
1642 if random & 0x08 != 0 {
1643 raw |= regs::bits::dmastatus::TU;
1644 }
1645 if random & 0x10 != 0 {
1646 raw |= regs::bits::dmastatus::UNF;
1647 }
1648 if random & 0x20 != 0 {
1649 raw |= regs::bits::dmastatus::RI;
1650 }
1651 if random & 0x40 != 0 {
1652 raw |= regs::bits::dmastatus::ERI;
1653 }
1654 if random & 0x80 != 0 {
1655 raw |= regs::bits::dmastatus::FBI;
1656 }
1657 if raw
1658 & (regs::bits::dmastatus::OVF
1659 | regs::bits::dmastatus::RU
1660 | regs::bits::dmastatus::RPS
1661 | regs::bits::dmastatus::TU
1662 | regs::bits::dmastatus::UNF
1663 | regs::bits::dmastatus::FBI)
1664 != 0
1665 {
1666 raw |= regs::bits::dmastatus::AIS;
1667 }
1668 if raw & (regs::bits::dmastatus::RI | regs::bits::dmastatus::TI) != 0 {
1669 raw |= regs::bits::dmastatus::NIS;
1670 }
1671
1672 regs::write(regs::dma::STATUS, raw);
1673 let previous_overflows = ethernet.rx_stats().overflow_resets;
1674 let status = ethernet.handle_dma_interrupt();
1675
1676 assert_eq!(status.raw(), raw);
1677 assert_eq!(regs::read(regs::dma::STATUS), status.clear_mask());
1678 assert_eq!(ethernet.mac_addr(), mac);
1679 assert!(ethernet.tx_ring.fuzz_current_index() < TX_DESC_COUNT);
1680 assert!(ethernet.rx_ring.fuzz_current_index() < RX_DESC_COUNT);
1681
1682 if status.has_rx_overflow()
1683 || status.has_rx_buffer_unavailable()
1684 || status.has_rx_process_stopped()
1685 || status.has_fatal_bus_error()
1686 {
1687 assert!(ethernet.rx_stats().overflow_resets >= previous_overflows);
1688 }
1689 }
1690 }
1691
1692 #[test]
1693 fn drop_disables_interrupts_and_powers_down_emac() {
1694 regs::reset_test_registers();
1695 {
1696 let mut tx_desc = zeroed_tx_descriptors();
1697 let mut rx_desc = zeroed_rx_descriptors();
1698 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1699 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1700 let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
1701
1702 let mut ethernet =
1703 Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1704 ethernet.start().unwrap();
1705 }
1706
1707 assert_eq!(regs::read(regs::dma::INT_EN), 0);
1708 assert_eq!(regs::read(regs::mac::INTMASK), 0);
1709 assert!(!emac_clock_tree_enabled());
1710 assert!(emac_reset_asserted());
1711 }
1712
1713 #[test]
1714 fn ethernet_can_be_reinitialized_after_drop() {
1715 regs::reset_test_registers();
1716 let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
1717
1718 {
1719 let mut tx_desc = zeroed_tx_descriptors();
1720 let mut rx_desc = zeroed_rx_descriptors();
1721 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1722 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1723
1724 let mut ethernet =
1725 Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1726 ethernet.start().unwrap();
1727 }
1728
1729 let mut tx_desc = zeroed_tx_descriptors();
1730 let mut rx_desc = zeroed_rx_descriptors();
1731 let mut tx_buf = [[0u8; BUF_SIZE]; TX_DESC_COUNT];
1732 let mut rx_buf = [[0u8; BUF_SIZE]; RX_DESC_COUNT];
1733
1734 let mut ethernet = Ethernet::new(mac, &mut tx_desc, &mut rx_desc, &mut tx_buf, &mut rx_buf);
1735 assert_eq!(ethernet.mac_addr(), mac);
1736 assert!(ethernet.start().is_ok());
1737 assert!(emac_clock_tree_enabled());
1738 assert!(!emac_reset_asserted());
1739 }
1740
1741 use core::sync::atomic::Ordering::Relaxed;
1751 use std::sync::Mutex;
1752 use std::vec;
1753 use std::vec::Vec;
1754
1755 static CLASSIFIER_LOCK: Mutex<()> = Mutex::new(());
1756
1757 fn eth_header(dst: [u8; 6], src: [u8; 6], ethertype: u16) -> Vec<u8> {
1759 let mut v = Vec::with_capacity(14);
1760 v.extend_from_slice(&dst);
1761 v.extend_from_slice(&src);
1762 v.push((ethertype >> 8) as u8);
1763 v.push(ethertype as u8);
1764 v
1765 }
1766
1767 fn ipv4_payload(proto: u8, ip_payload_len: usize) -> Vec<u8> {
1772 let mut v = vec![0u8; 20 + ip_payload_len];
1773 v[0] = 0x45; v[9] = proto;
1775 v
1776 }
1777
1778 fn snapshot_arp() -> u32 {
1779 RX_ARP.load(Relaxed)
1780 }
1781 fn snapshot_ipv4() -> u32 {
1782 RX_IPV4.load(Relaxed)
1783 }
1784 fn snapshot_icmp() -> u32 {
1785 RX_ICMP.load(Relaxed)
1786 }
1787 fn snapshot_dhcp() -> u32 {
1788 RX_DHCP_FRAMES.load(Relaxed)
1789 }
1790
1791 #[test]
1792 fn classify_rx_frame_runt_below_14_bytes_skips_classification() {
1793 let _g = CLASSIFIER_LOCK.lock().unwrap();
1794 let arp0 = snapshot_arp();
1795 let ipv4_0 = snapshot_ipv4();
1796
1797 let buf = [0u8; 13];
1799 classify_rx_frame(&buf, 13);
1800
1801 assert_eq!(snapshot_arp(), arp0, "runt must not bump RX_ARP");
1802 assert_eq!(snapshot_ipv4(), ipv4_0, "runt must not bump RX_IPV4");
1803 }
1804
1805 #[test]
1806 fn classify_rx_frame_arp_bumps_arp_counter() {
1807 let _g = CLASSIFIER_LOCK.lock().unwrap();
1808 let before = snapshot_arp();
1809
1810 let mut buf = eth_header(
1812 [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
1813 [0x02, 0x44, 0x52, 0x49, 0x56, 0x01],
1814 0x0806,
1815 );
1816 buf.resize(60, 0);
1817 classify_rx_frame(&buf, buf.len());
1818
1819 assert_eq!(snapshot_arp(), before + 1);
1820 assert_eq!(RX_LAST_ETHERTYPE.load(Relaxed), 0x0806);
1821 }
1822
1823 #[test]
1824 fn classify_rx_frame_ipv4_with_icmp_proto_bumps_both_counters() {
1825 let _g = CLASSIFIER_LOCK.lock().unwrap();
1826 let ipv4_before = snapshot_ipv4();
1827 let icmp_before = snapshot_icmp();
1828 let dhcp_before = snapshot_dhcp();
1829
1830 let mut buf = eth_header([0; 6], [0; 6], 0x0800);
1831 buf.extend_from_slice(&ipv4_payload(0x01, 8)); classify_rx_frame(&buf, buf.len());
1833
1834 assert_eq!(snapshot_ipv4(), ipv4_before + 1);
1835 assert_eq!(snapshot_icmp(), icmp_before + 1);
1836 assert_eq!(
1837 snapshot_dhcp(),
1838 dhcp_before,
1839 "ICMP must not be misclassified as DHCP"
1840 );
1841 }
1842
1843 #[test]
1844 fn classify_rx_frame_ipv4_with_unknown_proto_bumps_only_ipv4_counter() {
1845 let _g = CLASSIFIER_LOCK.lock().unwrap();
1846 let ipv4_before = snapshot_ipv4();
1847 let icmp_before = snapshot_icmp();
1848
1849 let mut buf = eth_header([0; 6], [0; 6], 0x0800);
1850 buf.extend_from_slice(&ipv4_payload(0x06, 16)); classify_rx_frame(&buf, buf.len());
1852
1853 assert_eq!(snapshot_ipv4(), ipv4_before + 1);
1854 assert_eq!(snapshot_icmp(), icmp_before, "TCP must not bump ICMP");
1855 }
1856
1857 #[test]
1858 fn classify_rx_frame_dhcp_bootp_server_port_67_is_detected() {
1859 let _g = CLASSIFIER_LOCK.lock().unwrap();
1860 let before = snapshot_dhcp();
1861
1862 let mut buf = eth_header([0; 6], [0; 6], 0x0800);
1864 let mut ip = ipv4_payload(0x11, 240); ip[20] = 0; ip[21] = 68; ip[22] = 0; ip[23] = 67; buf.extend_from_slice(&ip);
1872 classify_rx_frame(&buf, buf.len());
1873
1874 assert_eq!(snapshot_dhcp(), before + 1);
1875 }
1876
1877 #[test]
1878 fn classify_rx_frame_dhcp_offer_dst_port_68_is_detected() {
1879 let _g = CLASSIFIER_LOCK.lock().unwrap();
1880 let before = snapshot_dhcp();
1881
1882 let mut buf = eth_header([0; 6], [0; 6], 0x0800);
1883 let mut ip = ipv4_payload(0x11, 240);
1884 ip[21] = 67;
1886 ip[23] = 68;
1887 buf.extend_from_slice(&ip);
1888 classify_rx_frame(&buf, buf.len());
1889
1890 assert_eq!(snapshot_dhcp(), before + 1);
1891 }
1892
1893 #[test]
1894 fn classify_rx_frame_non_dhcp_udp_is_not_dhcp_classified() {
1895 let _g = CLASSIFIER_LOCK.lock().unwrap();
1896 let before = snapshot_dhcp();
1897
1898 let mut buf = eth_header([0; 6], [0; 6], 0x0800);
1899 let mut ip = ipv4_payload(0x11, 240);
1900 ip[20] = 0x12;
1902 ip[21] = 0x34;
1903 ip[22] = 0x12;
1904 ip[23] = 0x35;
1905 buf.extend_from_slice(&ip);
1906 classify_rx_frame(&buf, buf.len());
1907
1908 assert_eq!(snapshot_dhcp(), before);
1909 }
1910
1911 #[test]
1912 fn classify_rx_frame_ipv6_skips_classification() {
1913 let _g = CLASSIFIER_LOCK.lock().unwrap();
1914 let arp_before = snapshot_arp();
1915 let ipv4_before = snapshot_ipv4();
1916
1917 let mut buf = eth_header([0x33, 0x33, 0, 0, 0, 1], [0; 6], 0x86DD);
1918 buf.resize(80, 0);
1919 classify_rx_frame(&buf, buf.len());
1920
1921 assert_eq!(snapshot_arp(), arp_before);
1922 assert_eq!(snapshot_ipv4(), ipv4_before);
1923 assert_eq!(RX_LAST_ETHERTYPE.load(Relaxed), 0x86DD);
1924 }
1925
1926 #[test]
1927 fn classify_rx_frame_large_frame_writes_hex_dump_into_static_slot() {
1928 let _g = CLASSIFIER_LOCK.lock().unwrap();
1929 for cell in RX_LAST_LARGE_FRAME.iter() {
1931 cell.store(0, Relaxed);
1932 }
1933 RX_LAST_LARGE_FRAME_LEN.store(0, Relaxed);
1934
1935 let mut buf = vec![0u8; 250];
1937 buf[0] = 0xDE;
1938 buf[1] = 0xAD;
1939 buf[2] = 0xBE;
1940 buf[3] = 0xEF;
1941 buf[12] = 0x08;
1943 classify_rx_frame(&buf, buf.len());
1944
1945 assert_eq!(RX_LAST_LARGE_FRAME_LEN.load(Relaxed), 250);
1946 assert_eq!(RX_LAST_LARGE_FRAME[0].load(Relaxed), 0xDEAD_BEEF);
1947 }
1948
1949 #[test]
1950 fn classify_rx_frame_dhcp_writes_dump_into_static_slot() {
1951 let _g = CLASSIFIER_LOCK.lock().unwrap();
1952 for cell in RX_LAST_DHCP_FRAME.iter() {
1953 cell.store(0, Relaxed);
1954 }
1955
1956 let mut buf = eth_header([0; 6], [0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x01], 0x0800);
1957 buf[0] = 0xAA; buf[1] = 0xBB;
1959 buf[2] = 0xCC;
1960 buf[3] = 0xDD;
1961 let mut ip = ipv4_payload(0x11, 240);
1962 ip[21] = 67; ip[23] = 68; buf.extend_from_slice(&ip);
1965 classify_rx_frame(&buf, buf.len());
1966
1967 assert_eq!(RX_LAST_DHCP_FRAME[0].load(Relaxed), 0xAABB_CCDD);
1969 }
1970
1971 #[test]
1972 fn record_tx_frame_metadata_short_buf_records_only_length() {
1973 let _g = CLASSIFIER_LOCK.lock().unwrap();
1974 TX_LAST_LEN.store(0, Relaxed);
1975 TX_LAST_DST_MAC_HI.store(0xDEAD_BEEF, Relaxed);
1976 TX_LAST_SRC_MAC_HI.store(0xDEAD_BEEF, Relaxed);
1977 TX_LAST_ETHERTYPE.store(0xDEAD_BEEF, Relaxed);
1978
1979 let buf = [0u8; 10];
1980 record_tx_frame_metadata(&buf);
1981
1982 assert_eq!(TX_LAST_LEN.load(Relaxed), 10);
1983 assert_eq!(TX_LAST_DST_MAC_HI.load(Relaxed), 0xDEAD_BEEF);
1985 assert_eq!(TX_LAST_SRC_MAC_HI.load(Relaxed), 0xDEAD_BEEF);
1986 assert_eq!(TX_LAST_ETHERTYPE.load(Relaxed), 0xDEAD_BEEF);
1987 }
1988
1989 #[test]
1990 fn record_tx_frame_metadata_full_frame_captures_all_header_fields() {
1991 let _g = CLASSIFIER_LOCK.lock().unwrap();
1992 TX_LAST_LEN.store(0, Relaxed);
1993
1994 let buf: [u8; 14] = [
1995 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x08, 0x06, ];
1999 record_tx_frame_metadata(&buf);
2000
2001 assert_eq!(TX_LAST_LEN.load(Relaxed), 14);
2002 assert_eq!(TX_LAST_DST_MAC_HI.load(Relaxed), 0xAABB_CCDD);
2004 assert_eq!(TX_LAST_SRC_MAC_HI.load(Relaxed), 0x1122_3344);
2006 assert_eq!(TX_LAST_ETHERTYPE.load(Relaxed), 0x0806);
2008 }
2009
2010 #[test]
2011 fn record_tx_frame_metadata_handles_oversize_frame() {
2012 let _g = CLASSIFIER_LOCK.lock().unwrap();
2013
2014 let mut buf = vec![0u8; 1500];
2015 buf[0] = 0x12;
2016 buf[12] = 0x86;
2017 buf[13] = 0xDD;
2018 record_tx_frame_metadata(&buf);
2019
2020 assert_eq!(TX_LAST_LEN.load(Relaxed), 1500);
2021 assert_eq!(TX_LAST_ETHERTYPE.load(Relaxed), 0x86DD);
2022 }
2023
2024 #[test]
2025 fn link_state_action_down_to_down_is_no_change() {
2026 assert_eq!(link_state_action(false, false), LinkAction::NoChange);
2027 }
2028
2029 #[test]
2030 fn link_state_action_down_to_up_demands_negotiate() {
2031 assert_eq!(
2035 link_state_action(false, true),
2036 LinkAction::AttemptNegotiate
2037 );
2038 }
2039
2040 #[test]
2041 fn link_state_action_up_to_up_is_no_change() {
2042 assert_eq!(link_state_action(true, true), LinkAction::NoChange);
2043 }
2044
2045 #[test]
2046 fn link_state_action_up_to_down_signals_go_down() {
2047 assert_eq!(link_state_action(true, false), LinkAction::GoDown);
2048 }
2049
2050 }