blob: d8d832ab0667b1b9a68a66d0a1042eff10f2dda9 [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.
#pragma once
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/protocol/block.h>
#include <ddktl/device.h>
#include <ddktl/protocol/block.h>
#include <stdint.h>
namespace scsi {
enum class Opcode : uint8_t {
TEST_UNIT_READY = 0x00,
INQUIRY = 0x12,
MODE_SENSE_6 = 0x1A,
READ_16 = 0x88,
WRITE_16 = 0x8A,
READ_CAPACITY_16 = 0x9E,
};
// SCSI command structures (CDBs)
struct TestUnitReadyCDB {
Opcode opcode;
uint8_t reserved[4];
uint8_t control;
} __PACKED;
static_assert(sizeof(TestUnitReadyCDB) == 6, "TestUnitReady CDB must be 6 bytes");
struct InquiryCDB {
Opcode opcode;
// reserved_and_evpd(0) is 'Enable Vital Product Data'
uint8_t reserved_and_evpd;
uint8_t page_code;
// allocation_length is in network-byte-order.
uint16_t allocation_length;
uint8_t control;
} __PACKED;
static_assert(sizeof(InquiryCDB) == 6, "Inquiry CDB must be 6 bytes");
// Standard INQUIRY Data Header.
struct InquiryData {
// Peripheral Device Type Header and qualifier.
uint8_t peripheral_device_type;
// removable(7) is the 'Removable' bit.
uint8_t removable;
uint8_t version;
// response_data_format_and_control(3 downto 0) is Response Data Format
// response_data_format_and_control(4) is HiSup
// response_data_format_and_control(5) is NormACA
uint8_t response_data_format_and_control;
uint8_t additional_length;
// Various control bits, unused currently.
uint8_t control[3];
uint8_t t10_vendor_id[8];
uint8_t product_id[16];
uint8_t product_revision[4];
uint8_t drive_serial_number[8];
} __PACKED;
static_assert(offsetof(InquiryData, t10_vendor_id) == 8, "T10 Vendor ID is at offset 8");
static_assert(offsetof(InquiryData, product_id) == 16, "Product ID is at offset 16");
struct ModeSense6CDB {
Opcode opcode;
// If disable_block_descriptors(4) is '1', device will not return Block Descriptors.
uint8_t disable_block_descriptors;
// page_code(7 downto 6) is 'page control'. Should be 00h for current devices.
uint8_t page_code;
uint8_t subpage_code;
uint8_t allocation_length;
uint8_t control;
} __PACKED;
static_assert(sizeof(ModeSense6CDB) == 6, "Mode Sense 6 CDB must be 6 bytes");
struct ModeSense6ParameterHeader {
uint8_t mode_data_length;
// 00h is 'Direct Access Block Device'
uint8_t medium_type;
// For Direct Access Block Devices:
// device_specific_parameter(7) is write-protected bit
// device_specific_parameter(4) is disable page out/force unit access available
uint8_t device_specific_parameter;
uint8_t block_descriptor_length;
} __PACKED;
static_assert(sizeof(ModeSense6ParameterHeader) == 4, "Mode sense 6 parameters must be 4 bytes");
struct ReadCapacity16CDB {
Opcode opcode;
uint8_t service_action;
uint64_t reserved;
uint32_t allocation_length;
uint8_t pmi;
uint8_t control;
} __PACKED;
static_assert(sizeof(ReadCapacity16CDB) == 16, "Read Capacity 16 CDB must be 16 bytes");
struct ReadCapacity16ParameterData {
uint64_t returned_logical_block_address;
uint32_t block_length_in_bytes;
uint8_t prot_info;
uint8_t logical_blocks_exponent_info;
uint16_t lowest_aligned_logical_block;
uint8_t reserved[16];
} __PACKED;
static_assert(sizeof(ReadCapacity16ParameterData) == 32, "Read Capacity 16 Params are 32 bytes");
class Disk;
using DeviceType = ddk::Device<Disk, ddk::GetSizable, ddk::Unbindable>;
// |Disk| represents a single SCSI direct access block device.
// |Disk| bridges between the Zircon block protocol and SCSI commands/responses.
class Disk : public DeviceType, public ddk::BlockImplProtocol<Disk, ddk::base_protocol> {
public:
// Public so that we can use make_unique.
// Clients should use Disk::Create().
Disk(zx_device_t* parent, uint8_t target, uint16_t lun);
// Create a Disk at a specific target/lun.
static zx_status_t Create(zx_device_t* parent, uint8_t target, uint16_t lun);
const char* tag() const { return tag_; }
// DeviceType functions.
void DdkUnbind() { DdkRemove(); }
void DdkRelease() { delete this; }
// ddk::GetSizable functions.
zx_off_t DdkGetSize() { return 0; }
// ddk::BlockImplProtocol functions.
// TODO(ZX-2314): Implement these two functions.
void BlockImplQuery(block_info_t* info_out, size_t* block_op_size_out) {}
void BlockImplQueue(block_op_t* operation, block_impl_queue_callback completion_cb,
void* cookie) {
completion_cb(cookie, ZX_ERR_NOT_SUPPORTED, operation);
}
Disk(const Disk&) = delete;
Disk& operator=(const Disk&) = delete;
private:
zx_status_t Bind();
char tag_[24];
const uint8_t target_;
const uint16_t lun_;
};
} // namespace scsi