| // 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 `BLOCK_GROUP_ITEM`, 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 `BLOCK_GROUP_ITEM` flag is set for these messages. |
| /// 2) SEND a final Nth message with the same groupid. The `BLOCK_GROUP_ITEM |
| /// | BLOCK_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 `BLOCK_OP_READ` and `BLOCK_OP_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 |
| /// `BLOCK_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 `BLOCK_GROUP_ITEM` |
| /// that do not have `BLOCK_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; |
| |
| /// The Request ID allowing callers to correspond requests with responses. This |
| /// field is entirely for client-side bookkeeping; there is no obligation to |
| /// make request IDs unique. |
| alias Reqid = uint32; |
| alias Groupid = uint16; |
| |
| type BlockFifoRequest = struct { |
| opcode uint32; |
| /// Transmitted in the `block_fifo_response_t`. |
| reqid Reqid; |
| /// Only used if `opcode & BLOCK_GROUP_ITEM`. |
| group Groupid; |
| vmoid Vmoid; |
| length uint32; |
| vmo_offset uint64; |
| dev_offset uint64; |
| trace_flow_id uint64; |
| }; |
| |
| type BlockFifoResponse = struct { |
| status zx.Status; |
| reqid Reqid; |
| /// Only valid if transmitted in request. |
| group Groupid; |
| padding_to_satisfy_zerocopy uint16; |
| /// The number of messages in the transaction completed by the block server. |
| count uint32; |
| padding_to_match_request_size_and_alignment array<uint64, 3>; |
| }; |
| |
| /// `BLOCK_OP_READ`, `BLOCK_OP_WRITE` |
| // |
| // Note: this data structure is used for the driver <--> driver interfaces, |
| // while `BlockFifoRequest` above is part of a client <--> server interface and |
| // related to the block FIDL API. |
| type BlockReadWrite = resource struct { |
| /// Command and flags. |
| command uint32; |
| |
| /// 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; |
| }; |
| |
| /// `BLOCK_OP_TRIM` |
| type BlockTrim = struct { |
| /// Command and flags. |
| command uint32; |
| |
| /// 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 uint32; |
| |
| /// Read and Write ops use rw for parameters. |
| 2: rw BlockReadWrite; |
| 3: trim BlockTrim; |
| }; |
| |
| /// Performs a regular data read or write from the device. The operation may |
| /// be cached internally. |
| const BLOCK_OP_READ uint32 = 0x00000001; |
| const BLOCK_OP_WRITE uint32 = 0x00000002; |
| |
| /// Write any controller or device cached data to nonvolatile storage. |
| const BLOCK_OP_FLUSH uint32 = 0x00000003; |
| |
| /// 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. |
| const BLOCK_OP_TRIM uint32 = 0x00000004; |
| |
| /// Detaches the VMO from the block device. |
| const BLOCK_OP_CLOSE_VMO uint32 = 0x00000005; |
| |
| const BLOCK_OP_MASK uint32 = 0x000000FF; |
| |
| /// Mark this operation as "Force Unit Access" (FUA). For writes, this indicates that the data will |
| /// be written to the non-volatile medium, only after which the operation will complete. For reads, |
| /// any updated version of the requested data in the volatile cache will be written to the |
| /// non-volatile medium, and then read from the non-volatile medium. |
| const BLOCK_FL_FORCE_ACCESS uint32 = 0x00001000; |
| |
| /// 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. |
| const BLOCK_FL_PREFLUSH uint32 = 0x00002000; |
| |
| /// Associate the following request with `group`. |
| const BLOCK_GROUP_ITEM uint32 = 0x00000400; |
| |
| /// Only respond after this request (and all previous within group) have |
| /// completed. Only valid with `BLOCK_GROUP_ITEM`. |
| const BLOCK_GROUP_LAST uint32 = 0x00000800; |
| const BLOCK_FLAG_MASK uint32 = 0x0000FF00; |
| |
| 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. |
| 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 |
| Queue(resource struct { |
| @in_out |
| txn BlockOp; |
| }) -> (resource struct { |
| status zx.Status; |
| @mutable |
| op BlockOp; |
| }); |
| }; |
| |
| @transport("Banjo") |
| @banjo_layout("ddk-protocol") |
| protocol BlockImpl { |
| compose Common; |
| }; |
| |
| @transport("Banjo") |
| @banjo_layout("ddk-protocol") |
| protocol Block { |
| compose Common; |
| }; |