mirror of
https://github.com/akuker/RASCSI.git
synced 2025-02-01 11:33:42 +00:00
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:
parent
12b61ada84
commit
016a616b72
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
135
src/raspberrypi/command_util.cpp
Normal file
135
src/raspberrypi/command_util.cpp
Normal 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;
|
||||
}
|
@ -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& = "",
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
using namespace std;
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
|
||||
class BUS;
|
||||
class AbstractController;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
|
125
src/raspberrypi/devices/cd_track.cpp
Normal file
125
src/raspberrypi/devices/cd_track.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(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;
|
||||
}
|
47
src/raspberrypi/devices/cd_track.h
Normal file
47
src/raspberrypi/devices/cd_track.h
Normal file
@ -0,0 +1,47 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(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
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
249
src/raspberrypi/devices/disk_cache.cpp
Normal file
249
src/raspberrypi/devices/disk_cache.cpp
Normal file
@ -0,0 +1,249 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// X68000 EMULATOR "XM6"
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
//
|
||||
// XM6i
|
||||
// Copyright (C) 2010-2015 isaki@NetBSD.org
|
||||
// Copyright (C) 2010 Y.Sugahara
|
||||
//
|
||||
// Imported sava's Anex86/T98Next image and MO format support patch.
|
||||
// Comments translated to english by akuker.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
56
src/raspberrypi/devices/disk_track.h
Normal file
56
src/raspberrypi/devices/disk_track.h
Normal file
@ -0,0 +1,56 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// X68000 EMULATOR "XM6"
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
//
|
||||
// XM6i
|
||||
// Copyright (C) 2010-2015 isaki@NetBSD.org
|
||||
//
|
||||
// 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
|
||||
};
|
@ -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
|
||||
|
@ -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>;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "filepath.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
class Fileio
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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())
|
||||
|
@ -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; }
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -9,6 +9,7 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "rascsi_version.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
// The following should be updated for each release
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -10,6 +10,8 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <cerrno>
|
||||
#include <csignal>
|
||||
#include <unistd.h>
|
||||
#include "os.h"
|
||||
#include "fileio.h"
|
||||
#include "filepath.h"
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
108
src/raspberrypi/socket_connector.cpp
Normal file
108
src/raspberrypi/socket_connector.cpp
Normal 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;
|
||||
}
|
33
src/raspberrypi/socket_connector.h
Normal file
33
src/raspberrypi/socket_connector.h
Normal 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;
|
||||
};
|
@ -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());
|
||||
}
|
||||
|
61
src/raspberrypi/test/command_util_test.cpp
Normal file
61
src/raspberrypi/test/command_util_test.cpp
Normal 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");
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
393
src/raspberrypi/test/disk_test.cpp
Normal file
393
src/raspberrypi/test/disk_test.cpp
Normal 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());
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user