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
203unsafe impl Send for WifiControlHandle {}
204unsafe impl Sync for WifiControlHandle {}
205
206impl WifiControlHandle {
207    /// Access the wireless control plane.
208    ///
209    /// # Safety / concurrency
210    ///
211    /// This aliases the same `Interface` the data plane drives. The caller must
212    /// not invoke control operations concurrently with the device's RX/TX or
213    /// poll path on the same interface. In practice mode switching is issued
214    /// from a syscall/task context that is serialized against the stack's poll
215    /// task, never from inside an RX callback.
216    #[allow(clippy::mut_from_ref)]
217    pub fn wifi_control(&self) -> Option<&mut dyn WifiControl> {
218        let iface = unsafe { &mut **self.inner.interface.get() };
219        iface.wifi_control()
220    }
221
222    /// The device's current MAC address (may change across a mode switch as the
223    /// firmware re-creates its VIF).
224    pub fn mac_address(&self) -> [u8; 6] {
225        let iface = unsafe { &mut **self.inner.interface.get() };
226        iface.mac_address()
227    }
228}
229
230fn make_pool(
231    dma_op: &'static dyn DmaOp,
232    config: QueueConfig,
233    direction: DmaDirection,
234) -> Result<ContiguousBufferPool, NetError> {
235    let layout = Layout::from_size_align(config.buf_size, config.align.max(1))
236        .map_err(|_| other_error("invalid queue layout"))?;
237    let dma = DeviceDma::new(config.dma_mask, dma_op);
238    Ok(dma.contiguous_buffer_pool(layout, direction, config.ring_size))
239}
240
241pub struct IrqHandler {
242    inner: Arc<NetInner>,
243}
244
245unsafe impl Sync for IrqHandler {}
246
247impl IrqHandler {
248    pub fn enable(&self) {
249        let iface = unsafe { &mut **self.inner.interface.get() };
250        iface.enable_irq();
251    }
252
253    pub fn disable(&self) {
254        let iface = unsafe { &mut **self.inner.interface.get() };
255        iface.disable_irq();
256    }
257
258    /// Handles a device interrupt and returns queue events without waking task
259    /// wakers.
260    ///
261    /// This is the IRQ top-half entry: it only asks the portable driver to
262    /// identify/acknowledge the interrupt source and publish queue event bits.
263    /// Runtime queue wakers must be invoked later from task/deferred context
264    /// through [`handle`](Self::handle).
265    pub fn handle_irq(&self) -> rdif_eth::Event {
266        let iface = unsafe { &mut **self.inner.interface.get() };
267        iface.handle_irq()
268    }
269
270    /// Handles a device interrupt and wakes registered queue waiters.
271    ///
272    /// Use this only from task/deferred context. Hard IRQ callbacks should call
273    /// [`handle_irq`](Self::handle_irq) and defer waker execution.
274    pub fn handle(&self) {
275        let event = self.handle_irq();
276        for id in event.tx_queue.iter() {
277            self.inner.tx_wakers.wake(id);
278        }
279        for id in event.rx_queue.iter() {
280            self.inner.rx_wakers.wake(id);
281        }
282    }
283
284    pub fn enable_irq(&self) {
285        let iface = unsafe { &mut **self.inner.interface.get() };
286        iface.enable_irq();
287    }
288
289    pub fn disable_irq(&self) {
290        let iface = unsafe { &mut **self.inner.interface.get() };
291        iface.disable_irq();
292    }
293
294    pub fn is_irq_enabled(&self) -> bool {
295        let iface = unsafe { &mut **self.inner.interface.get() };
296        iface.is_irq_enabled()
297    }
298}
299
300pub struct TxQueue {
301    interface: Box<dyn ITxQueue>,
302    pool: ContiguousBufferPool,
303    inflight: BTreeMap<u64, ContiguousBuffer>,
304    config: QueueConfig,
305    _waker: Arc<AtomicWaker>,
306}
307
308impl TxQueue {
309    fn capacity(&self) -> usize {
310        self.config.ring_size.saturating_sub(1)
311    }
312
313    fn reclaim_bounded(&mut self, limit: usize) -> Result<usize, NetError> {
314        let mut reclaimed = 0;
315        while reclaimed < limit {
316            let Some(bus_addr) = self.interface.reclaim() else {
317                break;
318            };
319            let Some(buff) = self.inflight.remove(&bus_addr) else {
320                return Err(other_error("reclaimed unknown tx buffer"));
321            };
322            drop(buff);
323            reclaimed += 1;
324        }
325        Ok(reclaimed)
326    }
327
328    pub fn id(&self) -> usize {
329        self.interface.id()
330    }
331
332    pub fn buf_size(&self) -> usize {
333        self.config.buf_size
334    }
335
336    pub fn prepare_send<R>(
337        &mut self,
338        len: usize,
339        f: impl FnOnce(&mut [u8]) -> R,
340    ) -> Result<(R, TxPending<'_>), NetError> {
341        if len > self.config.buf_size {
342            return Err(other_error("tx packet too large"));
343        }
344
345        self.reclaim_bounded(self.capacity().max(1))?;
346
347        let mut buff = self.pool.alloc()?;
348        let bus_addr = buff.dma_addr().as_u64();
349        let ret = buff.write_with_cpu(len, f);
350        Ok((
351            ret,
352            TxPending {
353                queue: self,
354                len,
355                bus_addr,
356                buff: Some(buff),
357            },
358        ))
359    }
360}
361
362pub struct TxPending<'a> {
363    queue: &'a mut TxQueue,
364    len: usize,
365    bus_addr: u64,
366    buff: Option<ContiguousBuffer>,
367}
368
369impl TxPending<'_> {
370    pub fn bus_addr(&self) -> u64 {
371        self.bus_addr
372    }
373
374    pub fn len(&self) -> usize {
375        self.len
376    }
377
378    pub fn is_empty(&self) -> bool {
379        self.len == 0
380    }
381
382    pub fn try_submit(&mut self) -> Result<(), NetError> {
383        self.queue.reclaim_bounded(self.queue.capacity().max(1))?;
384        let buff = self
385            .buff
386            .as_ref()
387            .expect("tx pending buffer should exist until submit succeeds");
388        buff.prepare_for_device(0, self.len);
389        self.queue.interface.submit(DmaBuffer {
390            virt: buff.as_ptr(),
391            bus_addr: self.bus_addr,
392            len: self.len,
393        })?;
394        let buff = self
395            .buff
396            .take()
397            .expect("tx pending buffer should exist until submit succeeds");
398        self.queue.inflight.insert(self.bus_addr, buff);
399        Ok(())
400    }
401}
402
403pub struct RxQueue {
404    interface: Box<dyn IRxQueue>,
405    pool: ContiguousBufferPool,
406    inflight: BTreeMap<u64, ContiguousBuffer>,
407    config: QueueConfig,
408    _waker: Arc<AtomicWaker>,
409}
410
411impl RxQueue {
412    fn capacity(&self) -> usize {
413        self.config.ring_size.saturating_sub(1)
414    }
415
416    fn prefill(&mut self) -> Result<(), NetError> {
417        while self.inflight.len() < self.capacity() {
418            let buff = self.pool.alloc()?;
419            if let Err(err) = self.submit_buffer(buff) {
420                if matches!(err, NetError::Retry) {
421                    break;
422                }
423                return Err(err);
424            }
425        }
426        Ok(())
427    }
428
429    fn submit_buffer(&mut self, buff: ContiguousBuffer) -> Result<(), NetError> {
430        let bus_addr = buff.dma_addr().as_u64();
431        let len = self.config.buf_size.min(buff.len());
432        buff.prepare_for_device(0, len);
433        self.interface.submit(DmaBuffer {
434            virt: buff.as_ptr(),
435            bus_addr,
436            len,
437        })?;
438        self.inflight.insert(bus_addr, buff);
439        Ok(())
440    }
441
442    fn reclaim_packet(&mut self) -> Result<Option<(ContiguousBuffer, usize)>, NetError> {
443        let Some((bus_addr, len)) = self.interface.reclaim() else {
444            return Ok(None);
445        };
446        let Some(buff) = self.inflight.remove(&bus_addr) else {
447            return Err(other_error("reclaimed unknown rx buffer"));
448        };
449        let packet_len = len.min(self.config.buf_size).min(buff.len());
450        buff.complete_for_cpu(0, packet_len);
451        Ok(Some((buff, packet_len)))
452    }
453
454    pub fn id(&self) -> usize {
455        self.interface.id()
456    }
457
458    pub fn buf_size(&self) -> usize {
459        self.config.buf_size
460    }
461
462    pub fn try_receive(&mut self) -> Option<RxPacket<'_>> {
463        match self.reclaim_packet() {
464            Ok(Some((buff, len))) => Some(RxPacket {
465                queue: self,
466                len,
467                buff: Some(buff),
468            }),
469            Ok(None) | Err(_) => None,
470        }
471    }
472
473    pub fn receive<R>(&mut self, f: impl FnOnce(&[u8]) -> R) -> Option<R> {
474        let packet = self.try_receive()?;
475        Some(packet.consume(f))
476    }
477}
478
479pub struct RxPacket<'a> {
480    queue: &'a mut RxQueue,
481    len: usize,
482    buff: Option<ContiguousBuffer>,
483}
484
485impl RxPacket<'_> {
486    pub fn len(&self) -> usize {
487        self.len
488    }
489
490    pub fn is_empty(&self) -> bool {
491        self.len == 0
492    }
493
494    pub fn consume<R>(mut self, f: impl FnOnce(&[u8]) -> R) -> R {
495        let buff = self.buff.as_ref().expect("rx packet buffer should exist");
496        let ret = buff.read_with_cpu(self.len, f);
497        if let Some(buff) = self.buff.take() {
498            let _ = self.queue.submit_buffer(buff);
499        }
500        ret
501    }
502}
503
504impl Drop for RxPacket<'_> {
505    fn drop(&mut self) {
506        if let Some(buff) = self.buff.take() {
507            let _ = self.queue.submit_buffer(buff);
508        }
509    }
510}
511
512#[cfg(test)]
513mod tests {
514    use alloc::{boxed::Box, sync::Arc};
515    use core::{
516        any::Any,
517        num::NonZeroUsize,
518        ptr::NonNull,
519        sync::atomic::{AtomicUsize, Ordering},
520        task::{RawWaker, RawWakerVTable, Waker},
521    };
522
523    use dma_api::{DmaAllocHandle, DmaConstraints, DmaDirection, DmaError, DmaMapHandle};
524    use rdif_eth::{DriverGeneric, Event, IRxQueue, ITxQueue, IdList, Interface};
525
526    use super::*;
527
528    struct TestDma;
529
530    impl dma_api::DmaOp for TestDma {
531        fn page_size(&self) -> usize {
532            4096
533        }
534
535        unsafe fn alloc_contiguous(
536            &self,
537            _constraints: DmaConstraints,
538            _layout: core::alloc::Layout,
539        ) -> Option<DmaAllocHandle> {
540            panic!("test should not allocate contiguous DMA")
541        }
542
543        unsafe fn dealloc_contiguous(&self, _handle: DmaAllocHandle) {
544            panic!("test should not deallocate contiguous DMA")
545        }
546
547        unsafe fn alloc_coherent(
548            &self,
549            _constraints: DmaConstraints,
550            _layout: core::alloc::Layout,
551        ) -> Option<DmaAllocHandle> {
552            panic!("test should not allocate coherent DMA")
553        }
554
555        unsafe fn dealloc_coherent(&self, _handle: DmaAllocHandle) {
556            panic!("test should not deallocate coherent DMA")
557        }
558
559        unsafe fn map_streaming(
560            &self,
561            _constraints: DmaConstraints,
562            _addr: NonNull<u8>,
563            _size: NonZeroUsize,
564            _direction: DmaDirection,
565        ) -> Result<DmaMapHandle, DmaError> {
566            panic!("test should not map streaming DMA")
567        }
568
569        unsafe fn unmap_streaming(&self, _handle: DmaMapHandle) {
570            panic!("test should not unmap streaming DMA")
571        }
572    }
573
574    struct TestInterface {
575        irq_events: Event,
576        handle_calls: Arc<AtomicUsize>,
577    }
578
579    impl DriverGeneric for TestInterface {
580        fn name(&self) -> &str {
581            "test-net"
582        }
583
584        fn raw_any(&self) -> Option<&dyn Any> {
585            Some(self)
586        }
587
588        fn raw_any_mut(&mut self) -> Option<&mut dyn Any> {
589            Some(self)
590        }
591    }
592
593    impl Interface for TestInterface {
594        fn mac_address(&self) -> [u8; 6] {
595            [0x02, 0, 0, 0, 0, 1]
596        }
597
598        fn create_tx_queue(&mut self) -> Option<Box<dyn ITxQueue>> {
599            panic!("test should not create TX queue")
600        }
601
602        fn create_rx_queue(&mut self) -> Option<Box<dyn IRxQueue>> {
603            panic!("test should not create RX queue")
604        }
605
606        fn enable_irq(&mut self) {}
607
608        fn disable_irq(&mut self) {}
609
610        fn is_irq_enabled(&self) -> bool {
611            false
612        }
613
614        fn handle_irq(&mut self) -> Event {
615            self.handle_calls.fetch_add(1, Ordering::AcqRel);
616            self.irq_events
617        }
618    }
619
620    fn count_waker(counter: Arc<AtomicUsize>) -> Waker {
621        unsafe fn clone(data: *const ()) -> RawWaker {
622            let counter = unsafe { Arc::<AtomicUsize>::from_raw(data.cast()) };
623            let cloned = Arc::clone(&counter);
624            let _ = Arc::into_raw(counter);
625            RawWaker::new(Arc::into_raw(cloned).cast(), &VTABLE)
626        }
627
628        unsafe fn wake(data: *const ()) {
629            let counter = unsafe { Arc::<AtomicUsize>::from_raw(data.cast()) };
630            counter.fetch_add(1, Ordering::AcqRel);
631        }
632
633        unsafe fn wake_by_ref(data: *const ()) {
634            let counter = unsafe { Arc::<AtomicUsize>::from_raw(data.cast()) };
635            counter.fetch_add(1, Ordering::AcqRel);
636            let _ = Arc::into_raw(counter);
637        }
638
639        unsafe fn drop(data: *const ()) {
640            let _ = unsafe { Arc::<AtomicUsize>::from_raw(data.cast()) };
641        }
642
643        static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
644        let raw = RawWaker::new(Arc::into_raw(counter).cast(), &VTABLE);
645        unsafe { Waker::from_raw(raw) }
646    }
647
648    #[test]
649    fn irq_handler_fast_path_returns_events_without_waking_registered_wakers() {
650        static DMA: TestDma = TestDma;
651        let mut rx = IdList::none();
652        rx.insert(3);
653        let mut tx = IdList::none();
654        tx.insert(5);
655        let handle_calls = Arc::new(AtomicUsize::new(0));
656        let net = Net::new(
657            TestInterface {
658                irq_events: Event {
659                    tx_queue: tx,
660                    rx_queue: rx,
661                },
662                handle_calls: Arc::clone(&handle_calls),
663            },
664            &DMA,
665        );
666        let rx_wake_count = Arc::new(AtomicUsize::new(0));
667        let tx_wake_count = Arc::new(AtomicUsize::new(0));
668        net.inner
669            .rx_wakers
670            .register(3)
671            .register(&count_waker(Arc::clone(&rx_wake_count)));
672        net.inner
673            .tx_wakers
674            .register(5)
675            .register(&count_waker(Arc::clone(&tx_wake_count)));
676
677        let irq = net.irq_handler();
678        let events = irq.handle_irq();
679
680        assert!(events.rx_queue.contains(3));
681        assert!(events.tx_queue.contains(5));
682        assert_eq!(handle_calls.load(Ordering::Acquire), 1);
683        assert_eq!(rx_wake_count.load(Ordering::Acquire), 0);
684        assert_eq!(tx_wake_count.load(Ordering::Acquire), 0);
685    }
686}