Compare commits
1 Commits
595ffe01c3
...
c2eaeedce1
Author | SHA1 | Date | |
---|---|---|---|
c2eaeedce1 |
@@ -1,40 +1,42 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::{
|
||||
cell::UnsafeCell, mem::MaybeUninit, net::Ipv4Addr, panic::PanicInfo, sync::atomic::AtomicBool,
|
||||
};
|
||||
use core::{net::Ipv4Addr, panic::PanicInfo};
|
||||
use cortex_ar::asm::nop;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_net::{Ipv4Cidr, StaticConfigV4};
|
||||
use embassy_time::{Duration, Ticker, Timer};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embassy_net::{Ipv4Cidr, StaticConfigV4, udp::UdpSocket};
|
||||
use embassy_time::Timer;
|
||||
use embedded_io::Write;
|
||||
use log::{debug, error, info, LevelFilter};
|
||||
use log::{LevelFilter, debug, error, info};
|
||||
use rand::{RngCore, SeedableRng};
|
||||
use zedboard::{
|
||||
phy_marvell::{LatchingLinkStatus, MARVELL_88E1518_OUI},
|
||||
PS_CLOCK_FREQUENCY,
|
||||
phy_marvell::{LatchingLinkStatus, MARVELL_88E1518_OUI},
|
||||
};
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
configure_level_shifter,
|
||||
eth::{
|
||||
embassy_net::InterruptResult, AlignedBuffer, ClkDivCollection, EthernetConfig,
|
||||
EthernetLowLevel,
|
||||
AlignedBuffer, ClkDivCollection, EthernetConfig, EthernetLowLevel,
|
||||
embassy_net::InterruptResult,
|
||||
},
|
||||
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||
gpio::{GpioPins, Output, PinState},
|
||||
gtc::Gtc,
|
||||
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||
BootMode,
|
||||
};
|
||||
|
||||
use zynq7000::{slcr::LevelShifterConfig, PsPeripherals};
|
||||
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000_rt::{self as _, mmu::section_attrs::SHAREABLE_DEVICE, mmu_l1_table_mut};
|
||||
|
||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard Ethernet Example --\n\r";
|
||||
const USE_DHCP: bool = true;
|
||||
const PRINT_PACKET_STATS: bool = false;
|
||||
const LOG_LEVEL: LevelFilter = LevelFilter::Info;
|
||||
const NUM_RX_SLOTS: usize = 16;
|
||||
const NUM_TX_SLOTS: usize = 16;
|
||||
|
||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard Ethernet Example --\n\r";
|
||||
|
||||
// Unicast address with OUI of the Marvell 88E1518 PHY.
|
||||
const MAC_ADDRESS: [u8; 6] = [
|
||||
@@ -50,64 +52,14 @@ const MAC_ADDRESS: [u8; 6] = [
|
||||
/// MMU.
|
||||
const UNCACHED_ADDR: u32 = 0x4000000;
|
||||
|
||||
const NUM_RX_SLOTS: usize = 16;
|
||||
const NUM_TX_SLOTS: usize = 16;
|
||||
|
||||
static RX_DESCR_TAKEN: AtomicBool = AtomicBool::new(false);
|
||||
static TX_DESCR_TAKEN: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct RxDescriptor(
|
||||
pub UnsafeCell<MaybeUninit<[zynq7000_hal::eth::rx_descr::Descriptor; NUM_RX_SLOTS]>>,
|
||||
);
|
||||
|
||||
unsafe impl Sync for RxDescriptor {}
|
||||
|
||||
impl RxDescriptor {
|
||||
/// Initializes the RX descriptors and returns a mutable reference to them.
|
||||
pub fn take(
|
||||
&self,
|
||||
) -> Option<&'static mut [zynq7000_hal::eth::rx_descr::Descriptor; NUM_RX_SLOTS]> {
|
||||
if RX_DESCR_TAKEN.swap(true, core::sync::atomic::Ordering::SeqCst) {
|
||||
return None; // Already taken, return None
|
||||
}
|
||||
let descr = unsafe { &mut *self.0.get() };
|
||||
descr.write([const { zynq7000_hal::eth::rx_descr::Descriptor::new() }; NUM_RX_SLOTS]);
|
||||
Some(unsafe { descr.assume_init_mut() })
|
||||
}
|
||||
}
|
||||
|
||||
// These descriptors must be placed in uncached memory. The MMU will be used to configure the
|
||||
// .uncached memory segment as device memory.
|
||||
#[unsafe(link_section = ".uncached")]
|
||||
static RX_DESCRIPTORS: RxDescriptor = RxDescriptor(UnsafeCell::new(MaybeUninit::uninit()));
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct TxDescriptor(
|
||||
pub UnsafeCell<MaybeUninit<[zynq7000_hal::eth::tx_descr::Descriptor; NUM_TX_SLOTS]>>,
|
||||
);
|
||||
|
||||
unsafe impl Sync for TxDescriptor {}
|
||||
|
||||
impl TxDescriptor {
|
||||
/// Initializes the TX descriptors and returns a mutable reference to them.
|
||||
pub fn take(
|
||||
&self,
|
||||
) -> Option<&'static mut [zynq7000_hal::eth::tx_descr::Descriptor; NUM_TX_SLOTS]> {
|
||||
if TX_DESCR_TAKEN.swap(true, core::sync::atomic::Ordering::SeqCst) {
|
||||
return None; // Already taken, return None
|
||||
}
|
||||
let descr = unsafe { &mut *self.0.get() };
|
||||
descr.write([const { zynq7000_hal::eth::tx_descr::Descriptor::new() }; NUM_TX_SLOTS]);
|
||||
Some(unsafe { descr.assume_init_mut() })
|
||||
}
|
||||
}
|
||||
|
||||
static RX_DESCRIPTORS: zynq7000_hal::eth::rx_descr::DescriptorList<NUM_RX_SLOTS> =
|
||||
zynq7000_hal::eth::rx_descr::DescriptorList::new();
|
||||
#[unsafe(link_section = ".uncached")]
|
||||
static TX_DESCRIPTORS: TxDescriptor = TxDescriptor(UnsafeCell::new(MaybeUninit::uninit()));
|
||||
|
||||
static ETH_RX_BUFS: static_cell::ConstStaticCell<[AlignedBuffer; NUM_RX_SLOTS]> =
|
||||
static_cell::ConstStaticCell::new([AlignedBuffer([0; zynq7000_hal::eth::MTU]); NUM_RX_SLOTS]);
|
||||
static ETH_TX_BUFS: static_cell::ConstStaticCell<[AlignedBuffer; NUM_TX_SLOTS]> =
|
||||
static_cell::ConstStaticCell::new([AlignedBuffer([0; zynq7000_hal::eth::MTU]); NUM_TX_SLOTS]);
|
||||
static TX_DESCRIPTORS: zynq7000_hal::eth::tx_descr::DescriptorList<NUM_TX_SLOTS> =
|
||||
zynq7000_hal::eth::tx_descr::DescriptorList::new();
|
||||
|
||||
static ETH_ERR_QUEUE: embassy_sync::channel::Channel<
|
||||
embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
|
||||
@@ -139,12 +91,27 @@ async fn embassy_net_task(
|
||||
runner.run().await
|
||||
}
|
||||
|
||||
/// Simple UDP echo task.
|
||||
#[embassy_executor::task]
|
||||
async fn led_task(mut mio_led: Output) -> ! {
|
||||
let mut ticker = Ticker::every(Duration::from_millis(200));
|
||||
async fn udp_task(mut udp: UdpSocket<'static>) -> ! {
|
||||
let mut rx_buf = [0; zynq7000_hal::eth::MTU];
|
||||
udp.bind(8000)
|
||||
.expect("failed to bind UDP socket to port 8000");
|
||||
loop {
|
||||
mio_led.toggle().unwrap();
|
||||
ticker.next().await; // Wait for the next cycle of the ticker
|
||||
match udp.recv_from(&mut rx_buf).await {
|
||||
Ok((data, meta)) => {
|
||||
log::info!("UDP: rx {data} bytes from {meta:?}");
|
||||
match udp.send_to(&rx_buf[0..data], meta).await {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
log::warn!("udp send error: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("udp receive error: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +136,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
unsafe {
|
||||
gic.enable_interrupts();
|
||||
}
|
||||
let mut gpio_pins = GpioPins::new(dp.gpio);
|
||||
let gpio_pins = GpioPins::new(dp.gpio);
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||
@@ -192,6 +159,14 @@ async fn main(spawner: Spawner) -> ! {
|
||||
let boot_mode = BootMode::new();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
static ETH_RX_BUFS: static_cell::ConstStaticCell<[AlignedBuffer; NUM_RX_SLOTS]> =
|
||||
static_cell::ConstStaticCell::new(
|
||||
[AlignedBuffer([0; zynq7000_hal::eth::MTU]); NUM_RX_SLOTS],
|
||||
);
|
||||
static ETH_TX_BUFS: static_cell::ConstStaticCell<[AlignedBuffer; NUM_TX_SLOTS]> =
|
||||
static_cell::ConstStaticCell::new(
|
||||
[AlignedBuffer([0; zynq7000_hal::eth::MTU]); NUM_TX_SLOTS],
|
||||
);
|
||||
let rx_bufs = ETH_RX_BUFS.take();
|
||||
let tx_bufs = ETH_TX_BUFS.take();
|
||||
|
||||
@@ -199,11 +174,9 @@ async fn main(spawner: Spawner) -> ! {
|
||||
let tx_descr = TX_DESCRIPTORS.take().unwrap();
|
||||
// Unwraps okay, list length is not 0
|
||||
let mut rx_descr_ref =
|
||||
zynq7000_hal::eth::rx_descr::DescriptorList::new(rx_descr.as_mut_slice()).unwrap();
|
||||
// Create an unsafe copy for error handling.
|
||||
// let rx_descr_copy_for_error_handling = unsafe { rx_descr_ref.clone_unchecked() };
|
||||
zynq7000_hal::eth::rx_descr::DescriptorListWrapper::new(rx_descr.as_mut_slice()).unwrap();
|
||||
let mut tx_descr_ref =
|
||||
zynq7000_hal::eth::tx_descr::DescriptorList::new(tx_descr.as_mut_slice());
|
||||
zynq7000_hal::eth::tx_descr::DescriptorListWrapper::new(tx_descr.as_mut_slice());
|
||||
rx_descr_ref.init_with_aligned_bufs(rx_bufs.as_slice());
|
||||
tx_descr_ref.init_or_reset();
|
||||
|
||||
@@ -273,14 +246,15 @@ async fn main(spawner: Spawner) -> ! {
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let config = embassy_net::Config::dhcpv4(Default::default());
|
||||
/*
|
||||
let config = embassy_net::Config::ipv4_static(StaticConfigV4 {
|
||||
address: Ipv4Cidr::new(Ipv4Addr::new(192, 168, 179, 25), 24),
|
||||
gateway: None,
|
||||
dns_servers: Default::default(),
|
||||
});
|
||||
*/
|
||||
let config = if USE_DHCP {
|
||||
embassy_net::Config::dhcpv4(Default::default())
|
||||
} else {
|
||||
embassy_net::Config::ipv4_static(StaticConfigV4 {
|
||||
address: Ipv4Cidr::new(Ipv4Addr::new(192, 168, 179, 25), 24),
|
||||
gateway: None,
|
||||
dns_servers: Default::default(),
|
||||
})
|
||||
};
|
||||
static RESOURCES: static_cell::StaticCell<embassy_net::StackResources<3>> =
|
||||
static_cell::StaticCell::new();
|
||||
let mut rng = rand::rngs::SmallRng::seed_from_u64(1);
|
||||
@@ -290,20 +264,29 @@ async fn main(spawner: Spawner) -> ! {
|
||||
RESOURCES.init(embassy_net::StackResources::new()),
|
||||
rng.next_u64(),
|
||||
);
|
||||
|
||||
static RX_UDP_META: static_cell::ConstStaticCell<[embassy_net::udp::PacketMetadata; 8]> =
|
||||
static_cell::ConstStaticCell::new([embassy_net::udp::PacketMetadata::EMPTY; 8]);
|
||||
static TX_UDP_META: static_cell::ConstStaticCell<[embassy_net::udp::PacketMetadata; 8]> =
|
||||
static_cell::ConstStaticCell::new([embassy_net::udp::PacketMetadata::EMPTY; 8]);
|
||||
// Ensure those are in the data section by making them static.
|
||||
static TX_UDP_BUFS: static_cell::ConstStaticCell<[u8; zynq7000_hal::eth::MTU]> =
|
||||
static_cell::ConstStaticCell::new([0; zynq7000_hal::eth::MTU]);
|
||||
static RX_UDP_BUFS: static_cell::ConstStaticCell<[u8; zynq7000_hal::eth::MTU]> =
|
||||
static_cell::ConstStaticCell::new([0; zynq7000_hal::eth::MTU]);
|
||||
|
||||
let udp_socket = UdpSocket::new(
|
||||
stack,
|
||||
RX_UDP_META.take(),
|
||||
RX_UDP_BUFS.take(),
|
||||
TX_UDP_META.take(),
|
||||
TX_UDP_BUFS.take(),
|
||||
);
|
||||
spawner.spawn(embassy_net_task(runner)).unwrap();
|
||||
spawner.spawn(udp_task(udp_socket)).unwrap();
|
||||
|
||||
let mut mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
|
||||
|
||||
let mut _emio_leds: [Output; 8] = [
|
||||
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), PinState::Low),
|
||||
];
|
||||
let mut ip_mode = IpMode::LinkDown;
|
||||
let mut transmitted_frames = 0;
|
||||
let mut received_frames = 0;
|
||||
@@ -312,17 +295,21 @@ async fn main(spawner: Spawner) -> ! {
|
||||
while let Ok(msg) = receiver.try_receive() {
|
||||
info!("Received interrupt result: {msg:?}");
|
||||
}
|
||||
let sent_frames_since_last = eth.ll().regs.statistics().read_tx_count();
|
||||
if sent_frames_since_last > 0 {
|
||||
transmitted_frames += sent_frames_since_last;
|
||||
info!("Frame sent count: {transmitted_frames}");
|
||||
}
|
||||
let received_frames_since_last = eth.ll().regs.statistics().read_rx_count();
|
||||
if received_frames_since_last > 0 {
|
||||
received_frames += received_frames_since_last;
|
||||
info!("Frame received count: {received_frames}");
|
||||
if PRINT_PACKET_STATS {
|
||||
let sent_frames_since_last = eth.ll().regs.statistics().read_tx_count();
|
||||
if sent_frames_since_last > 0 {
|
||||
transmitted_frames += sent_frames_since_last;
|
||||
info!("Frame sent count: {transmitted_frames}");
|
||||
}
|
||||
let received_frames_since_last = eth.ll().regs.statistics().read_rx_count();
|
||||
if received_frames_since_last > 0 {
|
||||
received_frames += received_frames_since_last;
|
||||
info!("Frame received count: {received_frames}");
|
||||
}
|
||||
}
|
||||
|
||||
// This is basically a linker checker task. It also takes care of notifying the
|
||||
// embassy stack of link state changes.
|
||||
match ip_mode {
|
||||
// Assuming that auto-negotiation is performed automatically.
|
||||
IpMode::LinkDown => {
|
||||
@@ -435,54 +422,3 @@ fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {info:?}");
|
||||
loop {}
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn udp_tcp_test() {
|
||||
let mut rx_buffer = [0; MTU];
|
||||
let mut reception_buf = [0; MTU];
|
||||
let mut tx_buffer = [0; MTU];
|
||||
// For UDP, we can use a fixed size array.
|
||||
let mut rx_meta: [icmp::PacketMetadata; 8] = [icmp::PacketMetadata::EMPTY; 8];
|
||||
let mut tx_meta: [icmp::PacketMetadata; 8] = [icmp::PacketMetadata::EMPTY; 8];
|
||||
let mut rx_meta: [udp::PacketMetadata; 8] = [udp::PacketMetadata::EMPTY; 8];
|
||||
let mut tx_meta: [udp::PacketMetadata; 8] = [udp::PacketMetadata::EMPTY; 8];
|
||||
// Now what? maybe UDP Server?
|
||||
let mut socket = UdpSocket::new(
|
||||
stack,
|
||||
&mut rx_meta,
|
||||
&mut rx_buffer,
|
||||
&mut tx_meta,
|
||||
&mut tx_buffer,
|
||||
);
|
||||
|
||||
//socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));
|
||||
|
||||
// You need to start a server on the host machine, for example: `nc -ul 9000`
|
||||
let remote_endpoint = (Ipv4Addr::new(192, 168, 178, 105), 9000);
|
||||
info!("connecting...");
|
||||
socket.bind(0).unwrap();
|
||||
let port = socket.endpoint().port;
|
||||
//let r = socket.(remote_endpoint).await;
|
||||
if let Err(e) = r {
|
||||
info!("connect error: {e:?}");
|
||||
Timer::after_secs(1).await;
|
||||
continue;
|
||||
}
|
||||
info!("connected to port {port}!");
|
||||
let mut sent_once = true;
|
||||
loop {
|
||||
if sent_once {
|
||||
let r = socket.send_to(b"Hello\n", remote_endpoint).await;
|
||||
if let Err(e) = r {
|
||||
info!("write error: {e:?}");
|
||||
break;
|
||||
}
|
||||
sent_once = false;
|
||||
}
|
||||
let received = socket.recv_from(&mut reception_buf).await;
|
||||
log::info!("received: {received:?}");
|
||||
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@@ -165,17 +165,17 @@ pub fn on_interrupt(eth_id: super::EthernetId) -> InterruptResult {
|
||||
}
|
||||
|
||||
pub struct DescriptorsAndBuffers {
|
||||
pub rx_descr: rx_descr::DescriptorList<'static>,
|
||||
pub rx_descr: rx_descr::DescriptorListWrapper<'static>,
|
||||
pub rx_bufs: &'static mut [super::AlignedBuffer],
|
||||
pub tx_descr: tx_descr::DescriptorList<'static>,
|
||||
pub tx_descr: tx_descr::DescriptorListWrapper<'static>,
|
||||
pub tx_bufs: &'static mut [super::AlignedBuffer],
|
||||
}
|
||||
|
||||
impl DescriptorsAndBuffers {
|
||||
pub fn new(
|
||||
rx_descr: rx_descr::DescriptorList<'static>,
|
||||
rx_descr: rx_descr::DescriptorListWrapper<'static>,
|
||||
rx_bufs: &'static mut [super::AlignedBuffer],
|
||||
tx_descr: tx_descr::DescriptorList<'static>,
|
||||
tx_descr: tx_descr::DescriptorListWrapper<'static>,
|
||||
tx_bufs: &'static mut [super::AlignedBuffer],
|
||||
) -> Option<Self> {
|
||||
if rx_descr.len() != rx_bufs.len() || tx_descr.len() != tx_bufs.len() {
|
||||
@@ -214,7 +214,7 @@ impl Driver {
|
||||
}
|
||||
|
||||
pub struct EmbassyNetRxToken<'a> {
|
||||
descr_list: &'a mut rx_descr::DescriptorList<'static>,
|
||||
descr_list: &'a mut rx_descr::DescriptorListWrapper<'static>,
|
||||
slot_index: usize,
|
||||
rx_buf: &'a mut super::AlignedBuffer,
|
||||
rx_size: usize,
|
||||
@@ -246,7 +246,7 @@ impl embassy_net_driver::RxToken for EmbassyNetRxToken<'_> {
|
||||
|
||||
pub struct EmbassyNetTxToken<'a> {
|
||||
eth_id: super::EthernetId,
|
||||
descr_list: &'a mut tx_descr::DescriptorList<'static>,
|
||||
descr_list: &'a mut tx_descr::DescriptorListWrapper<'static>,
|
||||
tx_bufs: &'a mut [super::AlignedBuffer],
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,14 @@
|
||||
//! RX buffer descriptor module.
|
||||
use core::{cell::UnsafeCell, mem::MaybeUninit, sync::atomic::AtomicBool};
|
||||
|
||||
use crate::{cache::clean_and_invalidate_data_cache_range, eth::AlignedBuffer};
|
||||
|
||||
pub use super::shared::Ownership;
|
||||
use arbitrary_int::{Number, u2, u3, u13, u30};
|
||||
use vcell::VolatileCell;
|
||||
|
||||
static RX_DESCR_TAKEN: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// RX buffer descriptor.
|
||||
///
|
||||
/// The user should declare an array of this structure inside uncached memory.
|
||||
@@ -117,6 +121,38 @@ impl Default for Descriptor {
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a low level wrapper to simplify declaring a global descriptor list.
|
||||
///
|
||||
/// It allows placing the descriptor structure statically in memory which might not
|
||||
/// be zero-initialized.
|
||||
#[repr(transparent)]
|
||||
pub struct DescriptorList<const SLOTS: usize>(pub UnsafeCell<MaybeUninit<[Descriptor; SLOTS]>>);
|
||||
|
||||
unsafe impl<const SLOTS: usize> Sync for DescriptorList<SLOTS> {}
|
||||
|
||||
impl<const SLOTS: usize> DescriptorList<SLOTS> {
|
||||
#[inline]
|
||||
pub const fn new() -> Self {
|
||||
Self(UnsafeCell::new(MaybeUninit::uninit()))
|
||||
}
|
||||
|
||||
/// Initializes the RX descriptors and returns a mutable reference to them.
|
||||
pub fn take(&self) -> Option<&'static mut [Descriptor; SLOTS]> {
|
||||
if RX_DESCR_TAKEN.swap(true, core::sync::atomic::Ordering::SeqCst) {
|
||||
return None; // Already taken, return None
|
||||
}
|
||||
let descr = unsafe { &mut *self.0.get() };
|
||||
descr.write([const { Descriptor::new() }; SLOTS]);
|
||||
Some(unsafe { descr.assume_init_mut() })
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SLOTS: usize> Default for DescriptorList<SLOTS> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum FrameScanResult {
|
||||
NoFrames,
|
||||
SingleFrame {
|
||||
@@ -134,12 +170,16 @@ pub enum FrameScanResult {
|
||||
},
|
||||
}
|
||||
|
||||
pub struct DescriptorList<'a> {
|
||||
/// This is a thin wrapper around a descriptor list.
|
||||
///
|
||||
/// It provides a basic higher-level API to perform tasks like descriptor initialization
|
||||
/// and handling of received frames.
|
||||
pub struct DescriptorListWrapper<'a> {
|
||||
list: &'a mut [Descriptor],
|
||||
current_idx: usize,
|
||||
}
|
||||
|
||||
impl<'a> DescriptorList<'a> {
|
||||
impl<'a> DescriptorListWrapper<'a> {
|
||||
#[inline]
|
||||
pub fn new(descr_list: &'a mut [Descriptor]) -> Option<Self> {
|
||||
if descr_list.is_empty() {
|
||||
@@ -166,7 +206,7 @@ impl<'a> DescriptorList<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl DescriptorList<'_> {
|
||||
impl DescriptorListWrapper<'_> {
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
@@ -202,6 +242,11 @@ impl DescriptorList<'_> {
|
||||
last.set_word_0(word_0);
|
||||
}
|
||||
|
||||
/// Initialize the descriptor list with a provided RX buffer.
|
||||
///
|
||||
/// This function performs important initialization routines for the descriptors
|
||||
/// and also sets the addresses of the provided buffers. The number of buffers and the length
|
||||
/// of the descriptor list need to be the same.
|
||||
pub fn init_with_aligned_bufs(&mut self, aligned_bufs: &[AlignedBuffer]) {
|
||||
self.current_idx = 0;
|
||||
let list_len = self.list.len();
|
||||
@@ -231,6 +276,7 @@ impl DescriptorList<'_> {
|
||||
);
|
||||
}
|
||||
|
||||
/// This function tries to scan for received frames and handles the frame if it finds one.
|
||||
pub fn scan_and_handle_next_received_frame(
|
||||
&mut self,
|
||||
scan_full_descr_list: bool,
|
||||
|
@@ -1,3 +1,5 @@
|
||||
use core::{cell::UnsafeCell, mem::MaybeUninit, sync::atomic::AtomicBool};
|
||||
|
||||
use arbitrary_int::u14;
|
||||
|
||||
pub use super::shared::Ownership;
|
||||
@@ -17,6 +19,40 @@ pub struct Descriptor {
|
||||
pub word1: VolatileCell<Word1>,
|
||||
}
|
||||
|
||||
static TX_DESCR_TAKEN: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// This is a low level wrapper to simplify declaring a global descriptor list.
|
||||
///
|
||||
/// It allows placing the descriptor structure statically in memory which might not
|
||||
/// be zero-initialized.
|
||||
#[repr(transparent)]
|
||||
pub struct DescriptorList<const SLOTS: usize>(pub UnsafeCell<MaybeUninit<[Descriptor; SLOTS]>>);
|
||||
|
||||
unsafe impl<const SLOTS: usize> Sync for DescriptorList<SLOTS> {}
|
||||
|
||||
impl<const SLOTS: usize> DescriptorList<SLOTS> {
|
||||
#[inline]
|
||||
pub const fn new() -> Self {
|
||||
Self(UnsafeCell::new(MaybeUninit::uninit()))
|
||||
}
|
||||
|
||||
/// Initializes the TX descriptors and returns a mutable reference to them.
|
||||
pub fn take(&self) -> Option<&'static mut [Descriptor; SLOTS]> {
|
||||
if TX_DESCR_TAKEN.swap(true, core::sync::atomic::Ordering::SeqCst) {
|
||||
return None; // Already taken, return None
|
||||
}
|
||||
let descr = unsafe { &mut *self.0.get() };
|
||||
descr.write([const { Descriptor::new() }; SLOTS]);
|
||||
Some(unsafe { descr.assume_init_mut() })
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SLOTS: usize> Default for DescriptorList<SLOTS> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[bitbybit::bitenum(u3, exhaustive = true)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum TransmitChecksumGenerationStatus {
|
||||
@@ -157,7 +193,7 @@ pub enum BusyHandlingResult {
|
||||
Complete,
|
||||
}
|
||||
|
||||
pub struct DescriptorList<'a> {
|
||||
pub struct DescriptorListWrapper<'a> {
|
||||
list: &'a mut [Descriptor],
|
||||
/// The head index is used to handle the transmission of new frames.
|
||||
tx_idx: usize,
|
||||
@@ -165,7 +201,7 @@ pub struct DescriptorList<'a> {
|
||||
busy_idx: usize,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for DescriptorList<'_> {
|
||||
impl core::fmt::Debug for DescriptorListWrapper<'_> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("DescriptorList")
|
||||
.field("tx_idx", &self.tx_idx)
|
||||
@@ -175,7 +211,7 @@ impl core::fmt::Debug for DescriptorList<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DescriptorList<'a> {
|
||||
impl<'a> DescriptorListWrapper<'a> {
|
||||
#[inline]
|
||||
pub fn new(descr_list: &'a mut [Descriptor]) -> Self {
|
||||
Self {
|
||||
@@ -186,7 +222,7 @@ impl<'a> DescriptorList<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl DescriptorList<'_> {
|
||||
impl DescriptorListWrapper<'_> {
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
|
Reference in New Issue
Block a user