Added unit tests and convenience methods, fixed SonarCloud issues, include file cleanup (#849)

* Added unit tests, add convenience methods, fixed SonarCloud issues

* Replaced C-style arrays by C++ arrays

* Replaced ASSERT

* Constants cleanup

* Include cleanup

* Moved _LARGEFILE64_SOURCE to Makefile, so that os.h is not always needed

* os.h cleanup

* Fixed clang++ warnings

* Split protobuf_util

* Fixed SonarCloud issues

* Removed duplicate code

* DeviceFactory is not a singleton anymore

* Replaced macros

* Removed obsolete interface

* Replaced DWORD

* Improved locality of code

* Removed duplicate code

* Extracted CDTrack

* Split disk_track_cache

* Replaced localtime by localtime_r

* Moved CTapDriver cleanup to destructor

* Removed redunant struct keywords
This commit is contained in:
Uwe Seimet 2022-09-25 23:49:24 +02:00 committed by GitHub
parent 12b61ada84
commit 016a616b72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 3109 additions and 2478 deletions

View File

@ -31,7 +31,7 @@ ifeq ("$(shell uname -s)","Linux")
endif
CXXFLAGS += -std=c++17 -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP
CXXFLAGS += -std=c++17 -iquote . -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE -MD -MP
## EXTRA_FLAGS : Can be used to pass special purpose flags
CXXFLAGS += $(EXTRA_FLAGS)
@ -93,7 +93,8 @@ SRC_RASCSI_CORE = \
rascsi_image.cpp \
rascsi_response.cpp \
rasutil.cpp \
protobuf_util.cpp \
command_util.cpp \
socket_connector.cpp \
localizer.cpp
SRC_RASCSI_CORE += $(shell find ./controllers -name '*.cpp')
SRC_RASCSI_CORE += $(shell find ./devices -name '*.cpp')
@ -115,7 +116,8 @@ SRC_RASCTL = \
rasctl_display.cpp \
rascsi_version.cpp \
rasutil.cpp \
protobuf_util.cpp \
command_util.cpp \
socket_connector.cpp \
localizer.cpp
SRC_RASCTL += $(SRC_PROTOBUF)

View File

@ -11,7 +11,17 @@
#include <string>
struct CommandContext {
int fd = -1;
std::string locale = "";
class SocketConnector;
class Localizer;
struct CommandContext
{
CommandContext(SocketConnector *c, const Localizer *l, int f, const std::string& s)
: connector(c), localizer(l), fd(f), locale(s) {}
~CommandContext() = default;
SocketConnector *connector;
const Localizer *localizer;
int fd;
std::string locale;
};

View File

@ -0,0 +1,135 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "log.h"
#include "rascsi_interface.pb.h"
#include "localizer.h"
#include "socket_connector.h"
#include "command_util.h"
#include <sstream>
using namespace std;
using namespace rascsi_interface;
#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
static const char COMPONENT_SEPARATOR = ':';
static const char KEY_VALUE_SEPARATOR = '=';
void command_util::ParseParameters(PbDeviceDefinition& device, const string& params)
{
if (params.empty()) {
return;
}
if (params.find(KEY_VALUE_SEPARATOR) != string::npos) {
stringstream ss(params);
string p;
while (getline(ss, p, COMPONENT_SEPARATOR)) {
if (!p.empty()) {
size_t separator_pos = p.find(KEY_VALUE_SEPARATOR);
if (separator_pos != string::npos) {
AddParam(device, p.substr(0, separator_pos), string_view(p).substr(separator_pos + 1));
}
}
}
}
// Old style parameters, for backwards compatibility only.
// Only one of these parameters will be used by rascsi, depending on the device type.
else {
AddParam(device, "file", params);
if (params != "bridge" && params != "daynaport" && params != "printer" && params != "services") {
AddParam(device, "interfaces", params);
}
}
}
string command_util::GetParam(const PbCommand& command, const string& key)
{
const auto& it = command.params().find(key);
return it != command.params().end() ? it->second : "";
}
string command_util::GetParam(const PbDeviceDefinition& device, const string& key)
{
const auto& it = device.params().find(key);
return it != device.params().end() ? it->second : "";
}
void command_util::AddParam(PbCommand& command, const string& key, string_view value)
{
if (!key.empty() && !value.empty()) {
auto& map = *command.mutable_params();
map[key] = value;
}
}
void command_util::AddParam(PbDevice& device, const string& key, string_view value)
{
if (!key.empty() && !value.empty()) {
auto& map = *device.mutable_params();
map[key] = value;
}
}
void command_util::AddParam(PbDeviceDefinition& device, const string& key, string_view value)
{
if (!key.empty() && !value.empty()) {
auto& map = *device.mutable_params();
map[key] = value;
}
}
bool command_util::ReturnLocalizedError(const CommandContext& context, const LocalizationKey key,
const string& arg1, const string& arg2, const string& arg3)
{
return ReturnLocalizedError(context, key, NO_ERROR_CODE, arg1, arg2, arg3);
}
bool command_util::ReturnLocalizedError(const CommandContext& context, const LocalizationKey key,
const PbErrorCode error_code, const string& arg1, const string& arg2, const string& arg3)
{
// For the logfile always use English
LOGERROR("%s", context.localizer->Localize(key, "en", arg1, arg2, arg3).c_str())
return ReturnStatus(context, false, context.localizer->Localize(key, context.locale, arg1, arg2, arg3), error_code,
false);
}
bool command_util::ReturnStatus(const CommandContext& context, bool status, const string& msg,
const PbErrorCode error_code, bool log)
{
// Do not log twice if logging has already been done in the localized error handling above
if (log && !status && !msg.empty()) {
LOGERROR("%s", msg.c_str())
}
if (context.fd == -1) {
if (!msg.empty()) {
if (status) {
FPRT(stderr, "Error: ");
FPRT(stderr, "%s", msg.c_str());
FPRT(stderr, "\n");
}
else {
FPRT(stdout, "%s", msg.c_str());
FPRT(stderr, "\n");
}
}
}
else {
PbResult result;
result.set_status(status);
result.set_error_code(error_code);
result.set_msg(msg);
context.connector->SerializeMessage(context.fd, result);
}
return status;
}

View File

@ -15,13 +15,11 @@
#include "rascsi_interface.pb.h"
#include "command_context.h"
#include "localizer.h"
#include <sstream>
#include <string>
using namespace std;
using namespace rascsi_interface;
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi
namespace protobuf_util
namespace command_util
{
void ParseParameters(PbDeviceDefinition&, const string&);
string GetParam(const PbCommand&, const string&);
@ -29,9 +27,6 @@ namespace protobuf_util
void AddParam(PbCommand&, const string&, string_view);
void AddParam(PbDevice&, const string&, string_view);
void AddParam(PbDeviceDefinition&, const string&, string_view);
void SerializeMessage(int, const google::protobuf::Message&);
void DeserializeMessage(int, google::protobuf::Message&);
size_t ReadBytes(int, vector<byte>&);
bool ReturnLocalizedError(const CommandContext&, const LocalizationKey, const string& = "", const string& = "",
const string& = "");
bool ReturnLocalizedError(const CommandContext&, const LocalizationKey, const PbErrorCode, const string& = "",

View File

@ -20,6 +20,6 @@
#define USE_SEL_EVENT_ENABLE // Check SEL signal by event
// This avoids an indefinite loop with warnings if there is no RaSCSI hardware
// and thus helps with running rasctl and unit test on x86 hardware.
#if defined(__x86_64__) || defined(__X86__)
#if defined(__x86_64__) || defined(__X86__) || !defined(__linux)
#undef USE_SEL_EVENT_ENABLE
#endif

View File

@ -17,6 +17,10 @@ PrimaryDevice *AbstractController::GetDeviceForLun(int lun) const {
bool AbstractController::AddDevice(PrimaryDevice *device)
{
if (device->GetLun() >= GetMaxLuns()) {
return false;
}
if (HasDeviceForLun(device->GetLun())) {
return false;
}

View File

@ -11,16 +11,14 @@
#pragma once
#include "phase_handler.h"
#include "scsi.h"
#include <unordered_map>
#include <vector>
#include <memory>
using namespace std;
class PrimaryDevice;
class AbstractController : public PhaseHandler
class AbstractController
{
public:
@ -39,7 +37,7 @@ public:
BUS::phase_t phase = BUS::phase_t::busfree; // Transition phase
// commands
vector<int> cmd; // Command data, dynamically allocated per received command
std::vector<int> cmd; // Command data, dynamically allocated per received command
uint32_t status; // Status data
int message; // Message data
@ -52,14 +50,24 @@ public:
uint32_t length; // Transfer remaining length
// Logical units of this device controller mapped to their LUN numbers
unordered_map<int, PrimaryDevice *> luns;
std::unordered_map<int, PrimaryDevice *> luns;
};
AbstractController(shared_ptr<BUS> bus, int target_id) : target_id(target_id), bus(bus) {}
~AbstractController() override = default;
virtual ~AbstractController() = default;
AbstractController(AbstractController&) = delete;
AbstractController& operator=(const AbstractController&) = delete;
virtual void SetPhase(BUS::phase_t) = 0;
virtual void BusFree() = 0;
virtual void Selection() = 0;
virtual void Command() = 0;
virtual void Status() = 0;
virtual void DataIn() = 0;
virtual void DataOut() = 0;
virtual void MsgIn() = 0;
virtual void MsgOut() = 0;
virtual BUS::phase_t Process(int) = 0;
virtual void Error(scsi_defs::sense_key, scsi_defs::asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
@ -91,6 +99,7 @@ public:
protected:
scsi_defs::scsi_command GetOpcode() const { return (scsi_defs::scsi_command)ctrl.cmd[0]; }
int GetLun() const { return (ctrl.cmd[1] >> 5) & 0x07; }
shared_ptr<BUS> bus;

View File

@ -14,7 +14,7 @@
#include <unordered_map>
#include <memory>
using namespace std;
using namespace std; //NOSONAR Not relevant for rascsi
class BUS;
class AbstractController;

View File

@ -1,33 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
// An interface with methods for switching bus phases
//
//---------------------------------------------------------------------------
#pragma once
#include "scsi.h"
class PhaseHandler
{
public:
PhaseHandler() = default;
virtual ~PhaseHandler() = default;
virtual void SetPhase(BUS::phase_t) = 0;
virtual void BusFree() = 0;
virtual void Selection() = 0;
virtual void Command() = 0;
virtual void Status() = 0;
virtual void DataIn() = 0;
virtual void DataOut() = 0;
virtual void MsgIn() = 0;
virtual void MsgOut() = 0;
};

View File

@ -23,6 +23,9 @@
#include "scsi_controller.h"
#include <sstream>
#include <iomanip>
#ifdef __linux
#include <linux/if_tun.h>
#endif
using namespace scsi_defs;
@ -53,7 +56,7 @@ void ScsiController::Reset()
scsi.atnmsg = false;
scsi.msc = 0;
memset(scsi.msb, 0x00, sizeof(scsi.msb));
memset(scsi.msb.data(), 0x00, scsi.msb.size());
is_byte_transfer = false;
bytes_to_transfer = 0;
@ -210,7 +213,7 @@ void ScsiController::Selection()
{
if (ctrl.phase != BUS::phase_t::selection) {
// A different device controller was selected
if (int id = 1 << GetTargetId(); (bus->GetDAT() & id) == 0) {
if (int id = 1 << GetTargetId(); ((int)bus->GetDAT() & id) == 0) {
return;
}
@ -404,7 +407,7 @@ void ScsiController::MsgOut()
if (ctrl.phase == BUS::phase_t::selection) {
scsi.atnmsg = true;
scsi.msc = 0;
memset(scsi.msb, 0x00, sizeof(scsi.msb));
memset(scsi.msb.data(), 0x00, scsi.msb.size());
}
SetPhase(BUS::phase_t::msgout);
@ -621,9 +624,6 @@ void ScsiController::Receive()
return;
}
int len;
BYTE data;
LOGTRACE("%s",__PRETTY_FUNCTION__)
// REQ is low
@ -633,11 +633,10 @@ void ScsiController::Receive()
// Length != 0 if received
if (ctrl.length != 0) {
LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length)
// Receive
len = bus->ReceiveHandShake(&ctrl.buffer[ctrl.offset], ctrl.length);
// If not able to receive all, move to status phase
if (len != (int)ctrl.length) {
if (int len = bus->ReceiveHandShake(&ctrl.buffer[ctrl.offset], ctrl.length);
len != (int)ctrl.length) {
LOGERROR("%s Not able to receive %d bytes of data, only received %d",__PRETTY_FUNCTION__, ctrl.length, len)
Error(sense_key::ABORTED_COMMAND);
return;
@ -689,7 +688,7 @@ void ScsiController::Receive()
}
// Continue to receive if block !=0
if (ctrl.blocks != 0){
if (ctrl.blocks != 0) {
assert(ctrl.length > 0);
assert(ctrl.offset == 0);
return;
@ -698,98 +697,11 @@ void ScsiController::Receive()
// Move to next phase
switch (ctrl.phase) {
case BUS::phase_t::command:
len = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]);
for (int i = 0; i < len; i++) {
ctrl.cmd[i] = ctrl.buffer[i];
LOGTRACE("%s Command Byte %d: $%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i])
}
Execute();
ProcessCommand();
break;
case BUS::phase_t::msgout:
// Continue message out phase as long as ATN keeps asserting
if (bus->GetATN()) {
// Data transfer is 1 byte x 1 block
ctrl.offset = 0;
ctrl.length = 1;
ctrl.blocks = 1;
return;
}
// Parsing messages sent by ATN
if (scsi.atnmsg) {
int i = 0;
while (i < scsi.msc) {
// Message type
data = scsi.msb[i];
// ABORT
if (data == 0x06) {
LOGTRACE("Message code ABORT $%02X", data)
BusFree();
return;
}
// BUS DEVICE RESET
if (data == 0x0C) {
LOGTRACE("Message code BUS DEVICE RESET $%02X", data)
scsi.syncoffset = 0;
BusFree();
return;
}
// IDENTIFY
if (data >= 0x80) {
identified_lun = data & 0x1F;
LOGTRACE("Message code IDENTIFY $%02X, LUN %d selected", data, identified_lun)
}
// Extended Message
if (data == 0x01) {
LOGTRACE("Message code EXTENDED MESSAGE $%02X", data)
// Check only when synchronous transfer is possible
if (!scsi.syncenable || scsi.msb[i + 2] != 0x01) {
ctrl.length = 1;
ctrl.blocks = 1;
ctrl.buffer[0] = 0x07;
MsgIn();
return;
}
scsi.syncperiod = scsi.msb[i + 3];
if (scsi.syncperiod > MAX_SYNC_PERIOD) {
scsi.syncperiod = MAX_SYNC_PERIOD;
}
scsi.syncoffset = scsi.msb[i + 4];
if (scsi.syncoffset > MAX_SYNC_OFFSET) {
scsi.syncoffset = MAX_SYNC_OFFSET;
}
// STDR response message generation
ctrl.length = 5;
ctrl.blocks = 1;
ctrl.buffer[0] = 0x01;
ctrl.buffer[1] = 0x03;
ctrl.buffer[2] = 0x01;
ctrl.buffer[3] = scsi.syncperiod;
ctrl.buffer[4] = scsi.syncoffset;
MsgIn();
return;
}
// next
i++;
}
}
// Initialize ATN message reception status
scsi.atnmsg = false;
Command();
ProcessMessage();
break;
case BUS::phase_t::dataout:
@ -820,9 +732,6 @@ bool ScsiController::XferMsg(int msg)
void ScsiController::ReceiveBytes()
{
uint32_t len;
BYTE data;
LOGTRACE("%s",__PRETTY_FUNCTION__)
// REQ is low
@ -832,10 +741,9 @@ void ScsiController::ReceiveBytes()
if (ctrl.length) {
LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length)
len = bus->ReceiveHandShake(&ctrl.buffer[ctrl.offset], ctrl.length);
// If not able to receive all, move to status phase
if (len != ctrl.length) {
if (uint32_t len = bus->ReceiveHandShake(&ctrl.buffer[ctrl.offset], ctrl.length);
len != ctrl.length) {
LOGERROR("%s Not able to receive %d bytes of data, only received %d",
__PRETTY_FUNCTION__, ctrl.length, len)
Error(sense_key::ABORTED_COMMAND);
@ -886,98 +794,11 @@ void ScsiController::ReceiveBytes()
// Move to next phase
switch (ctrl.phase) {
case BUS::phase_t::command:
len = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]);
for (uint32_t i = 0; i < len; i++) {
ctrl.cmd[i] = ctrl.buffer[i];
LOGTRACE("%s Command Byte %d: $%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i])
}
Execute();
ProcessCommand();
break;
case BUS::phase_t::msgout:
// Continue message out phase as long as ATN keeps asserting
if (bus->GetATN()) {
// Data transfer is 1 byte x 1 block
ctrl.offset = 0;
ctrl.length = 1;
ctrl.blocks = 1;
return;
}
// Parsing messages sent by ATN
if (scsi.atnmsg) {
int i = 0;
while (i < scsi.msc) {
// Message type
data = scsi.msb[i];
// ABORT
if (data == 0x06) {
LOGTRACE("Message code ABORT $%02X", data)
BusFree();
return;
}
// BUS DEVICE RESET
if (data == 0x0C) {
LOGTRACE("Message code BUS DEVICE RESET $%02X", data)
scsi.syncoffset = 0;
BusFree();
return;
}
// IDENTIFY
if (data >= 0x80) {
identified_lun = data & 0x1F;
LOGTRACE("Message code IDENTIFY $%02X, LUN %d selected", data, identified_lun)
}
// Extended Message
if (data == 0x01) {
LOGTRACE("Message code EXTENDED MESSAGE $%02X", data)
// Check only when synchronous transfer is possible
if (!scsi.syncenable || scsi.msb[i + 2] != 0x01) {
ctrl.length = 1;
ctrl.blocks = 1;
ctrl.buffer[0] = 0x07;
MsgIn();
return;
}
scsi.syncperiod = scsi.msb[i + 3];
if (scsi.syncperiod > MAX_SYNC_PERIOD) {
scsi.syncperiod = MAX_SYNC_PERIOD;
}
scsi.syncoffset = scsi.msb[i + 4];
if (scsi.syncoffset > MAX_SYNC_OFFSET) {
scsi.syncoffset = MAX_SYNC_OFFSET;
}
// STDR response message generation
ctrl.length = 5;
ctrl.blocks = 1;
ctrl.buffer[0] = 0x01;
ctrl.buffer[1] = 0x03;
ctrl.buffer[2] = 0x01;
ctrl.buffer[3] = scsi.syncperiod;
ctrl.buffer[4] = scsi.syncoffset;
MsgIn();
return;
}
// next
i++;
}
}
// Initialize ATN message reception status
scsi.atnmsg = false;
Command();
ProcessMessage();
break;
case BUS::phase_t::dataout:
@ -1059,8 +880,6 @@ void ScsiController::FlushUnit()
//
// Data transfer IN
// *Reset offset and length
// TODO XferIn probably needs a dispatcher, in order to avoid subclassing Disk, i.e.
// just like the actual SCSI commands XferIn should be executed by the respective device
//
//---------------------------------------------------------------------------
bool ScsiController::XferIn(BYTE *buf)
@ -1107,8 +926,6 @@ bool ScsiController::XferIn(BYTE *buf)
//
// Data transfer OUT
// *If cont=true, reset the offset and length
// TODO XferOut probably needs a dispatcher, in order to avoid subclassing Disk, i.e.
// just like the actual SCSI commands XferOut should be executed by the respective device
//
//---------------------------------------------------------------------------
bool ScsiController::XferOutBlockOriented(bool cont)
@ -1201,9 +1018,107 @@ bool ScsiController::XferOutBlockOriented(bool cont)
return true;
}
void ScsiController::ProcessCommand()
{
uint32_t len = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]);
for (uint32_t i = 0; i < len; i++) {
ctrl.cmd[i] = ctrl.buffer[i];
LOGTRACE("%s Command Byte %d: $%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i])
}
Execute();
}
void ScsiController::ProcessMessage()
{
// Continue message out phase as long as ATN keeps asserting
if (bus->GetATN()) {
// Data transfer is 1 byte x 1 block
ctrl.offset = 0;
ctrl.length = 1;
ctrl.blocks = 1;
return;
}
// Parsing messages sent by ATN
if (scsi.atnmsg) {
int i = 0;
while (i < scsi.msc) {
// Message type
BYTE data = scsi.msb[i];
// ABORT
if (data == 0x06) {
LOGTRACE("Message code ABORT $%02X", data)
BusFree();
return;
}
// BUS DEVICE RESET
if (data == 0x0C) {
LOGTRACE("Message code BUS DEVICE RESET $%02X", data)
scsi.syncoffset = 0;
BusFree();
return;
}
// IDENTIFY
if (data >= 0x80) {
identified_lun = (int)data & 0x1F;
LOGTRACE("Message code IDENTIFY $%02X, LUN %d selected", data, identified_lun)
}
// Extended Message
if (data == 0x01) {
LOGTRACE("Message code EXTENDED MESSAGE $%02X", data)
// Check only when synchronous transfer is possible
if (!scsi.syncenable || scsi.msb[i + 2] != 0x01) {
ctrl.length = 1;
ctrl.blocks = 1;
ctrl.buffer[0] = 0x07;
MsgIn();
return;
}
scsi.syncperiod = scsi.msb[i + 3];
if (scsi.syncperiod > MAX_SYNC_PERIOD) {
scsi.syncperiod = MAX_SYNC_PERIOD;
}
scsi.syncoffset = scsi.msb[i + 4];
if (scsi.syncoffset > MAX_SYNC_OFFSET) {
scsi.syncoffset = MAX_SYNC_OFFSET;
}
// STDR response message generation
ctrl.length = 5;
ctrl.blocks = 1;
ctrl.buffer[0] = 0x01;
ctrl.buffer[1] = 0x03;
ctrl.buffer[2] = 0x01;
ctrl.buffer[3] = scsi.syncperiod;
ctrl.buffer[4] = scsi.syncoffset;
MsgIn();
return;
}
// next
i++;
}
}
// Initialize ATN message reception status
scsi.atnmsg = false;
Command();
}
int ScsiController::GetEffectiveLun() const
{
return identified_lun != -1 ? identified_lun : (ctrl.cmd[1] >> 5) & 0x07;
// Return LUN from IDENTIFY message, or return the LUN from the CDB as fallback
return identified_lun != -1 ? identified_lun : GetLun();
}
void ScsiController::Sleep()

View File

@ -19,6 +19,7 @@
#include "abstract_controller.h"
#include "os.h"
#include "scsi.h"
#include <array>
class PrimaryDevice;
@ -47,13 +48,13 @@ class ScsiController : public AbstractController
// ATN message
bool atnmsg;
int msc;
BYTE msb[256];
std::array<BYTE, 256> msb;
};
public:
ScsiController(shared_ptr<BUS>, int);
~ScsiController() override;
~ScsiController () override;
ScsiController(ScsiController&) = delete;
ScsiController& operator=(const ScsiController&) = delete;
@ -109,6 +110,9 @@ private:
void FlushUnit();
void Receive();
void ProcessCommand();
void ProcessMessage();
void ScheduleShutdown(rascsi_shutdown_mode mode) override { shutdown_mode = mode; }
void Sleep();

View File

@ -0,0 +1,125 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
//---------------------------------------------------------------------------
#include "cd_track.h"
void CDTrack::Init(int track, uint32_t first, uint32_t last)
{
assert(!valid);
assert(track >= 1);
assert(first < last);
// Set and enable track number
track_no = track;
valid = true;
// Remember LBA
first_lba = first;
last_lba = last;
}
void CDTrack::SetPath(bool cdda, const Filepath& path)
{
assert(valid);
// CD-DA or data
audio = cdda;
// Remember the path
imgpath = path;
}
void CDTrack::GetPath(Filepath& path) const
{
assert(valid);
// Return the path (by reference)
path = imgpath;
}
//---------------------------------------------------------------------------
//
// Gets the start of LBA
//
//---------------------------------------------------------------------------
uint32_t CDTrack::GetFirst() const
{
assert(valid);
assert(first_lba < last_lba);
return first_lba;
}
//---------------------------------------------------------------------------
//
// Get the end of LBA
//
//---------------------------------------------------------------------------
uint32_t CDTrack::GetLast() const
{
assert(valid);
assert(first_lba < last_lba);
return last_lba;
}
uint32_t CDTrack::GetBlocks() const
{
assert(valid);
assert(first_lba < last_lba);
// Calculate from start LBA and end LBA
return last_lba - first_lba + 1;
}
int CDTrack::GetTrackNo() const
{
assert(valid);
assert(track_no >= 1);
return track_no;
}
//---------------------------------------------------------------------------
//
// Is valid block
//
//---------------------------------------------------------------------------
bool CDTrack::IsValid(uint32_t lba) const
{
// false if the track itself is invalid
if (!valid) {
return false;
}
// If the block is BEFORE the first block
if (lba < first_lba) {
return false;
}
// If the block is AFTER the last block
if (last_lba < lba) {
return false;
}
// This track is valid
return true;
}
bool CDTrack::IsAudio() const
{
assert(valid);
return audio;
}

View File

@ -0,0 +1,47 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
//---------------------------------------------------------------------------
#pragma once
#include "filepath.h"
class CDTrack final
{
public:
CDTrack() = default;
~CDTrack() = default;
CDTrack(CDTrack&) = delete;
CDTrack& operator=(const CDTrack&) = delete;
void Init(int track, DWORD first, DWORD last);
// Properties
void SetPath(bool cdda, const Filepath& path); // Set the path
void GetPath(Filepath& path) const; // Get the path
uint32_t GetFirst() const; // Get the start LBA
uint32_t GetLast() const; // Get the last LBA
uint32_t GetBlocks() const; // Get the number of blocks
int GetTrackNo() const; // Get the track number
bool IsValid(DWORD lba) const; // Is this a valid LBA?
bool IsAudio() const; // Is this an audio track?
private:
bool valid = false; // Valid track
int track_no = -1; // Track number
uint32_t first_lba = 0; // First LBA
uint32_t last_lba = 0; // Last LBA
bool audio = false; // Audio track flag
Filepath imgpath; // Image file path
};

File diff suppressed because it is too large Load Diff

View File

@ -368,13 +368,13 @@ public:
void Insert(CRing* pRoot)
{
// Separate the relevant objects
ASSERT(next);
ASSERT(prev);
assert(next);
assert(prev);
next->prev = prev;
prev->next = next;
// Insert into the beginning of the ring
ASSERT(pRoot);
ASSERT(pRoot->next);
assert(pRoot);
assert(pRoot->next);
next = pRoot->next;
prev = pRoot;
pRoot->next->prev = this;
@ -385,13 +385,13 @@ public:
void InsertTail(CRing* pRoot)
{
// Separate the relevant objects
ASSERT(next);
ASSERT(prev);
assert(next);
assert(prev);
next->prev = prev;
prev->next = next;
// Insert into the end of the ring
ASSERT(pRoot);
ASSERT(pRoot->prev);
assert(pRoot);
assert(pRoot->prev);
next = pRoot;
prev = pRoot->prev;
pRoot->prev->next = this;
@ -404,8 +404,8 @@ public:
if (next == prev) return;
// Insert into the beginning of the ring
ASSERT(pRoot);
ASSERT(pRoot->next);
assert(pRoot);
assert(pRoot->next);
pRoot->next->prev = prev;
prev->next = pRoot->next;
pRoot->next = next;
@ -419,8 +419,8 @@ public:
void Remove()
{
// Separate the relevant objects
ASSERT(next);
ASSERT(prev);
assert(next);
assert(prev);
next->prev = prev;
prev->next = next;
// To be safe, assign self (nothing stops you from separating any number of times)

View File

@ -7,24 +7,27 @@
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) akuker
//
// [ TAP Driver ]
//
//---------------------------------------------------------------------------
#include <unistd.h>
#include <poll.h>
#include <arpa/inet.h>
#ifdef __linux__
#include <net/if.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
#endif
#include "os.h"
#include "ctapdriver.h"
#include "log.h"
#include "rasutil.h"
#include "rascsi_exceptions.h"
#include <net/if.h>
#include <sys/ioctl.h>
#include <sstream>
#ifdef __linux__
#include <sys/epoll.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/sockios.h>
#endif
using namespace std;
using namespace ras_util;
@ -34,7 +37,10 @@ using namespace ras_util;
//
//---------------------------------------------------------------------------
static bool br_setif(int br_socket_fd, const char* bridgename, const char* ifname, bool add) {
struct ifreq ifr;
#ifndef __linux
return false;
#else
ifreq ifr;
ifr.ifr_ifindex = if_nametoindex(ifname);
if (ifr.ifr_ifindex == 0) {
LOGERROR("Can't if_nametoindex %s: %s", ifname, strerror(errno))
@ -46,10 +52,41 @@ static bool br_setif(int br_socket_fd, const char* bridgename, const char* ifnam
return false;
}
return true;
#endif
}
CTapDriver::~CTapDriver()
{
if (m_hTAP != -1) {
if (int br_socket_fd; (br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
LOGERROR("Can't open bridge socket: %s", strerror(errno))
} else {
LOGDEBUG("brctl delif %s ras0", BRIDGE_NAME)
if (!br_setif(br_socket_fd, BRIDGE_NAME, "ras0", false)) {
LOGWARN("Warning: Removing ras0 from the bridge failed.")
LOGWARN("You may need to manually remove the ras0 tap device from the bridge")
}
close(br_socket_fd);
}
// Release TAP defice
close(m_hTAP);
}
if (m_pcap_dumper != nullptr) {
pcap_dump_close(m_pcap_dumper);
}
if (m_pcap != nullptr) {
pcap_close(m_pcap);
}
}
static bool ip_link(int fd, const char* ifname, bool up) {
struct ifreq ifr;
#ifndef __linux
return false;
#else
ifreq ifr;
strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1); // Need to save room for null terminator
int err = ioctl(fd, SIOCGIFFLAGS, &ifr);
if (err) {
@ -66,6 +103,7 @@ static bool ip_link(int fd, const char* ifname, bool up) {
return false;
}
return true;
#endif
}
static bool is_interface_up(string_view interface) {
@ -88,6 +126,9 @@ static bool is_interface_up(string_view interface) {
bool CTapDriver::Init(const unordered_map<string, string>& const_params)
{
#ifndef __linux
return false;
#else
unordered_map<string, string> params = const_params;
if (params.count("interfaces")) {
LOGWARN("You are using the deprecated 'interfaces' parameter. "
@ -119,7 +160,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
LOGTRACE("Opened tap device %d", m_hTAP)
// IFF_NO_PI for no extra packet information
struct ifreq ifr;
ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
string dev = "ras0";
@ -238,10 +279,10 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
return false;
}
struct ifreq ifr_a;
ifreq ifr_a;
ifr_a.ifr_addr.sa_family = AF_INET;
strncpy(ifr_a.ifr_name, BRIDGE_NAME, IFNAMSIZ);
if (auto addr = (struct sockaddr_in*)&ifr_a.ifr_addr;
if (auto addr = (sockaddr_in*)&ifr_a.ifr_addr;
inet_pton(AF_INET, address.c_str(), &addr->sin_addr) != 1) {
LOGERROR("Can't convert '%s' into a network address: %s", address.c_str(), strerror(errno))
@ -251,10 +292,10 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
return false;
}
struct ifreq ifr_n;
ifreq ifr_n;
ifr_n.ifr_addr.sa_family = AF_INET;
strncpy(ifr_n.ifr_name, BRIDGE_NAME, IFNAMSIZ);
if (auto mask = (struct sockaddr_in*)&ifr_n.ifr_addr;
if (auto mask = (sockaddr_in*)&ifr_n.ifr_addr;
inet_pton(AF_INET, netmask.c_str(), &mask->sin_addr) != 1) {
LOGERROR("Can't convert '%s' into a netmask: %s", netmask.c_str(), strerror(errno))
@ -331,6 +372,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
LOGINFO("Tap device %s created", ifr.ifr_name)
return true;
#endif
}
void CTapDriver::OpenDump(const Filepath& path) {
@ -349,36 +391,6 @@ void CTapDriver::OpenDump(const Filepath& path) {
LOGTRACE("%s Opened %s for dumping", __PRETTY_FUNCTION__, path.GetPath())
}
void CTapDriver::Cleanup()
{
if (int br_socket_fd; (br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
LOGERROR("Can't open bridge socket: %s", strerror(errno))
} else {
LOGDEBUG("brctl delif %s ras0", BRIDGE_NAME)
if (!br_setif(br_socket_fd, BRIDGE_NAME, "ras0", false)) {
LOGWARN("Warning: Removing ras0 from the bridge failed.")
LOGWARN("You may need to manually remove the ras0 tap device from the bridge")
}
close(br_socket_fd);
}
// Release TAP defice
if (m_hTAP != -1) {
close(m_hTAP);
m_hTAP = -1;
}
if (m_pcap_dumper != nullptr) {
pcap_dump_close(m_pcap_dumper);
m_pcap_dumper = nullptr;
}
if (m_pcap != nullptr) {
pcap_close(m_pcap);
m_pcap = nullptr;
}
}
bool CTapDriver::Enable() const
{
int fd = socket(PF_INET, SOCK_DGRAM, 0);
@ -400,15 +412,15 @@ bool CTapDriver::Disable() const
void CTapDriver::Flush()
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
while(PendingPackets()){
while (PendingPackets()) {
array<BYTE, ETH_FRAME_LEN> m_garbage_buffer;
(void)Rx(m_garbage_buffer.data());
(void)Receive(m_garbage_buffer.data());
}
}
void CTapDriver::GetMacAddr(BYTE *mac) const
{
ASSERT(mac);
assert(mac);
memcpy(mac, m_MacAddr.data(), m_MacAddr.size());
}
@ -420,11 +432,10 @@ void CTapDriver::GetMacAddr(BYTE *mac) const
//---------------------------------------------------------------------------
bool CTapDriver::PendingPackets() const
{
pollfd fds;
ASSERT(m_hTAP != -1);
assert(m_hTAP != -1);
// Check if there is data that can be received
pollfd fds;
fds.fd = m_hTAP;
fds.events = POLLIN | POLLERR;
fds.revents = 0;
@ -432,7 +443,7 @@ bool CTapDriver::PendingPackets() const
LOGTRACE("%s %u revents", __PRETTY_FUNCTION__, fds.revents)
if (!(fds.revents & POLLIN)) {
return false;
}else {
} else {
return true;
}
}
@ -450,14 +461,9 @@ uint32_t crc32(const BYTE *buf, int length) {
return ~crc;
}
//---------------------------------------------------------------------------
//
// Receive
//
//---------------------------------------------------------------------------
int CTapDriver::Rx(BYTE *buf)
int CTapDriver::Receive(BYTE *buf)
{
ASSERT(m_hTAP != -1);
assert(m_hTAP != -1);
// Check if there is data that can be received
if (!PendingPackets()) {
@ -490,7 +496,7 @@ int CTapDriver::Rx(BYTE *buf)
}
if (m_pcap_dumper != nullptr) {
struct pcap_pkthdr h = {
pcap_pkthdr h = {
.ts = {},
.caplen = dwReceived,
.len = dwReceived
@ -504,17 +510,12 @@ int CTapDriver::Rx(BYTE *buf)
return dwReceived;
}
//---------------------------------------------------------------------------
//
// Send
//
//---------------------------------------------------------------------------
int CTapDriver::Tx(const BYTE *buf, int len)
int CTapDriver::Send(const BYTE *buf, int len)
{
ASSERT(m_hTAP != -1);
assert(m_hTAP != -1);
if (m_pcap_dumper != nullptr) {
struct pcap_pkthdr h = {
pcap_pkthdr h = {
.ts = {},
.caplen = (bpf_u_int32)len,
.len = (bpf_u_int32)len,

View File

@ -7,30 +7,20 @@
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) akuker
//
// [ TAP Driver ]
//
//---------------------------------------------------------------------------
#pragma once
#include <pcap/pcap.h>
#include <net/ethernet.h>
#include "filepath.h"
#include <unordered_map>
#include <list>
#include <string>
#include <array>
#ifndef ETH_FRAME_LEN
#define ETH_FRAME_LEN 1514
#endif
using namespace std; //NOSONAR Not relevant for rascsi
using namespace std;
//===========================================================================
//
// Linux Tap Driver
//
//===========================================================================
class CTapDriver
{
friend class SCSIDaynaPort;
@ -39,7 +29,7 @@ class CTapDriver
static constexpr const char *BRIDGE_NAME = "rascsi_bridge";
CTapDriver() = default;
~CTapDriver() = default;
~CTapDriver();
CTapDriver(CTapDriver&) = delete;
CTapDriver& operator=(const CTapDriver&) = delete;
@ -47,20 +37,19 @@ class CTapDriver
public:
void OpenDump(const Filepath& path);
// Capture packets
void Cleanup(); // Cleanup
void GetMacAddr(BYTE *mac) const; // Get Mac Address
int Rx(BYTE *buf); // Receive
int Tx(const BYTE *buf, int len); // Send
void OpenDump(const Filepath& path); // Capture packets
void GetMacAddr(BYTE *mac) const;
int Receive(BYTE *buf);
int Send(const BYTE *buf, int len);
bool PendingPackets() const; // Check if there are IP packets available
bool Enable() const; // Enable the ras0 interface
bool Disable() const; // Disable the ras0 interface
void Flush(); // Purge all of the packets that are waiting to be processed
private:
array<byte, 6> m_MacAddr; // MAC Address
int m_hTAP = -1; // File handle
array<byte, 6> m_MacAddr; // MAC Address
int m_hTAP = -1; // File handle
pcap_t *m_pcap = nullptr;
pcap_dumper_t *m_pcap_dumper = nullptr;

View File

@ -9,10 +9,10 @@
#include <cassert>
#include "rascsi_version.h"
#include "os.h"
#include "log.h"
#include "rascsi_exceptions.h"
#include "device.h"
#include <sstream>
#include <iomanip>
using namespace std;
@ -132,6 +132,8 @@ void Device::Stop()
ready = false;
attn = false;
stopped = true;
status_code = 0;
}
bool Device::Eject(bool force)

View File

@ -13,7 +13,7 @@
#include <unordered_map>
#include <string>
using namespace std;
using namespace std; //NOSONAR Not relevant for rascsi
class Device
{
@ -97,8 +97,6 @@ public:
// Override for device specific initializations, to be called after all device properties have been set
virtual bool Init(const unordered_map<string, string>&) { return true; };
virtual bool Dispatch(scsi_defs::scsi_command) = 0;
const string& GetType() const { return type; }
bool IsReady() const { return ready; }

View File

@ -15,9 +15,12 @@
#include "scsi_host_bridge.h"
#include "scsi_daynaport.h"
#include "rascsi_exceptions.h"
#include "host_services.h"
#include "device_factory.h"
#include <ifaddrs.h>
#include "host_services.h"
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
using namespace std;
using namespace rascsi_interface;
@ -66,17 +69,11 @@ DeviceFactory::DeviceFactory()
extension_mapping["iso"] = SCCD;
}
DeviceFactory& DeviceFactory::instance()
void DeviceFactory::DeleteDevice(const PrimaryDevice& device) const
{
static DeviceFactory instance;
return instance;
}
void DeviceFactory::DeleteDevice(const PrimaryDevice *device) const
{
auto [begin, end] = devices.equal_range(device->GetId());
for (auto it = begin; it != end; ++it) {
if (it->second->GetLun() == device->GetLun()) {
auto [begin, end] = devices.equal_range(device.GetId());
for (auto& it = begin; it != end; ++it) {
if (it->second->GetLun() == device.GetLun()) {
devices.erase(it);
break;
@ -218,7 +215,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
break;
case SCHS:
device = make_unique<HostServices>(this);
device = make_unique<HostServices>(*this);
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
device->SetVendor("RaSCSI");
device->SetProduct("Host Services");
@ -248,21 +245,31 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
return d;
}
const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(PbDeviceType type) const
{
const auto& it = sector_sizes.find(type);
return it != sector_sizes.end() ? it->second : empty_set;
}
const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(const string& type) const
{
PbDeviceType t = UNDEFINED;
PbDeviceType_Parse(type, &t);
const auto it = sector_sizes.find(t);
assert (it != sector_sizes.end());
return GetSectorSizes(t);
}
return it->second;
const unordered_map<string, string>& DeviceFactory::GetDefaultParams(PbDeviceType type) const
{
const auto& it = default_params.find(type);
return it != default_params.end() ? it->second : empty_map;
}
list<string> DeviceFactory::GetNetworkInterfaces() const
{
list<string> network_interfaces;
#ifdef __linux
ifaddrs *addrs;
getifaddrs(&addrs);
ifaddrs *tmp = addrs;
@ -286,6 +293,7 @@ list<string> DeviceFactory::GetNetworkInterfaces() const
}
freeifaddrs(addrs);
#endif
return network_interfaces;
}

View File

@ -18,8 +18,8 @@
#include <string>
#include "rascsi_interface.pb.h"
using namespace std;
using namespace rascsi_interface;
using namespace std; //NOSONAR Not relevant for rascsi
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi
using Geometry = pair<uint32_t, uint32_t>;
@ -27,24 +27,22 @@ class PrimaryDevice;
class DeviceFactory
{
public:
DeviceFactory();
~DeviceFactory() = default;
DeviceFactory(DeviceFactory&) = delete;
DeviceFactory& operator=(const DeviceFactory&) = delete;
public:
static DeviceFactory& instance();
PrimaryDevice *CreateDevice(PbDeviceType, const string&, int);
void DeleteDevice(const PrimaryDevice *) const;
void DeleteDevice(const PrimaryDevice&) const;
void DeleteAllDevices() const;
const PrimaryDevice *GetDeviceByIdAndLun(int, int) const;
list<PrimaryDevice *> GetAllDevices() const;
PbDeviceType GetTypeForFile(const string&) const;
const unordered_set<uint32_t>& GetSectorSizes(PbDeviceType type) { return sector_sizes[type]; }
const unordered_set<uint32_t>& GetSectorSizes(PbDeviceType type) const;
const unordered_set<uint32_t>& GetSectorSizes(const string&) const;
const unordered_map<string, string>& GetDefaultParams(PbDeviceType type) { return default_params[type]; }
const unordered_map<string, string>& GetDefaultParams(PbDeviceType type) const;
list<string> GetNetworkInterfaces() const;
unordered_map<string, PbDeviceType> GetExtensionMapping() const { return extension_mapping; }
@ -62,4 +60,7 @@ private:
string GetExtension(const string&) const;
static std::multimap<int, unique_ptr<PrimaryDevice>> devices;
unordered_set<uint32_t> empty_set;
unordered_map<string, string> empty_map;
};

View File

@ -16,14 +16,16 @@
#include "os.h"
#include "fileio.h"
#include "device_factory.h"
#include "file_support.h"
#include "rascsi_exceptions.h"
#include "dispatcher.h"
#include "scsi_command_util.h"
#include "disk.h"
#include "mode_page_device.h"
using namespace scsi_defs;
using namespace scsi_command_util;
Disk::Disk(const string& id) : ModePageDevice(id), ScsiBlockCommands()
Disk::Disk(const string& id) : ModePageDevice(id)
{
dispatcher.Add(scsi_command::eCmdRezero, "Rezero", &Disk::Rezero);
dispatcher.Add(scsi_command::eCmdFormat, "FormatUnit", &Disk::FormatUnit);
@ -44,8 +46,8 @@ Disk::Disk(const string& id) : ModePageDevice(id), ScsiBlockCommands()
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::SynchronizeCache10);
dispatcher.Add(scsi_command::eCmdSynchronizeCache16, "SynchronizeCache16", &Disk::SynchronizeCache16);
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::eCmdReserve10, "Reserve10", &Disk::Reserve);
dispatcher.Add(scsi_command::eCmdRelease10, "Release10", &Disk::Release);
@ -126,7 +128,12 @@ void Disk::Rezero()
void Disk::FormatUnit()
{
Format(ctrl->cmd);
CheckReady();
// FMTDATA=1 is not supported (but OK if there is no DEFECT LIST)
if ((ctrl->cmd[1] & 0x10) != 0 && ctrl->cmd[4] != 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
EnterStatusPhase();
}
@ -138,8 +145,7 @@ void Disk::ReassignBlocks()
void Disk::Read(access_mode mode)
{
uint64_t start;
if (CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
ctrl->length = Read(ctrl->cmd, ctrl->buffer, start);
LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, ctrl->length)
@ -148,6 +154,9 @@ void Disk::Read(access_mode mode)
EnterDataInPhase();
}
else {
EnterStatusPhase();
}
}
void Disk::Read6()
@ -191,8 +200,7 @@ void Disk::ReadWriteLong16()
void Disk::Write(access_mode mode)
{
uint64_t start;
if (CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
ctrl->length = WriteCheck(start);
// Set next block
@ -200,6 +208,9 @@ void Disk::Write(access_mode mode)
EnterDataOutPhase();
}
else {
EnterStatusPhase();
}
}
void Disk::Write6()
@ -219,8 +230,7 @@ void Disk::Write16()
void Disk::Verify(access_mode mode)
{
uint64_t start;
if (CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
// if BytChk=0
if ((ctrl->cmd[1] & 0x02) == 0) {
Seek();
@ -235,6 +245,9 @@ void Disk::Verify(access_mode mode)
EnterDataOutPhase();
}
else {
EnterStatusPhase();
}
}
void Disk::Verify10()
@ -249,8 +262,33 @@ void Disk::Verify16()
void Disk::StartStopUnit()
{
if (!StartStop(ctrl->cmd)) {
throw scsi_error_exception();
bool start = ctrl->cmd[4] & 0x01;
bool load = ctrl->cmd[4] & 0x02;
if (load) {
LOGTRACE("%s", start ? "Loading medium" : "Ejecting medium")
}
else {
LOGTRACE("%s", start ? "Starting unit" : "Stopping unit")
SetStopped(!start);
}
if (!start) {
FlushCache();
// Look at the eject bit and eject if necessary
if (load) {
if (IsLocked()) {
// Cannot be ejected because it is locked
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
}
// Eject
if (!Eject(false)) {
throw scsi_error_exception();
}
}
}
EnterStatusPhase();
@ -258,7 +296,13 @@ void Disk::StartStopUnit()
void Disk::SendDiagnostic()
{
if (!SendDiag(ctrl->cmd)) {
// Do not support PF bit
if (ctrl->cmd[1] & 0x10) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// Do not support parameter list
if ((ctrl->cmd[3] != 0) || (ctrl->cmd[4] != 0)) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
@ -278,21 +322,16 @@ void Disk::PreventAllowMediumRemoval()
EnterStatusPhase();
}
void Disk::SynchronizeCache10()
void Disk::SynchronizeCache()
{
FlushCache();
EnterStatusPhase();
}
void Disk::SynchronizeCache16()
{
return SynchronizeCache10();
}
void Disk::ReadDefectData10()
{
int allocation_length = (ctrl->cmd[7] << 8) | ctrl->cmd[8];
int allocation_length = GetInt16(ctrl->cmd, 7);
if (allocation_length > 4) {
allocation_length = 4;
}
@ -315,7 +354,7 @@ void Disk::MediumChanged()
bool Disk::Eject(bool force)
{
bool status = Device::Eject(force);
bool status = super::Eject(force);
if (status) {
FlushCache();
delete disk.dcache;
@ -323,8 +362,7 @@ bool Disk::Eject(bool force)
// The image file for this drive is not in use anymore
// TODO This cast and the FileSupport class can be removed as soon as only disk-like devices inherit from Disk
auto file_support = dynamic_cast<FileSupport *>(this);
if (file_support) {
if (auto file_support = dynamic_cast<FileSupport *>(this); file_support) {
file_support->UnreserveFile();
}
}
@ -341,6 +379,11 @@ int Disk::ModeSense6(const vector<int>& cdb, BYTE *buf, int max_length) const
}
memset(buf, 0, length);
// DEVICE SPECIFIC PARAMETER
if (IsProtected()) {
buf[2] = 0x80;
}
// Basic information
int size = 4;
@ -352,18 +395,8 @@ int Disk::ModeSense6(const vector<int>& cdb, BYTE *buf, int max_length) const
// Only if ready
if (IsReady()) {
// Short LBA mode parameter block descriptor (number of blocks and block length)
uint64_t disk_blocks = GetBlockCount();
buf[4] = (BYTE)(disk_blocks >> 24);
buf[5] = (BYTE)(disk_blocks >> 16);
buf[6] = (BYTE)(disk_blocks >> 8);
buf[7] = (BYTE)disk_blocks;
// Block descriptor (block length)
uint32_t disk_size = GetSectorSizeInBytes();
buf[9] = (BYTE)(disk_size >> 16);
buf[10] = (BYTE)(disk_size >> 8);
buf[11] = (BYTE)disk_size;
SetInt32(&buf[4], (uint32_t)GetBlockCount());
SetInt32(&buf[8], GetSectorSizeInBytes());
}
size = 12;
@ -388,12 +421,17 @@ int Disk::ModeSense6(const vector<int>& cdb, BYTE *buf, int max_length) const
int Disk::ModeSense10(const vector<int>& cdb, BYTE *buf, int max_length) const
{
// Get length, clear buffer
int length = (cdb[7] << 8) | cdb[8];
int length = GetInt16(cdb, 7);
if (length > max_length) {
length = max_length;
}
memset(buf, 0, length);
// DEVICE SPECIFIC PARAMETER
if (IsProtected()) {
buf[3] = 0x80;
}
// Basic Information
int size = 8;
@ -408,15 +446,8 @@ int Disk::ModeSense10(const vector<int>& cdb, BYTE *buf, int max_length) const
buf[7] = 0x08;
// Short LBA mode parameter block descriptor (number of blocks and block length)
buf[8] = (BYTE)(disk_blocks >> 24);
buf[9] = (BYTE)(disk_blocks >> 16);
buf[10] = (BYTE)(disk_blocks >> 8);
buf[11] = (BYTE)disk_blocks;
buf[13] = (BYTE)(disk_size >> 16);
buf[14] = (BYTE)(disk_size >> 8);
buf[15] = (BYTE)disk_size;
SetInt32(&buf[8], (uint32_t)disk_blocks);
SetInt32(&buf[12], disk_size);
size = 16;
}
@ -428,20 +459,8 @@ int Disk::ModeSense10(const vector<int>& cdb, BYTE *buf, int max_length) const
buf[7] = 0x10;
// Long LBA mode parameter block descriptor (number of blocks and block length)
buf[8] = (BYTE)(disk_blocks >> 56);
buf[9] = (BYTE)(disk_blocks >> 48);
buf[10] = (BYTE)(disk_blocks >> 40);
buf[11] = (BYTE)(disk_blocks >> 32);
buf[12] = (BYTE)(disk_blocks >> 24);
buf[13] = (BYTE)(disk_blocks >> 16);
buf[14] = (BYTE)(disk_blocks >> 8);
buf[15] = (BYTE)disk_blocks;
buf[20] = (BYTE)(disk_size >> 24);
buf[21] = (BYTE)(disk_size >> 16);
buf[22] = (BYTE)(disk_size >> 8);
buf[23] = (BYTE)disk_size;
SetInt64(&buf[8], disk_blocks);
SetInt32(&buf[20], disk_size);
size = 24;
}
@ -458,21 +477,12 @@ int Disk::ModeSense10(const vector<int>& cdb, BYTE *buf, int max_length) const
}
// Final setting of mode data length
buf[0] = (BYTE)(size >> 8);
buf[1] = (BYTE)size;
SetInt16(buf, size);
return size;
}
void Disk::SetDeviceParameters(BYTE *buf) const
{
// DEVICE SPECIFIC PARAMETER
if (IsProtected()) {
buf[3] = 0x80;
}
}
void Disk::AddModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
void Disk::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
{
// Page code 1 (read-write error recovery)
if (page == 0x01 || page == 0x3f) {
@ -523,25 +533,19 @@ void Disk::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) const
buf[0x03] = (byte)0x08;
// Set sector/track to 25
buf[0x0a] = (byte)0x00;
buf[0x0b] = (byte)0x19;
SetInt16(buf, 0x0a, 25);
// Set the number of bytes in the physical sector
int size = 1 << disk.size;
buf[0x0c] = (byte)(size >> 8);
buf[0x0d] = (byte)size;
SetInt16(buf, 0x0c, 1 << disk.size);
// Interleave 1
buf[0x0e] = (byte)0x00;
buf[0x0f] = (byte)0x01;
SetInt16(buf, 0x0e, 1);
// Track skew factor 11
buf[0x10] = (byte)0x00;
buf[0x11] = (byte)0x0b;
SetInt16(buf, 0x10, 11);
// Cylinder skew factor 20
buf[0x12] = (byte)0x00;
buf[0x13] = (byte)0x14;
SetInt16(buf, 0x12, 20);
}
buf[20] = IsRemovable() ? (byte)0x20 : (byte)0x00;
@ -569,16 +573,13 @@ void Disk::AddDrivePage(map<int, vector<byte>>& pages, bool changeable) const
uint64_t cylinders = disk.blocks;
cylinders >>= 3;
cylinders /= 25;
buf[0x02] = (byte)(cylinders >> 16);
buf[0x03] = (byte)(cylinders >> 8);
buf[0x04] = (byte)cylinders;
SetInt32(buf, 0x01, (uint32_t)cylinders);
// Fix the head at 8
buf[0x05] = (byte)0x8;
// Medium rotation rate 7200
buf[0x14] = (byte)0x1c;
buf[0x15] = (byte)0x20;
SetInt16(buf, 0x14, 7200);
}
pages[4] = buf;
@ -598,16 +599,13 @@ void Disk::AddCachePage(map<int, vector<byte>>& pages, bool changeable) const
// Only read cache is valid
// Disable pre-fetch transfer length
buf[0x04] = (byte)0xff;
buf[0x05] = (byte)0xff;
SetInt16(buf, 0x04, -1);
// Maximum pre-fetch
buf[0x08] = (byte)0xff;
buf[0x09] = (byte)0xff;
SetInt16(buf, 0x08, -1);
// Maximum pre-fetch ceiling
buf[0x0a] = (byte)0xff;
buf[0x0b] = (byte)0xff;
SetInt16(buf, 0x0a, -1);
pages[8] = buf;
}
@ -617,16 +615,6 @@ void Disk::AddVendorPage(map<int, vector<byte>>&, int, bool) const
// Nothing to add by default
}
void Disk::Format(const vector<int>& cdb)
{
CheckReady();
// FMTDATA=1 is not supported (but OK if there is no DEFECT LIST)
if ((cdb[1] & 0x10) != 0 && cdb[4] != 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
}
// TODO Read more than one block in a single call. Currently blocked by the the track-oriented cache
int Disk::Read(const vector<int>&, BYTE *buf, uint64_t block)
{
@ -701,72 +689,27 @@ void Disk::Seek()
void Disk::Seek6()
{
uint64_t start;
if (CheckAndGetStartAndCount(start, ctrl->blocks, SEEK6)) {
Seek();
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, SEEK6)) {
CheckReady();
}
EnterStatusPhase();
}
void Disk::Seek10()
{
uint64_t start;
if (CheckAndGetStartAndCount(start, ctrl->blocks, SEEK10)) {
Seek();
}
}
bool Disk::StartStop(const vector<int>& cdb)
{
bool start = cdb[4] & 0x01;
bool load = cdb[4] & 0x02;
if (load) {
LOGTRACE("%s", start ? "Loading medium" : "Ejecting medium")
}
else {
LOGTRACE("%s", start ? "Starting unit" : "Stopping unit")
SetStopped(!start);
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, SEEK10)) {
CheckReady();
}
if (!start) {
FlushCache();
// Look at the eject bit and eject if necessary
if (load) {
if (IsLocked()) {
// Cannot be ejected because it is locked
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
}
// Eject
return Eject(false);
}
}
return true;
}
bool Disk::SendDiag(const vector<int>& cdb) const
{
// Do not support PF bit
if (cdb[1] & 0x10) {
return false;
}
// Do not support parameter list
if ((cdb[3] != 0) || (cdb[4] != 0)) {
return false;
}
return true;
EnterStatusPhase();
}
void Disk::ReadCapacity10()
{
CheckReady();
if (disk.blocks <= 0) {
if (disk.blocks == 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
}
@ -779,17 +722,10 @@ void Disk::ReadCapacity10()
if (blocks > 4294967295) {
blocks = -1;
}
buf[0] = (BYTE)(blocks >> 24);
buf[1] = (BYTE)(blocks >> 16);
buf[2] = (BYTE)(blocks >> 8);
buf[3] = (BYTE)blocks;
SetInt32(&buf[0], (uint32_t)blocks);
// Create block length (1 << disk.size)
uint32_t length = 1 << disk.size;
buf[4] = (BYTE)(length >> 24);
buf[5] = (BYTE)(length >> 16);
buf[6] = (BYTE)(length >> 8);
buf[7] = (BYTE)length;
SetInt32(&buf[4], 1 << disk.size);
// the size
ctrl->length = 8;
@ -801,29 +737,17 @@ void Disk::ReadCapacity16()
{
CheckReady();
if (disk.blocks <= 0) {
if (disk.blocks == 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
}
BYTE *buf = ctrl->buffer;
// Create end of logical block address (disk.blocks-1)
uint64_t blocks = disk.blocks - 1;
buf[0] = (BYTE)(blocks >> 56);
buf[1] = (BYTE)(blocks >> 48);
buf[2] = (BYTE)(blocks >> 40);
buf[3] = (BYTE)(blocks >> 32);
buf[4] = (BYTE)(blocks >> 24);
buf[5] = (BYTE)(blocks >> 16);
buf[6] = (BYTE)(blocks >> 8);
buf[7] = (BYTE)blocks;
SetInt64(&buf[0], disk.blocks - 1);
// Create block length (1 << disk.size)
uint32_t length = 1 << disk.size;
buf[8] = (BYTE)(length >> 24);
buf[9] = (BYTE)(length >> 16);
buf[10] = (BYTE)(length >> 8);
buf[11] = (BYTE)length;
SetInt32(&buf[8], 1 << disk.size);
buf[12] = 0;
@ -882,34 +806,18 @@ void Disk::Release()
void Disk::ValidateBlockAddress(access_mode mode) const
{
uint64_t block = ctrl->cmd[2];
block <<= 8;
block |= ctrl->cmd[3];
block <<= 8;
block |= ctrl->cmd[4];
block <<= 8;
block |= ctrl->cmd[5];
if (mode == RW16) {
block <<= 8;
block |= ctrl->cmd[6];
block <<= 8;
block |= ctrl->cmd[7];
block <<= 8;
block |= ctrl->cmd[8];
block <<= 8;
block |= ctrl->cmd[9];
}
uint64_t block = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
uint64_t capacity = GetBlockCount();
if (block > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " blocks exceeded: Trying to access block "
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ to_string(block)).c_str())
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
}
bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mode mode)
bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mode mode) const
{
if (mode == RW6 || mode == SEEK6) {
start = ctrl->cmd[1] & 0x1f;
@ -924,38 +832,13 @@ bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mod
}
}
else {
start = ctrl->cmd[2];
start <<= 8;
start |= ctrl->cmd[3];
start <<= 8;
start |= ctrl->cmd[4];
start <<= 8;
start |= ctrl->cmd[5];
start = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
if (mode == RW16) {
start <<= 8;
start |= ctrl->cmd[6];
start <<= 8;
start |= ctrl->cmd[7];
start <<= 8;
start |= ctrl->cmd[8];
start <<= 8;
start |= ctrl->cmd[9];
}
if (mode == RW16) {
count = ctrl->cmd[10];
count <<= 8;
count |= ctrl->cmd[11];
count <<= 8;
count |= ctrl->cmd[12];
count <<= 8;
count |= ctrl->cmd[13];
count = GetInt32(ctrl->cmd, 10);
}
else if (mode != SEEK6 && mode != SEEK10) {
count = ctrl->cmd[7];
count <<= 8;
count |= ctrl->cmd[8];
count = GetInt16(ctrl->cmd, 7);
}
else {
count = 0;
@ -966,14 +849,13 @@ bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mod
// Check capacity
if (uint64_t capacity = GetBlockCount(); start > capacity || start + count > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " blocks exceeded: Trying to access block "
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ to_string(start) + ", block count " + to_string(count)).c_str())
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
// Do not process 0 blocks
if (!count && (mode != SEEK6 && mode != SEEK10)) {
EnterStatusPhase();
return false;
}
@ -987,7 +869,8 @@ uint32_t Disk::GetSectorSizeInBytes() const
void Disk::SetSectorSizeInBytes(uint32_t size_in_bytes)
{
if (unordered_set<uint32_t> sizes = DeviceFactory::instance().GetSectorSizes(GetType());
DeviceFactory device_factory;
if (unordered_set<uint32_t> sizes = device_factory.GetSectorSizes(GetType());
!sizes.empty() && sizes.find(size_in_bytes) == sizes.end()) {
throw io_exception("Invalid block size of " + to_string(size_in_bytes) + " bytes");
}
@ -1040,10 +923,8 @@ uint32_t Disk::GetConfiguredSectorSize() const
return configured_sector_size;
}
bool Disk::SetConfiguredSectorSize(uint32_t size)
bool Disk::SetConfiguredSectorSize(const DeviceFactory& device_factory, uint32_t size)
{
const DeviceFactory& device_factory = DeviceFactory::instance();
if (unordered_set<uint32_t> sizes = device_factory.GetSectorSizes(GetType());
sizes.find(size) == sizes.end()) {
return false;

View File

@ -11,31 +11,22 @@
// Imported sava's Anex86/T98Next image and MO format support patch.
// Comments translated to english by akuker.
//
// [ Disk ]
//
//---------------------------------------------------------------------------
#pragma once
#include "log.h"
#include "scsi.h"
#include "device.h"
#include "device_factory.h"
#include "disk_track_cache.h"
#include "file_support.h"
#include "disk_track.h"
#include "disk_cache.h"
#include "filepath.h"
#include "interfaces/scsi_block_commands.h"
#include "mode_page_device.h"
#include <string>
#include <unordered_set>
#include <unordered_map>
using namespace std;
class Disk : public ModePageDevice, public ScsiBlockCommands
{
private:
enum access_mode { RW6, RW10, RW16, SEEK6, SEEK10 };
// The supported configurable block sizes, empty if not configurable
@ -72,7 +63,7 @@ public:
uint32_t GetSectorSizeInBytes() const;
bool IsSectorSizeConfigurable() const;
bool SetConfiguredSectorSize(uint32_t);
bool SetConfiguredSectorSize(const DeviceFactory&, uint32_t);
uint64_t GetBlockCount() const;
void FlushCache() override;
@ -84,8 +75,7 @@ private:
void StartStopUnit();
void SendDiagnostic();
void PreventAllowMediumRemoval();
void SynchronizeCache10();
void SynchronizeCache16();
void SynchronizeCache();
void ReadDefectData10();
virtual void Read6();
void Read10() override;
@ -111,12 +101,9 @@ private:
void ReadWriteLong10();
void ReadWriteLong16();
void ReadCapacity16_ReadLong16();
void Format(const vector<int>&);
bool SendDiag(const vector<int>&) const;
bool StartStop(const vector<int>&);
void ValidateBlockAddress(access_mode) const;
bool CheckAndGetStartAndCount(uint64_t&, uint32_t&, access_mode);
bool CheckAndGetStartAndCount(uint64_t&, uint32_t&, access_mode) const;
int ModeSense6(const vector<int>&, BYTE *, int) const override;
int ModeSense10(const vector<int>&, BYTE *, int) const override;
@ -125,8 +112,7 @@ protected:
virtual void Open(const Filepath&);
virtual void SetDeviceParameters(BYTE *) const;
void AddModePages(map<int, vector<byte>>&, int, bool) const override;
void SetUpModePages(map<int, vector<byte>>&, int, bool) const override;
virtual void AddErrorPage(map<int, vector<byte>>&, bool) const;
virtual void AddFormatPage(map<int, vector<byte>>&, bool) const;
virtual void AddDrivePage(map<int, vector<byte>>&, bool) const;

View File

@ -0,0 +1,249 @@
//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
//
// XM6i
// Copyright (C) 2010-2015 isaki@NetBSD.org
// Copyright (C) 2010 Y.Sugahara
//
// Imported sava's Anex86/T98Next image and MO format support patch.
// Comments translated to english by akuker.
//
//---------------------------------------------------------------------------
#include "log.h"
#include "disk_track.h"
#include "disk_cache.h"
DiskCache::DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff)
: sec_size(size), sec_blocks(blocks), imgoffset(imgoff)
{
assert(blocks > 0);
assert(imgoff >= 0);
sec_path = path;
}
DiskCache::~DiskCache()
{
// Clear the track
Clear();
}
void DiskCache::SetRawMode(bool raw)
{
// Configuration
cd_raw = raw;
}
bool DiskCache::Save() const
{
// Save track
for (const cache_t& c : cache) {
// Save if this is a valid track
if (c.disktrk && !c.disktrk->Save(sec_path)) {
return false;
}
}
return true;
}
//---------------------------------------------------------------------------
//
// Get disk cache information
//
//---------------------------------------------------------------------------
bool DiskCache::GetCache(int index, int& track, uint32_t& aserial) const
{
assert((index >= 0) && (index < CACHE_MAX));
// false if unused
if (!cache[index].disktrk) {
return false;
}
// Set track and serial
track = cache[index].disktrk->GetTrack();
aserial = cache[index].serial;
return true;
}
void DiskCache::Clear()
{
// Free the cache
for (cache_t& c : cache) {
if (c.disktrk) {
delete c.disktrk;
c.disktrk = nullptr;
}
}
}
bool DiskCache::ReadSector(BYTE *buf, uint32_t block)
{
assert(sec_size != 0);
// Update first
UpdateSerialNumber();
// Calculate track (fixed to 256 sectors/track)
int track = block >> 8;
// Get the track data
const DiskTrack *disktrk = Assign(track);
if (disktrk == nullptr) {
return false;
}
// Read the track data to the cache
return disktrk->ReadSector(buf, block & 0xff);
}
bool DiskCache::WriteSector(const BYTE *buf, uint32_t block)
{
assert(sec_size != 0);
// Update first
UpdateSerialNumber();
// Calculate track (fixed to 256 sectors/track)
int track = block >> 8;
// Get that track data
DiskTrack *disktrk = Assign(track);
if (disktrk == nullptr) {
return false;
}
// Write the data to the cache
return disktrk->WriteSector(buf, block & 0xff);
}
//---------------------------------------------------------------------------
//
// Track Assignment
//
//---------------------------------------------------------------------------
DiskTrack* DiskCache::Assign(int track)
{
assert(sec_size != 0);
assert(track >= 0);
// First, check if it is already assigned
for (cache_t& c : cache) {
if (c.disktrk && c.disktrk->GetTrack() == track) {
// Track match
c.serial = serial;
return c.disktrk;
}
}
// Next, check for empty
for (int i = 0; i < CACHE_MAX; i++) {
if (!cache[i].disktrk) {
// Try loading
if (Load(i, track)) {
// Success loading
cache[i].serial = serial;
return cache[i].disktrk;
}
// Load failed
return nullptr;
}
}
// Finally, find the youngest serial number and delete it
// Set index 0 as candidate c
uint32_t s = cache[0].serial;
int c = 0;
// Compare candidate with serial and update to smaller one
for (int i = 0; i < CACHE_MAX; i++) {
assert(cache[i].disktrk);
// Compare and update the existing serial
if (cache[i].serial < s) {
s = cache[i].serial;
c = i;
}
}
// Save this track
if (!cache[c].disktrk->Save(sec_path)) {
return nullptr;
}
// Delete this track
DiskTrack *disktrk = cache[c].disktrk;
cache[c].disktrk = nullptr;
if (Load(c, track, disktrk)) {
// Successful loading
cache[c].serial = serial;
return cache[c].disktrk;
}
// Load failed
return nullptr;
}
//---------------------------------------------------------------------------
//
// Load cache
//
//---------------------------------------------------------------------------
bool DiskCache::Load(int index, int track, DiskTrack *disktrk)
{
assert((index >= 0) && (index < CACHE_MAX));
assert(track >= 0);
assert(!cache[index].disktrk);
// Get the number of sectors on this track
int sectors = sec_blocks - (track << 8);
assert(sectors > 0);
if (sectors > 0x100) {
sectors = 0x100;
}
// Create a disk track
if (disktrk == nullptr) {
disktrk = new DiskTrack();
}
// Initialize disk track
disktrk->Init(track, sec_size, sectors, cd_raw, imgoffset);
// Try loading
if (!disktrk->Load(sec_path)) {
// Failure
delete disktrk;
return false;
}
// Allocation successful, work set
cache[index].disktrk = disktrk;
return true;
}
void DiskCache::UpdateSerialNumber()
{
// Update and do nothing except 0
serial++;
if (serial != 0) {
return;
}
// Clear serial of all caches
for (cache_t& c : cache) {
c.serial = 0;
}
}

View File

@ -11,8 +11,6 @@
// Imported sava's Anex86/T98Next image and MO format support patch.
// Comments translated to english by akuker.
//
// [ DiskTrack and DiskCache ]
//
//---------------------------------------------------------------------------
#pragma once
@ -22,51 +20,14 @@
// Number of tracks to cache
static const int CACHE_MAX = 16;
class DiskTrack
{
private:
struct {
int track; // Track Number
int size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096)
int sectors; // Number of sectors(<0x100)
DWORD length; // Data buffer length
BYTE *buffer; // Data buffer
bool init; // Is it initilized?
bool changed; // Changed flag
DWORD maplen; // Changed map length
bool *changemap; // Changed map
bool raw; // RAW mode flag
off_t imgoffset; // Offset to actual data
} dt = {};
public:
DiskTrack() = default;
~DiskTrack();
DiskTrack(DiskTrack&) = delete;
DiskTrack& operator=(const DiskTrack&) = delete;
private:
friend class DiskCache;
void Init(int track, int size, int sectors, bool raw = false, off_t imgoff = 0);
bool Load(const Filepath& path);
bool Save(const Filepath& path);
// Read / Write
bool ReadSector(BYTE *buf, int sec) const; // Sector Read
bool WriteSector(const BYTE *buf, int sec); // Sector Write
int GetTrack() const { return dt.track; } // Get track
};
class DiskCache
{
public:
// Internal data definition
using cache_t = struct {
DiskTrack *disktrk; // Disk Track
DWORD serial; // Serial
DiskTrack *disktrk; // Disk Track
uint32_t serial; // Serial
};
DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff = 0);
@ -80,9 +41,10 @@ public:
bool Save() const; // Save and release all
bool ReadSector(BYTE *buf, uint32_t block); // Sector Read
bool WriteSector(const BYTE *buf, uint32_t block); // Sector Write
bool GetCache(int index, int& track, DWORD& serial) const; // Get cache information
bool GetCache(int index, int& track, uint32_t& serial) const; // Get cache information
private:
// Internal Management
void Clear(); // Clear all tracks
DiskTrack* Assign(int track); // Load track
@ -91,7 +53,7 @@ private:
// Internal data
cache_t cache[CACHE_MAX] = {}; // Cache management
DWORD serial = 0; // Last serial number
uint32_t serial = 0; // Last serial number
Filepath sec_path; // Path
int sec_size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096)
int sec_blocks; // Blocks per sector

View File

@ -12,20 +12,11 @@
// Imported sava's Anex86/T98Next image and MO format support patch.
// Comments translated to english by akuker.
//
// [ DiskTrack and DiskCache ]
//
//---------------------------------------------------------------------------
#include "os.h"
#include "log.h"
#include "fileio.h"
#include "disk_track_cache.h"
//===========================================================================
//
// Disk Track
//
//===========================================================================
#include "disk_track.h"
DiskTrack::~DiskTrack()
{
@ -260,6 +251,7 @@ bool DiskTrack::ReadSector(BYTE *buf, int sec) const
assert((sec >= 0) && (sec < 0x100));
LOGTRACE("%s reading sector: %d", __PRETTY_FUNCTION__,sec)
// Error if not initialized
if (!dt.init) {
return false;
@ -314,239 +306,3 @@ bool DiskTrack::WriteSector(const BYTE *buf, int sec)
// Success
return true;
}
//===========================================================================
//
// Disk Cache
//
//===========================================================================
DiskCache::DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff)
: sec_size(size), sec_blocks(blocks), imgoffset(imgoff)
{
assert(blocks > 0);
assert(imgoff >= 0);
sec_path = path;
}
DiskCache::~DiskCache()
{
// Clear the track
Clear();
}
void DiskCache::SetRawMode(bool raw)
{
// Configuration
cd_raw = raw;
}
bool DiskCache::Save() const
{
// Save track
for (cache_t c : cache) {
// Save if this is a valid track
if (c.disktrk && !c.disktrk->Save(sec_path)) {
return false;
}
}
return true;
}
//---------------------------------------------------------------------------
//
// Get disk cache information
//
//---------------------------------------------------------------------------
bool DiskCache::GetCache(int index, int& track, DWORD& aserial) const
{
assert((index >= 0) && (index < CACHE_MAX));
// false if unused
if (!cache[index].disktrk) {
return false;
}
// Set track and serial
track = cache[index].disktrk->GetTrack();
aserial = cache[index].serial;
return true;
}
void DiskCache::Clear()
{
// Free the cache
for (cache_t& c : cache) {
if (c.disktrk) {
delete c.disktrk;
c.disktrk = nullptr;
}
}
}
bool DiskCache::ReadSector(BYTE *buf, uint32_t block)
{
assert(sec_size != 0);
// Update first
UpdateSerialNumber();
// Calculate track (fixed to 256 sectors/track)
int track = block >> 8;
// Get the track data
const DiskTrack *disktrk = Assign(track);
if (disktrk == nullptr) {
return false;
}
// Read the track data to the cache
return disktrk->ReadSector(buf, block & 0xff);
}
bool DiskCache::WriteSector(const BYTE *buf, uint32_t block)
{
assert(sec_size != 0);
// Update first
UpdateSerialNumber();
// Calculate track (fixed to 256 sectors/track)
int track = block >> 8;
// Get that track data
DiskTrack *disktrk = Assign(track);
if (disktrk == nullptr) {
return false;
}
// Write the data to the cache
return disktrk->WriteSector(buf, block & 0xff);
}
//---------------------------------------------------------------------------
//
// Track Assignment
//
//---------------------------------------------------------------------------
DiskTrack* DiskCache::Assign(int track)
{
assert(sec_size != 0);
assert(track >= 0);
// First, check if it is already assigned
for (cache_t c : cache) {
if (c.disktrk && c.disktrk->GetTrack() == track) {
// Track match
c.serial = serial;
return c.disktrk;
}
}
// Next, check for empty
for (int i = 0; i < CACHE_MAX; i++) {
if (!cache[i].disktrk) {
// Try loading
if (Load(i, track)) {
// Success loading
cache[i].serial = serial;
return cache[i].disktrk;
}
// Load failed
return nullptr;
}
}
// Finally, find the youngest serial number and delete it
// Set index 0 as candidate c
DWORD s = cache[0].serial;
int c = 0;
// Compare candidate with serial and update to smaller one
for (int i = 0; i < CACHE_MAX; i++) {
assert(cache[i].disktrk);
// Compare and update the existing serial
if (cache[i].serial < s) {
s = cache[i].serial;
c = i;
}
}
// Save this track
if (!cache[c].disktrk->Save(sec_path)) {
return nullptr;
}
// Delete this track
DiskTrack *disktrk = cache[c].disktrk;
cache[c].disktrk = nullptr;
if (Load(c, track, disktrk)) {
// Successful loading
cache[c].serial = serial;
return cache[c].disktrk;
}
// Load failed
return nullptr;
}
//---------------------------------------------------------------------------
//
// Load cache
//
//---------------------------------------------------------------------------
bool DiskCache::Load(int index, int track, DiskTrack *disktrk)
{
assert((index >= 0) && (index < CACHE_MAX));
assert(track >= 0);
assert(!cache[index].disktrk);
// Get the number of sectors on this track
int sectors = sec_blocks - (track << 8);
assert(sectors > 0);
if (sectors > 0x100) {
sectors = 0x100;
}
// Create a disk track
if (disktrk == nullptr) {
disktrk = new DiskTrack();
}
// Initialize disk track
disktrk->Init(track, sec_size, sectors, cd_raw, imgoffset);
// Try loading
if (!disktrk->Load(sec_path)) {
// Failure
delete disktrk;
return false;
}
// Allocation successful, work set
cache[index].disktrk = disktrk;
return true;
}
void DiskCache::UpdateSerialNumber()
{
// Update and do nothing except 0
serial++;
if (serial != 0) {
return;
}
// Clear serial of all caches
for (cache_t& c : cache) {
c.serial = 0;
}
}

View File

@ -0,0 +1,56 @@
//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
//
// XM6i
// Copyright (C) 2010-2015 isaki@NetBSD.org
//
// Imported sava's Anex86/T98Next image and MO format support patch.
// Comments translated to english by akuker.
//
//---------------------------------------------------------------------------
#pragma once
#include "filepath.h"
class DiskTrack
{
struct {
int track; // Track Number
int size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096)
int sectors; // Number of sectors(<0x100)
DWORD length; // Data buffer length
BYTE *buffer; // Data buffer
bool init; // Is it initilized?
bool changed; // Changed flag
DWORD maplen; // Changed map length
bool *changemap; // Changed map
bool raw; // RAW mode flag
off_t imgoffset; // Offset to actual data
} dt = {};
public:
DiskTrack() = default;
~DiskTrack();
DiskTrack(DiskTrack&) = delete;
DiskTrack& operator=(const DiskTrack&) = delete;
private:
friend class DiskCache;
void Init(int track, int size, int sectors, bool raw = false, off_t imgoff = 0);
bool Load(const Filepath& path);
bool Save(const Filepath& path);
// Read / Write
bool ReadSector(BYTE *buf, int sec) const; // Sector Read
bool WriteSector(const BYTE *buf, int sec); // Sector Write
int GetTrack() const { return dt.track; } // Get track
};

View File

@ -12,11 +12,10 @@
#pragma once
#include "log.h"
#include "scsi.h"
#include <unordered_map>
using namespace std;
using namespace scsi_defs;
using namespace std; //NOSONAR Not relevant for rascsi
using namespace scsi_defs; //NOSONAR Not relevant for rascsi
template<class T>
class Dispatcher

View File

@ -15,7 +15,7 @@
#include <string>
#include "filepath.h"
using namespace std;
using namespace std; //NOSONAR Not relevant for rascsi
using id_set = pair<int, int>;

View File

@ -21,13 +21,15 @@
//
#include "rascsi_exceptions.h"
#include "device.h"
#include "device_factory.h"
#include "scsi_command_util.h"
#include "dispatcher.h"
#include "host_services.h"
using namespace scsi_defs;
using namespace scsi_command_util;
HostServices::HostServices(const DeviceFactory *factory) : ModePageDevice("SCHS"), device_factory(factory)
HostServices::HostServices(const DeviceFactory& factory) : ModePageDevice("SCHS"), device_factory(factory)
{
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &HostServices::TestUnitReady);
dispatcher.Add(scsi_command::eCmdStartStop, "StartStopUnit", &HostServices::StartStopUnit);
@ -57,7 +59,7 @@ void HostServices::StartStopUnit()
if (!start) {
// Flush any caches
for (Device *device : device_factory->GetAllDevices()) {
for (PrimaryDevice *device : device_factory.GetAllDevices()) {
device->FlushCache();
}
@ -140,13 +142,12 @@ int HostServices::ModeSense10(const vector<int>& cdb, BYTE *buf, int max_length)
size = length;
}
buf[0] = (BYTE)(size >> 8);
buf[1] = (BYTE)size;
SetInt16(buf, size);
return size;
}
void HostServices::AddModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
void HostServices::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
{
if (page == 0x20 || page == 0x3f) {
AddRealtimeClockPage(pages, changeable);
@ -161,6 +162,8 @@ void HostServices::AddRealtimeClockPage(map<int, vector<byte>>& pages, bool chan
localtime_r(&t, &localtime);
mode_page_datetime datetime;
datetime.major_version = 0x01;
datetime.minor_version = 0x00;
datetime.year = (uint8_t)localtime.tm_year;
datetime.month = (uint8_t)localtime.tm_mon;
datetime.day = (uint8_t)localtime.tm_mday;

View File

@ -8,14 +8,13 @@
// Host Services with realtime clock and shutdown support
//
//---------------------------------------------------------------------------
#pragma once
#include "mode_page_device.h"
#include <vector>
#include <map>
using namespace std;
class DeviceFactory;
class HostServices: public ModePageDevice
@ -23,7 +22,7 @@ class HostServices: public ModePageDevice
public:
explicit HostServices(const DeviceFactory *);
explicit HostServices(const DeviceFactory&);
~HostServices() override = default;
HostServices(HostServices&) = delete;
HostServices& operator=(const HostServices&) = delete;
@ -38,14 +37,14 @@ public:
protected:
void AddModePages(map<int, vector<byte>>&, int, bool) const override;
void SetUpModePages(map<int, vector<byte>>&, int, bool) const override;
private:
using mode_page_datetime = struct __attribute__((packed)) {
// Major and minor version of this data structure (1.0)
uint8_t major_version = 0x01;
uint8_t minor_version = 0x00;
// Major and minor version of this data structure (e.g. 1.0)
uint8_t major_version;
uint8_t minor_version;
// Current date and time, with daylight savings time adjustment applied
uint8_t year; // year - 1900
uint8_t month; // 0-11
@ -64,5 +63,5 @@ private:
void AddRealtimeClockPage(map<int, vector<byte>>&, bool) const;
const DeviceFactory *device_factory;
const DeviceFactory& device_factory;
};

View File

@ -11,7 +11,9 @@
#include "log.h"
#include "rascsi_exceptions.h"
#include "dispatcher.h"
#include "mode_page_device.h"
#include <cstddef>
using namespace std;
using namespace scsi_defs;
@ -45,7 +47,7 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, BYTE *buf, int max_leng
// Mode page data mapped to the respective page numbers, C++ maps are ordered by key
map<int, vector<byte>> pages;
AddModePages(pages, page, changeable);
SetUpModePages(pages, page, changeable);
if (pages.empty()) {
LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, page)
@ -104,7 +106,7 @@ void ModePageDevice::ModeSense10()
EnterDataInPhase();
}
void ModePageDevice::ModeSelect(const vector<int>&, const BYTE *, int)
void ModePageDevice::ModeSelect(const vector<int>&, const BYTE *, int) const
{
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
}

View File

@ -14,8 +14,6 @@
#include <vector>
#include <map>
using namespace std;
class ModePageDevice: public PrimaryDevice
{
public:
@ -27,12 +25,12 @@ public:
bool Dispatch(scsi_command) override;
virtual void ModeSelect(const vector<int>&, const BYTE *, int);
virtual void ModeSelect(const vector<int>&, const BYTE *, int) const;
protected:
int AddModePages(const vector<int>&, BYTE *, int) const;
virtual void AddModePages(map<int, vector<byte>>&, int, bool) const = 0;
virtual void SetUpModePages(map<int, vector<byte>>&, int, bool) const = 0;
private:

View File

@ -15,7 +15,7 @@
using namespace std;
using namespace scsi_defs;
PrimaryDevice::PrimaryDevice(const string& id) : ScsiPrimaryCommands(), Device(id)
PrimaryDevice::PrimaryDevice(const string& id) : Device(id)
{
// Mandatory SCSI primary commands
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady);

View File

@ -17,8 +17,6 @@
#include "dispatcher.h"
#include <string>
using namespace std;
class PrimaryDevice: public ScsiPrimaryCommands, public Device
{
@ -29,7 +27,7 @@ public:
PrimaryDevice(PrimaryDevice&) = delete;
PrimaryDevice& operator=(const PrimaryDevice&) = delete;
bool Dispatch(scsi_command) override;
virtual bool Dispatch(scsi_command);
void SetController(AbstractController *);
virtual bool WriteByteSequence(BYTE *, uint32_t);

View File

@ -99,3 +99,62 @@ void scsi_command_util::AddAppleVendorModePage(map<int, vector<byte>>& pages, bo
pages[48] = buf;
}
int scsi_command_util::GetInt16(const vector<int>& buf, int offset)
{
return (buf[offset] << 8) | buf[offset + 1];
}
uint32_t scsi_command_util::GetInt32(const vector<int>& buf, int offset)
{
return ((uint32_t)buf[offset] << 24) | ((uint32_t)buf[offset + 1] << 16) |
((uint32_t)buf[offset + 2] << 8) | (uint32_t)buf[offset + 3];
}
uint64_t scsi_command_util::GetInt64(const vector<int>& buf, int offset)
{
return ((uint64_t)buf[offset] << 56) | ((uint64_t)buf[offset + 1] << 48) |
((uint64_t)buf[offset + 2] << 40) | ((uint64_t)buf[offset + 3] << 32) |
((uint64_t)buf[offset + 4] << 24) | ((uint64_t)buf[offset + 5] << 16) |
((uint64_t)buf[offset + 6] << 8) | (uint64_t)buf[offset + 7];
}
void scsi_command_util::SetInt16(BYTE *buf, int value)
{
buf[0] = (BYTE)(value >> 8);
buf[1] = (BYTE)value;
}
void scsi_command_util::SetInt32(BYTE *buf, uint32_t value)
{
buf[0] = (BYTE)(value >> 24);
buf[1] = (BYTE)(value >> 16);
buf[2] = (BYTE)(value >> 8);
buf[3] = (BYTE)value;
}
void scsi_command_util::SetInt64(BYTE *buf, uint64_t value)
{
buf[0] = (BYTE)(value >> 56);
buf[1] = (BYTE)(value >> 48);
buf[2] = (BYTE)(value >> 40);
buf[3] = (BYTE)(value >> 32);
buf[4] = (BYTE)(value >> 24);
buf[5] = (BYTE)(value >> 16);
buf[6] = (BYTE)(value >> 8);
buf[7] = (BYTE)value;
}
void scsi_command_util::SetInt16(vector<byte>& buf, int offset, int value)
{
buf[offset] = (byte)(value >> 8);
buf[offset + 1] = (byte)value;
}
void scsi_command_util::SetInt32(vector<byte>& buf, int offset, int value)
{
buf[offset] = (byte)(value >> 24);
buf[offset + 1] = (byte)(value >> 16);
buf[offset + 2] = (byte)(value >> 8);
buf[offset + 3] = (byte)value;
}

View File

@ -11,15 +11,23 @@
#pragma once
#include "os.h"
#include <vector>
#include <map>
using namespace std;
using namespace std; //NOSONAR Not relevant for rascsi
namespace scsi_command_util
{
void ModeSelect(const vector<int>&, const BYTE *, int, int);
void EnrichFormatPage(map<int, vector<byte>>&, bool, int);
void AddAppleVendorModePage(map<int, vector<byte>>&, bool);
int GetInt16(const vector<int>&, int);
uint32_t GetInt32(const vector<int>&, int);
uint64_t GetInt64(const vector<int>&, int);
void SetInt16(BYTE *, int);
void SetInt32(BYTE *, uint32_t);
void SetInt64(BYTE *, uint64_t);
void SetInt16(vector<byte>&, int, int);
void SetInt32(vector<byte>&, int, int);
}

View File

@ -28,12 +28,12 @@
//---------------------------------------------------------------------------
#include "rascsi_exceptions.h"
#include "scsi_command_util.h"
#include "dispatcher.h"
#include "scsi_daynaport.h"
using namespace scsi_defs;
// const array<byte, 6> SCSIDaynaPort::m_bcast_addr = { byte{0xff}, byte{0xff}, byte{0xff}, byte{0xff}, byte{0xff}, byte{0xff} };
// const array<byte, 6> SCSIDaynaPort::m_apple_talk_addr = { byte{0x09}, byte{0x00}, byte{0x07}, byte{0xff}, byte{0xff}, byte{0xff} };
using namespace scsi_command_util;
// TODO Disk must not be the superclass
SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP")
@ -47,12 +47,6 @@ SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP")
dispatcher.Add(scsi_command::eCmdEnableInterface, "EnableInterface", &SCSIDaynaPort::EnableInterface);
}
SCSIDaynaPort::~SCSIDaynaPort()
{
// TAP driver release
m_tap.Cleanup();
}
bool SCSIDaynaPort::Dispatch(scsi_command cmd)
{
// TODO As long as DaynaPort suffers from being a subclass of Disk at least reject MODE SENSE and MODE SELECT
@ -176,7 +170,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, BYTE *buf, uint64_t)
// The first 2 bytes are reserved for the length of the packet
// The next 4 bytes are reserved for a flag field
//rx_packet_size = m_tap.Rx(response->data);
rx_packet_size = m_tap.Rx(&buf[DAYNAPORT_READ_HEADER_SZ]);
rx_packet_size = m_tap.Receive(&buf[DAYNAPORT_READ_HEADER_SZ]);
// If we didn't receive anything, return size of 0
if (rx_packet_size <= 0) {
@ -252,17 +246,8 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, BYTE *buf, uint64_t)
// breaks because of this, the work-around has to be re-evaluated.
size = 64;
}
buf[0] = (BYTE)(size >> 8);
buf[1] = (BYTE)size;
buf[2] = 0;
buf[3] = 0;
buf[4] = 0;
if(m_tap.PendingPackets()){
buf[5] = 0x10;
} else {
buf[5] = 0;
}
SetInt16(&buf[0], size);
SetInt32(&buf[2], m_tap.PendingPackets() ? 0x10 : 0x00);
// Return the packet size + 2 for the length + 4 for the flag field
// The CRC was already appended by the ctapdriver
@ -312,13 +297,13 @@ bool SCSIDaynaPort::WriteBytes(const vector<int>& cdb, const BYTE *buf, uint64_t
int data_length = cdb[4] + (cdb[3] << 8);
if (data_format == 0x00){
m_tap.Tx(buf, data_length);
m_tap.Send(buf, data_length);
LOGTRACE("%s Transmitted %u bytes (00 format)", __PRETTY_FUNCTION__, data_length)
}
else if (data_format == 0x80){
// The data length is specified in the first 2 bytes of the payload
data_length=buf[1] + (buf[0] << 8);
m_tap.Tx(&buf[4], data_length);
m_tap.Send(&buf[4], data_length);
LOGTRACE("%s Transmitted %u bytes (80 format)", __PRETTY_FUNCTION__, data_length)
}
else

View File

@ -32,21 +32,20 @@
#include "disk.h"
#include "ctapdriver.h"
#include <string>
#include <unordered_map>
#include <array>
using namespace std;
//===========================================================================
//
// DaynaPort SCSI Link
//
//===========================================================================
class SCSIDaynaPort: public Disk
class SCSIDaynaPort final : public Disk
{
public:
SCSIDaynaPort();
~SCSIDaynaPort() final;
~SCSIDaynaPort() override = default;
SCSIDaynaPort(SCSIDaynaPort&) = delete;
SCSIDaynaPort& operator=(const SCSIDaynaPort&) = delete;

View File

@ -17,17 +17,20 @@
//---------------------------------------------------------------------------
#include "rascsi_exceptions.h"
#include "scsi_command_util.h"
#include "dispatcher.h"
#include "scsi_host_bridge.h"
#include "ctapdriver.h"
#include "cfilesystem.h"
#include <arpa/inet.h>
#include <array>
using namespace std;
using namespace scsi_defs;
using namespace scsi_command_util;
SCSIBR::SCSIBR() : Disk("SCBR"), fs(new CFileSys())
SCSIBR::SCSIBR() : Disk("SCBR")
{
// Create host file system
fs->Reset();
fs.Reset();
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIBR::TestUnitReady);
dispatcher.Add(scsi_command::eCmdRead6, "GetMessage10", &SCSIBR::GetMessage10);
@ -36,19 +39,15 @@ SCSIBR::SCSIBR() : Disk("SCBR"), fs(new CFileSys())
SCSIBR::~SCSIBR()
{
// TAP driver release
tap.Cleanup();
// Release host file system
fs->Reset();
delete fs;
fs.Reset();
}
bool SCSIBR::Init(const unordered_map<string, string>& params)
{
SetParams(params);
#ifdef __linux__
#ifdef __linux
// TAP Driver Generation
m_bTapEnable = tap.Init(GetParams());
if (!m_bTapEnable){
@ -139,8 +138,7 @@ int SCSIBR::GetMessage10(const vector<int>& cdb, BYTE *buf)
if (phase == 0) {
// Get packet size
ReceivePacket();
buf[0] = (BYTE)(packet_len >> 8);
buf[1] = (BYTE)packet_len;
SetInt16(&buf[0], packet_len);
return 2;
} else {
// Get package data
@ -150,8 +148,7 @@ int SCSIBR::GetMessage10(const vector<int>& cdb, BYTE *buf)
case 2: // Received packet acquisition (size + buffer simultaneously)
ReceivePacket();
buf[0] = (BYTE)(packet_len >> 8);
buf[1] = (BYTE)packet_len;
SetInt16(&buf[0], packet_len);
GetPacketBuf(&buf[2]);
return packet_len + 2;
@ -162,8 +159,9 @@ int SCSIBR::GetMessage10(const vector<int>& cdb, BYTE *buf)
int total_len = 0;
for (int i = 0; i < 10; i++) {
ReceivePacket();
*buf++ = (BYTE)(packet_len >> 8);
*buf++ = (BYTE)packet_len;
SetInt16(&buf[0], packet_len);
// TODO Callee should not modify buffer pointer
buf += 2;
total_len += 2;
if (packet_len == 0)
break;
@ -201,7 +199,7 @@ int SCSIBR::GetMessage10(const vector<int>& cdb, BYTE *buf)
}
// Error
ASSERT(false);
assert(false);
return 0;
}
@ -335,7 +333,7 @@ void SCSIBR::SetMacAddr(const BYTE *mac)
void SCSIBR::ReceivePacket()
{
static const BYTE bcast_addr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
static const array<BYTE, 6> bcast_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
// previous packet has not been received
if (packet_enable) {
@ -343,10 +341,10 @@ void SCSIBR::ReceivePacket()
}
// Receive packet
packet_len = tap.Rx(packet_buf);
packet_len = tap.Receive(packet_buf);
// Check if received packet
if (memcmp(packet_buf, mac_addr, 6) != 0 && memcmp(packet_buf, bcast_addr, 6) != 0) {
if (memcmp(packet_buf, mac_addr, 6) != 0 && memcmp(packet_buf, bcast_addr.data(), bcast_addr.size()) != 0) {
packet_len = 0;
return;
}
@ -380,7 +378,7 @@ void SCSIBR::GetPacketBuf(BYTE *buf)
void SCSIBR::SendPacket(const BYTE *buf, int len)
{
tap.Tx(buf, len);
tap.Send(buf, len);
}
//---------------------------------------------------------------------------
@ -390,8 +388,8 @@ void SCSIBR::SendPacket(const BYTE *buf, int len)
//---------------------------------------------------------------------------
void SCSIBR::FS_InitDevice(BYTE *buf)
{
fs->Reset();
fsresult = fs->InitDevice((Human68k::argument_t*)buf);
fs.Reset();
fsresult = fs.InitDevice((Human68k::argument_t*)buf);
}
//---------------------------------------------------------------------------
@ -407,7 +405,7 @@ void SCSIBR::FS_CheckDir(BYTE *buf)
const auto pNamests = (Human68k::namests_t*)&buf[i];
fsresult = fs->CheckDir(nUnit, pNamests);
fsresult = fs.CheckDir(nUnit, pNamests);
}
//---------------------------------------------------------------------------
@ -423,7 +421,7 @@ void SCSIBR::FS_MakeDir(BYTE *buf)
const auto pNamests = (Human68k::namests_t*)&buf[i];
fsresult = fs->MakeDir(nUnit, pNamests);
fsresult = fs.MakeDir(nUnit, pNamests);
}
//---------------------------------------------------------------------------
@ -439,7 +437,7 @@ void SCSIBR::FS_RemoveDir(BYTE *buf)
const auto pNamests = (Human68k::namests_t*)&buf[i];
fsresult = fs->RemoveDir(nUnit, pNamests);
fsresult = fs.RemoveDir(nUnit, pNamests);
}
//---------------------------------------------------------------------------
@ -458,7 +456,7 @@ void SCSIBR::FS_Rename(BYTE *buf)
const Human68k::namests_t *pNamestsNew = (Human68k::namests_t*)&buf[i];
fsresult = fs->Rename(nUnit, pNamests, pNamestsNew);
fsresult = fs.Rename(nUnit, pNamests, pNamestsNew);
}
//---------------------------------------------------------------------------
@ -474,7 +472,7 @@ void SCSIBR::FS_Delete(BYTE *buf)
const auto *pNamests = (Human68k::namests_t*)&buf[i];
fsresult = fs->Delete(nUnit, pNamests);
fsresult = fs.Delete(nUnit, pNamests);
}
//---------------------------------------------------------------------------
@ -494,7 +492,7 @@ void SCSIBR::FS_Attribute(BYTE *buf)
dp = (DWORD*)&buf[i];
DWORD nHumanAttribute = ntohl(*dp);
fsresult = fs->Attribute(nUnit, pNamests, nHumanAttribute);
fsresult = fs.Attribute(nUnit, pNamests, nHumanAttribute);
}
//---------------------------------------------------------------------------
@ -523,7 +521,7 @@ void SCSIBR::FS_Files(BYTE *buf)
files->date = ntohs(files->date);
files->size = ntohl(files->size);
fsresult = fs->Files(nUnit, nKey, pNamests, files);
fsresult = fs.Files(nUnit, nKey, pNamests, files);
files->sector = htonl(files->sector);
files->offset = htons(files->offset);
@ -561,7 +559,7 @@ void SCSIBR::FS_NFiles(BYTE *buf)
files->date = ntohs(files->date);
files->size = ntohl(files->size);
fsresult = fs->NFiles(nUnit, nKey, files);
fsresult = fs.NFiles(nUnit, nKey, files);
files->sector = htonl(files->sector);
files->offset = htons(files->offset);
@ -610,7 +608,7 @@ void SCSIBR::FS_Create(BYTE *buf)
pFcb->date = ntohs(pFcb->date);
pFcb->size = ntohl(pFcb->size);
fsresult = fs->Create(nUnit, nKey, pNamests, pFcb, nAttribute, bForce);
fsresult = fs.Create(nUnit, nKey, pNamests, pFcb, nAttribute, bForce);
pFcb->fileptr = htonl(pFcb->fileptr);
pFcb->mode = htons(pFcb->mode);
@ -651,7 +649,7 @@ void SCSIBR::FS_Open(BYTE *buf)
pFcb->date = ntohs(pFcb->date);
pFcb->size = ntohl(pFcb->size);
fsresult = fs->Open(nUnit, nKey, pNamests, pFcb);
fsresult = fs.Open(nUnit, nKey, pNamests, pFcb);
pFcb->fileptr = htonl(pFcb->fileptr);
pFcb->mode = htons(pFcb->mode);
@ -689,7 +687,7 @@ void SCSIBR::FS_Close(BYTE *buf)
pFcb->date = ntohs(pFcb->date);
pFcb->size = ntohl(pFcb->size);
fsresult = fs->Close(nUnit, nKey, pFcb);
fsresult = fs.Close(nUnit, nKey, pFcb);
pFcb->fileptr = htonl(pFcb->fileptr);
pFcb->mode = htons(pFcb->mode);
@ -727,7 +725,7 @@ void SCSIBR::FS_Read(BYTE *buf)
pFcb->date = ntohs(pFcb->date);
pFcb->size = ntohl(pFcb->size);
fsresult = fs->Read(nKey, pFcb, fsopt, nSize);
fsresult = fs.Read(nKey, pFcb, fsopt, nSize);
pFcb->fileptr = htonl(pFcb->fileptr);
pFcb->mode = htons(pFcb->mode);
@ -767,7 +765,7 @@ void SCSIBR::FS_Write(BYTE *buf)
pFcb->date = ntohs(pFcb->date);
pFcb->size = ntohl(pFcb->size);
fsresult = fs->Write(nKey, pFcb, fsopt, nSize);
fsresult = fs.Write(nKey, pFcb, fsopt, nSize);
pFcb->fileptr = htonl(pFcb->fileptr);
pFcb->mode = htons(pFcb->mode);
@ -809,7 +807,7 @@ void SCSIBR::FS_Seek(BYTE *buf)
pFcb->date = ntohs(pFcb->date);
pFcb->size = ntohl(pFcb->size);
fsresult = fs->Seek(nKey, pFcb, nMode, nOffset);
fsresult = fs.Seek(nKey, pFcb, nMode, nOffset);
pFcb->fileptr = htonl(pFcb->fileptr);
pFcb->mode = htons(pFcb->mode);
@ -851,7 +849,7 @@ void SCSIBR::FS_TimeStamp(BYTE *buf)
pFcb->date = ntohs(pFcb->date);
pFcb->size = ntohl(pFcb->size);
fsresult = fs->TimeStamp(nUnit, nKey, pFcb, nHumanTime);
fsresult = fs.TimeStamp(nUnit, nKey, pFcb, nHumanTime);
pFcb->fileptr = htonl(pFcb->fileptr);
pFcb->mode = htons(pFcb->mode);
@ -877,7 +875,7 @@ void SCSIBR::FS_GetCapacity(BYTE *buf)
DWORD nUnit = ntohl(*dp);
Human68k::capacity_t cap;
fsresult = fs->GetCapacity(nUnit, &cap);
fsresult = fs.GetCapacity(nUnit, &cap);
cap.freearea = htons(cap.freearea);
cap.clusters = htons(cap.clusters);
@ -901,7 +899,7 @@ void SCSIBR::FS_CtrlDrive(BYTE *buf)
auto pCtrlDrive = (Human68k::ctrldrive_t*)&buf[i];
fsresult = fs->CtrlDrive(nUnit, pCtrlDrive);
fsresult = fs.CtrlDrive(nUnit, pCtrlDrive);
memcpy(fsout, pCtrlDrive, sizeof(Human68k::ctrldrive_t));
fsoutlen = sizeof(Human68k::ctrldrive_t);
@ -918,7 +916,7 @@ void SCSIBR::FS_GetDPB(BYTE *buf)
DWORD nUnit = ntohl(*dp);
Human68k::dpb_t dpb;
fsresult = fs->GetDPB(nUnit, &dpb);
fsresult = fs.GetDPB(nUnit, &dpb);
dpb.sector_size = htons(dpb.sector_size);
dpb.fat_sector = htons(dpb.fat_sector);
@ -949,7 +947,7 @@ void SCSIBR::FS_DiskRead(BYTE *buf)
dp = (DWORD*)&buf[i];
DWORD nSize = ntohl(*dp);
fsresult = fs->DiskRead(nUnit, fsout, nSector, nSize);
fsresult = fs.DiskRead(nUnit, fsout, nSector, nSize);
fsoutlen = 0x200;
}
@ -963,7 +961,7 @@ void SCSIBR::FS_DiskWrite(BYTE *buf)
auto dp = (DWORD*)buf;
DWORD nUnit = ntohl(*dp);
fsresult = fs->DiskWrite(nUnit);
fsresult = fs.DiskWrite(nUnit);
}
//---------------------------------------------------------------------------
@ -992,7 +990,7 @@ void SCSIBR::FS_Ioctrl(BYTE *buf)
break;
}
fsresult = fs->Ioctrl(nUnit, nFunction, pIoctrl);
fsresult = fs.Ioctrl(nUnit, nFunction, pIoctrl);
switch (nFunction) {
case 0:
@ -1022,7 +1020,7 @@ void SCSIBR::FS_Flush(BYTE *buf)
auto dp = (DWORD*)buf;
DWORD nUnit = ntohl(*dp);
fsresult = fs->Flush(nUnit);
fsresult = fs.Flush(nUnit);
}
//---------------------------------------------------------------------------
@ -1035,7 +1033,7 @@ void SCSIBR::FS_CheckMedia(BYTE *buf)
auto dp = (DWORD*)buf;
DWORD nUnit = ntohl(*dp);
fsresult = fs->CheckMedia(nUnit);
fsresult = fs.CheckMedia(nUnit);
}
//---------------------------------------------------------------------------
@ -1048,7 +1046,7 @@ void SCSIBR::FS_Lock(BYTE *buf)
auto dp = (DWORD*)buf;
DWORD nUnit = ntohl(*dp);
fsresult = fs->Lock(nUnit);
fsresult = fs.Lock(nUnit);
}
//---------------------------------------------------------------------------

View File

@ -20,22 +20,15 @@
#include "os.h"
#include "disk.h"
#include "ctapdriver.h"
#include "cfilesystem.h"
#include <string>
//===========================================================================
//
// SCSI Host Bridge
//
//===========================================================================
class CFileSys;
class SCSIBR : public Disk
class SCSIBR final : public Disk
{
public:
SCSIBR();
~SCSIBR() final;
~SCSIBR() override;
SCSIBR(SCSIBR&) = delete;
SCSIBR& operator=(const SCSIBR&) = delete;
@ -102,7 +95,7 @@ private:
void FS_CheckMedia(BYTE *buf); // $57 - check media
void FS_Lock(BYTE *buf); // $58 - get exclusive control
CFileSys *fs; // File system accessor
CFileSys fs; // File system accessor
DWORD fsresult = 0; // File system access result code
BYTE fsout[0x800]; // File system access result buffer
DWORD fsoutlen = 0; // File system access result buffer size

View File

@ -42,13 +42,14 @@
#include <sys/stat.h>
#include "rascsi_exceptions.h"
#include "../rasutil.h"
#include "dispatcher.h"
#include "scsi_printer.h"
using namespace std;
using namespace scsi_defs;
using namespace ras_util;
SCSIPrinter::SCSIPrinter() : PrimaryDevice("SCLP"), ScsiPrinterCommands()
SCSIPrinter::SCSIPrinter() : PrimaryDevice("SCLP")
{
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIPrinter::TestUnitReady);
dispatcher.Add(scsi_command::eCmdReserve6, "ReserveUnit", &SCSIPrinter::ReserveUnit);

View File

@ -15,19 +15,17 @@
#include <string>
#include <unordered_map>
using namespace std;
class SCSIPrinter: public PrimaryDevice, public ScsiPrinterCommands //NOSONAR Custom destructor cannot be removed
class SCSIPrinter final : public PrimaryDevice, public ScsiPrinterCommands //NOSONAR Custom destructor cannot be removed
{
static constexpr const char *TMP_FILE_PATTERN = "/tmp/rascsi_sclp-XXXXXX";
static constexpr const int TMP_FILENAME_LENGTH = strlen(TMP_FILE_PATTERN);
static const int TMP_FILENAME_LENGTH = string_view(TMP_FILE_PATTERN).size();
static const int NOT_RESERVED = -2;
public:
SCSIPrinter();
~SCSIPrinter() final;
~SCSIPrinter() override;
SCSIPrinter(SCSIPrinter&) = delete;
SCSIPrinter& operator=(const SCSIPrinter&) = delete;

View File

@ -14,131 +14,17 @@
//
//---------------------------------------------------------------------------
#include "scsicd.h"
#include "fileio.h"
#include "rascsi_exceptions.h"
#include "scsi_command_util.h"
#include "dispatcher.h"
#include "scsicd.h"
#include <array>
using namespace scsi_defs;
using namespace scsi_command_util;
//===========================================================================
//
// CD Track
//
//===========================================================================
void CDTrack::Init(int track, DWORD first, DWORD last)
{
ASSERT(!valid);
ASSERT(track >= 1);
ASSERT(first < last);
// Set and enable track number
track_no = track;
valid = TRUE;
// Remember LBA
first_lba = first;
last_lba = last;
}
void CDTrack::SetPath(bool cdda, const Filepath& path)
{
ASSERT(valid);
// CD-DA or data
audio = cdda;
// Remember the path
imgpath = path;
}
void CDTrack::GetPath(Filepath& path) const
{
ASSERT(valid);
// Return the path (by reference)
path = imgpath;
}
//---------------------------------------------------------------------------
//
// Gets the start of LBA
//
//---------------------------------------------------------------------------
DWORD CDTrack::GetFirst() const
{
ASSERT(valid);
ASSERT(first_lba < last_lba);
return first_lba;
}
//---------------------------------------------------------------------------
//
// Get the end of LBA
//
//---------------------------------------------------------------------------
DWORD CDTrack::GetLast() const
{
ASSERT(valid);
ASSERT(first_lba < last_lba);
return last_lba;
}
DWORD CDTrack::GetBlocks() const
{
ASSERT(valid);
ASSERT(first_lba < last_lba);
// Calculate from start LBA and end LBA
return last_lba - first_lba + 1;
}
int CDTrack::GetTrackNo() const
{
ASSERT(valid);
ASSERT(track_no >= 1);
return track_no;
}
//---------------------------------------------------------------------------
//
// Is valid block
//
//---------------------------------------------------------------------------
bool CDTrack::IsValid(DWORD lba) const
{
// false if the track itself is invalid
if (!valid) {
return false;
}
// If the block is BEFORE the first block
if (lba < first_lba) {
return false;
}
// If the block is AFTER the last block
if (last_lba < lba) {
return false;
}
// This track is valid
return true;
}
bool CDTrack::IsAudio() const
{
assert(valid);
return audio;
}
SCSICD::SCSICD(const unordered_set<uint32_t>& sector_sizes) : Disk("SCCD"), ScsiMmcCommands(), FileSupport()
SCSICD::SCSICD(const unordered_set<uint32_t>& sector_sizes) : Disk("SCCD")
{
SetSectorSizes(sector_sizes);
@ -176,7 +62,7 @@ void SCSICD::Open(const Filepath& path)
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 2048);
// Close and transfer for physical CD access
if (path.GetPath()[0] == _T('\\')) {
if (path.GetPath()[0] == '\\') {
// Close
fio.Close();
@ -190,13 +76,13 @@ void SCSICD::Open(const Filepath& path)
}
// Judge whether it is a CUE sheet or an ISO file
TCHAR file[5];
fio.Read((BYTE *)file, 4);
array<TCHAR, 5> file;
fio.Read((BYTE *)file.data(), 4);
file[4] = '\0';
fio.Close();
// If it starts with FILE, consider it as a CUE sheet
if (!strncasecmp(file, _T("FILE"), 4)) {
if (!strcasecmp(file.data(), "FILE")) {
// Open as CUE
OpenCue(path);
} else {
@ -206,7 +92,7 @@ void SCSICD::Open(const Filepath& path)
}
// Successful opening
ASSERT(GetBlockCount() > 0);
assert(GetBlockCount() > 0);
super::Open(path);
FileSupport::SetPath(path);
@ -241,21 +127,21 @@ void SCSICD::OpenIso(const Filepath& path)
}
// Read the first 12 bytes and close
BYTE header[12];
if (!fio.Read(header, sizeof(header))) {
array<BYTE, 12> header;
if (!fio.Read(header.data(), header.size())) {
fio.Close();
throw io_exception("Can't read header of ISO CD-ROM file");
}
// Check if it is RAW format
BYTE sync[12];
memset(sync, 0xff, sizeof(sync));
array<BYTE, 12> sync;
memset(sync.data(), 0xff, sync.size());
sync[0] = 0x00;
sync[11] = 0x00;
rawfile = false;
if (memcmp(header, sync, sizeof(sync)) == 0) {
if (memcmp(header.data(), sync.data(), sync.size()) == 0) {
// 00,FFx10,00, so it is presumed to be RAW format
if (!fio.Read(header, 4)) {
if (!fio.Read(header.data(), 4)) {
fio.Close();
throw io_exception("Can't read header of raw ISO CD-ROM file");
}
@ -320,7 +206,7 @@ void SCSICD::OpenPhysical(const Filepath& path)
SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount()));
// Create only one data track
ASSERT(!tracks.size());
assert(!tracks.size());
auto track = make_unique<CDTrack>();
track->Init(1, 0, (int)GetBlockCount() - 1);
track->SetPath(false, path);
@ -330,7 +216,7 @@ void SCSICD::OpenPhysical(const Filepath& path)
void SCSICD::ReadToc()
{
ctrl->length = ReadToc(ctrl->cmd, ctrl->buffer);
ctrl->length = ReadTocInternal(ctrl->cmd, ctrl->buffer);
EnterDataInPhase();
}
@ -340,9 +226,9 @@ vector<byte> SCSICD::InquiryInternal() const
return HandleInquiry(device_type::CD_ROM, scsi_level::SCSI_2, true);
}
void SCSICD::AddModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
void SCSICD::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
{
super::AddModePages(pages, page, changeable);
super::SetUpModePages(pages, page, changeable);
// Page code 13
if (page == 0x0d || page == 0x3f) {
@ -386,7 +272,7 @@ void SCSICD::AddVendorPage(map<int, vector<byte>>& pages, int page, bool changea
{
// Page code 48
if (page == 0x30 || page == 0x3f) {
scsi_command_util::AddAppleVendorModePage(pages, changeable);
AddAppleVendorModePage(pages, changeable);
}
}
@ -415,7 +301,7 @@ int SCSICD::Read(const vector<int>& cdb, BYTE *buf, uint64_t block)
// Reset the number of blocks
SetBlockCount(tracks[index]->GetBlocks());
ASSERT(GetBlockCount() > 0);
assert(GetBlockCount() > 0);
// Recreate the disk cache
Filepath path;
@ -432,10 +318,8 @@ int SCSICD::Read(const vector<int>& cdb, BYTE *buf, uint64_t block)
return super::Read(cdb, buf, block);
}
int SCSICD::ReadToc(const vector<int>& cdb, BYTE *buf)
int SCSICD::ReadTocInternal(const vector<int>& cdb, BYTE *buf)
{
assert(buf);
CheckReady();
// If ready, there is at least one track
@ -477,12 +361,11 @@ int SCSICD::ReadToc(const vector<int>& cdb, BYTE *buf)
buf[2] = (BYTE)tracks[0]->GetTrackNo();
buf[3] = (BYTE)last;
buf[6] = 0xaa;
DWORD lba = tracks[tracks.size() - 1]->GetLast() + 1;
uint32_t lba = tracks[tracks.size() - 1]->GetLast() + 1;
if (msf) {
LBAtoMSF(lba, &buf[8]);
} else {
buf[10] = (BYTE)(lba >> 8);
buf[11] = (BYTE)lba;
SetInt16(&buf[10], lba);
}
return length;
}
@ -497,8 +380,7 @@ int SCSICD::ReadToc(const vector<int>& cdb, BYTE *buf)
assert(loop >= 1);
// Create header
buf[0] = (BYTE)(((loop << 3) + 2) >> 8);
buf[1] = (BYTE)((loop << 3) + 2);
SetInt16(&buf[0], (loop << 3) + 2);
buf[2] = (BYTE)tracks[0]->GetTrackNo();
buf[3] = (BYTE)last;
buf += 4;
@ -521,8 +403,7 @@ int SCSICD::ReadToc(const vector<int>& cdb, BYTE *buf)
if (msf) {
LBAtoMSF(tracks[index]->GetFirst(), &buf[4]);
} else {
buf[6] = (BYTE)(tracks[index]->GetFirst() >> 8);
buf[7] = (BYTE)(tracks[index]->GetFirst());
SetInt16(&buf[6], tracks[index]->GetFirst());
}
// Advance buffer pointer and index
@ -550,12 +431,12 @@ void SCSICD::GetEventStatusNotification()
// LBA→MSF Conversion
//
//---------------------------------------------------------------------------
void SCSICD::LBAtoMSF(DWORD lba, BYTE *msf) const
void SCSICD::LBAtoMSF(uint32_t lba, BYTE *msf) const
{
// 75 and 75*60 get the remainder
DWORD m = lba / (75 * 60);
DWORD s = lba % (75 * 60);
DWORD f = s % 75;
uint32_t m = lba / (75 * 60);
uint32_t s = lba % (75 * 60);
uint32_t f = s % 75;
s /= 75;
// The base point is M=0, S=2, F=0

View File

@ -13,58 +13,18 @@
// [ SCSI CD-ROM ]
//
//---------------------------------------------------------------------------
#pragma once
#include "os.h"
#include "disk.h"
#include "filepath.h"
#include "cd_track.h"
#include "file_support.h"
#include "interfaces/scsi_mmc_commands.h"
#include "interfaces/scsi_primary_commands.h"
//===========================================================================
//
// CD-ROM Track
//
//===========================================================================
class CDTrack
{
public:
CDTrack() = default;
virtual ~CDTrack() final = default;
CDTrack(CDTrack&) = delete;
CDTrack& operator=(const CDTrack&) = delete;
void Init(int track, DWORD first, DWORD last);
// Properties
void SetPath(bool cdda, const Filepath& path); // Set the path
void GetPath(Filepath& path) const; // Get the path
DWORD GetFirst() const; // Get the start LBA
DWORD GetLast() const; // Get the last LBA
DWORD GetBlocks() const; // Get the number of blocks
int GetTrackNo() const; // Get the track number
bool IsValid(DWORD lba) const; // Is this a valid LBA?
bool IsAudio() const; // Is this an audio track?
private:
bool valid = false; // Valid track
int track_no = -1; // Track number
DWORD first_lba = 0; // First LBA
DWORD last_lba = 0; // Last LBA
bool audio = false; // Audio track flag
Filepath imgpath; // Image file path
};
//===========================================================================
//
// SCSI CD-ROM
//
//===========================================================================
class SCSICD : public Disk, public ScsiMmcCommands, public FileSupport
{
public:
explicit SCSICD(const unordered_set<uint32_t>&);
@ -79,11 +39,10 @@ public:
// Commands
vector<byte> InquiryInternal() const override;
int Read(const vector<int>&, BYTE *, uint64_t) override;
int ReadToc(const vector<int>&, BYTE *);
protected:
void AddModePages(map<int, vector<byte>>&, int, bool) const override;
void SetUpModePages(map<int, vector<byte>>&, int, bool) const override;
void AddVendorPage(map<int, vector<byte>>&, int, bool) const override;
private:
@ -92,6 +51,8 @@ private:
Dispatcher<SCSICD> dispatcher;
int ReadTocInternal(const vector<int>&, BYTE *);
void AddCDROMPage(map<int, vector<byte>>&, bool) const;
void AddCDDAPage(map<int, vector<byte>>&, bool) const;
@ -103,7 +64,7 @@ private:
void ReadToc() override;
void GetEventStatusNotification() override;
void LBAtoMSF(DWORD lba, BYTE *msf) const; // LBA→MSF conversion
void LBAtoMSF(uint32_t, BYTE *) const; // LBA→MSF conversion
bool rawfile = false; // RAW flag

View File

@ -20,6 +20,8 @@
#include "scsi_command_util.h"
#include <sstream>
using namespace scsi_command_util;
SCSIHD::SCSIHD(const unordered_set<uint32_t>& sector_sizes, bool removable, scsi_defs::scsi_level level)
: Disk(removable ? "SCRM" : "SCHD")
{
@ -60,17 +62,6 @@ void SCSIHD::FinalizeSetup(const Filepath &path, off_t size)
FileSupport::SetPath(path);
}
void SCSIHD::Reset()
{
// Unlock and release attention
SetLocked(false);
SetAttn(false);
// No reset, clear code
SetReset(false);
SetStatusCode(0);
}
void SCSIHD::Open(const Filepath& path)
{
assert(!IsReady());
@ -100,7 +91,7 @@ vector<byte> SCSIHD::InquiryInternal() const
return HandleInquiry(device_type::DIRECT_ACCESS, scsi_level, IsRemovable());
}
void SCSIHD::ModeSelect(const vector<int>& cdb, const BYTE *buf, int length)
void SCSIHD::ModeSelect(const vector<int>& cdb, const BYTE *buf, int length) const
{
scsi_command_util::ModeSelect(cdb, buf, length, 1 << GetSectorSizeShiftCount());
}
@ -109,13 +100,13 @@ void SCSIHD::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) const
{
Disk::AddFormatPage(pages, changeable);
scsi_command_util::EnrichFormatPage(pages, changeable, 1 << GetSectorSizeShiftCount());
EnrichFormatPage(pages, changeable, 1 << GetSectorSizeShiftCount());
}
void SCSIHD::AddVendorPage(map<int, vector<byte>>& pages, int page, bool changeable) const
{
// Page code 48
if (page == 0x30 || page == 0x3f) {
scsi_command_util::AddAppleVendorModePage(pages, changeable);
AddAppleVendorModePage(pages, changeable);
}
}

View File

@ -13,10 +13,11 @@
// [ SCSI hard disk ]
//
//---------------------------------------------------------------------------
#pragma once
#include "os.h"
#include "disk.h"
#include "file_support.h"
#include "filepath.h"
class SCSIHD : public Disk, public FileSupport
@ -32,12 +33,11 @@ public:
void FinalizeSetup(const Filepath&, off_t);
void Reset();
void Open(const Filepath&) override;
// Commands
vector<byte> InquiryInternal() const override;
void ModeSelect(const vector<int>& cdb, const BYTE *buf, int length) override;
void ModeSelect(const vector<int>&, const BYTE *, int) const override;
void AddFormatPage(map<int, vector<byte>>&, bool) const override;
void AddVendorPage(map<int, vector<byte>>&, int, bool) const override;

View File

@ -17,6 +17,9 @@
#include "scsihd_nec.h"
#include "fileio.h"
#include "rascsi_exceptions.h"
#include "scsi_command_util.h"
using namespace scsi_command_util;
const unordered_set<uint32_t> SCSIHD_NEC::sector_sizes = { 512 };
@ -42,7 +45,7 @@ static inline DWORD getDwordLE(const BYTE *b)
void SCSIHD_NEC::Open(const Filepath& path)
{
ASSERT(!IsReady());
assert(!IsReady());
// Open as read-only
Fileio fio;
@ -54,8 +57,8 @@ void SCSIHD_NEC::Open(const Filepath& path)
off_t size = fio.GetFileSize();
// NEC root sector
BYTE root_sector[512];
if (size >= (off_t)sizeof(root_sector) && !fio.Read(root_sector, sizeof(root_sector))) {
array<BYTE, 512> root_sector;
if (size >= (off_t)root_sector.size() && !fio.Read(root_sector.data(), root_sector.size())) {
fio.Close();
throw io_exception("Can't read NEC hard disk file root sector");
}
@ -92,7 +95,7 @@ void SCSIHD_NEC::Open(const Filepath& path)
}
// T98Next HD image?
else if (!strcasecmp(ext, ".nhd")) {
if (!memcmp(root_sector, "T98HDDIMAGE.R0\0", 15)) {
if (!memcmp(root_sector.data(), "T98HDDIMAGE.R0\0", 15)) {
disk.image_offset = getDwordLE(&root_sector[0x110]);
cylinders = getDwordLE(&root_sector[0x114]);
heads = getWordLE(&root_sector[0x118]);
@ -153,8 +156,7 @@ void SCSIHD_NEC::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) c
// Make the number of bytes in the physical sector appear mutable (although it cannot actually be)
if (changeable) {
buf[0xc] = (byte)0xff;
buf[0xd] = (byte)0xff;
SetInt16(buf, 0x0c, -1);
pages[3] = buf;
@ -163,17 +165,13 @@ void SCSIHD_NEC::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) c
if (IsReady()) {
// Set the number of tracks in one zone (PC-9801-55 seems to see this value)
buf[0x2] = (byte)(heads >> 8);
buf[0x3] = (byte)heads;
SetInt16(buf, 0x02, heads);
// Set the number of sectors per track
buf[0xa] = (byte)(sectors >> 8);
buf[0xb] = (byte)sectors;
SetInt16(buf, 0x0a, sectors);
// Set the number of bytes in the physical sector
int size = 1 << disk.size;
buf[0xc] = (byte)(size >> 8);
buf[0xd] = (byte)size;
SetInt16(buf, 0x0c, 1 << disk.size);
}
// Set removable attributes (remains of the old days)
@ -191,9 +189,7 @@ void SCSIHD_NEC::AddDrivePage(map<int, vector<byte>>& pages, bool changeable) co
// No changeable area
if (!changeable && IsReady()) {
// Set the number of cylinders
buf[0x2] = (byte)(cylinders >> 16);
buf[0x3] = (byte)(cylinders >> 8);
buf[0x4] = (byte)cylinders;
SetInt32(buf, 0x01, cylinders);
// Set the number of heads
buf[0x5] = (byte)heads;

View File

@ -19,6 +19,8 @@
#include "scsi_command_util.h"
#include "scsimo.h"
using namespace scsi_command_util;
SCSIMO::SCSIMO(const unordered_set<uint32_t>& sector_sizes, const unordered_map<uint64_t, Geometry>& geometries)
: Disk("SCMO")
{
@ -67,17 +69,9 @@ vector<byte> SCSIMO::InquiryInternal() const
return HandleInquiry(device_type::OPTICAL_MEMORY, scsi_level::SCSI_2, true);
}
void SCSIMO::SetDeviceParameters(BYTE *buf) const
void SCSIMO::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
{
Disk::SetDeviceParameters(buf);
// MEDIUM TYPE: Optical reversible or erasable
buf[2] = 0x03;
}
void SCSIMO::AddModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
{
Disk::AddModePages(pages, page, changeable);
Disk::SetUpModePages(pages, page, changeable);
// Page code 6
if (page == 0x06 || page == 0x3f) {
@ -89,7 +83,7 @@ void SCSIMO::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) const
{
Disk::AddFormatPage(pages, changeable);
scsi_command_util::EnrichFormatPage(pages, changeable, 1 << GetSectorSizeShiftCount());
EnrichFormatPage(pages, changeable, 1 << GetSectorSizeShiftCount());
}
void SCSIMO::AddOptionPage(map<int, vector<byte>>& pages, bool) const
@ -100,7 +94,7 @@ void SCSIMO::AddOptionPage(map<int, vector<byte>>& pages, bool) const
// Do not report update blocks
}
void SCSIMO::ModeSelect(const vector<int>& cdb, const BYTE *buf, int length)
void SCSIMO::ModeSelect(const vector<int>& cdb, const BYTE *buf, int length) const
{
scsi_command_util::ModeSelect(cdb, buf, length, 1 << GetSectorSizeShiftCount());
}
@ -200,14 +194,9 @@ void SCSIMO::AddVendorPage(map<int, vector<byte>>& pages, int page, bool changea
buf[2] = (byte)0; // format mode
buf[3] = (byte)0; // type of format
buf[4] = (byte)(blocks >> 24);
buf[5] = (byte)(blocks >> 16);
buf[6] = (byte)(blocks >> 8);
buf[7] = (byte)blocks;
buf[8] = (byte)(spare >> 8);
buf[9] = (byte)spare;
buf[10] = (byte)(bands >> 8);
buf[11] = (byte)bands;
SetInt32(buf, 4, (uint32_t)blocks);
SetInt16(buf, 8, spare);
SetInt16(buf, 10, bands);
}
pages[32] = buf;

View File

@ -16,8 +16,8 @@
#pragma once
#include "os.h"
#include "disk.h"
#include "file_support.h"
#include "filepath.h"
class SCSIMO : public Disk, public FileSupport
@ -29,15 +29,14 @@ public:
SCSIMO(SCSIMO&) = delete;
SCSIMO& operator=(const SCSIMO&) = delete;
void Open(const Filepath& path) override;
void Open(const Filepath&) override;
vector<byte> InquiryInternal() const override;
void ModeSelect(const vector<int>&, const BYTE *, int) override;
void ModeSelect(const vector<int>&, const BYTE *, int) const override;
protected:
void SetDeviceParameters(BYTE *) const override;
void AddModePages(map<int, vector<byte>>&, int, bool) const override;
void SetUpModePages(map<int, vector<byte>>&, int, bool) const override;
void AddFormatPage(map<int, vector<byte>>&, bool) const override;
void AddVendorPage(map<int, vector<byte>>&, int, bool) const override;

View File

@ -8,16 +8,12 @@
//
//---------------------------------------------------------------------------
#include "os.h"
#include "filepath.h"
#include "fileio.h"
#include "config.h"
#include <fcntl.h>
#include <unistd.h>
Fileio::~Fileio()
{
ASSERT(handle == -1);
// Safety measure for Release
Close();
}
@ -28,7 +24,7 @@ bool Fileio::Open(const char *fname, OpenMode mode, bool directIO)
assert(handle < 0);
// Always fail a read from a null array
if (fname[0] == _T('\0')) {
if (fname[0] == '\0') {
handle = -1;
return false;
}

View File

@ -11,6 +11,7 @@
#pragma once
#include "filepath.h"
#include <sys/types.h>
class Fileio
{

View File

@ -12,6 +12,7 @@
#include "filepath.h"
#include "config.h"
#include "fileio.h"
#include <libgen.h>
Filepath::Filepath()
{
@ -29,10 +30,10 @@ Filepath& Filepath::operator=(const Filepath& path)
void Filepath::Clear()
{
// Clear the path and each part
m_szPath[0] = _T('\0');
m_szDir[0] = _T('\0');
m_szFile[0] = _T('\0');
m_szExt[0] = _T('\0');
m_szPath[0] = '\0';
m_szDir[0] = '\0';
m_szFile[0] = '\0';
m_szExt[0] = '\0';
}
//---------------------------------------------------------------------------
@ -42,8 +43,8 @@ void Filepath::Clear()
//---------------------------------------------------------------------------
void Filepath::SetPath(const char *path)
{
ASSERT(path);
ASSERT(strlen(path) < _MAX_PATH);
assert(path);
assert(strlen(path) < _MAX_PATH);
// Copy pathname
strcpy(m_szPath, path);
@ -60,9 +61,9 @@ void Filepath::SetPath(const char *path)
void Filepath::Split()
{
// Initialize the parts
m_szDir[0] = _T('\0');
m_szFile[0] = _T('\0');
m_szExt[0] = _T('\0');
m_szDir[0] = '\0';
m_szFile[0] = '\0';
m_szExt[0] = '\0';
// Split
char *pDir = strdup(m_szPath);

View File

@ -12,14 +12,15 @@
#include "os.h"
using TCHAR = char;
class Fileio;
//---------------------------------------------------------------------------
//
// Constant definition
//
//---------------------------------------------------------------------------
#define FILEPATH_MAX _MAX_PATH
static const int _MAX_EXT = 256;
static const int _MAX_DIR = 256;
static const int _MAX_PATH = 260;
static const int _MAX_FNAME = 256;
static const int FILEPATH_MAX = _MAX_PATH;
//===========================================================================
//
@ -35,10 +36,10 @@ public:
Filepath();
virtual ~Filepath() = default;
Filepath(Filepath&) = delete;
Filepath& operator=(const Filepath& path);
Filepath& operator=(const Filepath&);
void Clear();
void SetPath(const char *path); // File settings (user) for MBCS
void SetPath(const char *); // File settings (user) for MBCS
const char *GetPath() const { return m_szPath; } // Get path name
const char *GetFileExt() const; // Get short name (LPCTSTR)

View File

@ -11,15 +11,21 @@
//---------------------------------------------------------------------------
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include "os.h"
#include "hal/gpiobus.h"
#include "hal/systimer.h"
#include "config.h"
#include "log.h"
#include <array>
#ifdef __linux
#include <sys/epoll.h>
#endif
#ifdef __linux__
using namespace std;
#ifdef __linux
//---------------------------------------------------------------------------
//
// imported from bcm_host.c
@ -51,7 +57,7 @@ DWORD bcm_host_get_peripheral_address(void)
#endif
return address;
}
#endif // __linux__
#endif
#ifdef __NetBSD__
// Assume the Raspberry Pi series and estimate the address from CPU
@ -169,16 +175,16 @@ bool GPIOBUS::Init(mode_e mode)
// Initialize all signals
for (i = 0; SignalTable[i] >= 0; i++) {
int j = SignalTable[i];
PinSetSignal(j, FALSE);
PinSetSignal(j, OFF);
PinConfig(j, GPIO_INPUT);
PullConfig(j, pullmode);
}
// Set control signals
PinSetSignal(PIN_ACT, FALSE);
PinSetSignal(PIN_TAD, FALSE);
PinSetSignal(PIN_IND, FALSE);
PinSetSignal(PIN_DTD, FALSE);
PinSetSignal(PIN_ACT, OFF);
PinSetSignal(PIN_TAD, OFF);
PinSetSignal(PIN_IND, OFF);
PinSetSignal(PIN_DTD, OFF);
PinConfig(PIN_ACT, GPIO_OUTPUT);
PinConfig(PIN_TAD, GPIO_OUTPUT);
PinConfig(PIN_IND, GPIO_OUTPUT);
@ -304,11 +310,11 @@ void GPIOBUS::Cleanup()
#endif // USE_SEL_EVENT_ENABLE
// Set control signals
PinSetSignal(PIN_ENB, FALSE);
PinSetSignal(PIN_ACT, FALSE);
PinSetSignal(PIN_TAD, FALSE);
PinSetSignal(PIN_IND, FALSE);
PinSetSignal(PIN_DTD, FALSE);
PinSetSignal(PIN_ENB, OFF);
PinSetSignal(PIN_ACT, OFF);
PinSetSignal(PIN_TAD, OFF);
PinSetSignal(PIN_IND, OFF);
PinSetSignal(PIN_DTD, OFF);
PinConfig(PIN_ACT, GPIO_INPUT);
PinConfig(PIN_TAD, GPIO_INPUT);
PinConfig(PIN_IND, GPIO_INPUT);
@ -317,7 +323,7 @@ void GPIOBUS::Cleanup()
// Initialize all signals
for (int i = 0; SignalTable[i] >= 0; i++) {
int pin = SignalTable[i];
PinSetSignal(pin, FALSE);
PinSetSignal(pin, OFF);
PinConfig(pin, GPIO_INPUT);
PullConfig(pin, GPIO_PULLNONE);
}
@ -692,7 +698,7 @@ int GPIOBUS::CommandHandShake(BYTE *buf)
SetSignal(PIN_REQ, ON);
// Wait for ACK signal
bool ret = WaitSignal(PIN_ACK, TRUE);
bool ret = WaitSignal(PIN_ACK, ON);
// Wait until the signal line stabilizes
SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS);
@ -710,7 +716,7 @@ int GPIOBUS::CommandHandShake(BYTE *buf)
}
// Wait for ACK to clear
ret = WaitSignal(PIN_ACK, FALSE);
ret = WaitSignal(PIN_ACK, OFF);
// Timeout waiting for ACK to clear
if (!ret) {
@ -730,7 +736,7 @@ int GPIOBUS::CommandHandShake(BYTE *buf)
if (*buf == 0x1F) {
SetSignal(PIN_REQ, ON);
ret = WaitSignal(PIN_ACK, TRUE);
ret = WaitSignal(PIN_ACK, ON);
SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS);
@ -744,7 +750,7 @@ int GPIOBUS::CommandHandShake(BYTE *buf)
return 0;
}
WaitSignal(PIN_ACK, FALSE);
WaitSignal(PIN_ACK, OFF);
if (!ret) {
EnableIRQ();
@ -763,7 +769,7 @@ int GPIOBUS::CommandHandShake(BYTE *buf)
SetSignal(PIN_REQ, ON);
// Wait for ACK signal
ret = WaitSignal(PIN_ACK, TRUE);
ret = WaitSignal(PIN_ACK, ON);
// Wait until the signal line stabilizes
SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS);
@ -780,7 +786,7 @@ int GPIOBUS::CommandHandShake(BYTE *buf)
}
// Wait for ACK to clear
ret = WaitSignal(PIN_ACK, FALSE);
ret = WaitSignal(PIN_ACK, OFF);
// Check for timeout waiting for ACK to clear
if (!ret) {
@ -814,7 +820,7 @@ int GPIOBUS::ReceiveHandShake(BYTE *buf, int count)
SetSignal(PIN_REQ, ON);
// Wait for ACK
bool ret = WaitSignal(PIN_ACK, TRUE);
bool ret = WaitSignal(PIN_ACK, ON);
// Wait until the signal line stabilizes
SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS);
@ -831,7 +837,7 @@ int GPIOBUS::ReceiveHandShake(BYTE *buf, int count)
}
// Wait for ACK to clear
ret = WaitSignal(PIN_ACK, FALSE);
ret = WaitSignal(PIN_ACK, OFF);
// Check for timeout waiting for ACK to clear
if (!ret) {
@ -847,7 +853,7 @@ int GPIOBUS::ReceiveHandShake(BYTE *buf, int count)
for (i = 0; i < count; i++) {
// Wait for the REQ signal to be asserted
bool ret = WaitSignal(PIN_REQ, TRUE);
bool ret = WaitSignal(PIN_REQ, ON);
// Check for timeout waiting for REQ signal
if (!ret) {
@ -869,7 +875,7 @@ int GPIOBUS::ReceiveHandShake(BYTE *buf, int count)
SetSignal(PIN_ACK, ON);
// Wait for REQ to clear
ret = WaitSignal(PIN_REQ, FALSE);
ret = WaitSignal(PIN_REQ, OFF);
// Clear the ACK signal
SetSignal(PIN_ACK, OFF);
@ -919,7 +925,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes)
SetDAT(*buf);
// Wait for ACK to clear
bool ret = WaitSignal(PIN_ACK, FALSE);
bool ret = WaitSignal(PIN_ACK, OFF);
// Check for timeout waiting for ACK to clear
if (!ret) {
@ -932,7 +938,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes)
SetSignal(PIN_REQ, ON);
// Wait for ACK
ret = WaitSignal(PIN_ACK, TRUE);
ret = WaitSignal(PIN_ACK, ON);
// Clear REQ signal
SetSignal(PIN_REQ, OFF);
@ -947,7 +953,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes)
}
// Wait for ACK to clear
WaitSignal(PIN_ACK, FALSE);
WaitSignal(PIN_ACK, OFF);
} else {
// Get Phase
DWORD phase = Acquire() & GPIO_MCI;
@ -962,7 +968,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes)
SetDAT(*buf);
// Wait for REQ to be asserted
bool ret = WaitSignal(PIN_REQ, TRUE);
bool ret = WaitSignal(PIN_REQ, ON);
// Check for timeout waiting for REQ to be asserted
if (!ret) {
@ -980,7 +986,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes)
SetSignal(PIN_ACK, ON);
// Wait for REQ to clear
ret = WaitSignal(PIN_REQ, FALSE);
ret = WaitSignal(PIN_REQ, OFF);
// Clear the ACK signal
SetSignal(PIN_ACK, OFF);
@ -1063,27 +1069,18 @@ const int GPIOBUS::SignalTable[19] = {
//---------------------------------------------------------------------------
void GPIOBUS::MakeTable(void)
{
const int pintbl[] = {
const array<int, 9> pintbl = {
PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4,
PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP
};
int i;
int j;
bool tblParity[256];
#if SIGNAL_CONTROL_MODE == 0
int index;
int shift;
#else
DWORD gpclr;
DWORD gpset;
#endif
array<bool, 256> tblParity;
// Create parity table
for (i = 0; i < 0x100; i++) {
for (int i = 0; i < 0x100; i++) {
auto bits = (DWORD)i;
DWORD parity = 0;
for (j = 0; j < 8; j++) {
for (int j = 0; j < 8; j++) {
parity ^= bits & 1;
bits >>= 1;
}
@ -1095,7 +1092,7 @@ void GPIOBUS::MakeTable(void)
// Mask and setting data generation
memset(tblDatMsk, 0xff, sizeof(tblDatMsk));
memset(tblDatSet, 0x00, sizeof(tblDatSet));
for (i = 0; i < 0x100; i++) {
for (int i = 0; i < 0x100; i++) {
// Bit string for inspection
auto bits = (DWORD)i;
@ -1105,10 +1102,10 @@ void GPIOBUS::MakeTable(void)
}
// Bit check
for (j = 0; j < 9; j++) {
for (int j = 0; j < 9; j++) {
// Index and shift amount calculation
index = pintbl[j] / 10;
shift = (pintbl[j] % 10) * 3;
int index = pintbl[j] / 10;
int shift = (pintbl[j] % 10) * 3;
// Mask data
tblDatMsk[index][i] &= ~(0x7 << shift);
@ -1125,7 +1122,7 @@ void GPIOBUS::MakeTable(void)
// Mask and setting data generation
memset(tblDatMsk, 0x00, sizeof(tblDatMsk));
memset(tblDatSet, 0x00, sizeof(tblDatSet));
for (i = 0; i < 0x100; i++) {
for (int i = 0; i < 0x100; i++) {
// bit string for inspection
DWORD bits = (DWORD)i;
@ -1140,9 +1137,9 @@ void GPIOBUS::MakeTable(void)
#endif
// Create GPIO register information
gpclr = 0;
gpset = 0;
for (j = 0; j < 9; j++) {
DWORD gpclr = 0;
DWORD gpset = 0;
for (int j = 0; j < 9; j++) {
if (bits & 1) {
gpset |= (1 << pintbl[j]);
} else {
@ -1267,7 +1264,7 @@ bool GPIOBUS::WaitSignal(int pin, int ast)
void GPIOBUS::DisableIRQ()
{
#ifdef __linux__
#ifdef __linux
if (rpitype == 4) {
// RPI4 is disabled by GICC
giccpmr = gicc[GICC_PMR];

View File

@ -14,6 +14,10 @@
#include "config.h"
#include "scsi.h"
#ifdef __linux
#include <linux/gpio.h>
#endif
//---------------------------------------------------------------------------
//
// Connection method definitions
@ -135,67 +139,68 @@
// Constant declarations (GPIO)
//
//---------------------------------------------------------------------------
#define SYST_OFFSET 0x00003000
#define IRPT_OFFSET 0x0000B200
#define ARMT_OFFSET 0x0000B400
#define PADS_OFFSET 0x00100000
#define GPIO_OFFSET 0x00200000
#define QA7_OFFSET 0x01000000
#define GPIO_INPUT 0
#define GPIO_OUTPUT 1
#define GPIO_PULLNONE 0
#define GPIO_PULLDOWN 1
#define GPIO_PULLUP 2
#define GPIO_FSEL_0 0
#define GPIO_FSEL_1 1
#define GPIO_FSEL_2 2
#define GPIO_FSEL_3 3
#define GPIO_SET_0 7
#define GPIO_CLR_0 10
#define GPIO_LEV_0 13
#define GPIO_EDS_0 16
#define GPIO_REN_0 19
#define GPIO_FEN_0 22
#define GPIO_HEN_0 25
#define GPIO_LEN_0 28
#define GPIO_AREN_0 31
#define GPIO_AFEN_0 34
#define GPIO_PUD 37
#define GPIO_CLK_0 38
#define GPIO_GPPINMUXSD 52
#define GPIO_PUPPDN0 57
#define GPIO_PUPPDN1 58
#define GPIO_PUPPDN3 59
#define GPIO_PUPPDN4 60
#define PAD_0_27 11
#define SYST_CS 0
#define SYST_CLO 1
#define SYST_CHI 2
#define SYST_C0 3
#define SYST_C1 4
#define SYST_C2 5
#define SYST_C3 6
#define ARMT_LOAD 0
#define ARMT_VALUE 1
#define ARMT_CTRL 2
#define ARMT_CLRIRQ 3
#define ARMT_RAWIRQ 4
#define ARMT_MSKIRQ 5
#define ARMT_RELOAD 6
#define ARMT_PREDIV 7
#define ARMT_FREERUN 8
#define IRPT_PND_IRQ_B 0
#define IRPT_PND_IRQ_1 1
#define IRPT_PND_IRQ_2 2
#define IRPT_FIQ_CNTL 3
#define IRPT_ENB_IRQ_1 4
#define IRPT_ENB_IRQ_2 5
#define IRPT_ENB_IRQ_B 6
#define IRPT_DIS_IRQ_1 7
#define IRPT_DIS_IRQ_2 8
#define IRPT_DIS_IRQ_B 9
#define QA7_CORE0_TINTC 16
#define GPIO_IRQ (32 + 20) // GPIO3
const static uint32_t SYST_OFFSET = 0x00003000;
const static uint32_t IRPT_OFFSET = 0x0000B200;
const static uint32_t ARMT_OFFSET = 0x0000B400;
const static uint32_t PADS_OFFSET = 0x00100000;
const static uint32_t GPIO_OFFSET = 0x00200000;
const static uint32_t QA7_OFFSET = 0x01000000;
const static int GPIO_INPUT = 0;
const static int GPIO_OUTPUT = 1;
const static int GPIO_PULLNONE = 0;
const static int GPIO_PULLDOWN = 1;
const static int GPIO_PULLUP = 2;
const static int GPIO_FSEL_0 = 0;
const static int GPIO_FSEL_1 = 1;
const static int GPIO_FSEL_2 = 2;
const static int GPIO_FSEL_3 = 3;
const static int GPIO_SET_0 = 7;
const static int GPIO_CLR_0 = 10;
const static int GPIO_LEV_0 = 13;
const static int GPIO_EDS_0 = 16;
const static int GPIO_REN_0 = 19;
const static int GPIO_FEN_0 = 22;
const static int GPIO_HEN_0 = 25;
const static int GPIO_LEN_0 = 28;
const static int GPIO_AREN_0 = 31;
const static int GPIO_AFEN_0 = 34;
const static int GPIO_PUD = 37;
const static int GPIO_CLK_0 = 38;
const static int GPIO_GPPINMUXSD = 52;
const static int GPIO_PUPPDN0 = 57;
const static int GPIO_PUPPDN1 = 58;
const static int GPIO_PUPPDN3 = 59;
const static int GPIO_PUPPDN4 = 60;
const static int PAD_0_27 = 11;
const static int SYST_CS = 0;
const static int SYST_CLO = 1;
const static int SYST_CHI = 2;
const static int SYST_C0 = 3;
const static int SYST_C1 = 4;
const static int SYST_C2 = 5;
const static int SYST_C3 = 6;
const static int ARMT_LOAD = 0;
const static int ARMT_VALUE = 1;
const static int ARMT_CTRL = 2;
const static int ARMT_CLRIRQ = 3;
const static int ARMT_RAWIRQ = 4;
const static int ARMT_MSKIRQ = 5;
const static int ARMT_RELOAD = 6;
const static int ARMT_PREDIV = 7;
const static int ARMT_FREERUN = 8;
const static int IRPT_PND_IRQ_B = 0;
const static int IRPT_PND_IRQ_1 = 1;
const static int IRPT_PND_IRQ_2 = 2;
const static int IRPT_FIQ_CNTL = 3;
const static int IRPT_ENB_IRQ_1 = 4;
const static int IRPT_ENB_IRQ_2 = 5;
const static int IRPT_ENB_IRQ_B = 6;
const static int IRPT_DIS_IRQ_1 = 7;
const static int IRPT_DIS_IRQ_2 = 8;
const static int IRPT_DIS_IRQ_B = 9;
const static int QA7_CORE0_TINTC = 16;
const static int GPIO_IRQ = (32 + 20); // GPIO3
#define GPIO_INEDGE ((1 << PIN_BSY) | \
(1 << PIN_SEL) | \
@ -212,33 +217,33 @@
// Constant declarations (GIC)
//
//---------------------------------------------------------------------------
#define ARM_GICD_BASE 0xFF841000
#define ARM_GICC_BASE 0xFF842000
#define ARM_GIC_END 0xFF847FFF
#define GICD_CTLR 0x000
#define GICD_IGROUPR0 0x020
#define GICD_ISENABLER0 0x040
#define GICD_ICENABLER0 0x060
#define GICD_ISPENDR0 0x080
#define GICD_ICPENDR0 0x0A0
#define GICD_ISACTIVER0 0x0C0
#define GICD_ICACTIVER0 0x0E0
#define GICD_IPRIORITYR0 0x100
#define GICD_ITARGETSR0 0x200
#define GICD_ICFGR0 0x300
#define GICD_SGIR 0x3C0
#define GICC_CTLR 0x000
#define GICC_PMR 0x001
#define GICC_IAR 0x003
#define GICC_EOIR 0x004
const static uint32_t ARM_GICD_BASE = 0xFF841000;
const static uint32_t ARM_GICC_BASE = 0xFF842000;
const static uint32_t ARM_GIC_END = 0xFF847FFF;
const static int GICD_CTLR = 0x000;
const static int GICD_IGROUPR0 = 0x020;
const static int GICD_ISENABLER0 = 0x040;
const static int GICD_ICENABLER0 = 0x060;
const static int GICD_ISPENDR0 = 0x080;
const static int GICD_ICPENDR0 = 0x0A0;
const static int GICD_ISACTIVER0 = 0x0C0;
const static int GICD_ICACTIVER0 = 0x0E0;
const static int GICD_IPRIORITYR0 = 0x100;
const static int GICD_ITARGETSR0 = 0x200;
const static int GICD_ICFGR0 = 0x300;
const static int GICD_SGIR = 0x3C0;
const static int GICC_CTLR = 0x000;
const static int GICC_PMR = 0x001;
const static int GICC_IAR = 0x003;
const static int GICC_EOIR = 0x004;
//---------------------------------------------------------------------------
//
// Constant declarations (GIC IRQ)
//
//---------------------------------------------------------------------------
#define GIC_IRQLOCAL0 (16 + 14)
#define GIC_GPIO_IRQ (32 + 116) // GPIO3
const static int GIC_IRQLOCAL0 = (16 + 14);
const static int GIC_GPIO_IRQ = (32 + 116); // GPIO3
//---------------------------------------------------------------------------
//
@ -258,8 +263,8 @@
//---------------------------------------------------------------------------
#define IN GPIO_INPUT
#define OUT GPIO_OUTPUT
#define ON TRUE
#define OFF FALSE
const static int ON = 1;
const static int OFF = 0;
//---------------------------------------------------------------------------
//
@ -301,12 +306,12 @@ const static int SCSI_DELAY_SEND_DATA_DAYNAPORT_US = 100;
// Class definition
//
//---------------------------------------------------------------------------
class GPIOBUS : public BUS
class GPIOBUS final : public BUS
{
public:
// Basic Functions
GPIOBUS()= default;
~GPIOBUS() final = default;
~GPIOBUS() override = default;
// Destructor
bool Init(mode_e mode = mode_e::TARGET) override;
// Initialization

View File

@ -5,27 +5,28 @@
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ GPIO-SCSI bus ]
//
//---------------------------------------------------------------------------
#pragma once
#include <string>
//
// RaSCSI Adapter Aibom version
//
#define CONNECT_DESC "AIBOM PRODUCTS version" // Startup message
const std::string CONNECT_DESC = "AIBOM PRODUCTS version"; // Startup message
// Select signal control mode
const static int SIGNAL_CONTROL_MODE = 2; // SCSI positive logic specification
// Control signal output logic
#define ACT_ON TRUE // ACTIVE SIGNAL ON
#define ENB_ON TRUE // ENABLE SIGNAL ON
#define IND_IN FALSE // INITIATOR SIGNAL INPUT
#define TAD_IN FALSE // TARGET SIGNAL INPUT
#define DTD_IN FALSE // DATA SIGNAL INPUT
#define ACT_ON ON // ACTIVE SIGNAL ON
#define ENB_ON ON // ENABLE SIGNAL ON
#define IND_IN OFF // INITIATOR SIGNAL INPUT
#define TAD_IN OFF // TARGET SIGNAL INPUT
#define DTD_IN OFF // DATA SIGNAL INPUT
// Control signal pin assignment (-1 means no control)
const static int PIN_ACT = 4; // ACTIVE

View File

@ -5,15 +5,18 @@
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ GPIO-SCSI bus ]
//
//---------------------------------------------------------------------------
#pragma once
#include <string>
//
// RaSCSI standard (SCSI logic, standard pin assignment)
//
#define CONNECT_DESC "FULLSPEC" // Startup message
const std::string CONNECT_DESC = "FULLSPEC"; // Startup message
// Select signal control mode
const static int SIGNAL_CONTROL_MODE = 0; // SCSI logical specification
@ -26,11 +29,11 @@ const static int PIN_TAD = 7; // TARGET CTRL DIRECTION
const static int PIN_DTD = 8; // DATA DIRECTION
// Control signal output logic
#define ACT_ON TRUE // ACTIVE SIGNAL ON
#define ENB_ON TRUE // ENABLE SIGNAL ON
#define IND_IN FALSE // INITIATOR SIGNAL INPUT
#define TAD_IN FALSE // TARGET SIGNAL INPUT
#define DTD_IN TRUE // DATA SIGNAL INPUT
#define ACT_ON ON // ACTIVE SIGNAL ON
#define ENB_ON ON // ENABLE SIGNAL ON
#define IND_IN OFF // INITIATOR SIGNAL INPUT
#define TAD_IN OFF // TARGET SIGNAL INPUT
#define DTD_IN ON // DATA SIGNAL INPUT
// SCSI signal pin assignment
const static int PIN_DT0 = 10; // Data 0

View File

@ -5,27 +5,28 @@
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ GPIO-SCSI bus ]
//
//---------------------------------------------------------------------------
#pragma once
#include <string>
//
// RaSCSI Adapter GAMERnium.com version
//
#define CONNECT_DESC "GAMERnium.com version"// Startup message
const std::string CONNECT_DESC = "GAMERnium.com version"; // Startup message
// Select signal control mode
const static int SIGNAL_CONTROL_MODE = 0; // SCSI logical specification
// Control signal output logic
#define ACT_ON TRUE // ACTIVE SIGNAL ON
#define ENB_ON TRUE // ENABLE SIGNAL ON
#define IND_IN FALSE // INITIATOR SIGNAL INPUT
#define TAD_IN FALSE // TARGET SIGNAL INPUT
#define DTD_IN TRUE // DATA SIGNAL INPUT
#define ACT_ON ON // ACTIVE SIGNAL ON
#define ENB_ON ON // ENABLE SIGNAL ON
#define IND_IN OFF // INITIATOR SIGNAL INPUT
#define TAD_IN OFF // TARGET SIGNAL INPUT
#define DTD_IN ON // DATA SIGNAL INPUT
// Control signal pin assignment (-1 means no control)
const static int PIN_ACT = 14; // ACTIVE

View File

@ -5,16 +5,18 @@
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ GPIO-SCSI bus ]
//
//---------------------------------------------------------------------------
#pragma once
#include <string>
//
// RaSCSI standard (SCSI logic, standard pin assignment)
//
#define CONNECT_DESC "STANDARD" // Startup message
const std::string CONNECT_DESC = "STANDARD"; // Startup message
// Select signal control mode
const static int SIGNAL_CONTROL_MODE = 0; // SCSI logical specification
@ -27,11 +29,11 @@ const static int PIN_TAD = -1; // TARGET CTRL DIRECTION
const static int PIN_DTD = -1; // DATA DIRECTION
// Control signal output logic
#define ACT_ON TRUE // ACTIVE SIGNAL ON
#define ENB_ON TRUE // ENABLE SIGNAL ON
#define IND_IN FALSE // INITIATOR SIGNAL INPUT
#define TAD_IN FALSE // TARGET SIGNAL INPUT
#define DTD_IN TRUE // DATA SIGNAL INPUT
#define ACT_ON ON // ACTIVE SIGNAL ON
#define ENB_ON ON // ENABLE SIGNAL ON
#define IND_IN OFF // INITIATOR SIGNAL INPUT
#define TAD_IN OFF // TARGET SIGNAL INPUT
#define DTD_IN ON // DATA SIGNAL INPUT
// SCSI signal pin assignment
const static int PIN_DT0 = 10; // Data 0

View File

@ -12,13 +12,13 @@
//---------------------------------------------------------------------------
#include "hal/systimer.h"
#include <sys/mman.h>
#include "os.h"
#include "hal/gpiobus.h"
#include "config.h"
#include "log.h"
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <array>
//---------------------------------------------------------------------------
//
@ -59,7 +59,7 @@ void SysTimer::Init(uint32_t *syst, uint32_t *armt)
//
// Clock id
// 0x000000004: CORE
uint32_t maxclock[32] = { 32, 0, 0x00030004, 8, 0, 4, 0, 0 };
array<uint32_t, 32> maxclock = { 32, 0, 0x00030004, 8, 0, 4, 0, 0 };
// Save the base address
systaddr = syst;

View File

@ -15,7 +15,7 @@
#include <unordered_set>
#include <unordered_map>
using namespace std;
using namespace std; //NOSONAR Not relevant for rascsi
enum LocalizationKey {
ERROR_AUTHENTICATION,

View File

@ -10,15 +10,16 @@
//
//---------------------------------------------------------------------------
#if !defined(log_h)
#define log_h
#pragma once
#include "spdlog/spdlog.h"
#include "spdlog/sinks/sink.h"
static const int LOGBUF_SIZE = 512;
#define SPDLOGWRAPPER(loglevel, ...) \
{ \
char logbuf[_MAX_FNAME*2]; \
char logbuf[LOGBUF_SIZE]; \
snprintf(logbuf, sizeof(logbuf), __VA_ARGS__); \
spdlog::log(loglevel, logbuf); \
};
@ -29,5 +30,3 @@
#define LOGWARN(...) SPDLOGWRAPPER(spdlog::level::warn, __VA_ARGS__)
#define LOGERROR(...) SPDLOGWRAPPER(spdlog::level::err, __VA_ARGS__)
#define LOGCRITICAL(...) SPDLOGWRAPPER(spdlog::level::critical, __VA_ARGS__)
#endif

View File

@ -11,99 +11,13 @@
//
//---------------------------------------------------------------------------
#if !defined(os_h)
#define os_h
#pragma once
//---------------------------------------------------------------------------
//
// #define
//
//---------------------------------------------------------------------------
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
//---------------------------------------------------------------------------
//
// #include
//
//---------------------------------------------------------------------------
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include <cstdarg>
#include <cstring>
#include <csignal>
#include <cassert>
#include <unistd.h>
#include <utime.h>
#include <fcntl.h>
#include <sched.h>
#include <pthread.h>
#include <iconv.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <poll.h>
#include <dirent.h>
#include <pwd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstdint>
#ifdef __linux
#include <sys/epoll.h>
#include <linux/gpio.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#endif
//---------------------------------------------------------------------------
//
// Basic Macros
//
//---------------------------------------------------------------------------
#if !defined(ASSERT)
#if !defined(NDEBUG)
#define ASSERT(cond) assert(cond)
#else
#define ASSERT(cond) ((void)0)
#endif // NDEBUG
#endif // ASSERT
#define ARRAY_SIZE(x) (sizeof(x)/(sizeof(x[0])))
//---------------------------------------------------------------------------
//
// Basic Type Definitions
//
//---------------------------------------------------------------------------
using BYTE = unsigned char;
using WORD = uint16_t;
using DWORD = uint32_t;
using TCHAR = char;
#if !defined(FALSE)
#define FALSE 0
#endif
#if !defined(TRUE)
#define TRUE 1
#endif
#if !defined(_T)
#define _T(x) x
#endif
static const int _MAX_PATH = 260;
static const int _MAX_DIR = 256;
static const int _MAX_FNAME = 256;
static const int _MAX_EXT = 256;
#endif // os_h

View File

@ -1,198 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include <unistd.h>
#include "os.h"
#include "log.h"
#include "rascsi_interface.pb.h"
#include "localizer.h"
#include "rascsi_exceptions.h"
#include "protobuf_util.h"
using namespace std;
using namespace rascsi_interface;
#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
static const char COMPONENT_SEPARATOR = ':';
static const char KEY_VALUE_SEPARATOR = '=';
const Localizer localizer;
void protobuf_util::ParseParameters(PbDeviceDefinition& device, const string& params)
{
if (!params.empty()) {
if (params.find(KEY_VALUE_SEPARATOR) != string::npos) {
stringstream ss(params);
string p;
while (getline(ss, p, COMPONENT_SEPARATOR)) {
if (!p.empty()) {
size_t separator_pos = p.find(KEY_VALUE_SEPARATOR);
if (separator_pos != string::npos) {
AddParam(device, p.substr(0, separator_pos), string_view(p).substr(separator_pos + 1));
}
}
}
}
// Old style parameters, for backwards compatibility only.
// Only one of these parameters will be used by rascsi, depending on the device type.
else {
AddParam(device, "file", params);
if (params != "bridge" && params != "daynaport" && params != "printer" && params != "services") {
AddParam(device, "interfaces", params);
}
}
}
}
string protobuf_util::GetParam(const PbCommand& command, const string& key)
{
auto map = command.params();
return map[key];
}
string protobuf_util::GetParam(const PbDeviceDefinition& device, const string& key)
{
auto map = device.params();
return map[key];
}
void protobuf_util::AddParam(PbCommand& command, const string& key, string_view value)
{
if (!key.empty() && !value.empty()) {
auto& map = *command.mutable_params();
map[key] = value;
}
}
void protobuf_util::AddParam(PbDevice& device, const string& key,string_view value)
{
if (!key.empty() && !value.empty()) {
auto& map = *device.mutable_params();
map[key] = value;
}
}
void protobuf_util::AddParam(PbDeviceDefinition& device, const string& key, string_view value)
{
if (!key.empty() && !value.empty()) {
auto& map = *device.mutable_params();
map[key] = value;
}
}
//---------------------------------------------------------------------------
//
// Serialize/Deserialize protobuf message: Length followed by the actual data.
// Little endian is assumed.
//
//---------------------------------------------------------------------------
void protobuf_util::SerializeMessage(int fd, const google::protobuf::Message& message)
{
string data;
message.SerializeToString(&data);
// Write the size of the protobuf data as a header
auto size = (int32_t)data.length();
if (write(fd, &size, sizeof(size)) != sizeof(size)) {
throw io_exception("Can't write protobuf message header");
}
// Write the actual protobuf data
if (write(fd, data.data(), size) != size) {
throw io_exception("Can't write protobuf message data");
}
}
void protobuf_util::DeserializeMessage(int fd, google::protobuf::Message& message)
{
// Read the header with the size of the protobuf data
vector<byte> header_buf(4);
if (ReadBytes(fd, header_buf) < header_buf.size()) {
return;
}
size_t size = ((int)header_buf[3] << 24) + ((int)header_buf[2] << 16) + ((int)header_buf[1] << 8) + (int)header_buf[0];
if (size <= 0) {
throw io_exception("Invalid protobuf message header");
}
// Read the binary protobuf data
vector<byte> data_buf(size);
if (ReadBytes(fd, data_buf) < data_buf.size()) {
throw io_exception("Missing protobuf message data");
}
// Create protobuf message
string data((const char *)data_buf.data(), size);
message.ParseFromString(data);
}
size_t protobuf_util::ReadBytes(int fd, vector<byte>& buf)
{
size_t offset = 0;
while (offset < buf.size()) {
ssize_t len = read(fd, &buf[offset], buf.size() - offset);
if (len <= 0) {
return len;
}
offset += len;
}
return offset;
}
bool protobuf_util::ReturnLocalizedError(const CommandContext& context, const LocalizationKey key,
const string& arg1, const string& arg2, const string& arg3)
{
return ReturnLocalizedError(context, key, NO_ERROR_CODE, arg1, arg2, arg3);
}
bool protobuf_util::ReturnLocalizedError(const CommandContext& context, const LocalizationKey key,
const PbErrorCode error_code, const string& arg1, const string& arg2, const string& arg3)
{
// For the logfile always use English
LOGERROR("%s", localizer.Localize(key, "en", arg1, arg2, arg3).c_str())
return ReturnStatus(context, false, localizer.Localize(key, context.locale, arg1, arg2, arg3), error_code, false);
}
bool protobuf_util::ReturnStatus(const CommandContext& context, bool status, const string& msg,
const PbErrorCode error_code, bool log)
{
// Do not log twice if logging has already been done in the localized error handling above
if (log && !status && !msg.empty()) {
LOGERROR("%s", msg.c_str())
}
if (context.fd == -1) {
if (!msg.empty()) {
if (status) {
FPRT(stderr, "Error: ");
FPRT(stderr, "%s", msg.c_str());
FPRT(stderr, "\n");
}
else {
FPRT(stdout, "%s", msg.c_str());
FPRT(stderr, "\n");
}
}
}
else {
PbResult result;
result.set_status(status);
result.set_error_code(error_code);
result.set_msg(msg);
SerializeMessage(context.fd, result);
}
return status;
}

View File

@ -11,7 +11,6 @@
//---------------------------------------------------------------------------
#include "config.h"
#include "os.h"
#include "controllers/controller_manager.h"
#include "devices/device_factory.h"
#include "devices/device.h"
@ -20,7 +19,8 @@
#include "hal/gpiobus.h"
#include "hal/systimer.h"
#include "rascsi_exceptions.h"
#include "protobuf_util.h"
#include "socket_connector.h"
#include "command_util.h"
#include "rascsi_version.h"
#include "rascsi_response.h"
#include "rasutil.h"
@ -28,6 +28,9 @@
#include "rascsi_interface.pb.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include <pthread.h>
#include <netinet/in.h>
#include <csignal>
#include <string>
#include <sstream>
#include <iostream>
@ -39,7 +42,7 @@ using namespace std;
using namespace spdlog;
using namespace rascsi_interface;
using namespace ras_util;
using namespace protobuf_util;
using namespace command_util;
//---------------------------------------------------------------------------
//
@ -60,16 +63,18 @@ static volatile bool active; // Processing flag
shared_ptr<GPIOBUS> bus; // GPIO Bus
int monsocket; // Monitor Socket
pthread_t monthread; // Monitor Thread
pthread_mutex_t ctrl_mutex; // Semaphore for the ctrl array
static void *MonThread(void *param);
pthread_mutex_t ctrl_mutex; // Semaphore for the ctrl array
string current_log_level; // Some versions of spdlog do not support get_log_level()
string access_token;
unordered_set<int> reserved_ids;
DeviceFactory& device_factory = DeviceFactory::instance();
DeviceFactory device_factory;
ControllerManager controller_manager;
RascsiImage rascsi_image;
RascsiResponse rascsi_response(&device_factory, &rascsi_image);
SocketConnector socket_connector;
void DetachAll();
static void *MonThread(void *);
//---------------------------------------------------------------------------
//
@ -97,7 +102,7 @@ void Banner(int argc, char* argv[])
FPRT(stdout,"Powered by XM6 TypeG Technology / ");
FPRT(stdout,"Copyright (C) 2016-2020 GIMONS\n");
FPRT(stdout,"Copyright (C) 2020-2022 Contributors to the RaSCSI Reloaded project\n");
FPRT(stdout,"Connect type: %s\n", CONNECT_DESC);
FPRT(stdout,"Connect type: %s\n", CONNECT_DESC.c_str());
if ((argc > 1 && strcmp(argv[1], "-h") == 0) ||
(argc > 1 && strcmp(argv[1], "--help") == 0)){
@ -254,7 +259,7 @@ bool ReadAccessToken(const char *filename)
string ValidateLunSetup(const PbCommand& command)
{
// Mapping of available LUNs (bit vector) to devices
map<uint32_t, uint32_t> luns;
unordered_map<uint32_t, uint32_t> luns;
// Collect LUN bit vectors of new devices
for (const auto& device : command.devices()) {
@ -262,7 +267,7 @@ string ValidateLunSetup(const PbCommand& command)
}
// Collect LUN bit vectors of existing devices
for (const auto device : device_factory.GetAllDevices()) {
for (const auto& device : device_factory.GetAllDevices()) {
luns[device->GetId()] |= 1 << device->GetLun();
}
@ -404,12 +409,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
// If no filename was provided the medium is considered removed
auto file_support = dynamic_cast<FileSupport *>(device);
if (file_support != nullptr) {
device->SetRemoved(filename.empty());
}
else {
device->SetRemoved(false);
}
device->SetRemoved(file_support != nullptr ? filename.empty() : false);
device->SetLun(unit);
@ -431,14 +431,14 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
if (pb_device.block_size()) {
auto disk = dynamic_cast<Disk *>(device);
if (disk != nullptr && disk->IsSectorSizeConfigurable()) {
if (!disk->SetConfiguredSectorSize(pb_device.block_size())) {
device_factory.DeleteDevice(device);
if (!disk->SetConfiguredSectorSize(device_factory, pb_device.block_size())) {
device_factory.DeleteDevice(*device);
return ReturnLocalizedError(context, ERROR_BLOCK_SIZE, to_string(pb_device.block_size()));
}
}
else {
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
return ReturnLocalizedError(context, ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, PbDeviceType_Name(type));
}
@ -446,7 +446,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
// File check (type is HD, for removable media drives, CD and MO the medium (=file) may be inserted later
if (file_support != nullptr && !device->IsRemovable() && filename.empty()) {
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
return ReturnLocalizedError(context, ERROR_MISSING_FILENAME, PbDeviceType_Name(type));
}
@ -459,7 +459,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
int id;
int unit;
if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) {
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
return ReturnLocalizedError(context, ERROR_IMAGE_IN_USE, filename, to_string(id), to_string(unit));
}
@ -473,7 +473,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
filepath.SetPath(string(rascsi_image.GetDefaultImageFolder() + "/" + filename).c_str());
if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) {
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
return ReturnLocalizedError(context, ERROR_IMAGE_IN_USE, filename, to_string(id), to_string(unit));
}
@ -482,7 +482,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
}
}
catch(const io_exception& e) {
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
return ReturnLocalizedError(context, ERROR_FILE_OPEN, initial_filename, e.get_msg());
}
@ -498,7 +498,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
// Stop the dry run here, before permanently modifying something
if (dryRun) {
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
return true;
}
@ -508,7 +508,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
params.erase("file");
}
if (!device->Init(params)) {
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
return ReturnLocalizedError(context, ERROR_INITIALIZATION, PbDeviceType_Name(type), to_string(id), to_string(unit));
}
@ -563,7 +563,7 @@ bool Detach(const CommandContext& context, PrimaryDevice *device, bool dryRun)
return ReturnLocalizedError(context, ERROR_DETACH);
}
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
pthread_mutex_unlock(&ctrl_mutex);
LOGINFO("Detached %s device with ID %d, unit %d", type.c_str(), id, lun)
@ -598,7 +598,7 @@ bool Insert(const CommandContext& context, const PbDeviceDefinition& pb_device,
if (pb_device.block_size()) {
if (disk != nullptr&& disk->IsSectorSizeConfigurable()) {
if (!disk->SetConfiguredSectorSize(pb_device.block_size())) {
if (!disk->SetConfiguredSectorSize(device_factory, pb_device.block_size())) {
return ReturnLocalizedError(context, ERROR_BLOCK_SIZE, to_string(pb_device.block_size()));
}
}
@ -869,7 +869,7 @@ bool ProcessCmd(const CommandContext& context, const PbCommand& command)
}
// Remember the list of reserved files, than run the dry run
const auto reserved_files = FileSupport::GetReservedFiles();
const auto& reserved_files = FileSupport::GetReservedFiles();
for (const auto& device : command.devices()) {
if (!ProcessCmd(context, device, command, true)) {
// Dry run failed, restore the file list
@ -897,7 +897,7 @@ bool ProcessCmd(const CommandContext& context, const PbCommand& command)
PbCommand command;
PbResult result;
rascsi_response.GetDevicesInfo(result, command);
SerializeMessage(context.fd, result);
socket_connector.SerializeMessage(context.fd, result);
return true;
}
@ -935,7 +935,7 @@ void ShutDown(const CommandContext& context, const string& mode) {
if (mode == "rascsi") {
LOGINFO("RaSCSI shutdown requested");
SerializeMessage(context.fd, result);
socket_connector.SerializeMessage(context.fd, result);
TerminationHandler(0);
}
@ -949,7 +949,7 @@ void ShutDown(const CommandContext& context, const string& mode) {
if (mode == "system") {
LOGINFO("System shutdown requested")
SerializeMessage(context.fd, result);
socket_connector.SerializeMessage(context.fd, result);
DetachAll();
@ -960,7 +960,7 @@ void ShutDown(const CommandContext& context, const string& mode) {
else if (mode == "reboot") {
LOGINFO("System reboot requested")
SerializeMessage(context.fd, result);
socket_connector.SerializeMessage(context.fd, result);
DetachAll();
@ -1131,9 +1131,8 @@ bool ParseArgument(int argc, char* argv[], int& port)
// Attach all specified devices
command.set_operation(ATTACH);
CommandContext context;
context.fd = -1;
context.locale = locale;
Localizer localizer;
CommandContext context(&socket_connector, &localizer, -1, locale);
if (!ProcessCmd(context, command)) {
return false;
}
@ -1156,6 +1155,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
//---------------------------------------------------------------------------
void FixCpu(int cpu)
{
#ifdef __linux
// Get the number of CPUs
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
@ -1168,6 +1168,153 @@ void FixCpu(int cpu)
CPU_SET(cpu, &cpuset);
sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
}
#endif
}
static bool ExecuteCommand(PbCommand& command, CommandContext& context)
{
context.locale = GetParam(command, "locale");
if (context.locale.empty()) {
context.locale = "en";
}
if (!access_token.empty() && access_token != GetParam(command, "token")) {
return ReturnLocalizedError(context, ERROR_AUTHENTICATION, UNAUTHORIZED);
}
if (!PbOperation_IsValid(command.operation())) {
LOGERROR("Received unknown command with operation opcode %d", command.operation())
return ReturnLocalizedError(context, ERROR_OPERATION, UNKNOWN_OPERATION);
}
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str())
PbResult result;
switch(command.operation()) {
case LOG_LEVEL: {
string log_level = GetParam(command, "level");
if (bool status = SetLogLevel(log_level); !status) {
ReturnLocalizedError(context, ERROR_LOG_LEVEL, log_level);
}
else {
ReturnStatus(context);
}
break;
}
case DEFAULT_FOLDER: {
if (string status = rascsi_image.SetDefaultImageFolder(GetParam(command, "folder")); !status.empty()) {
ReturnStatus(context, false, status);
}
else {
ReturnStatus(context);
}
break;
}
case DEVICES_INFO: {
rascsi_response.GetDevicesInfo(result, command);
socket_connector.SerializeMessage(context.fd, result);
break;
}
case DEVICE_TYPES_INFO: {
result.set_allocated_device_types_info(rascsi_response.GetDeviceTypesInfo(result));
socket_connector.SerializeMessage(context.fd, result);
break;
}
case SERVER_INFO: {
result.set_allocated_server_info(rascsi_response.GetServerInfo(
result, reserved_ids, current_log_level, GetParam(command, "folder_pattern"),
GetParam(command, "file_pattern"), rascsi_image.GetDepth()));
socket_connector.SerializeMessage(context.fd, result);
break;
}
case VERSION_INFO: {
result.set_allocated_version_info(rascsi_response.GetVersionInfo(result));
socket_connector.SerializeMessage(context.fd, result);
break;
}
case LOG_LEVEL_INFO: {
result.set_allocated_log_level_info(rascsi_response.GetLogLevelInfo(result, current_log_level));
socket_connector.SerializeMessage(context.fd, result);
break;
}
case DEFAULT_IMAGE_FILES_INFO: {
result.set_allocated_image_files_info(rascsi_response.GetAvailableImages(result,
GetParam(command, "folder_pattern"), GetParam(command, "file_pattern"),
rascsi_image.GetDepth()));
socket_connector.SerializeMessage(context.fd, result);
break;
}
case IMAGE_FILE_INFO: {
if (string filename = GetParam(command, "file"); filename.empty()) {
ReturnLocalizedError(context, ERROR_MISSING_FILENAME);
}
else {
auto image_file = make_unique<PbImageFile>();
bool status = rascsi_response.GetImageFile(image_file.get(), filename);
if (status) {
result.set_status(true);
result.set_allocated_image_file_info(image_file.get());
socket_connector.SerializeMessage(context.fd, result);
}
else {
ReturnLocalizedError(context, ERROR_IMAGE_FILE_INFO);
}
}
break;
}
case NETWORK_INTERFACES_INFO: {
result.set_allocated_network_interfaces_info(rascsi_response.GetNetworkInterfacesInfo(result));
socket_connector.SerializeMessage(context.fd, result);
break;
}
case MAPPING_INFO: {
result.set_allocated_mapping_info(rascsi_response.GetMappingInfo(result));
socket_connector.SerializeMessage(context.fd, result);
break;
}
case OPERATION_INFO: {
result.set_allocated_operation_info(rascsi_response.GetOperationInfo(result,
rascsi_image.GetDepth()));
socket_connector.SerializeMessage(context.fd, result);
break;
}
case RESERVED_IDS_INFO: {
result.set_allocated_reserved_ids_info(rascsi_response.GetReservedIds(result, reserved_ids));
socket_connector.SerializeMessage(context.fd, result);
break;
}
case SHUT_DOWN: {
ShutDown(context, GetParam(command, "mode"));
break;
}
default: {
// Wait until we become idle
while (active) {
usleep(500 * 1000);
}
ProcessCmd(context, command);
break;
}
}
return true;
}
//---------------------------------------------------------------------------
@ -1194,175 +1341,19 @@ static void *MonThread(void *) //NOSONAR The pointer cannot be const void * beca
listen(monsocket, 1);
while (true) {
CommandContext context;
context.fd = -1;
Localizer localizer;
CommandContext context(&socket_connector, &localizer, -1, "");
try {
// Wait for connection
sockaddr_in client;
socklen_t socklen = sizeof(client);
memset(&client, 0, socklen);
context.fd = accept(monsocket, (sockaddr*)&client, &socklen);
if (context.fd < 0) {
throw io_exception("accept() failed");
}
// Read magic string
vector<byte> magic(6);
size_t bytes_read = ReadBytes(context.fd, magic);
if (!bytes_read) {
continue;
}
if (bytes_read != magic.size() || memcmp(magic.data(), "RASCSI", magic.size())) {
throw io_exception("Invalid magic");
}
// Fetch the command
PbCommand command;
DeserializeMessage(context.fd, command);
context.locale = GetParam(command, "locale");
if (context.locale.empty()) {
context.locale = "en";
}
if (!access_token.empty() && access_token != GetParam(command, "token")) {
ReturnLocalizedError(context, ERROR_AUTHENTICATION, UNAUTHORIZED);
context.fd = socket_connector.ReadCommand(command, monsocket);
if (context.fd == -1) {
continue;
}
if (!PbOperation_IsValid(command.operation())) {
LOGERROR("Received unknown command with operation opcode %d", command.operation())
ReturnLocalizedError(context, ERROR_OPERATION, UNKNOWN_OPERATION);
if (!ExecuteCommand(command, context)) {
continue;
}
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str())
PbResult result;
switch(command.operation()) {
case LOG_LEVEL: {
string log_level = GetParam(command, "level");
if (bool status = SetLogLevel(log_level); !status) {
ReturnLocalizedError(context, ERROR_LOG_LEVEL, log_level);
}
else {
ReturnStatus(context);
}
break;
}
case DEFAULT_FOLDER: {
if (string status = rascsi_image.SetDefaultImageFolder(GetParam(command, "folder")); !status.empty()) {
ReturnStatus(context, false, status);
}
else {
ReturnStatus(context);
}
break;
}
case DEVICES_INFO: {
rascsi_response.GetDevicesInfo(result, command);
SerializeMessage(context.fd, result);
break;
}
case DEVICE_TYPES_INFO: {
result.set_allocated_device_types_info(rascsi_response.GetDeviceTypesInfo(result));
SerializeMessage(context.fd, result);
break;
}
case SERVER_INFO: {
result.set_allocated_server_info(rascsi_response.GetServerInfo(
result, reserved_ids, current_log_level, GetParam(command, "folder_pattern"),
GetParam(command, "file_pattern"), rascsi_image.GetDepth()));
SerializeMessage(context.fd, result);
break;
}
case VERSION_INFO: {
result.set_allocated_version_info(rascsi_response.GetVersionInfo(result));
SerializeMessage(context.fd, result);
break;
}
case LOG_LEVEL_INFO: {
result.set_allocated_log_level_info(rascsi_response.GetLogLevelInfo(result, current_log_level));
SerializeMessage(context.fd, result);
break;
}
case DEFAULT_IMAGE_FILES_INFO: {
result.set_allocated_image_files_info(rascsi_response.GetAvailableImages(result,
GetParam(command, "folder_pattern"), GetParam(command, "file_pattern"),
rascsi_image.GetDepth()));
SerializeMessage(context.fd, result);
break;
}
case IMAGE_FILE_INFO: {
if (string filename = GetParam(command, "file"); filename.empty()) {
ReturnLocalizedError(context, ERROR_MISSING_FILENAME);
}
else {
auto image_file = make_unique<PbImageFile>();
bool status = rascsi_response.GetImageFile(image_file.get(), filename);
if (status) {
result.set_status(true);
result.set_allocated_image_file_info(image_file.get());
SerializeMessage(context.fd, result);
}
else {
ReturnLocalizedError(context, ERROR_IMAGE_FILE_INFO);
}
}
break;
}
case NETWORK_INTERFACES_INFO: {
result.set_allocated_network_interfaces_info(rascsi_response.GetNetworkInterfacesInfo(result));
SerializeMessage(context.fd, result);
break;
}
case MAPPING_INFO: {
result.set_allocated_mapping_info(rascsi_response.GetMappingInfo(result));
SerializeMessage(context.fd, result);
break;
}
case OPERATION_INFO: {
result.set_allocated_operation_info(rascsi_response.GetOperationInfo(result,
rascsi_image.GetDepth()));
SerializeMessage(context.fd, result);
break;
}
case RESERVED_IDS_INFO: {
result.set_allocated_reserved_ids_info(rascsi_response.GetReservedIds(result, reserved_ids));
SerializeMessage(context.fd, result);
break;
}
case SHUT_DOWN: {
ShutDown(context, GetParam(command, "mode"));
break;
}
default: {
// Wait until we become idle
while (active) {
usleep(500 * 1000);
}
ProcessCmd(context, command);
break;
}
}
}
catch(const io_exception& e) {
LOGWARN("%s", e.get_msg().c_str())

View File

@ -13,7 +13,7 @@
#include <exception>
#include <string>
using namespace std;
using namespace std; //NOSONAR Not relevant for rascsi
class illegal_argument_exception final : public exception {
private:
@ -41,7 +41,7 @@ class file_not_found_exception : public io_exception {
using io_exception::io_exception;
};
class scsi_error_exception : public exception {
class scsi_error_exception final : public exception {
private:
scsi_defs::sense_key sense_key;
scsi_defs::asc asc;
@ -52,7 +52,7 @@ public:
scsi_defs::asc asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
scsi_defs::status status = scsi_defs::status::CHECK_CONDITION)
: sense_key(sense_key), asc(asc), status(status) {}
~scsi_error_exception() final = default;
~scsi_error_exception() override = default;
scsi_defs::sense_key get_sense_key() const { return sense_key; }
scsi_defs::asc get_asc() const { return asc; }

View File

@ -3,45 +3,34 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include <unistd.h>
#include <sys/sendfile.h>
#include "os.h"
#include <pwd.h>
#include "log.h"
#include "filepath.h"
#include "spdlog/spdlog.h"
#include "devices/file_support.h"
#include "protobuf_util.h"
#include "command_util.h"
#include "rascsi_image.h"
#include <string>
#include <array>
#include <filesystem>
#ifdef __linux
#include <sys/sendfile.h>
#endif
using namespace std;
using namespace spdlog;
using namespace rascsi_interface;
using namespace protobuf_util;
#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
using namespace command_util;
RascsiImage::RascsiImage()
{
// ~/images is the default folder for device image files, for the root user it is /home/pi/images
int uid = getuid();
if (auto sudo_user = getenv("SUDO_UID"); sudo_user) {
uid = stoi(sudo_user);
}
const passwd *passwd = getpwuid(uid);
if (uid && passwd) {
default_image_folder = passwd->pw_dir;
default_image_folder += "/images";
}
else {
default_image_folder = "/home/pi/images";
}
default_image_folder = GetHomeDir() + "/images";
}
bool RascsiImage::CheckDepth(string_view filename) const
@ -79,17 +68,7 @@ string RascsiImage::SetDefaultImageFolder(const string& f)
// If a relative path is specified the path is assumed to be relative to the user's home directory
if (folder[0] != '/') {
int uid = getuid();
if (const char *sudo_user = getenv("SUDO_UID"); sudo_user) {
uid = stoi(sudo_user);
}
const passwd *passwd = getpwuid(uid);
if (passwd) {
folder = passwd->pw_dir;
folder += "/";
folder += f;
}
folder = GetHomeDir() + "/" + f;
}
else {
if (folder.find("/home/") != 0) {
@ -173,6 +152,13 @@ bool RascsiImage::CreateImage(const CommandContext& context, const PbCommand& co
return ReturnStatus(context, false, "Can't create image file '" + full_filename + "': " + string(strerror(errno)));
}
#ifndef __linux
close(image_fd);
unlink(full_filename.c_str());
return false;
#else
if (fallocate(image_fd, 0, 0, len)) {
close(image_fd);
@ -187,6 +173,7 @@ bool RascsiImage::CreateImage(const CommandContext& context, const PbCommand& co
"' with a size of " + to_string(len) + " bytes").c_str())
return ReturnStatus(context);
#endif
}
bool RascsiImage::DeleteImage(const CommandContext& context, const PbCommand& command) const
@ -347,6 +334,14 @@ bool RascsiImage::CopyImage(const CommandContext& context, const PbCommand& comm
return ReturnStatus(context, false, "Can't open destination image file '" + to + "': " + string(strerror(errno)));
}
#ifndef __linux
close(fd_dst);
close(fd_src);
unlink(to.c_str());
return false;
#else
if (sendfile(fd_dst, fd_src, nullptr, st.st_size) == -1) {
close(fd_dst);
close(fd_src);
@ -362,6 +357,7 @@ bool RascsiImage::CopyImage(const CommandContext& context, const PbCommand& comm
LOGINFO("Copied image file '%s' to '%s'", from.c_str(), to.c_str())
return ReturnStatus(context);
#endif
}
bool RascsiImage::SetImagePermissions(const CommandContext& context, const PbCommand& command) const
@ -397,3 +393,22 @@ bool RascsiImage::SetImagePermissions(const CommandContext& context, const PbCom
return ReturnStatus(context);
}
string RascsiImage::GetHomeDir() const
{
int uid = getuid();
if (const char *sudo_user = getenv("SUDO_UID"); sudo_user != nullptr) {
uid = stoi(sudo_user);
}
passwd pwd = {};
passwd *p_pwd;
array<char, 256> pwbuf;
if (uid && !getpwuid_r(uid, &pwd, pwbuf.data(), pwbuf.size(), &p_pwd)) {
return pwd.pw_dir;
}
else {
return "/home/pi";
}
}

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
@ -13,8 +13,7 @@
#include "command_context.h"
#include <string>
using namespace std;
using namespace rascsi_interface;
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi
class RascsiImage
{
@ -41,6 +40,9 @@ public:
private:
string GetHomeDir() const;
string default_image_folder;
int depth = 1;
};

View File

@ -7,17 +7,18 @@
//
//---------------------------------------------------------------------------
#include <dirent.h>
#include "devices/file_support.h"
#include "devices/disk.h"
#include "devices/device_factory.h"
#include "protobuf_util.h"
#include "command_util.h"
#include "rascsi_version.h"
#include "rascsi_interface.pb.h"
#include "rascsi_image.h"
#include "rascsi_response.h"
using namespace rascsi_interface;
using namespace protobuf_util;
using namespace command_util;
PbDeviceProperties *RascsiResponse::GetDeviceProperties(const Device *device)
{
@ -55,7 +56,7 @@ void RascsiResponse::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_inf
type_properties->set_type(type);
const PrimaryDevice *device = device_factory->CreateDevice(type, "", -1);
type_properties->set_allocated_properties(GetDeviceProperties(device));
device_factory->DeleteDevice(device); //NOSONAR The alloced memory is managed by protobuf
device_factory->DeleteDevice(*device); //NOSONAR The alloced memory is managed by protobuf
}
void RascsiResponse::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info)

View File

@ -14,8 +14,7 @@
#include <list>
#include <string>
using namespace std;
using namespace rascsi_interface;
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi
class DeviceFactory;
class RascsiImage;

View File

@ -9,6 +9,7 @@
//---------------------------------------------------------------------------
#include "rascsi_version.h"
#include <sstream>
#include <iomanip>
// The following should be updated for each release

View File

@ -10,12 +10,13 @@
//
//---------------------------------------------------------------------------
#include "os.h"
#include <unistd.h>
#include "rascsi_version.h"
#include "protobuf_util.h"
#include "command_util.h"
#include "rasutil.h"
#include "rasctl_commands.h"
#include "rascsi_interface.pb.h"
#include <unistd.h>
#include <clocale>
#include <iostream>
#include <list>
@ -26,7 +27,7 @@ static const char COMPONENT_SEPARATOR = ':';
using namespace std;
using namespace rascsi_interface;
using namespace ras_util;
using namespace protobuf_util;
using namespace command_util;
PbOperation ParseOperation(const string& operation)
{
@ -370,8 +371,7 @@ int main(int argc, char* argv[])
}
}
// TODO For macos only 'if (optind != argc)' appears to work, but then non-argument options do not reject arguments
// For macos only 'if (optind != argc)' appears to work, but then non-argument options do not reject arguments
if (optopt) {
exit(EXIT_FAILURE);
}
@ -388,84 +388,7 @@ int main(int argc, char* argv[])
ParseParameters(*device, param);
RasctlCommands rasctl_commands(command, hostname, port, token, locale);
switch(command.operation()) {
case LOG_LEVEL:
rasctl_commands.CommandLogLevel(log_level);
break;
case DEFAULT_FOLDER:
rasctl_commands.CommandDefaultImageFolder(default_folder);
break;
case RESERVE_IDS:
rasctl_commands.CommandReserveIds(reserved_ids);
break;
case CREATE_IMAGE:
rasctl_commands.CommandCreateImage(image_params);
break;
case DELETE_IMAGE:
rasctl_commands.CommandDeleteImage(image_params);
break;
case RENAME_IMAGE:
rasctl_commands.CommandRenameImage(image_params);
break;
case COPY_IMAGE:
rasctl_commands.CommandCopyImage(image_params);
break;
case DEVICES_INFO:
rasctl_commands.CommandDeviceInfo();
break;
case DEVICE_TYPES_INFO:
rasctl_commands.CommandDeviceTypesInfo();
break;
case VERSION_INFO:
rasctl_commands.CommandVersionInfo();
break;
case SERVER_INFO:
rasctl_commands.CommandServerInfo();
break;
case DEFAULT_IMAGE_FILES_INFO:
rasctl_commands.CommandDefaultImageFilesInfo();
break;
case IMAGE_FILE_INFO:
rasctl_commands.CommandImageFileInfo(filename);
break;
case NETWORK_INTERFACES_INFO:
rasctl_commands.CommandNetworkInterfacesInfo();
break;
case LOG_LEVEL_INFO:
rasctl_commands.CommandLogLevelInfo();
break;
case RESERVED_IDS_INFO:
rasctl_commands.CommandReservedIdsInfo();
break;
case MAPPING_INFO:
rasctl_commands.CommandMappingInfo();
break;
case OPERATION_INFO:
rasctl_commands.CommandOperationInfo();
break;
default:
rasctl_commands.SendCommand();
break;
}
rasctl_commands.Execute(log_level, default_folder, reserved_ids, image_params, filename);
exit(EXIT_SUCCESS);
}

View File

@ -3,26 +3,27 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include <netdb.h>
#include "os.h"
#include "rascsi_exceptions.h"
#include "protobuf_util.h"
#include "socket_connector.h"
#include "command_util.h"
#include "rasutil.h"
#include "rasctl_commands.h"
#include "rascsi_interface.pb.h"
#include <unistd.h>
#include <netdb.h>
#include <iostream>
#include <list>
// Separator for the INQUIRY name components
static const char COMPONENT_SEPARATOR = ':';
using namespace std;
using namespace rascsi_interface;
using namespace protobuf_util;
using namespace command_util;
// Separator for the INQUIRY name components
static const char COMPONENT_SEPARATOR = ':';
RasctlCommands::RasctlCommands(const PbCommand& command, const string& hostname, int port, const string& token,
const string& locale)
@ -30,6 +31,88 @@ RasctlCommands::RasctlCommands(const PbCommand& command, const string& hostname,
{
}
void RasctlCommands::Execute(const string& log_level, const string& default_folder, const string& reserved_ids,
const string& image_params, const string& filename)
{
switch(command.operation()) {
case LOG_LEVEL:
CommandLogLevel(log_level);
break;
case DEFAULT_FOLDER:
CommandDefaultImageFolder(default_folder);
break;
case RESERVE_IDS:
CommandReserveIds(reserved_ids);
break;
case CREATE_IMAGE:
CommandCreateImage(image_params);
break;
case DELETE_IMAGE:
CommandDeleteImage(image_params);
break;
case RENAME_IMAGE:
CommandRenameImage(image_params);
break;
case COPY_IMAGE:
CommandCopyImage(image_params);
break;
case DEVICES_INFO:
CommandDeviceInfo();
break;
case DEVICE_TYPES_INFO:
CommandDeviceTypesInfo();
break;
case VERSION_INFO:
CommandVersionInfo();
break;
case SERVER_INFO:
CommandServerInfo();
break;
case DEFAULT_IMAGE_FILES_INFO:
CommandDefaultImageFilesInfo();
break;
case IMAGE_FILE_INFO:
CommandImageFileInfo(filename);
break;
case NETWORK_INTERFACES_INFO:
CommandNetworkInterfacesInfo();
break;
case LOG_LEVEL_INFO:
CommandLogLevelInfo();
break;
case RESERVED_IDS_INFO:
CommandReservedIdsInfo();
break;
case MAPPING_INFO:
CommandMappingInfo();
break;
case OPERATION_INFO:
CommandOperationInfo();
break;
default:
SendCommand();
break;
}
}
void RasctlCommands::SendCommand()
{
if (!token.empty()) {
@ -68,7 +151,7 @@ void RasctlCommands::SendCommand()
throw io_exception("Can't write magic");
}
SerializeMessage(fd, command);
socket_connector.SerializeMessage(fd, command);
}
catch(const io_exception& e) {
cerr << "Error: " << e.get_msg() << endl;
@ -82,7 +165,7 @@ void RasctlCommands::SendCommand()
// Receive result
try {
DeserializeMessage(fd, result);
socket_connector.DeserializeMessage(fd, result);
if (!result.status()) {
throw io_exception(result.msg());

View File

@ -9,12 +9,12 @@
#pragma once
#include "socket_connector.h"
#include "rascsi_interface.pb.h"
#include "rasctl_display.h"
#include <string>
using namespace std;
using namespace rascsi_interface;
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi
class RasctlCommands
{
@ -25,8 +25,12 @@ public:
RasctlCommands(RasctlCommands&) = delete;
RasctlCommands& operator=(const RasctlCommands&) = delete;
void SendCommand();
void Execute(const string&, const string&, const string&, const string&, const string&);
void CommandDevicesInfo();
private:
void CommandLogLevel(const string&);
void CommandReserveIds(const string&);
void CommandCreateImage(const string&);
@ -45,9 +49,9 @@ public:
void CommandReservedIdsInfo();
void CommandMappingInfo();
void CommandOperationInfo();
void SendCommand();
private:
SocketConnector socket_connector;
PbCommand command;
string hostname;
int port;

View File

@ -11,11 +11,11 @@
#include "rascsi_interface.pb.h"
using namespace rascsi_interface;
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi
class RasctlDisplay
{
public:
friend class RasctlCommands;
RasctlDisplay() = default;
~RasctlDisplay() = default;

View File

@ -10,6 +10,8 @@
//---------------------------------------------------------------------------
#include <cerrno>
#include <csignal>
#include <unistd.h>
#include "os.h"
#include "fileio.h"
#include "filepath.h"

View File

@ -14,7 +14,7 @@
#include "os.h"
#include <array>
using namespace std;
using namespace std; //NOSONAR Not relevant for rascsi
//===========================================================================
//
@ -32,7 +32,7 @@ public:
};
// Phase definitions
enum class phase_t : BYTE {
enum class phase_t : int {
busfree,
arbitration,
selection,

View File

@ -15,6 +15,7 @@
#include "spdlog/spdlog.h"
#include <sys/time.h>
#include <climits>
#include <csignal>
#include <sstream>
#include <iostream>
#include <getopt.h>
@ -24,6 +25,8 @@
using namespace std;
static const int _MAX_FNAME = 256;
//---------------------------------------------------------------------------
//
// Variable declarations
@ -151,7 +154,7 @@ void Banner(int, char *[])
else
{
LOGINFO("Reading live data from the GPIO pins")
LOGINFO(" Connection type : %s", CONNECT_DESC)
LOGINFO(" Connection type : %s", CONNECT_DESC.c_str())
}
LOGINFO(" Data buffer size: %u", buff_size)
LOGINFO(" ")
@ -231,7 +234,7 @@ void Reset()
// Pin the thread to a specific CPU (Only applies to Linux)
//
//---------------------------------------------------------------------------
#ifdef __linux__
#ifdef __linux
void FixCpu(int cpu)
{
// Get the number of CPUs
@ -323,7 +326,7 @@ int main(int argc, char *argv[])
// Reset
Reset();
#ifdef __linux__
#ifdef __linux
// Set the affinity to a specific processor core
FixCpu(3);

View File

@ -0,0 +1,108 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "rascsi_interface.pb.h"
#include "rascsi_exceptions.h"
#include "socket_connector.h"
#include <unistd.h>
#include <netinet/in.h>
#include <sstream>
using namespace std;
using namespace rascsi_interface;
int SocketConnector::ReadCommand(PbCommand& command, int socket) const
{
// Wait for connection
sockaddr_in client;
socklen_t socklen = sizeof(client);
memset(&client, 0, socklen);
int fd = accept(socket, (sockaddr*)&client, &socklen);
if (fd < 0) {
throw io_exception("accept() failed");
}
// Read magic string
vector<byte> magic(6);
size_t bytes_read = ReadBytes(fd, magic);
if (!bytes_read) {
return -1;
}
if (bytes_read != magic.size() || memcmp(magic.data(), "RASCSI", magic.size())) {
throw io_exception("Invalid magic");
}
// Fetch the command
DeserializeMessage(fd, command);
return fd;
}
//---------------------------------------------------------------------------
//
// Serialize/Deserialize protobuf message: Length followed by the actual data.
// Little endian is assumed.
//
//---------------------------------------------------------------------------
void SocketConnector::SerializeMessage(int fd, const google::protobuf::Message& message) const
{
string data;
message.SerializeToString(&data);
// Write the size of the protobuf data as a header
auto size = (int32_t)data.length();
if (write(fd, &size, sizeof(size)) != sizeof(size)) {
throw io_exception("Can't write protobuf message header");
}
// Write the actual protobuf data
if (write(fd, data.data(), size) != size) {
throw io_exception("Can't write protobuf message data");
}
}
void SocketConnector::DeserializeMessage(int fd, google::protobuf::Message& message) const
{
// Read the header with the size of the protobuf data
vector<byte> header_buf(4);
if (ReadBytes(fd, header_buf) < header_buf.size()) {
return;
}
size_t size = ((int)header_buf[3] << 24) + ((int)header_buf[2] << 16) + ((int)header_buf[1] << 8) + (int)header_buf[0];
if (size <= 0) {
throw io_exception("Invalid protobuf message header");
}
// Read the binary protobuf data
vector<byte> data_buf(size);
if (ReadBytes(fd, data_buf) < data_buf.size()) {
throw io_exception("Missing protobuf message data");
}
// Create protobuf message
string data((const char *)data_buf.data(), size);
message.ParseFromString(data);
}
size_t SocketConnector::ReadBytes(int fd, vector<byte>& buf) const
{
size_t offset = 0;
while (offset < buf.size()) {
ssize_t len = read(fd, &buf[offset], buf.size() - offset);
if (len <= 0) {
return len;
}
offset += len;
}
return offset;
}

View File

@ -0,0 +1,33 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
// Helper for serializing/deserializing protobuf messages to/fromn sockets
//
//---------------------------------------------------------------------------
#pragma once
#include "google/protobuf/message.h"
#include "rascsi_interface.pb.h"
#include "command_context.h"
#include "localizer.h"
#include <string>
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi
class SocketConnector
{
public:
SocketConnector() = default;
~SocketConnector() = default;
int ReadCommand(PbCommand&, int) const;
void SerializeMessage(int, const google::protobuf::Message&) const;
void DeserializeMessage(int, google::protobuf::Message&) const;
size_t ReadBytes(int, vector<byte>&) const;
};

View File

@ -10,24 +10,55 @@
#include "testing.h"
#include "controllers/abstract_controller.h"
TEST(AbstractControllerTest, AbstractController)
TEST(AbstractControllerTest, DeviceLunLifeCycle)
{
const int ID = 1;
const int LUN = 4;
const int INITIATOR_ID = 7;
const int UNKNOWN_INITIATOR_ID = -1;
MockAbstractController controller(ID);
MockPrimaryDevice device;
device.SetLun(LUN);
EXPECT_TRUE(controller.AddDevice(&device));
EXPECT_EQ(ID, controller.GetTargetId());
EXPECT_TRUE(controller.AddDevice(&device));
EXPECT_TRUE(controller.HasDeviceForLun(LUN));
EXPECT_FALSE(controller.HasDeviceForLun(0));
EXPECT_EQ(&device, controller.GetDeviceForLun(LUN));
EXPECT_EQ(nullptr, controller.GetDeviceForLun(0));
EXPECT_TRUE(controller.DeleteDevice(&device));
}
TEST(AbstractControllerTest, ExtractInitiatorId)
{
const int ID = 1;
const int INITIATOR_ID = 7;
const int UNKNOWN_INITIATOR_ID = -1;
MockAbstractController controller(ID);
EXPECT_EQ(INITIATOR_ID, controller.ExtractInitiatorId((1 << INITIATOR_ID) | ( 1 << ID)));
EXPECT_EQ(UNKNOWN_INITIATOR_ID, controller.ExtractInitiatorId(1 << ID));
}
TEST(AbstractControllerTest, GetOpcode)
{
MockAbstractController controller(0);
controller.ctrl.cmd.resize(1);
controller.ctrl.cmd[0] = 0x12;
EXPECT_EQ(0x12, (int)controller.GetOpcode());
}
TEST(AbstractControllerTest, GetLun)
{
const int LUN = 3;
MockAbstractController controller(0);
controller.ctrl.cmd.resize(2);
controller.ctrl.cmd[1] = LUN << 5;
EXPECT_EQ(LUN, controller.GetLun());
}

View File

@ -0,0 +1,61 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "testing.h"
#include "rascsi_interface.pb.h"
#include "command_util.h"
using namespace rascsi_interface;
using namespace command_util;
void TestSpecialDevice(const string& name)
{
PbDeviceDefinition device;
ParseParameters(device, name);
EXPECT_EQ(name, GetParam(device, "file"));
EXPECT_EQ("", GetParam(device, "interfaces"));
}
TEST(CommandUtil, AddGetParam)
{
PbCommand command;
AddParam(command, "key", "value");
EXPECT_EQ("value", GetParam(command, "key"));
EXPECT_EQ("", GetParam(command, "xyz"));
PbDeviceDefinition definition;
AddParam(definition, "key", "value");
EXPECT_EQ("value", GetParam(definition, "key"));
EXPECT_EQ("", GetParam(definition, "xyz"));
PbDevice device;
AddParam(device, "key", "value");
const auto& it = device.params().find("key");
EXPECT_EQ("value", it->second);
}
TEST(CommandUtil, ParseParameters)
{
PbDeviceDefinition device1;
ParseParameters(device1, "a=b:c=d:e");
EXPECT_EQ("b", GetParam(device1, "a"));
EXPECT_EQ("d", GetParam(device1, "c"));
EXPECT_EQ("", GetParam(device1, "e"));
// Old style parameters
PbDeviceDefinition device2;
ParseParameters(device2, "a");
EXPECT_EQ("a", GetParam(device2, "file"));
EXPECT_EQ("a", GetParam(device2, "interfaces"));
TestSpecialDevice("bridge");
TestSpecialDevice("daynaport");
TestSpecialDevice("printer");
TestSpecialDevice("services");
}

View File

@ -8,12 +8,14 @@
//---------------------------------------------------------------------------
#include "testing.h"
#include "devices/device_factory.h"
#include "controllers/controller_manager.h"
TEST(ControllerManagerTest, ControllerManager)
{
const int ID = 4;
const int LUN = 6;
DeviceFactory device_factory;
ControllerManager controller_manager;
auto device = device_factory.CreateDevice(UNDEFINED, "services", ID);

View File

@ -15,6 +15,8 @@
TEST(DeviceFactoryTest, GetTypeForFile)
{
DeviceFactory device_factory;
EXPECT_EQ(device_factory.GetTypeForFile("test.hd1"), SCHD);
EXPECT_EQ(device_factory.GetTypeForFile("test.hds"), SCHD);
EXPECT_EQ(device_factory.GetTypeForFile("test.HDS"), SCHD);
@ -36,6 +38,8 @@ TEST(DeviceFactoryTest, GetTypeForFile)
TEST(DeviceFactoryTest, LifeCycle)
{
DeviceFactory device_factory;
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "services", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHS", device->GetType());
@ -47,7 +51,7 @@ TEST(DeviceFactoryTest, LifeCycle)
EXPECT_EQ(device, device_factory.GetDeviceByIdAndLun(-1, 0));
EXPECT_EQ(nullptr, device_factory.GetDeviceByIdAndLun(-1, 1));
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
devices = device_factory.GetAllDevices();
EXPECT_EQ(0, devices.size());
EXPECT_EQ(nullptr, device_factory.GetDeviceByIdAndLun(-1, 0));
@ -55,6 +59,7 @@ TEST(DeviceFactoryTest, LifeCycle)
TEST(DeviceFactoryTest, GetSectorSizes)
{
DeviceFactory device_factory;
unordered_set<uint32_t> sector_sizes;
sector_sizes = device_factory.GetSectorSizes("SCHD");
@ -98,12 +103,16 @@ TEST(DeviceFactoryTest, GetSectorSizes)
TEST(DeviceFactoryTest, UnknownDeviceType)
{
DeviceFactory device_factory;
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "test", -1);
EXPECT_EQ(nullptr, device);
}
TEST(DeviceFactoryTest, SCHD_Device_Defaults)
{
DeviceFactory device_factory;
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "test.hda", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHD", device->GetType());
@ -124,26 +133,28 @@ TEST(DeviceFactoryTest, SCHD_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
device = device_factory.CreateDevice(UNDEFINED, "test.hds", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHD", device->GetType());
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
device = device_factory.CreateDevice(UNDEFINED, "test.hdi", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHD", device->GetType());
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
device = device_factory.CreateDevice(UNDEFINED, "test.nhd", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHD", device->GetType());
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
}
TEST(DeviceFactoryTest, SCRM_Device_Defaults)
{
DeviceFactory device_factory;
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "test.hdr", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCRM", device->GetType());
@ -164,11 +175,13 @@ TEST(DeviceFactoryTest, SCRM_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
}
TEST(DeviceFactoryTest, SCMO_Device_Defaults)
{
DeviceFactory device_factory;
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "test.mos", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCMO", device->GetType());
@ -189,11 +202,13 @@ TEST(DeviceFactoryTest, SCMO_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
}
TEST(DeviceFactoryTest, SCCD_Device_Defaults)
{
DeviceFactory device_factory;
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "test.iso", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCCD", device->GetType());
@ -214,11 +229,13 @@ TEST(DeviceFactoryTest, SCCD_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
}
TEST(DeviceFactoryTest, SCBR_Device_Defaults)
{
DeviceFactory device_factory;
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "bridge", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCBR", device->GetType());
@ -239,11 +256,13 @@ TEST(DeviceFactoryTest, SCBR_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
}
TEST(DeviceFactoryTest, SCDP_Device_Defaults)
{
DeviceFactory device_factory;
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "daynaport", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCDP", device->GetType());
@ -263,11 +282,13 @@ TEST(DeviceFactoryTest, SCDP_Device_Defaults)
EXPECT_EQ("SCSI/Link", device->GetProduct());
EXPECT_EQ("1.4a", device->GetRevision());
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
}
TEST(DeviceFactoryTest, SCHS_Device_Defaults)
{
DeviceFactory device_factory;
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "services", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHS", device->GetType());
@ -288,11 +309,13 @@ TEST(DeviceFactoryTest, SCHS_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
}
TEST(DeviceFactoryTest, SCLP_Device_Defaults)
{
DeviceFactory device_factory;
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "printer", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCLP", device->GetType());
@ -313,5 +336,5 @@ TEST(DeviceFactoryTest, SCLP_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
device_factory.DeleteDevice(device);
device_factory.DeleteDevice(*device);
}

View File

@ -11,16 +11,71 @@
#include "rascsi_exceptions.h"
#include "devices/device.h"
class TestDevice : public Device
class TestDevice final : public Device
{
FRIEND_TEST(DeviceTest, GetSetParams);
FRIEND_TEST(DeviceTest, StatusCode);
FRIEND_TEST(DeviceTest, Reset);
FRIEND_TEST(DeviceTest, Start);
FRIEND_TEST(DeviceTest, Stop);
FRIEND_TEST(DeviceTest, Eject);
public:
TestDevice() : Device("test") {}
~TestDevice() final = default;
bool Dispatch(scsi_defs::scsi_command) final { return false; }
~TestDevice() override = default;
};
TEST(DeviceTest, Properties)
{
const int ID = 4;
const int LUN = 5;
TestDevice device;
EXPECT_FALSE(device.IsProtectable());
device.SetProtectable(true);
EXPECT_TRUE(device.IsProtectable());
EXPECT_FALSE(device.IsProtected());
device.SetProtected(true);
EXPECT_TRUE(device.IsProtected());
EXPECT_FALSE(device.IsReadOnly());
device.SetReadOnly(true);
EXPECT_TRUE(device.IsReadOnly());
EXPECT_FALSE(device.IsStoppable());
device.SetStoppable(true);
EXPECT_TRUE(device.IsStoppable());
EXPECT_FALSE(device.IsStopped());
device.SetStopped(true);
EXPECT_TRUE(device.IsStopped());
EXPECT_FALSE(device.IsRemovable());
device.SetRemovable(true);
EXPECT_TRUE(device.IsRemovable());
EXPECT_FALSE(device.IsRemoved());
device.SetRemoved(true);
EXPECT_TRUE(device.IsRemoved());
EXPECT_FALSE(device.IsLockable());
device.SetLockable(true);
EXPECT_TRUE(device.IsLockable());
EXPECT_FALSE(device.IsLocked());
device.SetLocked(true);
EXPECT_TRUE(device.IsLocked());
device.SetId(ID);
EXPECT_EQ(ID, device.GetId());
device.SetLun(LUN);
EXPECT_EQ(LUN, device.GetLun());
}
TEST(DeviceTest, ProductData)
{
TestDevice device;
@ -46,3 +101,99 @@ TEST(DeviceTest, ProductData)
EXPECT_EQ("V P R ", device.GetPaddedName());
}
TEST(DeviceTest, GetSetParams)
{
TestDevice device;
unordered_map<string, string> params;
params["key"] = "value";
EXPECT_EQ("", device.GetParam("key"));
device.SetParams(params);
EXPECT_EQ("", device.GetParam("key"));
unordered_map<string, string> default_params;
default_params["key"] = "value";
device.SetDefaultParams(default_params);
EXPECT_EQ("", device.GetParam("key"));
device.SetParams(params);
EXPECT_EQ("value", device.GetParam("key"));
}
TEST(DeviceTest, StatusCode)
{
TestDevice device;
device.SetStatusCode(123);
EXPECT_EQ(123, device.GetStatusCode());
}
TEST(DeviceTest, Init)
{
TestDevice device;
unordered_map<string, string> params;
EXPECT_TRUE(device.Init(params));
}
TEST(DeviceTest, Reset)
{
TestDevice device;
device.SetLocked(true);
device.SetAttn(true);
device.SetReset(true);
device.Reset();
EXPECT_FALSE(device.IsLocked());
EXPECT_FALSE(device.IsAttn());
EXPECT_FALSE(device.IsReset());
}
TEST(DeviceTest, Start)
{
TestDevice device;
device.SetStopped(true);
device.SetReady(false);
EXPECT_FALSE(device.Start());
EXPECT_TRUE(device.IsStopped());
device.SetReady(true);
EXPECT_TRUE(device.Start());
EXPECT_FALSE(device.IsStopped());
}
TEST(DeviceTest, Stop)
{
TestDevice device;
device.SetReady(true);
device.SetAttn(true);
device.SetStopped(false);
device.Stop();
EXPECT_FALSE(device.IsReady());
EXPECT_FALSE(device.IsAttn());
EXPECT_TRUE(device.IsStopped());
}
TEST(DeviceTest, Eject)
{
TestDevice device;
device.SetReady(false);
device.SetRemovable(false);
EXPECT_FALSE(device.Eject(false));
device.SetReady(true);
EXPECT_FALSE(device.Eject(false));
device.SetRemovable(true);
device.SetLocked(true);
EXPECT_FALSE(device.Eject(false));
device.SetLocked(false);
EXPECT_TRUE(device.Eject(false));
EXPECT_FALSE(device.IsReady());
EXPECT_FALSE(device.IsAttn());
EXPECT_TRUE(device.IsRemoved());
EXPECT_FALSE(device.IsLocked());
EXPECT_TRUE(device.IsStopped());
}

View File

@ -0,0 +1,393 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "testing.h"
#include "rascsi_exceptions.h"
TEST(DiskTest, Rezero)
{
MockScsiController controller(nullptr, 0);
MockSCSIHD_NEC disk;
controller.AddDevice(&disk);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdRezero), scsi_error_exception)
<< "REZERO must fail because drive is not ready";
disk.SetReady(true);
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdRezero));
EXPECT_EQ(0, controller.ctrl.status);
}
TEST(DiskTest, FormatUnit)
{
MockScsiController controller(nullptr, 0);
MockSCSIHD_NEC disk;
controller.AddDevice(&disk);
controller.ctrl.cmd.resize(6);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdFormat), scsi_error_exception);
disk.SetReady(true);
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdFormat));
EXPECT_EQ(0, controller.ctrl.status);
controller.ctrl.cmd[1] = 0x10;
controller.ctrl.cmd[4] = 1;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdFormat), scsi_error_exception);
}
TEST(DiskTest, ReassignBlocks)
{
MockScsiController controller(nullptr, 0);
MockSCSIHD_NEC disk;
controller.AddDevice(&disk);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReassign), scsi_error_exception)
<< "REASSIGN must fail because drive is not ready";
disk.SetReady(true);
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReassign));
EXPECT_EQ(0, controller.ctrl.status);
}
TEST(DiskTest, Seek)
{
MockScsiController controller(nullptr, 0);
MockSCSIHD_NEC disk;
controller.AddDevice(&disk);
controller.ctrl.cmd.resize(10);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSeek6), scsi_error_exception)
<< "SEEK(6) must fail for a medium with 0 blocks";
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSeek10), scsi_error_exception)
<< "SEEK(10) must fail for a medium with 0 blocks";
disk.SetBlockCount(1);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSeek6), scsi_error_exception)
<< "SEEK(6) must fail because drive is not ready";
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSeek10), scsi_error_exception)
<< "SEEK(10) must fail because drive is not ready";
disk.SetReady(true);
// Block count for SEEK(6)
controller.ctrl.cmd[4] = 1;
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdSeek6));
EXPECT_EQ(0, controller.ctrl.status);
controller.ctrl.cmd[4] = 0;
// Block count for SEEK(10)
controller.ctrl.cmd[5] = 1;
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdSeek10));
EXPECT_EQ(0, controller.ctrl.status);
}
TEST(DiskTest, ReadCapacity)
{
MockScsiController controller(nullptr, 0);
MockSCSIHD_NEC disk;
controller.AddDevice(&disk);
controller.ctrl.cmd.resize(16);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception)
<< "Neithed READ CAPACITY(16) nor READ LONG(16)";
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity10), scsi_error_exception)
<< "READ CAPACITY(10) must fail because drive is not ready";
// READ CAPACITY(16), not READ LONG(16)
controller.ctrl.cmd[1] = 0x10;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception)
<< "READ CAPACITY(16) must fail because drive is not ready";
controller.ctrl.cmd[1] = 0x00;
disk.SetReady(true);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity10), scsi_error_exception)
<< "READ CAPACITY(10) must fail because the medium has no capacity";
// READ CAPACITY(16), not READ LONG(16)
controller.ctrl.cmd[1] = 0x10;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception)
<< "READ CAPACITY(16) must fail because the medium has no capacity";
controller.ctrl.cmd[1] = 0x00;
disk.SetBlockCount(0x12345678);
EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReadCapacity10));
EXPECT_EQ(0x12, controller.ctrl.buffer[0]);
EXPECT_EQ(0x34, controller.ctrl.buffer[1]);
EXPECT_EQ(0x56, controller.ctrl.buffer[2]);
EXPECT_EQ(0x77, controller.ctrl.buffer[3]);
disk.SetBlockCount(0x1234567887654321);
EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReadCapacity10));
EXPECT_EQ(0xff, controller.ctrl.buffer[0]);
EXPECT_EQ(0xff, controller.ctrl.buffer[1]);
EXPECT_EQ(0xff, controller.ctrl.buffer[2]);
EXPECT_EQ(0xff, controller.ctrl.buffer[3]);
disk.SetSectorSizeInBytes(1024);
// READ CAPACITY(16), not READ LONG(16)
controller.ctrl.cmd[1] = 0x10;
EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16));
EXPECT_EQ(0x12, controller.ctrl.buffer[0]);
EXPECT_EQ(0x34, controller.ctrl.buffer[1]);
EXPECT_EQ(0x56, controller.ctrl.buffer[2]);
EXPECT_EQ(0x78, controller.ctrl.buffer[3]);
EXPECT_EQ(0x87, controller.ctrl.buffer[4]);
EXPECT_EQ(0x65, controller.ctrl.buffer[5]);
EXPECT_EQ(0x43, controller.ctrl.buffer[6]);
EXPECT_EQ(0x20, controller.ctrl.buffer[7]);
EXPECT_EQ(0x00, controller.ctrl.buffer[8]);
EXPECT_EQ(0x00, controller.ctrl.buffer[9]);
EXPECT_EQ(0x04, controller.ctrl.buffer[10]);
EXPECT_EQ(0x00, controller.ctrl.buffer[11]);
}
TEST(DiskTest, ReadWriteLong)
{
MockScsiController controller(nullptr, 0);
MockSCSIHD_NEC disk;
controller.AddDevice(&disk);
controller.ctrl.cmd.resize(16);
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReadLong10));
EXPECT_EQ(0, controller.ctrl.status);
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdWriteLong10));
EXPECT_EQ(0, controller.ctrl.status);
controller.ctrl.cmd[2] = 1;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadLong10), scsi_error_exception)
<< "READ LONG(10) must fail because the capacity is exceeded";
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdWriteLong10), scsi_error_exception)
<< "WRITE LONG(10) must fail because the capacity is exceeded";
// READ LONG(16), not READ CAPACITY(16)
controller.ctrl.cmd[1] = 0x11;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception)
<< "READ LONG(16) must fail because the capacity is exceeded";
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdWriteLong16), scsi_error_exception)
<< "WRITE LONG(16) must fail because the capacity is exceeded";
controller.ctrl.cmd[2] = 0;
controller.ctrl.cmd[7] = 1;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadLong10), scsi_error_exception)
<< "READ LONG(10) must fail because it currently only supports 0 bytes transfer length";
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdWriteLong10), scsi_error_exception)
<< "WRITE LONG(10) must fail because it currently only supports 0 bytes transfer length";
controller.ctrl.cmd[7] = 0;
// READ LONG(16), not READ CAPACITY(16)
controller.ctrl.cmd[1] = 0x11;
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16));
EXPECT_EQ(0, controller.ctrl.status);
controller.ctrl.cmd[1] = 0x00;
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdWriteLong16));
EXPECT_EQ(0, controller.ctrl.status);
controller.ctrl.cmd[13] = 1;
// READ LONG(16), not READ CAPACITY(16)
controller.ctrl.cmd[1] = 0x11;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception)
<< "READ LONG(16) must fail because it currently only supports 0 bytes transfer length";
controller.ctrl.cmd[1] = 0x00;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdWriteLong16), scsi_error_exception)
<< "WRITE LONG(16) must fail because it currently only supports 0 bytes transfer length";
}
TEST(DiskTest, ReserveRelease)
{
MockScsiController controller(nullptr, 0);
MockSCSIHD_NEC disk;
controller.AddDevice(&disk);
controller.ctrl.cmd.resize(6);
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReserve6));
EXPECT_EQ(0, controller.ctrl.status);
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdRelease6));
EXPECT_EQ(0, controller.ctrl.status);
}
TEST(DiskTest, SendDiagnostic)
{
MockScsiController controller(nullptr, 0);
MockSCSIHD_NEC disk;
controller.AddDevice(&disk);
controller.ctrl.cmd.resize(6);
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdSendDiag));
EXPECT_EQ(0, controller.ctrl.status);
controller.ctrl.cmd[1] = 0x10;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSendDiag), scsi_error_exception)
<< "SEND DIAGNOSTIC must fail because PF bit is not supported";
controller.ctrl.cmd[1] = 0;
controller.ctrl.cmd[3] = 1;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSendDiag), scsi_error_exception)
<< "SEND DIAGNOSTIC must fail because parameter list is not supported";
controller.ctrl.cmd[3] = 0;
controller.ctrl.cmd[4] = 1;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSendDiag), scsi_error_exception)
<< "SEND DIAGNOSTIC must fail because parameter list is not supported";
}
TEST(DiskTest, PreventAllowMediumRemoval)
{
MockScsiController controller(nullptr, 0);
MockSCSIHD_NEC disk;
controller.AddDevice(&disk);
controller.ctrl.cmd.resize(6);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdRemoval), scsi_error_exception)
<< "REMOVAL must fail because drive is not ready";
disk.SetReady(true);
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdRemoval));
EXPECT_EQ(0, controller.ctrl.status);
EXPECT_FALSE(disk.IsLocked());
controller.ctrl.cmd[4] = 1;
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdRemoval));
EXPECT_EQ(0, controller.ctrl.status);
EXPECT_TRUE(disk.IsLocked());
}
TEST(DiskTest, SynchronizeCache)
{
MockScsiController controller(nullptr, 0);
MockSCSIHD_NEC disk;
controller.AddDevice(&disk);
controller.ctrl.cmd.resize(10);
EXPECT_CALL(disk, FlushCache()).Times(1);
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdSynchronizeCache10));
EXPECT_EQ(0, controller.ctrl.status);
controller.ctrl.cmd.resize(16);
EXPECT_CALL(disk, FlushCache()).Times(1);
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdSynchronizeCache16));
EXPECT_EQ(0, controller.ctrl.status);
}
TEST(DiskTest, ReadDefectData)
{
MockScsiController controller(nullptr, 0);
MockSCSIHD_NEC disk;
controller.AddDevice(&disk);
controller.ctrl.cmd.resize(10);
EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReadDefectData10));
EXPECT_EQ(0, controller.ctrl.status);
}
TEST(DiskTest, SectorSize)
{
MockSCSIHD_NEC disk;
unordered_set<uint32_t> sizes = { 1, 2, 3 };
disk.SetSectorSizes(sizes);
EXPECT_TRUE(disk.IsSectorSizeConfigurable());
sizes.clear();
disk.SetSectorSizes(sizes);
EXPECT_FALSE(disk.IsSectorSizeConfigurable());
disk.SetSectorSizeShiftCount(9);
EXPECT_EQ(9, disk.GetSectorSizeShiftCount());
EXPECT_EQ(512, disk.GetSectorSizeInBytes());
disk.SetSectorSizeInBytes(512);
EXPECT_EQ(9, disk.GetSectorSizeShiftCount());
EXPECT_EQ(512, disk.GetSectorSizeInBytes());
disk.SetSectorSizeShiftCount(10);
EXPECT_EQ(10, disk.GetSectorSizeShiftCount());
EXPECT_EQ(1024, disk.GetSectorSizeInBytes());
disk.SetSectorSizeInBytes(1024);
EXPECT_EQ(10, disk.GetSectorSizeShiftCount());
EXPECT_EQ(1024, disk.GetSectorSizeInBytes());
disk.SetSectorSizeShiftCount(11);
EXPECT_EQ(11, disk.GetSectorSizeShiftCount());
EXPECT_EQ(2048, disk.GetSectorSizeInBytes());
disk.SetSectorSizeInBytes(2048);
EXPECT_EQ(11, disk.GetSectorSizeShiftCount());
EXPECT_EQ(2048, disk.GetSectorSizeInBytes());
disk.SetSectorSizeShiftCount(12);
EXPECT_EQ(12, disk.GetSectorSizeShiftCount());
EXPECT_EQ(4096, disk.GetSectorSizeInBytes());
disk.SetSectorSizeInBytes(4096);
EXPECT_EQ(12, disk.GetSectorSizeShiftCount());
EXPECT_EQ(4096, disk.GetSectorSizeInBytes());
EXPECT_THROW(disk.SetSectorSizeInBytes(1234), io_exception);
}
TEST(DiskTest, ConfiguredSectorSize)
{
DeviceFactory device_factory;
MockSCSIHD_NEC disk;
EXPECT_TRUE(disk.SetConfiguredSectorSize(device_factory, 512));
EXPECT_EQ(512, disk.GetConfiguredSectorSize());
EXPECT_FALSE(disk.SetConfiguredSectorSize(device_factory, 1234));
EXPECT_EQ(512, disk.GetConfiguredSectorSize());
}
TEST(DiskTest, BlockCount)
{
MockSCSIHD_NEC disk;
disk.SetBlockCount(0x1234567887654321);
EXPECT_EQ(0x1234567887654321, disk.GetBlockCount());
}

View File

@ -17,7 +17,7 @@ class TestFileSupport : public FileSupport
}
};
TEST(FileSupportTest, FileSupport)
TEST(FileSupportTest, Reserve)
{
const int ID = 1;
const int LUN = 2;
@ -43,3 +43,19 @@ TEST(FileSupportTest, FileSupport)
file_support.UnreserveFile();
EXPECT_FALSE(FileSupport::GetIdsForReservedFile(path, id, lun));
}
TEST(FileSupportTest, UnreserveAll)
{
const int ID = 2;
const int LUN = 31;
Filepath path;
path.SetPath("path");
TestFileSupport file_support;
file_support.ReserveFile(path, ID, LUN);
FileSupport::UnreserveAll();
int id;
int lun;
EXPECT_FALSE(FileSupport::GetIdsForReservedFile(path, id, lun));
}

