1mod buffer;
8
9use std::fmt::Debug;
10use std::mem::MaybeUninit;
11use std::num::{NonZeroU16, NonZeroU32, NonZeroU64, TryFromIntError};
12use std::ops::Range;
13use std::pin::Pin;
14use std::sync::Arc;
15use std::sync::atomic::{self, AtomicUsize};
16use std::task::Waker;
17
18use explicit::{PollExt as _, ResultExt as _};
19use fidl_fuchsia_hardware_network as netdev;
20use fidl_fuchsia_hardware_network::DelegatedRxLease;
21use fidl_table_validation::ValidFidlTable;
22use fuchsia_async as fasync;
23use fuchsia_sync::Mutex;
24use futures::future::{Future, poll_fn};
25use futures::task::{Context, Poll};
26use futures::{Stream, StreamExt as _, ready};
27
28use crate::error::{Error, Result};
29use buffer::pool::{Pool, RxLeaseWatcher};
30use buffer::{
31 AllocKind, DescId, NETWORK_DEVICE_DESCRIPTOR_LENGTH, NETWORK_DEVICE_DESCRIPTOR_VERSION,
32};
33pub use buffer::{Buffer, ChecksumRxOffloading, Rx, SinglePartTxBuffer, Tx};
34
35const DEFAULT_VMO_ID: u8 = 0;
39
40#[derive(Clone)]
42pub struct Session {
43 inner: Arc<Inner>,
44}
45
46impl Debug for Session {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 let Self { inner } = self;
49 let Inner {
50 name,
51 pool: _,
52 proxy: _,
53 rx: _,
54 tx: _,
55 tx_pending: _,
56 rx_ready: _,
57 tx_ready: _,
58 tx_idle_listeners: _,
59 } = &**inner;
60 f.debug_struct("Session").field("name", &name).finish_non_exhaustive()
61 }
62}
63
64impl Session {
65 pub async fn new(
67 device: &netdev::DeviceProxy,
68 name: &str,
69 config: Config,
70 ) -> Result<(Self, Task)> {
71 let inner = Inner::new(device, name, config).await?;
72 Ok((Session { inner: Arc::clone(&inner) }, Task { inner }))
73 }
74
75 pub fn send(&self, buffer: Buffer<Tx>) {
77 self.inner.send(buffer)
78 }
79
80 pub async fn recv(&self) -> Result<Buffer<Rx>> {
82 self.inner.recv().await
83 }
84
85 pub async fn alloc_tx_buffer(&self, num_bytes: usize) -> Result<Buffer<Tx>> {
89 self.inner.pool.alloc_tx_buffer(num_bytes).await
90 }
91
92 pub fn try_alloc_single_part_tx_buffer(
97 &self,
98 num_bytes: usize,
99 ) -> Result<Option<SinglePartTxBuffer>> {
100 self.inner.pool.try_alloc_single_part_tx_buffer(num_bytes)
101 }
102
103 pub async fn alloc_tx_buffers(
116 &self,
117 num_bytes: usize,
118 ) -> Result<impl Iterator<Item = Result<Buffer<Tx>>> + '_> {
119 self.inner.pool.alloc_tx_buffers(num_bytes).await
120 }
121
122 pub async fn attach(&self, port: Port, rx_frames: &[netdev::FrameType]) -> Result<()> {
124 let fut = self.inner.proxy.attach(&port.into(), rx_frames);
131 let () = fut.await?.map_err(|raw| Error::Attach(port, zx::Status::from_raw(raw)))?;
132 Ok(())
133 }
134
135 pub async fn detach(&self, port: Port) -> Result<()> {
137 let () = self
138 .inner
139 .proxy
140 .detach(&port.into())
141 .await?
142 .map_err(|raw| Error::Detach(port, zx::Status::from_raw(raw)))?;
143 Ok(())
144 }
145
146 pub async fn wait_tx_idle(&self) {
159 self.inner.tx_idle_listeners.wait().await;
160 }
161
162 pub fn watch_rx_leases(&self) -> impl Stream<Item = Result<RxLease>> + Send + Sync + use<> {
177 let inner = Arc::clone(&self.inner);
178 let watcher = RxLeaseWatcher::new(Arc::clone(&inner.pool));
179 futures::stream::try_unfold((inner, watcher), |(inner, mut watcher)| async move {
180 let DelegatedRxLease {
181 hold_until_frame,
182 handle,
183 __source_breaking: fidl::marker::SourceBreaking,
184 } = match inner.proxy.watch_delegated_rx_lease().await {
185 Ok(lease) => lease,
186 Err(e) => {
187 if e.is_closed() {
188 return Ok(None);
189 } else {
190 return Err(Error::Fidl(e));
191 }
192 }
193 };
194 let hold_until_frame = hold_until_frame.ok_or(Error::InvalidLease)?;
195 let handle = RxLease { handle: handle.ok_or(Error::InvalidLease)? };
196
197 watcher.wait_until(hold_until_frame).await;
198 Ok(Some((handle, (inner, watcher))))
199 })
200 }
201
202 pub async fn close(&self) -> Result<()> {
204 self.inner.proxy.close()?;
205 let mut event_stream = self.inner.proxy.take_event_stream();
207 while let Some(event) = event_stream.next().await {
208 match event {
209 Err(fidl::Error::ClientChannelClosed { .. }) => break,
210 Ok(_) => {} Err(e) => return Err(Error::Fidl(e)),
212 }
213 }
214 Ok(())
215 }
216}
217
218struct Inner {
219 pool: Arc<Pool>,
220 proxy: netdev::SessionProxy,
221 name: String,
222 rx: fasync::Fifo<DescId<Rx>>,
223 tx: fasync::Fifo<DescId<Tx>>,
224 tx_pending: Pending<Tx>,
226 rx_ready: Mutex<ReadyBuffer<DescId<Rx>>>,
227 tx_ready: Mutex<ReadyBuffer<DescId<Tx>>>,
228 tx_idle_listeners: TxIdleListeners,
229}
230
231impl Inner {
232 async fn new(device: &netdev::DeviceProxy, name: &str, config: Config) -> Result<Arc<Self>> {
234 let (pool, descriptors, data) = Pool::new(config)?;
235
236 let session_info = {
237 let descriptor_length =
240 u8::try_from(NETWORK_DEVICE_DESCRIPTOR_LENGTH / std::mem::size_of::<u64>())
241 .expect("descriptor length in 64-bit words not representable by u8");
242 let data = vec![fidl_fuchsia_hardware_network::DataVmo {
243 id: Some(DEFAULT_VMO_ID),
244 vmo: Some(data),
245 num_rx_buffers: Some(config.num_rx_buffers.get()),
246 __source_breaking: fidl::marker::SourceBreaking,
247 }];
248 netdev::SessionInfo {
249 descriptors: Some(descriptors),
250 data: Some(data),
251 descriptor_version: Some(NETWORK_DEVICE_DESCRIPTOR_VERSION),
252 descriptor_length: Some(descriptor_length),
253 descriptor_count: Some(config.num_tx_buffers.get() + config.num_rx_buffers.get()),
254 options: Some(config.options),
255 ..Default::default()
256 }
257 };
258
259 let (client, netdev::Fifos { rx, tx }) = device
260 .open_session(name, session_info)
261 .await?
262 .map_err(|raw| Error::Open(name.to_owned(), zx::Status::from_raw(raw)))?;
263 let proxy = client.into_proxy();
264
265 if config.num_tx_buffers.get() > 0 {
266 let (_successful, status) =
267 proxy.register_for_tx(&[DEFAULT_VMO_ID]).await.map_err(Error::Fidl)?;
268 zx::Status::ok(status).map_err(Error::RegisterForTx)?;
269 }
270
271 let rx = fasync::Fifo::from_fifo(rx);
272 let tx = fasync::Fifo::from_fifo(tx);
273
274 Ok(Arc::new(Self {
275 pool,
276 proxy,
277 name: name.to_owned(),
278 rx,
279 tx,
280 tx_pending: Pending::new(Vec::new()),
281 rx_ready: Mutex::new(ReadyBuffer::new(config.num_rx_buffers.get().into())),
282 tx_ready: Mutex::new(ReadyBuffer::new(config.num_tx_buffers.get().into())),
283 tx_idle_listeners: TxIdleListeners::new(),
284 }))
285 }
286
287 fn poll_submit_rx(&self, cx: &mut Context<'_>) -> Poll<Result<usize>> {
291 self.pool.rx_pending.poll_submit(&self.rx, cx)
292 }
293
294 fn poll_complete_rx(&self, cx: &mut Context<'_>) -> Poll<Result<DescId<Rx>>> {
298 let mut rx_ready = self.rx_ready.lock();
299 rx_ready.poll_with_fifo(cx, &self.rx).map_err(|status| Error::Fifo("read", "rx", status))
300 }
301
302 fn poll_submit_tx(&self, cx: &mut Context<'_>) -> Poll<Result<usize>> {
306 self.tx_pending.poll_submit(&self.tx, cx)
307 }
308
309 fn poll_complete_tx(&self, cx: &mut Context<'_>) -> Poll<Result<()>> {
311 let result = {
312 let mut tx_ready = self.tx_ready.lock();
313 tx_ready.poll_with_fifo(cx, &self.tx).map(|r| match r {
317 Ok(desc) => self.pool.tx_completed(desc),
318 Err(status) => Err(Error::Fifo("read", "tx", status)),
319 })
320 };
321
322 match &result {
323 Poll::Ready(Ok(())) => self.tx_idle_listeners.tx_complete(),
324 Poll::Pending | Poll::Ready(Err(_)) => {}
325 }
326 result
327 }
328
329 fn send(&self, buffer: Buffer<Tx>) {
336 self.tx_idle_listeners.tx_started();
337 self.tx_pending.extend(std::iter::once(buffer.leak()));
338 }
339
340 async fn recv(&self) -> Result<Buffer<Rx>> {
344 poll_fn(|cx| -> Poll<Result<Buffer<Rx>>> {
345 let head = ready!(self.poll_complete_rx(cx))?;
346 Poll::Ready(self.pool.rx_completed(head))
347 })
348 .await
349 }
350}
351
352#[must_use = "futures do nothing unless you `.await` or poll them"]
356pub struct Task {
357 inner: Arc<Inner>,
358}
359
360impl Future for Task {
361 type Output = Result<()>;
362 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
363 let inner = &Pin::into_inner(self).inner;
364 loop {
365 let mut all_pending = true;
366 while inner.poll_complete_tx(cx)?.is_ready_checked::<()>() {
369 all_pending = false;
370 }
371 if inner.poll_submit_rx(cx)?.is_ready_checked::<usize>() {
372 all_pending = false;
373 }
374 if inner.poll_submit_tx(cx)?.is_ready_checked::<usize>() {
375 all_pending = false;
376 }
377 if all_pending {
378 return Poll::Pending;
379 }
380 }
381 }
382}
383
384#[derive(Debug, Clone, Copy)]
386pub struct Config {
387 buffer_stride: NonZeroU64,
389 num_rx_buffers: NonZeroU16,
391 num_tx_buffers: NonZeroU16,
393 options: netdev::SessionFlags,
395 buffer_layout: BufferLayout,
397}
398
399#[derive(Debug, Clone, Copy)]
401struct BufferLayout {
402 min_tx_data: usize,
404 min_tx_head: u16,
406 min_tx_tail: u16,
408 length: usize,
410}
411
412#[derive(Debug, Clone, ValidFidlTable)]
414#[fidl_table_src(netdev::DeviceBaseInfo)]
415#[fidl_table_strict]
416pub struct DeviceBaseInfo {
417 pub rx_depth: u16,
419 pub tx_depth: u16,
421 pub buffer_alignment: u32,
423 #[fidl_field_type(optional)]
425 pub max_buffer_length: Option<NonZeroU32>,
426 pub min_rx_buffer_length: u32,
428 pub min_tx_buffer_length: u32,
430 pub min_tx_buffer_head: u16,
432 pub min_tx_buffer_tail: u16,
434 pub max_buffer_parts: u8,
436 #[fidl_field_type(optional)]
439 pub min_rx_buffers: Option<NonZeroU16>,
440}
441
442#[derive(Debug, Clone, ValidFidlTable)]
444#[fidl_table_src(netdev::DeviceInfo)]
445#[fidl_table_strict]
446pub struct DeviceInfo {
447 pub min_descriptor_length: u8,
449 pub descriptor_version: u8,
451 pub base_info: DeviceBaseInfo,
453}
454
455#[derive(Debug, Copy, Clone)]
458pub struct DerivableConfig {
459 pub default_buffer_length: usize,
461 pub watch_rx_leases: bool,
463}
464
465impl DerivableConfig {
466 pub const DEFAULT_BUFFER_LENGTH: usize = 2048;
473 pub const DEFAULT: Self =
475 Self { default_buffer_length: Self::DEFAULT_BUFFER_LENGTH, watch_rx_leases: false };
476}
477
478impl Default for DerivableConfig {
479 fn default() -> Self {
480 Self::DEFAULT
481 }
482}
483
484impl DeviceInfo {
485 pub fn make_config(&self, config: DerivableConfig) -> Result<Config> {
490 let DeviceInfo {
491 min_descriptor_length,
492 descriptor_version,
493 base_info:
494 DeviceBaseInfo {
495 rx_depth,
496 tx_depth,
497 buffer_alignment,
498 max_buffer_length,
499 min_rx_buffer_length,
500 min_tx_buffer_length,
501 min_tx_buffer_head,
502 min_tx_buffer_tail,
503 max_buffer_parts: _,
504 min_rx_buffers: _,
505 },
506 } = self;
507 if NETWORK_DEVICE_DESCRIPTOR_VERSION != *descriptor_version {
508 return Err(Error::Config(format!(
509 "descriptor version mismatch: {} != {}",
510 NETWORK_DEVICE_DESCRIPTOR_VERSION, descriptor_version
511 )));
512 }
513 if NETWORK_DEVICE_DESCRIPTOR_LENGTH < usize::from(*min_descriptor_length) {
514 return Err(Error::Config(format!(
515 "descriptor length too small: {} < {}",
516 NETWORK_DEVICE_DESCRIPTOR_LENGTH, min_descriptor_length
517 )));
518 }
519
520 let DerivableConfig { default_buffer_length, watch_rx_leases } = config;
521
522 let num_rx_buffers =
523 NonZeroU16::new(*rx_depth).ok_or_else(|| Error::Config("no RX buffers".to_owned()))?;
524 let num_tx_buffers =
525 NonZeroU16::new(*tx_depth).ok_or_else(|| Error::Config("no TX buffers".to_owned()))?;
526
527 let max_buffer_length = max_buffer_length
528 .and_then(|max| {
529 usize::try_from(max.get()).ok_checked::<TryFromIntError>()
533 })
534 .unwrap_or(usize::MAX);
535 let min_buffer_length = usize::try_from(*min_rx_buffer_length)
536 .ok_checked::<TryFromIntError>()
537 .unwrap_or(usize::MAX);
538
539 let buffer_length =
540 usize::min(max_buffer_length, usize::max(min_buffer_length, default_buffer_length));
541
542 let buffer_alignment = usize::try_from(*buffer_alignment).map_err(
543 |std::num::TryFromIntError { .. }| {
544 Error::Config(format!(
545 "buffer_alignment not representable within usize: {}",
546 buffer_alignment,
547 ))
548 },
549 )?;
550
551 let buffer_stride = buffer_length
552 .checked_add(buffer_alignment - 1)
553 .map(|x| x / buffer_alignment * buffer_alignment)
554 .ok_or_else(|| {
555 Error::Config(format!(
556 "not possible to align {} to {} under usize::MAX",
557 buffer_length, buffer_alignment,
558 ))
559 })?;
560
561 if buffer_stride < buffer_length {
562 return Err(Error::Config(format!(
563 "buffer stride too small {} < {}",
564 buffer_stride, buffer_length
565 )));
566 }
567
568 if buffer_length < usize::from(*min_tx_buffer_head) + usize::from(*min_tx_buffer_tail) {
569 return Err(Error::Config(format!(
570 "buffer length {} does not meet minimum tx buffer head/tail requirement {}/{}",
571 buffer_length, min_tx_buffer_head, min_tx_buffer_tail,
572 )));
573 }
574
575 let num_buffers =
576 rx_depth.checked_add(*tx_depth).filter(|num| *num != u16::MAX).ok_or_else(|| {
577 Error::Config(format!(
578 "too many buffers requested: {} + {} > u16::MAX",
579 rx_depth, tx_depth
580 ))
581 })?;
582
583 let buffer_stride =
584 u64::try_from(buffer_stride).map_err(|std::num::TryFromIntError { .. }| {
585 Error::Config(format!("buffer_stride too big: {} > u64::MAX", buffer_stride))
586 })?;
587
588 match buffer_stride.checked_mul(num_buffers.into()).map(isize::try_from) {
592 None | Some(Err(std::num::TryFromIntError { .. })) => {
593 return Err(Error::Config(format!(
594 "too much memory required for the buffers: {} * {} > isize::MAX",
595 buffer_stride, num_buffers
596 )));
597 }
598 Some(Ok(_total)) => (),
599 };
600
601 let buffer_stride = NonZeroU64::new(buffer_stride)
602 .ok_or_else(|| Error::Config("buffer_stride is zero".to_owned()))?;
603
604 let min_tx_data = match usize::try_from(*min_tx_buffer_length)
605 .map(|min_tx| (min_tx <= buffer_length).then_some(min_tx))
606 {
607 Ok(Some(min_tx_buffer_length)) => min_tx_buffer_length,
608 Ok(None) | Err(std::num::TryFromIntError { .. }) => {
610 return Err(Error::Config(format!(
611 "buffer_length smaller than minimum TX requirement: {} < {}",
612 buffer_length, *min_tx_buffer_length
613 )));
614 }
615 };
616
617 let mut options = netdev::SessionFlags::empty();
618 options.set(netdev::SessionFlags::RECEIVE_RX_POWER_LEASES, watch_rx_leases);
619
620 Ok(Config {
621 buffer_stride,
622 num_rx_buffers,
623 num_tx_buffers,
624 options,
625 buffer_layout: BufferLayout {
626 length: buffer_length,
627 min_tx_head: *min_tx_buffer_head,
628 min_tx_tail: *min_tx_buffer_tail,
629 min_tx_data,
630 },
631 })
632 }
633}
634
635#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
637pub struct Port {
638 pub(crate) base: u8,
639 pub(crate) salt: u8,
640}
641
642impl TryFrom<netdev::PortId> for Port {
643 type Error = Error;
644 fn try_from(netdev::PortId { base, salt }: netdev::PortId) -> Result<Self> {
645 if base <= netdev::MAX_PORTS {
646 Ok(Self { base, salt })
647 } else {
648 Err(Error::InvalidPortId(base))
649 }
650 }
651}
652
653impl From<Port> for netdev::PortId {
654 fn from(Port { base, salt }: Port) -> Self {
655 Self { base, salt }
656 }
657}
658
659struct Pending<K: AllocKind> {
661 inner: Mutex<(Vec<DescId<K>>, Option<Waker>)>,
662}
663
664impl<K: AllocKind> Pending<K> {
665 fn new(descs: Vec<DescId<K>>) -> Self {
666 Self { inner: Mutex::new((descs, None)) }
667 }
668
669 fn extend(&self, descs: impl IntoIterator<Item = DescId<K>>) {
671 let mut guard = self.inner.lock();
672 let (storage, waker) = &mut *guard;
673 storage.extend(descs);
674 if let Some(waker) = waker.take() {
675 waker.wake();
676 }
677 }
678
679 fn poll_submit(
685 &self,
686 fifo: &fasync::Fifo<DescId<K>>,
687 cx: &mut Context<'_>,
688 ) -> Poll<Result<usize>> {
689 let mut guard = self.inner.lock();
690 let (storage, waker) = &mut *guard;
691 if storage.is_empty() {
692 *waker = Some(cx.waker().clone());
693 return Poll::Pending;
694 }
695
696 let submitted = ready!(fifo.try_write(cx, &storage[..]))
701 .map_err(|status| Error::Fifo("write", K::REFL.as_str(), status))?;
702 let _drained = storage.drain(0..submitted);
703 Poll::Ready(Ok(submitted))
704 }
705}
706
707struct ReadyBuffer<T> {
714 data: Vec<MaybeUninit<T>>,
720 available: Range<usize>,
721}
722
723impl<T> Drop for ReadyBuffer<T> {
724 fn drop(&mut self) {
725 let Self { data, available } = self;
726 for initialized in &mut data[available.clone()] {
727 unsafe { initialized.assume_init_drop() }
730 }
731 *available = 0..0;
732 }
733}
734
735impl<T> ReadyBuffer<T> {
736 fn new(capacity: usize) -> Self {
737 let data = std::iter::from_fn(|| Some(MaybeUninit::uninit())).take(capacity).collect();
738 Self { data, available: 0..0 }
739 }
740
741 fn poll_with_fifo(
742 &mut self,
743 cx: &mut Context<'_>,
744 fifo: &fuchsia_async::Fifo<T>,
745 ) -> Poll<std::result::Result<T, zx::Status>>
746 where
747 T: fasync::FifoEntry,
748 {
749 let Self { data, available: Range { start, end } } = self;
750
751 loop {
752 if *start != *end {
754 let desc = std::mem::replace(&mut data[*start], MaybeUninit::uninit());
755 *start += 1;
756 let desc = unsafe { desc.assume_init() };
759 return Poll::Ready(Ok(desc));
760 }
761 let count = ready!(fifo.try_read(cx, &mut data[..]))?;
763 *start = 0;
764 *end = count;
765 }
766 }
767}
768
769struct TxIdleListeners {
770 event: event_listener::Event,
771 tx_in_flight: AtomicUsize,
772}
773
774impl TxIdleListeners {
775 fn new() -> Self {
776 Self { event: event_listener::Event::new(), tx_in_flight: AtomicUsize::new(0) }
777 }
778
779 fn tx_complete(&self) {
783 let Self { event, tx_in_flight } = self;
784 let old_value = tx_in_flight.fetch_sub(1, atomic::Ordering::SeqCst);
785 debug_assert_ne!(old_value, 0);
786 if old_value == 1 {
787 let _notified: usize = event.notify(usize::MAX);
788 }
789 }
790
791 fn tx_started(&self) {
793 let Self { event: _, tx_in_flight } = self;
794 let _: usize = tx_in_flight.fetch_add(1, atomic::Ordering::SeqCst);
795 }
796
797 async fn wait(&self) {
798 let Self { event, tx_in_flight } = self;
799 loop {
808 if tx_in_flight.load(atomic::Ordering::SeqCst) == 0 {
809 return;
810 }
811
812 event_listener::listener!(event => listener);
813
814 if tx_in_flight.load(atomic::Ordering::SeqCst) == 0 {
815 return;
816 }
817
818 listener.await;
819 }
820 }
821}
822
823#[derive(Debug)]
829pub struct RxLease {
830 handle: netdev::DelegatedRxLeaseHandle,
831}
832
833impl Drop for RxLease {
834 fn drop(&mut self) {
835 let Self { handle } = self;
836 match handle {
839 netdev::DelegatedRxLeaseHandle::Channel(_channel) => {
840 }
842 netdev::DelegatedRxLeaseHandle::Eventpair(_eventpair) => {
843 }
845 netdev::DelegatedRxLeaseHandle::__SourceBreaking { .. } => {}
846 }
847 }
848}
849
850impl RxLease {
851 pub fn inner(&self) -> &netdev::DelegatedRxLeaseHandle {
853 &self.handle
854 }
855}
856
857#[cfg(test)]
858mod tests {
859 use std::num::NonZeroU32;
860 use std::ops::Deref;
861 use std::sync::Arc;
862 use std::task::Poll;
863
864 use assert_matches::assert_matches;
865 use fuchsia_async::Fifo;
866 use test_case::test_case;
867 use zerocopy::{FromBytes, Immutable, IntoBytes};
868
869 use crate::session::DerivableConfig;
870
871 use super::buffer::{
872 AllocKind, DescId, NETWORK_DEVICE_DESCRIPTOR_LENGTH, NETWORK_DEVICE_DESCRIPTOR_VERSION,
873 };
874 use super::{
875 BufferLayout, Config, DeviceBaseInfo, DeviceInfo, Error, Inner, Mutex, Pending, Pool,
876 ReadyBuffer, Task, TxIdleListeners,
877 };
878
879 const DEFAULT_DEVICE_BASE_INFO: DeviceBaseInfo = DeviceBaseInfo {
880 rx_depth: 1,
881 tx_depth: 1,
882 buffer_alignment: 1,
883 max_buffer_length: None,
884 min_rx_buffer_length: 0,
885 min_tx_buffer_head: 0,
886 min_tx_buffer_length: 0,
887 min_tx_buffer_tail: 0,
888 max_buffer_parts: fidl_fuchsia_hardware_network::MAX_DESCRIPTOR_CHAIN,
889 min_rx_buffers: None,
890 };
891
892 const DEFAULT_DEVICE_INFO: DeviceInfo = DeviceInfo {
893 min_descriptor_length: 0,
894 descriptor_version: 1,
895 base_info: DEFAULT_DEVICE_BASE_INFO,
896 };
897
898 const DEFAULT_BUFFER_LENGTH: usize = 2048;
899
900 #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
901 min_descriptor_length: u8::MAX,
902 ..DEFAULT_DEVICE_INFO
903 }, format!("descriptor length too small: {} < {}", NETWORK_DEVICE_DESCRIPTOR_LENGTH, u8::MAX))]
904 #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
905 descriptor_version: 42,
906 ..DEFAULT_DEVICE_INFO
907 }, format!("descriptor version mismatch: {} != {}", NETWORK_DEVICE_DESCRIPTOR_VERSION, 42))]
908 #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
909 base_info: DeviceBaseInfo {
910 tx_depth: 0,
911 ..DEFAULT_DEVICE_BASE_INFO
912 },
913 ..DEFAULT_DEVICE_INFO
914 }, "no TX buffers")]
915 #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
916 base_info: DeviceBaseInfo {
917 rx_depth: 0,
918 ..DEFAULT_DEVICE_BASE_INFO
919 },
920 ..DEFAULT_DEVICE_INFO
921 }, "no RX buffers")]
922 #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
923 base_info: DeviceBaseInfo {
924 tx_depth: u16::MAX,
925 rx_depth: u16::MAX,
926 ..DEFAULT_DEVICE_BASE_INFO
927 },
928 ..DEFAULT_DEVICE_INFO
929 }, format!("too many buffers requested: {} + {} > u16::MAX", u16::MAX, u16::MAX))]
930 #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
931 base_info: DeviceBaseInfo {
932 min_tx_buffer_length: DEFAULT_BUFFER_LENGTH as u32 + 1,
933 ..DEFAULT_DEVICE_BASE_INFO
934 },
935 ..DEFAULT_DEVICE_INFO
936 }, format!(
937 "buffer_length smaller than minimum TX requirement: {} < {}",
938 DEFAULT_BUFFER_LENGTH, DEFAULT_BUFFER_LENGTH + 1))]
939 #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
940 base_info: DeviceBaseInfo {
941 min_tx_buffer_head: DEFAULT_BUFFER_LENGTH as u16 + 1,
942 ..DEFAULT_DEVICE_BASE_INFO
943 },
944 ..DEFAULT_DEVICE_INFO
945 }, format!(
946 "buffer length {} does not meet minimum tx buffer head/tail requirement {}/0",
947 DEFAULT_BUFFER_LENGTH, DEFAULT_BUFFER_LENGTH + 1))]
948 #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
949 base_info: DeviceBaseInfo {
950 min_tx_buffer_tail: DEFAULT_BUFFER_LENGTH as u16 + 1,
951 ..DEFAULT_DEVICE_BASE_INFO
952 },
953 ..DEFAULT_DEVICE_INFO
954 }, format!(
955 "buffer length {} does not meet minimum tx buffer head/tail requirement 0/{}",
956 DEFAULT_BUFFER_LENGTH, DEFAULT_BUFFER_LENGTH + 1))]
957 #[test_case(0, DEFAULT_DEVICE_INFO, "buffer_stride is zero")]
958 #[test_case(usize::MAX, DEFAULT_DEVICE_INFO,
959 format!(
960 "too much memory required for the buffers: {} * {} > isize::MAX",
961 usize::MAX, 2))]
962 #[test_case(usize::MAX, DeviceInfo {
963 base_info: DeviceBaseInfo {
964 buffer_alignment: 2,
965 ..DEFAULT_DEVICE_BASE_INFO
966 },
967 ..DEFAULT_DEVICE_INFO
968 }, format!(
969 "not possible to align {} to {} under usize::MAX",
970 usize::MAX, 2))]
971 fn configs_from_device_info_err(
972 buffer_length: usize,
973 info: DeviceInfo,
974 expected: impl Deref<Target = str>,
975 ) {
976 let config = DerivableConfig { default_buffer_length: buffer_length, ..Default::default() };
977 assert_matches!(
978 info.make_config(config),
979 Err(Error::Config(got)) if got.as_str() == expected.deref()
980 );
981 }
982
983 #[test_case(DeviceInfo {
984 base_info: DeviceBaseInfo {
985 min_rx_buffer_length: DEFAULT_BUFFER_LENGTH as u32 + 1,
986 ..DEFAULT_DEVICE_BASE_INFO
987 },
988 ..DEFAULT_DEVICE_INFO
989 }, DEFAULT_BUFFER_LENGTH + 1; "default below min")]
990 #[test_case(DeviceInfo {
991 base_info: DeviceBaseInfo {
992 max_buffer_length: NonZeroU32::new(DEFAULT_BUFFER_LENGTH as u32 - 1),
993 ..DEFAULT_DEVICE_BASE_INFO
994 },
995 ..DEFAULT_DEVICE_INFO
996 }, DEFAULT_BUFFER_LENGTH - 1; "default above max")]
997 #[test_case(DeviceInfo {
998 base_info: DeviceBaseInfo {
999 min_rx_buffer_length: DEFAULT_BUFFER_LENGTH as u32 - 1,
1000 max_buffer_length: NonZeroU32::new(DEFAULT_BUFFER_LENGTH as u32 + 1),
1001 ..DEFAULT_DEVICE_BASE_INFO
1002 },
1003 ..DEFAULT_DEVICE_INFO
1004 }, DEFAULT_BUFFER_LENGTH; "default in bounds")]
1005 fn configs_from_device_buffer_length(info: DeviceInfo, expected_length: usize) {
1006 let config = info
1007 .make_config(DerivableConfig {
1008 default_buffer_length: DEFAULT_BUFFER_LENGTH,
1009 ..Default::default()
1010 })
1011 .expect("is valid");
1012 let Config {
1013 buffer_layout: BufferLayout { length, min_tx_data: _, min_tx_head: _, min_tx_tail: _ },
1014 buffer_stride: _,
1015 num_rx_buffers: _,
1016 num_tx_buffers: _,
1017 options: _,
1018 } = config;
1019 assert_eq!(length, expected_length);
1020 }
1021
1022 fn make_fifos<K: AllocKind>() -> (Fifo<DescId<K>>, zx::Fifo<DescId<K>>) {
1023 let (handle, other_end) = zx::Fifo::create(1).unwrap();
1024 (Fifo::from_fifo(handle), other_end)
1025 }
1026
1027 fn remove_rights<T: FromBytes + IntoBytes + Immutable>(
1028 fifo: Fifo<T>,
1029 rights_to_remove: zx::Rights,
1030 ) -> Fifo<T> {
1031 let fifo = zx::Fifo::from(fifo);
1032 let rights = fifo.as_handle_ref().basic_info().expect("can retrieve info").rights;
1033
1034 let fifo = fifo.replace_handle(rights ^ rights_to_remove).expect("can replace");
1035 Fifo::from_fifo(fifo)
1036 }
1037
1038 enum TxOrRx {
1039 Tx,
1040 Rx,
1041 }
1042 #[test_case(TxOrRx::Tx, zx::Rights::READ; "tx read")]
1043 #[test_case(TxOrRx::Tx, zx::Rights::WRITE; "tx write")]
1044 #[test_case(TxOrRx::Rx, zx::Rights::WRITE; "rx read")]
1045 #[fuchsia_async::run_singlethreaded(test)]
1046 async fn task_as_future_poll_error(which_fifo: TxOrRx, right_to_remove: zx::Rights) {
1047 let (pool, _descriptors, _data) = Pool::new(
1055 DEFAULT_DEVICE_INFO
1056 .make_config(DerivableConfig {
1057 default_buffer_length: DEFAULT_BUFFER_LENGTH,
1058 ..Default::default()
1059 })
1060 .expect("is valid"),
1061 )
1062 .expect("is valid");
1063 let (session_proxy, _session_server) =
1064 fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::SessionMarker>();
1065
1066 let (rx, _rx_sender) = make_fifos();
1067 let (tx, _tx_receiver) = make_fifos();
1068
1069 let (tx, rx) = match which_fifo {
1071 TxOrRx::Tx => (remove_rights(tx, right_to_remove), rx),
1072 TxOrRx::Rx => (tx, remove_rights(rx, right_to_remove)),
1073 };
1074
1075 let buf = pool.alloc_tx_buffer(1).await.expect("can allocate");
1076 let inner = Arc::new(Inner {
1077 pool,
1078 proxy: session_proxy,
1079 name: "fake_task".to_string(),
1080 rx,
1081 tx,
1082 tx_pending: Pending::new(vec![]),
1083 rx_ready: Mutex::new(ReadyBuffer::new(10)),
1084 tx_ready: Mutex::new(ReadyBuffer::new(10)),
1085 tx_idle_listeners: TxIdleListeners::new(),
1086 });
1087
1088 inner.send(buf);
1089
1090 let mut task = Task { inner };
1091
1092 assert_matches!(futures::poll!(&mut task), Poll::Ready(Err(Error::Fifo(_, _, _))));
1095 }
1096}