mirror of
https://github.com/akuker/RASCSI.git
synced 2024-05-27 17:48:29 +00:00
Update based on new scsidump code
This commit is contained in:
parent
c50dd13ee3
commit
5306061771
273
cpp/scsisend/phase_executor.cpp
Normal file
273
cpp/scsisend/phase_executor.cpp
Normal file
|
@ -0,0 +1,273 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "hal/bus.h"
|
||||
#include "phase_executor.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std;
|
||||
using namespace spdlog;
|
||||
|
||||
void PhaseExecutor::Reset() const
|
||||
{
|
||||
bus.SetDAT(0);
|
||||
bus.SetBSY(false);
|
||||
bus.SetSEL(false);
|
||||
bus.SetATN(false);
|
||||
}
|
||||
|
||||
bool PhaseExecutor::Execute(scsi_command cmd, span<uint8_t> cdb, span<uint8_t> buffer, size_t length)
|
||||
{
|
||||
status = 0;
|
||||
size = 0;
|
||||
|
||||
spdlog::trace(
|
||||
fmt::format("Executing {0} for target {1}:{2}", command_mapping.find(cmd)->second.second, target_id,
|
||||
target_lun));
|
||||
|
||||
if (!Arbitration()) {
|
||||
bus.Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Selection()) {
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Timeout 3 s
|
||||
auto now = chrono::steady_clock::now();
|
||||
while ((chrono::duration_cast<chrono::seconds>(chrono::steady_clock::now() - now).count()) < 3) {
|
||||
bus.Acquire();
|
||||
|
||||
if (bus.GetREQ()) {
|
||||
try {
|
||||
if (Dispatch(cmd, cdb, buffer, static_cast<int>(length))) {
|
||||
now = chrono::steady_clock::now();
|
||||
}
|
||||
else {
|
||||
bus.Reset();
|
||||
return !GetStatus();
|
||||
}
|
||||
}
|
||||
catch (const phase_exception &e) {
|
||||
cerr << "Error: " << e.what() << endl;
|
||||
bus.Reset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PhaseExecutor::Dispatch(scsi_command cmd, span<uint8_t> cdb, span<uint8_t> buffer, int length)
|
||||
{
|
||||
const phase_t phase = bus.GetPhase();
|
||||
|
||||
spdlog::trace(string("Handling ") + BUS::GetPhaseStrRaw(phase) + " phase");
|
||||
|
||||
switch (phase) {
|
||||
case phase_t::command:
|
||||
Command(cmd, cdb);
|
||||
break;
|
||||
|
||||
case phase_t::status:
|
||||
Status();
|
||||
break;
|
||||
|
||||
case phase_t::datain:
|
||||
DataIn(buffer, length);
|
||||
break;
|
||||
|
||||
case phase_t::dataout:
|
||||
DataOut(buffer, length);
|
||||
break;
|
||||
|
||||
case phase_t::msgin:
|
||||
MsgIn();
|
||||
// Done with this command cycle
|
||||
return false;
|
||||
|
||||
case phase_t::msgout:
|
||||
MsgOut();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw phase_exception(string("Ignoring ") + BUS::GetPhaseStrRaw(phase) + " phase");
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PhaseExecutor::Arbitration() const
|
||||
{
|
||||
if (!WaitForFree()) {
|
||||
spdlog::trace("Bus is not free");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sleep(BUS_FREE_DELAY);
|
||||
|
||||
bus.SetDAT(static_cast<uint8_t>(1 << initiator_id));
|
||||
|
||||
bus.SetBSY(true);
|
||||
|
||||
Sleep(ARBITRATION_DELAY);
|
||||
|
||||
if (bus.GetDAT() > (1 << initiator_id)) {
|
||||
spdlog::trace(
|
||||
fmt::format("Lost ARBITRATION, competing initiator ID is {}", bus.GetDAT() - (1 << initiator_id)));
|
||||
return false;
|
||||
}
|
||||
|
||||
bus.SetSEL(true);
|
||||
|
||||
Sleep(BUS_CLEAR_DELAY);
|
||||
Sleep(BUS_SETTLE_DELAY);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PhaseExecutor::Selection() const
|
||||
{
|
||||
bus.SetDAT(static_cast<uint8_t>((1 << initiator_id) + (1 << target_id)));
|
||||
|
||||
bus.SetSEL(true);
|
||||
|
||||
// Request MESSAGE OUT for IDENTIFY
|
||||
bus.SetATN(true);
|
||||
|
||||
Sleep(DESKEW_DELAY);
|
||||
Sleep(DESKEW_DELAY);
|
||||
|
||||
bus.SetBSY(false);
|
||||
|
||||
Sleep(BUS_SETTLE_DELAY);
|
||||
|
||||
if (!WaitForBusy()) {
|
||||
spdlog::trace("SELECTION failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sleep(DESKEW_DELAY);
|
||||
Sleep(DESKEW_DELAY);
|
||||
|
||||
bus.SetSEL(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PhaseExecutor::Command(scsi_command cmd, span<uint8_t> cdb) const
|
||||
{
|
||||
cdb[0] = static_cast<uint8_t>(cmd);
|
||||
if (target_lun < 8) {
|
||||
// Encode LUN in the CDB for backwards compatibility with SCSI-1-CCS
|
||||
cdb[1] = static_cast<uint8_t>(cdb[1] + (target_lun << 5));
|
||||
}
|
||||
|
||||
if (static_cast<int>(cdb.size()) !=
|
||||
bus.SendHandShake(cdb.data(), static_cast<int>(cdb.size()), BUS::SEND_NO_DELAY)) {
|
||||
throw phase_exception(command_mapping.find(cmd)->second.second + string(" failed"));
|
||||
}
|
||||
}
|
||||
|
||||
void PhaseExecutor::Status()
|
||||
{
|
||||
array<uint8_t, 1> buf;
|
||||
|
||||
if (bus.ReceiveHandShake(buf.data(), 1) != 1) {
|
||||
throw phase_exception("STATUS failed");
|
||||
}
|
||||
|
||||
status = buf[0];
|
||||
}
|
||||
|
||||
void PhaseExecutor::DataIn(span<uint8_t> buffer, int length)
|
||||
{
|
||||
size = bus.ReceiveHandShake(buffer.data(), length);
|
||||
if (!size) {
|
||||
throw phase_exception("DATA IN failed");
|
||||
}
|
||||
}
|
||||
|
||||
void PhaseExecutor::DataOut(span<uint8_t> buffer, int length)
|
||||
{
|
||||
if (!bus.SendHandShake(buffer.data(), length, BUS::SEND_NO_DELAY)) {
|
||||
throw phase_exception("DATA OUT failed");
|
||||
}
|
||||
}
|
||||
|
||||
void PhaseExecutor::MsgIn() const
|
||||
{
|
||||
array<uint8_t, 1> buf;
|
||||
|
||||
if (bus.ReceiveHandShake(buf.data(), 1) != 1) {
|
||||
throw phase_exception("MESSAGE IN failed");
|
||||
}
|
||||
|
||||
if (buf[0]) {
|
||||
throw phase_exception("MESSAGE IN did not report COMMAND COMPLETE");
|
||||
}
|
||||
}
|
||||
|
||||
void PhaseExecutor::MsgOut() const
|
||||
{
|
||||
array<uint8_t, 1> buf;
|
||||
|
||||
// IDENTIFY
|
||||
buf[0] = static_cast<uint8_t>(target_lun | 0x80);
|
||||
|
||||
if (bus.SendHandShake(buf.data(), buf.size(), BUS::SEND_NO_DELAY) != buf.size()) {
|
||||
throw phase_exception("MESSAGE OUT failed");
|
||||
}
|
||||
}
|
||||
|
||||
bool PhaseExecutor::WaitForFree() const
|
||||
{
|
||||
// Wait for up to 2 s
|
||||
int count = 10000;
|
||||
do {
|
||||
// Wait 20 ms
|
||||
Sleep( { .tv_sec = 0, .tv_nsec = 20'000 });
|
||||
bus.Acquire();
|
||||
if (!bus.GetBSY() && !bus.GetSEL()) {
|
||||
return true;
|
||||
}
|
||||
} while (count--);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PhaseExecutor::WaitForBusy() const
|
||||
{
|
||||
// Wait for up to 2 s
|
||||
int count = 10000;
|
||||
do {
|
||||
// Wait 20 ms
|
||||
Sleep( { .tv_sec = 0, .tv_nsec = 20'000 });
|
||||
bus.Acquire();
|
||||
if (bus.GetBSY()) {
|
||||
return true;
|
||||
}
|
||||
} while (count--);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PhaseExecutor::SetTarget(int id, int lun)
|
||||
{
|
||||
target_id = id;
|
||||
target_lun = lun;
|
||||
}
|
97
cpp/scsisend/phase_executor.h
Normal file
97
cpp/scsisend/phase_executor.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/bus.h"
|
||||
#include <ctime>
|
||||
#include <stdexcept>
|
||||
#include <span>
|
||||
|
||||
using namespace std;
|
||||
using namespace scsi_defs;
|
||||
|
||||
class PhaseExecutor
|
||||
{
|
||||
class phase_exception: public runtime_error
|
||||
{
|
||||
using runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
PhaseExecutor(BUS &b, int id) : bus(b), initiator_id(id)
|
||||
{
|
||||
}
|
||||
~PhaseExecutor() = default;
|
||||
|
||||
void SetTarget(int, int);
|
||||
bool Execute(scsi_command, span<uint8_t>, span<uint8_t>, size_t);
|
||||
|
||||
int GetSize() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool Dispatch(scsi_command, span<uint8_t>, span<uint8_t>, int);
|
||||
|
||||
void Reset() const;
|
||||
|
||||
bool Arbitration() const;
|
||||
bool Selection() const;
|
||||
void Command(scsi_command, span<uint8_t>) const;
|
||||
void Status();
|
||||
void DataIn(span<uint8_t>, int);
|
||||
void DataOut(span<uint8_t>, int);
|
||||
void MsgIn() const;
|
||||
void MsgOut() const;
|
||||
|
||||
bool WaitForFree() const;
|
||||
bool WaitForBusy() const;
|
||||
|
||||
int GetStatus() const
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
inline void Sleep(const timespec &ns) const
|
||||
{
|
||||
nanosleep(&ns, nullptr);
|
||||
}
|
||||
|
||||
BUS &bus;
|
||||
|
||||
int initiator_id;
|
||||
|
||||
int target_id = -1;
|
||||
int target_lun = -1;
|
||||
|
||||
int status = -1;
|
||||
|
||||
int size = 0;
|
||||
|
||||
// Timeout values see bus.h
|
||||
|
||||
inline static const long BUS_SETTLE_DELAY_NS = 400;
|
||||
inline static const timespec BUS_SETTLE_DELAY = { .tv_sec = 0, .tv_nsec = BUS_SETTLE_DELAY_NS };
|
||||
|
||||
inline static const long BUS_CLEAR_DELAY_NS = 800;
|
||||
inline static const timespec BUS_CLEAR_DELAY = { .tv_sec = 0, .tv_nsec = BUS_CLEAR_DELAY_NS };
|
||||
|
||||
inline static const long BUS_FREE_DELAY_NS = 800;
|
||||
inline static const timespec BUS_FREE_DELAY = { .tv_sec = 0, .tv_nsec = BUS_FREE_DELAY_NS };
|
||||
|
||||
inline static const long DESKEW_DELAY_NS = 45;
|
||||
inline static const timespec DESKEW_DELAY = { .tv_sec = 0, .tv_nsec = DESKEW_DELAY_NS };
|
||||
|
||||
inline static const long ARBITRATION_DELAY_NS = 2'400;
|
||||
inline static const timespec ARBITRATION_DELAY = { .tv_sec = 0, .tv_nsec = ARBITRATION_DELAY_NS };
|
||||
};
|
72
cpp/scsisend/scsi_executor.cpp
Normal file
72
cpp/scsisend/scsi_executor.cpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/scsi.h"
|
||||
#include "scsisend/scsi_executor.h"
|
||||
#include "generated/piscsi_interface.pb.h"
|
||||
#include <google/protobuf/util/json_util.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace std;
|
||||
using namespace filesystem;
|
||||
using namespace spdlog;
|
||||
using namespace scsi_defs;
|
||||
using namespace piscsi_interface;
|
||||
|
||||
void ScsiExecutor::Execute(bool binary)
|
||||
{
|
||||
const string file = binary ? "test.bin" : "test.json";
|
||||
|
||||
int size = 0;
|
||||
if (!binary) {
|
||||
ifstream in(file);
|
||||
assert(!in.fail());
|
||||
stringstream buf;
|
||||
buf << in.rdbuf();
|
||||
const string json = buf.str();
|
||||
memcpy(buffer.data(), json.data(), json.size());
|
||||
size = json.size();
|
||||
}
|
||||
else {
|
||||
ifstream in(file, ios::binary);
|
||||
assert(!in.fail());
|
||||
vector<char> b(file_size(file));
|
||||
in.read(b.data(), b.size());
|
||||
memcpy(buffer.data(), b.data(), b.size());
|
||||
size = b.size();
|
||||
}
|
||||
|
||||
vector<uint8_t> cdb(10);
|
||||
cdb[1] = binary ? 0x0a : 0x05;
|
||||
cdb[5] = static_cast<uint8_t>(size >> 8);
|
||||
cdb[6] = static_cast<uint8_t>(size);
|
||||
cdb[7] = static_cast<uint8_t>(65535 >> 8);
|
||||
cdb[8] = static_cast<uint8_t>(65535);
|
||||
phase_executor->Execute(scsi_command::eCmdExecute, cdb, buffer, 65535);
|
||||
|
||||
const int length = phase_executor->GetSize();
|
||||
|
||||
if (!binary) {
|
||||
const string json((const char *)buffer.data(), length);
|
||||
cerr << "json received:\n" << json << endl;
|
||||
}
|
||||
else {
|
||||
PbResult result;
|
||||
if (!result.ParseFromArray(buffer.data(), length)) {
|
||||
assert(false);
|
||||
}
|
||||
string json;
|
||||
google::protobuf::util::MessageToJsonString(result, &json);
|
||||
cerr << "json (converted from binary) received:\n" << json << endl;
|
||||
}
|
||||
}
|
42
cpp/scsisend/scsi_executor.h
Normal file
42
cpp/scsisend/scsi_executor.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include "scsisend/phase_executor.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class ScsiExecutor
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
ScsiExecutor(BUS &bus, int id)
|
||||
{
|
||||
phase_executor = make_unique<PhaseExecutor>(bus, id);
|
||||
}
|
||||
~ScsiExecutor() = default;
|
||||
|
||||
void Execute(bool);
|
||||
|
||||
void SetTarget(int id, int lun)
|
||||
{
|
||||
phase_executor->SetTarget(id, lun);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
vector<uint8_t> buffer = vector<uint8_t>(65536);
|
||||
|
||||
unique_ptr<PhaseExecutor> phase_executor;
|
||||
};
|
|
@ -15,5 +15,5 @@ int main(int argc, char *argv[])
|
|||
{
|
||||
vector<char *> args(argv, argv + argc);
|
||||
|
||||
return ScsiDump().run(args);
|
||||
return ScsiSend().run(args);
|
||||
}
|
||||
|
|
|
@ -8,64 +8,58 @@
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "scsisend/scsisend_core.h"
|
||||
#include "hal/sbc_version.h"
|
||||
#include "hal/gpiobus_factory.h"
|
||||
#include "hal/systimer.h"
|
||||
#include "controllers/controller_manager.h"
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include "shared/piscsi_util.h"
|
||||
#include "generated/piscsi_interface.pb.h"
|
||||
#include <google/protobuf/util/json_util.h>
|
||||
#include <unistd.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <filesystem>
|
||||
#include <chrono>
|
||||
#include <csignal>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
#include <array>
|
||||
|
||||
using namespace std;
|
||||
using namespace filesystem;
|
||||
using namespace spdlog;
|
||||
using namespace scsi_defs;
|
||||
using namespace piscsi_util;
|
||||
using namespace piscsi_interface;
|
||||
|
||||
void ScsiDump::CleanUp()
|
||||
void ScsiSend::CleanUp() const
|
||||
{
|
||||
if (bus != nullptr) {
|
||||
bus->Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void ScsiDump::TerminationHandler(int)
|
||||
void ScsiSend::TerminationHandler(int)
|
||||
{
|
||||
CleanUp();
|
||||
instance->bus->SetRST(true);
|
||||
|
||||
// Process will terminate automatically
|
||||
instance->CleanUp();
|
||||
|
||||
// Process will terminate automatically
|
||||
}
|
||||
|
||||
bool ScsiDump::Banner(span<char *> args) const
|
||||
bool ScsiSend::Banner(span<char*> args) const
|
||||
{
|
||||
cout << piscsi_util::Banner("(PiSCSI Action Trigger Utility)");
|
||||
cout << piscsi_util::Banner("(SCSI Action Trigger Utility)");
|
||||
|
||||
if (args.size() < 2 || string(args[1]) == "-h" || string(args[1]) == "--help") {
|
||||
cout << "Usage: " << args[0] << " -t ID[:LUN] [-i BID] -f FILE [-v] [-r] [-s BUFFER_SIZE] [-p] [-I] [-S]\n"
|
||||
<< " ID is the target device ID (0-" << (ControllerManager::GetScsiIdMax() - 1) << ").\n"
|
||||
<< " LUN is the optional target device LUN (0-" << (ControllerManager::GetScsiLunMax() -1 ) << ")."
|
||||
<< " Default is 0.\n"
|
||||
<< " BID is the PiSCSI board ID (0-7). Default is 7.\n"
|
||||
<< " FILE is the dump file path.\n"
|
||||
<< " BUFFER_SIZE is the transfer buffer size in bytes, at least " << MINIMUM_BUFFER_SIZE
|
||||
<< " bytes. Default is 1 MiB.\n"
|
||||
<< " -v Enable verbose logging.\n"
|
||||
<< " -r Restore instead of dump.\n"
|
||||
<< " -p Generate .properties file to be used with the PiSCSI web interface. Only valid for dump mode.\n"
|
||||
<< " -I Display INQUIRY data of ID[:LUN].\n"
|
||||
<< " -S Scan SCSI bus for devices.\n"
|
||||
<< flush;
|
||||
cout << "Usage: " << args[0] << " -t ID[:LUN] [-i BID] [-f FILE] [-a] [-r] [-b BUFFER_SIZE]"
|
||||
<< " [-L log_level] [-p] [-I] [-s]\n"
|
||||
<< " ID is the target device ID (0-" << (ControllerManager::GetScsiIdMax() - 1) << ").\n"
|
||||
<< " LUN is the optional target device LUN (0-" << (ControllerManager::GetScsiLunMax() - 1) << ")."
|
||||
<< " Default is 0.\n"
|
||||
<< " BID is the PiSCSI board ID (0-7). Default is 7.\n"
|
||||
<< " FILE is the protobuf input data path.\n"
|
||||
<< " BUFFER_SIZE is the transfer buffer size in bytes, at least " << MINIMUM_BUFFER_SIZE
|
||||
<< " bytes. Default is 1 MiB.\n"
|
||||
<< flush;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -73,29 +67,33 @@ bool ScsiDump::Banner(span<char *> args) const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ScsiDump::Init() const
|
||||
bool ScsiSend::Init(bool in_process)
|
||||
{
|
||||
// Signal handler for cleaning up
|
||||
struct sigaction termination_handler;
|
||||
termination_handler.sa_handler = TerminationHandler;
|
||||
sigemptyset(&termination_handler.sa_mask);
|
||||
termination_handler.sa_flags = 0;
|
||||
sigaction(SIGTERM, &termination_handler, nullptr);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
instance = this;
|
||||
// Signal handler for cleaning up
|
||||
struct sigaction termination_handler;
|
||||
termination_handler.sa_handler = TerminationHandler;
|
||||
sigemptyset(&termination_handler.sa_mask);
|
||||
termination_handler.sa_flags = 0;
|
||||
sigaction(SIGINT, &termination_handler, nullptr);
|
||||
sigaction(SIGTERM, &termination_handler, nullptr);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
bus = GPIOBUS_Factory::Create(BUS::mode_e::INITIATOR);
|
||||
|
||||
if (bus != nullptr) {
|
||||
scsi_executor = make_unique<ScsiExecutor>(*bus, initiator_id);
|
||||
}
|
||||
|
||||
return bus != nullptr;
|
||||
}
|
||||
|
||||
void ScsiDump::ParseArguments(span<char *> args)
|
||||
void ScsiSend::ParseArguments(span<char*> args)
|
||||
{
|
||||
int opt;
|
||||
|
||||
int buffer_size = DEFAULT_BUFFER_SIZE;
|
||||
|
||||
optind = 1;
|
||||
opterr = 0;
|
||||
while ((opt = getopt(static_cast<int>(args.size()), args.data(), "i:f:s:t:rvpIS")) != -1) {
|
||||
int opt;
|
||||
while ((opt = getopt(static_cast<int>(args.size()), args.data(), "i:f:b:t:L:arspI")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
if (!GetAsUnsignedInt(optarg, initiator_id) || initiator_id > 7) {
|
||||
|
@ -107,37 +105,22 @@ void ScsiDump::ParseArguments(span<char *> args)
|
|||
filename = optarg;
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
inquiry = true;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if (!GetAsUnsignedInt(optarg, buffer_size) || buffer_size < MINIMUM_BUFFER_SIZE) {
|
||||
throw parser_exception("Buffer size must be at least " + to_string(MINIMUM_BUFFER_SIZE / 1024) + " KiB");
|
||||
}
|
||||
case 'b':
|
||||
// if (!GetAsUnsignedInt(optarg, buffer_size) || buffer_size < MINIMUM_BUFFER_SIZE) {
|
||||
// throw parser_exception(
|
||||
// "Buffer size must be at least " + to_string(MINIMUM_BUFFER_SIZE / 1024) + " KiB");
|
||||
// }
|
||||
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
scan_bus = true;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
if (const string error = ProcessId(optarg, target_id, target_lun); !error.empty()) {
|
||||
throw parser_exception(error);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
set_level(level::debug);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
restore = true;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
properties_file = true;
|
||||
case 'L':
|
||||
log_level = optarg;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -145,330 +128,14 @@ void ScsiDump::ParseArguments(span<char *> args)
|
|||
}
|
||||
}
|
||||
|
||||
if (!scan_bus && !inquiry && filename.empty()) {
|
||||
throw parser_exception("Missing filename");
|
||||
}
|
||||
|
||||
if (!scan_bus && target_id == -1) {
|
||||
throw parser_exception("Missing target ID");
|
||||
}
|
||||
|
||||
if (target_id == initiator_id) {
|
||||
throw parser_exception("Target ID and PiSCSI board ID must not be identical");
|
||||
}
|
||||
|
||||
if (target_lun == -1) {
|
||||
target_lun = 0;
|
||||
target_lun = 0;
|
||||
}
|
||||
|
||||
if (scan_bus) {
|
||||
inquiry = false;
|
||||
}
|
||||
|
||||
buffer = vector<uint8_t>(buffer_size);
|
||||
scsi_executor->Execute(false);
|
||||
}
|
||||
|
||||
void ScsiDump::WaitForPhase(phase_t phase) const
|
||||
{
|
||||
spdlog::debug(string("Waiting for ") + BUS::GetPhaseStrRaw(phase) + " phase");
|
||||
|
||||
// Timeout (3000ms)
|
||||
const uint32_t now = SysTimer::GetTimerLow();
|
||||
while ((SysTimer::GetTimerLow() - now) < 3'000'000) {
|
||||
bus->Acquire();
|
||||
if (bus->GetREQ() && bus->GetPhase() == phase) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw phase_exception("Expected " + string(BUS::GetPhaseStrRaw(phase)) + " phase, actual phase is " +
|
||||
string(BUS::GetPhaseStrRaw(bus->GetPhase())));
|
||||
}
|
||||
|
||||
void ScsiDump::Selection() const
|
||||
{
|
||||
// Set initiator and target ID
|
||||
auto data = static_cast<byte>(1 << initiator_id);
|
||||
data |= static_cast<byte>(1 << target_id);
|
||||
bus->SetDAT(static_cast<uint8_t>(data));
|
||||
|
||||
bus->SetSEL(true);
|
||||
|
||||
WaitForBusy();
|
||||
|
||||
bus->SetSEL(false);
|
||||
}
|
||||
|
||||
void ScsiDump::Command(scsi_command cmd, vector<uint8_t>& cdb) const
|
||||
{
|
||||
spdlog::debug("Executing " + command_mapping.find(cmd)->second.second);
|
||||
|
||||
Selection();
|
||||
|
||||
WaitForPhase(phase_t::command);
|
||||
|
||||
cdb[0] = static_cast<uint8_t>(cmd);
|
||||
cdb[1] = static_cast<uint8_t>(static_cast<byte>(cdb[1]) | static_cast<byte>(target_lun << 5));
|
||||
if (static_cast<int>(cdb.size()) !=
|
||||
bus->SendHandShake(cdb.data(), static_cast<int>(cdb.size()), BUS::SEND_NO_DELAY)) {
|
||||
BusFree();
|
||||
|
||||
throw phase_exception(command_mapping.find(cmd)->second.second + string(" failed"));
|
||||
}
|
||||
}
|
||||
|
||||
int ScsiDump::DataIn(int length)
|
||||
{
|
||||
WaitForPhase(phase_t::datain);
|
||||
|
||||
const int received = bus->ReceiveHandShake(buffer.data(), length);
|
||||
if (!received) {
|
||||
throw phase_exception("DATA IN failed");
|
||||
}
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
void ScsiDump::DataOut(int length)
|
||||
{
|
||||
WaitForPhase(phase_t::dataout);
|
||||
|
||||
if (!bus->SendHandShake(buffer.data(), length, BUS::SEND_NO_DELAY)) {
|
||||
throw phase_exception("DATA OUT failed");
|
||||
}
|
||||
}
|
||||
|
||||
void ScsiDump::Status() const
|
||||
{
|
||||
WaitForPhase(phase_t::status);
|
||||
|
||||
if (array<uint8_t, 256> buf; bus->ReceiveHandShake(buf.data(), 1) != 1) {
|
||||
throw phase_exception("STATUS failed");
|
||||
}
|
||||
}
|
||||
|
||||
void ScsiDump::MessageIn() const
|
||||
{
|
||||
WaitForPhase(phase_t::msgin);
|
||||
|
||||
if (array<uint8_t, 256> buf; bus->ReceiveHandShake(buf.data(), 1) != 1) {
|
||||
throw phase_exception("MESSAGE IN failed");
|
||||
}
|
||||
}
|
||||
|
||||
void ScsiDump::BusFree() const
|
||||
{
|
||||
bus->Reset();
|
||||
}
|
||||
|
||||
void ScsiDump::TestUnitReady() const
|
||||
{
|
||||
vector<uint8_t> cdb(6);
|
||||
Command(scsi_command::eCmdTestUnitReady, cdb);
|
||||
|
||||
Status();
|
||||
|
||||
MessageIn();
|
||||
|
||||
BusFree();
|
||||
}
|
||||
|
||||
void ScsiDump::RequestSense()
|
||||
{
|
||||
vector<uint8_t> cdb(6);
|
||||
cdb[4] = 0xff;
|
||||
Command(scsi_command::eCmdRequestSense, cdb);
|
||||
|
||||
DataIn(256);
|
||||
|
||||
Status();
|
||||
|
||||
MessageIn();
|
||||
|
||||
BusFree();
|
||||
}
|
||||
|
||||
void ScsiDump::Inquiry()
|
||||
{
|
||||
vector<uint8_t> cdb(6);
|
||||
cdb[4] = 0xff;
|
||||
Command(scsi_command::eCmdInquiry, cdb);
|
||||
|
||||
DataIn(256);
|
||||
|
||||
Status();
|
||||
|
||||
MessageIn();
|
||||
|
||||
BusFree();
|
||||
}
|
||||
|
||||
void ScsiDump::Execute()
|
||||
{
|
||||
const string file = restore ? "test.bin" : "test.json";
|
||||
|
||||
// restore means binary
|
||||
int size = 0;
|
||||
if (!restore) {
|
||||
ifstream in(file);
|
||||
assert(!in.fail());
|
||||
stringstream buf;
|
||||
buf << in.rdbuf();
|
||||
const string json = buf.str();
|
||||
memcpy(buffer.data(), json.data(), json.size());
|
||||
size = json.size();
|
||||
}
|
||||
else {
|
||||
ifstream in(file, ios::binary);
|
||||
assert(!in.fail());
|
||||
vector<char> b(file_size(file));
|
||||
in.read(b.data(), b.size());
|
||||
memcpy(buffer.data(), b.data(), b.size());
|
||||
size = b.size();
|
||||
}
|
||||
|
||||
vector<uint8_t> cdb(10);
|
||||
cdb[1] = restore ? 0x0a : 0x05;
|
||||
cdb[5] = static_cast<uint8_t>(size >> 8);
|
||||
cdb[6] = static_cast<uint8_t>(size);
|
||||
cdb[7] = static_cast<uint8_t>(65535 >> 8);
|
||||
cdb[8] = static_cast<uint8_t>(65535);
|
||||
Command(scsi_command::eCmdExecute, cdb);
|
||||
|
||||
DataOut(size);
|
||||
|
||||
const int length = DataIn(65535);
|
||||
|
||||
if (!restore) {
|
||||
const string json((const char *)buffer.data(), length);
|
||||
cerr << "json received:\n" << json << endl;
|
||||
}
|
||||
else {
|
||||
PbResult result;
|
||||
if (!result.ParseFromArray(buffer.data(), length)) {
|
||||
assert(false);
|
||||
}
|
||||
string json;
|
||||
google::protobuf::util::MessageToJsonString(result, &json);
|
||||
cerr << "json (converted from binary) received:\n" << json << endl;
|
||||
}
|
||||
|
||||
Status();
|
||||
|
||||
MessageIn();
|
||||
|
||||
BusFree();
|
||||
}
|
||||
|
||||
pair<uint64_t, uint32_t> ScsiDump::ReadCapacity()
|
||||
{
|
||||
vector<uint8_t> cdb(10);
|
||||
Command(scsi_command::eCmdReadCapacity10, cdb);
|
||||
|
||||
DataIn(8);
|
||||
|
||||
Status();
|
||||
|
||||
MessageIn();
|
||||
|
||||
BusFree();
|
||||
|
||||
uint64_t capacity = (static_cast<uint32_t>(buffer[0]) << 24) | (static_cast<uint32_t>(buffer[1]) << 16) |
|
||||
(static_cast<uint32_t>(buffer[2]) << 8) | static_cast<uint32_t>(buffer[3]);
|
||||
|
||||
int sector_size_offset = 4;
|
||||
|
||||
if (static_cast<int32_t>(capacity) == -1) {
|
||||
cdb.resize(16);
|
||||
// READ CAPACITY(16), not READ LONG(16)
|
||||
cdb[1] = 0x10;
|
||||
Command(scsi_command::eCmdReadCapacity16_ReadLong16, cdb);
|
||||
|
||||
DataIn(14);
|
||||
|
||||
Status();
|
||||
|
||||
MessageIn();
|
||||
|
||||
BusFree();
|
||||
|
||||
capacity = (static_cast<uint64_t>(buffer[0]) << 56) | (static_cast<uint64_t>(buffer[1]) << 48) |
|
||||
(static_cast<uint64_t>(buffer[2]) << 40) | (static_cast<uint64_t>(buffer[3]) << 32) |
|
||||
(static_cast<uint64_t>(buffer[4]) << 24) | (static_cast<uint64_t>(buffer[5]) << 16) |
|
||||
(static_cast<uint64_t>(buffer[6]) << 8) | static_cast<uint64_t>(buffer[7]);
|
||||
|
||||
sector_size_offset = 8;
|
||||
}
|
||||
|
||||
const uint32_t sector_size = (static_cast<uint32_t>(buffer[sector_size_offset]) << 24) |
|
||||
(static_cast<uint32_t>(buffer[sector_size_offset + 1]) << 16) |
|
||||
(static_cast<uint32_t>(buffer[sector_size_offset + 2]) << 8) |
|
||||
static_cast<uint32_t>(buffer[sector_size_offset + 3]);
|
||||
|
||||
return { capacity, sector_size };
|
||||
}
|
||||
|
||||
void ScsiDump::Read10(uint32_t bstart, uint32_t blength, uint32_t length)
|
||||
{
|
||||
vector<uint8_t> cdb(10);
|
||||
cdb[2] = (uint8_t)(bstart >> 24);
|
||||
cdb[3] = (uint8_t)(bstart >> 16);
|
||||
cdb[4] = (uint8_t)(bstart >> 8);
|
||||
cdb[5] = (uint8_t)bstart;
|
||||
cdb[7] = (uint8_t)(blength >> 8);
|
||||
cdb[8] = (uint8_t)blength;
|
||||
Command(scsi_command::eCmdRead10, cdb);
|
||||
|
||||
DataIn(length);
|
||||
|
||||
Status();
|
||||
|
||||
MessageIn();
|
||||
|
||||
BusFree();
|
||||
}
|
||||
|
||||
void ScsiDump::Write10(uint32_t bstart, uint32_t blength, uint32_t length)
|
||||
{
|
||||
vector<uint8_t> cdb(10);
|
||||
cdb[2] = (uint8_t)(bstart >> 24);
|
||||
cdb[3] = (uint8_t)(bstart >> 16);
|
||||
cdb[4] = (uint8_t)(bstart >> 8);
|
||||
cdb[5] = (uint8_t)bstart;
|
||||
cdb[7] = (uint8_t)(blength >> 8);
|
||||
cdb[8] = (uint8_t)blength;
|
||||
Command(scsi_command::eCmdWrite10, cdb);
|
||||
|
||||
DataOut(length);
|
||||
|
||||
Status();
|
||||
|
||||
MessageIn();
|
||||
|
||||
BusFree();
|
||||
}
|
||||
|
||||
void ScsiDump::WaitForBusy() const
|
||||
{
|
||||
// Wait for busy for up to 2 s
|
||||
int count = 10000;
|
||||
do {
|
||||
// Wait 20 ms
|
||||
const timespec ts = {.tv_sec = 0, .tv_nsec = 20 * 1000};
|
||||
nanosleep(&ts, nullptr);
|
||||
bus->Acquire();
|
||||
if (bus->GetBSY()) {
|
||||
break;
|
||||
}
|
||||
} while (count--);
|
||||
|
||||
// Success if the target is busy
|
||||
if (!bus->GetBSY()) {
|
||||
throw phase_exception("SELECTION failed");
|
||||
}
|
||||
}
|
||||
|
||||
int ScsiDump::run(span<char *> args)
|
||||
int ScsiSend::run(span<char*> args, bool in_process)
|
||||
{
|
||||
if (!Banner(args)) {
|
||||
return EXIT_SUCCESS;
|
||||
|
@ -477,38 +144,29 @@ int ScsiDump::run(span<char *> args)
|
|||
try {
|
||||
ParseArguments(args);
|
||||
}
|
||||
catch (const parser_exception& e) {
|
||||
catch (const parser_exception &e) {
|
||||
cerr << "Error: " << e.what() << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (getuid()) {
|
||||
cerr << "Error: GPIO bus access requires root permissions. Are you running as root?" << endl;
|
||||
if (target_id == initiator_id) {
|
||||
cerr << "Target ID and PiSCSI board ID must not be identical" << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#ifndef USE_SEL_EVENT_ENABLE
|
||||
cerr << "Error: No PiSCSI hardware support" << endl;
|
||||
return EXIT_FAILURE;
|
||||
#endif
|
||||
|
||||
if (!Init()) {
|
||||
cerr << "Error: Can't initialize bus" << endl;
|
||||
if (!Init(in_process)) {
|
||||
cerr << "Error: Can't initialize bus" << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
try {
|
||||
DisplayBoardId();
|
||||
|
||||
inquiry_info_t inq_info;
|
||||
DisplayInquiry(inq_info, false);
|
||||
if (!in_process && !SBC_Version::IsRaspberryPi()) {
|
||||
cerr << "Error: No PiSCSI hardware support" << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
catch (const phase_exception& e) {
|
||||
cerr << "Error: " << e.what() << endl;
|
||||
|
||||
CleanUp();
|
||||
|
||||
return EXIT_FAILURE;
|
||||
if (!SetLogLevel()) {
|
||||
cerr << "Error: Invalid log level '" + log_level + "'";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
CleanUp();
|
||||
|
@ -516,110 +174,16 @@ int ScsiDump::run(span<char *> args)
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void ScsiDump::DisplayBoardId() const
|
||||
bool ScsiSend::SetLogLevel() const
|
||||
{
|
||||
cout << DIVIDER << "\nPiSCSI board ID is " << initiator_id << "\n";
|
||||
}
|
||||
|
||||
void ScsiDump::ScanBus()
|
||||
{
|
||||
DisplayBoardId();
|
||||
|
||||
for (target_id = 0; target_id < ControllerManager::GetScsiIdMax(); target_id++) {
|
||||
if (initiator_id == target_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (target_lun = 0; target_lun < 8; target_lun++) {
|
||||
inquiry_info_t inq_info;
|
||||
try {
|
||||
DisplayInquiry(inq_info, false);
|
||||
}
|
||||
catch(const phase_exception&) {
|
||||
// Continue with next ID if there is no LUN 0
|
||||
if (!target_lun) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScsiDump::DisplayInquiry(ScsiDump::inquiry_info_t& inq_info, bool)
|
||||
{
|
||||
// Assert RST for 1 ms
|
||||
bus->SetRST(true);
|
||||
const timespec ts = {.tv_sec = 0, .tv_nsec = 1000 * 1000};
|
||||
nanosleep(&ts, nullptr);
|
||||
bus->SetRST(false);
|
||||
|
||||
cout << DIVIDER << "\nTarget device is " << target_id << ":" << target_lun << "\n" << flush;
|
||||
|
||||
Inquiry();
|
||||
|
||||
const auto type = static_cast<byte>(buffer[0]);
|
||||
if ((type & byte{0x1f}) == byte{0x1f}) {
|
||||
// Requested LUN is not available
|
||||
return false;
|
||||
const level::level_enum l = level::from_str(log_level);
|
||||
// Compensate for spdlog using 'off' for unknown levels
|
||||
if (to_string_view(l) != log_level) {
|
||||
return false;
|
||||
}
|
||||
|
||||
array<char, 17> str = {};
|
||||
memcpy(str.data(), &buffer[8], 8);
|
||||
inq_info.vendor = string(str.data());
|
||||
cout << "Vendor: " << inq_info.vendor << "\n";
|
||||
|
||||
str.fill(0);
|
||||
memcpy(str.data(), &buffer[16], 16);
|
||||
inq_info.product = string(str.data());
|
||||
cout << "Product: " << inq_info.product << "\n";
|
||||
|
||||
str.fill(0);
|
||||
memcpy(str.data(), &buffer[32], 4);
|
||||
inq_info.revision = string(str.data());
|
||||
cout << "Revision: " << inq_info.revision << "\n" << flush;
|
||||
|
||||
if (const auto& t = DEVICE_TYPES.find(type & byte{0x1f}); t != DEVICE_TYPES.end()) {
|
||||
cout << "Device Type: " << (*t).second << "\n";
|
||||
}
|
||||
else {
|
||||
cout << "Device Type: Unknown\n";
|
||||
}
|
||||
|
||||
cout << "Removable: " << (((static_cast<byte>(buffer[1]) & byte{0x80}) == byte{0x80}) ? "Yes" : "No") << "\n";
|
||||
|
||||
Execute();
|
||||
set_level(l);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ScsiDump::GetDeviceInfo(inquiry_info_t& inq_info)
|
||||
{
|
||||
DisplayBoardId();
|
||||
|
||||
if (!DisplayInquiry(inq_info, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TestUnitReady();
|
||||
|
||||
RequestSense();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScsiDump::inquiry_info::GeneratePropertiesFile(const string& property_file) const
|
||||
{
|
||||
ofstream prop_stream(property_file);
|
||||
|
||||
prop_stream << "{" << endl;
|
||||
prop_stream << " \"vendor\": \"" << vendor << "\"," << endl;
|
||||
prop_stream << " \"product\": \"" << product << "\"," << endl;
|
||||
prop_stream << " \"revision\": \"" << revision << "\"," << endl;
|
||||
prop_stream << " \"block_size\": \"" << sector_size << "\"" << endl;
|
||||
prop_stream << "}" << endl;
|
||||
|
||||
if (prop_stream.fail()) {
|
||||
spdlog::warn("Unable to create properties file '" + property_file + "'");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,125 +3,64 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2023 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/bus.h"
|
||||
#include <memory>
|
||||
#include "scsisend/scsi_executor.h"
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class phase_exception : public runtime_error
|
||||
{
|
||||
using runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
class ScsiDump
|
||||
class ScsiSend
|
||||
{
|
||||
|
||||
public:
|
||||
public:
|
||||
|
||||
ScsiDump() = default;
|
||||
~ScsiDump() = default;
|
||||
ScsiSend() = default;
|
||||
~ScsiSend() = default;
|
||||
|
||||
int run(const span<char *>);
|
||||
int run(span<char*>, bool = false);
|
||||
|
||||
struct inquiry_info {
|
||||
string vendor;
|
||||
string product;
|
||||
string revision;
|
||||
uint32_t sector_size;
|
||||
uint64_t capacity;
|
||||
private:
|
||||
|
||||
void GeneratePropertiesFile(const string&) const;
|
||||
};
|
||||
using inquiry_info_t = struct inquiry_info;
|
||||
bool Banner(span<char*>) const;
|
||||
bool Init(bool);
|
||||
void ParseArguments(span<char*>);
|
||||
long CalculateEffectiveSize(ostream&) const;
|
||||
|
||||
private:
|
||||
bool SetLogLevel() const;
|
||||
|
||||
bool Banner(span<char *>) const;
|
||||
bool Init() const;
|
||||
void ParseArguments(span<char *>);
|
||||
void DisplayBoardId() const;
|
||||
void ScanBus();
|
||||
bool DisplayInquiry(inquiry_info_t&, bool);
|
||||
bool GetDeviceInfo(inquiry_info_t&);
|
||||
void WaitForPhase(phase_t) const;
|
||||
void Selection() const;
|
||||
void Command(scsi_defs::scsi_command, vector<uint8_t>&) const;
|
||||
int DataIn(int);
|
||||
void DataOut(int);
|
||||
void Status() const;
|
||||
void MessageIn() const;
|
||||
void BusFree() const;
|
||||
void TestUnitReady() const;
|
||||
void RequestSense();
|
||||
void Inquiry();
|
||||
void Execute();
|
||||
pair<uint64_t, uint32_t> ReadCapacity();
|
||||
void Read10(uint32_t, uint32_t, uint32_t);
|
||||
void Write10(uint32_t, uint32_t, uint32_t);
|
||||
void WaitForBusy() const;
|
||||
void Reset() const;
|
||||
|
||||
static void CleanUp();
|
||||
void CleanUp() const;
|
||||
static void TerminationHandler(int);
|
||||
|
||||
// A static instance is needed because of the signal handler
|
||||
static inline unique_ptr<BUS> bus;
|
||||
unique_ptr<BUS> bus;
|
||||
|
||||
unique_ptr<ScsiExecutor> scsi_executor;
|
||||
|
||||
vector<uint8_t> buffer;
|
||||
|
||||
int target_id = -1;
|
||||
|
||||
int target_lun = 0;
|
||||
|
||||
int initiator_id = 7;
|
||||
int target_id = -1;
|
||||
int target_lun = 0;
|
||||
|
||||
string filename;
|
||||
|
||||
bool inquiry = false;
|
||||
|
||||
bool scan_bus = false;
|
||||
string log_level = "info";
|
||||
|
||||
bool restore = false;
|
||||
|
||||
bool properties_file = false;
|
||||
// Required for the termination handler
|
||||
static inline ScsiSend *instance;
|
||||
|
||||
static const int MINIMUM_BUFFER_SIZE = 1024 * 64;
|
||||
static const int DEFAULT_BUFFER_SIZE = 1024 * 1024;
|
||||
|
||||
static inline const string DIVIDER = "----------------------------------------";
|
||||
|
||||
static inline const unordered_map<byte, string> DEVICE_TYPES = {
|
||||
{ byte{0}, "Direct Access" },
|
||||
{ byte{1}, "Sequential Access" },
|
||||
{ byte{2}, "Printer" },
|
||||
{ byte{3}, "Processor" },
|
||||
{ byte{4}, "Write-Once" },
|
||||
{ byte{5}, "CD-ROM/DVD/BD/DVD-RAM" },
|
||||
{ byte{6}, "Scanner" },
|
||||
{ byte{7}, "Optical Memory" },
|
||||
{ byte{8}, "Media Changer" },
|
||||
{ byte{9}, "Communications" },
|
||||
{ byte{10}, "Graphic Arts Pre-Press" },
|
||||
{ byte{11}, "Graphic Arts Pre-Press" },
|
||||
{ byte{12}, "Storage Array Controller" },
|
||||
{ byte{13}, "Enclosure Services" },
|
||||
{ byte{14}, "Simplified Direct Access" },
|
||||
{ byte{15}, "Optical Card Reader/Writer" },
|
||||
{ byte{16}, "Bridge Controller" },
|
||||
{ byte{17}, "Object-based Storage" },
|
||||
{ byte{18}, "Automation/Drive Interface" },
|
||||
{ byte{19}, "Security Manager" },
|
||||
{ byte{20}, "Host Managed Zoned Block" },
|
||||
{ byte{30}, "Well Known Logical Unit" }
|
||||
};
|
||||
};
|
||||
|
|
|
@ -186,5 +186,6 @@ static const unordered_map<scsi_command, pair<int, string>> command_mapping = {
|
|||
{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")}};
|
||||
}; // namespace scsi_defs
|
||||
{scsi_command::eCmdReportLuns, make_pair(12, "ReportLuns")},
|
||||
{ scsi_command::eCmdExecute, make_pair(10, "Execute") }};
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user