Skip to main content

rd_net/
lib.rs

1#![no_std]
2
3extern crate alloc;
4
5use alloc::{boxed::Box, collections::BTreeMap, sync::Arc};
6use core::{alloc::Layout, cell::UnsafeCell};
7
8use dma_api::{ContiguousBuffer, ContiguousBufferPool, DeviceDma, DmaDirection, DmaOp};
9use futures::task::AtomicWaker;
10pub use rdif_eth::*;
11
12fn other_error(msg: &'static str) -> NetError {
13    NetError::Other(Box::new(KError::Unknown(msg)))
14}
15
16struct QueueWakerMap(UnsafeCell<BTreeMap<usize, Arc<AtomicWaker>>>);
17
18impl QueueWakerMap {
19    fn new() -> Self {
20        Self(UnsafeCell::new(BTreeMap::new()))
21    }
22
23    fn register(&self, queue_id: usize) -> Arc<AtomicWaker> {
24        let waker = Arc::new(AtomicWaker::new());
25        unsafe { &mut *self.0.get() }.insert(queue_id, waker.clone());
26        waker
27    }
28
29    fn wake(&self, queue_id: usize) {
30        if let Some(waker) = unsafe { &*self.0.get() }.get(&queue_id) {
31            waker.wake();
32        }
33    }
34}
35
36struct NetInner {
37    interface: UnsafeCell<Box<dyn Interface>>,
38    dma_op: &'static dyn DmaOp,
39    tx_wakers: QueueWakerMap,
40    rx_wakers: QueueWakerMap,
41}
42
43unsafe impl Send for NetInner {}
44unsafe impl Sync for NetInner {}
45
46struct IrqGuard<'a> {
47    enabled: bool,
48    inner: &'a Net,
49}
50
51impl Drop for IrqGuard<'_> {
52    fn drop(&mut self) {
53        if self.enabled {
54            self.inner.interface().enable_irq();
55        }
56    }
57}
58
59pub struct Net {
60    inner: Arc<NetInner>,
61}
62
63impl DriverGeneric for Net {
64    fn name(&self) -> &str {
65        self.interface().name()
66    }
67
68    fn raw_any(&self) -> Option<&dyn core::any::Any> {
69        Some(self)
70    }
71
72    fn raw_any_mut(&mut self) -> Option<&mut dyn core::any::Any> {
73        Some(self)
74    }
75}
76
77impl Net {
78    pub fn new(interface: impl Interface, dma_op: &'static dyn DmaOp) -> Self {
79        Self {
80            inner: Arc::new(NetInner {
81                interface: UnsafeCell::new(Box::new(interface)),
82                dma_op,
83                tx_wakers: QueueWakerMap::new(),
84                rx_wakers: QueueWakerMap::new(),
85            }),
86        }
87    }
88
89    #[allow(clippy::mut_from_ref)]
90    fn interface(&self) -> &mut dyn Interface {
91        unsafe { &mut **self.inner.interface.get() }
92    }
93
94    fn irq_guard(&self) -> IrqGuard<'_> {
95        let enabled = self.interface().is_irq_enabled();
96        if enabled {
97            self.interface().disable_irq();
98        }
99        IrqGuard {
100            enabled,
101            inner: self,
102        }
103    }
104
105    pub fn mac_address(&self) -> [u8; 6] {
106        self.interface().mac_address()
107    }
108
109    /// Access the device's optional wireless control plane.
110    ///
111    /// Returns `None` for a plain wired NIC. Forwards to
112    /// [`Interface::wifi_control`] so the upper layers can drive a wireless
113    /// device (STA/SoftAP control, link policy, RX wake) through the same net
114    /// device handle as any other NIC.
115    #[allow(clippy::mut_from_ref)]
116    pub fn wifi_control(&self) -> Option<&mut dyn WifiControl> {
117        self.interface().wifi_control()
118    }
119
120    pub fn enable_irq(&mut self) {
121        self.interface().enable_irq();
122    }
123
124    pub fn disable_irq(&mut self) {
125        self.interface().disable_irq();
126    }
127
128    pub fn is_irq_enabled(&self) -> bool {
129        self.interface().is_irq_enabled()
130    }
131
132    pub fn create_tx_queue(&mut self) -> Result<TxQueue, NetError> {
133        let irq_guard = self.irq_guard();
134        let queue = self
135            .interface()
136            .create_tx_queue()
137            .ok_or_else(|| other_error("failed to create tx queue"))?;
138        let config = queue.config();
139        let pool = make_pool(self.inner.dma_op, config, DmaDirection::ToDevice)?;
140        let waker = self.inner.tx_wakers.register(queue.id());
141        drop(irq_guard);
142
143        Ok(TxQueue {
144            interface: queue,
145            pool,
146            inflight: BTreeMap::new(),
147            config,
148            _waker: waker,
149        })
150    }
151
152    pub fn create_rx_queue(&mut self) -> Result<RxQueue, NetError> {
153        let irq_guard = self.irq_guard();
154        let queue = self
155            .interface()
156            .create_rx_queue()
157            .ok_or_else(|| other_error("failed to create rx queue"))?;
158        let config = queue.config();
159        let pool = make_pool(self.inner.dma_op, config, DmaDirection::FromDevice)?;
160        let waker = self.inner.rx_wakers.register(queue.id());
161        drop(irq_guard);
162
163        let mut rx = RxQueue {
164            interface: queue,
165            pool,
166            inflight: BTreeMap::new(),
167            config,
168            _waker: waker,
169        };
170        rx.prefill()?;
171        Ok(rx)
172    }
173
174    pub fn irq_handler(&self) -> IrqHandler {
175        IrqHandler {
176            inner: self.inner.clone(),
177        }
178    }
179
180    /// Detaches a standalone control-plane handle for this device.
181    ///
182    /// Returns `None` for a plain wired NIC. The handle clones the same
183    /// `Arc<NetInner>` the data plane uses (like [`Net::irq_handler`]), so the
184    /// control plane (STA/SoftAP switch, link policy) stays reachable *after*
185    /// `Net` is consumed into a driver. Used to drive runtime Wi-Fi mode
186    /// switching from a separate task/syscall context.
187    pub fn wifi_control_handle(&self) -> Option<WifiControlHandle> {
188        self.wifi_control().is_some().then(|| WifiControlHandle {
189            inner: self.inner.clone(),
190        })
191    }
192}
193
194/// Standalone handle to a device's wireless control plane.
195///
196/// Holds a clone of the device's `Arc<NetInner>`, so it keeps working after the
197/// originating [`Net`] has been consumed into a data-plane driver. See
198/// [`Net::wifi_control_handle`].
199pub struct WifiControlHandle {
200    inner: Arc<NetInner>,
201}
202
203impl Clone for WifiControlHandle {
204    fn clone(&self) -> Self {
205        Self {
206            inner: self.inner.clone(),
207        }
208    }
209}
210
211unsafe impl Send for WifiControlHandle {}
212unsafe impl Sync for WifiControlHandle {}
213
214impl WifiControlHandle {
215    /// Access the wireless control plane.
216    ///
217    /// # Safety / concurrency
218    ///
219    /// This aliases the same `Interface` the data plane drives. The caller must
220    /// not invoke control operations concurrently with the device's RX/TX or
221    /// poll path on the same interface. In practice mode switching is issued
222    /// from a syscall/task context that is serialized against the stack's poll
223    /// task, never from inside an RX callback.
224    #[allow(clippy::mut_from_ref)]
225    pub fn wifi_control(&self) -> Option<&mut dyn WifiControl> {
226        let iface = unsafe { &mut **self.inner.interface.get() };
227        iface.wifi_control()
228    }
229
230    /// The device's current MAC address (may change across a mode switch as the
231    /// firmware re-creates its VIF).
232    pub fn mac_address(&self) -> [u8; 6] {
233        let iface = unsafe { &mut **self.inner.interface.get() };
234        iface.mac_address()
235    }
236}
237
238fn make_pool(
239    dma_op: &'static dyn DmaOp,
240    config: QueueConfig,
241    direction: DmaDirection,
242) -> Result<ContiguousBufferPool, NetError> {
243    let layout = Layout::from_size_align(config.buf_size, config.align.max(1))
244        .map_err(|_| other_error("invalid queue layout"))?;
245    let dma = DeviceDma::new(config.dma_mask, dma_op);
246    Ok(dma.contiguous_buffer_pool(layout, direction, config.ring_size))
247}
248
249pub struct IrqHandler {
250    inner: Arc<NetInner>,
251}
252
253unsafe impl Sync for IrqHandler {}
254
255impl IrqHandler {
256    pub fn enable(&self) {
257        let iface = unsafe { &mut **self.inner.interface.get() };
258        iface.enable_irq();
259    }
260
261    pub fn disable(&self) {
262        let iface = unsafe { &mut **self.inner.interface.get() };
263        iface.disable_irq();
264    }
265
266    /// Handles a device interrupt and returns queue events without waking task
267    /// wakers.
268    ///
269    /// This is the IRQ top-half entry: it only asks the portable driver to
270    /// identify/acknowledge the interrupt source and publish queue event bits.
271    /// Runtime queue wakers must be invoked later from task/deferred context
272    /// through [`handle`](Self::handle).
273    pub fn handle_irq(&self) -> rdif_eth::Event {
274        let iface = unsafe { &mut **self.inner.interface.get() };
275        iface.handle_irq()
276    }
277
278    /// Handles a device interrupt and wakes registered queue waiters.
279    ///
280    /// Use this only from task/deferred context. Hard IRQ callbacks should call
281    /// [`handle_irq`](Self::handle_irq) and defer waker execution.
282    pub fn handle(&self) {
283        let event = self.handle_irq();
284        for id in event.tx_queue.iter() {
285            self.inner.tx_wakers.wake(id);
286        }
287        for id in event.rx_queue.iter() {
288            self.inner.rx_wakers.wake(id);
289        }
290    }
291
292    pub fn enable_irq(&self) {
293        let iface = unsafe { &mut **self.inner.interface.get() };
294        iface.enable_irq();
295    }
296
297    pub fn disable_irq(&self) {
298        let iface = unsafe { &mut **self.inner.interface.get() };
299        iface.disable_irq();
300    }
301
302    pub fn is_irq_enabled(&self) -> bool {
303        let iface = unsafe { &mut **self.inner.interface.get() };
304        iface.is_irq_enabled()
305    }
306}
307
308pub struct TxQueue {
309    interface: Box<dyn ITxQueue>,
310    pool: ContiguousBufferPool,
311    inflight: BTreeMap<u64, ContiguousBuffer>,
312    config: QueueConfig,
313    _waker: Arc<AtomicWaker>,
314}
315
316impl TxQueue {
317    fn capacity(&self) -> usize {
318        self.config.ring_size.saturating_sub(1)
319    }
320
321    fn reclaim_bounded(&mut self, limit: usize) -> Result<usize, NetError> {
322        let mut reclaimed = 0;
323        while reclaimed < limit {
324            let Some(bus_addr) = self.interface.reclaim() else {
325                break;
326            };
327            let Some(buff) = self.inflight.remove(&bus_addr) else {
328                return Err(other_error("reclaimed unknown tx buffer"));
329            };
330            drop(buff);
331            reclaimed += 1;
332        }
333        Ok(reclaimed)
334    }
335
336    pub fn id(&self) -> usize {
337        self.interface.id()
338    }
339
340    pub fn buf_size(&self) -> usize {
341        self.config.buf_size
342    }
343
344    pub fn prepare_send<R>(
345        &mut self,
346        len: usize,
347        f: impl FnOnce(&mut [u8]) -> R,
348    ) -> Result<(R, TxPending<'_>), NetError> {
349        if len > self.config.buf_size {
350            return Err(other_error("tx packet too large"));
351        }
352
353        self.reclaim_bounded(self.capacity().max(1))?;
354
355        let mut buff = self.pool.alloc()?;
356        let bus_addr = buff.dma_addr().as_u64();
357        let ret = buff.write_with_cpu(len, f);
358        Ok((
359            ret,
360            TxPending {
361                queue: self,
362                len,
363                bus_addr,
364                buff: Some(buff),
365            },
366        ))
367    }
368}
369
370pub struct TxPending<'a> {
371    queue: &'a mut TxQueue,
372    len: usize,
373    bus_addr: u64,
374    buff: Option<ContiguousBuffer>,
375}
376
377impl TxPending<'_> {
378    pub fn bus_addr(&self) -> u64 {
379        self.bus_addr
380    }
381
382    pub fn len(&self) -> usize {
383        self.len
384    }
385
386    pub fn is_empty(&self) -> bool {
387        self.len == 0
388    }
389
390    pub fn try_submit(&mut self) -> Result<(), NetError> {
391        self.queue.reclaim_bounded(self.queue.capacity().max(1))?;
392        let buff = self
393            .buff
394            .as_ref()
395            .expect("tx pending buffer should exist until submit succeeds");
396        buff.prepare_for_device(0, self.len);
397        self.queue.interface.submit(DmaBuffer {
398            virt: buff.as_ptr(),
399            bus_addr: self.bus_addr,
400            len: self.len,
401        })?;
402        let buff = self
403            .buff
404            .take()
405            .expect("tx pending buffer should exist until submit succeeds");
406        self.queue.inflight.insert(self.bus_addr, buff);
407        Ok(())
408    }
409}
410
411pub struct RxQueue {
412    interface: Box<dyn IRxQueue>,
413    pool: ContiguousBufferPool,
414    inflight: BTreeMap<u64, ContiguousBuffer>,
415    config: QueueConfig,
416    _waker: Arc<AtomicWaker>,
417}
418
419impl RxQueue {
420    fn capacity(&self) -> usize {
421        self.config.ring_size.saturating_sub(1)
422    }
423
424    fn prefill(&mut self) -> Result<(), NetError> {
425        while self.inflight.len() < self.capacity() {
426            let buff = self.pool.alloc()?;
427            if let Err(err) = self.submit_buffer(buff) {
428                if matches!(err, NetError::Retry) {
429                    break;
430                }
431                return Err(err);
432            }
433        }
434        Ok(())
435    }
436
437    fn submit_buffer(&mut self, buff: ContiguousBuffer) -> Result<(), NetError> {
438        let bus_addr = buff.dma_addr().as_u64();
439        let len = self.config.buf_size.min(buff.len());
440        buff.prepare_for_device(0, len);
441        self.interface.submit(DmaBuffer {
442            virt: buff.as_ptr(),
443            bus_addr,
444            len,
445        })?;
446        self.inflight.insert(bus_addr, buff);
447        Ok(())
448    }
449
450    fn reclaim_packet(&mut self) -> Result<Option<(ContiguousBuffer, usize)>, NetError> {
451        let Some((bus_addr, len)) = self.interface.reclaim() else {
452            return Ok(None);
453        };
454        let Some(buff) = self.inflight.remove(&bus_addr) else {
455            return Err(other_error("reclaimed unknown rx buffer"));
456        };
457        let packet_len = len.min(self.config.buf_size).min(buff.len());
458        buff.complete_for_cpu(0, packet_len);
459        Ok(Some((buff, packet_len)))
460    }
461
462    pub fn id(&self) -> usize {
463        self.interface.id()
464    }
465
466    pub fn buf_size(&self) -> usize {
467        self.config.buf_size
468    }
469
470    pub fn try_receive(&mut self) -> Option<RxPacket<'_>> {
471        match self.reclaim_packet() {
472            Ok(Some((buff, len))) => Some(RxPacket {
473                queue: self,
474                len,
475                buff: Some(buff),
476            }),
477            Ok(None) | Err(_) => None,
478        }
479    }
480
481    pub fn receive<R>(&mut self, f: impl FnOnce(&[u8]) -> R) -> Option<R> {
482        let packet = self.try_receive()?;
483        Some(packet.consume(f))
484    }
485}
486
487pub struct RxPacket<'a> {
488    queue: &'a mut RxQueue,
489    len: usize,
490    buff: Option<ContiguousBuffer>,
491}
492
493impl RxPacket<'_> {
494    pub fn len(&self) -> usize {
495        self.len
496    }
497
498    pub fn is_empty(&self) -> bool {
499        self.len == 0
500    }
501
502    pub fn consume<R>(mut self, f: impl FnOnce(&[u8]) -> R) -> R {
503        let buff = self.buff.as_ref().expect("rx packet buffer should exist");
504        let ret = buff.read_with_cpu(self.len, f);
505        if let Some(buff) = self.buff.take() {
506            let _ = self.queue.submit_buffer(buff);
507        }
508        ret
509    }
510}
511
512impl Drop for RxPacket<'_> {
513    fn drop(&mut self) {
514        if let Some(buff) = self.buff.take() {
515            let _ = self.queue.submit_buffer(buff);
516        }
517    }
518}
519
520#[cfg(test)]
521mod tests {
522    use alloc::{boxed::Box, sync::Arc};
523    use core::{
524        any::Any,
525        num::NonZeroUsize,
526        ptr::NonNull,
527        sync::atomic::{AtomicUsize, Ordering},
528        task::{RawWaker, RawWakerVTable, Waker},
529    };
530
531    use dma_api::{DmaAllocHandle, DmaConstraints, DmaDirection, DmaError, DmaMapHandle};
532    use rdif_eth::{DriverGeneric, Event, IRxQueue, ITxQueue, IdList, Interface};
533
534    use super::*;
535
536    struct TestDma;
537
538    impl dma_api::DmaOp for TestDma {
539        fn page_size(&self) -> usize {
540            4096
541        }
542
543        unsafe fn alloc_contiguous(
544            &self,
545            _constraints: DmaConstraints,
546            _layout: core::alloc::Layout,
547        ) -> Option<DmaAllocHandle> {
548            panic!("test should not allocate contiguous DMA")
549        }
550
551        unsafe fn dealloc_contiguous(&self, _handle: DmaAllocHandle) {
552            panic!("test should not deallocate contiguous DMA")
553        }
554
555        unsafe fn alloc_coherent(
556            &self,
557            _constraints: DmaConstraints,
558            _layout: core::alloc::Layout,
559        ) -> Option<DmaAllocHandle> {
560            panic!("test should not allocate coherent DMA")
561        }
562
563        unsafe fn dealloc_coherent(&self, _handle: DmaAllocHandle) {
564            panic!("test should not deallocate coherent DMA")
565        }
566
567        unsafe fn map_streaming(
568            &self,
569            _constraints: DmaConstraints,
570            _addr: NonNull<u8>,
571            _size: NonZeroUsize,
572            _direction: DmaDirection,
573        ) -> Result<DmaMapHandle, DmaError> {
574            panic!("test should not map streaming DMA")
575        }
576
577        unsafe fn unmap_streaming(&self, _handle: DmaMapHandle) {
578            panic!("test should not unmap streaming DMA")
579        }
580    }
581
582    struct TestInterface {
583        irq_events: Event,
584        handle_calls: Arc<AtomicUsize>,
585    }
586
587    impl DriverGeneric for TestInterface {
588        fn name(&self) -> &str {
589            "test-net"
590        }
591
592        fn raw_any(&self) -> Option<&dyn Any> {
593            Some(self)
594        }
595
596        fn raw_any_mut(&mut self) -> Option<&mut dyn Any> {
597            Some(self)
598        }
599    }
600
601    impl Interface for TestInterface {
602        fn mac_address(&self) -> [u8; 6] {
603            [0x02, 0, 0, 0, 0, 1]
604        }
605
606        fn create_tx_queue(&mut self) -> Option<Box<dyn ITxQueue>> {
607            panic!("test should not create TX queue")
608        }
609
610        fn create_rx_queue(&mut self) -> Option<Box<dyn IRxQueue>> {
611            panic!("test should not create RX queue")
612        }
613
614        fn enable_irq(&mut self) {}
615
616        fn disable_irq(&mut self) {}
617
618        fn is_irq_enabled(&self) -> bool {
619            false
620        }
621
622        fn handle_irq(&mut self) -> Event {
623            self.handle_calls.fetch_add(1, Ordering::AcqRel);
624            self.irq_events
625        }
626    }
627
628    fn count_waker(counter: Arc<AtomicUsize>) -> Waker {
629        unsafe fn clone(data: *const ()) -> RawWaker {
630            let counter = unsafe { Arc::<AtomicUsize>::from_raw(data.cast()) };
631            let cloned = Arc::clone(&counter);
632            let _ = Arc::into_raw(counter);
633            RawWaker::new(Arc::into_raw(cloned).cast(), &VTABLE)
634        }
635
636        unsafe fn wake(data: *const ()) {
637            let counter = unsafe { Arc::<AtomicUsize>::from_raw(data.cast()) };
638            counter.fetch_add(1, Ordering::AcqRel);
639        }
640
641        unsafe fn wake_by_ref(data: *const ()) {
642            let counter = unsafe { Arc::<AtomicUsize>::from_raw(data.cast()) };
643            counter.fetch_add(1, Ordering::AcqRel);
644            let _ = Arc::into_raw(counter);
645        }
646
647        unsafe fn drop(data: *const ()) {
648            let _ = unsafe { Arc::<AtomicUsize>::from_raw(data.cast()) };
649        }
650
651        static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
652        let raw = RawWaker::new(Arc::into_raw(counter).cast(), &VTABLE);
653        unsafe { Waker::from_raw(raw) }
654    }
655
656    #[test]
657    fn irq_handler_fast_path_returns_events_without_waking_registered_wakers() {
658        static DMA: TestDma = TestDma;
659        let mut rx = IdList::none();
660        rx.insert(3);
661        let mut tx = IdList::none();
662        tx.insert(5);
663        let handle_calls = Arc::new(AtomicUsize::new(0));
664        let net = Net::new(
665            TestInterface {
666                irq_events: Event {
667                    tx_queue: tx,
668                    rx_queue: rx,
669                },
670                handle_calls: Arc::clone(&handle_calls),
671            },
672            &DMA,
673        );
674        let rx_wake_count = Arc::new(AtomicUsize::new(0));
675        let tx_wake_count = Arc::new(AtomicUsize::new(0));
676        net.inner
677            .rx_wakers
678            .register(3)
679            .register(&count_waker(Arc::clone(&rx_wake_count)));
680        net.inner
681            .tx_wakers
682            .register(5)
683            .register(&count_waker(Arc::clone(&tx_wake_count)));
684
685        let irq = net.irq_handler();
686        let events = irq.handle_irq();
687
688        assert!(events.rx_queue.contains(3));
689        assert!(events.tx_queue.contains(5));
690        assert_eq!(handle_calls.load(Ordering::Acquire), 1);
691        assert_eq!(rx_wake_count.load(Ordering::Acquire), 0);
692        assert_eq!(tx_wake_count.load(Ordering::Acquire), 0);
693    }
694}