View File

@ -19,31 +19,29 @@
using namespace std;
const unordered_set<uint32_t> sector_sizes;
TEST(ModePagesTest, ModePageDevice_AddModePages)
{
vector<int> cdb(6);
BYTE buf[512];
vector<BYTE> buf(512);
MockModePageDevice device;
cdb[2] = 0x3f;
EXPECT_EQ(0, device.AddModePages(cdb, buf, 0)) << "Allocation length was not limited";
EXPECT_EQ(1, device.AddModePages(cdb, buf, 1)) << "Allocation length was not limited";
EXPECT_EQ(0, device.AddModePages(cdb, buf.data(), 0)) << "Allocation length was not limited";
EXPECT_EQ(1, device.AddModePages(cdb, buf.data(), 1)) << "Allocation length was not limited";
cdb[2] = 0x00;
EXPECT_THROW(device.AddModePages(cdb, buf, 12), scsi_error_exception) << "Data for non-existing code page 0 were returned";
EXPECT_THROW(device.AddModePages(cdb, buf.data(), 12), scsi_error_exception) << "Data for non-existing mode page 0 were returned";
}
TEST(ModePagesTest, SCSIHD_AddModePages)
TEST(ModePagesTest, SCSIHD_SetUpModePages)
{
map<int, vector<byte>> mode_pages;
const unordered_set<uint32_t> sector_sizes;
MockSCSIHD device(sector_sizes);
device.AddModePages(mode_pages, 0x3f, false);
device.SetUpModePages(mode_pages, 0x3f, false);
EXPECT_EQ(5, mode_pages.size()) << "Unexpected number of code pages";
EXPECT_EQ(5, mode_pages.size()) << "Unexpected number of mode pages";
EXPECT_EQ(12, mode_pages[1].size());
EXPECT_EQ(24, mode_pages[3].size());
EXPECT_EQ(24, mode_pages[4].size());
@ -51,14 +49,13 @@ TEST(ModePagesTest, SCSIHD_AddModePages)
EXPECT_EQ(30, mode_pages[48].size());
}
TEST(ModePagesTest, SCSIHD_NEC_AddModePages)
TEST(ModePagesTest, SCSIHD_NEC_SetUpModePages)
{
map<int, vector<byte>> mode_pages;
MockSCSIHD_NEC device;
device.AddModePages(mode_pages, 0x3f, false);
device.SetUpModePages(mode_pages, 0x3f, false);
EXPECT_EQ(5, mode_pages.size()) << "Unexpected number of code pages";
EXPECT_EQ(5, mode_pages.size()) << "Unexpected number of mode pages";
EXPECT_EQ(8, mode_pages[1].size());
EXPECT_EQ(24, mode_pages[3].size());
EXPECT_EQ(20, mode_pages[4].size());
@ -66,14 +63,14 @@ TEST(ModePagesTest, SCSIHD_NEC_AddModePages)
EXPECT_EQ(30, mode_pages[48].size());
}
TEST(ModePagesTest, SCSICD_AddModePages)
TEST(ModePagesTest, SCSICD_SetUpModePages)
{
map<int, vector<byte>> mode_pages;
const unordered_set<uint32_t> sector_sizes;
MockSCSICD device(sector_sizes);
device.AddModePages(mode_pages, 0x3f, false);
device.SetUpModePages(mode_pages, 0x3f, false);
EXPECT_EQ(7, mode_pages.size()) << "Unexpected number of code pages";
EXPECT_EQ(7, mode_pages.size()) << "Unexpected number of mode pages";
EXPECT_EQ(12, mode_pages[1].size());
EXPECT_EQ(24, mode_pages[3].size());
EXPECT_EQ(24, mode_pages[4].size());
@ -83,15 +80,15 @@ TEST(ModePagesTest, SCSICD_AddModePages)
EXPECT_EQ(30, mode_pages[48].size());
}
TEST(ModePagesTest, SCSIMO_AddModePages)
TEST(ModePagesTest, SCSIMO_SetUpModePages)
{
map<int, vector<byte>> mode_pages;
unordered_map<uint64_t, Geometry> geometries;
const unordered_set<uint32_t> sector_sizes;
MockSCSIMO device(sector_sizes, geometries);
device.AddModePages(mode_pages, 0x3f, false);
device.SetUpModePages(mode_pages, 0x3f, false);
EXPECT_EQ(6, mode_pages.size()) << "Unexpected number of code pages";
EXPECT_EQ(6, mode_pages.size()) << "Unexpected number of mode pages";
EXPECT_EQ(12, mode_pages[1].size());
EXPECT_EQ(24, mode_pages[3].size());
EXPECT_EQ(24, mode_pages[4].size());
@ -100,14 +97,14 @@ TEST(ModePagesTest, SCSIMO_AddModePages)
EXPECT_EQ(12, mode_pages[32].size());
}
TEST(ModePagesTest, HostServices_AddModePages)
TEST(ModePagesTest, HostServices_SetUpModePages)
{
map<int, vector<byte>> mode_pages;
DeviceFactory device_factory;
MockHostServices device(device_factory);
device.SetUpModePages(mode_pages, 0x3f, false);
MockHostServices device;
device.AddModePages(mode_pages, 0x3f, false);
EXPECT_EQ(1, mode_pages.size()) << "Unexpected number of code pages";
EXPECT_EQ(1, mode_pages.size()) << "Unexpected number of mode pages";
EXPECT_EQ(10, mode_pages[32].size());
}
@ -116,11 +113,11 @@ TEST(ModePagesTest, ModeSelect)
const int LENGTH = 12;
vector<int> cdb(16);
BYTE buf[255] = {};
vector<BYTE> buf(255);
// PF (vendor-specific parameter format)
cdb[1] = 0x00;
EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf, LENGTH, 0), scsi_error_exception)
EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf.data(), LENGTH, 0), scsi_error_exception)
<< "Vendor-specific parameters are not supported";
// PF (standard parameter format)
@ -129,20 +126,20 @@ TEST(ModePagesTest, ModeSelect)
buf[9] = 0x00;
buf[10] = 0x02;
buf[11] = 0x00;
EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf, LENGTH, 256), scsi_error_exception)
EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf.data(), LENGTH, 256), scsi_error_exception)
<< "Requested sector size does not match current sector size";
// Page 0
buf[LENGTH] = 0x00;
EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf, LENGTH + 2, 512), scsi_error_exception)
EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf.data(), LENGTH + 2, 512), scsi_error_exception)
<< "Unsupported page 0 was not rejected";
// Page 3 (Format Device Page)
buf[LENGTH] = 0x03;
EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf, LENGTH + 2, 512), scsi_error_exception)
EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf.data(), LENGTH + 2, 512), scsi_error_exception)
<< "Requested sector size does not match current sector size";
// Match the requested to the current sector size
buf[LENGTH + 12] = 0x02;
scsi_command_util::ModeSelect(cdb, buf, LENGTH + 2, 512);
scsi_command_util::ModeSelect(cdb, buf.data(), LENGTH + 2, 512);
}

