blob: 9860da8672f74d53e2e302fb577aa5139928665e [file] [log] [blame]
// Copyright 2018 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 fuchsia.hardware.block.driver;
using fuchsia.hardware.block;
using zx;
alias Vmoid = uint16;
// Dummy vmoid value reserved for "invalid". Will never be allocated; can be
// used as a local value for unallocated / freed ID.
const BLOCK_VMOID_INVALID Vmoid = 0;
const BLOCK_GUID_LEN uint32 = 16;
/// Multiple block I/O operations may be sent at once before a response is
/// actually sent back. Block I/O ops may be sent concurrently to different
/// vmoids, and they also may be sent to different groups at any point in time.
///
/// `MAX_TXN_GROUP_COUNT` "groups" are pre-allocated lanes separated on the
/// block server. Using a group allows multiple message to be buffered at once
/// on a single communication channel before receiving a response.
///
/// Usage of groups is identified by the `GROUP_ITEM` flag, and is optional.
///
/// These groups may be referred to with a "groupid", in the range [0,
/// `MAX_TXN_GROUP_COUNT`).
///
/// The protocol to communicate with a single group is as follows:
/// 1) SEND [N - 1] messages with an allocated groupid for any value of 1 <= N.
/// The `GROUP_ITEM` flag is set for these messages.
/// 2) SEND a final Nth message with the same groupid. The `GROUP_ITEM
/// | GROUP_LAST` flags are set for this message.
/// 3) RECEIVE a single response from the Block I/O server after all N requests
/// have completed. This response is sent once all operations either complete
/// or a single operation fails. At this point, step (1) may begin again for
/// the same groupid.
///
/// For `READ` and `WRITE`, N may be greater than 1. Otherwise,
/// N == 1 (skipping step (1) in the protocol above).
///
/// Notes:
/// - groupids may operate on any number of vmoids at once.
/// - If additional requests are sent on the same groupid before step (3) has
/// completed, then the additional request will not be processed. If
/// `GROUP_LAST` is set, an error will be returned. Otherwise, the
/// request will be silently dropped.
/// - Messages within a group are not guaranteed to be processed in any order
/// relative to each other.
/// - All requests receive responses, except for ones with `GROUP_ITEM`
/// that do not have `GROUP_LAST` set.
///
/// For example, the following is a valid sequence of transactions:
///
/// -> (groupid = 1, vmoid = 1, OP = Write | GroupItem, reqid = 1)
/// -> (groupid = 1, vmoid = 2, OP = Write | GroupItem, reqid = 2)
/// -> (groupid = 2, vmoid = 3, OP = Write | GroupItem | GroupLast, reqid = 0)
/// <- Response sent to groupid = 2, reqid = 0
/// -> (groupid = 1, vmoid = 1, OP = Read | GroupItem | GroupLast, reqid = 3)
/// <- Response sent to groupid = 1, reqid = 3
/// -> (groupid = 3, vmoid = 1, OP = Write | GroupItem, reqid = 4)
/// -> (groupid = don't care, vmoid = 1, OP = Read, reqid = 5)
/// <- Response sent to reqid = 5
/// -> (groupid = 3, vmoid = 1, OP = Read | GroupItem | GroupLast, reqid = 6)
/// <- Response sent to groupid = 3, reqid = 6
///
/// Each transaction reads or writes up to `length` blocks from the device,
/// starting at `dev_offset` blocks, into the VMO associated with `vmoid`,
/// starting at `vmo_offset` blocks. If the transaction is out of range, for
/// example if `length` is too large or if `dev_offset` is beyond the end of the
/// device, `ZX_ERR_OUT_OF_RANGE` is returned.
const MAX_TXN_GROUP_COUNT uint32 = 8;
/// `READ`, `WRITE`
//
// Note: this data structure is used for the driver <--> driver interfaces,
// while `BlockFifoRequest` is part of a client <--> server interface and
// related to the block FIDL API.
//
// See src/storage/lib/block_client/cpp/block_fifo.h for FIFO protocol.
//
type BlockReadWrite = resource struct {
/// Opcode and flags.
command BlockCommand;
/// Available for temporary use.
extra uint32;
/// VMO of data to read or write.
vmo zx.Handle:VMO;
/// Transfer length in blocks (0 is invalid).
length uint32;
/// Device offset in blocks.
offset_dev uint64;
/// VMO offset in blocks.
offset_vmo uint64;
};
/// `TRIM`
type BlockTrim = struct {
/// Opcode and flags.
command BlockCommand;
/// Transfer length in blocks (0 is invalid).
length uint32;
/// Device offset in blocks.
offset_dev uint64;
};
type BlockOp = strict resource union {
/// All Commands
1: command BlockCommand;
/// Read and Write ops use rw for parameters.
2: rw BlockReadWrite;
3: trim BlockTrim;
};
type BlockCommand = struct {
opcode @generated_name("BlockOpcode") strict enum : uint8 {
/// Performs a regular data read or write from the device. The operation may
/// be cached internally.
READ = 1;
WRITE = 2;
/// Write any controller or device cached data to nonvolatile storage.
FLUSH = 3;
/// Instructs the device to invalidate a number of blocks, making them usable
/// for storing something else. This is basically a "delete" optimization,
/// where the device is in charge of discarding the old content without
/// clients having to write a given pattern. The operation may be cached
/// internally.
TRIM = 4;
/// Detaches the VMO from the block device.
CLOSE_VMO = 5;
};
flags @generated_name("BlockIoFlag") strict bits : uint32 {
/// Associate the following request with `group`.
GROUP_ITEM = 0x00000001;
/// Only respond after this request (and all previous within group) have
/// completed. Only valid with `GROUP_ITEM`.
GROUP_LAST = 0x00000002;
/// Mark this operation as "Force Unit Access" (FUA), indicating that
/// it should not complete until the data is written to the non-volatile
/// medium (write), and that reads should bypass any on-device caches.
FORCE_ACCESS = 0x00000004;
/// The pre-flush flag generates a flush operation before running this operation
/// and flushes on-device caches. It ensures that previous data is written to
/// the non-volatile medium. Applying the preflush to an operation in a group
/// will only apply to the operation with the flag and not all the other
/// operations in the group. The preflush only applies to an operation where the
/// preflush flag is set. It does not affect other operations and can proceed in
/// parallel. If the flush operation fails, the operation will not proceed.
PREFLUSH = 0x00000008;
};
};
closed protocol Common {
/// Obtains the parameters of the block device (`block_info_t`) and the
/// required size of `block_txn_t`. The `block_txn_t`s submitted via
/// `Queue()` must have `block_op_size_out - sizeof(block_op_t)` bytes
/// available at the end of the structure for the use of the driver.
strict Query() -> (struct {
info fuchsia.hardware.block.BlockInfo;
block_op_size uint64;
});
/// Submits an I/O request for processing. Ownership of `op` is transferred
/// to callee until `completion_cb` is invoked. Success or failure will be
/// reported via the `completion_cb`. This callback may be called before the
/// `Queue()` method returns.
@async
strict Queue(resource struct {
@in_out
txn BlockOp;
}) -> (resource struct {
status zx.Status;
@mutable
op BlockOp;
});
};
@transport("Banjo")
@banjo_layout("ddk-protocol")
closed protocol BlockImpl {
compose Common;
};
@transport("Banjo")
@banjo_layout("ddk-protocol")
closed protocol Block {
compose Common;
};