blob: 0b9cf72682641c77933126c4cc771d597fa6b02d [file] [log] [blame]
// 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.
library ddk.protocol.network.device;
using zx;
/// 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 [`ddk.protocol.network.device/NetworkDeviceIfc`], which is offered by the
/// middleware implementation.
///
/// A receive buffer flows from [`ddk.protocol.network.device/NetworkDeviceIfc`] into
/// [`ddk.protocol.network.device/NetworkDeviceImpl`] through the
/// [`ddk.protocol.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
/// [`ddk.protocol.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
/// [`ddk.protocol.network.device/RxSpaceBuffers`], making it an
/// [`ddk.protocol.network.device/NetworkDeviceImpl.RxBuffer`].
/// (4) `NetworkDeviceImpl` sends the fulfilled `RxBuffer` to `NetworkDeviceIfc` through
/// [`ddk.protocol.network.device/NetworkDeviceIfc.CompleteRx`], which, in turn, sends that data
/// over to applications.
///
/// A receive buffer flows from `NetworkDeviceIfc` into `NetworkDeviceImpl` through the
/// [`ddk.protocol.network.device/NetworkDeviceImpl.QueueTx`] method, and it's returned to
/// `NetworkDeviceIfc` as a [`ddk.protocol.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
/// [`ddk.protocol.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
/// [`ddk.protocol.network.device/NetworkDeviceIfc.CompleteTx`].
/// 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
/// [`ddk.protocol.network.device/NetworkDeviceIfc.Snoop`] interface method to expose any tx frames
/// they receive.
const uint32 FEATURE_NO_AUTO_SNOOP = 0x01;
/// Maximum number of [`ddk.protocol.network.device/BufferRegion`] parts in a
/// [`ddk.protocol.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.
/// Every vmo_id is guaranteed to exist 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;
/// A contiguous memory region in [`ddk.protocol.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
/// [`ddk.protocol.network.device/NetworkDeviceImpl.PrepareVmo`].
uint8 vmo_id;
/// Regions of VMO holding frame data.
vector<BufferRegion>:MAX_BUFFER_PARTS parts;
};
/// No extra frame metadata, [`ddk.protocol.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 [`ddk.protocol.network.device/BufferMetadata`].
union FrameInfo {
/// No extra frame metadata.
NoInfo no_info;
};
/// Metadata associated with a [`ddk.protocol.network.device/TxBuffer`] or
/// [`ddk.protocol.network.device/RxBuffer`].
struct BufferMetadata {
/// Extra frame metadata information. The type of the [`ddk.protocol.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 [`ddk.protocol.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 [`ddk.protocol.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 [`ddk.protocol.network.device/RxBuffer`] using
/// [`ddk.protocol.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 [`ddk.protocol.network.device/RxSpaceBuffer`] that
/// originated this `RxBuffer`.
uint32 id;
/// The total length of bytes written in the [`ddk.protocol.network.device/RxSpaceBuffer`]
/// referenced by `id`.
uint64 total_length;
/// Metadata associated with buffer.
BufferMetadata meta;
};
/// The result of a tx operation, reported to [`ddk.protocol.network.device/NetworkDeviceIfc`]
/// through [`ddk.protocol.network.device/NetworkDeviceIfc.CompleteTx`].
struct TxResult {
/// The buffer identifier informed in the [`ddk.protocol.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;
};
[Layout = "ddk-interface"]
protocol NetworkDeviceIfc {
/// Notifies interface of changes to device status.
StatusChanged(Status new_status);
/// Notifies interface of incoming rx data, contained in [`ddk.protocol.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
/// [`ddk.protocol.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> 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
/// [`ddk.protocol.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> tx);
/// Notifies interface of a snooped tx frame.
/// Typically used by implementations that have the
/// [`ddk.protocol.network.device/FEATURE_NO_AUTO_SNOOP`] bit set in
/// [`ddk.protocol.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 [`ddk.protocol.network.device/NetworkDeviceIfc.Snoop`]
/// ONLY if [`ddk.protocol.network.device/NetworkDeviceImpl.SetSnoop`] was set to `true` by the
/// interface, otherwise any buffers in `Snoop` will be ignored.
Snoop(vector<RxBuffer> 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 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;
/// 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> rx_types;
/// Supported tx frame types.
vector<TxSupport> tx_types;
/// 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> 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> tx_accel;
};
/// Dynamic device information.
/// `Status` reflects the operational status of a device, changes to status are reported through
/// `StatusChanged`.
struct Status {
/// Device's maximum transmission unit.
uint32 mtu;
/// Device status flags
/// Status flags, as defined in [`fuchsia.hardware.network/Status`].
uint32 flags;
};
[Layout = "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);
/// Gets operational status on the device.
/// Changes to operational status must be reported on
/// [`ddk.protocol.network.device/NetworkDeviceIfc.StatusChanged`]
GetStatus() -> (Status status);
/// 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
/// [`ddk.protocol.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
/// [`ddk.protocol.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> 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 [`ddk.protocol.network.device/NetworkDeviceIfc.CompleteRx`].
/// The total number of outstanding rx buffers given to a device will never exceed the reported
/// [`ddk.protocol.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> 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
/// [`ddk.protocol.network.device/NetworkDeviceImpl.ReleaseVmo`] is called with the same `vmo_id`.
PrepareVmo(uint8 vmo_id, 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
/// [`ddk.protocol.network.device/NetworkDeviceIfc.Snoop`].
SetSnoop(bool snoop);
};