mirror of
https://github.com/akuker/RASCSI.git
synced 2026-04-20 11:17:58 +00:00
Move C++ code into cpp/ dir (#941)
- Moved C++ code to cpp/ from src/raspberrypi - Related updates to Makefile, easyinstall.sh, and the github build rules - Removed the native X68k C code in src/x68k from the repo
This commit is contained in:
@@ -0,0 +1,735 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// X68000 EMULATOR "XM6"
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
//
|
||||
// XM6i
|
||||
// Copyright (C) 2010-2015 isaki@NetBSD.org
|
||||
// Copyright (C) 2010 Y.Sugahara
|
||||
//
|
||||
// Imported sava's Anex86/T98Next image and MO format support patch.
|
||||
// Comments translated to english by akuker.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "dispatcher.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include "disk.h"
|
||||
|
||||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
const unordered_map<uint32_t, uint32_t> Disk::shift_counts = { { 512, 9 }, { 1024, 10 }, { 2048, 11 }, { 4096, 12 } };
|
||||
|
||||
Disk::Disk(PbDeviceType type, int lun) : StorageDevice(type, lun)
|
||||
{
|
||||
// REZERO implementation is identical with Seek
|
||||
dispatcher.Add(scsi_command::eCmdRezero, "Rezero", &Disk::Seek);
|
||||
dispatcher.Add(scsi_command::eCmdFormat, "FormatUnit", &Disk::FormatUnit);
|
||||
// REASSIGN BLOCKS implementation is identical with Seek
|
||||
dispatcher.Add(scsi_command::eCmdReassign, "ReassignBlocks", &Disk::Seek);
|
||||
dispatcher.Add(scsi_command::eCmdRead6, "Read6", &Disk::Read6);
|
||||
dispatcher.Add(scsi_command::eCmdWrite6, "Write6", &Disk::Write6);
|
||||
dispatcher.Add(scsi_command::eCmdSeek6, "Seek6", &Disk::Seek6);
|
||||
dispatcher.Add(scsi_command::eCmdStartStop, "StartStopUnit", &Disk::StartStopUnit);
|
||||
dispatcher.Add(scsi_command::eCmdRemoval, "PreventAllowMediumRemoval", &Disk::PreventAllowMediumRemoval);
|
||||
dispatcher.Add(scsi_command::eCmdReadCapacity10, "ReadCapacity10", &Disk::ReadCapacity10);
|
||||
dispatcher.Add(scsi_command::eCmdRead10, "Read10", &Disk::Read10);
|
||||
dispatcher.Add(scsi_command::eCmdWrite10, "Write10", &Disk::Write10);
|
||||
dispatcher.Add(scsi_command::eCmdReadLong10, "ReadLong10", &Disk::ReadWriteLong10);
|
||||
dispatcher.Add(scsi_command::eCmdWriteLong10, "WriteLong10", &Disk::ReadWriteLong10);
|
||||
dispatcher.Add(scsi_command::eCmdWriteLong16, "WriteLong16", &Disk::ReadWriteLong16);
|
||||
dispatcher.Add(scsi_command::eCmdSeek10, "Seek10", &Disk::Seek10);
|
||||
dispatcher.Add(scsi_command::eCmdVerify10, "Verify10", &Disk::Verify10);
|
||||
dispatcher.Add(scsi_command::eCmdSynchronizeCache10, "SynchronizeCache10", &Disk::SynchronizeCache);
|
||||
dispatcher.Add(scsi_command::eCmdSynchronizeCache16, "SynchronizeCache16", &Disk::SynchronizeCache);
|
||||
dispatcher.Add(scsi_command::eCmdReadDefectData10, "ReadDefectData10", &Disk::ReadDefectData10);
|
||||
dispatcher.Add(scsi_command::eCmdRead16, "Read16", &Disk::Read16);
|
||||
dispatcher.Add(scsi_command::eCmdWrite16, "Write16", &Disk::Write16);
|
||||
dispatcher.Add(scsi_command::eCmdVerify16, "Verify16", &Disk::Verify16);
|
||||
dispatcher.Add(scsi_command::eCmdReadCapacity16_ReadLong16, "ReadCapacity16/ReadLong16", &Disk::ReadCapacity16_ReadLong16);
|
||||
}
|
||||
|
||||
Disk::~Disk()
|
||||
{
|
||||
// Save disk cache, only if ready
|
||||
if (IsReady() && cache != nullptr) {
|
||||
cache->Save();
|
||||
}
|
||||
}
|
||||
|
||||
bool Disk::Dispatch(scsi_command cmd)
|
||||
{
|
||||
// Media changes must be reported on the next access, i.e. not only for TEST UNIT READY
|
||||
if (IsMediumChanged()) {
|
||||
assert(IsRemovable());
|
||||
|
||||
SetMediumChanged(false);
|
||||
|
||||
throw scsi_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
|
||||
}
|
||||
|
||||
// The superclass handles the less specific commands
|
||||
return dispatcher.Dispatch(this, cmd) ? true : super::Dispatch(cmd);
|
||||
}
|
||||
|
||||
void Disk::SetUpCache(off_t image_offset, bool raw)
|
||||
{
|
||||
cache = make_unique<DiskCache>(GetFilename(), size_shift_count, (uint32_t)GetBlockCount(), image_offset);
|
||||
cache->SetRawMode(raw);
|
||||
}
|
||||
|
||||
void Disk::ResizeCache(const string& path, bool raw)
|
||||
{
|
||||
cache.reset(new DiskCache(path, size_shift_count, (uint32_t)GetBlockCount()));
|
||||
cache->SetRawMode(raw);
|
||||
}
|
||||
|
||||
void Disk::FlushCache()
|
||||
{
|
||||
if (cache != nullptr) {
|
||||
cache->Save();
|
||||
}
|
||||
}
|
||||
|
||||
void Disk::FormatUnit()
|
||||
{
|
||||
CheckReady();
|
||||
|
||||
// FMTDATA=1 is not supported (but OK if there is no DEFECT LIST)
|
||||
if ((ctrl->cmd[1] & 0x10) != 0 && ctrl->cmd[4] != 0) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::Read(access_mode mode)
|
||||
{
|
||||
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
|
||||
ctrl->length = Read(ctrl->cmd, controller->GetBuffer(), start);
|
||||
LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, controller->GetLength())
|
||||
|
||||
// Set next block
|
||||
ctrl->next = start + 1;
|
||||
|
||||
EnterDataInPhase();
|
||||
}
|
||||
else {
|
||||
EnterStatusPhase();
|
||||
}
|
||||
}
|
||||
|
||||
void Disk::ReadWriteLong10()
|
||||
{
|
||||
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
|
||||
if (GetInt16(ctrl->cmd, 7) != 0) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
ValidateBlockAddress(RW10);
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::ReadWriteLong16()
|
||||
{
|
||||
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
|
||||
if (GetInt16(ctrl->cmd, 12) != 0) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
ValidateBlockAddress(RW16);
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::Write(access_mode mode)
|
||||
{
|
||||
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
|
||||
ctrl->length = WriteCheck(start);
|
||||
|
||||
// Set next block
|
||||
ctrl->next = start + 1;
|
||||
|
||||
EnterDataOutPhase();
|
||||
}
|
||||
else {
|
||||
EnterStatusPhase();
|
||||
}
|
||||
}
|
||||
|
||||
void Disk::Verify(access_mode mode)
|
||||
{
|
||||
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
|
||||
// if BytChk=0
|
||||
if ((ctrl->cmd[1] & 0x02) == 0) {
|
||||
Seek();
|
||||
return;
|
||||
}
|
||||
|
||||
// Test reading
|
||||
ctrl->length = Read(ctrl->cmd, controller->GetBuffer(), start);
|
||||
|
||||
// Set next block
|
||||
ctrl->next = start + 1;
|
||||
|
||||
EnterDataOutPhase();
|
||||
}
|
||||
else {
|
||||
EnterStatusPhase();
|
||||
}
|
||||
}
|
||||
|
||||
void Disk::StartStopUnit()
|
||||
{
|
||||
const bool start = ctrl->cmd[4] & 0x01;
|
||||
const bool load = ctrl->cmd[4] & 0x02;
|
||||
|
||||
if (load) {
|
||||
LOGTRACE(start ? "Loading medium" : "Ejecting medium")
|
||||
}
|
||||
else {
|
||||
LOGTRACE(start ? "Starting unit" : "Stopping unit")
|
||||
|
||||
SetStopped(!start);
|
||||
}
|
||||
|
||||
if (!start) {
|
||||
// Look at the eject bit and eject if necessary
|
||||
if (load) {
|
||||
if (IsLocked()) {
|
||||
// Cannot be ejected because it is locked
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
|
||||
}
|
||||
|
||||
// Eject
|
||||
if (!Eject(false)) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
|
||||
}
|
||||
}
|
||||
else {
|
||||
FlushCache();
|
||||
}
|
||||
}
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::PreventAllowMediumRemoval()
|
||||
{
|
||||
CheckReady();
|
||||
|
||||
const bool lock = ctrl->cmd[4] & 0x01;
|
||||
|
||||
LOGTRACE(lock ? "Locking medium" : "Unlocking medium")
|
||||
|
||||
SetLocked(lock);
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::SynchronizeCache()
|
||||
{
|
||||
FlushCache();
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::ReadDefectData10()
|
||||
{
|
||||
const size_t allocation_length = min((size_t)GetInt16(ctrl->cmd, 7), (size_t)4);
|
||||
|
||||
// The defect list is empty
|
||||
fill_n(controller->GetBuffer().begin(), allocation_length, 0);
|
||||
ctrl->length = (int)allocation_length;
|
||||
|
||||
EnterDataInPhase();
|
||||
}
|
||||
|
||||
bool Disk::Eject(bool force)
|
||||
{
|
||||
const bool status = super::Eject(force);
|
||||
if (status) {
|
||||
FlushCache();
|
||||
cache.reset();
|
||||
|
||||
// The image file for this drive is not in use anymore
|
||||
UnreserveFile();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf) const
|
||||
{
|
||||
// Get length, clear buffer
|
||||
const auto length = (int)min(buf.size(), (size_t)cdb[4]);
|
||||
fill_n(buf.begin(), length, 0);
|
||||
|
||||
// DEVICE SPECIFIC PARAMETER
|
||||
if (IsProtected()) {
|
||||
buf[2] = 0x80;
|
||||
}
|
||||
|
||||
// Basic information
|
||||
int size = 4;
|
||||
|
||||
// Add block descriptor if DBD is 0
|
||||
if ((cdb[1] & 0x08) == 0) {
|
||||
// Mode parameter header, block descriptor length
|
||||
buf[3] = 0x08;
|
||||
|
||||
// Only if ready
|
||||
if (IsReady()) {
|
||||
// Short LBA mode parameter block descriptor (number of blocks and block length)
|
||||
SetInt32(buf, 4, (uint32_t)GetBlockCount());
|
||||
SetInt32(buf, 8, GetSectorSizeInBytes());
|
||||
}
|
||||
|
||||
size = 12;
|
||||
}
|
||||
|
||||
size = AddModePages(cdb, buf, size, length, 255);
|
||||
|
||||
buf[0] = (BYTE)size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf) const
|
||||
{
|
||||
// Get length, clear buffer
|
||||
const auto length = (int)min(buf.size(), (size_t)GetInt16(cdb, 7));
|
||||
fill_n(buf.begin(), length, 0);
|
||||
|
||||
// DEVICE SPECIFIC PARAMETER
|
||||
if (IsProtected()) {
|
||||
buf[3] = 0x80;
|
||||
}
|
||||
|
||||
// Basic Information
|
||||
int size = 8;
|
||||
|
||||
// Add block descriptor if DBD is 0, only if ready
|
||||
if ((cdb[1] & 0x08) == 0 && IsReady()) {
|
||||
uint64_t disk_blocks = GetBlockCount();
|
||||
uint32_t disk_size = GetSectorSizeInBytes();
|
||||
|
||||
// Check LLBAA for short or long block descriptor
|
||||
if ((cdb[1] & 0x10) == 0 || disk_blocks <= 0xFFFFFFFF) {
|
||||
// Mode parameter header, block descriptor length
|
||||
buf[7] = 0x08;
|
||||
|
||||
// Short LBA mode parameter block descriptor (number of blocks and block length)
|
||||
SetInt32(buf, 8, (uint32_t)disk_blocks);
|
||||
SetInt32(buf, 12, disk_size);
|
||||
|
||||
size = 16;
|
||||
}
|
||||
else {
|
||||
// Mode parameter header, LONGLBA
|
||||
buf[4] = 0x01;
|
||||
|
||||
// Mode parameter header, block descriptor length
|
||||
buf[7] = 0x10;
|
||||
|
||||
// Long LBA mode parameter block descriptor (number of blocks and block length)
|
||||
SetInt64(buf, 8, disk_blocks);
|
||||
SetInt32(buf, 20, disk_size);
|
||||
|
||||
size = 24;
|
||||
}
|
||||
}
|
||||
|
||||
size = AddModePages(cdb, buf, size, length, 65535);
|
||||
|
||||
SetInt16(buf, 0, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void Disk::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
|
||||
{
|
||||
// Page code 1 (read-write error recovery)
|
||||
if (page == 0x01 || page == 0x3f) {
|
||||
AddErrorPage(pages, changeable);
|
||||
}
|
||||
|
||||
// Page code 3 (format device)
|
||||
if (page == 0x03 || page == 0x3f) {
|
||||
AddFormatPage(pages, changeable);
|
||||
}
|
||||
|
||||
// Page code 4 (rigid drive page)
|
||||
if (page == 0x04 || page == 0x3f) {
|
||||
AddDrivePage(pages, changeable);
|
||||
}
|
||||
|
||||
// Page code 8 (caching)
|
||||
if (page == 0x08 || page == 0x3f) {
|
||||
AddCachePage(pages, changeable);
|
||||
}
|
||||
|
||||
// Page (vendor special)
|
||||
AddVendorPage(pages, page, changeable);
|
||||
}
|
||||
|
||||
void Disk::AddErrorPage(map<int, vector<byte>>& pages, bool) const
|
||||
{
|
||||
vector<byte> buf(12);
|
||||
|
||||
// Retry count is 0, limit time uses internal default value
|
||||
|
||||
pages[1] = buf;
|
||||
}
|
||||
|
||||
void Disk::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) const
|
||||
{
|
||||
vector<byte> buf(24);
|
||||
|
||||
// No changeable area
|
||||
if (changeable) {
|
||||
pages[3] = buf;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsReady()) {
|
||||
// Set the number of tracks in one zone to 8
|
||||
buf[0x03] = (byte)0x08;
|
||||
|
||||
// Set sector/track to 25
|
||||
SetInt16(buf, 0x0a, 25);
|
||||
|
||||
// Set the number of bytes in the physical sector
|
||||
SetInt16(buf, 0x0c, 1 << size_shift_count);
|
||||
|
||||
// Interleave 1
|
||||
SetInt16(buf, 0x0e, 1);
|
||||
|
||||
// Track skew factor 11
|
||||
SetInt16(buf, 0x10, 11);
|
||||
|
||||
// Cylinder skew factor 20
|
||||
SetInt16(buf, 0x12, 20);
|
||||
}
|
||||
|
||||
buf[20] = IsRemovable() ? (byte)0x20 : (byte)0x00;
|
||||
|
||||
// Hard-sectored
|
||||
buf[20] |= (byte)0x40;
|
||||
|
||||
pages[3] = buf;
|
||||
}
|
||||
|
||||
void Disk::AddDrivePage(map<int, vector<byte>>& pages, bool changeable) const
|
||||
{
|
||||
vector<byte> buf(24);
|
||||
|
||||
// No changeable area
|
||||
if (changeable) {
|
||||
pages[4] = buf;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsReady()) {
|
||||
// Set the number of cylinders (total number of blocks
|
||||
// divided by 25 sectors/track and 8 heads)
|
||||
uint64_t cylinders = GetBlockCount();
|
||||
cylinders >>= 3;
|
||||
cylinders /= 25;
|
||||
SetInt32(buf, 0x01, (uint32_t)cylinders);
|
||||
|
||||
// Fix the head at 8
|
||||
buf[0x05] = (byte)0x8;
|
||||
|
||||
// Medium rotation rate 7200
|
||||
SetInt16(buf, 0x14, 7200);
|
||||
}
|
||||
|
||||
pages[4] = buf;
|
||||
}
|
||||
|
||||
void Disk::AddCachePage(map<int, vector<byte>>& pages, bool changeable) const
|
||||
{
|
||||
vector<byte> buf(12);
|
||||
|
||||
// No changeable area
|
||||
if (changeable) {
|
||||
pages[8] = buf;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Only read cache is valid
|
||||
|
||||
// Disable pre-fetch transfer length
|
||||
SetInt16(buf, 0x04, -1);
|
||||
|
||||
// Maximum pre-fetch
|
||||
SetInt16(buf, 0x08, -1);
|
||||
|
||||
// Maximum pre-fetch ceiling
|
||||
SetInt16(buf, 0x0a, -1);
|
||||
|
||||
pages[8] = buf;
|
||||
}
|
||||
|
||||
int Disk::Read(const vector<int>&, vector<BYTE>& buf, uint64_t block)
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
|
||||
CheckReady();
|
||||
|
||||
// Error if the total number of blocks is exceeded
|
||||
if (block >= GetBlockCount()) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
// leave it to the cache
|
||||
if (!cache->ReadSector(buf, (uint32_t)block)) {
|
||||
throw scsi_exception(sense_key::MEDIUM_ERROR, asc::READ_FAULT);
|
||||
}
|
||||
|
||||
return GetSectorSizeInBytes();
|
||||
}
|
||||
|
||||
int Disk::WriteCheck(uint64_t block)
|
||||
{
|
||||
CheckReady();
|
||||
|
||||
// Error if the total number of blocks is exceeded
|
||||
if (block >= GetBlockCount()) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
// Error if write protected
|
||||
if (IsProtected()) {
|
||||
throw scsi_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
|
||||
}
|
||||
|
||||
return GetSectorSizeInBytes();
|
||||
}
|
||||
|
||||
void Disk::Write(const vector<int>&, const vector<BYTE>& buf, uint64_t block)
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
|
||||
CheckReady();
|
||||
|
||||
// Error if the total number of blocks is exceeded
|
||||
if (block >= GetBlockCount()) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
|
||||
}
|
||||
|
||||
// Error if write protected
|
||||
if (IsProtected()) {
|
||||
throw scsi_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
|
||||
}
|
||||
|
||||
// Leave it to the cache
|
||||
if (!cache->WriteSector(buf, (uint32_t)block)) {
|
||||
throw scsi_exception(sense_key::MEDIUM_ERROR, asc::WRITE_FAULT);
|
||||
}
|
||||
}
|
||||
|
||||
void Disk::Seek()
|
||||
{
|
||||
CheckReady();
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::Seek6()
|
||||
{
|
||||
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, SEEK6)) {
|
||||
CheckReady();
|
||||
}
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::Seek10()
|
||||
{
|
||||
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, SEEK10)) {
|
||||
CheckReady();
|
||||
}
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::ReadCapacity10()
|
||||
{
|
||||
CheckReady();
|
||||
|
||||
if (GetBlockCount() == 0) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
|
||||
}
|
||||
|
||||
vector<BYTE>& buf = controller->GetBuffer();
|
||||
|
||||
// Create end of logical block address (blocks-1)
|
||||
uint64_t capacity = GetBlockCount() - 1;
|
||||
|
||||
// If the capacity exceeds 32 bit, -1 must be returned and the client has to use READ CAPACITY(16)
|
||||
if (capacity > 4294967295) {
|
||||
capacity = -1;
|
||||
}
|
||||
SetInt32(buf, 0, (uint32_t)capacity);
|
||||
|
||||
// Create block length (1 << size)
|
||||
SetInt32(buf, 4, 1 << size_shift_count);
|
||||
|
||||
// the size
|
||||
ctrl->length = 8;
|
||||
|
||||
EnterDataInPhase();
|
||||
}
|
||||
|
||||
void Disk::ReadCapacity16()
|
||||
{
|
||||
CheckReady();
|
||||
|
||||
if (GetBlockCount() == 0) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
|
||||
}
|
||||
|
||||
vector<BYTE>& buf = controller->GetBuffer();
|
||||
|
||||
// Create end of logical block address (blocks-1)
|
||||
SetInt64(buf, 0, GetBlockCount() - 1);
|
||||
|
||||
// Create block length (1 << size)
|
||||
SetInt32(buf, 8, 1 << size_shift_count);
|
||||
|
||||
buf[12] = 0;
|
||||
|
||||
// Logical blocks per physical block: not reported (1 or more)
|
||||
buf[13] = 0;
|
||||
|
||||
// the size
|
||||
ctrl->length = 14;
|
||||
|
||||
EnterDataInPhase();
|
||||
}
|
||||
|
||||
void Disk::ReadCapacity16_ReadLong16()
|
||||
{
|
||||
// The service action determines the actual command
|
||||
switch (ctrl->cmd[1] & 0x1f) {
|
||||
case 0x10:
|
||||
ReadCapacity16();
|
||||
break;
|
||||
|
||||
case 0x11:
|
||||
ReadWriteLong16();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Check/Get start sector and sector count for a READ/WRITE or READ/WRITE LONG operation
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void Disk::ValidateBlockAddress(access_mode mode) const
|
||||
{
|
||||
const uint64_t block = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
|
||||
|
||||
if (block > GetBlockCount()) {
|
||||
LOGTRACE("%s", ("Capacity of " + to_string(GetBlockCount()) + " block(s) exceeded: Trying to access block "
|
||||
+ to_string(block)).c_str())
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
|
||||
}
|
||||
}
|
||||
|
||||
bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mode mode) const
|
||||
{
|
||||
if (mode == RW6 || mode == SEEK6) {
|
||||
start = GetInt24(ctrl->cmd, 1);
|
||||
|
||||
count = ctrl->cmd[4];
|
||||
if (!count) {
|
||||
count= 0x100;
|
||||
}
|
||||
}
|
||||
else {
|
||||
start = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
|
||||
|
||||
if (mode == RW16) {
|
||||
count = GetInt32(ctrl->cmd, 10);
|
||||
}
|
||||
else if (mode != SEEK6 && mode != SEEK10) {
|
||||
count = GetInt16(ctrl->cmd, 7);
|
||||
}
|
||||
else {
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
LOGTRACE("%s READ/WRITE/VERIFY/SEEK command record=$%08X blocks=%d", __PRETTY_FUNCTION__, (uint32_t)start, count)
|
||||
|
||||
// Check capacity
|
||||
if (uint64_t capacity = GetBlockCount(); !capacity || start > capacity || start + count > capacity) {
|
||||
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
|
||||
+ to_string(start) + ", block count " + to_string(count)).c_str())
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
|
||||
}
|
||||
|
||||
// Do not process 0 blocks
|
||||
if (!count && (mode != SEEK6 && mode != SEEK10)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t Disk::CalculateShiftCount(uint32_t size_in_bytes)
|
||||
{
|
||||
const auto& it = shift_counts.find(size_in_bytes);
|
||||
return it != shift_counts.end() ? it->second : 0;
|
||||
}
|
||||
|
||||
uint32_t Disk::GetSectorSizeInBytes() const
|
||||
{
|
||||
return size_shift_count ? 1 << size_shift_count : 0;
|
||||
}
|
||||
|
||||
void Disk::SetSectorSizeInBytes(uint32_t size_in_bytes)
|
||||
{
|
||||
DeviceFactory device_factory;
|
||||
if (const auto& sizes = device_factory.GetSectorSizes(GetType());
|
||||
!sizes.empty() && sizes.find(size_in_bytes) == sizes.end()) {
|
||||
throw io_exception("Invalid sector size of " + to_string(size_in_bytes) + " byte(s)");
|
||||
}
|
||||
|
||||
size_shift_count = CalculateShiftCount(size_in_bytes);
|
||||
assert(size_shift_count);
|
||||
}
|
||||
|
||||
uint32_t Disk::GetConfiguredSectorSize() const
|
||||
{
|
||||
return configured_sector_size;
|
||||
}
|
||||
|
||||
bool Disk::SetConfiguredSectorSize(const DeviceFactory& device_factory, uint32_t configured_size)
|
||||
{
|
||||
if (unordered_set<uint32_t> sizes = device_factory.GetSectorSizes(GetType());
|
||||
sizes.find(configured_size) == sizes.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
configured_sector_size = configured_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user