| // 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. |
| |
| /// Generic Network Device interface. |
| /// |
| /// The definitions herein provide the API surface to expose a hardware device as a network device |
| /// interface to the system through the FIDL protocol [`fuchsia.hardware.network/Device`]. A network |
| /// device interface is the data-plane contract that allows the networking stack to send and receive |
| /// frames on a physical or virtual network. A device exposes this capability by implementing the |
| /// `NetworkDeviceImpl` protocol, which allows a middleware implementation to bind to it and offer |
| /// the FIDL protocol to applications. |
| /// |
| /// The API contract is based on three key concepts: |
| /// - Frame Types |
| /// - Receive and Transmit buffers |
| /// - `BufferData` memory layout |
| /// |
| /// Frame Types are the defined contract that is exposed to applications, which convey the data type |
| /// contained in a tx or rx buffer. The supported frame types are defined in |
| /// [`fuchsia.hardware.network/FrameType`]. Upon initialization, the middleware implementation will |
| /// fetch the supported frame types from the device through the `GetInfo` call. |
| /// |
| /// Receive and Transmit buffers are buffers that are headed to different directions: a receive |
| /// buffer is a piece of data that is received from the network, and makes its way to the |
| /// application layer. A transmit buffer travels in the opposite direction: it originates in the |
| /// application layer and makes its way out into the network. The device implementation receives |
| /// buffers from the [`fuchsia.hardware.network.device/NetworkDeviceIfc`], which is offered by the |
| /// middleware implementation. |
| /// |
| /// A receive buffer flows from [`fuchsia.hardware.network.device/NetworkDeviceIfc`] into |
| /// [`fuchsia.hardware.network.device/NetworkDeviceImpl`] through the |
| /// [`fuchsia.hardware.network.device/NetworkDeviceImpl.QueueRxSpace`] method, which gives access to |
| /// receive buffers. The diagram below illustrates the mechanism: |
| /// |
| /// ++++++++++++++++++++++++++++ +++++++++++++++++++++++ |
| /// | (1) | => RxSpaceBuffer => | (2) | |
| /// | NetworkDeviceIfc | | NetworkDeviceImpl | |
| /// | (4) | <= RxBuffer <= | (3) | <= Network data |
| /// ++++++++++++++++++++++++++++ +++++++++++++++++++++++ |
| /// |
| /// (1) `NetworkDeviceIfc` pushes available rx buffer space to `NetworkDeviceImpl` through |
| /// [`fuchsia.hardware.network.device/NetworkDeviceImpl.QueueRxSpace`]. |
| /// (2) `NetworkDeviceImpl` retains the available space buffers until network data comes in. |
| /// (3) `NetworkDeviceImpl` receives data from the network, stores it in one of its available |
| /// [`fuchsia.hardware.network.device/RxSpaceBuffers`], making it an |
| /// [`fuchsia.hardware.network.device/NetworkDeviceImpl.RxBuffer`]. |
| /// (4) `NetworkDeviceImpl` sends the fulfilled `RxBuffer` to `NetworkDeviceIfc` through |
| /// [`fuchsia.hardware.network.device/NetworkDeviceIfc.CompleteRx`], which, in turn, sends that |
| /// data over to applications. |
| /// |
| /// A receive buffer flows from `NetworkDeviceIfc` into `NetworkDeviceImpl` through the |
| /// [`fuchsia.hardware.network.device/NetworkDeviceImpl.QueueTx`] method, and it's returned to |
| /// `NetworkDeviceIfc` as a [`fuchsia.hardware.network.device/TxResult`]. The diagram below |
| /// illustrates the mechanism: |
| /// |
| /// ++++++++++++++++++++++++++++ +++++++++++++++++++++++ |
| /// | (1) | => TxBuffer => | (2) | |
| /// | NetworkDeviceIfc | | NetworkDeviceImpl | |
| /// | (4) | <= TxResult <= | (3) | => Network data |
| /// ++++++++++++++++++++++++++++ +++++++++++++++++++++++ |
| /// |
| /// (1) `NetworkDeviceIfc` receives a transmit buffer from applications filled with data intended |
| /// to be delivered to the network. |
| /// (2) `NetworkDeviceIfc` pushes the buffer into `NetworkDeviceImpl` through the |
| /// [`fuchsia.hardware.network.device/NetworkDeviceImpl.QueueTx`] call. |
| /// (3) `NetworkDeviceImpl` sends the data contained in the buffer out into the network. |
| /// (4) When the data is successfully transmitted, `NetworkDeviceImpl` marks the transmission as |
| /// complete referencing the buffer's identifier to |
| /// [`fuchsia.hardware.network.device/NetworkDeviceIfc.CompleteTx`]. |
| library fuchsia.hardware.network.device; |
| |
| using zx; |
| using fuchsia.hardware.network.mac; |
| |
| /// Disables automatic snooping for the device. The generic NetworkDevice layer typically |
| /// automatically copies all tx traffic to any snooping clients. Devices may turn off that behavior |
| /// by setting the `FEATURE_NO_AUTO_SNOOP` flag. |
| /// Devices that disable auto-snooping SHOULD use the |
| /// [`fuchsia.hardware.network.device/NetworkDeviceIfc.Snoop`] interface method to expose any tx |
| /// frames they receive. |
| const uint32 FEATURE_NO_AUTO_SNOOP = 0x01; |
| |
| /// Maximum number of [`fuchsia.hardware.network.device/BufferRegion`] parts in a |
| /// [`fuchsia.hardware.network.device/BufferData`]. |
| // NOTE: the number 4 should cover the most common use case for split buffers: 1 contiguous buffer |
| // for header, 1 contiguous buffer for data, 0 or 1 contiguous buffer for trailer. Rounded up to |
| // nearest power of two. |
| const uint32 MAX_BUFFER_PARTS = 4; |
| |
| /// The maximum number of concurrent shared VMOs that may exist. |
| /// VMO IDs must be in the range `[0, MAX_VMOS)`. |
| // NOTE: The expected number of VMOs in use is going to be between 1 and 3 with common client usage. |
| // This value is chosen to be larger than that to account for possible transitions between clients |
| // (there may be an overlap of one client shutting of a data session as one comes online) and also |
| // not limit too harshly the number of clients. |
| const uint8 MAX_VMOS = 32; |
| |
| /// The maximum number of ports a device can have at once. |
| /// Port IDs must be in the range `[0, MAX_PORTS)`. |
| // NOTE: Chosen arbitrarily to allow for static memory allocation for port information. Expectation |
| // is that devices are not going to have more than 4 ports in typical use cases. |
| const uint8 MAX_PORTS = 32; |
| |
| /// A contiguous memory region in [`fuchsia.hardware.network.device/BufferData`]. |
| /// |
| /// Note that a `BufferRegion` is only contiguous in terms of the VMO it references, it does not |
| /// necessarily translate into contiguous physical memory. |
| struct BufferRegion { |
| /// Offset, in bytes, of data chunk in VMO. |
| uint64 offset; |
| /// Length, in bytes, of data chunk in VMO. |
| uint64 length; |
| }; |
| |
| /// A buffer representation that references the data inside a VMO. |
| struct BufferData { |
| /// Identifier of VMO backing the data in this buffer. |
| /// The VMO identifier matches the one reported to devices through |
| /// [`fuchsia.hardware.network.device/NetworkDeviceImpl.PrepareVmo`]. |
| uint8 vmo_id; |
| /// Regions of VMO holding frame data. |
| vector<BufferRegion>:MAX_BUFFER_PARTS parts; |
| }; |
| |
| /// No extra frame metadata, [`fuchsia.hardware.network.device/FrameInfo.info_type`] must be |
| /// [`fuchsia.hardware.network/InfoType.NO_INFO`]. |
| struct NoInfo { |
| // TODO(brunodalbo) remove this dummy field once banjo supports it. Currently banjo uses empty |
| // structs to allow forward-declaration from FIDL. |
| uint8 nothing; |
| }; |
| |
| /// Extra frame sidecar metadata stored in [`fuchsia.hardware.network.device/BufferMetadata`]. |
| union FrameInfo { |
| /// No extra frame metadata. |
| 1: NoInfo no_info; |
| }; |
| |
| /// Metadata associated with a [`fuchsia.hardware.network.device/TxBuffer`] or |
| /// [`fuchsia.hardware.network.device/RxBuffer`]. |
| struct BufferMetadata { |
| /// Destination or source port identifier for this buffer. |
| uint8 port_id; |
| /// Extra frame metadata information. The type of the |
| /// [`fuchsia.hardware.network.device/FrameInfo`] union is defined by the value in `info_type`. |
| FrameInfo info; |
| /// Type of data in `info`, as defined in [`fuchsia.hardware.network/InfoType`]. |
| uint32 info_type; |
| /// Frame tx or rx flags, as defined in [`fuchsia.hardware.network/RxFlags`], |
| /// [`fuchsia.hardware.network/txFlags`], and [`fuchsia.hardware.network/TxReturnFlags`]. |
| uint32 flags; |
| /// Type of frame contained in this buffer, as defined in |
| /// [`fuchsia.hardware.network/FrameType`]. |
| uint8 frame_type; |
| }; |
| |
| /// A transmit buffer containing a single frame. |
| struct TxBuffer { |
| /// Unique buffer identifier. |
| uint32 id; |
| /// Description of data in this buffer. |
| BufferData data; |
| /// Metadata associated with this buffer. |
| BufferMetadata meta; |
| /// Length of header bytes in the data contained in this buffer. Will always be either 0 or the |
| /// requested [`fuchsia.hardware.network.device/DeviceInfo.tx_head_length`] value. The head |
| /// bytes are always at the beginning of the buffer. |
| uint16 head_length; |
| /// Length of tail bytes in the data contained in this buffer. Will always be either 0 or the |
| /// requested [`fuchsia.hardware.network.device/tx_tail_length`] value. The tail bytes are |
| /// always at the end of the buffer. |
| uint16 tail_length; |
| }; |
| |
| /// A buffer with allocated space to receive frames in. An `RxSpaceBuffer` must always be returned |
| /// as an [`fuchsia.hardware.network.device/RxBuffer`] using |
| /// [`fuchsia.hardware.network.device/NetworkDeviceIfc.CompleteRx`]. |
| struct RxSpaceBuffer { |
| /// Unique buffer identifier. |
| uint32 id; |
| /// Description of data in this buffer. |
| BufferData data; |
| }; |
| |
| /// A buffer containing a single frame received by the device. |
| struct RxBuffer { |
| /// The buffer identifier informed in the [`fuchsia.hardware.network.device/RxSpaceBuffer`] that |
| /// originated this `RxBuffer`. |
| uint32 id; |
| /// The total length of bytes written in the [`fuchsia.hardware.network.device/RxSpaceBuffer`] |
| /// referenced by `id`, excluding any `offset` bytes. |
| uint64 length; |
| /// Metadata associated with buffer. |
| BufferMetadata meta; |
| }; |
| |
| /// The result of a tx operation, reported to [`fuchsia.hardware.network.device/NetworkDeviceIfc`] |
| /// through [`fuchsia.hardware.network.device/NetworkDeviceIfc.CompleteTx`]. |
| struct TxResult { |
| /// The buffer identifier informed in the [`fuchsia.hardware.network.device/TxBuffer`] that |
| /// originated this `TxResult`. |
| uint32 id; |
| /// The result status to report. |
| /// - `ZX_OK` if the frame was sent successfully. |
| /// - `ZX_ERR_NOT_SUPPORTED` if any of the frame's flags are not supported. |
| /// - `ZX_ERR_NO_RESOURCES` if the transmit failed to allocate space in its output queue for the |
| /// frame. |
| /// - `ZX_ERR_UNAVAILABLE` if the device is offline (or went offline before getting a |
| /// confirmation that the frame was sent). |
| zx.status status; |
| }; |
| |
| [Transport = "Banjo", BanjoLayout = "ddk-interface"] |
| protocol NetworkPort { |
| /// Gets information about the port. |
| /// |
| /// Port information must not change over the port's lifetime. |
| GetInfo() -> (PortInfo info); |
| /// Gets operational status of the port. |
| /// |
| /// Changes to operational status must be reported via |
| /// [`fuchsia.hardware.network.device/NetworkDeviceIfc.StatusChanged`] |
| GetStatus() -> (PortStatus status); |
| /// Notifies the port that there are sessions interested in it. |
| /// |
| /// An active port has sessions attached to it. Implementations may employ power saving or other |
| /// strategies on disabled ports. Implementations that do employ such strategies: |
| /// - should not report inbound frames for inactive ports; |
| /// - must return errors for outbound frames destined to inactive ports. |
| /// |
| /// All ports are inactive on creation. |
| SetActive(bool active); |
| /// Gets an interface to the MAC addressing layer of the port. |
| /// |
| /// Ports that do not support MAC addressing must return an empty interface. That means the |
| /// generated-banjo bindings `ctx` and `ops` fields must both be null. |
| // TODO(https://fxbug.dev/67196): Make this an optional interface return once this is FIDL. |
| GetMac() -> (fuchsia.hardware.network.mac.MacAddr mac_ifc); |
| /// Notifies this port has been removed from the interface. |
| /// |
| /// Resources associated with the port must only be freed upon receiving the `Removed` call. |
| Removed(); |
| }; |
| |
| [Transport = "Banjo", BanjoLayout = "ddk-interface"] |
| protocol NetworkDeviceIfc { |
| /// Notifies the interface of status changes on port with `port_id`. |
| /// |
| /// Port status changes must always be notified through `StatusChanged`. The interface will not |
| /// poll ports for status via [`fuchsia.hardware.network.device/NetworkPort.GetStatus`]. |
| PortStatusChanged(uint8 port_id, PortStatus new_status); |
| /// Instantiates a new port with `port_id`. |
| /// |
| /// `port_id` must not be currently in use by any other ports. `port_id`s may be reused once the |
| /// provided port is destroyed by [`fuchsia.hardware.network.device/NetworkPort.Removed`]. |
| /// |
| /// `port_id` must be in the range [0, [`fuchsia.hardware.network.device/MAX_PORTS`]). |
| /// |
| /// Port identifiers do not need to be stable across instantiations or reboots. |
| /// Port identifiers don't need to be allocated in any specific order as long as |
| /// [`fuchsia.hardware.network.device/MAX_PORTS`] is not exceeded. |
| AddPort(uint8 port_id, NetworkPort port); |
| /// Destroys port with `port_id`. |
| /// |
| /// NOTE: Resources associated with the port must not be freed until |
| /// [`fuchsia.hardware.network.device/NetworkPort.Removed`] is called. |
| RemovePort(uint8 port_id); |
| /// Notifies interface of incoming rx data, contained in |
| /// [`fuchsia.hardware.network.device/RxBuffer`]. |
| /// |
| /// Callers should attempt to batch as many buffers as possible in a single call. |
| /// Number of buffers in a single call must be limited to the |
| /// [`fuchsia.hardware.network.device/DeviceInfo.rx_depth`] reported by the `NetworkDeviceImpl` |
| /// that is returning the buffers. |
| /// By calling `CompleteRx` the caller relinquishes ownership of all buffers that are being |
| /// marked as complete. |
| CompleteRx(vector<RxBuffer>:MAX rx); |
| /// Notifies interface of complete transmit buffers. |
| /// Callers should attempt to batch as many buffers as possible in a single call. |
| /// Number of buffers in a single call must be limited to the |
| /// [`fuchsia.hardware.network.device/DeviceInfo.tx_depth`] reported by the `NetworkDeviceImpl` |
| /// that is returning the buffers. |
| /// By calling `CompleteTx` the caller relinquishes ownership of all buffers that are being |
| /// returned. |
| CompleteTx(vector<TxResult>:MAX tx); |
| /// Notifies interface of a snooped tx frame. |
| /// |
| /// Typically used by implementations that have the |
| /// [`fuchsia.hardware.network.device/FEATURE_NO_AUTO_SNOOP`] bit set in |
| /// [`fuchsia.hardware.network.device/DeviceInfo.device_features`]. Implementations that |
| /// generate transmit traffic internally should use `Snoop` regardless of |
| /// `FEATURE_NO_AUTO_SNOOP` being set. |
| /// Snooped frames are *ALWAYS* outbound frames that are being fed back into the interface for |
| /// traffic snooping. |
| /// Device implementations need to call |
| /// [`fuchsia.hardware.network.device/NetworkDeviceIfc.Snoop`] ONLY if |
| /// [`fuchsia.hardware.network.device/NetworkDeviceImpl.SetSnoop`] was set to `true` by the |
| /// interface, otherwise any buffers in `Snoop` will be ignored. |
| Snoop(vector<RxBuffer>:MAX rx); |
| }; |
| |
| /// Supported tx frame types |
| // NOTE(brunodalbo): TxSupport has exactly the same structure as |
| // `fuchsia.hardware.network.FrameTypeSupport`, but importing FIDL types into banjo is not yet |
| // supported. |
| struct TxSupport { |
| /// The frame type this support entry refers to. |
| uint8 type; |
| /// The frame type-specific features supported. |
| uint32 features; |
| /// The flags supported for the given frame type. |
| uint32 supported_flags; |
| }; |
| |
| /// Static port information. |
| struct PortInfo { |
| /// Device class, as defined in [`fuchsia.hardware.network/DeviceClass`]. |
| uint8 device_class; |
| /// Supported rx frame types, as defined by [`fuchsia.hardware.network/FrameType`]. |
| vector<uint8>:MAX rx_types; |
| /// Supported tx frame types. |
| vector<TxSupport>:MAX tx_types; |
| }; |
| |
| /// Static device information. |
| /// `DeviceInfo` must not change for the entire lifetime of a device. |
| struct DeviceInfo { |
| /// Device features |
| uint32 device_features; |
| /// Maximum depth of tx frames in device's outgoing queue. |
| uint16 tx_depth; |
| /// Maximum number of rx frames in a device's incoming queue. |
| uint16 rx_depth; |
| /// Rx depth threshold at which the device should be fed new rx buffers. |
| /// |
| /// New buffer notifications from [`NetworkDeviceIfc`] may be skipped while the number of rx |
| /// buffers held by the implementation is larger than `rx_threshold`. It is invalid to provide a |
| /// value larger than `rx_depth`. `rx_threshold = rx_depth` is functionally equivalent to |
| /// `rx_threshold = rx_depth - 1`. |
| /// |
| /// A large value (close to `rx_depth`) may cause considerable CPU thrash for small rx |
| /// completion transaction sizes, while a small value may cause the implementation to be starved |
| /// of buffers. The typical choice of value is `rx_depth / 2`. |
| uint16 rx_threshold; |
| /// Maximum virtual discontiguous buffer parts accepted by the device. |
| /// |
| /// Devices that can't perform scatter-gather operations must set `max_buffer_parts` to 1. |
| /// |
| /// Must be in the range [1, `MAX_BUFFER_PARTS`]. |
| uint8 max_buffer_parts; |
| /// Maximum total length of buffers. May be set to zero for no maximum. |
| /// Devices that do not support scatter-gather DMA may set this to a value smaller than a |
| /// page size to guarantee compatibility. |
| /// Can't be larger than `MAX_PHYSICAL_PARTS` pages. |
| uint32 max_buffer_length; |
| /// Alignment requirement for buffers relative to the start of VMOs. |
| /// Must be greater than zero. |
| uint32 buffer_alignment; |
| /// The minimum rx buffer length for correct operation, in bytes. |
| uint32 min_rx_buffer_length; |
| /// The minimum tx buffer length for correct operation, in bytes. This length accounts only for |
| /// the buffer's body, and should not account for `tx_head_length` or `tx_tail_length`. |
| uint32 min_tx_buffer_length; |
| /// Number of bytes requested as header bytes on tx buffers. If set to zero, tx buffers will |
| /// never contain header space. Otherwise, tx buffers will start at the beginning of the header |
| /// space, and the header region will be informed. |
| uint16 tx_head_length; |
| /// Number of bytes requested as tail bytes on tx buffers. If set to zero, tx buffers will never |
| /// contain tail space. Otherwise, tx buffers will end at the end of the tail space, |
| /// and the tail region will be informed. |
| uint16 tx_tail_length; |
| /// Available Rx acceleration flags for this device, as defined in |
| /// [`fuchsia.hardware.network/RxAcceleration`]. |
| /// `rx_accel` maps the `RX_ACCEL_*` flags in the frame descriptors with semantic acceleration |
| /// features described by `RxAcceleration`. Position `n` of `rx_accel` conveys the meaning of |
| /// the `RX_ACCEL_n` flag. |
| vector<uint8>:MAX rx_accel; |
| /// Available tx acceleration flags for this device, as defined in |
| /// [`fuchsia.hardware.network/TxAcceleration`]. |
| /// `tx_accel` maps the `TX_ACCEL_*` flags in the frame descriptors with semantic acceleration |
| /// features described by `TxAcceleration`. Position `n` of `tx_accel` conveys the meaning of |
| /// the `TX_ACCEL_n` flag. |
| vector<uint8>:MAX tx_accel; |
| }; |
| |
| /// Dynamic port information. |
| /// `Status` reflects the operational status of a port, changes to status are reported through |
| /// [`fuchsia.hardware.network/NetworkDeviceIfc.StatusChanged`]. |
| struct PortStatus { |
| /// Port's maximum transmission unit, in bytes. |
| uint32 mtu; |
| /// Port status flags. |
| /// Status flags, as defined in [`fuchsia.hardware.network/Status`]. |
| uint32 flags; |
| }; |
| |
| [Transport = "Banjo", BanjoLayout = "ddk-protocol"] |
| protocol NetworkDeviceImpl { |
| /// Initializes the network device. |
| /// `Init` is only called once during the lifetime of the device to register `iface` as the |
| /// callback target for the Network Device implementation. |
| /// Upon initialization, the device is expected to be in a "Stopped" state, the `Start` method |
| /// will be called once the data path needs to be opened. |
| Init(NetworkDeviceIfc iface) -> (zx.status s); |
| /// Starts the device's data path. |
| /// `start` signals to the device implementation that it should bring up its data path and be |
| /// ready to receive tx frames and `iface` will start accepting rx frames. |
| /// The device is only considered started once `Start` returns. Until then, the contract |
| /// guarantees that no other data-path calls will be made to the device (`QueueTx`, |
| /// `RxAvailable`, `Stop`), implementers can safely assume or assert that this contract is |
| /// upheld. |
| [Async] |
| Start() -> (); |
| /// Stops the network device, data-path callbacks to the NetworkDeviceIfc are no longer allowed |
| /// after `Stop` returns. |
| /// Implementation may turn off the rx path on the underlying hardware upon receiving this call. |
| /// Upon completion, no more calls can be made to open data path sessions with |
| /// `NetworkDeviceIfc` (doing so is a logic error and may be protected by assertions) and all |
| /// the data buffers that were previously held by the implementation are automatically returned |
| /// back. Pending tx buffers will be returned with an incomplete error, and pending rx buffers |
| /// will be reclaimed. |
| [Async] |
| Stop() -> (); |
| /// Gets information about the device. Device information must not change over the course |
| /// of the lifetime of the device. |
| GetInfo() -> (DeviceInfo info); |
| /// Enqueues a list of buffers for transmission on the network device. |
| /// The driver takes ownership of the buffer and must complete the tx transaction by using |
| /// [`fuchsia.hardware.network.device/NetworkDeviceIfc.CompleteTx`] operations once it is done |
| /// with each buffer in `buffers`. |
| /// The total number of outstanding tx buffers given to a device will never exceed the reported |
| /// [`fuchsia.hardware.network.device/DeviceInfo.tx_depth`] value. Which also means that no more |
| /// than `tx_depth` buffers are going to be informed at once in a single call to `QueueTx`. |
| QueueTx(vector<TxBuffer>:MAX buffers); |
| /// Enqueues a list of rx buffer space on the network device. |
| /// The driver takes ownership of the buffer and must complete the transaction (once network |
| /// data arrives) using [`fuchsia.hardware.network.device/NetworkDeviceIfc.CompleteRx`]. |
| /// The total number of outstanding rx buffers given to a device will never exceed the reported |
| /// [`fuchsia.hardware.network.device/DeviceInfo.rx_depth`] value. Which also means that no more |
| /// than `rx_depth` buffers are going to be informed at once in a single call to `QueueRxSpace`. |
| QueueRxSpace(vector<RxSpaceBuffer>:MAX buffers); |
| /// Informs device that a new VMO is being used to store frame data. |
| /// Implementers must store the VMO handle referenced by `vmo_id` until |
| /// [`fuchsia.hardware.network.device/NetworkDeviceImpl.ReleaseVmo`] is called with the same |
| /// `vmo_id`. |
| PrepareVmo(uint8 vmo_id, zx.handle:VMO vmo); |
| /// Device may dispose of all references to the VMO referenced by vmo_id, no more buffers will |
| /// be sent with this identifier. |
| /// `ReleaseVmo` is guaranteed to only be called when the implementation holds no buffers |
| /// that reference that `vmo_id`. |
| ReleaseVmo(uint8 vmo_id); |
| /// Informs the device that it should start or stop reporting snooped transmit frames through |
| /// [`fuchsia.hardware.network.device/NetworkDeviceIfc.Snoop`]. |
| SetSnoop(bool snoop); |
| }; |