mirror of
https://github.com/akuker/RASCSI.git
synced 2024-12-21 08:29:59 +00:00
Use lambdas for dispatcher, code cleanup, test updates (#958)
* Using lambdas instead of member function pointers simplifies the command dispatching and reduces the code volume * Removed duplicate error handling * Fix for issue #956 * Unit test updates * Resolved SonarQube issues
This commit is contained in:
parent
31dd063611
commit
c41373d9bd
19
cpp/bus.cpp
19
cpp/bus.cpp
@ -12,6 +12,25 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Get the number of bytes for a command
|
||||
// TODO Add the byte count to the enum value/command name mapping and remove the comparisons
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int BUS::GetCommandByteCount(uint8_t opcode)
|
||||
{
|
||||
if (opcode == 0x88 || opcode == 0x8A || opcode == 0x8F || opcode == 0x91 || opcode == 0x9E || opcode == 0x9F) {
|
||||
return 16;
|
||||
} else if (opcode == 0xA0) {
|
||||
return 12;
|
||||
} else if (opcode >= 0x20 && opcode <= 0x7D) {
|
||||
return 10;
|
||||
} else {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Phase Acquisition
|
||||
|
@ -44,7 +44,8 @@ public:
|
||||
BUS() = default;
|
||||
virtual ~BUS() = default;
|
||||
|
||||
// Basic Functions
|
||||
static int GetCommandByteCount(uint8_t);
|
||||
|
||||
virtual bool Init(mode_e mode) = 0;
|
||||
virtual void Reset() = 0;
|
||||
virtual void Cleanup() = 0;
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "log.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "devices/primary_device.h"
|
||||
#include "abstract_controller.h"
|
||||
|
@ -196,8 +196,7 @@ void ScsiController::Command()
|
||||
GetBus()->SetIO(false);
|
||||
|
||||
const int actual_count = GetBus()->CommandHandShake(GetBuffer().data());
|
||||
// TODO Try to move GetCommandByteCount() to BUS, so that the controller does not need to know GPIOBUS
|
||||
const int command_byte_count = GPIOBUS::GetCommandByteCount(GetBuffer()[0]);
|
||||
const int command_byte_count = BUS::GetCommandByteCount(GetBuffer()[0]);
|
||||
|
||||
// If not able to receive all, move to the status phase
|
||||
if (actual_count != command_byte_count) {
|
||||
@ -225,7 +224,7 @@ void ScsiController::Command()
|
||||
|
||||
void ScsiController::Execute()
|
||||
{
|
||||
LOGDEBUG("++++ CMD ++++ %s Executing command $%02X", __PRETTY_FUNCTION__, static_cast<int>(GetOpcode()))
|
||||
LOGDEBUG("++++ CMD ++++ Executing command $%02X", static_cast<int>(GetOpcode()))
|
||||
|
||||
// Initialization for data transfer
|
||||
ResetOffset();
|
||||
@ -240,24 +239,25 @@ void ScsiController::Execute()
|
||||
int lun = GetEffectiveLun();
|
||||
if (!HasDeviceForLun(lun)) {
|
||||
if (GetOpcode() != scsi_command::eCmdInquiry && GetOpcode() != scsi_command::eCmdRequestSense) {
|
||||
LOGDEBUG("Invalid LUN %d for ID %d", lun, GetTargetId())
|
||||
LOGTRACE("Invalid LUN %d for device ID %d", lun, GetTargetId())
|
||||
|
||||
Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN);
|
||||
|
||||
return;
|
||||
}
|
||||
// Use LUN 0 for INQUIRY and REQUEST SENSE because LUN0 is assumed to be always available.
|
||||
// INQUIRY and REQUEST SENSE have a special LUN handling of their own, required by the SCSI standard.
|
||||
else {
|
||||
if (!HasDeviceForLun(0)) {
|
||||
LOGERROR("No LUN 0 for device %d", GetTargetId())
|
||||
|
||||
GetBuffer().data()[0] = 0x7f;
|
||||
assert(HasDeviceForLun(0));
|
||||
|
||||
return;
|
||||
}
|
||||
lun = 0;
|
||||
}
|
||||
|
||||
lun = 0;
|
||||
}
|
||||
// SCSI-2 4.4.3 Incorrect logical unit handling
|
||||
if (GetOpcode() == scsi_command::eCmdInquiry && !HasDeviceForLun(lun)) {
|
||||
LOGTRACE("Reporting LUN %d for device ID %d as not supported", GetEffectiveLun(), GetTargetId())
|
||||
|
||||
GetBuffer().data()[0] = 0x7f;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetDeviceForLun(lun);
|
||||
@ -267,31 +267,16 @@ void ScsiController::Execute()
|
||||
device->SetStatusCode(0);
|
||||
}
|
||||
|
||||
if (!device->CheckReservation(initiator_id, GetOpcode(), GetCmd(4) & 0x01)) {
|
||||
Error(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION, status::RESERVATION_CONFLICT);
|
||||
if (device->CheckReservation(initiator_id, GetOpcode(), GetCmd(4) & 0x01)) {
|
||||
try {
|
||||
device->Dispatch(GetOpcode());
|
||||
}
|
||||
catch(const scsi_exception& e) {
|
||||
Error(e.get_sense_key(), e.get_asc());
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
if (!device->Dispatch(GetOpcode())) {
|
||||
LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetTargetId(), lun, static_cast<int>(GetOpcode()))
|
||||
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
}
|
||||
}
|
||||
catch(const scsi_exception& e) { //NOSONAR This exception is handled properly
|
||||
Error(e.get_sense_key(), e.get_asc());
|
||||
|
||||
// Fall through
|
||||
}
|
||||
}
|
||||
|
||||
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
|
||||
if (GetOpcode() == scsi_command::eCmdInquiry && !HasDeviceForLun(lun)) {
|
||||
lun = GetEffectiveLun();
|
||||
|
||||
LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, device->GetId())
|
||||
|
||||
GetBuffer().data()[0] = 0x7f;
|
||||
Error(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION, status::RESERVATION_CONFLICT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -494,7 +479,7 @@ void ScsiController::Send()
|
||||
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Sending handhake with offset " + to_string(GetOffset()) + ", length "
|
||||
+ to_string(GetLength())).c_str())
|
||||
|
||||
// TODO The delay has to be taken from ctrl.unit[lun], but as there are currently no Daynaport drivers for
|
||||
// The delay should be taken from the respective LUN, but as there are no Daynaport drivers for
|
||||
// LUNs other than 0 this work-around works.
|
||||
if (const int len = GetBus()->SendHandShake(GetBuffer().data() + GetOffset(), GetLength(),
|
||||
HasDeviceForLun(0) ? GetDeviceForLun(0)->GetSendDelay() : 0);
|
||||
@ -850,7 +835,6 @@ bool ScsiController::XferIn(vector<uint8_t>& buf)
|
||||
ResetOffset();
|
||||
break;
|
||||
|
||||
// Other (impossible)
|
||||
default:
|
||||
assert(false);
|
||||
return false;
|
||||
@ -929,14 +913,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
|
||||
break;
|
||||
}
|
||||
|
||||
// Check the next block
|
||||
try {
|
||||
SetLength(disk->WriteCheck(GetNext() - 1));
|
||||
}
|
||||
catch(const scsi_exception&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SetLength(disk->GetSectorSizeInBytes());
|
||||
ResetOffset();
|
||||
break;
|
||||
}
|
||||
|
@ -129,19 +129,6 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
|
||||
return false;
|
||||
#else
|
||||
unordered_map<string, string> params = const_params;
|
||||
if (params.count("interfaces")) {
|
||||
LOGWARN("You are using the deprecated 'interfaces' parameter. "
|
||||
"Provide the interface list and the IP address/netmask with the 'interface' and 'inet' parameters")
|
||||
|
||||
// TODO Remove the deprecated syntax in a future version
|
||||
const string& ifaces = params["interfaces"];
|
||||
size_t separatorPos = ifaces.find(':');
|
||||
if (separatorPos != string::npos) {
|
||||
params["interface"] = ifaces.substr(0, separatorPos);
|
||||
params["inet"] = ifaces.substr(separatorPos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
stringstream s(params["interface"]);
|
||||
string interface;
|
||||
while (getline(s, interface, ',')) {
|
||||
|
@ -64,11 +64,10 @@ class Device //NOSONAR The number of fields and methods is justified, the comple
|
||||
// The default parameters
|
||||
unordered_map<string, string> default_params;
|
||||
|
||||
// Sense Key, ASC and ASCQ
|
||||
// Sense Key and ASC
|
||||
// MSB Reserved (0x00)
|
||||
// Sense Key
|
||||
// Additional Sense Code (ASC)
|
||||
// LSB Additional Sense Code Qualifier(ASCQ)
|
||||
int status_code = 0;
|
||||
|
||||
protected:
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace rascsi_interface;
|
||||
|
@ -14,45 +14,14 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "log.h"
|
||||
#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
|
||||
@ -61,7 +30,40 @@ Disk::~Disk()
|
||||
}
|
||||
}
|
||||
|
||||
bool Disk::Dispatch(scsi_command cmd)
|
||||
bool Disk::Init(const unordered_map<string, string>& params)
|
||||
{
|
||||
StorageDevice::Init(params);
|
||||
|
||||
// REZERO implementation is identical with Seek
|
||||
AddCommand(scsi_command::eCmdRezero, [this] { Seek(); });
|
||||
AddCommand(scsi_command::eCmdFormatUnit, [this] { FormatUnit(); });
|
||||
// REASSIGN BLOCKS implementation is identical with Seek
|
||||
AddCommand(scsi_command::eCmdReassignBlocks, [this] { Seek(); });
|
||||
AddCommand(scsi_command::eCmdRead6, [this] { Read6(); });
|
||||
AddCommand(scsi_command::eCmdWrite6, [this] { Write6(); });
|
||||
AddCommand(scsi_command::eCmdSeek6, [this] { Seek6(); });
|
||||
AddCommand(scsi_command::eCmdStartStop, [this] { StartStopUnit(); });
|
||||
AddCommand(scsi_command::eCmdPreventAllowMediumRemoval, [this]{ PreventAllowMediumRemoval(); });
|
||||
AddCommand(scsi_command::eCmdReadCapacity10, [this] { ReadCapacity10(); });
|
||||
AddCommand(scsi_command::eCmdRead10, [this] { Read10(); });
|
||||
AddCommand(scsi_command::eCmdWrite10, [this] { Write10(); });
|
||||
AddCommand(scsi_command::eCmdReadLong10, [this] { ReadWriteLong10(); });
|
||||
AddCommand(scsi_command::eCmdWriteLong10, [this] { ReadWriteLong10(); });
|
||||
AddCommand(scsi_command::eCmdWriteLong16, [this] { ReadWriteLong16(); });
|
||||
AddCommand(scsi_command::eCmdSeek10, [this] { Seek10(); });
|
||||
AddCommand(scsi_command::eCmdVerify10, [this] { Verify10(); });
|
||||
AddCommand(scsi_command::eCmdSynchronizeCache10, [this] { SynchronizeCache(); });
|
||||
AddCommand(scsi_command::eCmdSynchronizeCache16, [this] { SynchronizeCache(); });
|
||||
AddCommand(scsi_command::eCmdReadDefectData10, [this] { ReadDefectData10(); });
|
||||
AddCommand(scsi_command::eCmdRead16,[this] { Read16(); });
|
||||
AddCommand(scsi_command::eCmdWrite16, [this] { Write16(); });
|
||||
AddCommand(scsi_command::eCmdVerify16, [this] { Verify16(); });
|
||||
AddCommand(scsi_command::eCmdReadCapacity16_ReadLong16, [this] { ReadCapacity16_ReadLong16(); });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Disk::Dispatch(scsi_command cmd)
|
||||
{
|
||||
// Media changes must be reported on the next access, i.e. not only for TEST UNIT READY
|
||||
if (IsMediumChanged()) {
|
||||
@ -69,11 +71,11 @@ bool Disk::Dispatch(scsi_command cmd)
|
||||
|
||||
SetMediumChanged(false);
|
||||
|
||||
throw scsi_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
|
||||
controller->Error(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
|
||||
}
|
||||
else {
|
||||
PrimaryDevice::Dispatch(cmd);
|
||||
}
|
||||
|
||||
// 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)
|
||||
@ -109,11 +111,11 @@ void Disk::FormatUnit()
|
||||
|
||||
void Disk::Read(access_mode mode)
|
||||
{
|
||||
uint64_t start;
|
||||
uint32_t blocks;
|
||||
if (CheckAndGetStartAndCount(start, blocks, mode)) {
|
||||
const auto& [valid, start, blocks] = CheckAndGetStartAndCount(mode);
|
||||
if (valid) {
|
||||
controller->SetBlocks(blocks);
|
||||
controller->SetLength(Read(controller->GetCmd(), controller->GetBuffer(), start));
|
||||
|
||||
LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, controller->GetLength())
|
||||
|
||||
// Set next block
|
||||
@ -128,35 +130,38 @@ void Disk::Read(access_mode mode)
|
||||
|
||||
void Disk::ReadWriteLong10()
|
||||
{
|
||||
ValidateBlockAddress(RW10);
|
||||
|
||||
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
|
||||
if (GetInt16(controller->GetCmd(), 7) != 0) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
ValidateBlockAddress(RW10);
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::ReadWriteLong16()
|
||||
{
|
||||
ValidateBlockAddress(RW16);
|
||||
|
||||
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
|
||||
if (GetInt16(controller->GetCmd(), 12) != 0) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
ValidateBlockAddress(RW16);
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::Write(access_mode mode)
|
||||
{
|
||||
uint64_t start;
|
||||
uint32_t blocks;
|
||||
if (CheckAndGetStartAndCount(start, blocks, mode)) {
|
||||
if (IsProtected()) {
|
||||
throw scsi_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
|
||||
}
|
||||
|
||||
const auto& [valid, start, blocks] = CheckAndGetStartAndCount(mode);
|
||||
if (valid) {
|
||||
controller->SetBlocks(blocks);
|
||||
controller->SetLength(WriteCheck(start));
|
||||
controller->SetLength(GetSectorSizeInBytes());
|
||||
|
||||
// Set next block
|
||||
controller->SetNext(start + 1);
|
||||
@ -170,9 +175,8 @@ void Disk::Write(access_mode mode)
|
||||
|
||||
void Disk::Verify(access_mode mode)
|
||||
{
|
||||
uint64_t start;
|
||||
uint32_t blocks;
|
||||
if (CheckAndGetStartAndCount(start, blocks, mode)) {
|
||||
const auto& [valid, start, blocks] = CheckAndGetStartAndCount(mode);
|
||||
if (valid) {
|
||||
// if BytChk=0
|
||||
if ((controller->GetCmd(1) & 0x02) == 0) {
|
||||
Seek();
|
||||
@ -261,7 +265,7 @@ void Disk::ReadDefectData10()
|
||||
|
||||
bool Disk::Eject(bool force)
|
||||
{
|
||||
const bool status = super::Eject(force);
|
||||
const bool status = PrimaryDevice::Eject(force);
|
||||
if (status) {
|
||||
FlushCache();
|
||||
cache.reset();
|
||||
@ -389,11 +393,8 @@ void Disk::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeab
|
||||
|
||||
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;
|
||||
pages[1] = vector<byte>(12);
|
||||
}
|
||||
|
||||
void Disk::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) const
|
||||
@ -491,16 +492,10 @@ void Disk::AddCachePage(map<int, vector<byte>>& pages, bool changeable) const
|
||||
|
||||
int Disk::Read(const vector<int>&, vector<uint8_t>& buf, uint64_t block)
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
assert(block < GetBlockCount());
|
||||
|
||||
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, static_cast<uint32_t>(block))) {
|
||||
throw scsi_exception(sense_key::MEDIUM_ERROR, asc::READ_FAULT);
|
||||
}
|
||||
@ -508,40 +503,12 @@ int Disk::Read(const vector<int>&, vector<uint8_t>& buf, uint64_t block)
|
||||
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<uint8_t>& buf, uint64_t block)
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
assert(block < GetBlockCount());
|
||||
|
||||
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, static_cast<uint32_t>(block))) {
|
||||
throw scsi_exception(sense_key::MEDIUM_ERROR, asc::WRITE_FAULT);
|
||||
}
|
||||
@ -556,8 +523,8 @@ void Disk::Seek()
|
||||
|
||||
void Disk::Seek6()
|
||||
{
|
||||
uint64_t start;
|
||||
if (uint32_t blocks; CheckAndGetStartAndCount(start, blocks, SEEK6)) {
|
||||
const auto& [valid, start, blocks] = CheckAndGetStartAndCount(SEEK6);
|
||||
if (valid) {
|
||||
CheckReady();
|
||||
}
|
||||
|
||||
@ -566,8 +533,8 @@ void Disk::Seek6()
|
||||
|
||||
void Disk::Seek10()
|
||||
{
|
||||
uint64_t start;
|
||||
if (uint32_t blocks; CheckAndGetStartAndCount(start, blocks, SEEK10)) {
|
||||
const auto& [valid, start, blocks] = CheckAndGetStartAndCount(SEEK10);
|
||||
if (valid) {
|
||||
CheckReady();
|
||||
}
|
||||
|
||||
@ -596,7 +563,6 @@ void Disk::ReadCapacity10()
|
||||
// Create block length (1 << size)
|
||||
SetInt32(buf, 4, 1 << size_shift_count);
|
||||
|
||||
// the size
|
||||
controller->SetLength(8);
|
||||
|
||||
EnterDataInPhase();
|
||||
@ -623,7 +589,6 @@ void Disk::ReadCapacity16()
|
||||
// Logical blocks per physical block: not reported (1 or more)
|
||||
buf[13] = 0;
|
||||
|
||||
// the size
|
||||
controller->SetLength(14);
|
||||
|
||||
EnterDataInPhase();
|
||||
@ -647,12 +612,6 @@ void Disk::ReadCapacity16_ReadLong16()
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 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(controller->GetCmd(), 2) : GetInt32(controller->GetCmd(), 2);
|
||||
@ -664,8 +623,11 @@ void Disk::ValidateBlockAddress(access_mode mode) const
|
||||
}
|
||||
}
|
||||
|
||||
bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mode mode) const
|
||||
tuple<bool, uint64_t, uint32_t> Disk::CheckAndGetStartAndCount(access_mode mode) const
|
||||
{
|
||||
uint64_t start;
|
||||
uint32_t count;
|
||||
|
||||
if (mode == RW6 || mode == SEEK6) {
|
||||
start = GetInt24(controller->GetCmd(), 1);
|
||||
|
||||
@ -700,10 +662,10 @@ bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mod
|
||||
|
||||
// Do not process 0 blocks
|
||||
if (!count && (mode != SEEK6 && mode != SEEK10)) {
|
||||
return false;
|
||||
return tuple(false, start, count);
|
||||
}
|
||||
|
||||
return true;
|
||||
return tuple(true, start, count);
|
||||
}
|
||||
|
||||
uint32_t Disk::CalculateShiftCount(uint32_t size_in_bytes)
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -31,8 +32,6 @@ class Disk : public StorageDevice, private ScsiBlockCommands
|
||||
{
|
||||
enum access_mode { RW6, RW10, RW16, SEEK6, SEEK10 };
|
||||
|
||||
Dispatcher<Disk> dispatcher;
|
||||
|
||||
unique_ptr<DiskCache> cache;
|
||||
|
||||
// The supported configurable sector sizes, empty if not configurable
|
||||
@ -44,15 +43,15 @@ class Disk : public StorageDevice, private ScsiBlockCommands
|
||||
|
||||
public:
|
||||
|
||||
Disk(PbDeviceType, int);
|
||||
Disk(PbDeviceType type, int lun) : StorageDevice(type, lun) {}
|
||||
~Disk() override;
|
||||
|
||||
bool Dispatch(scsi_command) override;
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
|
||||
void Dispatch(scsi_command) override;
|
||||
|
||||
bool Eject(bool) override;
|
||||
|
||||
// Command helpers
|
||||
virtual int WriteCheck(uint64_t);
|
||||
virtual void Write(const vector<int>&, const vector<uint8_t>&, uint64_t);
|
||||
|
||||
virtual int Read(const vector<int>&, vector<uint8_t>& , uint64_t);
|
||||
@ -64,8 +63,6 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
using super = StorageDevice;
|
||||
|
||||
// Commands covered by the SCSI specifications (see https://www.t10.org/drafts.htm)
|
||||
void StartStopUnit();
|
||||
void PreventAllowMediumRemoval();
|
||||
@ -93,12 +90,13 @@ private:
|
||||
void ReadCapacity16_ReadLong16();
|
||||
|
||||
void ValidateBlockAddress(access_mode) const;
|
||||
bool CheckAndGetStartAndCount(uint64_t&, uint32_t&, access_mode) const;
|
||||
tuple<bool, uint64_t, uint32_t> CheckAndGetStartAndCount(access_mode) const;
|
||||
|
||||
int ModeSense6(const vector<int>&, vector<uint8_t>&) const override;
|
||||
int ModeSense10(const vector<int>&, vector<uint8_t>&) const override;
|
||||
|
||||
static const unordered_map<uint32_t, uint32_t> shift_counts;
|
||||
static inline const unordered_map<uint32_t, uint32_t> shift_counts =
|
||||
{ { 512, 9 }, { 1024, 10 }, { 2048, 11 }, { 4096, 12 } };
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -1,55 +0,0 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
//
|
||||
// A template for dispatching SCSI commands
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "log.h"
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace std;
|
||||
using namespace scsi_defs;
|
||||
|
||||
template<class T>
|
||||
class Dispatcher
|
||||
{
|
||||
public:
|
||||
|
||||
Dispatcher() = default;
|
||||
~Dispatcher() = default;
|
||||
|
||||
using operation = void (T::*)();
|
||||
using command_t = struct _command_t {
|
||||
const char *name;
|
||||
operation execute;
|
||||
|
||||
_command_t(const char *_name, operation _execute) : name(_name), execute(_execute) { };
|
||||
};
|
||||
unordered_map<scsi_command, unique_ptr<command_t>> commands;
|
||||
|
||||
void Add(scsi_command opcode, const char *name, operation execute)
|
||||
{
|
||||
commands[opcode] = make_unique<command_t>(name, execute);
|
||||
}
|
||||
|
||||
bool Dispatch(T *instance, scsi_command cmd) const
|
||||
{
|
||||
if (const auto& it = commands.find(cmd); it != commands.end()) {
|
||||
LOGDEBUG("%s Executing %s ($%02X)", __PRETTY_FUNCTION__, it->second->name, (int)cmd)
|
||||
|
||||
(instance->*it->second->execute)();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Unknown command
|
||||
return false;
|
||||
}
|
||||
};
|
@ -24,26 +24,22 @@
|
||||
#include "controllers/scsi_controller.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include "dispatcher.h"
|
||||
#include "host_services.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
HostServices::HostServices(int lun, const ControllerManager& manager)
|
||||
: ModePageDevice(SCHS, lun), controller_manager(manager)
|
||||
bool HostServices::Init(const unordered_map<string, string>& params)
|
||||
{
|
||||
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &HostServices::TestUnitReady);
|
||||
dispatcher.Add(scsi_command::eCmdStartStop, "StartStopUnit", &HostServices::StartStopUnit);
|
||||
ModePageDevice::Init(params);
|
||||
|
||||
AddCommand(scsi_command::eCmdTestUnitReady, [this] { TestUnitReady(); });
|
||||
AddCommand(scsi_command::eCmdStartStop, [this] { StartStopUnit(); });
|
||||
|
||||
SetReady(true);
|
||||
}
|
||||
|
||||
bool HostServices::Dispatch(scsi_command cmd)
|
||||
{
|
||||
// The superclass class handles the less specific commands
|
||||
return dispatcher.Dispatch(this, cmd) ? true : super::Dispatch(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void HostServices::TestUnitReady()
|
||||
@ -52,7 +48,7 @@ void HostServices::TestUnitReady()
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
vector<byte> HostServices::InquiryInternal() const
|
||||
vector<uint8_t> HostServices::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(device_type::PROCESSOR, scsi_level::SPC_3, false);
|
||||
}
|
||||
|
@ -22,12 +22,12 @@ class HostServices: public ModePageDevice
|
||||
|
||||
public:
|
||||
|
||||
HostServices(int, const ControllerManager&);
|
||||
HostServices(int lun, const ControllerManager& manager) : ModePageDevice(SCHS, lun), controller_manager(manager) {}
|
||||
~HostServices() override = default;
|
||||
|
||||
bool Dispatch(scsi_command) override;
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
|
||||
vector<byte> InquiryInternal() const override;
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
void TestUnitReady() override;
|
||||
|
||||
protected:
|
||||
@ -49,10 +49,6 @@ private:
|
||||
uint8_t second; // 0-59
|
||||
};
|
||||
|
||||
using super = ModePageDevice;
|
||||
|
||||
Dispatcher<HostServices> dispatcher;
|
||||
|
||||
const ControllerManager& controller_manager;
|
||||
|
||||
void StartStopUnit();
|
||||
|
@ -24,6 +24,9 @@ public:
|
||||
virtual void Inquiry() = 0;
|
||||
virtual void ReportLuns() = 0;
|
||||
|
||||
// Implemented for all RaSCSI device types
|
||||
// Optional commands implemented by all RaSCSI device types
|
||||
virtual void RequestSense() = 0;
|
||||
virtual void ReleaseUnit() = 0;
|
||||
virtual void ReserveUnit() = 0;
|
||||
virtual void SendDiagnostic() = 0;
|
||||
};
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "log.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include "dispatcher.h"
|
||||
#include "mode_page_device.h"
|
||||
#include <cstddef>
|
||||
|
||||
@ -20,23 +19,21 @@ using namespace std;
|
||||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
ModePageDevice::ModePageDevice(PbDeviceType type, int lun) : PrimaryDevice(type, lun)
|
||||
bool ModePageDevice::Init(const unordered_map<string, string>& params)
|
||||
{
|
||||
dispatcher.Add(scsi_command::eCmdModeSense6, "ModeSense6", &ModePageDevice::ModeSense6);
|
||||
dispatcher.Add(scsi_command::eCmdModeSense10, "ModeSense10", &ModePageDevice::ModeSense10);
|
||||
dispatcher.Add(scsi_command::eCmdModeSelect6, "ModeSelect6", &ModePageDevice::ModeSelect6);
|
||||
dispatcher.Add(scsi_command::eCmdModeSelect10, "ModeSelect10", &ModePageDevice::ModeSelect10);
|
||||
}
|
||||
PrimaryDevice::Init(params);
|
||||
|
||||
bool ModePageDevice::Dispatch(scsi_command cmd)
|
||||
{
|
||||
// The superclass class handles the less specific commands
|
||||
return dispatcher.Dispatch(this, cmd) ? true : super::Dispatch(cmd);
|
||||
AddCommand(scsi_command::eCmdModeSense6, [this] { ModeSense6(); });
|
||||
AddCommand(scsi_command::eCmdModeSense10, [this] { ModeSense10(); });
|
||||
AddCommand(scsi_command::eCmdModeSelect6, [this] { ModeSelect6(); });
|
||||
AddCommand(scsi_command::eCmdModeSelect10, [this] { ModeSelect10(); });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ModePageDevice::AddModePages(const vector<int>& cdb, vector<uint8_t>& buf, int offset, int length, int max_size) const
|
||||
{
|
||||
int max_length = length - offset;
|
||||
const int max_length = length - offset;
|
||||
if (max_length < 0) {
|
||||
return length;
|
||||
}
|
||||
@ -61,7 +58,7 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<uint8_t>& buf, i
|
||||
vector<byte> result;
|
||||
|
||||
vector<byte> page0;
|
||||
for (auto const& [index, data] : pages) {
|
||||
for (const auto& [index, data] : pages) {
|
||||
// The specification mandates that page 0 must be returned after all others
|
||||
if (index) {
|
||||
const size_t off = result.size();
|
||||
@ -80,7 +77,7 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<uint8_t>& buf, i
|
||||
|
||||
// Page 0 must be last
|
||||
if (!page0.empty()) {
|
||||
size_t off = result.size();
|
||||
const size_t off = result.size();
|
||||
|
||||
// Page data
|
||||
result.insert(result.end(), page0.begin(), page0.end());
|
||||
@ -92,7 +89,7 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<uint8_t>& buf, i
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
auto size = static_cast<int>(min(static_cast<size_t>(max_length), result.size()));
|
||||
const auto size = static_cast<int>(min(static_cast<size_t>(max_length), result.size()));
|
||||
memcpy(&buf.data()[offset], result.data(), size);
|
||||
|
||||
// Do not return more than the requested number of bytes
|
||||
@ -115,30 +112,29 @@ void ModePageDevice::ModeSense10()
|
||||
|
||||
void ModePageDevice::ModeSelect(scsi_command, const vector<int>&, const vector<uint8_t>&, int) const
|
||||
{
|
||||
// There is no default implementation of MDOE SELECT
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
}
|
||||
|
||||
void ModePageDevice::ModeSelect6()
|
||||
{
|
||||
controller->SetLength(SaveParametersCheck(controller->GetCmd(4)));
|
||||
|
||||
EnterDataOutPhase();
|
||||
SaveParametersCheck(controller->GetCmd(4));
|
||||
}
|
||||
|
||||
void ModePageDevice::ModeSelect10()
|
||||
{
|
||||
const size_t length = min(controller->GetBuffer().size(), static_cast<size_t>(GetInt16(controller->GetCmd(), 7)));
|
||||
const auto length = min(controller->GetBuffer().size(), static_cast<size_t>(GetInt16(controller->GetCmd(), 7)));
|
||||
|
||||
controller->SetLength(SaveParametersCheck(static_cast<uint32_t>(length)));
|
||||
|
||||
EnterDataOutPhase();
|
||||
SaveParametersCheck(static_cast<uint32_t>(length));
|
||||
}
|
||||
|
||||
int ModePageDevice::SaveParametersCheck(int length) const
|
||||
void ModePageDevice::SaveParametersCheck(int length)
|
||||
{
|
||||
if (!SupportsSaveParameters() && (controller->GetCmd(1) & 0x01)) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
return length;
|
||||
controller->SetLength(length);
|
||||
|
||||
EnterDataOutPhase();
|
||||
}
|
||||
|
@ -14,14 +14,14 @@
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class ModePageDevice: public PrimaryDevice
|
||||
// TODO Maybe this should better be a mixin class because not all storage devicess have mode pages
|
||||
class ModePageDevice : public PrimaryDevice
|
||||
{
|
||||
public:
|
||||
|
||||
ModePageDevice(PbDeviceType, int);
|
||||
~ModePageDevice() override = default;
|
||||
using PrimaryDevice::PrimaryDevice;
|
||||
|
||||
bool Dispatch(scsi_command) override;
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
|
||||
virtual void ModeSelect(scsi_defs::scsi_command, const vector<int>&, const vector<uint8_t>&, int) const;
|
||||
|
||||
@ -37,10 +37,6 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
using super = PrimaryDevice;
|
||||
|
||||
Dispatcher<ModePageDevice> dispatcher;
|
||||
|
||||
bool supports_save_parameters = false;
|
||||
|
||||
virtual int ModeSense6(const vector<int>&, vector<uint8_t>&) const = 0;
|
||||
@ -51,5 +47,5 @@ private:
|
||||
void ModeSelect6();
|
||||
void ModeSelect10();
|
||||
|
||||
int SaveParametersCheck(int) const;
|
||||
void SaveParametersCheck(int);
|
||||
};
|
||||
|
@ -10,30 +10,47 @@
|
||||
#include "log.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include "dispatcher.h"
|
||||
#include "primary_device.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
PrimaryDevice::PrimaryDevice(PbDeviceType type, int lun) : Device(type, lun)
|
||||
bool PrimaryDevice::Init(const unordered_map<string, string>& params)
|
||||
{
|
||||
// Mandatory SCSI primary commands
|
||||
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady);
|
||||
dispatcher.Add(scsi_command::eCmdInquiry, "Inquiry", &PrimaryDevice::Inquiry);
|
||||
dispatcher.Add(scsi_command::eCmdReportLuns, "ReportLuns", &PrimaryDevice::ReportLuns);
|
||||
AddCommand(scsi_command::eCmdTestUnitReady, [this] { TestUnitReady(); });
|
||||
AddCommand(scsi_command::eCmdInquiry, [this] { Inquiry(); });
|
||||
AddCommand(scsi_command::eCmdReportLuns, [this] { ReportLuns(); });
|
||||
|
||||
// Optional commands supported by all RaSCSI devices
|
||||
dispatcher.Add(scsi_command::eCmdRequestSense, "RequestSense", &PrimaryDevice::RequestSense);
|
||||
dispatcher.Add(scsi_command::eCmdReserve6, "ReserveUnit", &PrimaryDevice::ReserveUnit);
|
||||
dispatcher.Add(scsi_command::eCmdRelease6, "ReleaseUnit", &PrimaryDevice::ReleaseUnit);
|
||||
dispatcher.Add(scsi_command::eCmdSendDiag, "SendDiagnostic", &PrimaryDevice::SendDiagnostic);
|
||||
AddCommand(scsi_command::eCmdRequestSense, [this] { RequestSense(); });
|
||||
AddCommand(scsi_command::eCmdReserve6, [this] { ReserveUnit(); });
|
||||
AddCommand(scsi_command::eCmdRelease6, [this] { ReleaseUnit(); });
|
||||
AddCommand(scsi_command::eCmdSendDiagnostic, [this] { SendDiagnostic(); });
|
||||
|
||||
SetParams(params);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrimaryDevice::Dispatch(scsi_command cmd)
|
||||
void PrimaryDevice::AddCommand(scsi_command opcode, const operation& execute)
|
||||
{
|
||||
return dispatcher.Dispatch(this, cmd);
|
||||
commands[opcode] = execute;
|
||||
}
|
||||
|
||||
void PrimaryDevice::Dispatch(scsi_command cmd)
|
||||
{
|
||||
if (const auto& it = commands.find(cmd); it != commands.end()) {
|
||||
LOGDEBUG("Executing %s ($%02X)", command_names.find(cmd)->second, static_cast<int>(cmd))
|
||||
|
||||
it->second();
|
||||
}
|
||||
else {
|
||||
LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetId(), GetLun(), static_cast<int>(cmd))
|
||||
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
void PrimaryDevice::Reset()
|
||||
@ -71,7 +88,7 @@ void PrimaryDevice::Inquiry()
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
vector<byte> buf = InquiryInternal();
|
||||
vector<uint8_t> buf = InquiryInternal();
|
||||
|
||||
const size_t allocation_length = min(buf.size(), static_cast<size_t>(GetInt16(controller->GetCmd(), 3)));
|
||||
|
||||
@ -187,9 +204,9 @@ void PrimaryDevice::CheckReady()
|
||||
LOGTRACE("%s Device is ready", __PRETTY_FUNCTION__)
|
||||
}
|
||||
|
||||
vector<byte> PrimaryDevice::HandleInquiry(device_type type, scsi_level level, bool is_removable) const
|
||||
vector<uint8_t> PrimaryDevice::HandleInquiry(device_type type, scsi_level level, bool is_removable) const
|
||||
{
|
||||
vector<byte> buf(0x1F + 5);
|
||||
vector<uint8_t> buf(0x1F + 5);
|
||||
|
||||
// Basic data
|
||||
// buf[0] ... SCSI device type
|
||||
@ -197,11 +214,12 @@ vector<byte> PrimaryDevice::HandleInquiry(device_type type, scsi_level level, bo
|
||||
// buf[2] ... SCSI compliance level of command system
|
||||
// buf[3] ... SCSI compliance level of Inquiry response
|
||||
// buf[4] ... Inquiry additional data
|
||||
buf[0] = (byte)type;
|
||||
buf[1] = (byte)(is_removable ? 0x80 : 0x00);
|
||||
buf[2] = (byte)level;
|
||||
buf[3] = (byte)(level >= scsi_level::SCSI_2 ? scsi_level::SCSI_2 : scsi_level::SCSI_1_CCS);
|
||||
buf[4] = (byte)0x1F;
|
||||
buf[0] = static_cast<uint8_t>(type);
|
||||
buf[1] = is_removable ? 0x80 : 0x00;
|
||||
buf[2] = static_cast<uint8_t>(level);
|
||||
buf[3] = level >= scsi_level::SCSI_2 ?
|
||||
static_cast<uint8_t>(scsi_level::SCSI_2) : static_cast<uint8_t>(scsi_level::SCSI_1_CCS);
|
||||
buf[4] = 0x1F;
|
||||
|
||||
// Padded vendor, product, revision
|
||||
memcpy(&buf.data()[8], GetPaddedName().c_str(), 28);
|
||||
@ -280,7 +298,7 @@ bool PrimaryDevice::CheckReservation(int initiator_id, scsi_command cmd, bool pr
|
||||
return true;
|
||||
}
|
||||
// PREVENT ALLOW MEDIUM REMOVAL is permitted if the prevent bit is 0
|
||||
if (cmd == scsi_command::eCmdRemoval && !prevent_removal) {
|
||||
if (cmd == scsi_command::eCmdPreventAllowMediumRemoval && !prevent_removal) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -11,24 +11,34 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scsi.h"
|
||||
#include "interfaces/scsi_primary_commands.h"
|
||||
#include "controllers/abstract_controller.h"
|
||||
#include "device.h"
|
||||
#include "dispatcher.h"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
using namespace scsi_defs;
|
||||
|
||||
class PrimaryDevice: private ScsiPrimaryCommands, public Device
|
||||
{
|
||||
using operation = function<void()>;
|
||||
|
||||
public:
|
||||
|
||||
PrimaryDevice(PbDeviceType, int);
|
||||
PrimaryDevice(PbDeviceType type, int lun) : Device(type, lun) {}
|
||||
~PrimaryDevice() override = default;
|
||||
|
||||
virtual bool Dispatch(scsi_command);
|
||||
virtual bool Init(const unordered_map<string, string>&);
|
||||
|
||||
virtual void Dispatch(scsi_command);
|
||||
|
||||
int GetId() const override;
|
||||
|
||||
void SetController(AbstractController *);
|
||||
|
||||
virtual bool WriteByteSequence(vector<uint8_t>&, uint32_t);
|
||||
|
||||
int GetSendDelay() const { return send_delay; }
|
||||
@ -36,8 +46,6 @@ public:
|
||||
bool CheckReservation(int, scsi_command, bool) const;
|
||||
void DiscardReservation();
|
||||
|
||||
// Override for device specific initializations
|
||||
virtual bool Init(const unordered_map<string, string>&) { return false; };
|
||||
void Reset() override;
|
||||
|
||||
virtual void FlushCache() {
|
||||
@ -46,15 +54,17 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
vector<byte> HandleInquiry(scsi_defs::device_type, scsi_level, bool) const;
|
||||
virtual vector<byte> InquiryInternal() const = 0;
|
||||
void AddCommand(scsi_command, const operation&);
|
||||
|
||||
vector<uint8_t> HandleInquiry(scsi_defs::device_type, scsi_level, bool) const;
|
||||
virtual vector<uint8_t> InquiryInternal() const = 0;
|
||||
void CheckReady();
|
||||
|
||||
void SetSendDelay(int s) { send_delay = s; }
|
||||
|
||||
virtual void SendDiagnostic();
|
||||
virtual void ReserveUnit();
|
||||
virtual void ReleaseUnit();
|
||||
void SendDiagnostic() override;
|
||||
void ReserveUnit() override;
|
||||
void ReleaseUnit() override;
|
||||
|
||||
void EnterStatusPhase() { controller->Status(); }
|
||||
void EnterDataInPhase() { controller->DataIn(); }
|
||||
@ -74,7 +84,7 @@ private:
|
||||
|
||||
vector<byte> HandleRequestSense() const;
|
||||
|
||||
Dispatcher<PrimaryDevice> dispatcher;
|
||||
unordered_map<scsi_command, operation> commands;
|
||||
|
||||
int send_delay = BUS::SEND_NO_DELAY;
|
||||
|
||||
|
@ -21,8 +21,9 @@ void scsi_command_util::ModeSelect(scsi_command cmd, const vector<int>& cdb, con
|
||||
|
||||
// PF
|
||||
if (!(cdb[1] & 0x10)) {
|
||||
// Vendor-specific parameters (SCSI-1) are not supported
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
// Vendor-specific parameters (SCSI-1) are not supported.
|
||||
// Do not report an error in order to support Apple's HD SC Setup.
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip block descriptors
|
||||
@ -62,7 +63,7 @@ void scsi_command_util::ModeSelect(scsi_command cmd, const vector<int>& cdb, con
|
||||
}
|
||||
|
||||
// Advance to the next page
|
||||
int size = buf[offset + 1] + 2;
|
||||
const int size = buf[offset + 1] + 2;
|
||||
|
||||
length -= size;
|
||||
offset += size;
|
||||
@ -77,9 +78,7 @@ void scsi_command_util::EnrichFormatPage(map<int, vector<byte>>& pages, bool cha
|
||||
{
|
||||
if (changeable) {
|
||||
// The sector size is simulated to be changeable, see the MODE SELECT implementation for details
|
||||
vector<byte>& format_page = pages[3];
|
||||
format_page[12] = (byte)(sector_size >> 8);
|
||||
format_page[13] = (byte)sector_size;
|
||||
SetInt16(pages[3], 12, sector_size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,15 +87,13 @@ void scsi_command_util::AddAppleVendorModePage(map<int, vector<byte>>& pages, bo
|
||||
// Page code 48 (30h) - Apple Vendor Mode Page
|
||||
// Needed for SCCD for stock Apple driver support
|
||||
// Needed for SCHD for stock Apple HD SC Setup
|
||||
vector<byte> buf(30);
|
||||
pages[48] = vector<byte>(30);
|
||||
|
||||
// No changeable area
|
||||
if (!changeable) {
|
||||
const char APPLE_DATA[] = "APPLE COMPUTER, INC ";
|
||||
memcpy(&buf.data()[2], APPLE_DATA, sizeof(APPLE_DATA));
|
||||
memcpy(&pages[48].data()[2], APPLE_DATA, sizeof(APPLE_DATA));
|
||||
}
|
||||
|
||||
pages[48] = buf;
|
||||
}
|
||||
|
||||
int scsi_command_util::GetInt16(const vector<uint8_t>& buf, int offset)
|
||||
|
@ -24,40 +24,31 @@
|
||||
// Note: This requires a DaynaPort SCSI Link driver.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "log.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include "dispatcher.h"
|
||||
#include "scsi_daynaport.h"
|
||||
|
||||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
SCSIDaynaPort::SCSIDaynaPort(int lun) : PrimaryDevice(SCDP, lun)
|
||||
bool SCSIDaynaPort::Init(const unordered_map<string, string>& params)
|
||||
{
|
||||
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIDaynaPort::TestUnitReady);
|
||||
dispatcher.Add(scsi_command::eCmdRead6, "Read6", &SCSIDaynaPort::Read6);
|
||||
dispatcher.Add(scsi_command::eCmdWrite6, "Write6", &SCSIDaynaPort::Write6);
|
||||
dispatcher.Add(scsi_command::eCmdRetrieveStats, "RetrieveStats", &SCSIDaynaPort::RetrieveStatistics);
|
||||
dispatcher.Add(scsi_command::eCmdSetIfaceMode, "SetIfaceMode", &SCSIDaynaPort::SetInterfaceMode);
|
||||
dispatcher.Add(scsi_command::eCmdSetMcastAddr, "SetMcastAddr", &SCSIDaynaPort::SetMcastAddr);
|
||||
dispatcher.Add(scsi_command::eCmdEnableInterface, "EnableInterface", &SCSIDaynaPort::EnableInterface);
|
||||
PrimaryDevice::Init(params);
|
||||
|
||||
AddCommand(scsi_command::eCmdTestUnitReady, [this] { TestUnitReady(); });
|
||||
AddCommand(scsi_command::eCmdRead6, [this] { Read6(); });
|
||||
AddCommand(scsi_command::eCmdWrite6, [this] { Write6(); });
|
||||
AddCommand(scsi_command::eCmdRetrieveStats, [this] { RetrieveStatistics(); });
|
||||
AddCommand(scsi_command::eCmdSetIfaceMode, [this] { SetInterfaceMode(); });
|
||||
AddCommand(scsi_command::eCmdSetMcastAddr, [this] { SetMcastAddr(); });
|
||||
AddCommand(scsi_command::eCmdEnableInterface, [this] { EnableInterface(); });
|
||||
|
||||
// The Daynaport needs to have a delay after the size/flags field of the read response.
|
||||
// In the MacOS driver, it looks like the driver is doing two "READ" system calls.
|
||||
SetSendDelay(DAYNAPORT_READ_HEADER_SZ);
|
||||
|
||||
SupportsParams(true);
|
||||
}
|
||||
|
||||
bool SCSIDaynaPort::Dispatch(scsi_command cmd)
|
||||
{
|
||||
// The superclass class handles the less specific commands
|
||||
return dispatcher.Dispatch(this, cmd) ? true : super::Dispatch(cmd);
|
||||
}
|
||||
|
||||
bool SCSIDaynaPort::Init(const unordered_map<string, string>& params)
|
||||
{
|
||||
SetParams(params);
|
||||
|
||||
m_bTapEnable = m_tap.Init(GetParams());
|
||||
if(!m_bTapEnable){
|
||||
@ -78,14 +69,14 @@ bool SCSIDaynaPort::Init(const unordered_map<string, string>& params)
|
||||
return true;
|
||||
}
|
||||
|
||||
vector<byte> SCSIDaynaPort::InquiryInternal() const
|
||||
vector<uint8_t> SCSIDaynaPort::InquiryInternal() const
|
||||
{
|
||||
vector<byte> buf = HandleInquiry(device_type::PROCESSOR, scsi_level::SCSI_2, false);
|
||||
vector<uint8_t> buf = HandleInquiry(device_type::PROCESSOR, scsi_level::SCSI_2, false);
|
||||
|
||||
// The Daynaport driver for the Mac expects 37 bytes: Increase additional length and
|
||||
// add a vendor-specific byte in order to satisfy this driver.
|
||||
buf[4] = (byte)(to_integer<int>(buf[4]) + 1);
|
||||
buf.push_back((byte)0);
|
||||
buf[4]++;
|
||||
buf.push_back(0);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
@ -45,13 +45,13 @@ class SCSIDaynaPort : public PrimaryDevice, public ByteWriter
|
||||
{
|
||||
public:
|
||||
|
||||
explicit SCSIDaynaPort(int);
|
||||
explicit SCSIDaynaPort(int lun) : PrimaryDevice(SCDP, lun) {}
|
||||
~SCSIDaynaPort() override = default;
|
||||
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
|
||||
// Commands
|
||||
vector<byte> InquiryInternal() const override;
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
int Read(const vector<int>&, vector<uint8_t>&, uint64_t);
|
||||
bool WriteBytes(const vector<int>&, vector<uint8_t>&, uint32_t) override;
|
||||
|
||||
@ -65,8 +65,6 @@ public:
|
||||
void SetMcastAddr();
|
||||
void EnableInterface();
|
||||
|
||||
bool Dispatch(scsi_command) override;
|
||||
|
||||
static const int DAYNAPORT_BUFFER_SIZE = 0x1000000;
|
||||
|
||||
static const int CMD_SCSILINK_STATS = 0x09;
|
||||
@ -88,10 +86,6 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
using super = PrimaryDevice;
|
||||
|
||||
Dispatcher<SCSIDaynaPort> dispatcher;
|
||||
|
||||
enum class read_data_flags_t : uint32_t {
|
||||
e_no_more_data = 0x00000000,
|
||||
e_more_data_available = 0x00000001,
|
||||
|
@ -16,9 +16,9 @@
|
||||
// work with the Sharp X68000 operating system.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "log.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include "dispatcher.h"
|
||||
#include "scsi_host_bridge.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <array>
|
||||
@ -27,21 +27,18 @@ using namespace std;
|
||||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
SCSIBR::SCSIBR(int lun) : PrimaryDevice(SCBR, lun)
|
||||
bool SCSIBR::Init(const unordered_map<string, string>& params)
|
||||
{
|
||||
PrimaryDevice::Init(params);
|
||||
|
||||
// Create host file system
|
||||
fs.Reset();
|
||||
|
||||
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIBR::TestUnitReady);
|
||||
dispatcher.Add(scsi_command::eCmdRead6, "GetMessage10", &SCSIBR::GetMessage10);
|
||||
dispatcher.Add(scsi_command::eCmdWrite6, "SendMessage10", &SCSIBR::SendMessage10);
|
||||
AddCommand(scsi_command::eCmdTestUnitReady, [this] { TestUnitReady(); });
|
||||
AddCommand(scsi_command::eCmdGetMessage10, [this] { GetMessage10(); });
|
||||
AddCommand(scsi_command::eCmdSendMessage10, [this] { SendMessage10(); });
|
||||
|
||||
SupportsParams(true);
|
||||
}
|
||||
|
||||
bool SCSIBR::Init(const unordered_map<string, string>& params)
|
||||
{
|
||||
SetParams(params);
|
||||
|
||||
#ifdef __linux__
|
||||
// TAP Driver Generation
|
||||
@ -71,31 +68,25 @@ bool SCSIBR::Init(const unordered_map<string, string>& params)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SCSIBR::Dispatch(scsi_command cmd)
|
||||
vector<uint8_t> SCSIBR::InquiryInternal() const
|
||||
{
|
||||
// The superclass class handles the less specific commands
|
||||
return dispatcher.Dispatch(this, cmd) ? true : super::Dispatch(cmd);
|
||||
}
|
||||
|
||||
vector<byte> SCSIBR::InquiryInternal() const
|
||||
{
|
||||
vector<byte> buf = HandleInquiry(device_type::COMMUNICATIONS, scsi_level::SCSI_2, false);
|
||||
vector<uint8_t> buf = HandleInquiry(device_type::COMMUNICATIONS, scsi_level::SCSI_2, false);
|
||||
|
||||
// The bridge returns more additional bytes than the other devices
|
||||
buf.resize(0x1F + 8 + 5);
|
||||
|
||||
buf[4] = byte{0x1F + 8};
|
||||
buf[4] = 0x1F + 8;
|
||||
|
||||
// Optional function valid flag
|
||||
buf[36] = byte{'0'};
|
||||
buf[36] = '0';
|
||||
|
||||
// TAP Enable
|
||||
if (m_bTapEnable) {
|
||||
buf[37] = byte{'1'};
|
||||
buf[37] = '1';
|
||||
}
|
||||
|
||||
// CFileSys Enable
|
||||
buf[38] = byte{'1'};
|
||||
buf[38] = '1';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
@ -32,14 +32,13 @@ class SCSIBR : public PrimaryDevice, public ByteWriter
|
||||
|
||||
public:
|
||||
|
||||
explicit SCSIBR(int);
|
||||
explicit SCSIBR(int lun) : PrimaryDevice(SCBR, lun) {}
|
||||
~SCSIBR() override = default;
|
||||
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
bool Dispatch(scsi_command) override;
|
||||
|
||||
// Commands
|
||||
vector<byte> InquiryInternal() const override;
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
int GetMessage10(const vector<int>&, vector<uint8_t>&);
|
||||
bool WriteBytes(const vector<int>&, vector<uint8_t>&, uint32_t) override;
|
||||
void TestUnitReady() override;
|
||||
@ -48,10 +47,6 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
using super = PrimaryDevice;
|
||||
|
||||
Dispatcher<SCSIBR> dispatcher;
|
||||
|
||||
int GetMacAddr(vector<uint8_t>&) const; // Get MAC address
|
||||
void SetMacAddr(const vector<uint8_t>&); // Set MAC address
|
||||
void ReceivePacket(); // Receive a packet
|
||||
|
@ -29,11 +29,11 @@
|
||||
// With STOP PRINT printing can be cancelled before SYNCHRONIZE BUFFER was sent.
|
||||
//
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "log.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include "dispatcher.h"
|
||||
#include "scsi_printer.h"
|
||||
#include <sys/stat.h>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace std;
|
||||
@ -41,18 +41,25 @@ using namespace filesystem;
|
||||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
SCSIPrinter::SCSIPrinter(int lun) : PrimaryDevice(SCLP, lun)
|
||||
bool SCSIPrinter::Init(const unordered_map<string, string>& params)
|
||||
{
|
||||
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIPrinter::TestUnitReady);
|
||||
dispatcher.Add(scsi_command::eCmdPrint, "Print", &SCSIPrinter::Print);
|
||||
dispatcher.Add(scsi_command::eCmdSynchronizeBuffer, "SynchronizeBuffer", &SCSIPrinter::SynchronizeBuffer);
|
||||
PrimaryDevice::Init(params);
|
||||
|
||||
AddCommand(scsi_command::eCmdTestUnitReady, [this] { TestUnitReady(); });
|
||||
AddCommand(scsi_command::eCmdPrint, [this] { Print(); });
|
||||
AddCommand(scsi_command::eCmdSynchronizeBuffer, [this] { SynchronizeBuffer(); });
|
||||
// STOP PRINT is identical with TEST UNIT READY, it just returns the status
|
||||
dispatcher.Add(scsi_command::eCmdStopPrint, "StopPrint", &SCSIPrinter::TestUnitReady);
|
||||
AddCommand(scsi_command::eCmdStopPrint, [this] { TestUnitReady(); });
|
||||
|
||||
// Required also in this class in order to fulfill the ScsiPrinterCommands interface contract
|
||||
dispatcher.Add(scsi_command::eCmdReserve6, "ReserveUnit", &SCSIPrinter::ReserveUnit);
|
||||
dispatcher.Add(scsi_command::eCmdRelease6, "ReleaseUnit", &SCSIPrinter::ReleaseUnit);
|
||||
dispatcher.Add(scsi_command::eCmdSendDiag, "SendDiagnostic", &SCSIPrinter::SendDiagnostic);
|
||||
AddCommand(scsi_command::eCmdReserve6, [this] { ReserveUnit(); });
|
||||
AddCommand(scsi_command::eCmdRelease6, [this] { ReleaseUnit(); });
|
||||
AddCommand(scsi_command::eCmdSendDiagnostic, [this] { SendDiagnostic(); });
|
||||
|
||||
if (GetParam("cmd").find("%f") == string::npos) {
|
||||
LOGERROR("Missing filename specifier %%f")
|
||||
return false;
|
||||
}
|
||||
|
||||
error_code error;
|
||||
file_template = temp_directory_path(error); //NOSONAR Publicly writable directory is fine here
|
||||
@ -60,38 +67,17 @@ SCSIPrinter::SCSIPrinter(int lun) : PrimaryDevice(SCLP, lun)
|
||||
|
||||
SupportsParams(true);
|
||||
SetReady(true);
|
||||
}
|
||||
|
||||
SCSIPrinter::~SCSIPrinter()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
bool SCSIPrinter::Init(const unordered_map<string, string>& params)
|
||||
{
|
||||
SetParams(params);
|
||||
|
||||
if (GetParam("cmd").find("%f") == string::npos) {
|
||||
LOGERROR("Missing filename specifier %s", "%f")
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SCSIPrinter::Dispatch(scsi_command cmd)
|
||||
{
|
||||
// The superclass class handles the less specific commands
|
||||
return dispatcher.Dispatch(this, cmd) ? true : super::Dispatch(cmd);
|
||||
}
|
||||
|
||||
void SCSIPrinter::TestUnitReady()
|
||||
{
|
||||
// The printer is always ready
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
vector<byte> SCSIPrinter::InquiryInternal() const
|
||||
vector<uint8_t> SCSIPrinter::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(device_type::PRINTER, scsi_level::SCSI_2, false);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
class SCSIPrinter : public PrimaryDevice, ScsiPrinterCommands //NOSONAR Custom destructor cannot be removed
|
||||
class SCSIPrinter : public PrimaryDevice, private ScsiPrinterCommands
|
||||
{
|
||||
static const int NOT_RESERVED = -2;
|
||||
|
||||
@ -26,15 +26,17 @@ class SCSIPrinter : public PrimaryDevice, ScsiPrinterCommands //NOSONAR Custom d
|
||||
|
||||
public:
|
||||
|
||||
explicit SCSIPrinter(int);
|
||||
~SCSIPrinter() override;
|
||||
|
||||
bool Dispatch(scsi_command) override;
|
||||
explicit SCSIPrinter(int lun) : PrimaryDevice(SCLP, lun) {}
|
||||
~SCSIPrinter() override = default;
|
||||
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
void Cleanup();
|
||||
|
||||
vector<byte> InquiryInternal() const override;
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
|
||||
bool WriteByteSequence(vector<uint8_t>&, uint32_t) override;
|
||||
|
||||
private:
|
||||
|
||||
void TestUnitReady() override;
|
||||
void ReserveUnit() override { PrimaryDevice::ReserveUnit(); }
|
||||
void ReleaseUnit() override { PrimaryDevice::ReleaseUnit(); }
|
||||
@ -42,13 +44,7 @@ public:
|
||||
void Print() override;
|
||||
void SynchronizeBuffer();
|
||||
|
||||
bool WriteByteSequence(vector<uint8_t>&, uint32_t) override;
|
||||
|
||||
private:
|
||||
|
||||
using super = PrimaryDevice;
|
||||
|
||||
Dispatcher<SCSIPrinter> dispatcher;
|
||||
void Cleanup();
|
||||
|
||||
string file_template;
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include "dispatcher.h"
|
||||
#include "scsicd.h"
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
@ -27,18 +26,19 @@ using namespace scsi_command_util;
|
||||
SCSICD::SCSICD(int lun, const unordered_set<uint32_t>& sector_sizes) : Disk(SCCD, lun)
|
||||
{
|
||||
SetSectorSizes(sector_sizes);
|
||||
}
|
||||
|
||||
dispatcher.Add(scsi_command::eCmdReadToc, "ReadToc", &SCSICD::ReadToc);
|
||||
bool SCSICD::Init(const unordered_map<string, string>& params)
|
||||
{
|
||||
Disk::Init(params);
|
||||
|
||||
AddCommand(scsi_command::eCmdReadToc, [this] { ReadToc(); });
|
||||
|
||||
SetReadOnly(true);
|
||||
SetRemovable(true);
|
||||
SetLockable(true);
|
||||
}
|
||||
|
||||
bool SCSICD::Dispatch(scsi_command cmd)
|
||||
{
|
||||
// The superclass class handles the less specific commands
|
||||
return dispatcher.Dispatch(this, cmd) ? true : super::Dispatch(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SCSICD::Open()
|
||||
@ -72,7 +72,7 @@ void SCSICD::Open()
|
||||
}
|
||||
}
|
||||
|
||||
super::ValidateFile();
|
||||
Disk::ValidateFile();
|
||||
|
||||
SetUpCache(0, rawfile);
|
||||
|
||||
@ -167,14 +167,14 @@ void SCSICD::ReadToc()
|
||||
EnterDataInPhase();
|
||||
}
|
||||
|
||||
vector<byte> SCSICD::InquiryInternal() const
|
||||
vector<uint8_t> SCSICD::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(device_type::CD_ROM, scsi_level::SCSI_2, true);
|
||||
}
|
||||
|
||||
void SCSICD::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
|
||||
{
|
||||
super::SetUpModePages(pages, page, changeable);
|
||||
Disk::SetUpModePages(pages, page, changeable);
|
||||
|
||||
// Page code 13
|
||||
if (page == 0x0d || page == 0x3f) {
|
||||
@ -251,7 +251,7 @@ int SCSICD::Read(const vector<int>& cdb, vector<uint8_t>& buf, uint64_t block)
|
||||
|
||||
// Base class
|
||||
assert(dataindex >= 0);
|
||||
return super::Read(cdb, buf, block);
|
||||
return Disk::Read(cdb, buf, block);
|
||||
}
|
||||
|
||||
int SCSICD::ReadTocInternal(const vector<int>& cdb, vector<uint8_t>& buf)
|
||||
|
@ -25,12 +25,11 @@ public:
|
||||
SCSICD(int, const unordered_set<uint32_t>&);
|
||||
~SCSICD() override = default;
|
||||
|
||||
bool Dispatch(scsi_command) override;
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
|
||||
void Open() override;
|
||||
|
||||
// Commands
|
||||
vector<byte> InquiryInternal() const override;
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
int Read(const vector<int>&, vector<uint8_t>&, uint64_t) override;
|
||||
|
||||
protected:
|
||||
@ -40,10 +39,6 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
using super = Disk;
|
||||
|
||||
Dispatcher<SCSICD> dispatcher;
|
||||
|
||||
int ReadTocInternal(const vector<int>&, vector<uint8_t>&);
|
||||
|
||||
void AddCDROMPage(map<int, vector<byte>>&, bool) const;
|
||||
|
@ -56,7 +56,7 @@ string SCSIHD::GetProductData() const
|
||||
|
||||
void SCSIHD::FinalizeSetup(off_t image_offset)
|
||||
{
|
||||
super::ValidateFile();
|
||||
Disk::ValidateFile();
|
||||
|
||||
// For non-removable media drives set the default product name based on the drive capacity
|
||||
if (!IsRemovable()) {
|
||||
@ -79,7 +79,7 @@ void SCSIHD::Open()
|
||||
FinalizeSetup(0);
|
||||
}
|
||||
|
||||
vector<byte> SCSIHD::InquiryInternal() const
|
||||
vector<uint8_t> SCSIHD::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(device_type::DIRECT_ACCESS, scsi_level, IsRemovable());
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ public:
|
||||
void Open() override;
|
||||
|
||||
// Commands
|
||||
vector<byte> InquiryInternal() const override;
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
void ModeSelect(scsi_defs::scsi_command, const vector<int>&, const vector<uint8_t>&, int) const override;
|
||||
|
||||
void AddFormatPage(map<int, vector<byte>>&, bool) const override;
|
||||
@ -45,7 +45,5 @@ private:
|
||||
|
||||
string GetProductData() const;
|
||||
|
||||
using super = Disk;
|
||||
|
||||
scsi_defs::scsi_level scsi_level;
|
||||
};
|
||||
|
@ -112,7 +112,7 @@ pair<int, int> SCSIHD_NEC::SetParameters(const array<char, 512>& data, int size)
|
||||
return make_pair(image_size, sector_size);
|
||||
}
|
||||
|
||||
vector<byte> SCSIHD_NEC::InquiryInternal() const
|
||||
vector<uint8_t> SCSIHD_NEC::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_1_CCS, false);
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
vector<byte> InquiryInternal() const override;
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
|
||||
void AddFormatPage(map<int, vector<byte>>&, bool) const override;
|
||||
void AddDrivePage(map<int, vector<byte>>&, bool) const override;
|
||||
|
@ -49,7 +49,7 @@ void SCSIMO::Open()
|
||||
SetBlockCount(size >> GetSectorSizeShiftCount());
|
||||
}
|
||||
|
||||
super::ValidateFile();
|
||||
Disk::ValidateFile();
|
||||
|
||||
SetUpCache(0);
|
||||
|
||||
@ -59,7 +59,7 @@ void SCSIMO::Open()
|
||||
}
|
||||
}
|
||||
|
||||
vector<byte> SCSIMO::InquiryInternal() const
|
||||
vector<uint8_t> SCSIMO::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(device_type::OPTICAL_MEMORY, scsi_level::SCSI_2, true);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public:
|
||||
|
||||
void Open() override;
|
||||
|
||||
vector<byte> InquiryInternal() const override;
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
void ModeSelect(scsi_defs::scsi_command, const vector<int>&, const vector<uint8_t>&, int) const override;
|
||||
|
||||
protected:
|
||||
@ -40,8 +40,6 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
using super = Disk;
|
||||
|
||||
void AddOptionPage(map<int, vector<byte>>&, bool) const;
|
||||
|
||||
bool SetGeometryForCapacity(uint64_t);
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "storage_device.h"
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <filesystem>
|
||||
|
||||
@ -49,13 +50,6 @@ void StorageDevice::ValidateFile()
|
||||
SetReady(true);
|
||||
}
|
||||
|
||||
void StorageDevice::MediumChanged()
|
||||
{
|
||||
assert(IsRemovable());
|
||||
|
||||
medium_changed = true;
|
||||
}
|
||||
|
||||
void StorageDevice::ReserveFile(const string& file, int id, int lun) const
|
||||
{
|
||||
assert(!file.empty());
|
||||
@ -71,7 +65,7 @@ void StorageDevice::UnreserveFile()
|
||||
filename = "";
|
||||
}
|
||||
|
||||
pair<int, int> StorageDevice::GetIdsForReservedFile(const string& file)
|
||||
id_set StorageDevice::GetIdsForReservedFile(const string& file)
|
||||
{
|
||||
if (const auto& it = reserved_files.find(file); it != reserved_files.end()) {
|
||||
return make_pair(it->second.first, it->second.second);
|
||||
|
@ -31,8 +31,6 @@ public:
|
||||
string GetFilename() const { return filename; }
|
||||
void SetFilename(string_view f) { filename = f; }
|
||||
|
||||
void MediumChanged();
|
||||
|
||||
uint64_t GetBlockCount() const { return blocks; }
|
||||
|
||||
void ReserveFile(const string&, int, int) const;
|
||||
@ -42,16 +40,17 @@ public:
|
||||
static bool FileExists(const string&);
|
||||
bool IsReadOnlyFile() const;
|
||||
|
||||
void SetMediumChanged(bool b) { medium_changed = b; }
|
||||
|
||||
static unordered_map<string, id_set> GetReservedFiles() { return reserved_files; }
|
||||
static void SetReservedFiles(const unordered_map<string, id_set>& r) { reserved_files = r; }
|
||||
static pair<int, int> GetIdsForReservedFile(const string&);
|
||||
static id_set GetIdsForReservedFile(const string&);
|
||||
|
||||
protected:
|
||||
|
||||
void ValidateFile();
|
||||
|
||||
bool IsMediumChanged() const { return medium_changed; }
|
||||
void SetMediumChanged(bool b) { medium_changed = b; }
|
||||
|
||||
void SetBlockCount(uint64_t b) { blocks = b; }
|
||||
|
||||
|
@ -505,7 +505,7 @@ int GPIOBUS::CommandHandShake(uint8_t *buf)
|
||||
}
|
||||
}
|
||||
|
||||
int command_byte_count = GetCommandByteCount(*buf);
|
||||
const int command_byte_count = GetCommandByteCount(*buf);
|
||||
|
||||
// Increment buffer pointer
|
||||
buf++;
|
||||
@ -966,23 +966,3 @@ BUS::phase_t GPIOBUS::GetPhaseRaw(uint32_t raw_data)
|
||||
mci |= GetPinRaw(raw_data, PIN_IO) ? 0x01 : 0x00;
|
||||
return GetPhase(mci);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Get the number of bytes for a command
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int GPIOBUS::GetCommandByteCount(uint8_t opcode)
|
||||
{
|
||||
GPIO_FUNCTION_TRACE
|
||||
|
||||
if (opcode == 0x88 || opcode == 0x8A || opcode == 0x8F || opcode == 0x91 || opcode == 0x9E || opcode == 0x9F) {
|
||||
return 16;
|
||||
} else if (opcode == 0xA0) {
|
||||
return 12;
|
||||
} else if (opcode >= 0x20 && opcode <= 0x7D) {
|
||||
return 10;
|
||||
} else {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
@ -342,8 +342,6 @@ class GPIOBUS : public BUS
|
||||
static BUS::phase_t GetPhaseRaw(uint32_t raw_data);
|
||||
// Get the phase based on raw data
|
||||
|
||||
static int GetCommandByteCount(uint8_t opcode);
|
||||
|
||||
#ifdef USE_SEL_EVENT_ENABLE
|
||||
// SEL signal interrupt
|
||||
bool PollSelectEvent();
|
||||
|
@ -318,7 +318,7 @@ bool RascsiExecutor::Attach(const CommandContext& context, const PbDeviceDefinit
|
||||
params.erase("file");
|
||||
}
|
||||
|
||||
if (device->SupportsParams() && !device->Init(params)) {
|
||||
if (!device->Init(params)) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_INITIALIZATION, PbDeviceType_Name(device->GetType()),
|
||||
to_string(id), to_string(lun));
|
||||
}
|
||||
@ -389,7 +389,7 @@ bool RascsiExecutor::Insert(const CommandContext& context, const PbDeviceDefinit
|
||||
|
||||
storage_device->SetProtected(pb_device.protected_());
|
||||
storage_device->ReserveFile(full_path, storage_device->GetId(), storage_device->GetLun());
|
||||
storage_device->MediumChanged();
|
||||
storage_device->SetMediumChanged(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -522,7 +522,7 @@ string RascsiExecutor::SetReservedIds(string_view ids)
|
||||
if (!reserved_ids.empty()) {
|
||||
string s;
|
||||
bool isFirst = true;
|
||||
for (auto const& reserved_id : reserved) {
|
||||
for (const auto& reserved_id : reserved) {
|
||||
if (!isFirst) {
|
||||
s += ", ";
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "log.h"
|
||||
#include "controllers/controller_manager.h"
|
||||
#include "devices/disk.h"
|
||||
#include "devices/device_factory.h"
|
||||
|
64
cpp/scsi.h
64
cpp/scsi.h
@ -9,6 +9,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace scsi_defs {
|
||||
enum class scsi_level : int {
|
||||
SCSI_1_CCS = 1,
|
||||
@ -30,17 +34,20 @@ namespace scsi_defs {
|
||||
COMMUNICATIONS = 9
|
||||
};
|
||||
|
||||
// TODO Use a mapping of enum to structure with command byte count and enum name
|
||||
enum class scsi_command : int {
|
||||
eCmdTestUnitReady = 0x00,
|
||||
eCmdRezero = 0x01,
|
||||
eCmdRequestSense = 0x03,
|
||||
eCmdFormat = 0x04,
|
||||
eCmdReassign = 0x07,
|
||||
eCmdFormatUnit = 0x04,
|
||||
eCmdReassignBlocks = 0x07,
|
||||
eCmdRead6 = 0x08,
|
||||
// Bridge specific command
|
||||
eCmdGetMessage10 = 0x08,
|
||||
// DaynaPort specific command
|
||||
eCmdRetrieveStats = 0x09,
|
||||
eCmdWrite6 = 0x0A,
|
||||
// Bridge specific ommand
|
||||
eCmdSendMessage10 = 0x0A,
|
||||
eCmdPrint = 0x0A,
|
||||
eCmdSeek6 = 0x0B,
|
||||
// DaynaPort specific command
|
||||
@ -57,10 +64,8 @@ namespace scsi_defs {
|
||||
eCmdModeSense6 = 0x1A,
|
||||
eCmdStartStop = 0x1B,
|
||||
eCmdStopPrint = 0x1B,
|
||||
eCmdSendDiag = 0x1D,
|
||||
eCmdRemoval = 0x1E,
|
||||
// ICD specific command, evaluated by the controller
|
||||
eCmdIcd = 0x1F,
|
||||
eCmdSendDiagnostic = 0x1D,
|
||||
eCmdPreventAllowMediumRemoval = 0x1E,
|
||||
eCmdReadCapacity10 = 0x25,
|
||||
eCmdRead10 = 0x28,
|
||||
eCmdWrite10 = 0x2A,
|
||||
@ -114,4 +119,49 @@ namespace scsi_defs {
|
||||
MEDIUM_NOT_PRESENT = 0x3a,
|
||||
LOAD_OR_EJECT_FAILED = 0x53
|
||||
};
|
||||
|
||||
static const unordered_map<scsi_command, const char *> command_names = {
|
||||
{ scsi_command::eCmdTestUnitReady, "TestUnitReady" },
|
||||
{ scsi_command::eCmdRezero, "Rezero" },
|
||||
{ scsi_command::eCmdRequestSense, "RequestSense" },
|
||||
{ scsi_command::eCmdFormatUnit, "FormatUnit" },
|
||||
{ scsi_command::eCmdReassignBlocks, "ReassignBlocks" },
|
||||
{ scsi_command::eCmdRead6, "Read6/GetMessage10" },
|
||||
{ scsi_command::eCmdRetrieveStats, "RetrieveStats" },
|
||||
{ scsi_command::eCmdWrite6, "Write6/Print/SendMessage10" },
|
||||
{ scsi_command::eCmdSeek6, "Seek6" },
|
||||
{ scsi_command::eCmdSetIfaceMode, "SetIfaceMode" },
|
||||
{ scsi_command::eCmdSetMcastAddr, "SetMcastAddr" },
|
||||
{ scsi_command::eCmdEnableInterface, "EnableInterface" },
|
||||
{ scsi_command::eCmdSynchronizeBuffer, "SynchronizeBuffer" },
|
||||
{ scsi_command::eCmdInquiry, "Inquiry" },
|
||||
{ scsi_command::eCmdModeSelect6, "ModeSelect6" },
|
||||
{ scsi_command::eCmdReserve6, "Reserve6" },
|
||||
{ scsi_command::eCmdRelease6, "Release6" },
|
||||
{ scsi_command::eCmdModeSense6, "ModeSense6" },
|
||||
{ scsi_command::eCmdStartStop, "StartStop" },
|
||||
{ scsi_command::eCmdStopPrint, "StopPrint" },
|
||||
{ scsi_command::eCmdSendDiagnostic, "SendDiagnostic" },
|
||||
{ scsi_command::eCmdPreventAllowMediumRemoval, "PreventAllowMediumRemoval" },
|
||||
{ scsi_command::eCmdReadCapacity10, "ReadCapacity10" },
|
||||
{ scsi_command::eCmdRead10, "Read10" },
|
||||
{ scsi_command::eCmdWrite10, "Write10" },
|
||||
{ scsi_command::eCmdSeek10, "Seek10" },
|
||||
{ scsi_command::eCmdVerify10, "Verify10" },
|
||||
{ scsi_command::eCmdSynchronizeCache10, "SynchronizeCache10" },
|
||||
{ scsi_command::eCmdReadDefectData10, "ReadDefectData10" },
|
||||
{ scsi_command::eCmdReadLong10, "ReadLong10" },
|
||||
{ scsi_command::eCmdWriteLong10, "WriteLong10" },
|
||||
{ scsi_command::eCmdReadToc, "ReadToc" },
|
||||
{ scsi_command::eCmdGetEventStatusNotification, "GetEventStatusNotification" },
|
||||
{ scsi_command::eCmdModeSelect10, "ModeSelect10" },
|
||||
{ scsi_command::eCmdModeSense10, "ModeSense10" },
|
||||
{ scsi_command::eCmdRead16, "Read16" },
|
||||
{ scsi_command::eCmdWrite16, "Write16" },
|
||||
{ scsi_command::eCmdVerify16, "Verify16" },
|
||||
{ scsi_command::eCmdSynchronizeCache16, "SynchronizeCache16" },
|
||||
{ scsi_command::eCmdReadCapacity16_ReadLong16, "ReadCapacity16/ReadLong16" },
|
||||
{ scsi_command::eCmdWriteLong16, "WriteLong16" },
|
||||
{ scsi_command::eCmdReportLuns, "ReportLuns" }
|
||||
};
|
||||
};
|
||||
|
@ -48,6 +48,24 @@ TEST(AbstractControllerTest, Reset)
|
||||
EXPECT_EQ(0, controller.GetLength());
|
||||
}
|
||||
|
||||
TEST(AbstractControllerTest, Next)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
|
||||
controller.SetNext(0x1234);
|
||||
EXPECT_EQ(0x1234, controller.GetNext());
|
||||
controller.IncrementNext();
|
||||
EXPECT_EQ(0x1235, controller.GetNext());
|
||||
}
|
||||
|
||||
TEST(AbstractControllerTest, Message)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
|
||||
controller.SetMessage(0x12);
|
||||
EXPECT_EQ(0x12, controller.GetMessage());
|
||||
}
|
||||
|
||||
TEST(AbstractControllerTest, ByteTransfer)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
@ -58,6 +76,16 @@ TEST(AbstractControllerTest, ByteTransfer)
|
||||
EXPECT_TRUE(controller.IsByteTransfer());
|
||||
}
|
||||
|
||||
TEST(AbstractControllerTest, BytesToTransfer)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
|
||||
controller.SetBytesToTransfer(0x1234);
|
||||
EXPECT_EQ(0x1234, controller.GetBytesToTransfer());
|
||||
controller.SetByteTransfer(false);
|
||||
EXPECT_EQ(0, controller.GetBytesToTransfer());
|
||||
}
|
||||
|
||||
TEST(AbstractControllerTest, GetMaxLuns)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
@ -78,35 +106,35 @@ TEST(AbstractControllerTest, ProcessPhase)
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
|
||||
controller.SetPhase(BUS::phase_t::selection);
|
||||
EXPECT_CALL(controller, Selection());
|
||||
EXPECT_CALL(controller, Selection);
|
||||
controller.ProcessPhase();
|
||||
|
||||
controller.SetPhase(BUS::phase_t::busfree);
|
||||
EXPECT_CALL(controller, BusFree());
|
||||
EXPECT_CALL(controller, BusFree);
|
||||
controller.ProcessPhase();
|
||||
|
||||
controller.SetPhase(BUS::phase_t::datain);
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_CALL(controller, DataIn);
|
||||
controller.ProcessPhase();
|
||||
|
||||
controller.SetPhase(BUS::phase_t::dataout);
|
||||
EXPECT_CALL(controller, DataOut());
|
||||
EXPECT_CALL(controller, DataOut);
|
||||
controller.ProcessPhase();
|
||||
|
||||
controller.SetPhase(BUS::phase_t::command);
|
||||
EXPECT_CALL(controller, Command());
|
||||
EXPECT_CALL(controller, Command);
|
||||
controller.ProcessPhase();
|
||||
|
||||
controller.SetPhase(BUS::phase_t::status);
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_CALL(controller, Status);
|
||||
controller.ProcessPhase();
|
||||
|
||||
controller.SetPhase(BUS::phase_t::msgin);
|
||||
EXPECT_CALL(controller, MsgIn());
|
||||
EXPECT_CALL(controller, MsgIn);
|
||||
controller.ProcessPhase();
|
||||
|
||||
controller.SetPhase(BUS::phase_t::msgout);
|
||||
EXPECT_CALL(controller, MsgOut());
|
||||
EXPECT_CALL(controller, MsgOut);
|
||||
controller.ProcessPhase();
|
||||
|
||||
controller.SetPhase(BUS::phase_t::reselection);
|
||||
@ -162,7 +190,7 @@ TEST(AbstractControllerTest, GetOpcode)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
cmd[0] = static_cast<int>(scsi_command::eCmdInquiry);
|
||||
EXPECT_EQ(scsi_command::eCmdInquiry, controller.GetOpcode());
|
||||
@ -174,7 +202,7 @@ TEST(AbstractControllerTest, GetLun)
|
||||
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
cmd[1] = LUN << 5;
|
||||
EXPECT_EQ(LUN, controller.GetLun());
|
||||
|
@ -10,6 +10,37 @@
|
||||
#include "mocks.h"
|
||||
#include "bus.h"
|
||||
|
||||
TEST(BusTest, GetCommandByteCount)
|
||||
{
|
||||
unordered_set<int> opcodes;
|
||||
|
||||
EXPECT_EQ(16, BUS::GetCommandByteCount(0x88));
|
||||
opcodes.insert(0x88);
|
||||
EXPECT_EQ(16, BUS::GetCommandByteCount(0x8a));
|
||||
opcodes.insert(0x8a);
|
||||
EXPECT_EQ(16, BUS::GetCommandByteCount(0x8f));
|
||||
opcodes.insert(0x8f);
|
||||
EXPECT_EQ(16, BUS::GetCommandByteCount(0x91));
|
||||
opcodes.insert(0x91);
|
||||
EXPECT_EQ(16, BUS::GetCommandByteCount(0x9e));
|
||||
opcodes.insert(0x9e);
|
||||
EXPECT_EQ(16, BUS::GetCommandByteCount(0x9f));
|
||||
opcodes.insert(0x9f);
|
||||
EXPECT_EQ(12, BUS::GetCommandByteCount(0xa0));
|
||||
opcodes.insert(0xa0);
|
||||
|
||||
for (int i = 0x20; i <= 0x7d; i++) {
|
||||
EXPECT_EQ(10, BUS::GetCommandByteCount(i));
|
||||
opcodes.insert(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (opcodes.find(i) == opcodes.end()) {
|
||||
EXPECT_EQ(6, BUS::GetCommandByteCount(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BusTest, GetPhase)
|
||||
{
|
||||
EXPECT_EQ(BUS::phase_t::dataout, BUS::GetPhase(0b000));
|
||||
|
@ -17,8 +17,8 @@ TEST(ControllerManagerTest, LifeCycle)
|
||||
const int LUN1 = 0;
|
||||
const int LUN2 = 3;
|
||||
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
auto bus = make_shared<MockBus>();
|
||||
ControllerManager controller_manager(bus);
|
||||
DeviceFactory device_factory;
|
||||
|
||||
auto device = device_factory.CreateDevice(controller_manager, SCHS, -1, "");
|
||||
@ -52,8 +52,8 @@ TEST(ControllerManagerTest, AttachToScsiController)
|
||||
const int LUN1 = 3;
|
||||
const int LUN2 = 0;
|
||||
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
auto bus = make_shared<MockBus>();
|
||||
ControllerManager controller_manager(bus);
|
||||
DeviceFactory device_factory;
|
||||
|
||||
auto device1 = device_factory.CreateDevice(controller_manager, SCHS, LUN1, "");
|
||||
@ -69,8 +69,8 @@ TEST(ControllerManagerTest, ResetAllControllers)
|
||||
{
|
||||
const int ID = 2;
|
||||
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
auto bus = make_shared<MockBus>();
|
||||
ControllerManager controller_manager(bus);
|
||||
DeviceFactory device_factory;
|
||||
|
||||
auto device = device_factory.CreateDevice(controller_manager, SCHS, 0, "");
|
||||
|
@ -122,9 +122,9 @@ TEST(DeviceFactoryTest, GetDefaultParams)
|
||||
|
||||
TEST(DeviceFactoryTest, UnknownDeviceType)
|
||||
{
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
auto bus = make_shared<MockBus>();
|
||||
DeviceFactory device_factory;
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
ControllerManager controller_manager(bus);
|
||||
|
||||
auto device1 = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "test");
|
||||
EXPECT_EQ(nullptr, device1);
|
||||
@ -138,11 +138,14 @@ TEST(DeviceFactoryTest, UnknownDeviceType)
|
||||
|
||||
TEST(DeviceFactoryTest, SCHD_Device_Defaults)
|
||||
{
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
auto bus = make_shared<MockBus>();
|
||||
DeviceFactory device_factory;
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
ControllerManager controller_manager(bus);
|
||||
|
||||
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "test.hda");
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_EQ(SCHD, device->GetType());
|
||||
EXPECT_TRUE(device->SupportsFile());
|
||||
@ -177,11 +180,14 @@ TEST(DeviceFactoryTest, SCHD_Device_Defaults)
|
||||
|
||||
void TestRemovableDrive(PbDeviceType type, const string& filename, const string& product)
|
||||
{
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
auto bus = make_shared<MockBus>();
|
||||
DeviceFactory device_factory;
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
ControllerManager controller_manager(bus);
|
||||
|
||||
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, filename);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_EQ(type, device->GetType());
|
||||
EXPECT_TRUE(device->SupportsFile());
|
||||
@ -215,11 +221,14 @@ TEST(DeviceFactoryTest, SCMO_Device_Defaults)
|
||||
|
||||
TEST(DeviceFactoryTest, SCCD_Device_Defaults)
|
||||
{
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
auto bus = make_shared<MockBus>();
|
||||
DeviceFactory device_factory;
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
ControllerManager controller_manager(bus);
|
||||
|
||||
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "test.iso");
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_EQ(SCCD, device->GetType());
|
||||
EXPECT_TRUE(device->SupportsFile());
|
||||
@ -242,11 +251,14 @@ TEST(DeviceFactoryTest, SCCD_Device_Defaults)
|
||||
|
||||
TEST(DeviceFactoryTest, SCBR_Device_Defaults)
|
||||
{
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
auto bus = make_shared<MockBus>();
|
||||
DeviceFactory device_factory;
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
ControllerManager controller_manager(bus);
|
||||
|
||||
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "bridge");
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_EQ(SCBR, device->GetType());
|
||||
EXPECT_FALSE(device->SupportsFile());
|
||||
@ -269,11 +281,14 @@ TEST(DeviceFactoryTest, SCBR_Device_Defaults)
|
||||
|
||||
TEST(DeviceFactoryTest, SCDP_Device_Defaults)
|
||||
{
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
auto bus = make_shared<MockBus>();
|
||||
DeviceFactory device_factory;
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
ControllerManager controller_manager(bus);
|
||||
|
||||
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "daynaport");
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_EQ(SCDP, device->GetType());
|
||||
EXPECT_FALSE(device->SupportsFile());
|
||||
@ -295,11 +310,14 @@ TEST(DeviceFactoryTest, SCDP_Device_Defaults)
|
||||
|
||||
TEST(DeviceFactoryTest, SCHS_Device_Defaults)
|
||||
{
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
auto bus = make_shared<MockBus>();
|
||||
DeviceFactory device_factory;
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
ControllerManager controller_manager(bus);
|
||||
|
||||
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "services");
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_EQ(SCHS, device->GetType());
|
||||
EXPECT_FALSE(device->SupportsFile());
|
||||
@ -322,11 +340,14 @@ TEST(DeviceFactoryTest, SCHS_Device_Defaults)
|
||||
|
||||
TEST(DeviceFactoryTest, SCLP_Device_Defaults)
|
||||
{
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
auto bus = make_shared<MockBus>();
|
||||
DeviceFactory device_factory;
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
ControllerManager controller_manager(bus);
|
||||
|
||||
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "printer");
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_EQ(SCLP, device->GetType());
|
||||
EXPECT_FALSE(device->SupportsFile());
|
||||
|
@ -19,18 +19,31 @@ using namespace scsi_command_util;
|
||||
TEST(DiskTest, Dispatch)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
const unordered_set<uint32_t> sector_sizes;
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
EXPECT_FALSE(disk->Dispatch(scsi_command::eCmdIcd)) << "Command is not supported by this class";
|
||||
disk->SetRemovable(true);
|
||||
disk->SetMediumChanged(false);
|
||||
disk->SetReady(true);
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdTestUnitReady);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
disk->SetMediumChanged(true);
|
||||
EXPECT_CALL(controller, Error);
|
||||
disk->Dispatch(scsi_command::eCmdTestUnitReady);
|
||||
EXPECT_FALSE(disk->IsMediumChanged());
|
||||
}
|
||||
|
||||
TEST(DiskTest, Rezero)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
@ -41,8 +54,8 @@ TEST(DiskTest, Rezero)
|
||||
|
||||
disk->SetReady(true);
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdRezero));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdRezero);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -50,25 +63,27 @@ TEST(DiskTest, FormatUnit)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdFormat); }, Throws<scsi_exception>(AllOf(
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdFormatUnit); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::NOT_READY),
|
||||
Property(&scsi_exception::get_asc, asc::MEDIUM_NOT_PRESENT))))
|
||||
<< "FORMAT UNIT must fail because drive is not ready";
|
||||
|
||||
disk->SetReady(true);
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdFormat));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdFormatUnit);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
cmd[1] = 0x10;
|
||||
cmd[4] = 1;
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdFormat); }, Throws<scsi_exception>(AllOf(
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdFormatUnit); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_CDB))));
|
||||
}
|
||||
@ -77,18 +92,20 @@ TEST(DiskTest, ReassignBlocks)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdReassign); }, Throws<scsi_exception>(AllOf(
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdReassignBlocks); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::NOT_READY),
|
||||
Property(&scsi_exception::get_asc, asc::MEDIUM_NOT_PRESENT))))
|
||||
<< "REASSIGN must fail because drive is not ready";
|
||||
|
||||
disk->SetReady(true);
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReassign));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdReassignBlocks);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -96,10 +113,12 @@ TEST(DiskTest, Seek6)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdSeek6); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
@ -116,8 +135,8 @@ TEST(DiskTest, Seek6)
|
||||
|
||||
disk->SetReady(true);
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdSeek6));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdSeek6);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -125,10 +144,12 @@ TEST(DiskTest, Seek10)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdSeek10); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
@ -145,8 +166,8 @@ TEST(DiskTest, Seek10)
|
||||
|
||||
disk->SetReady(true);
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdSeek10));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdSeek10);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -154,6 +175,8 @@ TEST(DiskTest, ReadCapacity10)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
@ -169,15 +192,15 @@ TEST(DiskTest, ReadCapacity10)
|
||||
<< "READ CAPACITY(10) must fail because the medium has no capacity";
|
||||
|
||||
disk->SetBlockCount(0x12345678);
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReadCapacity10));
|
||||
EXPECT_CALL(controller, DataIn);
|
||||
disk->Dispatch(scsi_command::eCmdReadCapacity10);
|
||||
auto& buf = controller.GetBuffer();
|
||||
EXPECT_EQ(0x1234, GetInt16(buf, 0));
|
||||
EXPECT_EQ(0x5677, GetInt16(buf, 2));
|
||||
|
||||
disk->SetBlockCount(0x1234567887654321);
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReadCapacity10));
|
||||
EXPECT_CALL(controller, DataIn);
|
||||
disk->Dispatch(scsi_command::eCmdReadCapacity10);
|
||||
buf = controller.GetBuffer();
|
||||
EXPECT_EQ(0xffff, GetInt16(buf, 0));
|
||||
EXPECT_EQ(0xffff, GetInt16(buf, 2));
|
||||
@ -187,10 +210,12 @@ TEST(DiskTest, ReadCapacity16)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
cmd[1] = 0x00;
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16); }, Throws<scsi_exception>(AllOf(
|
||||
@ -213,8 +238,8 @@ TEST(DiskTest, ReadCapacity16)
|
||||
|
||||
disk->SetBlockCount(0x1234567887654321);
|
||||
disk->SetSectorSizeInBytes(1024);
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16));
|
||||
EXPECT_CALL(controller, DataIn);
|
||||
disk->Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16);
|
||||
const auto& buf = controller.GetBuffer();
|
||||
EXPECT_EQ(0x1234, GetInt16(buf, 0));
|
||||
EXPECT_EQ(0x5678, GetInt16(buf, 2));
|
||||
@ -228,6 +253,8 @@ TEST(DiskTest, Read6)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
@ -243,6 +270,8 @@ TEST(DiskTest, Read10)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
@ -252,8 +281,8 @@ TEST(DiskTest, Read10)
|
||||
<< "READ(10) must fail for a medium with 0 blocks";
|
||||
|
||||
disk->SetBlockCount(1);
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdRead10));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdRead10);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
// Further testing requires filesystem access
|
||||
@ -263,6 +292,8 @@ TEST(DiskTest, Read16)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
@ -272,8 +303,8 @@ TEST(DiskTest, Read16)
|
||||
<< "READ(16) must fail for a medium with 0 blocks";
|
||||
|
||||
disk->SetBlockCount(1);
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdRead16));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdRead16);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
// Further testing requires filesystem access
|
||||
@ -283,13 +314,23 @@ TEST(DiskTest, Write6)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdWrite6); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
Property(&scsi_exception::get_asc, asc::LBA_OUT_OF_RANGE))))
|
||||
<< "WRIte(6) must fail for a medium with 0 blocks";
|
||||
<< "WRITE(6) must fail for a medium with 0 blocks";
|
||||
|
||||
disk->SetBlockCount(1);
|
||||
disk->SetReady(true);
|
||||
disk->SetProtectable(true);
|
||||
disk->SetProtected(true);
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdWrite6); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::DATA_PROTECT),
|
||||
Property(&scsi_exception::get_asc, asc::WRITE_PROTECTED))));
|
||||
|
||||
// Further testing requires filesystem access
|
||||
}
|
||||
@ -298,6 +339,8 @@ TEST(DiskTest, Write10)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
@ -307,8 +350,8 @@ TEST(DiskTest, Write10)
|
||||
<< "WRITE(10) must fail for a medium with 0 blocks";
|
||||
|
||||
disk->SetBlockCount(1);
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdWrite10));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdWrite10);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
// Further testing requires filesystem access
|
||||
@ -318,6 +361,8 @@ TEST(DiskTest, Write16)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
@ -327,8 +372,8 @@ TEST(DiskTest, Write16)
|
||||
<< "WRITE(16) must fail for a medium with 0 blocks";
|
||||
|
||||
disk->SetBlockCount(1);
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdWrite16));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdWrite16);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
// Further testing requires filesystem access
|
||||
@ -338,6 +383,8 @@ TEST(DiskTest, Verify10)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
@ -347,8 +394,8 @@ TEST(DiskTest, Verify10)
|
||||
<< "VERIFY(10) must fail for a medium with 0 blocks";
|
||||
|
||||
disk->SetBlockCount(1);
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdVerify10));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdVerify10);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
// Further testing requires filesystem access
|
||||
@ -358,6 +405,8 @@ TEST(DiskTest, Verify16)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
@ -367,8 +416,8 @@ TEST(DiskTest, Verify16)
|
||||
<< "VERIFY(16) must fail for a medium with 0 blocks";
|
||||
|
||||
disk->SetBlockCount(1);
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdVerify16));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdVerify16);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
// Further testing requires filesystem access
|
||||
@ -378,13 +427,15 @@ TEST(DiskTest, ReadLong10)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReadLong10));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdReadLong10);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
cmd[2] = 1;
|
||||
@ -405,10 +456,12 @@ TEST(DiskTest, ReadLong16)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
// READ LONG(16), not READ CAPACITY(16)
|
||||
cmd[1] = 0x11;
|
||||
@ -419,8 +472,8 @@ TEST(DiskTest, ReadLong16)
|
||||
<< "READ LONG(16) must fail because the capacity is exceeded";
|
||||
cmd[2] = 0;
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
cmd[13] = 1;
|
||||
@ -434,13 +487,15 @@ TEST(DiskTest, WriteLong10)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdWriteLong10));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdWriteLong10);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
cmd[2] = 1;
|
||||
@ -461,10 +516,12 @@ TEST(DiskTest, WriteLong16)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
cmd[2] = 1;
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdWriteLong16); }, Throws<scsi_exception>(AllOf(
|
||||
@ -473,8 +530,8 @@ TEST(DiskTest, WriteLong16)
|
||||
<< "WRITE LONG(16) must fail because the capacity is exceeded";
|
||||
cmd[2] = 0;
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdWriteLong16));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdWriteLong16);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
cmd[13] = 1;
|
||||
@ -488,17 +545,20 @@ TEST(DiskTest, StartStopUnit)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
disk->SetRemovable(true);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
// Stop/Unload
|
||||
disk->SetReady(true);
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_CALL(controller, Status);
|
||||
EXPECT_CALL(*disk, FlushCache);
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdStartStop));
|
||||
disk->Dispatch(scsi_command::eCmdStartStop);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
EXPECT_TRUE(disk->IsStopped());
|
||||
|
||||
@ -506,9 +566,9 @@ TEST(DiskTest, StartStopUnit)
|
||||
cmd[4] = 0x02;
|
||||
disk->SetReady(true);
|
||||
disk->SetLocked(false);
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_CALL(controller, Status);
|
||||
EXPECT_CALL(*disk, FlushCache);
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdStartStop));
|
||||
disk->Dispatch(scsi_command::eCmdStartStop);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
disk->SetReady(false);
|
||||
@ -526,15 +586,15 @@ TEST(DiskTest, StartStopUnit)
|
||||
|
||||
// Start/Unload
|
||||
cmd[4] = 0x01;
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdStartStop));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdStartStop);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
EXPECT_FALSE(disk->IsStopped());
|
||||
|
||||
// Start/Load
|
||||
cmd[4] = 0x03;
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdStartStop));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdStartStop);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -542,26 +602,28 @@ TEST(DiskTest, PreventAllowMediumRemoval)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdRemoval); }, Throws<scsi_exception>(AllOf(
|
||||
EXPECT_THAT([&] { disk->Dispatch(scsi_command::eCmdPreventAllowMediumRemoval); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::NOT_READY),
|
||||
Property(&scsi_exception::get_asc, asc::MEDIUM_NOT_PRESENT))))
|
||||
<< "REMOVAL must fail because drive is not ready";
|
||||
|
||||
disk->SetReady(true);
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdRemoval));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdPreventAllowMediumRemoval);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
EXPECT_FALSE(disk->IsLocked());
|
||||
|
||||
cmd[4] = 1;
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdRemoval));
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdPreventAllowMediumRemoval);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
EXPECT_TRUE(disk->IsLocked());
|
||||
}
|
||||
@ -629,10 +691,12 @@ TEST(DiskTest, ModeSense6)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
// Drive must be ready on order to return all data
|
||||
disk->SetReady(true);
|
||||
@ -640,18 +704,18 @@ TEST(DiskTest, ModeSense6)
|
||||
cmd[2] = 0x3f;
|
||||
// ALLOCATION LENGTH
|
||||
cmd[4] = 255;
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense6));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense6);
|
||||
EXPECT_EQ(0x08, controller.GetBuffer()[3]) << "Wrong block descriptor length";
|
||||
|
||||
// No block descriptor
|
||||
cmd[1] = 0x08;
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense6));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense6);
|
||||
EXPECT_EQ(0x00, controller.GetBuffer()[2]) << "Wrong device-specific parameter";
|
||||
|
||||
disk->SetReadOnly(false);
|
||||
disk->SetProtectable(true);
|
||||
disk->SetProtected(true);
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense6));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense6);
|
||||
const auto& buf = controller.GetBuffer();
|
||||
EXPECT_EQ(0x80, buf[2]) << "Wrong device-specific parameter";
|
||||
|
||||
@ -661,18 +725,18 @@ TEST(DiskTest, ModeSense6)
|
||||
// Format page
|
||||
cmd[2] = 3;
|
||||
disk->SetSectorSizeInBytes(1024);
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense6));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense6);
|
||||
DiskTest_ValidateFormatPage(controller, 12);
|
||||
|
||||
// Rigid disk drive page
|
||||
cmd[2] = 4;
|
||||
disk->SetBlockCount(0x12345678);
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense6));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense6);
|
||||
DiskTest_ValidateDrivePage(controller, 12);
|
||||
|
||||
// Cache page
|
||||
cmd[2] = 8;
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense6));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense6);
|
||||
DiskTest_ValidateCachePage(controller, 12);
|
||||
}
|
||||
|
||||
@ -680,10 +744,12 @@ TEST(DiskTest, ModeSense10)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
// Drive must be ready on order to return all data
|
||||
disk->SetReady(true);
|
||||
@ -691,19 +757,19 @@ TEST(DiskTest, ModeSense10)
|
||||
cmd[2] = 0x3f;
|
||||
// ALLOCATION LENGTH
|
||||
cmd[8] = 255;
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense10));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense10);
|
||||
EXPECT_EQ(0x08, controller.GetBuffer()[7]) << "Wrong block descriptor length";
|
||||
|
||||
// No block descriptor
|
||||
cmd[1] = 0x08;
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense10));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense10);
|
||||
auto& buf = controller.GetBuffer();
|
||||
EXPECT_EQ(0x00, controller.GetBuffer()[3]) << "Wrong device-specific parameter";
|
||||
|
||||
disk->SetReadOnly(false);
|
||||
disk->SetProtectable(true);
|
||||
disk->SetProtected(true);
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense10));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense10);
|
||||
buf = controller.GetBuffer();
|
||||
EXPECT_EQ(0x80, buf[3]) << "Wrong device-specific parameter";
|
||||
|
||||
@ -711,7 +777,7 @@ TEST(DiskTest, ModeSense10)
|
||||
cmd[1] = 0x00;
|
||||
disk->SetBlockCount(0x1234);
|
||||
disk->SetSectorSizeInBytes(1024);
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense10));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense10);
|
||||
buf = controller.GetBuffer();
|
||||
EXPECT_EQ(0x00, buf[4]) << "Wrong LONGLBA field";
|
||||
EXPECT_EQ(0x08, buf[7]) << "Wrong block descriptor length";
|
||||
@ -723,7 +789,7 @@ TEST(DiskTest, ModeSense10)
|
||||
// Return long block descriptor
|
||||
cmd[1] = 0x10;
|
||||
disk->SetBlockCount((uint64_t)0xffffffff + 1);
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense10));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense10);
|
||||
buf = controller.GetBuffer();
|
||||
EXPECT_EQ(0x01, buf[4]) << "Wrong LONGLBA field";
|
||||
EXPECT_EQ(0x10, buf[7]) << "Wrong block descriptor length";
|
||||
@ -738,18 +804,18 @@ TEST(DiskTest, ModeSense10)
|
||||
// Format page
|
||||
cmd[2] = 3;
|
||||
disk->SetSectorSizeInBytes(1024);
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense10));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense10);
|
||||
DiskTest_ValidateFormatPage(controller, 16);
|
||||
|
||||
// Rigid disk drive page
|
||||
cmd[2] = 4;
|
||||
disk->SetBlockCount(0x12345678);
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense10));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense10);
|
||||
DiskTest_ValidateDrivePage(controller, 16);
|
||||
|
||||
// Cache page
|
||||
cmd[2] = 8;
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdModeSense10));
|
||||
disk->Dispatch(scsi_command::eCmdModeSense10);
|
||||
DiskTest_ValidateCachePage(controller, 16);
|
||||
}
|
||||
|
||||
@ -757,17 +823,19 @@ TEST(DiskTest, SynchronizeCache)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
EXPECT_CALL(*disk, FlushCache());
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdSynchronizeCache10));
|
||||
EXPECT_CALL(*disk, FlushCache);
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdSynchronizeCache10);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
EXPECT_CALL(*disk, FlushCache());
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdSynchronizeCache16));
|
||||
EXPECT_CALL(*disk, FlushCache);
|
||||
EXPECT_CALL(controller, Status);
|
||||
disk->Dispatch(scsi_command::eCmdSynchronizeCache16);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -775,11 +843,13 @@ TEST(DiskTest, ReadDefectData)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto disk = make_shared<MockDisk>();
|
||||
const unordered_map<string, string> params;
|
||||
disk->Init(params);
|
||||
|
||||
controller.AddDevice(disk);
|
||||
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReadDefectData10));
|
||||
EXPECT_CALL(controller, DataIn);
|
||||
disk->Dispatch(scsi_command::eCmdReadDefectData10);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
|
@ -1,44 +0,0 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "hal/gpiobus.h"
|
||||
#include <unordered_set>
|
||||
|
||||
TEST(GpioBusTest, GetCommandByteCount)
|
||||
{
|
||||
unordered_set<int> opcodes;
|
||||
|
||||
EXPECT_EQ(16, GPIOBUS::GetCommandByteCount(0x88));
|
||||
opcodes.insert(0x88);
|
||||
EXPECT_EQ(16, GPIOBUS::GetCommandByteCount(0x8a));
|
||||
opcodes.insert(0x8a);
|
||||
EXPECT_EQ(16, GPIOBUS::GetCommandByteCount(0x8f));
|
||||
opcodes.insert(0x8f);
|
||||
EXPECT_EQ(16, GPIOBUS::GetCommandByteCount(0x91));
|
||||
opcodes.insert(0x91);
|
||||
EXPECT_EQ(16, GPIOBUS::GetCommandByteCount(0x9e));
|
||||
opcodes.insert(0x9e);
|
||||
EXPECT_EQ(16, GPIOBUS::GetCommandByteCount(0x9f));
|
||||
opcodes.insert(0x9f);
|
||||
EXPECT_EQ(12, GPIOBUS::GetCommandByteCount(0xa0));
|
||||
opcodes.insert(0xa0);
|
||||
|
||||
for (int i = 0x20; i <= 0x7d; i++) {
|
||||
EXPECT_EQ(10, GPIOBUS::GetCommandByteCount(i));
|
||||
opcodes.insert(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (opcodes.find(i) == opcodes.end()) {
|
||||
EXPECT_EQ(6, GPIOBUS::GetCommandByteCount(i));
|
||||
}
|
||||
}
|
||||
}
|
@ -20,18 +20,13 @@ void HostServices_SetUpModePages(map<int, vector<byte>>& pages)
|
||||
EXPECT_EQ(10, pages[32].size());
|
||||
}
|
||||
|
||||
TEST(HostServicesTest, Dispatch)
|
||||
{
|
||||
TestDispatch(SCHS);
|
||||
}
|
||||
|
||||
TEST(HostServicesTest, TestUnitReady)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto services = CreateDevice(SCHS, controller);
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(services->Dispatch(scsi_command::eCmdTestUnitReady)) << "TEST UNIT READY must never fail";
|
||||
services->Dispatch(scsi_command::eCmdTestUnitReady);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -45,26 +40,26 @@ TEST(HostServicesTest, StartStopUnit)
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto services = CreateDevice(SCHS, controller);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
// STOP
|
||||
EXPECT_CALL(controller, ScheduleShutdown(AbstractController::rascsi_shutdown_mode::STOP_RASCSI));
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(services->Dispatch(scsi_command::eCmdStartStop));
|
||||
services->Dispatch(scsi_command::eCmdStartStop);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
// LOAD
|
||||
cmd[4] = 0x02;
|
||||
EXPECT_CALL(controller, ScheduleShutdown(AbstractController::rascsi_shutdown_mode::STOP_PI));
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(services->Dispatch(scsi_command::eCmdStartStop));
|
||||
services->Dispatch(scsi_command::eCmdStartStop);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
// UNLOAD
|
||||
cmd[4] = 0x03;
|
||||
EXPECT_CALL(controller, ScheduleShutdown(AbstractController::rascsi_shutdown_mode::RESTART_PI));
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(services->Dispatch(scsi_command::eCmdStartStop));
|
||||
services->Dispatch(scsi_command::eCmdStartStop);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
// START
|
||||
@ -78,8 +73,10 @@ TEST(HostServicesTest, ModeSense6)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto services = CreateDevice(SCHS, controller);
|
||||
const unordered_map<string, string> params;
|
||||
services->Init(params);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_THAT([&] { services->Dispatch(scsi_command::eCmdModeSense6); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
@ -96,7 +93,7 @@ TEST(HostServicesTest, ModeSense6)
|
||||
// ALLOCATION LENGTH
|
||||
cmd[4] = 255;
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(services->Dispatch(scsi_command::eCmdModeSense6));
|
||||
services->Dispatch(scsi_command::eCmdModeSense6);
|
||||
vector<uint8_t>& buffer = controller.GetBuffer();
|
||||
// Major version 1
|
||||
EXPECT_EQ(0x01, buffer[6]);
|
||||
@ -110,7 +107,7 @@ TEST(HostServicesTest, ModeSense6)
|
||||
// ALLOCATION LENGTH
|
||||
cmd[4] = 2;
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(services->Dispatch(scsi_command::eCmdModeSense6));
|
||||
services->Dispatch(scsi_command::eCmdModeSense6);
|
||||
buffer = controller.GetBuffer();
|
||||
EXPECT_EQ(0x02, buffer[0]);
|
||||
}
|
||||
@ -119,8 +116,10 @@ TEST(HostServicesTest, ModeSense10)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto services = CreateDevice(SCHS, controller);
|
||||
const unordered_map<string, string> params;
|
||||
services->Init(params);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_THAT([&] { services->Dispatch(scsi_command::eCmdModeSense10); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
@ -137,7 +136,7 @@ TEST(HostServicesTest, ModeSense10)
|
||||
// ALLOCATION LENGTH
|
||||
cmd[8] = 255;
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(services->Dispatch(scsi_command::eCmdModeSense10));
|
||||
services->Dispatch(scsi_command::eCmdModeSense10);
|
||||
vector<uint8_t>& buffer = controller.GetBuffer();
|
||||
// Major version 1
|
||||
EXPECT_EQ(0x01, buffer[10]);
|
||||
@ -151,7 +150,7 @@ TEST(HostServicesTest, ModeSense10)
|
||||
// ALLOCATION LENGTH
|
||||
cmd[8] = 2;
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(services->Dispatch(scsi_command::eCmdModeSense10));
|
||||
services->Dispatch(scsi_command::eCmdModeSense10);
|
||||
buffer = controller.GetBuffer();
|
||||
EXPECT_EQ(0x02, buffer[1]);
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "devices/host_services.h"
|
||||
#include "rascsi/command_context.h"
|
||||
#include "rascsi/rascsi_executor.h"
|
||||
#include <fcntl.h>
|
||||
|
||||
using namespace testing;
|
||||
|
||||
@ -221,7 +222,7 @@ class MockPrimaryDevice : public PrimaryDevice
|
||||
|
||||
public:
|
||||
|
||||
MOCK_METHOD(vector<byte>, InquiryInternal, (), (const));
|
||||
MOCK_METHOD(vector<uint8_t>, InquiryInternal, (), (const));
|
||||
|
||||
explicit MockPrimaryDevice(int lun) : PrimaryDevice(UNDEFINED, lun) {}
|
||||
~MockPrimaryDevice() override = default;
|
||||
@ -235,7 +236,7 @@ class MockModePageDevice : public ModePageDevice
|
||||
|
||||
public:
|
||||
|
||||
MOCK_METHOD(vector<byte>, InquiryInternal, (), (const));
|
||||
MOCK_METHOD(vector<uint8_t>, InquiryInternal, (), (const));
|
||||
MOCK_METHOD(int, ModeSense6, (const vector<int>&, vector<uint8_t>&), (const override));
|
||||
MOCK_METHOD(int, ModeSense10, (const vector<int>&, vector<uint8_t>&), (const override));
|
||||
|
||||
@ -277,7 +278,7 @@ class MockStorageDevice : public StorageDevice
|
||||
|
||||
public:
|
||||
|
||||
MOCK_METHOD(vector<byte>, InquiryInternal, (), (const));
|
||||
MOCK_METHOD(vector<uint8_t>, InquiryInternal, (), (const));
|
||||
MOCK_METHOD(void, Open, (), (override));
|
||||
MOCK_METHOD(int, ModeSense6, (const vector<int>&, vector<uint8_t>&), (const override));
|
||||
MOCK_METHOD(int, ModeSense10, (const vector<int>&, vector<uint8_t>&), (const override));
|
||||
@ -323,7 +324,7 @@ class MockDisk : public Disk
|
||||
|
||||
public:
|
||||
|
||||
MOCK_METHOD(vector<byte>, InquiryInternal, (), (const));
|
||||
MOCK_METHOD(vector<uint8_t>, InquiryInternal, (), (const));
|
||||
MOCK_METHOD(void, FlushCache, (), (override));
|
||||
MOCK_METHOD(void, Open, (), (override));
|
||||
|
||||
|
@ -78,36 +78,30 @@ TEST(ModePageDeviceTest, AddVendorPage)
|
||||
EXPECT_TRUE(pages.empty()) << "There must not be any default vendor page";
|
||||
}
|
||||
|
||||
TEST(ModePageDeviceTest, Dispatch)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<NiceMock<MockModePageDevice>>();
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
EXPECT_FALSE(device->Dispatch(scsi_command::eCmdIcd)) << "Command is not supported by this class";
|
||||
}
|
||||
|
||||
TEST(ModePageDeviceTest, ModeSense6)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<NiceMock<MockModePageDevice>>();
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdModeSense6));
|
||||
device->Dispatch(scsi_command::eCmdModeSense6);
|
||||
}
|
||||
|
||||
TEST(ModePageDeviceTest, ModeSense10)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<NiceMock<MockModePageDevice>>();
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdModeSense10));
|
||||
device->Dispatch(scsi_command::eCmdModeSense10);
|
||||
}
|
||||
|
||||
TEST(ModePageDeviceTest, ModeSelect)
|
||||
@ -130,13 +124,15 @@ TEST(ModePageDeviceTest, ModeSelect6)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<MockModePageDevice>();
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_CALL(controller, DataOut());
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdModeSelect6));
|
||||
device->Dispatch(scsi_command::eCmdModeSelect6);
|
||||
|
||||
cmd[1] = 0x01;
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdModeSelect6); }, Throws<scsi_exception>(AllOf(
|
||||
@ -149,13 +145,15 @@ TEST(ModePageDeviceTest, ModeSelect10)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<MockModePageDevice>();
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_CALL(controller, DataOut());
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdModeSelect10));
|
||||
device->Dispatch(scsi_command::eCmdModeSelect10);
|
||||
|
||||
cmd[1] = 0x01;
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdModeSelect10); }, Throws<scsi_exception>(AllOf(
|
||||
|
@ -12,8 +12,10 @@
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "devices/primary_device.h"
|
||||
#include "devices/device_factory.h"
|
||||
#include "devices/scsi_command_util.h"
|
||||
|
||||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
TEST(PrimaryDeviceTest, GetId)
|
||||
{
|
||||
@ -21,6 +23,8 @@ TEST(PrimaryDeviceTest, GetId)
|
||||
|
||||
MockAbstractController controller(make_shared<MockBus>(), ID);
|
||||
auto device = make_shared<MockPrimaryDevice>(0);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
EXPECT_EQ(-1, device->GetId()) << "Device ID cannot be known without assignment to a controller";
|
||||
|
||||
@ -32,16 +36,18 @@ TEST(PrimaryDeviceTest, PhaseChange)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<MockPrimaryDevice>(0);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_CALL(controller, Status);
|
||||
device->EnterStatusPhase();
|
||||
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_CALL(controller, DataIn);
|
||||
device->EnterDataInPhase();
|
||||
|
||||
EXPECT_CALL(controller, DataOut());
|
||||
EXPECT_CALL(controller, DataOut);
|
||||
device->EnterDataOutPhase();
|
||||
}
|
||||
|
||||
@ -49,10 +55,12 @@ TEST(PrimaryDeviceTest, Reset)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<MockPrimaryDevice>(0);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdReserve6));
|
||||
device->Dispatch(scsi_command::eCmdReserve6);
|
||||
EXPECT_FALSE(device->CheckReservation(1, scsi_command::eCmdTestUnitReady, false))
|
||||
<< "Device must be reserved for initiator ID 1";
|
||||
device->Reset();
|
||||
@ -64,13 +72,15 @@ TEST(PrimaryDeviceTest, CheckReservation)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<MockPrimaryDevice>(0);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
EXPECT_TRUE(device->CheckReservation(0, scsi_command::eCmdTestUnitReady, false))
|
||||
<< "Device must not be reserved for initiator ID 0";
|
||||
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdReserve6));
|
||||
device->Dispatch(scsi_command::eCmdReserve6);
|
||||
EXPECT_TRUE(device->CheckReservation(0, scsi_command::eCmdTestUnitReady, false))
|
||||
<< "Device must not be reserved for initiator ID 0";
|
||||
EXPECT_FALSE(device->CheckReservation(1, scsi_command::eCmdTestUnitReady, false))
|
||||
@ -84,9 +94,9 @@ TEST(PrimaryDeviceTest, CheckReservation)
|
||||
EXPECT_TRUE(device->CheckReservation(1, scsi_command::eCmdRelease6, false))
|
||||
<< "Device must not be reserved for RELEASE (6)";
|
||||
|
||||
EXPECT_TRUE(device->CheckReservation(1, scsi_command::eCmdRemoval, false))
|
||||
EXPECT_TRUE(device->CheckReservation(1, scsi_command::eCmdPreventAllowMediumRemoval, false))
|
||||
<< "Device must not be reserved for PREVENT ALLOW MEDIUM REMOVAL with prevent bit not set";
|
||||
EXPECT_FALSE(device->CheckReservation(1, scsi_command::eCmdRemoval, true))
|
||||
EXPECT_FALSE(device->CheckReservation(1, scsi_command::eCmdPreventAllowMediumRemoval, true))
|
||||
<< "Device must be reserved for PREVENT ALLOW MEDIUM REMOVAL with prevent bit set";
|
||||
}
|
||||
|
||||
@ -94,23 +104,25 @@ TEST(PrimaryDeviceTest, ReserveReleaseUnit)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<MockPrimaryDevice>(0);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdReserve6));
|
||||
device->Dispatch(scsi_command::eCmdReserve6);
|
||||
EXPECT_FALSE(device->CheckReservation(1, scsi_command::eCmdTestUnitReady, false))
|
||||
<< "Device must be reserved for initiator ID 1";
|
||||
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdRelease6));
|
||||
device->Dispatch(scsi_command::eCmdRelease6);
|
||||
EXPECT_TRUE(device->CheckReservation(1, scsi_command::eCmdTestUnitReady, false))
|
||||
<< "Device must not be reserved anymore for initiator ID 1";
|
||||
|
||||
ON_CALL(controller, GetInitiatorId).WillByDefault(Return(-1));
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdReserve6));
|
||||
device->Dispatch(scsi_command::eCmdReserve6);
|
||||
EXPECT_FALSE(device->CheckReservation(1, scsi_command::eCmdTestUnitReady, false))
|
||||
<< "Device must be reserved for unknown initiator";
|
||||
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdRelease6));
|
||||
device->Dispatch(scsi_command::eCmdRelease6);
|
||||
EXPECT_TRUE(device->CheckReservation(1, scsi_command::eCmdTestUnitReady, false))
|
||||
<< "Device must not be reserved anymore for unknown initiator";
|
||||
}
|
||||
@ -119,10 +131,12 @@ TEST(PrimaryDeviceTest, DiscardReservation)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<MockPrimaryDevice>(0);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdReserve6));
|
||||
device->Dispatch(scsi_command::eCmdReserve6);
|
||||
EXPECT_FALSE(device->CheckReservation(1, scsi_command::eCmdTestUnitReady, false))
|
||||
<< "Device must be reserved for initiator ID 1";
|
||||
device->DiscardReservation();
|
||||
@ -134,46 +148,48 @@ TEST(PrimaryDeviceTest, TestUnitReady)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<MockPrimaryDevice>(0);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
device->SetReset(true);
|
||||
device->SetAttn(true);
|
||||
device->SetReady(false);
|
||||
EXPECT_CALL(controller, DataIn()).Times(0);
|
||||
EXPECT_CALL(controller, DataIn).Times(0);
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdTestUnitReady); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::UNIT_ATTENTION),
|
||||
Property(&scsi_exception::get_asc, asc::POWER_ON_OR_RESET))));
|
||||
|
||||
device->SetReset(false);
|
||||
EXPECT_CALL(controller, DataIn()).Times(0);
|
||||
EXPECT_CALL(controller, DataIn).Times(0);
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdTestUnitReady); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::UNIT_ATTENTION),
|
||||
Property(&scsi_exception::get_asc, asc::NOT_READY_TO_READY_CHANGE))));
|
||||
|
||||
device->SetReset(true);
|
||||
device->SetAttn(false);
|
||||
EXPECT_CALL(controller, DataIn()).Times(0);
|
||||
EXPECT_CALL(controller, DataIn).Times(0);
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdTestUnitReady); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::UNIT_ATTENTION),
|
||||
Property(&scsi_exception::get_asc, asc::POWER_ON_OR_RESET))));
|
||||
|
||||
device->SetReset(false);
|
||||
device->SetAttn(true);
|
||||
EXPECT_CALL(controller, DataIn()).Times(0);
|
||||
EXPECT_CALL(controller, DataIn).Times(0);
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdTestUnitReady); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::UNIT_ATTENTION),
|
||||
Property(&scsi_exception::get_asc, asc::NOT_READY_TO_READY_CHANGE))));
|
||||
|
||||
device->SetAttn(false);
|
||||
EXPECT_CALL(controller, DataIn()).Times(0);
|
||||
EXPECT_CALL(controller, DataIn).Times(0);
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdTestUnitReady); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::NOT_READY),
|
||||
Property(&scsi_exception::get_asc, asc::MEDIUM_NOT_PRESENT))));
|
||||
|
||||
device->SetReady(true);
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdTestUnitReady));
|
||||
EXPECT_CALL(controller, Status);
|
||||
device->Dispatch(scsi_command::eCmdTestUnitReady);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -181,27 +197,30 @@ TEST(PrimaryDeviceTest, Inquiry)
|
||||
{
|
||||
auto controller = make_shared<NiceMock<MockAbstractController>>(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<MockPrimaryDevice>(0);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller->AddDevice(device);
|
||||
|
||||
vector<int>& cmd = controller->GetCmd();
|
||||
auto& cmd = controller->GetCmd();
|
||||
|
||||
// ALLOCATION LENGTH
|
||||
cmd[4] = 255;
|
||||
|
||||
ON_CALL(*device, InquiryInternal()).WillByDefault([&device]() {
|
||||
return device->HandleInquiry(device_type::PROCESSOR, scsi_level::SPC_3, false);
|
||||
});
|
||||
EXPECT_CALL(*device, InquiryInternal());
|
||||
EXPECT_CALL(*controller, DataIn());
|
||||
EXPECT_CALL(*device, InquiryInternal);
|
||||
EXPECT_CALL(*controller, DataIn);
|
||||
ON_CALL(*controller, GetEffectiveLun()).WillByDefault(Return(1));
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdInquiry));
|
||||
device->Dispatch(scsi_command::eCmdInquiry);
|
||||
EXPECT_EQ(0x7f, controller->GetBuffer()[0]) << "Invalid LUN was not reported";
|
||||
ON_CALL(*controller, GetEffectiveLun()).WillByDefault(Return(0));
|
||||
|
||||
EXPECT_FALSE(controller->AddDevice(make_shared<MockPrimaryDevice>(0))) << "Duplicate LUN was not rejected";
|
||||
EXPECT_CALL(*device, InquiryInternal());
|
||||
EXPECT_CALL(*controller, DataIn());
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdInquiry));
|
||||
EXPECT_CALL(*device, InquiryInternal);
|
||||
EXPECT_CALL(*controller, DataIn);
|
||||
device->Dispatch(scsi_command::eCmdInquiry);
|
||||
EXPECT_EQ(device_type::PROCESSOR, (device_type)controller->GetBuffer()[0]);
|
||||
EXPECT_EQ(0x00, controller->GetBuffer()[1]) << "Device was not reported as non-removable";
|
||||
EXPECT_EQ(scsi_level::SPC_3, (scsi_level)controller->GetBuffer()[2]) << "Wrong SCSI level";
|
||||
@ -211,9 +230,9 @@ TEST(PrimaryDeviceTest, Inquiry)
|
||||
ON_CALL(*device, InquiryInternal()).WillByDefault([&device]() {
|
||||
return device->HandleInquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_1_CCS, true);
|
||||
});
|
||||
EXPECT_CALL(*device, InquiryInternal());
|
||||
EXPECT_CALL(*controller, DataIn());
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdInquiry));
|
||||
EXPECT_CALL(*device, InquiryInternal);
|
||||
EXPECT_CALL(*controller, DataIn);
|
||||
device->Dispatch(scsi_command::eCmdInquiry);
|
||||
EXPECT_EQ(device_type::DIRECT_ACCESS, (device_type)controller->GetBuffer()[0]);
|
||||
EXPECT_EQ(0x80, controller->GetBuffer()[1]) << "Device was not reported as removable";
|
||||
EXPECT_EQ(scsi_level::SCSI_1_CCS, (scsi_level)controller->GetBuffer()[2]) << "Wrong SCSI level";
|
||||
@ -221,14 +240,14 @@ TEST(PrimaryDeviceTest, Inquiry)
|
||||
EXPECT_EQ(0x1f, controller->GetBuffer()[4]) << "Wrong additional data size";
|
||||
|
||||
cmd[1] = 0x01;
|
||||
EXPECT_CALL(*controller, DataIn()).Times(0);
|
||||
EXPECT_CALL(*controller, DataIn).Times(0);
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdInquiry); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_CDB))))
|
||||
<< "EVPD bit is not supported";
|
||||
|
||||
cmd[2] = 0x01;
|
||||
EXPECT_CALL(*controller, DataIn()).Times(0);
|
||||
EXPECT_CALL(*controller, DataIn).Times(0);
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdInquiry); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_CDB))))
|
||||
@ -238,9 +257,9 @@ TEST(PrimaryDeviceTest, Inquiry)
|
||||
cmd[2] = 0x00;
|
||||
// ALLOCATION LENGTH
|
||||
cmd[4] = 1;
|
||||
EXPECT_CALL(*device, InquiryInternal());
|
||||
EXPECT_CALL(*controller, DataIn());
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdInquiry));
|
||||
EXPECT_CALL(*device, InquiryInternal);
|
||||
EXPECT_CALL(*controller, DataIn);
|
||||
device->Dispatch(scsi_command::eCmdInquiry);
|
||||
EXPECT_EQ(0x1f, controller->GetBuffer()[4]) << "Wrong additional data size";
|
||||
EXPECT_EQ(1, controller->GetLength()) << "Wrong ALLOCATION LENGTH handling";
|
||||
}
|
||||
@ -249,10 +268,12 @@ TEST(PrimaryDeviceTest, RequestSense)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<MockPrimaryDevice>(0);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
// ALLOCATION LENGTH
|
||||
cmd[4] = 255;
|
||||
|
||||
@ -262,8 +283,8 @@ TEST(PrimaryDeviceTest, RequestSense)
|
||||
Property(&scsi_exception::get_asc, asc::MEDIUM_NOT_PRESENT))));
|
||||
|
||||
device->SetReady(true);
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdRequestSense));
|
||||
EXPECT_CALL(controller, DataIn);
|
||||
device->Dispatch(scsi_command::eCmdRequestSense);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -271,30 +292,32 @@ TEST(PrimaryDeviceTest, SendDiagnostic)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<MockPrimaryDevice>(0);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdSendDiag));
|
||||
EXPECT_CALL(controller, Status);
|
||||
device->Dispatch(scsi_command::eCmdSendDiagnostic);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
cmd[1] = 0x10;
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdSendDiag); }, Throws<scsi_exception>(AllOf(
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdSendDiagnostic); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_CDB))))
|
||||
<< "SEND DIAGNOSTIC must fail because PF bit is not supported";
|
||||
cmd[1] = 0;
|
||||
|
||||
cmd[3] = 1;
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdSendDiag); }, Throws<scsi_exception>(AllOf(
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdSendDiagnostic); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_CDB))))
|
||||
<< "SEND DIAGNOSTIC must fail because parameter list is not supported";
|
||||
cmd[3] = 0;
|
||||
cmd[4] = 1;
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdSendDiag); }, Throws<scsi_exception>(AllOf(
|
||||
EXPECT_THAT([&] { device->Dispatch(scsi_command::eCmdSendDiagnostic); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_CDB))))
|
||||
<< "SEND DIAGNOSTIC must fail because parameter list is not supported";
|
||||
@ -308,39 +331,32 @@ TEST(PrimaryDeviceTest, ReportLuns)
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto device1 = make_shared<MockPrimaryDevice>(LUN1);
|
||||
auto device2 = make_shared<MockPrimaryDevice>(LUN2);
|
||||
const unordered_map<string, string> params;
|
||||
device1->Init(params);
|
||||
device2->Init(params);
|
||||
|
||||
controller.AddDevice(device1);
|
||||
EXPECT_TRUE(controller.HasDeviceForLun(LUN1));
|
||||
controller.AddDevice(device2);
|
||||
EXPECT_TRUE(controller.HasDeviceForLun(LUN2));
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
// ALLOCATION LENGTH
|
||||
cmd[9] = 255;
|
||||
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(device1->Dispatch(scsi_command::eCmdReportLuns));
|
||||
EXPECT_CALL(controller, DataIn);
|
||||
device1->Dispatch(scsi_command::eCmdReportLuns);
|
||||
const vector<uint8_t>& buffer = controller.GetBuffer();
|
||||
EXPECT_EQ(0x00, buffer[0]) << "Wrong data length";
|
||||
EXPECT_EQ(0x00, buffer[1]) << "Wrong data length";
|
||||
EXPECT_EQ(0x00, buffer[2]) << "Wrong data length";
|
||||
EXPECT_EQ(0x10, buffer[3]) << "Wrong data length";
|
||||
EXPECT_EQ(0x00, buffer[8]) << "Wrong LUN1 number";
|
||||
EXPECT_EQ(0x00, buffer[9]) << "Wrong LUN1 number";
|
||||
EXPECT_EQ(0x00, buffer[10]) << "Wrong LUN1 number";
|
||||
EXPECT_EQ(0x00, buffer[11]) << "Wrong LUN1 number";
|
||||
EXPECT_EQ(0x00, buffer[12]) << "Wrong LUN1 number";
|
||||
EXPECT_EQ(0x00, buffer[13]) << "Wrong LUN1 number";
|
||||
EXPECT_EQ(0x00, buffer[14]) << "Wrong LUN1 number";
|
||||
EXPECT_EQ(LUN1, buffer[15]) << "Wrong LUN1 number";
|
||||
EXPECT_EQ(0x00, buffer[16]) << "Wrong LUN2 number";
|
||||
EXPECT_EQ(0x00, buffer[17]) << "Wrong LUN2 number";
|
||||
EXPECT_EQ(0x00, buffer[18]) << "Wrong LUN2 number";
|
||||
EXPECT_EQ(0x00, buffer[19]) << "Wrong LUN2 number";
|
||||
EXPECT_EQ(0x00, buffer[20]) << "Wrong LUN2 number";
|
||||
EXPECT_EQ(0x00, buffer[21]) << "Wrong LUN2 number";
|
||||
EXPECT_EQ(0x00, buffer[22]) << "Wrong LUN2 number";
|
||||
EXPECT_EQ(LUN2, buffer[23]) << "Wrong LUN2 number";
|
||||
EXPECT_EQ(0, GetInt16(buffer, 0)) << "Wrong data length";
|
||||
EXPECT_EQ(16, GetInt16(buffer, 2)) << "Wrong data length";
|
||||
EXPECT_EQ(0, GetInt16(buffer, 8)) << "Wrong LUN1 number";
|
||||
EXPECT_EQ(0, GetInt16(buffer, 10)) << "Wrong LUN1 number";
|
||||
EXPECT_EQ(0, GetInt16(buffer, 12)) << "Wrong LUN1 number";
|
||||
EXPECT_EQ(LUN1, GetInt16(buffer, 14)) << "Wrong LUN1 number";
|
||||
EXPECT_EQ(0, GetInt16(buffer, 16)) << "Wrong LUN2 number";
|
||||
EXPECT_EQ(0, GetInt16(buffer, 18)) << "Wrong LUN2 number";
|
||||
EXPECT_EQ(0, GetInt16(buffer, 20)) << "Wrong LUN2 number";
|
||||
EXPECT_EQ(LUN2, GetInt16(buffer, 22)) << "Wrong LUN2 number";
|
||||
|
||||
cmd[2] = 0x01;
|
||||
EXPECT_THAT([&] { device1->Dispatch(scsi_command::eCmdReportLuns); }, Throws<scsi_exception>(AllOf(
|
||||
@ -349,24 +365,16 @@ TEST(PrimaryDeviceTest, ReportLuns)
|
||||
<< "Only SELECT REPORT mode 0 is supported";
|
||||
}
|
||||
|
||||
TEST(PrimaryDeviceTest, UnknownCommand)
|
||||
TEST(PrimaryDeviceTest, Dispatch)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<MockPrimaryDevice>(0);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
EXPECT_FALSE(device->Dispatch((scsi_command)0xFF));
|
||||
}
|
||||
|
||||
TEST(PrimaryDeviceTest, Dispatch)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<NiceMock<MockPrimaryDevice>>(0);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
EXPECT_FALSE(device->Dispatch(scsi_command::eCmdIcd)) << "Command is not supported by this class";
|
||||
EXPECT_THROW(device->Dispatch(static_cast<scsi_command>(0x1f)), scsi_exception) << "Unknown command";
|
||||
}
|
||||
|
||||
TEST(PrimaryDeviceTest, WriteByteSequence)
|
||||
@ -391,7 +399,7 @@ TEST(PrimaryDeviceTest, Init)
|
||||
unordered_map<string, string> params;
|
||||
MockPrimaryDevice device(0);
|
||||
|
||||
EXPECT_FALSE(device.Init(params)) << "Initialization of primary device must fail";
|
||||
EXPECT_TRUE(device.Init(params)) << "Initialization of primary device must not fail";
|
||||
}
|
||||
|
||||
TEST(PrimaryDeviceTest, FlushCache)
|
||||
|
@ -152,10 +152,10 @@ TEST_F(RascsiExecutorTest, ProcessDeviceCmd)
|
||||
|
||||
TEST_F(RascsiExecutorTest, ProcessCmd)
|
||||
{
|
||||
shared_ptr<MockBus> bus_ptr;
|
||||
shared_ptr<MockBus> bus;
|
||||
DeviceFactory device_factory;
|
||||
MockAbstractController controller(bus_ptr, 0);
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
MockAbstractController controller(bus, 0);
|
||||
ControllerManager controller_manager(bus);
|
||||
RascsiImage rascsi_image;
|
||||
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
|
||||
auto executor = make_shared<MockRascsiExecutor>(rascsi_response, rascsi_image, device_factory, controller_manager);
|
||||
|
@ -18,8 +18,8 @@ using namespace rascsi_interface;
|
||||
|
||||
TEST(RascsiResponseTest, Operation_Count)
|
||||
{
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
auto bus = make_shared<MockBus>();
|
||||
ControllerManager controller_manager(bus);
|
||||
DeviceFactory device_factory;
|
||||
RascsiResponse response(device_factory, controller_manager, 32);
|
||||
PbResult result;
|
||||
@ -30,29 +30,32 @@ TEST(RascsiResponseTest, Operation_Count)
|
||||
|
||||
void TestNonDiskDevice(PbDeviceType type, int default_param_count)
|
||||
{
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
auto bus = make_shared<MockBus>();
|
||||
ControllerManager controller_manager(bus);
|
||||
DeviceFactory device_factory;
|
||||
RascsiResponse response(device_factory, controller_manager, 32);
|
||||
|
||||
auto d = device_factory.CreateDevice(controller_manager, type, 0, "");
|
||||
const unordered_map<string, string> params;
|
||||
d->Init(params);
|
||||
EXPECT_TRUE(controller_manager.AttachToScsiController(0, d));
|
||||
|
||||
PbServerInfo info;
|
||||
response.GetDevices(info, "image_folder");
|
||||
|
||||
EXPECT_EQ(1, info.devices_info().devices().size());
|
||||
|
||||
const auto& device = info.devices_info().devices()[0];
|
||||
EXPECT_FALSE(device.properties().read_only());
|
||||
EXPECT_FALSE(device.properties().protectable());
|
||||
EXPECT_FALSE(device.properties().stoppable());
|
||||
EXPECT_FALSE(device.properties().removable());
|
||||
EXPECT_FALSE(device.properties().lockable());
|
||||
EXPECT_EQ(0, device.params().size());
|
||||
EXPECT_EQ(32, device.properties().luns());
|
||||
EXPECT_EQ(0, device.block_size());
|
||||
EXPECT_EQ(0, device.block_count());
|
||||
EXPECT_EQ(default_param_count, device.properties().default_params().size());
|
||||
EXPECT_EQ(default_param_count, device.params().size());
|
||||
EXPECT_FALSE(device.properties().supports_file());
|
||||
if (default_param_count) {
|
||||
EXPECT_TRUE(device.properties().supports_params());
|
||||
@ -85,8 +88,8 @@ TEST(RascsiResponseTest, GetImageFile)
|
||||
|
||||
TEST(RascsiResponseTest, GetReservedIds)
|
||||
{
|
||||
auto bus_ptr = make_shared<MockBus>();
|
||||
ControllerManager controller_manager(bus_ptr);
|
||||
auto bus = make_shared<MockBus>();
|
||||
ControllerManager controller_manager(bus);
|
||||
DeviceFactory device_factory;
|
||||
RascsiResponse response(device_factory, controller_manager, 32);
|
||||
unordered_set<int> ids;
|
||||
|
@ -21,12 +21,9 @@ TEST(ScsiCommandUtilTest, ModeSelect6)
|
||||
vector<int> cdb(6);
|
||||
vector<uint8_t> buf(LENGTH);
|
||||
|
||||
// PF (vendor-specific parameter format)
|
||||
// PF (vendor-specific parameter format) must not fail but be ignored
|
||||
cdb[1] = 0x00;
|
||||
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 0); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_PARAMETER_LIST))))
|
||||
<< "Vendor-specific parameters are not supported";
|
||||
ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 0);
|
||||
|
||||
cdb[0] = 0x15;
|
||||
// PF (standard parameter format)
|
||||
@ -71,12 +68,9 @@ TEST(ScsiCommandUtilTest, ModeSelect10)
|
||||
vector<int> cdb(10);
|
||||
vector<uint8_t> buf(LENGTH);
|
||||
|
||||
// PF (vendor-specific parameter format)
|
||||
// PF (vendor-specific parameter format) must not fail but be ignored
|
||||
cdb[1] = 0x00;
|
||||
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 0); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_PARAMETER_LIST))))
|
||||
<< "Vendor-specific parameters are not supported";
|
||||
ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 0);
|
||||
|
||||
// PF (standard parameter format)
|
||||
cdb[1] = 0x10;
|
||||
|
@ -284,10 +284,12 @@ TEST(ScsiControllerTest, RequestSense)
|
||||
{
|
||||
MockScsiController controller(make_shared<NiceMock<MockBus>>());
|
||||
auto device = make_shared<MockPrimaryDevice>(0);
|
||||
const unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
// ALLOCATION LENGTH
|
||||
cmd[4] = 255;
|
||||
// Non-existing LUN
|
||||
@ -295,6 +297,6 @@ TEST(ScsiControllerTest, RequestSense)
|
||||
|
||||
device->SetReady(true);
|
||||
EXPECT_CALL(controller, Status);
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdRequestSense));
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus()) << "Illegal CHECK CONDITION for non-exsting LUN";
|
||||
device->Dispatch(scsi_command::eCmdRequestSense);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus()) << "Illegal CHECK CONDITION for non-existing LUN";
|
||||
}
|
||||
|
@ -16,21 +16,13 @@ TEST(ScsiDaynaportTest, Inquiry)
|
||||
TestInquiry(SCDP, device_type::PROCESSOR, scsi_level::SCSI_2, "Dayna SCSI/Link 1.4a", 0x20, false);
|
||||
}
|
||||
|
||||
TEST(ScsiDaynaportTest, Dispatch)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto daynaport = CreateDevice(SCDP, controller);
|
||||
|
||||
EXPECT_FALSE(daynaport->Dispatch(scsi_command::eCmdIcd)) << "Command is not supported by this class";
|
||||
}
|
||||
|
||||
TEST(ScsiDaynaportTest, TestUnitReady)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto daynaport = CreateDevice(SCDP, controller);
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(daynaport->Dispatch(scsi_command::eCmdTestUnitReady)) << "TEST UNIT READY must never fail";
|
||||
daynaport->Dispatch(scsi_command::eCmdTestUnitReady);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -40,7 +32,7 @@ TEST(ScsiDaynaportTest, Read)
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto daynaport = dynamic_pointer_cast<SCSIDaynaPort>(CreateDevice(SCDP, controller));
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
// ALLOCATION LENGTH
|
||||
cmd[4] = 1;
|
||||
@ -53,7 +45,7 @@ TEST(ScsiDaynaportTest, WriteBytes)
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto daynaport = dynamic_pointer_cast<SCSIDaynaPort>(CreateDevice(SCDP, controller));
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
// Unknown data format
|
||||
cmd[5] = 0xff;
|
||||
@ -65,7 +57,7 @@ TEST(ScsiDaynaportTest, Read6)
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto daynaport = CreateDevice(SCDP, controller);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
cmd[5] = 0xff;
|
||||
EXPECT_THAT([&] { daynaport->Dispatch(scsi_command::eCmdRead6); }, Throws<scsi_exception>(AllOf(
|
||||
@ -79,7 +71,7 @@ TEST(ScsiDaynaportTest, Write6)
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto daynaport = CreateDevice(SCDP, controller);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
cmd[5] = 0x00;
|
||||
EXPECT_THAT([&] { daynaport->Dispatch(scsi_command::eCmdWrite6); }, Throws<scsi_exception>(AllOf(
|
||||
@ -109,12 +101,12 @@ TEST(ScsiDaynaportTest, TestRetrieveStats)
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto daynaport = CreateDevice(SCDP, controller);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
// ALLOCATION LENGTH
|
||||
cmd[4] = 255;
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(daynaport->Dispatch(scsi_command::eCmdRetrieveStats));
|
||||
daynaport->Dispatch(scsi_command::eCmdRetrieveStats);
|
||||
}
|
||||
|
||||
TEST(ScsiDaynaportTest, SetInterfaceMode)
|
||||
@ -122,7 +114,7 @@ TEST(ScsiDaynaportTest, SetInterfaceMode)
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto daynaport = CreateDevice(SCDP, controller);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
// Unknown interface command
|
||||
EXPECT_THAT([&] { daynaport->Dispatch(scsi_command::eCmdSetIfaceMode); }, Throws<scsi_exception>(AllOf(
|
||||
@ -132,12 +124,12 @@ TEST(ScsiDaynaportTest, SetInterfaceMode)
|
||||
// Not implemented, do nothing
|
||||
cmd[5] = SCSIDaynaPort::CMD_SCSILINK_SETMODE;
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(daynaport->Dispatch(scsi_command::eCmdSetIfaceMode));
|
||||
daynaport->Dispatch(scsi_command::eCmdSetIfaceMode);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
|
||||
cmd[5] = SCSIDaynaPort::CMD_SCSILINK_SETMAC;
|
||||
EXPECT_CALL(controller, DataOut());
|
||||
EXPECT_TRUE(daynaport->Dispatch(scsi_command::eCmdSetIfaceMode));
|
||||
daynaport->Dispatch(scsi_command::eCmdSetIfaceMode);
|
||||
|
||||
// Not implemented
|
||||
cmd[5] = SCSIDaynaPort::CMD_SCSILINK_STATS;
|
||||
@ -163,7 +155,7 @@ TEST(ScsiDaynaportTest, SetMcastAddr)
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto daynaport = CreateDevice(SCDP, controller);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_THAT([&] { daynaport->Dispatch(scsi_command::eCmdSetMcastAddr); }, Throws<scsi_exception>(AllOf(
|
||||
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
|
||||
@ -172,7 +164,7 @@ TEST(ScsiDaynaportTest, SetMcastAddr)
|
||||
|
||||
cmd[4] = 1;
|
||||
EXPECT_CALL(controller, DataOut());
|
||||
EXPECT_TRUE(daynaport->Dispatch(scsi_command::eCmdSetMcastAddr));
|
||||
daynaport->Dispatch(scsi_command::eCmdSetMcastAddr);
|
||||
}
|
||||
|
||||
TEST(ScsiDaynaportTest, EnableInterface)
|
||||
@ -180,7 +172,7 @@ TEST(ScsiDaynaportTest, EnableInterface)
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto daynaport = CreateDevice(SCDP, controller);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
// Enable
|
||||
EXPECT_THAT([&] { daynaport->Dispatch(scsi_command::eCmdEnableInterface); }, Throws<scsi_exception>(AllOf(
|
||||
@ -197,6 +189,8 @@ TEST(ScsiDaynaportTest, EnableInterface)
|
||||
TEST(ScsiDaynaportTest, GetSendDelay)
|
||||
{
|
||||
SCSIDaynaPort daynaport(0);
|
||||
const unordered_map<string, string> params;
|
||||
daynaport.Init(params);
|
||||
|
||||
EXPECT_EQ(6, daynaport.GetSendDelay());
|
||||
}
|
||||
|
@ -13,8 +13,3 @@ TEST(ScsiHostBridgeTest, Inquiry)
|
||||
{
|
||||
TestInquiry(SCBR, device_type::COMMUNICATIONS, scsi_level::SCSI_2, "RaSCSI RASCSI BRIDGE ", 0x27, false);
|
||||
}
|
||||
|
||||
TEST(ScsiHostBridgeTest, Dispatch)
|
||||
{
|
||||
TestDispatch(SCBR);
|
||||
}
|
||||
|
@ -29,18 +29,13 @@ TEST(ScsiPrinterTest, Init)
|
||||
EXPECT_TRUE(printer->Init(params));
|
||||
}
|
||||
|
||||
TEST(ScsiPrinterTest, Dispatch)
|
||||
{
|
||||
TestDispatch(SCLP);
|
||||
}
|
||||
|
||||
TEST(ScsiPrinterTest, TestUnitReady)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto printer = CreateDevice(SCLP, controller);
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(printer->Dispatch(scsi_command::eCmdTestUnitReady));
|
||||
printer->Dispatch(scsi_command::eCmdTestUnitReady);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -55,7 +50,7 @@ TEST(ScsiPrinterTest, ReserveUnit)
|
||||
auto printer = CreateDevice(SCLP, controller);
|
||||
|
||||
EXPECT_CALL(controller, Status()).Times(1);
|
||||
EXPECT_TRUE(printer->Dispatch(scsi_command::eCmdReserve6));
|
||||
printer->Dispatch(scsi_command::eCmdReserve6);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -65,7 +60,7 @@ TEST(ScsiPrinterTest, ReleaseUnit)
|
||||
auto printer = CreateDevice(SCLP, controller);
|
||||
|
||||
EXPECT_CALL(controller, Status()).Times(1);
|
||||
EXPECT_TRUE(printer->Dispatch(scsi_command::eCmdRelease6));
|
||||
printer->Dispatch(scsi_command::eCmdRelease6);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -75,7 +70,7 @@ TEST(ScsiPrinterTest, SendDiagnostic)
|
||||
auto printer = CreateDevice(SCLP, controller);
|
||||
|
||||
EXPECT_CALL(controller, Status()).Times(1);
|
||||
EXPECT_TRUE(printer->Dispatch(scsi_command::eCmdSendDiag));
|
||||
printer->Dispatch(scsi_command::eCmdSendDiagnostic);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -84,10 +79,10 @@ TEST(ScsiPrinterTest, Print)
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto printer = CreateDevice(SCLP, controller);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
EXPECT_CALL(controller, DataOut());
|
||||
EXPECT_TRUE(printer->Dispatch(scsi_command::eCmdPrint));
|
||||
printer->Dispatch(scsi_command::eCmdPrint);
|
||||
|
||||
cmd[3] = 0xff;
|
||||
cmd[4] = 0xff;
|
||||
@ -103,7 +98,7 @@ TEST(ScsiPrinterTest, StopPrint)
|
||||
auto printer = CreateDevice(SCLP, controller);
|
||||
|
||||
EXPECT_CALL(controller, Status());
|
||||
EXPECT_TRUE(printer->Dispatch(scsi_command::eCmdStopPrint));
|
||||
printer->Dispatch(scsi_command::eCmdStopPrint);
|
||||
EXPECT_EQ(status::GOOD, controller.GetStatus());
|
||||
}
|
||||
|
||||
@ -127,5 +122,4 @@ TEST(ScsiPrinterTest, WriteByteSequence)
|
||||
|
||||
vector<uint8_t> buf(1);
|
||||
EXPECT_TRUE(printer->WriteByteSequence(buf, buf.size()));
|
||||
printer->Cleanup();
|
||||
}
|
||||
|
@ -32,11 +32,6 @@ TEST(ScsiCdTest, Inquiry)
|
||||
TestInquiry(SCCD, device_type::CD_ROM, scsi_level::SCSI_2, "RaSCSI SCSI CD-ROM ", 0x1f, true);
|
||||
}
|
||||
|
||||
TEST(ScsiCdTest, Dispatch)
|
||||
{
|
||||
TestDispatch(SCCD);
|
||||
}
|
||||
|
||||
TEST(ScsiCdTest, SetUpModePages)
|
||||
{
|
||||
map<int, vector<byte>> pages;
|
||||
@ -120,6 +115,8 @@ TEST(ScsiCdTest, ReadToc)
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
const unordered_set<uint32_t> sector_sizes;
|
||||
auto cd = make_shared<MockSCSICD>(0, sector_sizes);
|
||||
const unordered_map<string, string> params;
|
||||
cd->Init(params);
|
||||
|
||||
controller.AddDevice(cd);
|
||||
|
||||
|
@ -117,8 +117,3 @@ TEST(ScsiHdTest, ModeSelect)
|
||||
buf[20] = 0x02;
|
||||
EXPECT_NO_THROW(hd.ModeSelect(scsi_command::eCmdModeSelect10, cmd, buf, 255)) << "MODE SELECT(10) is supported";
|
||||
}
|
||||
|
||||
TEST(ScsiHdTest, Dispatch)
|
||||
{
|
||||
TestDispatch(SCHD);
|
||||
}
|
||||
|
@ -148,9 +148,3 @@ TEST(ScsiMoTest, ModeSelect)
|
||||
buf[20] = 0x08;
|
||||
EXPECT_NO_THROW(mo.ModeSelect(scsi_command::eCmdModeSelect10, cmd, buf, 255)) << "MODE SELECT(10) is supported";
|
||||
}
|
||||
|
||||
TEST(ScsiMoTest, Dispatch)
|
||||
{
|
||||
TestDispatch(SCMO);
|
||||
}
|
||||
|
||||
|
@ -151,14 +151,3 @@ TEST(StorageDeviceTest, GetFileSize)
|
||||
device.SetFilename("/non_existing_file");
|
||||
EXPECT_THROW(device.GetFileSize(), io_exception);
|
||||
}
|
||||
|
||||
TEST(StorageDeviceTest, Dispatch)
|
||||
{
|
||||
MockAbstractController controller(make_shared<MockBus>(), 0);
|
||||
auto device = make_shared<NiceMock<MockStorageDevice>>();
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
EXPECT_FALSE(device->Dispatch(scsi_command::eCmdIcd)) << "Command is not supported by this class";
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "mocks.h"
|
||||
#include "controllers/controller_manager.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "rascsi_version.h"
|
||||
#include "test_shared.h"
|
||||
#include <unistd.h>
|
||||
@ -25,6 +26,8 @@ shared_ptr<PrimaryDevice> CreateDevice(PbDeviceType type, MockAbstractController
|
||||
auto controller_manager = make_shared<ControllerManager>(controller.GetBus());
|
||||
|
||||
auto device = device_factory.CreateDevice(*controller_manager, type, 0, extension);
|
||||
unordered_map<string, string> params;
|
||||
device->Init(params);
|
||||
|
||||
controller.AddDevice(device);
|
||||
|
||||
@ -37,12 +40,12 @@ void TestInquiry(PbDeviceType type, device_type t, scsi_level l, const string& i
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto device = CreateDevice(type, controller, extension);
|
||||
|
||||
vector<int>& cmd = controller.GetCmd();
|
||||
auto& cmd = controller.GetCmd();
|
||||
|
||||
// ALLOCATION LENGTH
|
||||
cmd[4] = 255;
|
||||
EXPECT_CALL(controller, DataIn());
|
||||
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdInquiry));
|
||||
device->Dispatch(scsi_command::eCmdInquiry);
|
||||
const vector<uint8_t>& buffer = controller.GetBuffer();
|
||||
EXPECT_EQ(t, static_cast<device_type>(buffer[0]));
|
||||
EXPECT_EQ(removable ? 0x80: 0x00, buffer[1]);
|
||||
@ -61,14 +64,6 @@ void TestInquiry(PbDeviceType type, device_type t, scsi_level l, const string& i
|
||||
EXPECT_TRUE(!memcmp(product_data.c_str(), &buffer[8], 28));
|
||||
}
|
||||
|
||||
void TestDispatch(PbDeviceType type)
|
||||
{
|
||||
NiceMock<MockAbstractController> controller(make_shared<MockBus>(), 0);
|
||||
auto device = CreateDevice(type, controller);
|
||||
|
||||
EXPECT_FALSE(device->Dispatch(scsi_command::eCmdIcd)) << "Command is not supported by this class";
|
||||
}
|
||||
|
||||
pair<int, path> OpenTempFile()
|
||||
{
|
||||
const string filename = string(temp_directory_path()) + "/rascsi_test-XXXXXX"; //NOSONAR Publicly writable directory is fine here
|
||||
|
@ -27,8 +27,6 @@ shared_ptr<PrimaryDevice> CreateDevice(PbDeviceType, MockAbstractController&, co
|
||||
void TestInquiry(PbDeviceType, scsi_defs::device_type, scsi_defs::scsi_level, const string&,
|
||||
int, bool, const string& = "");
|
||||
|
||||
void TestDispatch(PbDeviceType);
|
||||
|
||||
pair<int, path> OpenTempFile();
|
||||
path CreateTempFile(int);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user