View File

@ -12,15 +12,29 @@
#include "devices/primary_device.h"
#include "devices/device_factory.h"
TEST(PrimaryDeviceTest, UnitReady)
TEST(PrimaryDeviceTest, PhaseChange)
{
MockScsiController controller(nullptr, 0);
MockPrimaryDevice device;
controller.AddDevice(&device);
controller.ctrl.cmd = vector<int>(6);
controller.ctrl.cmd[0] = (int)scsi_command::eCmdTestUnitReady;
EXPECT_CALL(controller, Status()).Times(1);
device.EnterStatusPhase();
EXPECT_CALL(controller, DataIn()).Times(1);
device.EnterDataInPhase();
EXPECT_CALL(controller, DataOut()).Times(1);
device.EnterDataOutPhase();
}
TEST(PrimaryDeviceTest, TestUnitReady)
{
MockScsiController controller(nullptr, 0);
MockPrimaryDevice device;
controller.AddDevice(&device);
device.SetReset(true);
device.SetAttn(true);
@ -59,8 +73,7 @@ TEST(PrimaryDeviceTest, Inquiry)
device.SetController(&controller);
controller.ctrl.cmd = vector<int>(6);
controller.ctrl.cmd[0] = (int)scsi_command::eCmdInquiry;
controller.ctrl.cmd.resize(6);
// ALLOCATION LENGTH
controller.ctrl.cmd[4] = 255;
@ -121,8 +134,7 @@ TEST(PrimaryDeviceTest, RequestSense)
controller.AddDevice(&device);
controller.ctrl.cmd = vector<int>(6);
controller.ctrl.cmd[0] = (int)scsi_command::eCmdRequestSense;
controller.ctrl.cmd.resize(6);
// ALLOCATION LENGTH
controller.ctrl.cmd[4] = 255;
@ -146,13 +158,12 @@ TEST(PrimaryDeviceTest, ReportLuns)
device1.SetLun(LUN1);
controller.AddDevice(&device1);
ASSERT_TRUE(controller.HasDeviceForLun(LUN1));
EXPECT_TRUE(controller.HasDeviceForLun(LUN1));
device2.SetLun(LUN2);
controller.AddDevice(&device2);
ASSERT_TRUE(controller.HasDeviceForLun(LUN2));
EXPECT_TRUE(controller.HasDeviceForLun(LUN2));
controller.ctrl.cmd = vector<int>(10);
controller.ctrl.cmd[0] = (int)scsi_command::eCmdReportLuns;
controller.ctrl.cmd.resize(10);
// ALLOCATION LENGTH
controller.ctrl.cmd[9] = 255;
@ -191,7 +202,13 @@ TEST(PrimaryDeviceTest, UnknownCommand)
controller.AddDevice(&device);
controller.ctrl.cmd = vector<int>(1);
controller.ctrl.cmd[0] = 0xFF;
controller.ctrl.cmd.resize(1);
EXPECT_FALSE(device.Dispatch((scsi_command)0xFF));
}
TEST(PrimaryDeviceTest, GetSendDelay)
{
MockPrimaryDevice device;
EXPECT_EQ(-1, device.GetSendDelay());
}

