From bff52d77b2999bd63a06cfd34c3b6b755c45228e Mon Sep 17 00:00:00 2001 From: Ben Hamlin Date: Mon, 2 Oct 2023 22:12:42 +0000 Subject: [PATCH] Add client-side device implementation to smoltcp::phy adapter Signed-off-by: Ben Hamlin --- .../src/smoltcp/phy/device/inner.rs | 326 ++++++++++++++++++ .../src/smoltcp/phy/device/mod.rs | 141 ++++++++ .../sel4-hal-adapters/src/smoltcp/phy/mod.rs | 3 + 3 files changed, 470 insertions(+) create mode 100644 crates/sel4-hal-adapters/src/smoltcp/phy/device/inner.rs create mode 100644 crates/sel4-hal-adapters/src/smoltcp/phy/device/mod.rs diff --git a/crates/sel4-hal-adapters/src/smoltcp/phy/device/inner.rs b/crates/sel4-hal-adapters/src/smoltcp/phy/device/inner.rs new file mode 100644 index 000000000..83a8f477d --- /dev/null +++ b/crates/sel4-hal-adapters/src/smoltcp/phy/device/inner.rs @@ -0,0 +1,326 @@ +//! Mutable inner part of phy::Device impl + +extern crate alloc; + +use alloc::vec::Vec; +use core::alloc::Layout; +use core::iter; +use core::ops::Range; + +use sel4_bounce_buffer_allocator::{Basic, BounceBufferAllocator}; +use sel4_externally_shared::ExternallySharedRef; +use sel4_shared_ring_buffer::{ + Descriptor, Error as SharedRingBuffersError, RingBuffers, RING_BUFFER_SIZE, +}; + +pub(crate) struct Inner { + dma_region: ExternallySharedRef<'static, [u8]>, + dma_region_paddr: usize, + bounce_buffer_allocator: BounceBufferAllocator, + rx_ring_buffers: RingBuffers<'static, fn() -> Result<(), !>>, + tx_ring_buffers: RingBuffers<'static, fn() -> Result<(), !>>, + rx_buffers: Vec, + tx_buffers: Vec, + mtu: usize, +} + +pub(crate) type RxBufferIndex = usize; + +#[derive(Clone, Debug, Eq, PartialEq)] +struct RxBufferEntry { + state: RxBufferState, + range: Range, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +enum RxBufferState { + Free, + Used { len: usize }, + Claimed { len: usize }, +} + +pub(crate) type TxBufferIndex = usize; + +#[derive(Clone, Debug, Eq, PartialEq)] +struct TxBufferEntry { + state: TxBufferState, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +enum TxBufferState { + Unclaimed, + SlotClaimed, + Sent { range: Range }, +} + +impl Inner { + pub(crate) fn new( + dma_region: ExternallySharedRef<'static, [u8]>, + dma_region_paddr: usize, + mut rx_ring_buffers: RingBuffers<'static, fn() -> Result<(), !>>, + tx_ring_buffers: RingBuffers<'static, fn() -> Result<(), !>>, + num_rx_buffers: usize, + rx_buffer_size: usize, + mtu: usize, + ) -> Self { + let max_alignment = 1 + << dma_region + .as_ptr() + .as_raw_ptr() + .addr() + .trailing_zeros() + .min(dma_region_paddr.trailing_zeros()); + + let mut bounce_buffer_allocator = + BounceBufferAllocator::new(Basic::new(dma_region.as_ptr().len()), max_alignment); + + let rx_buffers = iter::repeat_with(|| RxBufferEntry { + state: RxBufferState::Free, + range: bounce_buffer_allocator + .allocate(Layout::from_size_align(rx_buffer_size, 1).unwrap()) + .unwrap(), + }) + .take(num_rx_buffers) + .collect::>(); + + for entry in rx_buffers.iter() { + rx_ring_buffers + .free_mut() + .enqueue(descriptor_of(dma_region_paddr, entry.range.clone())) + .unwrap(); + } + let tx_buffers = iter::repeat_with(|| TxBufferEntry { + state: TxBufferState::Unclaimed, + }) + .take(RING_BUFFER_SIZE) + .collect::>(); + + Self { + dma_region, + dma_region_paddr, + bounce_buffer_allocator, + rx_ring_buffers, + tx_ring_buffers, + rx_buffers, + tx_buffers, + mtu, + } + } + + pub(crate) fn mtu(&self) -> usize { + self.mtu + } + + pub(crate) fn poll(&mut self) -> bool { + let mut notify_rx = false; + + while let Ok(desc) = self + .rx_ring_buffers + .used_mut() + .dequeue() + .map_err(|err| assert_eq!(err, SharedRingBuffersError::RingIsEmpty)) + { + let ix = self + .lookup_rx_buffer_by_encoded_addr(desc.encoded_addr()) + .unwrap(); + let entry = self.rx_buffer_entry_mut(ix); + assert_eq!(entry.state, RxBufferState::Free); + entry.state = RxBufferState::Used { + len: desc.len().try_into().unwrap(), + }; + notify_rx = true; + } + + if notify_rx { + self.rx_ring_buffers.notify().unwrap(); + } + + let mut notify_tx = false; + + while let Ok(desc) = self + .tx_ring_buffers + .used_mut() + .dequeue() + .map_err(|err| assert_eq!(err, SharedRingBuffersError::RingIsEmpty)) + { + let ix = self.lookup_tx_buffer_by_descriptor(&desc).unwrap(); + let entry = self.tx_buffer_entry_mut(ix); + let range = match &entry.state { + TxBufferState::Sent { range } => range.clone(), + _ => { + panic!() + } + }; + entry.state = TxBufferState::Unclaimed; + self.bounce_buffer_allocator.deallocate(range); + notify_tx = true; + } + + if notify_tx { + self.tx_ring_buffers.notify().unwrap(); + } + + notify_rx || notify_tx + } + + fn lookup_rx_buffer_by_encoded_addr(&self, encoded_addr: usize) -> Option { + self.rx_buffers + .iter() + .enumerate() + .find(|(_i, entry)| self.dma_region_paddr + entry.range.start == encoded_addr) + .map(|(i, _entry)| i) + } + + fn lookup_tx_buffer_by_descriptor(&self, desc: &Descriptor) -> Option { + self.tx_buffers + .iter() + .enumerate() + .find(|(_i, entry)| match &entry.state { + TxBufferState::Sent { range } => { + &descriptor_of(self.dma_region_paddr, range.clone()) == desc + } + _ => false, + }) + .map(|(i, _entry)| i) + } + + fn rx_buffer_entry_mut(&mut self, index: RxBufferIndex) -> &mut RxBufferEntry { + &mut self.rx_buffers[index] + } + + fn tx_buffer_entry_mut(&mut self, index: TxBufferIndex) -> &mut TxBufferEntry { + &mut self.tx_buffers[index] + } + + pub(crate) fn receive(&mut self) -> Result<(RxBufferIndex, TxBufferIndex), ()> { + if let (Some(rx), Some(tx)) = (self.can_claim_rx_buffer(), self.can_claim_tx_buffer()) { + self.claim_rx_buffer(rx); + self.claim_tx_buffer(tx); + Ok((rx, tx)) + } else { + Err(()) + } + } + + pub(crate) fn transmit(&mut self) -> Result { + self.can_claim_tx_buffer() + .map(|index| { + self.claim_tx_buffer(index); + index + }) + .ok_or(()) + } + + fn can_claim_rx_buffer(&self) -> Option { + self.rx_buffers + .iter() + .enumerate() + .find(|(_i, entry)| matches!(entry.state, RxBufferState::Used { .. })) + .map(|(i, _entry)| i) + } + + fn claim_rx_buffer(&mut self, index: RxBufferIndex) { + let entry = self.rx_buffer_entry_mut(index); + let len = match entry.state { + RxBufferState::Used { len } => len, + _ => panic!(), + }; + entry.state = RxBufferState::Claimed { len }; + } + + fn can_claim_tx_buffer(&self) -> Option { + self.tx_buffers + .iter() + .enumerate() + .find(|(_i, entry)| entry.state == TxBufferState::Unclaimed) + .map(|(i, _entry)| i) + } + + fn claim_tx_buffer(&mut self, index: TxBufferIndex) { + let entry = self.tx_buffer_entry_mut(index); + assert_eq!(entry.state, TxBufferState::Unclaimed); + entry.state = TxBufferState::SlotClaimed; + } + + pub(crate) fn consume_rx_start(&mut self, index: RxBufferIndex) -> *mut [u8] { + let entry = self.rx_buffer_entry_mut(index); + let range = entry.range.clone(); + let len = match entry.state { + RxBufferState::Claimed { len } => len, + _ => panic!(), + }; + unsafe { + self.dma_region + .as_mut_ptr() + .index(range.start..range.start + len) + .as_raw_ptr() + .as_mut() + } + } + + pub(crate) fn drop_rx(&mut self, index: RxBufferIndex) { + let entry = self.rx_buffer_entry_mut(index); + let state = entry.state.clone(); + match state { + RxBufferState::Used { .. } => {} + RxBufferState::Claimed { .. } => { + entry.state = RxBufferState::Free; + let range = entry.range.clone(); + let desc = descriptor_of(self.dma_region_paddr, range); + self.rx_ring_buffers.free_mut().enqueue(desc).unwrap(); + self.rx_ring_buffers.notify().unwrap(); + } + _ => { + panic!() + } + } + } + + pub(crate) fn consume_tx(&mut self, index: TxBufferIndex, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let range = self + .bounce_buffer_allocator + .allocate(Layout::from_size_align(len, 1).unwrap()) + .unwrap(); + let entry = self.tx_buffer_entry_mut(index); + assert_eq!(entry.state, TxBufferState::SlotClaimed); + entry.state = TxBufferState::Sent { + range: range.clone(), + }; + let r = f(unsafe { + self.dma_region + .as_mut_ptr() + .index(range.clone()) + .as_raw_ptr() + .as_mut() + }); + let desc = descriptor_of(self.dma_region_paddr, range); + self.tx_ring_buffers.free_mut().enqueue(desc).unwrap(); + self.tx_ring_buffers.notify().unwrap(); + r + } + + pub(crate) fn drop_tx(&mut self, index: TxBufferIndex) { + let entry = self.tx_buffer_entry_mut(index); + let state = entry.state.clone(); + match state { + TxBufferState::SlotClaimed => { + entry.state = TxBufferState::Unclaimed; + } + TxBufferState::Sent { .. } => {} + _ => { + panic!() + } + } + } +} + +fn descriptor_of(dma_region_paddr: usize, range: Range) -> Descriptor { + Descriptor::new( + dma_region_paddr + range.start, + range.len().try_into().unwrap(), + 0, + ) +} diff --git a/crates/sel4-hal-adapters/src/smoltcp/phy/device/mod.rs b/crates/sel4-hal-adapters/src/smoltcp/phy/device/mod.rs new file mode 100644 index 000000000..c413012bb --- /dev/null +++ b/crates/sel4-hal-adapters/src/smoltcp/phy/device/mod.rs @@ -0,0 +1,141 @@ +//! Client-side phy::Device impl + +extern crate alloc; + +use alloc::rc::Rc; +use core::cell::RefCell; + +use smoltcp::phy::{self, Device, DeviceCapabilities}; +use smoltcp::time::Instant; + +use sel4_externally_shared::ExternallySharedRef; +use sel4_shared_ring_buffer::RingBuffers; + +mod inner; +use inner::{Inner, RxBufferIndex, TxBufferIndex}; + +pub struct DeviceImpl { + shared_inner: SharedInner, +} + +type SharedInner = Rc>; + +impl DeviceImpl { + pub fn new( + dma_region: ExternallySharedRef<'static, [u8]>, + dma_region_paddr: usize, + rx_ring_buffers: RingBuffers<'static, fn() -> Result<(), !>>, + tx_ring_buffers: RingBuffers<'static, fn() -> Result<(), !>>, + num_rx_buffers: usize, + rx_buffer_size: usize, + mtu: usize, + ) -> Self { + Self { + shared_inner: Rc::new(RefCell::new(Inner::new( + dma_region, + dma_region_paddr, + rx_ring_buffers, + tx_ring_buffers, + num_rx_buffers, + rx_buffer_size, + mtu, + ))), + } + } + + fn shared_inner(&self) -> &SharedInner { + &self.shared_inner + } + + pub fn poll(&self) -> bool { + self.shared_inner().borrow_mut().poll() + } + + fn new_rx_token(&self, rx_buffer: RxBufferIndex) -> RxToken { + RxToken { + buffer: rx_buffer, + shared_inner: self.shared_inner().clone(), + } + } + + fn new_tx_token(&self, tx_buffer: TxBufferIndex) -> TxToken { + TxToken { + buffer: tx_buffer, + shared_inner: self.shared_inner().clone(), + } + } +} + +impl Device for DeviceImpl { + type RxToken<'a> = RxToken; + type TxToken<'a> = TxToken; + + fn capabilities(&self) -> DeviceCapabilities { + let mut cap = DeviceCapabilities::default(); + cap.max_transmission_unit = self.shared_inner().borrow().mtu(); + cap + } + + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + let r = self.shared_inner().borrow_mut().receive(); + r.ok() + .map(|(rx_ix, tx_ix)| (self.new_rx_token(rx_ix), self.new_tx_token(tx_ix))) + } + + fn transmit(&mut self, _timestamp: Instant) -> Option> { + self.shared_inner() + .borrow_mut() + .transmit() + .ok() + .map(|ix| self.new_tx_token(ix)) + } +} + +pub struct RxToken { + buffer: RxBufferIndex, + shared_inner: SharedInner, +} + +impl phy::RxToken for RxToken { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // let r = self.shared_inner.borrow_mut().consume_rx(self.buffer, f); + let ptr = self.shared_inner.borrow_mut().consume_rx_start(self.buffer); + let r = f(unsafe { ptr.as_mut().unwrap() }); + drop(self); + r + } +} + +impl Drop for RxToken { + fn drop(&mut self) { + self.shared_inner.borrow_mut().drop_rx(self.buffer) + } +} + +pub struct TxToken { + buffer: TxBufferIndex, + shared_inner: SharedInner, +} + +impl phy::TxToken for TxToken { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let r = self + .shared_inner + .borrow_mut() + .consume_tx(self.buffer, len, f); + drop(self); + r + } +} + +impl Drop for TxToken { + fn drop(&mut self) { + self.shared_inner.borrow_mut().drop_tx(self.buffer) + } +} diff --git a/crates/sel4-hal-adapters/src/smoltcp/phy/mod.rs b/crates/sel4-hal-adapters/src/smoltcp/phy/mod.rs index e84874656..9789e45c8 100644 --- a/crates/sel4-hal-adapters/src/smoltcp/phy/mod.rs +++ b/crates/sel4-hal-adapters/src/smoltcp/phy/mod.rs @@ -1,2 +1,5 @@ mod handler; pub use handler::*; + +mod device; +pub use device::*;