| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use super::{ |
| devices::{BindingId, DeviceSpecificInfo, Devices, EthernetInfo}, |
| DeviceStatusNotifier, InterfaceControl as _, LockableContext, MutableDeviceState as _, |
| }; |
| use anyhow::Error; |
| use assert_matches::assert_matches; |
| use ethernet as eth; |
| pub use fidl_ethernet::DeviceStatus; |
| use fidl_fuchsia_hardware_ethernet as fidl_ethernet; |
| use fuchsia_async as fasync; |
| use fuchsia_zircon as zx; |
| use futures::{TryFutureExt as _, TryStreamExt as _}; |
| use log::{debug, error, info, trace}; |
| use netstack3_core::{receive_frame, BufferNonSyncContext, Ctx}; |
| use packet::serialize::Buf; |
| use std::ops::DerefMut as _; |
| |
| pub async fn setup_ethernet( |
| dev: fidl_ethernet::DeviceProxy, |
| ) -> Result<(eth::Client, fidl_fuchsia_hardware_ethernet_ext::EthernetInfo), Error> { |
| let vmo = zx::Vmo::create(256 * eth::DEFAULT_BUFFER_SIZE as u64)?; |
| let eth_client = eth::Client::new(dev, vmo, eth::DEFAULT_BUFFER_SIZE, "netstack3").await?; |
| let info = eth_client.info().await?; |
| eth_client.start().await?; |
| Ok((eth_client, info)) |
| } |
| |
| /// The worker that receives messages from the ethernet device, and passes them |
| /// on to the main event loop. |
| pub(crate) struct EthernetWorker<C> { |
| id: BindingId, |
| ctx: C, |
| } |
| |
| impl<C> EthernetWorker<C> { |
| pub(crate) fn new(id: BindingId, ctx: C) -> Self { |
| EthernetWorker { id, ctx } |
| } |
| } |
| |
| // TODO(https://github.com/rust-lang/rust/issues/20671): Replace the duplicate associated type with |
| // a where clause bounding the parent trait's associated type. |
| // |
| // OR |
| // |
| // TODO(https://github.com/rust-lang/rust/issues/52662): Replace the duplicate associated type with |
| // a bound on the parent trait's associated type. |
| pub(crate) trait EthernetWorkerContext: |
| LockableContext<NonSyncCtx = <Self as EthernetWorkerContext>::NonSyncCtx> + Send + Sync + 'static |
| { |
| type NonSyncCtx: for<'a> BufferNonSyncContext<Buf<&'a mut [u8]>> |
| + AsRef<Devices> |
| + AsMut<Devices> |
| + DeviceStatusNotifier |
| + Send |
| + Sync; |
| } |
| |
| impl<T> EthernetWorkerContext for T |
| where |
| T: LockableContext + Send + Sync + 'static, |
| T::NonSyncCtx: for<'a> BufferNonSyncContext<Buf<&'a mut [u8]>> |
| + AsRef<Devices> |
| + AsMut<Devices> |
| + DeviceStatusNotifier |
| + Send |
| + Sync, |
| { |
| type NonSyncCtx = T::NonSyncCtx; |
| } |
| |
| impl<C: EthernetWorkerContext> EthernetWorker<C> { |
| pub fn spawn(self, mut events: eth::EventStream) { |
| fasync::Task::spawn( |
| async move { |
| let Self { ref ctx, id } = self; |
| // TODO(brunodalbo) remove this temporary buffer, we should be |
| // owning buffers until processing is done, this is just an |
| // unnecessary copy. Also, its size is implicitly defined by the |
| // buffer size that we used to create the VMO, which is not |
| // taking into consideration the device's MTU at all. |
| let mut buf = [0; eth::DEFAULT_BUFFER_SIZE]; |
| while let Some(evt) = events.try_next().await? { |
| match evt { |
| eth::Event::StatusChanged => { |
| let mut ctx = ctx.lock().await; |
| info!("device {:?} status changed signal", id); |
| // We need to call get_status even if we don't use the output, since |
| // calling it acks the message, and prevents the device from sending |
| // more status changed messages. |
| if let Some(device) = ctx.non_sync_ctx.as_ref().get_device(id) { |
| let device = assert_matches!( |
| device.info(), |
| DeviceSpecificInfo::Ethernet(device) => device |
| ); |
| let EthernetInfo { common_info: _, client, mac: _, features: _, phy_up: _} = device; |
| if let Ok(status) = client.get_status().await { |
| info!("device {:?} status changed to: {:?}", id, status); |
| // Handle the new device state. If this results in no change, no |
| // state will be modified. |
| if status.contains(fidl_ethernet::DeviceStatus::ONLINE) { |
| ctx.update_device_state(id, |dev_info| { |
| let phy_up: &mut bool = assert_matches!( |
| dev_info.info_mut(), |
| DeviceSpecificInfo::Ethernet(EthernetInfo { common_info: _, client: _, mac: _, features: _, phy_up}) => phy_up |
| ); |
| *phy_up = true; |
| }); |
| ctx.enable_interface(id).unwrap_or_else(|e| { |
| trace!("phy enable interface failed: {:?}", e) |
| }); |
| } else { |
| ctx.update_device_state(id, |dev_info| { |
| let phy_up: &mut bool = assert_matches!( |
| dev_info.info_mut(), |
| DeviceSpecificInfo::Ethernet(EthernetInfo { common_info: _, client: _, mac: _, features: _, phy_up}) => phy_up |
| ); |
| *phy_up = false; |
| }); |
| ctx.disable_interface(id).unwrap_or_else(|e| { |
| trace!("phy enable disable failed: {:?}", e) |
| }); |
| } |
| } |
| } |
| } |
| eth::Event::Receive(rx, _flags) => { |
| let len = rx.read(&mut buf); |
| let mut ctx = ctx.lock().await; let Ctx { sync_ctx, non_sync_ctx } = ctx.deref_mut(); |
| |
| if let Some(id) = |
| AsRef::<Devices>::as_ref(non_sync_ctx).get_core_id(id) |
| { |
| receive_frame(sync_ctx, non_sync_ctx,id, Buf::new(&mut buf[..len], ..)) |
| .unwrap_or_else(|e| error!("error receiving frame: {}", e)) |
| } else { |
| debug!("received ethernet frame on disabled device: {}", id); |
| } |
| } |
| } |
| } |
| Ok(()) |
| } |
| .unwrap_or_else(|e: Error| error!("{:?}", e)), |
| ) |
| .detach() |
| } |
| } |