View File

@ -8,6 +8,7 @@
//---------------------------------------------------------------------------
#include "testing.h"
#include "devices/device_factory.h"
#include "rascsi_interface.pb.h"
#include "rascsi_response.h"
#include "rascsi_image.h"
@ -16,6 +17,7 @@ using namespace rascsi_interface;
TEST(ResponseTest, Operation_Count)
{
DeviceFactory device_factory;
RascsiImage rascsi_image;
RascsiResponse rascsi_response(&device_factory, &rascsi_image);
PbResult pb_operation_info_result;

View File

@ -10,6 +10,8 @@
#include "testing.h"
#include "devices/scsi_command_util.h"
using namespace scsi_command_util;
TEST(ScsiCommandUtilTest, EnrichFormatPage)
{
const int SECTOR_SIZE = 512;
@ -18,12 +20,12 @@ TEST(ScsiCommandUtilTest, EnrichFormatPage)
vector<byte> format_page(24);
pages[3] = format_page;
scsi_command_util::EnrichFormatPage(pages, false, SECTOR_SIZE);
EnrichFormatPage(pages, false, SECTOR_SIZE);
format_page = pages[3];
EXPECT_EQ(byte{0}, format_page[12]);
EXPECT_EQ(byte{0}, format_page[13]);
scsi_command_util::EnrichFormatPage(pages, true, SECTOR_SIZE);
EnrichFormatPage(pages, true, SECTOR_SIZE);
format_page = pages[3];
EXPECT_EQ(byte{SECTOR_SIZE >> 8}, format_page[12]);
EXPECT_EQ(byte{0}, format_page[13]);
@ -35,11 +37,73 @@ TEST(ScsiCommandUtilTest, AddAppleVendorModePage)
vector<byte> vendor_page(30);
pages[48] = vendor_page;
scsi_command_util::AddAppleVendorModePage(pages, true);
AddAppleVendorModePage(pages, true);
vendor_page = pages[48];
EXPECT_EQ(byte{0}, vendor_page[2]);
scsi_command_util::AddAppleVendorModePage(pages, false);
AddAppleVendorModePage(pages, false);
vendor_page = pages[48];
EXPECT_STREQ("APPLE COMPUTER, INC ", (const char *)&vendor_page[2]);
}
TEST(ScsiCommandUtilTest, GetInt16)
{
vector<int> v = { 0x12, 0x34 };
EXPECT_EQ(0x1234, GetInt16(v, 0));
}
TEST(ScsiCommandUtilTest, GetInt32)
{
vector<int> v = { 0x12, 0x34, 0x56, 0x78 };
EXPECT_EQ(0x12345678, GetInt32(v, 0));
}
TEST(ScsiCommandUtilTest, GetInt64)
{
vector<int> v = { 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21 };
EXPECT_EQ(0x1234567887654321, GetInt64(v, 0));
}
TEST(ScsiCommandUtilTest, SetInt16)
{
vector<BYTE> buf(2);
SetInt16(buf.data(), 0x1234);
EXPECT_EQ(0x12, buf[0]);
EXPECT_EQ(0x34, buf[1]);
vector<byte> v(2);
SetInt16(v, 0, 0x1234);
EXPECT_EQ(byte{0x12}, v[0]);
EXPECT_EQ(byte{0x34}, v[1]);
}
TEST(ScsiCommandUtilTest, SetInt32)
{
vector<BYTE> buf(4);
SetInt32(buf.data(), 0x12345678);
EXPECT_EQ(0x12, buf[0]);
EXPECT_EQ(0x34, buf[1]);
EXPECT_EQ(0x56, buf[2]);
EXPECT_EQ(0x78, buf[3]);
vector<byte> v(4);
SetInt32(v, 0, 0x12345678);
EXPECT_EQ(byte{0x12}, v[0]);
EXPECT_EQ(byte{0x34}, v[1]);
EXPECT_EQ(byte{0x56}, v[2]);
EXPECT_EQ(byte{0x78}, v[3]);
}
TEST(ScsiCommandUtilTest, SetInt64)
{
vector<BYTE> buf(8);
SetInt64(buf.data(), 0x1234567887654321);
EXPECT_EQ(0x12, buf[0]);
EXPECT_EQ(0x34, buf[1]);
EXPECT_EQ(0x56, buf[2]);
EXPECT_EQ(0x78, buf[3]);
EXPECT_EQ(0x87, buf[4]);
EXPECT_EQ(0x65, buf[5]);
EXPECT_EQ(0x43, buf[6]);
EXPECT_EQ(0x21, buf[7]);
}

View File

@ -10,12 +10,17 @@
#include "testing.h"
#include "controllers/scsi_controller.h"
TEST(ScsiControllerTest, ScsiController)
TEST(ScsiControllerTest, Reset)
{
MockScsiController controller(nullptr, 0);
EXPECT_EQ(32, controller.GetMaxLuns());
EXPECT_CALL(controller, SetPhase(BUS::phase_t::busfree)).Times(1);
controller.Reset();
}
TEST(ScsiControllerTest, GetMaxLuns)
{
MockScsiController controller(nullptr, 0);
EXPECT_EQ(32, controller.GetMaxLuns());
}

View File

@ -10,27 +10,25 @@
#include <gtest/gtest.h>
#include "spdlog/spdlog.h"
#include "controllers/controller_manager.h"
#include "devices/device_factory.h"
class Environment : public ::testing::Environment
{
spdlog::level::level_enum log_level;
public:
Environment() = default;
explicit Environment(spdlog::level::level_enum level) : log_level(level) {}
~Environment() final = default;
// Turn off logging
void SetUp() override { spdlog::set_level(spdlog::level::off); }
void SetUp() override { spdlog::set_level(log_level); }
};
const DeviceFactory& device_factory = DeviceFactory::instance();
int main(int, char*[])
int main(int argc, char *[])
{
testing::AddGlobalTestEnvironment(new Environment());
// If any argument is provided the log level is set to trace
testing::AddGlobalTestEnvironment(new Environment(argc > 1 ? spdlog::level::trace : spdlog::level::off));
testing::InitGoogleTest();
testing::InitGoogleTest();
return RUN_ALL_TESTS();
return RUN_ALL_TESTS();
}

View File

@ -20,11 +20,7 @@
#include "devices/scsimo.h"
#include "devices/host_services.h"
// Note that these global variables are convenient,
// but might cause issues because they are reused by all tests
extern DeviceFactory& device_factory;
class MockAbstractController : public AbstractController
class MockAbstractController final : public AbstractController
{
public:
@ -51,17 +47,23 @@ public:
MOCK_METHOD(void, FlushUnit, (), ());
MOCK_METHOD(void, Receive, (), ());
MOCK_METHOD(bool, HasUnit, (), (const override));
MOCK_METHOD(int, GetMaxLuns, (), (const override));
MOCK_METHOD(void, SetByteTransfer, (bool), (override));
MOCK_METHOD(void, ScheduleShutdown, (rascsi_shutdown_mode), (override));
MOCK_METHOD(void, SetPhase, (BUS::phase_t), (override));
MOCK_METHOD(void, Reset, (), (override));
FRIEND_TEST(AbstractControllerTest, DeviceLunLifeCycle);
FRIEND_TEST(AbstractControllerTest, ExtractInitiatorId);
FRIEND_TEST(AbstractControllerTest, GetOpcode);
FRIEND_TEST(AbstractControllerTest, GetLun);
explicit MockAbstractController(int target_id) : AbstractController(nullptr, target_id) {}
~MockAbstractController() final = default;
~MockAbstractController() override = default;
int GetMaxLuns() const override { return 32; }
};
class MockScsiController : public ScsiController
class MockScsiController final : public ScsiController
{
public:
@ -90,98 +92,115 @@ public:
MOCK_METHOD(void, SetPhase, (BUS::phase_t), (override));
MOCK_METHOD(void, Sleep, (), ());
FRIEND_TEST(PrimaryDeviceTest, UnitReady);
FRIEND_TEST(PrimaryDeviceTest, TestUnitReady);
FRIEND_TEST(PrimaryDeviceTest, Inquiry);
FRIEND_TEST(PrimaryDeviceTest, RequestSense);
FRIEND_TEST(PrimaryDeviceTest, ReportLuns);
FRIEND_TEST(PrimaryDeviceTest, UnknownCommand);
FRIEND_TEST(DiskTest, Rezero);
FRIEND_TEST(DiskTest, FormatUnit);
FRIEND_TEST(DiskTest, ReassignBlocks);
FRIEND_TEST(DiskTest, Seek);
FRIEND_TEST(DiskTest, ReadCapacity);
FRIEND_TEST(DiskTest, ReadWriteLong);
FRIEND_TEST(DiskTest, ReserveRelease);
FRIEND_TEST(DiskTest, SendDiagnostic);
FRIEND_TEST(DiskTest, PreventAllowMediumRemoval);
FRIEND_TEST(DiskTest, SynchronizeCache);
FRIEND_TEST(DiskTest, ReadDefectData);
using ScsiController::ScsiController;
};
class MockPrimaryDevice : public PrimaryDevice
class MockPrimaryDevice final : public PrimaryDevice
{
FRIEND_TEST(PrimaryDeviceTest, PhaseChange);
FRIEND_TEST(PrimaryDeviceTest, TestUnitReady);
FRIEND_TEST(PrimaryDeviceTest, RequestSense);
FRIEND_TEST(PrimaryDeviceTest, Inquiry);
public:
MOCK_METHOD(vector<byte>, InquiryInternal, (), (const));
MockPrimaryDevice() : PrimaryDevice("test") {}
~MockPrimaryDevice() final = default;
// Make protected methods visible for testing
void SetReady(bool ready) { PrimaryDevice::SetReady(ready); }
void SetReset(bool reset) { PrimaryDevice::SetReset(reset); }
void SetAttn(bool attn) { PrimaryDevice::SetAttn(attn); }
vector<byte> HandleInquiry(device_type type, scsi_level level, bool is_removable) const {
return PrimaryDevice::HandleInquiry(type, level, is_removable);
}
~MockPrimaryDevice() override = default;
};
class MockModePageDevice : public ModePageDevice
class MockModePageDevice final : public ModePageDevice
{
FRIEND_TEST(ModePagesTest, ModePageDevice_AddModePages);
public:
MockModePageDevice() : ModePageDevice("test") {}
~MockModePageDevice() final = default;
~MockModePageDevice() override = default;
MOCK_METHOD(vector<byte>, InquiryInternal, (), (const));
MOCK_METHOD(int, ModeSense6, (const vector<int>&, BYTE *, int), (const override));
MOCK_METHOD(int, ModeSense10, (const vector<int>&, BYTE *, int), (const override));
void AddModePages(map<int, vector<byte>>& pages, int page, bool) const override {
void SetUpModePages(map<int, vector<byte>>& pages, int page, bool) const override {
// Return dummy data for other pages than page 0
if (page) {
vector<byte> buf(255);
pages[page] = buf;
}
}
// Make protected method visible for testing
int AddModePages(const vector<int>& cdb, BYTE *buf, int max_length) const {
return ModePageDevice::AddModePages(cdb, buf, max_length);
}
};
class MockSCSIHD : public SCSIHD
class MockSCSIHD final : public SCSIHD
{
FRIEND_TEST(ModePagesTest, SCSIHD_AddModePages);
FRIEND_TEST(ModePagesTest, SCSIHD_SetUpModePages);
explicit MockSCSIHD(const unordered_set<uint32_t>& sector_sizes) : SCSIHD(sector_sizes, false) {}
~MockSCSIHD() final = default;
~MockSCSIHD() override = default;
};
class MockSCSIHD_NEC : public SCSIHD_NEC //NOSONAR Ignore inheritance hierarchy depth in unit tests
class MockSCSIHD_NEC final : public SCSIHD_NEC //NOSONAR Ignore inheritance hierarchy depth in unit tests
{
FRIEND_TEST(ModePagesTest, SCSIHD_NEC_AddModePages);
FRIEND_TEST(ModePagesTest, SCSIHD_NEC_SetUpModePages);
FRIEND_TEST(DiskTest, Rezero);
FRIEND_TEST(DiskTest, FormatUnit);
FRIEND_TEST(DiskTest, ReassignBlocks);
FRIEND_TEST(DiskTest, Seek);
FRIEND_TEST(DiskTest, ReadCapacity);
FRIEND_TEST(DiskTest, ReadWriteLong);
FRIEND_TEST(DiskTest, ReserveRelease);
FRIEND_TEST(DiskTest, SendDiagnostic);
FRIEND_TEST(DiskTest, PreventAllowMediumRemoval);
FRIEND_TEST(DiskTest, SynchronizeCache);
FRIEND_TEST(DiskTest, ReadDefectData);
FRIEND_TEST(DiskTest, SectorSize);
FRIEND_TEST(DiskTest, ConfiguredSectorSize);
FRIEND_TEST(DiskTest, BlockCount);
MOCK_METHOD(void, FlushCache, (), (override));
MockSCSIHD_NEC() = default;
~MockSCSIHD_NEC() final = default;
~MockSCSIHD_NEC() override = default;
};
class MockSCSICD : public SCSICD
class MockSCSICD final : public SCSICD
{
FRIEND_TEST(ModePagesTest, SCSICD_AddModePages);
FRIEND_TEST(ModePagesTest, SCSICD_SetUpModePages);
explicit MockSCSICD(const unordered_set<uint32_t>& sector_sizes) : SCSICD(sector_sizes) {}
~MockSCSICD() final = default;
~MockSCSICD() override = default;
};
class MockSCSIMO : public SCSIMO
class MockSCSIMO final : public SCSIMO
{
FRIEND_TEST(ModePagesTest, SCSIMO_AddModePages);
FRIEND_TEST(ModePagesTest, SCSIMO_SetUpModePages);
MockSCSIMO(const unordered_set<uint32_t>& sector_sizes, const unordered_map<uint64_t, Geometry>& geometries)
: SCSIMO(sector_sizes, geometries) {}
~MockSCSIMO() final = default;
~MockSCSIMO() override = default;
};
class MockHostServices : public HostServices
class MockHostServices final : public HostServices
{
FRIEND_TEST(ModePagesTest, HostServices_AddModePages);
FRIEND_TEST(ModePagesTest, HostServices_SetUpModePages);
public:
MockHostServices() : HostServices(&DeviceFactory::instance()) {}
~MockHostServices() final = default;
using HostServices::HostServices;
};