blob: abde13e76a1c4a0ccd6f8830d6bf8c00b1d3d238 [file] [log] [blame]
// Copyright 2016 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.
#pragma once
#include "device.h"
#include "ring.h"
#include <zircon/compiler.h>
#include <stdlib.h>
#include <ddk/protocol/block.h>
#include <virtio/block.h>
namespace virtio {
class Ring;
class BlockDevice : public Device {
public:
BlockDevice(zx_device_t* device);
virtual ~BlockDevice();
virtual zx_status_t Init();
virtual void IrqRingUpdate();
virtual void IrqConfigChange();
uint64_t GetSize() const { return config_.capacity * config_.blk_size; }
uint32_t GetBlockSize() const { return config_.blk_size; }
uint64_t GetBlockCount() const { return config_.capacity; }
private:
// DDK driver hooks
static void virtio_block_iotxn_queue(void* ctx, iotxn_t* txn);
static zx_off_t virtio_block_get_size(void* ctx);
static zx_status_t virtio_block_ioctl(void* ctx, uint32_t op, const void* in_buf, size_t in_len,
void* out_buf, size_t out_len, size_t* out_actual);
static void virtio_block_set_callbacks(void* ctx, block_callbacks_t* cb);
static void virtio_block_get_info(void* ctx, block_info_t* info);
static void virtio_block_complete(iotxn_t* txn, void* cookie);
static void virtio_block_read(void* ctx, zx_handle_t vmo,
uint64_t length, uint64_t vmo_offset,
uint64_t dev_offset, void* cookie);
static void virtio_block_write(void* ctx, zx_handle_t vmo,
uint64_t length, uint64_t vmo_offset,
uint64_t dev_offset, void* cookie);
static void block_do_txn(BlockDevice* dev, uint32_t opcode, zx_handle_t vmo,
uint64_t length, uint64_t vmo_offset,
uint64_t dev_offset, void* cookie);
void GetInfo(block_info_t* info);
void QueueReadWriteTxn(iotxn_t* txn);
// the main virtio ring
Ring vring_ = {this};
static const uint16_t ring_size = 128; // 128 matches legacy pci
// saved block device configuration out of the pci config BAR
virtio_blk_config_t config_ = {};
// a queue of block request/responses
static const size_t blk_req_count = 32;
zx_paddr_t blk_req_pa_ = 0;
virtio_blk_req_t* blk_req_ = nullptr;
zx_paddr_t blk_res_pa_ = 0;
uint8_t* blk_res_ = nullptr;
uint32_t blk_req_bitmap_ = 0;
static_assert(blk_req_count <= sizeof(blk_req_bitmap_) * CHAR_BIT, "");
size_t alloc_blk_req() {
size_t i = 0;
if (blk_req_bitmap_ != 0)
i = sizeof(blk_req_bitmap_) * CHAR_BIT - __builtin_clz(blk_req_bitmap_);
blk_req_bitmap_ |= (1 << i);
return i;
}
void free_blk_req(size_t i) {
blk_req_bitmap_ &= ~(1 << i);
}
// Callbacks for PROTOCOL_BLOCK
block_callbacks_t* callbacks_;
block_protocol_ops_t device_block_ops_;
// pending iotxns
list_node iotxn_list = LIST_INITIAL_VALUE(iotxn_list);
};
} // namespace virtio