blob: a79d9708e71e9872078d7ade2b6b9702d8b4c929 [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.
/// 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
/// - 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 [`NetworkDeviceIfc`], which is offered by the middleware implementation.
///
/// A receive buffer flows from [`NetworkDeviceIfc`] into [`NetworkDeviceImpl`] through the
/// [`NetworkDeviceImpl.QueueRxSpace`] method, which gives access to receive buffers. The diagram
/// below illustrates the mechanism:
/// ```text
/// ++++++++++++++++++++++++++++ +++++++++++++++++++++++
/// | (1) | => RxSpaceBuffer => | (2) |
/// | NetworkDeviceIfc | | NetworkDeviceImpl |
/// | (4) | <= RxBuffer <= | (3) | <= Network data
/// ++++++++++++++++++++++++++++ +++++++++++++++++++++++
/// ```
///
/// - (1) `NetworkDeviceIfc` pushes available rx buffer space to `NetworkDeviceImpl` through
/// [`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
/// [`RxSpaceBuffers`], making it a [`NetworkDeviceImpl.RxBuffer`].
/// - (4) `NetworkDeviceImpl` sends the fulfilled `RxBuffer` to `NetworkDeviceIfc` through
/// [`NetworkDeviceIfc.CompleteRx`], which, in turn, sends that data over to applications.
///
/// A receive buffer flows from `NetworkDeviceIfc` into `NetworkDeviceImpl` through the
/// [`NetworkDeviceImpl.QueueTx`] method, and it's returned to [`NetworkDeviceIfc`] as a
/// [`TxResult`]. The diagram below illustrates the mechanism:
///
/// ```text
/// ++++++++++++++++++++++++++++ +++++++++++++++++++++++
/// | (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
/// [`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 [`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 [`NetworkDeviceIfc.Snoop`] interface method to
/// expose any tx frames they receive.
const FEATURE_NO_AUTO_SNOOP uint32 = 0x01;
/// Maximum number of disjoint parts a buffer may have.
// 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 MAX_BUFFER_PARTS uint32 = 4;
/// The maximum number of concurrent shared VMOs that may exist.
// 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 MAX_VMOS uint8 = 32;
/// The maximum number of ports a device can have at once.
// 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 MAX_PORTS uint8 = 32;
/// VMO identifier.
///
/// VMO identifiers are always in the range [0, [`MAX_VMOS`]).
///
/// VMO identifiers are reported to devices through [`NetworkDeviceImpl.PrepareVmo`].
alias vmo_id = uint8;
/// Buffer identifier.
///
/// Rx space buffers and tx buffers are identified by these when exchanged between interface and
/// implementation.
alias buffer_id = uint32;
/// Port identifier.
///
/// Port identifiers are always in the range [0, [`MAX_PORTS`]).
alias port_id = uint8;
/// A contiguous memory region in a VMO.
///
/// Note that a `BufferRegion` is only contiguous in terms of the VMO it references, it does not
/// necessarily translate into contiguous physical memory.
type BufferRegion = struct {
/// VMO backing this region.
vmo vmo_id;
/// Offset, in bytes, of data chunk in VMO.
offset uint64;
/// Length, in bytes, of data chunk in VMO.
length uint64;
};
/// No extra frame metadata, [`FrameInfo.info_type`] must be
/// [`fuchsia.hardware.network/InfoType.NO_INFO`].
type NoInfo = struct {
// TODO(brunodalbo) remove this dummy field once banjo supports it. Currently banjo uses empty
// structs to allow forward-declaration from FIDL.
nothing uint8;
};
/// Extra frame sidecar metadata stored in [`BufferMetadata`].
type FrameInfo = strict union {
/// No extra frame metadata.
1: no_info NoInfo;
};
/// Metadata associated with a [`TxBuffer`] or an [`RxBuffer`].
type BufferMetadata = struct {
/// Destination or source port identifier for this buffer.
port port_id;
/// Extra frame metadata information. The type of the
/// [`FrameInfo`] union is defined by the value in `info_type`.
info FrameInfo;
/// Type of data in `info`, as defined in [`fuchsia.hardware.network/InfoType`].
info_type uint32;
/// Frame tx or rx flags, as defined in [`fuchsia.hardware.network/RxFlags`],
/// [`fuchsia.hardware.network/TxFlags`], and [`fuchsia.hardware.network/TxReturnFlags`].
flags uint32;
/// Type of frame contained in this buffer, as defined in
/// [`fuchsia.hardware.network/FrameType`].
frame_type uint8;
};
/// A transmit buffer containing a single frame.
type TxBuffer = struct {
/// Unique buffer identifier.
id buffer_id;
/// Regions of VMO holding frame data.
data vector<BufferRegion>:MAX_BUFFER_PARTS;
/// Metadata associated with this buffer.
meta BufferMetadata;
/// Length of header bytes in the data contained in this buffer. Will always be either 0 or the
/// requested [`DeviceInfo.tx_head_length`] value. The head bytes are always at the beginning of
/// the buffer.
head_length uint16;
/// Length of tail bytes in the data contained in this buffer. Will always be either 0 or the
/// requested [`DeviceInfo.tx_tail_length`] value. The tail bytes are always at the end of the
/// buffer.
tail_length uint16;
};
/// A buffer with allocated space to receive frames in. An `RxSpaceBuffer` must always be returned
/// as an [`RxBufferPart`] within an [`RxBuffer`].
type RxSpaceBuffer = struct {
/// Unique buffer identifier.
id buffer_id;
/// VMO region where buffer space is located.
region BufferRegion;
};
/// A single contiguous part of an [`RxBuffer`], created from an [`RxSpaceBuffer`].
type RxBufferPart = struct {
/// The buffer identifier informed in the [`RxSpaceBuffer`] that originated this `RxBuffer`.
id buffer_id;
/// Offset in bytes from [`RxSpaceBuffer`]'s start where inbound data begins.
///
/// This is a relative offset within the region defined by the originating space, not an
/// absolute offset in the space's VMO.
offset uint32;
/// The total length in bytes written in the [`RxSpaceBuffer`] referenced by `id`, excluding any
/// `offset` bytes.
length uint32;
};
/// A buffer containing a single frame received by the device.
type RxBuffer = struct {
/// Metadata associated with buffer.
meta BufferMetadata;
/// Fulfilled rx buffer space comprising this frame.
///
/// Must have at least one part.
data vector<RxBufferPart>:MAX_BUFFER_PARTS;
};
/// The result of a tx operation, reported to [`NetworkDeviceIfc`] through
/// [`NetworkDeviceIfc.CompleteTx`].
type TxResult = struct {
/// The buffer identifier informed in the [`TxBuffer`] that originated this `TxResult`.
id buffer_id;
/// The result status to report.
///
/// Error results map to their equivalents in [`fuchsia.hardware.network/TxReturnFlags`].
///
/// - `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) or stopped.
/// - `ZX_ERR_INTERNAL` or any other unlisted errors will be reported as a generic
/// [`fuchsia.hardware.network/TxReturnFlags.TX_RET_ERROR`].
status zx.status;
};
@transport("Banjo")
@banjo_layout("ddk-interface")
protocol NetworkPort {
/// Gets information about the port.
///
/// Port information must not change over the port's lifetime.
///
/// - response `info` port information.
GetInfo() -> (struct {
info PortInfo;
});
/// Gets operational status of the port.
///
/// Changes to operational status must be reported via [`NetworkDeviceIfc.StatusChanged`]
///
/// - response `status` snapshot of port's operational status.
GetStatus() -> (struct {
status PortStatus;
});
/// 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.
///
/// + request `active` `true` if port has sessions attached to it, `false` otherwise.
SetActive(struct {
active bool;
});
/// 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.
///
/// - response `mac_ifc` mac addressing handle.
// TODO(https://fxbug.dev/67196): Make this an optional interface return once this is FIDL.
GetMac() -> (resource struct {
mac_ifc client_end:fuchsia.hardware.network.mac.MacAddr;
});
/// 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")
@banjo_layout("ddk-interface")
protocol NetworkDeviceIfc {
/// Notifies the interface of status changes on port with `id`.
///
/// Port status changes must always be notified through `StatusChanged`. The interface will not
/// poll ports for status via [`NetworkPort.GetStatus`].
///
/// + request `id` port identifier.
/// + request `new_status` new port's status.
PortStatusChanged(struct {
id port_id;
new_status PortStatus;
});
/// Instantiates a new port with `id`.
///
/// `id` must not be currently in use by any other ports. `id`s may be reused once the provided
/// port is destroyed by [`NetworkPort.Removed`].
///
/// 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 [`MAX_PORTS`] is not exceeded.
///
/// + request `id` new port identifier.
/// + request `port` handle to network port implementation.
AddPort(resource struct {
id port_id;
port client_end:NetworkPort;
});
/// Destroys port with `id`.
///
/// NOTE: Resources associated with the port must not be freed until [`NetworkPort.Removed`] is
/// called.
///
/// + request `id` removed port identifier.
RemovePort(struct {
id port_id;
});
/// Notifies interface of incoming rx data, contained in [`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 [`DeviceInfo.rx_depth`] reported by the
/// `NetworkDeviceImpl` that is returning the buffers.
///
/// Buffers with zero length are considered "unfulfilled". They're not reported to any sessions
/// and the buffer space comprising them may be reused. Devices should return any outstanding
/// buffer space as unfulfilled on stop. See [`NetworkDeviceImpl.Stop`].
///
/// By calling `CompleteRx` the caller relinquishes ownership of all buffers that are being
/// marked as complete.
///
/// + request `rx` buffers containing incoming data.
CompleteRx(struct {
rx vector<RxBuffer>:MAX;
});
/// 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 [`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.
///
/// + request `tx` transmit results.
CompleteTx(struct {
tx vector<TxResult>:MAX;
});
/// Notifies interface of a snooped tx frame.
///
/// Typically used by implementations that have the [`FEATURE_NO_AUTO_SNOOP`] bit set in
/// [`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 [`NetworkDeviceIfc.Snoop`] ONLY if
/// [`NetworkDeviceImpl.SetSnoop`] was set to `true` by the interface, otherwise any buffers in
/// `Snoop` will be ignored.
///
/// + request `rx` snooped transmit frames to be looped back as incoming data.
Snoop(struct {
rx vector<RxBuffer>:MAX;
});
};
/// 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.
type TxSupport = struct {
/// The frame type this support entry refers to.
type uint8;
/// The frame type-specific features supported.
features uint32;
/// The flags supported for the given frame type.
supported_flags uint32;
};
/// Static port information.
type PortInfo = struct {
/// Port class, as defined in [`fuchsia.hardware.network/PortClass`].
port_class uint8;
/// Supported rx frame types, as defined by [`fuchsia.hardware.network/FrameType`].
rx_types vector<uint8>:MAX;
/// Supported tx frame types.
tx_types vector<TxSupport>:MAX;
};
/// Static device information.
/// `DeviceInfo` must not change for the entire lifetime of a device.
type DeviceInfo = struct {
/// Device features
device_features uint32;
/// Maximum depth of tx frames in device's outgoing queue.
tx_depth uint16;
/// Maximum number of rx frames in a device's incoming queue.
rx_depth uint16;
/// 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`.
rx_threshold uint16;
/// 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`].
max_buffer_parts uint8;
/// 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.
max_buffer_length uint32;
/// Alignment requirement for buffers relative to the start of VMOs.
///
/// Must be greater than zero.
buffer_alignment uint32;
/// The minimum rx buffer length for correct operation, in bytes.
min_rx_buffer_length uint32;
/// 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`.
min_tx_buffer_length uint32;
/// 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.
tx_head_length uint16;
/// 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.
tx_tail_length uint16;
/// 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.
rx_accel vector<uint8>:MAX;
/// 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.
tx_accel vector<uint8>:MAX;
};
/// Dynamic port information.
///
/// `PortStatus` reflects the operational status of a port, changes to status are reported through
/// [`NetworkDeviceIfc.StatusChanged`].
type PortStatus = struct {
/// Port's maximum transmission unit, in bytes.
mtu uint32;
/// Port status flags.
///
/// Status flags, as defined in [`fuchsia.hardware.network/Status`].
flags uint32;
};
@transport("Banjo")
@banjo_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 the "Stopped" state, the `Start` method
/// will be called once the data path needs to be opened.
///
/// + request `iface` handle to the device interface.
/// - response `s` initialization status. A value other than `ZX_OK` will cause the device
/// to unbind.
Init(resource struct {
iface client_end:NetworkDeviceIfc;
}) -> (struct {
s zx.status;
});
/// 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.
///
/// The device implementation must return all outstanding Tx and Rx buffers upon receiving this
/// call. Any new buffers received in the stopped state must be returned with an appropriate
/// error (tx) or unfulfilled (rx). See [`NetworkDeviceIfc.CompleteTx`] and
/// [`NetworkDeviceIfc.CompleteRx`] for details.
///
/// The device implementation may perform any power saving measures after observing stop.
@async
Stop() -> ();
/// Gets information about the device.
///
/// Device information must not change over the course of the lifetime of the device.
///
/// - response `info` device information.
GetInfo() -> (struct {
info DeviceInfo;
});
/// 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
/// [`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
/// [`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`.
///
/// Buffers enqueued while the device in is the stopped state must be returned with an
/// appropriate error. See [`TxResult.status`] for specific
/// error codes.
///
/// + request `buffers` tx buffers to enqueue for sending.
QueueTx(struct {
buffers vector<TxBuffer>:MAX;
});
/// 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 [`NetworkDeviceIfc.CompleteRx`].
///
/// The total number of outstanding rx buffers given to a device will never exceed the reported
/// [`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`.
///
/// Buffers enqueued while the device in is the stopped state must be returned with a zero
/// length.
///
/// + request `buffers` rx space buffers to be filled with network data when it arrives.
QueueRxSpace(struct {
buffers vector<RxSpaceBuffer>:MAX;
});
/// Informs device that a new VMO is being used to store frame data.
///
/// Implementers must store the VMO handle referenced by `id` until
/// [`NetworkDeviceImpl.ReleaseVmo`] is called with the same `id`.
///
/// + request `id` identifier used to reference this VMO.
/// + request `vmo` VMO where frame data will be stored.
PrepareVmo(resource struct {
id vmo_id;
vmo zx.handle:VMO;
});
/// Device may dispose of all references to the VMO referenced by `id`.
///
/// No more buffers will be sent with this `id`.
///
/// `ReleaseVmo` is guaranteed to only be called when the implementation holds no buffers that
/// reference that `id`.
///
/// + request `id` VMO identifier.
ReleaseVmo(struct {
id vmo_id;
});
/// Informs the device that it should start or stop reporting snooped transmit frames through
/// [`NetworkDeviceIfc.Snoop`].
///
/// + request `snoop` if `true` device should start reporting snooping frames. If `false` device
/// should stop reporting snooped frames. Upon initialization, device assumes `snop` is `false`.
SetSnoop(struct {
snoop bool;
});
};