Improve code sharing and dependencies, address code duplication (#976)

This commit is contained in:
Uwe Seimet
2022-11-10 07:44:06 +01:00
committed by GitHub
parent 1c0179e7e3
commit 4fa513090a
104 changed files with 541 additions and 635 deletions
+23
View File
@@ -0,0 +1,23 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
//
//---------------------------------------------------------------------------
#pragma once
//---------------------------------------------------------------------------
//
// Various Operation Settings
//
//---------------------------------------------------------------------------
#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__) || !defined(__linux__)
#undef USE_SEL_EVENT_ENABLE
#endif
+31
View File
@@ -0,0 +1,31 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2020 akuker
// [ Logging utilities ]
//
//---------------------------------------------------------------------------
#pragma once
#include "spdlog/spdlog.h"
#include "spdlog/sinks/sink.h"
static const int LOGBUF_SIZE = 512;
#define SPDLOGWRAPPER(loglevel, ...) \
{ \
char logbuf[LOGBUF_SIZE]; \
snprintf(logbuf, sizeof(logbuf), __VA_ARGS__); \
spdlog::log(loglevel, logbuf); \
};
#define LOGTRACE(...) SPDLOGWRAPPER(spdlog::level::trace, __VA_ARGS__)
#define LOGDEBUG(...) SPDLOGWRAPPER(spdlog::level::debug, __VA_ARGS__)
#define LOGINFO(...) SPDLOGWRAPPER(spdlog::level::info, __VA_ARGS__)
#define LOGWARN(...) SPDLOGWRAPPER(spdlog::level::warn, __VA_ARGS__)
#define LOGERROR(...) SPDLOGWRAPPER(spdlog::level::err, __VA_ARGS__)
+80
View File
@@ -0,0 +1,80 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "shared/protobuf_serializer.h"
#include "shared/rascsi_exceptions.h"
#include "generated/rascsi_interface.pb.h"
#include <unistd.h>
using namespace std;
using namespace rascsi_interface;
//---------------------------------------------------------------------------
//
// Serialize/Deserialize protobuf message: Length followed by the actual data.
// A little endian platform is assumed.
//
//---------------------------------------------------------------------------
void ProtobufSerializer::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 = static_cast<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 ProtobufSerializer::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()) {
throw io_exception("Invalid protobuf message header");
}
const int size = (static_cast<int>(header_buf[3]) << 24) + (static_cast<int>(header_buf[2]) << 16)
+ (static_cast<int>(header_buf[1]) << 8) + static_cast<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 ProtobufSerializer::ReadBytes(int fd, vector<byte>& buf) const
{
size_t offset = 0;
while (offset < buf.size()) {
const ssize_t len = read(fd, &buf.data()[offset], buf.size() - offset);
if (len <= 0) {
return len;
}
offset += len;
}
return offset;
}
+29
View File
@@ -0,0 +1,29 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
// Helper for serializing/deserializing protobuf messages
//
//---------------------------------------------------------------------------
#pragma once
#include "google/protobuf/message.h"
#include <vector>
using namespace std;
class ProtobufSerializer
{
public:
ProtobufSerializer() = default;
~ProtobufSerializer() = default;
void SerializeMessage(int, const google::protobuf::Message&) const;
void DeserializeMessage(int, google::protobuf::Message&) const;
size_t ReadBytes(int, vector<byte>&) const;
};
+146
View File
@@ -0,0 +1,146 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "log.h"
#include "rasutil.h"
#include "protobuf_serializer.h"
#include "protobuf_util.h"
#include <sstream>
using namespace std;
using namespace ras_util;
using namespace rascsi_interface;
#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
void protobuf_util::ParseParameters(PbDeviceDefinition& device, const string& params)
{
if (params.empty()) {
return;
}
// Old style parameter (filename), for backwards compatibility only
if (params.find(KEY_VALUE_SEPARATOR) == string::npos) {
SetParam(device, "file", params);
return;
}
stringstream ss(params);
string p;
while (getline(ss, p, COMPONENT_SEPARATOR)) {
if (!p.empty()) {
const size_t separator_pos = p.find(KEY_VALUE_SEPARATOR);
if (separator_pos != string::npos) {
SetParam(device, p.substr(0, separator_pos), string_view(p).substr(separator_pos + 1));
}
}
}
}
string protobuf_util::GetParam(const PbCommand& command, const string& key)
{
const auto& it = command.params().find(key);
return it != command.params().end() ? it->second : "";
}
string protobuf_util::GetParam(const PbDeviceDefinition& device, const string& key)
{
const auto& it = device.params().find(key);
return it != device.params().end() ? it->second : "";
}
void protobuf_util::SetParam(PbCommand& command, const string& key, string_view value)
{
if (!key.empty() && !value.empty()) {
auto& map = *command.mutable_params();
map[key] = value;
}
}
void protobuf_util::SetParam(PbDevice& device, const string& key, string_view value)
{
if (!key.empty() && !value.empty()) {
auto& map = *device.mutable_params();
map[key] = value;
}
}
void protobuf_util::SetParam(PbDeviceDefinition& device, const string& key, string_view value)
{
if (!key.empty() && !value.empty()) {
auto& map = *device.mutable_params();
map[key] = value;
}
}
void protobuf_util::SetPatternParams(PbCommand& command, string_view patterns)
{
string folder_pattern;
string file_pattern;
if (const size_t separator_pos = patterns.find(COMPONENT_SEPARATOR); separator_pos != string::npos) {
folder_pattern = patterns.substr(0, separator_pos);
file_pattern = patterns.substr(separator_pos + 1);
}
else {
file_pattern = patterns;
}
SetParam(command, "folder_pattern", folder_pattern);
SetParam(command, "file_pattern", file_pattern);
}
string protobuf_util::ListDevices(const list<PbDevice>& pb_devices)
{
if (pb_devices.empty()) {
return "No devices currently attached.\n";
}
ostringstream s;
s << "+----+-----+------+-------------------------------------\n"
<< "| ID | LUN | TYPE | IMAGE FILE\n"
<< "+----+-----+------+-------------------------------------\n";
list<PbDevice> devices = pb_devices;
devices.sort([](const auto& a, const auto& b) { return a.id() < b.id() || a.unit() < b.unit(); });
for (const auto& device : devices) {
string filename;
switch (device.type()) {
case SCBR:
filename = "X68000 HOST BRIDGE";
break;
case SCDP:
filename = "DaynaPort SCSI/Link";
break;
case SCHS:
filename = "Host Services";
break;
case SCLP:
filename = "SCSI Printer";
break;
default:
filename = device.file().name();
break;
}
s << "| " << device.id() << " | " << device.unit() << " | " << PbDeviceType_Name(device.type()) << " | "
<< (filename.empty() ? "NO MEDIUM" : filename)
<< (!device.status().removed() && (device.properties().read_only() || device.status().protected_()) ? " (READ-ONLY)" : "")
<< '\n';
}
s << "+----+-----+------+-------------------------------------\n";
return s.str();
}
+34
View File
@@ -0,0 +1,34 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
// Helper methods for setting up/evaluating protobuf messages
//
//---------------------------------------------------------------------------
#pragma once
#include "google/protobuf/message.h"
#include "generated/rascsi_interface.pb.h"
#include <string>
#include <list>
using namespace std;
using namespace rascsi_interface;
namespace protobuf_util
{
static const char KEY_VALUE_SEPARATOR = '=';
void ParseParameters(PbDeviceDefinition&, const string&);
string GetParam(const PbCommand&, const string&);
string GetParam(const PbDeviceDefinition&, const string&);
void SetParam(PbCommand&, const string&, string_view);
void SetParam(PbDevice&, const string&, string_view);
void SetParam(PbDeviceDefinition&, const string&, string_view);
void SetPatternParams(PbCommand&, string_view);
string ListDevices(const list<PbDevice>&);
}
+45
View File
@@ -0,0 +1,45 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include "scsi.h"
#include <stdexcept>
using namespace std;
class parser_exception : public runtime_error
{
using runtime_error::runtime_error;
};
class io_exception : public runtime_error
{
using runtime_error::runtime_error;
};
class file_not_found_exception : public io_exception
{
using io_exception::io_exception;
};
class scsi_exception : public exception
{
scsi_defs::sense_key sense_key;
scsi_defs::asc asc;
public:
scsi_exception(scsi_defs::sense_key sense_key, scsi_defs::asc asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION)
: sense_key(sense_key), asc(asc) {}
~scsi_exception() override = default;
scsi_defs::sense_key get_sense_key() const { return sense_key; }
scsi_defs::asc get_asc() const { return asc; }
};
+38
View File
@@ -0,0 +1,38 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2020 akuker
// [ Define the version string ]
//
//---------------------------------------------------------------------------
#include "rascsi_version.h"
#include <sstream>
#include <iomanip>
// The following should be updated for each release
const int rascsi_major_version = 22; // Last two digits of year
const int rascsi_minor_version = 11; // Month
const int rascsi_patch_version = -1; // Patch number - increment for each update
using namespace std;
string rascsi_get_version_string()
{
stringstream s;
s << setw(2) << setfill('0') << rascsi_major_version << '.' << rascsi_minor_version;
if (rascsi_patch_version < 0) {
s << " --DEVELOPMENT BUILD--";
}
else {
s << '.' << rascsi_patch_version;
}
return s.str();
}
+18
View File
@@ -0,0 +1,18 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2020 akuker
// [ Define the version string ]
//
//---------------------------------------------------------------------------
#pragma once
#include <string>
extern const int rascsi_major_version; // Last two digits of year
extern const int rascsi_minor_version; // Month
extern const int rascsi_patch_version; // Patch number
std::string rascsi_get_version_string();
+95
View File
@@ -0,0 +1,95 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "rascsi_exceptions.h"
#include "rascsi_version.h"
#include "rasutil.h"
#include <sstream>
#include <algorithm>
using namespace std;
bool ras_util::GetAsUnsignedInt(const string& value, int& result)
{
if (value.find_first_not_of("0123456789") != string::npos) {
return false;
}
try {
auto v = stoul(value);
result = (int)v;
}
catch(const invalid_argument&) {
return false;
}
catch(const out_of_range&) {
return false;
}
return true;
}
void ras_util::ProcessId(const string& id_spec, int max_luns, int& id, int& lun)
{
if (const size_t separator_pos = id_spec.find(COMPONENT_SEPARATOR); separator_pos == string::npos) {
if (!GetAsUnsignedInt(id_spec, id) || id >= 8) {
throw parser_exception("Invalid device ID (0-7)");
}
lun = 0;
}
else if (!GetAsUnsignedInt(id_spec.substr(0, separator_pos), id) || id > 7 ||
!GetAsUnsignedInt(id_spec.substr(separator_pos + 1), lun) || lun >= max_luns) {
throw parser_exception("Invalid LUN (0-" + to_string(max_luns - 1) + ")");
}
}
string ras_util::Banner(const string& app)
{
ostringstream s;
s << "SCSI Target Emulator RaSCSI " << app << " version " << rascsi_get_version_string()
<< " (" << __DATE__ << ' ' << __TIME__ << ")\n";
s << "Powered by XM6 TypeG Technology / ";
s << "Copyright (C) 2016-2020 GIMONS\n";
s << "Copyright (C) 2020-2022 Contributors to the RaSCSI Reloaded project\n";
return s.str();
}
string ras_util::GetExtensionLowerCase(const string& filename)
{
string ext;
if (const size_t separator = filename.rfind('.'); separator != string::npos) {
ext = filename.substr(separator + 1);
}
transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){ return std::tolower(c); });
return ext;
}
// Pin the thread to a specific CPU
// TODO Check whether just using a single CPU really makes sense
void ras_util::FixCpu(int cpu)
{
#ifdef __linux__
// Get the number of CPUs
cpu_set_t mask;
CPU_ZERO(&mask);
sched_getaffinity(0, sizeof(cpu_set_t), &mask);
const int cpus = CPU_COUNT(&mask);
// Set the thread affinity
if (cpu < cpus) {
CPU_ZERO(&mask);
CPU_SET(cpu, &mask);
sched_setaffinity(0, sizeof(cpu_set_t), &mask);
}
#endif
}
+28
View File
@@ -0,0 +1,28 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include <string>
using namespace std;
namespace ras_util
{
// Separator for compound options like ID:LUN
static const char COMPONENT_SEPARATOR = ':';
bool GetAsUnsignedInt(const string&, int&);
void ProcessId(const string&, int, int&, int&);
string Banner(const string&);
string GetExtensionLowerCase(const string&);
void FixCpu(int);
}
+167
View File
@@ -0,0 +1,167 @@
//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
//
//---------------------------------------------------------------------------
#pragma once
#include <unordered_map>
using namespace std;
namespace scsi_defs {
enum class scsi_level : int {
SCSI_1_CCS = 1,
SCSI_2 = 2,
SPC = 3,
SPC_2 = 4,
SPC_3 = 5,
SPC_4 = 6,
SPC_5 = 7,
SPC_6 = 8
};
enum class device_type : int {
DIRECT_ACCESS = 0,
PRINTER = 2,
PROCESSOR = 3,
CD_ROM = 5,
OPTICAL_MEMORY = 7,
COMMUNICATIONS = 9
};
enum class scsi_command : int {
eCmdTestUnitReady = 0x00,
eCmdRezero = 0x01,
eCmdRequestSense = 0x03,
eCmdFormatUnit = 0x04,
eCmdReassignBlocks = 0x07,
eCmdRead6 = 0x08,
// Bridge specific command
eCmdGetMessage10 = 0x08,
// DaynaPort specific command
eCmdRetrieveStats = 0x09,
eCmdWrite6 = 0x0A,
// Bridge specific ommand
eCmdSendMessage10 = 0x0A,
eCmdPrint = 0x0A,
eCmdSeek6 = 0x0B,
// DaynaPort specific command
eCmdSetIfaceMode = 0x0C,
// DaynaPort specific command
eCmdSetMcastAddr = 0x0D,
// DaynaPort specific command
eCmdEnableInterface = 0x0E,
eCmdSynchronizeBuffer = 0x10,
eCmdInquiry = 0x12,
eCmdModeSelect6 = 0x15,
eCmdReserve6 = 0x16,
eCmdRelease6 = 0x17,
eCmdModeSense6 = 0x1A,
eCmdStartStop = 0x1B,
eCmdStopPrint = 0x1B,
eCmdSendDiagnostic = 0x1D,
eCmdPreventAllowMediumRemoval = 0x1E,
eCmdReadCapacity10 = 0x25,
eCmdRead10 = 0x28,
eCmdWrite10 = 0x2A,
eCmdSeek10 = 0x2B,
eCmdVerify10 = 0x2F,
eCmdSynchronizeCache10 = 0x35,
eCmdReadDefectData10 = 0x37,
eCmdReadLong10 = 0x3E,
eCmdWriteLong10 = 0x3F,
eCmdReadToc = 0x43,
eCmdGetEventStatusNotification = 0x4A,
eCmdModeSelect10 = 0x55,
eCmdModeSense10 = 0x5A,
eCmdRead16 = 0x88,
eCmdWrite16 = 0x8A,
eCmdVerify16 = 0x8F,
eCmdSynchronizeCache16 = 0x91,
eCmdReadCapacity16_ReadLong16 = 0x9E,
eCmdWriteLong16 = 0x9F,
eCmdReportLuns = 0xA0
};
enum class status : int {
GOOD = 0x00,
CHECK_CONDITION = 0x02,
RESERVATION_CONFLICT = 0x18
};
enum class sense_key : int {
NO_SENSE = 0x00,
NOT_READY = 0x02,
MEDIUM_ERROR = 0x03,
ILLEGAL_REQUEST = 0x05,
UNIT_ATTENTION = 0x06,
DATA_PROTECT = 0x07,
ABORTED_COMMAND = 0x0b
};
enum class asc : int {
NO_ADDITIONAL_SENSE_INFORMATION = 0x00,
WRITE_FAULT = 0x03,
READ_FAULT = 0x11,
INVALID_COMMAND_OPERATION_CODE = 0x20,
LBA_OUT_OF_RANGE = 0x21,
INVALID_FIELD_IN_CDB = 0x24,
INVALID_LUN = 0x25,
INVALID_FIELD_IN_PARAMETER_LIST = 0x26,
WRITE_PROTECTED = 0x27,
NOT_READY_TO_READY_CHANGE = 0x28,
POWER_ON_OR_RESET = 0x29,
MEDIUM_NOT_PRESENT = 0x3a,
LOAD_OR_EJECT_FAILED = 0x53
};
static const unordered_map<scsi_command, pair<int, const char *>> command_mapping = {
{ scsi_command::eCmdTestUnitReady, make_pair(6, "TestUnitReady") },
{ scsi_command::eCmdRezero, make_pair(6, "Rezero") },
{ scsi_command::eCmdRequestSense, make_pair(6, "RequestSense") },
{ scsi_command::eCmdFormatUnit, make_pair(6, "FormatUnit") },
{ scsi_command::eCmdReassignBlocks, make_pair(6, "ReassignBlocks") },
{ scsi_command::eCmdRead6, make_pair(6, "Read6/GetMessage10") },
{ scsi_command::eCmdRetrieveStats,make_pair( 6, "RetrieveStats") },
{ scsi_command::eCmdWrite6, make_pair(6, "Write6/Print/SendMessage10") },
{ scsi_command::eCmdSeek6, make_pair(6, "Seek6") },
{ scsi_command::eCmdSetIfaceMode, make_pair(6, "SetIfaceMode") },
{ scsi_command::eCmdSetMcastAddr, make_pair(6, "SetMcastAddr") },
{ scsi_command::eCmdEnableInterface, make_pair(6, "EnableInterface") },
{ scsi_command::eCmdSynchronizeBuffer, make_pair(6, "SynchronizeBuffer") },
{ scsi_command::eCmdInquiry, make_pair(6, "Inquiry") },
{ scsi_command::eCmdModeSelect6, make_pair(6, "ModeSelect6") },
{ scsi_command::eCmdReserve6, make_pair(6, "Reserve6") },
{ scsi_command::eCmdRelease6, make_pair(6, "Release6") },
{ scsi_command::eCmdModeSense6, make_pair(6, "ModeSense6") },
{ scsi_command::eCmdStartStop, make_pair(6, "StartStop") },
{ scsi_command::eCmdStopPrint, make_pair(6, "StopPrint") },
{ scsi_command::eCmdSendDiagnostic, make_pair(6, "SendDiagnostic") },
{ scsi_command::eCmdPreventAllowMediumRemoval, make_pair(6, "PreventAllowMediumRemoval") },
{ scsi_command::eCmdReadCapacity10, make_pair(10, "ReadCapacity10") },
{ scsi_command::eCmdRead10, make_pair(10, "Read10") },
{ scsi_command::eCmdWrite10, make_pair(10, "Write10") },
{ scsi_command::eCmdSeek10, make_pair(10, "Seek10") },
{ scsi_command::eCmdVerify10, make_pair(10, "Verify10") },
{ scsi_command::eCmdSynchronizeCache10, make_pair(10, "SynchronizeCache10") },
{ scsi_command::eCmdReadDefectData10, make_pair(10, "ReadDefectData10") },
{ scsi_command::eCmdReadLong10, make_pair(10, "ReadLong10") },
{ scsi_command::eCmdWriteLong10, make_pair(10, "WriteLong10") },
{ scsi_command::eCmdReadToc, make_pair(10, "ReadToc") },
{ scsi_command::eCmdGetEventStatusNotification, make_pair(10, "GetEventStatusNotification") },
{ scsi_command::eCmdModeSelect10, make_pair(10, "ModeSelect10") },
{ scsi_command::eCmdModeSense10, make_pair(10, "ModeSense10") },
{ scsi_command::eCmdRead16, make_pair(16, "Read16") },
{ scsi_command::eCmdWrite16, make_pair(16, "Write16") },
{ scsi_command::eCmdVerify16, make_pair(16, "Verify16") },
{ scsi_command::eCmdSynchronizeCache16, make_pair(16, "SynchronizeCache16") },
{ scsi_command::eCmdReadCapacity16_ReadLong16, make_pair(16, "ReadCapacity16/ReadLong16") },
{ scsi_command::eCmdWriteLong16, make_pair(16, "WriteLong16") },
{ scsi_command::eCmdReportLuns, make_pair(12, "ReportLuns") }
};
};