continue
Some checks failed
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Some checks failed
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
This commit is contained in:
@@ -35,7 +35,7 @@ critical-section = "1"
|
||||
libm = "0.2"
|
||||
log = "0.4"
|
||||
embassy-sync = "0.6"
|
||||
embassy-net = { path = "../../../Rust/embassy/embassy-net", version = "0.7" }
|
||||
embassy-net-driver = { path = "../../../Rust/embassy/embassy-net-driver", version = "0.2" }
|
||||
raw-slicee = "0.1"
|
||||
embedded-io-async = "0.6"
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use arbitrary_int::u14;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
pub use super::rx_descr;
|
||||
@@ -11,10 +12,12 @@ pub struct EthernetEmbassyNet {
|
||||
pub burst_size: usize,
|
||||
pub mac_addr: [u8; 6],
|
||||
pub rx_descr: &'static [rx_descr::Descriptor],
|
||||
pub rx_index: usize,
|
||||
pub tx_descr: &'static [tx_descr::Descriptor],
|
||||
pub tx_index: usize,
|
||||
pub link_state: embassy_net::driver::LinkState,
|
||||
//pub rx_index: usize,
|
||||
//pub tx_descr: &'static [tx_descr::Descriptor],
|
||||
//pub tx_index: usize,
|
||||
pub tx_descr: tx_descr::DescriptorList<'static>,
|
||||
pub tx_bufs: &'static mut [super::AlignedBuffer],
|
||||
pub link_state: embassy_net_driver::LinkState,
|
||||
}
|
||||
|
||||
pub struct EmbassyNetRxToken {
|
||||
@@ -22,45 +25,54 @@ pub struct EmbassyNetRxToken {
|
||||
rx_index: usize,
|
||||
}
|
||||
|
||||
impl embassy_net::driver::RxToken for EmbassyNetRxToken {
|
||||
impl embassy_net_driver::RxToken for EmbassyNetRxToken {
|
||||
fn consume<R, F>(self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
f(&mut [])
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EmbassyNetTxToken {
|
||||
tx_descr_ref: tx_descr::DescriptorListRef<'static>,
|
||||
tx_index: usize,
|
||||
pub struct EmbassyNetTxToken<'a> {
|
||||
descr_list: &'a mut tx_descr::DescriptorList<'static>,
|
||||
tx_bufs: &'a mut [super::AlignedBuffer],
|
||||
}
|
||||
|
||||
impl embassy_net::driver::TxToken for EmbassyNetRxToken {
|
||||
fn consume<R, F>(self, len: usize, f: F) -> R
|
||||
impl embassy_net_driver::TxToken for EmbassyNetTxToken<'_> {
|
||||
fn consume<R, F>(mut self, len: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
assert!(len <= super::MTU, "packet length exceeds MTU");
|
||||
let tx_idx = self.descr_list.current_tx_idx();
|
||||
let buffer = self.tx_bufs.get_mut(tx_idx).unwrap();
|
||||
let result = f(&mut buffer.0);
|
||||
// TODO: Clean and invalidate cache.
|
||||
let addr = buffer.0.as_ptr() as u32;
|
||||
self.descr_list.prepare_transfer_unchecked(addr, u14::new(len as u16), true, false);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl EthernetEmbassyNet {
|
||||
#[inline]
|
||||
pub fn update_link_state(&mut self, link_state: embassy_net::driver::LinkState) {
|
||||
pub fn update_link_state(&mut self, link_state: embassy_net_driver::LinkState) {
|
||||
self.link_state = link_state;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_link_state_up(&mut self) {
|
||||
self.update_link_state(embassy_net::driver::LinkState::Up);
|
||||
self.update_link_state(embassy_net_driver::LinkState::Up);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_link_state_down(&mut self) {
|
||||
self.update_link_state(embassy_net::driver::LinkState::Down);
|
||||
self.update_link_state(embassy_net_driver::LinkState::Down);
|
||||
}
|
||||
}
|
||||
|
||||
impl embassy_net::Driver for EthernetEmbassyNet {
|
||||
impl embassy_net_driver::Driver for EthernetEmbassyNet {
|
||||
type RxToken<'a>
|
||||
where
|
||||
Self: 'a,
|
||||
@@ -69,38 +81,44 @@ impl embassy_net::Driver for EthernetEmbassyNet {
|
||||
type TxToken<'a>
|
||||
where
|
||||
Self: 'a,
|
||||
= EmbassyNetTxToken;
|
||||
= EmbassyNetTxToken<'a>;
|
||||
|
||||
fn receive(
|
||||
&mut self,
|
||||
cx: &mut core::task::Context,
|
||||
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||
RX_WAKER.register(cx.waker());
|
||||
None
|
||||
}
|
||||
|
||||
fn transmit(&mut self, cx: &mut core::task::Context) -> Option<Self::TxToken<'_>> {
|
||||
TX_WAKER.register(cx.waker());
|
||||
if self.tx_descr.full() {
|
||||
return None;
|
||||
}
|
||||
Some(EmbassyNetTxToken {
|
||||
descr_list: &mut self.tx_descr,
|
||||
tx_bufs: &mut self.tx_bufs,
|
||||
})
|
||||
}
|
||||
|
||||
fn link_state(&mut self, cx: &mut core::task::Context) -> embassy_net::driver::LinkState {
|
||||
fn link_state(&mut self, cx: &mut core::task::Context) -> embassy_net_driver::LinkState {
|
||||
self.link_state
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> embassy_net::driver::Capabilities {
|
||||
embassy_net::driver::Capabilities {
|
||||
max_transmission_unit: super::MTU,
|
||||
max_burst_size: Some(self.burst_size),
|
||||
checksum: embassy_net::driver::ChecksumCapabilities {
|
||||
ipv4: true,
|
||||
udp: true,
|
||||
tcp: true,
|
||||
icmpv4: true,
|
||||
icmpv6: true,
|
||||
},
|
||||
}
|
||||
fn capabilities(&self) -> embassy_net_driver::Capabilities {
|
||||
let mut capabilities = embassy_net_driver::Capabilities::default();
|
||||
capabilities.max_transmission_unit = super::MTU;
|
||||
capabilities.max_burst_size = Some(self.burst_size);
|
||||
capabilities.checksum.ipv4 = embassy_net_driver::Checksum::Both;
|
||||
capabilities.checksum.udp = embassy_net_driver::Checksum::Both;
|
||||
capabilities.checksum.tcp = embassy_net_driver::Checksum::Both;
|
||||
capabilities.checksum.icmpv4 = embassy_net_driver::Checksum::None;
|
||||
capabilities.checksum.icmpv6 = embassy_net_driver::Checksum::None;
|
||||
capabilities
|
||||
}
|
||||
|
||||
fn hardware_address(&self) -> embassy_net::driver::HardwareAddress {
|
||||
embassy_net::driver::HardwareAddress::Ethernet(self.mac_addr)
|
||||
fn hardware_address(&self) -> embassy_net_driver::HardwareAddress {
|
||||
embassy_net_driver::HardwareAddress::Ethernet(self.mac_addr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ pub mod embassy_net;
|
||||
pub const MTU: usize = 1536;
|
||||
pub const MAX_MDC_SPEED: Hertz = Hertz::from_raw(2_500_000);
|
||||
|
||||
#[repr(align(32))]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct AlignedBuffer(pub [u8; MTU]);
|
||||
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
use crate::gpio::mio::{
|
||||
Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27,
|
||||
|
||||
@@ -9,6 +9,7 @@ pub use super::shared::Ownership;
|
||||
/// These descriptors are shared between software and hardware and contain information
|
||||
/// related to frame reception.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Descriptor {
|
||||
/// The first word of the descriptor which is the byte address of the buffer.
|
||||
pub word0: u32,
|
||||
@@ -32,6 +33,9 @@ pub enum TransmitChecksumGenerationStatus {
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Word1 {
|
||||
/// The ownership bit must be set to [Ownership::Hardware] if a frame should be transmitted.
|
||||
///
|
||||
/// The controller will set this to [Ownership::Software] once the frame has been transmitted.
|
||||
#[bit(31, rw)]
|
||||
ownership: Ownership,
|
||||
#[bit(30, rw)]
|
||||
@@ -66,22 +70,39 @@ impl Descriptor {
|
||||
self.word1.set_ownership(ownership);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ownership(&self) -> Ownership {
|
||||
self.word1.ownership()
|
||||
}
|
||||
|
||||
/// Set the wrap bit, which should be done for the last descriptor in the descriptor list.
|
||||
#[inline]
|
||||
pub fn set_wrap_bit(&mut self) {
|
||||
self.word1.set_wrap(true);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_tx_transfer_addr_unchecked(&mut self, addr: u32) {
|
||||
self.word0 = addr;
|
||||
}
|
||||
|
||||
/// Set the information for a transfer.
|
||||
pub fn set_tx_transfer_info(
|
||||
pub fn setup_tx_transfer_unchecked(
|
||||
&mut self,
|
||||
addr: u32,
|
||||
tx_len: u14,
|
||||
last_buffer: bool,
|
||||
no_crc_generation: bool,
|
||||
) {
|
||||
self.word1.set_tx_len(tx_len);
|
||||
self.word1.set_last_buffer(last_buffer);
|
||||
self.word1.set_no_crc_generation(no_crc_generation);
|
||||
self.set_tx_transfer_addr_unchecked(addr);
|
||||
// Perform the read-modify-write sequence manually to ensure a minimum of reads/writes
|
||||
// for the uncached memory.
|
||||
let mut word1 = Word1::new_with_raw_value(self.word1.raw_value());
|
||||
word1.set_tx_len(tx_len);
|
||||
word1.set_last_buffer(last_buffer);
|
||||
word1.set_no_crc_generation(no_crc_generation);
|
||||
word1.set_ownership(Ownership::Hardware);
|
||||
self.word1 = word1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,19 +113,72 @@ impl Default for Descriptor {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DescriptorListRef<'a>(&'a mut [Descriptor]);
|
||||
#[derive(Debug)]
|
||||
pub struct DescriptorList<'a> {
|
||||
list: &'a mut [Descriptor],
|
||||
/// The head index is used to handle the transmission of new frames.
|
||||
tx_idx: usize,
|
||||
/// The tail index is used to track the progress of active transmissions.
|
||||
busy_idx: usize,
|
||||
}
|
||||
|
||||
impl<'a> DescriptorListRef<'a> {
|
||||
#[inline]
|
||||
pub fn new(descriptor: &'a mut [Descriptor]) -> Self {
|
||||
Self(descriptor)
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
#[error("tx error: {self:?}")]
|
||||
pub struct TxError {
|
||||
retry_limit_exceeded: bool,
|
||||
late_collisions: bool,
|
||||
ahb_error: bool,
|
||||
checksum_generation: Option<TransmitChecksumGenerationStatus>,
|
||||
}
|
||||
|
||||
impl TxError {
|
||||
pub fn from_word1(word1: &Word1) -> Self {
|
||||
Self {
|
||||
retry_limit_exceeded: word1.retry_limit_exceeded(),
|
||||
late_collisions: word1.late_collision(),
|
||||
ahb_error: word1.transmit_frame_corruption_ahb_error(),
|
||||
checksum_generation: if word1.checksum_status()
|
||||
== TransmitChecksumGenerationStatus::NoError
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(word1.checksum_status())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DescriptorListRef<'_> {
|
||||
pub enum IncrementResult {
|
||||
Busy,
|
||||
Ok,
|
||||
}
|
||||
|
||||
impl<'a> DescriptorList<'a> {
|
||||
#[inline]
|
||||
pub fn new(descr_list: &'a mut [Descriptor]) -> Self {
|
||||
Self {
|
||||
list: descr_list,
|
||||
tx_idx: 0,
|
||||
busy_idx: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum BusyHandlingResult {
|
||||
/// Handled a descriptor slot where a TX error has occured.
|
||||
TxError(TxError),
|
||||
/// Handled one busy descriptor slot. More calls to this function are required to handle
|
||||
/// all busy slots.
|
||||
SlotHandled,
|
||||
/// The busy index has caught up to the transmission index (all frames have been sent), or
|
||||
/// the busy index is at an active transmission index.
|
||||
Complete,
|
||||
}
|
||||
|
||||
impl DescriptorList<'_> {
|
||||
#[inline]
|
||||
pub fn base_ptr(&self) -> *const Descriptor {
|
||||
self.0.as_ptr()
|
||||
self.list.as_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -112,10 +186,83 @@ impl DescriptorListRef<'_> {
|
||||
self.base_ptr() as u32
|
||||
}
|
||||
|
||||
pub fn init(&mut self) {
|
||||
for desc in self.0.iter_mut() {
|
||||
desc.set_ownership(Ownership::Hardware);
|
||||
pub fn init_or_reset(&mut self) {
|
||||
self.tx_idx = 0;
|
||||
self.busy_idx = 0;
|
||||
for desc in self.list.iter_mut() {
|
||||
desc.set_ownership(Ownership::Software);
|
||||
}
|
||||
self.0.last_mut().unwrap().set_wrap_bit();
|
||||
self.list.last_mut().unwrap().set_wrap_bit();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn increment_tx_idx(&mut self) {
|
||||
self.tx_idx = (self.tx_idx + 1) % self.list.len();
|
||||
}
|
||||
|
||||
/// Increment the busy index without checking whether it has become
|
||||
/// larger than the transmission index.
|
||||
#[inline]
|
||||
pub fn increment_busy_idx_unchecked(&mut self) {
|
||||
self.busy_idx = (self.busy_idx + 1) % self.list.len();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn full(&self) -> bool {
|
||||
self.list[self.tx_idx].ownership() == Ownership::Hardware
|
||||
}
|
||||
|
||||
/// Check whether a tranfer has completed and handles the descriptor accordingly.
|
||||
///
|
||||
/// This should be called continuosly when a TX error or a TX completion interrupt has
|
||||
/// occured until it returns [BusyHandlingResult::Complete].
|
||||
pub fn check_and_handle_completed_transfer(&mut self) -> BusyHandlingResult {
|
||||
let word1 = self.list[self.busy_idx].word1;
|
||||
if word1.ownership() == Ownership::Hardware || self.busy_idx == self.tx_idx {
|
||||
return BusyHandlingResult::Complete;
|
||||
}
|
||||
if word1.transmit_frame_corruption_ahb_error()
|
||||
|| word1.retry_limit_exceeded()
|
||||
|| word1.checksum_status() != TransmitChecksumGenerationStatus::NoError
|
||||
|| word1.late_collision()
|
||||
{
|
||||
return BusyHandlingResult::TxError(TxError::from_word1(&word1));
|
||||
}
|
||||
self.list[self.busy_idx].word1 = Word1::builder()
|
||||
.with_ownership(Ownership::Software)
|
||||
.with_wrap(word1.wrap())
|
||||
.with_retry_limit_exceeded(false)
|
||||
.with_transmit_frame_corruption_ahb_error(false)
|
||||
.with_late_collision(false)
|
||||
.with_checksum_status(TransmitChecksumGenerationStatus::NoError)
|
||||
.with_no_crc_generation(false)
|
||||
.with_last_buffer(false)
|
||||
.with_tx_len(u14::new(0))
|
||||
.build();
|
||||
self.increment_busy_idx_unchecked();
|
||||
return BusyHandlingResult::SlotHandled;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current_tx_idx(&self) -> usize {
|
||||
self.tx_idx
|
||||
}
|
||||
|
||||
/// Prepare a transfer without checking whether that particular descriptor slot is used by
|
||||
/// the hardware.
|
||||
pub fn prepare_transfer_unchecked(
|
||||
&mut self,
|
||||
addr: u32,
|
||||
tx_len: u14,
|
||||
last_buffer: bool,
|
||||
no_crc_generation: bool,
|
||||
) {
|
||||
self.list[self.tx_idx].setup_tx_transfer_unchecked(
|
||||
addr,
|
||||
tx_len,
|
||||
last_buffer,
|
||||
no_crc_generation,
|
||||
);
|
||||
self.increment_tx_idx();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user