mirror of
https://github.com/akuker/RASCSI.git
synced 2024-12-10 11:49:28 +00:00
Issues 1179 and 1182 (#1232)
* Update logging * Remove duplicate code * Update unit tests * Clean up includes * Merge ProtobufSerializer into protobuf_util namespace * Precompile regex * Add const * Add Split() convenience method, update log level/ID parsing * Move log.h to legacy folder * Elimininate gotos * Fixes for gcc 13 * Update compiler flags * Update default folder handling * Use references instead of pointers * Move code for better encapsulation * Move code * Remove unused method argument * Move device logger * Remove redundant to_string * Rename for consistency * Update handling of protobuf pointers * Simplify protobuf usage * Memory handling update * Add hasher
This commit is contained in:
parent
c1f6f3ffea
commit
41bdcd4aed
40
cpp/Makefile
40
cpp/Makefile
@ -7,8 +7,6 @@
|
||||
CROSS_COMPILE =
|
||||
|
||||
CXX = $(CROSS_COMPILE)g++
|
||||
AR = $(CROSS_COMPILE)ar
|
||||
RANLIB = $(CROSS_COMPILE)ranlib
|
||||
|
||||
## DEBUG=1 : A Debug build includes the debugger symbols
|
||||
## and disables compiler optimization. Typically,
|
||||
@ -16,30 +14,21 @@ RANLIB = $(CROSS_COMPILE)ranlib
|
||||
DEBUG ?= 0
|
||||
ifeq ($(DEBUG), 1)
|
||||
# Debug compiler flags
|
||||
CXXFLAGS += -O0 -g -Wall -Wextra -DDEBUG
|
||||
BUILD_TYPE = Debug
|
||||
CXXFLAGS += -Og -g -Wall -Wextra -DDEBUG
|
||||
else
|
||||
# Release compiler flags
|
||||
CXXFLAGS += -O3 -Wall -Werror -Wextra -DNDEBUG
|
||||
BUILD_TYPE = Release
|
||||
endif
|
||||
ifeq ("$(shell uname -s)","Linux")
|
||||
# -Wno-psabi might not work on non-Linux platforms
|
||||
CXXFLAGS += -Wno-psabi
|
||||
endif
|
||||
|
||||
# Depending on the GCC version the compilation flags differ
|
||||
GCCVERSION10 := $(shell expr `$(CXX) -dumpversion` \>= 10)
|
||||
|
||||
CXXFLAGS += -std=c++17 -iquote . -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE -MD -MP
|
||||
CXXFLAGS += -std=c++20 -iquote . -D_FILE_OFFSET_BITS=64 -DFMT_HEADER_ONLY -DSPDLOG_FMT_EXTERNAL -MD -MP
|
||||
|
||||
## EXTRA_FLAGS : Can be used to pass special purpose flags
|
||||
CXXFLAGS += $(EXTRA_FLAGS)
|
||||
|
||||
ifeq "$(GCCVERSION10)" "1"
|
||||
CXXFLAGS += -DFMT_HEADER_ONLY
|
||||
endif
|
||||
|
||||
|
||||
## CONNECT_TYPE=FULLSPEC : Specify the type of PiSCSI board type
|
||||
## that you are using. The typical options are
|
||||
@ -86,12 +75,12 @@ SRC_PROTOC = piscsi_interface.proto
|
||||
SRC_GENERATED = $(GENERATED_DIR)/piscsi_interface.pb.cpp
|
||||
|
||||
SRC_PROTOBUF = \
|
||||
shared/protobuf_util.cpp \
|
||||
shared/protobuf_serializer.cpp
|
||||
shared/protobuf_util.cpp
|
||||
|
||||
SRC_SHARED = \
|
||||
shared/piscsi_version.cpp \
|
||||
shared/piscsi_util.cpp
|
||||
shared/piscsi_util.cpp \
|
||||
shared/network_util.cpp
|
||||
|
||||
SRC_PISCSI_CORE = $(shell find ./piscsi -name '*.cpp')
|
||||
SRC_PISCSI_CORE += $(shell find ./controllers -name '*.cpp')
|
||||
@ -167,7 +156,7 @@ $(SRC_GENERATED) : $(SRC_PROTOC)
|
||||
protoc --cpp_out=$(GENERATED_DIR) $(SRC_PROTOC)
|
||||
mv $(GENERATED_DIR)/piscsi_interface.pb.cc $@
|
||||
|
||||
$(OBJ_GENERATED) : $(SRC_GENERATED)
|
||||
$(OBJ_GENERATED) : $(SRC_GENERATED) | $(OBJDIR)
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
## Build Targets:
|
||||
@ -180,10 +169,11 @@ $(OBJ_GENERATED) : $(SRC_GENERATED)
|
||||
## Note that you have to run 'make clean' before switching
|
||||
## between coverage and non-coverage builds.
|
||||
.DEFAULT_GOAL := all
|
||||
.PHONY: all ALL docs test coverage lcov
|
||||
all: $(BIN_ALL) docs
|
||||
.PHONY: all docs test coverage lcov
|
||||
|
||||
test: $(BINDIR)/$(PISCSI_TEST)
|
||||
all: $(SRC_GENERATED) $(BIN_ALL) docs
|
||||
|
||||
test: $(SRC_GENERATED) $(BINDIR)/$(PISCSI_TEST)
|
||||
$(BINDIR)/$(PISCSI_TEST)
|
||||
|
||||
coverage: CXXFLAGS += --coverage
|
||||
@ -198,10 +188,10 @@ docs: $(DOC_DIR)/piscsi_man_page.txt $(DOC_DIR)/scsictl_man_page.txt $(DOC_DIR)/
|
||||
|
||||
$(SRC_PISCSI_CORE) $(SRC_SCSICTL_CORE) : $(OBJ_GENERATED)
|
||||
|
||||
$(BINDIR)/$(PISCSI): $(SRC_GENERATED) $(OBJ_PISCSI_CORE) $(OBJ_PISCSI) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJ_PISCSI_CORE) $(OBJ_PISCSI) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) -lpthread -lpcap -lprotobuf -lstdc++fs
|
||||
$(BINDIR)/$(PISCSI): $(OBJ_GENERATED) $(OBJ_PISCSI_CORE) $(OBJ_PISCSI) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJ_PISCSI_CORE) $(OBJ_PISCSI) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) -lpthread -lpcap -lprotobuf
|
||||
|
||||
$(BINDIR)/$(SCSICTL): $(SRC_GENERATED) $(OBJ_SCSICTL_CORE) $(OBJ_SCSICTL) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) | $(BINDIR)
|
||||
$(BINDIR)/$(SCSICTL): $(OBJ_GENERATED) $(OBJ_SCSICTL_CORE) $(OBJ_SCSICTL) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJ_SCSICTL_CORE) $(OBJ_SCSICTL) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) -lpthread -lprotobuf
|
||||
|
||||
$(BINDIR)/$(SCSIDUMP): $(OBJ_SCSIDUMP) $(OBJ_SHARED) | $(BINDIR)
|
||||
@ -213,8 +203,8 @@ $(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) $(OBJ_SHARED) | $(BINDIR)
|
||||
$(BINDIR)/$(SCSILOOP): $(OBJ_SHARED) $(OBJ_SCSILOOP) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJ_SHARED) $(OBJ_SCSILOOP)
|
||||
|
||||
$(BINDIR)/$(PISCSI_TEST): $(SRC_GENERATED) $(OBJ_PISCSI_CORE) $(OBJ_SCSICTL_CORE) $(OBJ_PISCSI_TEST) $(OBJ_SCSICTL_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(TEST_WRAPS) -o $@ $(OBJ_PISCSI_CORE) $(OBJ_SCSICTL_CORE) $(OBJ_PISCSI_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) -lpthread -lpcap -lprotobuf -lstdc++fs -lgmock -lgtest
|
||||
$(BINDIR)/$(PISCSI_TEST): $(OBJ_GENERATED) $(OBJ_PISCSI_CORE) $(OBJ_SCSICTL_CORE) $(OBJ_PISCSI_TEST) $(OBJ_SCSICTL_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(TEST_WRAPS) -o $@ $(OBJ_PISCSI_CORE) $(OBJ_SCSICTL_CORE) $(OBJ_PISCSI_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) -lpthread -lpcap -lprotobuf -lgmock -lgtest
|
||||
|
||||
# Phony rules for building individual utilities
|
||||
.PHONY: $(PISCSI) $(SCSICTL) $(SCSIDUMP) $(SCSIMON) $(PISCSI_TEST) $(SCSILOOP)
|
||||
|
@ -3,16 +3,22 @@
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include "devices/primary_device.h"
|
||||
#include "abstract_controller.h"
|
||||
#include <ranges>
|
||||
|
||||
using namespace scsi_defs;
|
||||
|
||||
AbstractController::AbstractController(BUS& bus, int target_id, int max_luns) : bus(bus), target_id(target_id), max_luns(max_luns)
|
||||
{
|
||||
device_logger.SetIdAndLun(target_id, -1);
|
||||
}
|
||||
|
||||
void AbstractController::AllocateCmd(size_t size)
|
||||
{
|
||||
if (size > ctrl.cmd.size()) {
|
||||
@ -40,9 +46,8 @@ unordered_set<shared_ptr<PrimaryDevice>> AbstractController::GetDevices() const
|
||||
{
|
||||
unordered_set<shared_ptr<PrimaryDevice>> devices;
|
||||
|
||||
for (const auto& [id, lun] : luns) {
|
||||
devices.insert(lun);
|
||||
}
|
||||
// "luns | views:values" is not supported by the bullseye compiler
|
||||
ranges::transform(luns, inserter(devices, devices.begin()), [] (const auto& l) { return l.second; } );
|
||||
|
||||
return devices;
|
||||
}
|
||||
@ -56,100 +61,66 @@ void AbstractController::Reset()
|
||||
{
|
||||
SetPhase(phase_t::busfree);
|
||||
|
||||
ctrl.status = status::GOOD;
|
||||
ctrl.message = 0x00;
|
||||
ctrl.blocks = 0;
|
||||
ctrl.next = 0;
|
||||
ctrl.offset = 0;
|
||||
ctrl.length = 0;
|
||||
ctrl = {};
|
||||
|
||||
SetByteTransfer(false);
|
||||
|
||||
// Reset all LUNs
|
||||
for (const auto& [lun, device] : luns) {
|
||||
for (const auto& [_, device] : luns) {
|
||||
device->Reset();
|
||||
}
|
||||
|
||||
GetBus().Reset();
|
||||
}
|
||||
|
||||
void AbstractController::ProcessPhase()
|
||||
void AbstractController::ProcessOnController(int id_data)
|
||||
{
|
||||
switch (GetPhase()) {
|
||||
case phase_t::busfree:
|
||||
BusFree();
|
||||
break;
|
||||
device_logger.SetIdAndLun(GetTargetId(), -1);
|
||||
|
||||
case phase_t::selection:
|
||||
Selection();
|
||||
break;
|
||||
const int initiator_id = ExtractInitiatorId(id_data);
|
||||
if (initiator_id != UNKNOWN_INITIATOR_ID) {
|
||||
LogTrace("++++ Starting processing for initiator ID " + to_string(initiator_id));
|
||||
}
|
||||
else {
|
||||
LogTrace("++++ Starting processing for unknown initiator ID");
|
||||
}
|
||||
|
||||
case phase_t::dataout:
|
||||
DataOut();
|
||||
break;
|
||||
|
||||
case phase_t::datain:
|
||||
DataIn();
|
||||
break;
|
||||
|
||||
case phase_t::command:
|
||||
Command();
|
||||
break;
|
||||
|
||||
case phase_t::status:
|
||||
Status();
|
||||
break;
|
||||
|
||||
case phase_t::msgout:
|
||||
MsgOut();
|
||||
break;
|
||||
|
||||
case phase_t::msgin:
|
||||
MsgIn();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw scsi_exception(sense_key::ABORTED_COMMAND);
|
||||
break;
|
||||
while (Process(initiator_id)) {
|
||||
// Handle bus phases until the bus is free for the next command
|
||||
}
|
||||
}
|
||||
|
||||
bool AbstractController::AddDevice(shared_ptr<PrimaryDevice> device)
|
||||
{
|
||||
if (device->GetLun() < 0 || device->GetLun() >= GetMaxLuns() || HasDeviceForLun(device->GetLun())) {
|
||||
const int lun = device->GetLun();
|
||||
|
||||
if (lun < 0 || lun >= GetMaxLuns() || HasDeviceForLun(lun) || device->GetController()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
luns[device->GetLun()] = device;
|
||||
device->SetController(shared_from_this());
|
||||
luns[lun] = device;
|
||||
device->SetController(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractController::RemoveDevice(shared_ptr<PrimaryDevice> device)
|
||||
bool AbstractController::RemoveDevice(PrimaryDevice& device)
|
||||
{
|
||||
device->SetController(nullptr);
|
||||
device.CleanUp();
|
||||
|
||||
return luns.erase(device->GetLun()) == 1;
|
||||
return luns.erase(device.GetLun()) == 1;
|
||||
}
|
||||
|
||||
bool AbstractController::HasDeviceForLun(int lun) const
|
||||
{
|
||||
return luns.find(lun) != luns.end();
|
||||
return luns.contains(lun);
|
||||
}
|
||||
|
||||
int AbstractController::ExtractInitiatorId(int id_data) const
|
||||
{
|
||||
int initiator_id = UNKNOWN_INITIATOR_ID;
|
||||
|
||||
if (int tmp = id_data - (1 << target_id); tmp) {
|
||||
initiator_id = 0;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
tmp >>= 1;
|
||||
if (tmp) {
|
||||
initiator_id++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (const int id_data_without_target = id_data - (1 << target_id); id_data_without_target) {
|
||||
return static_cast<int>(log2(id_data_without_target & -id_data_without_target));
|
||||
}
|
||||
|
||||
return initiator_id;
|
||||
return UNKNOWN_INITIATOR_ID;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// Base class for device controllers
|
||||
//
|
||||
@ -14,17 +14,19 @@
|
||||
#include "shared/scsi.h"
|
||||
#include "hal/bus.h"
|
||||
#include "phase_handler.h"
|
||||
#include "controller_manager.h"
|
||||
#include "devices/device_logger.h"
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class PrimaryDevice;
|
||||
|
||||
class AbstractController : public PhaseHandler, public enable_shared_from_this<AbstractController>
|
||||
class AbstractController : public PhaseHandler
|
||||
{
|
||||
public:
|
||||
|
||||
@ -37,19 +39,19 @@ public:
|
||||
RESTART_PI
|
||||
};
|
||||
|
||||
AbstractController(shared_ptr<ControllerManager> controller_manager, int target_id, int max_luns)
|
||||
: controller_manager(controller_manager), target_id(target_id), max_luns(max_luns) {}
|
||||
AbstractController(BUS&, int, int);
|
||||
~AbstractController() override = default;
|
||||
|
||||
virtual void Error(scsi_defs::sense_key, scsi_defs::asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
|
||||
scsi_defs::status = scsi_defs::status::CHECK_CONDITION) = 0;
|
||||
virtual void Error(scsi_defs::sense_key, scsi_defs::asc = scsi_defs::asc::no_additional_sense_information,
|
||||
scsi_defs::status = scsi_defs::status::check_condition) = 0;
|
||||
virtual void Reset();
|
||||
virtual int GetInitiatorId() const = 0;
|
||||
|
||||
// Get requested LUN based on IDENTIFY message, with LUN from the CDB as fallback
|
||||
virtual int GetEffectiveLun() const = 0;
|
||||
|
||||
virtual void ScheduleShutdown(piscsi_shutdown_mode) = 0;
|
||||
void ScheduleShutdown(piscsi_shutdown_mode mode) { shutdown_mode = mode; }
|
||||
piscsi_shutdown_mode GetShutdownMode() const { return shutdown_mode; }
|
||||
|
||||
int GetTargetId() const { return target_id; }
|
||||
int GetMaxLuns() const { return max_luns; }
|
||||
@ -58,52 +60,59 @@ public:
|
||||
unordered_set<shared_ptr<PrimaryDevice>> GetDevices() const;
|
||||
shared_ptr<PrimaryDevice> GetDeviceForLun(int) const;
|
||||
bool AddDevice(shared_ptr<PrimaryDevice>);
|
||||
bool RemoveDevice(shared_ptr<PrimaryDevice>);
|
||||
bool RemoveDevice(PrimaryDevice&);
|
||||
bool HasDeviceForLun(int) const;
|
||||
int ExtractInitiatorId(int) const;
|
||||
void ProcessOnController(int);
|
||||
|
||||
// TODO These should probably be extracted into a new TransferHandler class
|
||||
void AllocateBuffer(size_t);
|
||||
vector<uint8_t>& GetBuffer() { return ctrl.buffer; }
|
||||
scsi_defs::status GetStatus() const { return ctrl.status; }
|
||||
auto& GetBuffer() { return ctrl.buffer; }
|
||||
auto GetStatus() const { return ctrl.status; }
|
||||
void SetStatus(scsi_defs::status s) { ctrl.status = s; }
|
||||
uint32_t GetLength() const { return ctrl.length; }
|
||||
auto GetLength() const { return ctrl.length; }
|
||||
void SetLength(uint32_t l) { ctrl.length = l; }
|
||||
uint32_t GetBlocks() const { return ctrl.blocks; }
|
||||
bool HasBlocks() const { return ctrl.blocks; }
|
||||
void SetBlocks(uint32_t b) { ctrl.blocks = b; }
|
||||
void DecrementBlocks() { --ctrl.blocks; }
|
||||
uint64_t GetNext() const { return ctrl.next; }
|
||||
auto GetNext() const { return ctrl.next; }
|
||||
void SetNext(uint64_t n) { ctrl.next = n; }
|
||||
void IncrementNext() { ++ctrl.next; }
|
||||
int GetMessage() const { return ctrl.message; }
|
||||
void SetMessage(int m) { ctrl.message = m; }
|
||||
vector<int>& GetCmd() { return ctrl.cmd; }
|
||||
int GetCmd(int index) const { return ctrl.cmd[index]; }
|
||||
auto GetCmd() const { return ctrl.cmd; }
|
||||
int GetCmdByte(int index) const { return ctrl.cmd[index]; }
|
||||
bool IsByteTransfer() const { return is_byte_transfer; }
|
||||
void SetByteTransfer(bool);
|
||||
uint32_t GetBytesToTransfer() const { return bytes_to_transfer; }
|
||||
auto GetBytesToTransfer() const { return bytes_to_transfer; }
|
||||
void SetBytesToTransfer(uint32_t b) { bytes_to_transfer = b; }
|
||||
|
||||
protected:
|
||||
|
||||
shared_ptr<ControllerManager> GetControllerManager() const { return controller_manager.lock(); }
|
||||
inline BUS& GetBus() const { return controller_manager.lock()->GetBus(); }
|
||||
BUS& GetBus() const { return bus; }
|
||||
|
||||
scsi_defs::scsi_command GetOpcode() const { return static_cast<scsi_defs::scsi_command>(ctrl.cmd[0]); }
|
||||
auto GetOpcode() const { return static_cast<scsi_defs::scsi_command>(ctrl.cmd[0]); }
|
||||
int GetLun() const { return (ctrl.cmd[1] >> 5) & 0x07; }
|
||||
|
||||
void ProcessPhase();
|
||||
|
||||
void AllocateCmd(size_t);
|
||||
|
||||
void SetCmdByte(int index, int value) { ctrl.cmd[index] = value; }
|
||||
|
||||
// TODO These should probably be extracted into a new TransferHandler class
|
||||
bool HasValidLength() const { return ctrl.length != 0; }
|
||||
int GetOffset() const { return ctrl.offset; }
|
||||
void ResetOffset() { ctrl.offset = 0; }
|
||||
void UpdateOffsetAndLength() { ctrl.offset += ctrl.length; ctrl.length = 0; }
|
||||
|
||||
void LogTrace(const string& s) const { device_logger.Trace(s); }
|
||||
void LogDebug(const string& s) const { device_logger.Debug(s); }
|
||||
void LogInfo(const string& s) const { device_logger.Info(s); }
|
||||
void LogWarn(const string& s) const { device_logger.Warn(s); }
|
||||
void LogError(const string& s) const { device_logger.Error(s); }
|
||||
|
||||
private:
|
||||
|
||||
int ExtractInitiatorId(int) const;
|
||||
|
||||
using ctrl_t = struct _ctrl_t {
|
||||
// Command data, dynamically resized if required
|
||||
vector<int> cmd = vector<int>(16);
|
||||
@ -121,7 +130,9 @@ private:
|
||||
|
||||
ctrl_t ctrl = {};
|
||||
|
||||
weak_ptr<ControllerManager> controller_manager;
|
||||
BUS& bus;
|
||||
|
||||
DeviceLogger device_logger;
|
||||
|
||||
// Logical units of this controller mapped to their LUN numbers
|
||||
unordered_map<int, shared_ptr<PrimaryDevice>> luns;
|
||||
@ -132,4 +143,6 @@ private:
|
||||
|
||||
bool is_byte_transfer = false;
|
||||
uint32_t bytes_to_transfer = 0;
|
||||
|
||||
piscsi_shutdown_mode shutdown_mode = piscsi_shutdown_mode::NONE;
|
||||
};
|
||||
|
@ -3,29 +3,34 @@
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "devices/device_factory.h"
|
||||
#include "devices/primary_device.h"
|
||||
#include "scsi_controller.h"
|
||||
#include "controller_manager.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool ControllerManager::AttachToScsiController(int id, shared_ptr<PrimaryDevice> device)
|
||||
shared_ptr<ScsiController> ControllerManager::CreateScsiController(BUS& bus, int id) const
|
||||
{
|
||||
auto controller = FindController(id);
|
||||
if (controller != nullptr) {
|
||||
return make_shared<ScsiController>(bus, id);
|
||||
}
|
||||
|
||||
bool ControllerManager::AttachToController(BUS& bus, int id, shared_ptr<PrimaryDevice> device)
|
||||
{
|
||||
if (auto controller = FindController(id); controller != nullptr) {
|
||||
if (controller->HasDeviceForLun(device->GetLun())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return controller->AddDevice(device);
|
||||
}
|
||||
|
||||
// If there is no LUN yet the first LUN must be LUN 0
|
||||
if (device->GetLun() == 0) {
|
||||
controller = make_shared<ScsiController>(shared_from_this(), id);
|
||||
|
||||
if (controller->AddDevice(device)) {
|
||||
// If this is LUN 0 create a new controller
|
||||
if (!device->GetLun()) {
|
||||
if (auto controller = CreateScsiController(bus, id); controller->AddDevice(device)) {
|
||||
controllers[id] = controller;
|
||||
|
||||
return true;
|
||||
@ -35,20 +40,37 @@ bool ControllerManager::AttachToScsiController(int id, shared_ptr<PrimaryDevice>
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ControllerManager::DeleteController(shared_ptr<AbstractController> controller)
|
||||
bool ControllerManager::DeleteController(const AbstractController& controller)
|
||||
{
|
||||
return controllers.erase(controller->GetTargetId()) == 1;
|
||||
}
|
||||
|
||||
shared_ptr<AbstractController> ControllerManager::IdentifyController(int data) const
|
||||
{
|
||||
for (const auto& [id, controller] : controllers) {
|
||||
if (data & (1 << controller->GetTargetId())) {
|
||||
return controller;
|
||||
}
|
||||
for (const auto& device : controller.GetDevices()) {
|
||||
device->CleanUp();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return controllers.erase(controller.GetTargetId()) == 1;
|
||||
}
|
||||
|
||||
void ControllerManager::DeleteAllControllers()
|
||||
{
|
||||
unordered_set<shared_ptr<AbstractController>> values;
|
||||
ranges::transform(controllers, inserter(values, values.begin()), [] (const auto& controller) { return controller.second; } );
|
||||
|
||||
for (const auto& controller : values) {
|
||||
DeleteController(*controller);
|
||||
}
|
||||
|
||||
assert(controllers.empty());
|
||||
}
|
||||
|
||||
AbstractController::piscsi_shutdown_mode ControllerManager::ProcessOnController(int id_data) const
|
||||
{
|
||||
if (const auto& it = ranges::find_if(controllers, [&] (const auto& c) { return (id_data & (1 << c.first)); } );
|
||||
it != controllers.end()) {
|
||||
(*it).second->ProcessOnController(id_data);
|
||||
|
||||
return (*it).second->GetShutdownMode();
|
||||
}
|
||||
|
||||
return AbstractController::piscsi_shutdown_mode::NONE;
|
||||
}
|
||||
|
||||
shared_ptr<AbstractController> ControllerManager::FindController(int target_id) const
|
||||
@ -57,11 +79,15 @@ shared_ptr<AbstractController> ControllerManager::FindController(int target_id)
|
||||
return it == controllers.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
bool ControllerManager::HasController(int target_id) const {
|
||||
return controllers.contains(target_id);
|
||||
}
|
||||
|
||||
unordered_set<shared_ptr<PrimaryDevice>> ControllerManager::GetAllDevices() const
|
||||
{
|
||||
unordered_set<shared_ptr<PrimaryDevice>> devices;
|
||||
|
||||
for (const auto& [id, controller] : controllers) {
|
||||
for (const auto& [_, controller] : controllers) {
|
||||
const auto& d = controller->GetDevices();
|
||||
devices.insert(d.begin(), d.end());
|
||||
}
|
||||
@ -69,12 +95,12 @@ unordered_set<shared_ptr<PrimaryDevice>> ControllerManager::GetAllDevices() cons
|
||||
return devices;
|
||||
}
|
||||
|
||||
void ControllerManager::DeleteAllControllers()
|
||||
bool ControllerManager::HasDeviceForIdAndLun(int id, int lun) const
|
||||
{
|
||||
controllers.clear();
|
||||
return GetDeviceForIdAndLun(id, lun) != nullptr;
|
||||
}
|
||||
|
||||
shared_ptr<PrimaryDevice> ControllerManager::GetDeviceByIdAndLun(int id, int lun) const
|
||||
shared_ptr<PrimaryDevice> ControllerManager::GetDeviceForIdAndLun(int id, int lun) const
|
||||
{
|
||||
if (const auto& controller = FindController(id); controller != nullptr) {
|
||||
return controller->GetDeviceForLun(lun);
|
||||
|
@ -3,7 +3,7 @@
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// Keeps track of and manages the controllers
|
||||
//
|
||||
@ -11,36 +11,41 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/bus.h"
|
||||
#include "controllers/abstract_controller.h"
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <memory>
|
||||
#include "hal/bus.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class AbstractController;
|
||||
class ScsiController;
|
||||
class PrimaryDevice;
|
||||
|
||||
class ControllerManager : public enable_shared_from_this<ControllerManager>
|
||||
class ControllerManager
|
||||
{
|
||||
BUS& bus;
|
||||
|
||||
unordered_map<int, shared_ptr<AbstractController>> controllers;
|
||||
|
||||
public:
|
||||
|
||||
explicit ControllerManager(BUS& bus) : bus(bus) {}
|
||||
ControllerManager() = default;
|
||||
~ControllerManager() = default;
|
||||
|
||||
// Maximum number of controller devices
|
||||
static const int DEVICE_MAX = 8;
|
||||
|
||||
inline BUS& GetBus() const { return bus; }
|
||||
bool AttachToScsiController(int, shared_ptr<PrimaryDevice>);
|
||||
bool DeleteController(shared_ptr<AbstractController>);
|
||||
shared_ptr<AbstractController> IdentifyController(int) const;
|
||||
shared_ptr<AbstractController> FindController(int) const;
|
||||
unordered_set<shared_ptr<PrimaryDevice>> GetAllDevices() const;
|
||||
bool AttachToController(BUS&, int, shared_ptr<PrimaryDevice>);
|
||||
bool DeleteController(const AbstractController&);
|
||||
void DeleteAllControllers();
|
||||
shared_ptr<PrimaryDevice> GetDeviceByIdAndLun(int, int) const;
|
||||
AbstractController::piscsi_shutdown_mode ProcessOnController(int) const;
|
||||
shared_ptr<AbstractController> FindController(int) const;
|
||||
bool HasController(int) const;
|
||||
unordered_set<shared_ptr<PrimaryDevice>> GetAllDevices() const;
|
||||
bool HasDeviceForIdAndLun(int, int) const;
|
||||
shared_ptr<PrimaryDevice> GetDeviceForIdAndLun(int, int) const;
|
||||
|
||||
static int GetScsiIdMax() { return 8; }
|
||||
static int GetScsiLunMax() { return 32; }
|
||||
|
||||
private:
|
||||
|
||||
shared_ptr<ScsiController> CreateScsiController(BUS&, int) const;
|
||||
|
||||
// Controllers mapped to their device IDs
|
||||
unordered_map<int, shared_ptr<AbstractController>> controllers;
|
||||
};
|
||||
|
@ -3,15 +3,18 @@
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "shared/scsi.h"
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
|
||||
using namespace scsi_defs;
|
||||
using namespace scsi_defs;
|
||||
|
||||
class PhaseHandler
|
||||
{
|
||||
@ -31,7 +34,7 @@ public:
|
||||
virtual void MsgIn() = 0;
|
||||
virtual void MsgOut() = 0;
|
||||
|
||||
virtual phase_t Process(int) = 0;
|
||||
virtual bool Process(int) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
@ -45,4 +48,27 @@ protected:
|
||||
bool IsDataOut() const { return phase == phase_t::dataout; }
|
||||
bool IsMsgIn() const { return phase == phase_t::msgin; }
|
||||
bool IsMsgOut() const { return phase == phase_t::msgout; }
|
||||
|
||||
void ProcessPhase() const
|
||||
{
|
||||
try {
|
||||
phase_executors.at(phase)();
|
||||
}
|
||||
catch(const out_of_range&) {
|
||||
throw scsi_exception(sense_key::aborted_command);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const unordered_map<phase_t, function<void()>> phase_executors = {
|
||||
{ phase_t::busfree, [this] () { BusFree(); } },
|
||||
{ phase_t::selection, [this] () { Selection(); } },
|
||||
{ phase_t::dataout, [this] () { DataOut(); } },
|
||||
{ phase_t::datain, [this] () { DataIn(); } },
|
||||
{ phase_t::command, [this] () { Command(); } },
|
||||
{ phase_t::status, [this] () { Status(); } },
|
||||
{ phase_t::msgout, [this] () { MsgOut(); } },
|
||||
{ phase_t::msgin, [this] () { MsgIn(); } },
|
||||
};
|
||||
};
|
||||
|
@ -6,7 +6,7 @@
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
@ -16,7 +16,9 @@
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include "hal/gpiobus.h"
|
||||
#include "hal/systimer.h"
|
||||
#include "devices/interfaces/byte_writer.h"
|
||||
#include "controllers/controller_manager.h"
|
||||
#include "devices/scsi_host_bridge.h"
|
||||
#include "devices/scsi_daynaport.h"
|
||||
#include "devices/mode_page_device.h"
|
||||
#include "devices/disk.h"
|
||||
#include "scsi_controller.h"
|
||||
@ -28,11 +30,8 @@
|
||||
|
||||
using namespace scsi_defs;
|
||||
|
||||
ScsiController::ScsiController(shared_ptr<ControllerManager> controller_manager, int target_id)
|
||||
: AbstractController(controller_manager, target_id, LUN_MAX)
|
||||
ScsiController::ScsiController(BUS& bus, int target_id) : AbstractController(bus, target_id, ControllerManager::GetScsiLunMax())
|
||||
{
|
||||
logger.SetIdAndLun(target_id, -1);
|
||||
|
||||
// The initial buffer size will default to either the default buffer size OR
|
||||
// the size of an Ethernet message, whichever is larger.
|
||||
AllocateBuffer(std::max(DEFAULT_BUFFER_SIZE, ETH_FRAME_LEN + 16 + ETH_FCS_LEN));
|
||||
@ -44,26 +43,21 @@ void ScsiController::Reset()
|
||||
|
||||
execstart = 0;
|
||||
identified_lun = -1;
|
||||
initiator_id = UNKNOWN_INITIATOR_ID;
|
||||
|
||||
scsi.atnmsg = false;
|
||||
scsi.msc = 0;
|
||||
scsi.msb = {};
|
||||
|
||||
SetByteTransfer(false);
|
||||
scsi = {};
|
||||
}
|
||||
|
||||
phase_t ScsiController::Process(int id)
|
||||
bool ScsiController::Process(int id)
|
||||
{
|
||||
GetBus().Acquire();
|
||||
|
||||
if (GetBus().GetRST()) {
|
||||
logger.Warn("RESET signal received!");
|
||||
LogWarn("RESET signal received!");
|
||||
|
||||
Reset();
|
||||
|
||||
GetBus().Reset();
|
||||
|
||||
return GetPhase();
|
||||
return false;
|
||||
}
|
||||
|
||||
initiator_id = id;
|
||||
@ -72,23 +66,20 @@ phase_t ScsiController::Process(int id)
|
||||
ProcessPhase();
|
||||
}
|
||||
catch(const scsi_exception&) {
|
||||
// Any exception should have been handled during the phase processing
|
||||
logger.Error("Unhandled SCSI error, resetting controller and bus and entering bus free phase");
|
||||
LogError("Unhandled SCSI error, resetting controller and bus and entering bus free phase");
|
||||
|
||||
Reset();
|
||||
GetBus().Reset();
|
||||
|
||||
BusFree();
|
||||
}
|
||||
|
||||
return GetPhase();
|
||||
return !IsBusFree();
|
||||
}
|
||||
|
||||
void ScsiController::BusFree()
|
||||
{
|
||||
if (!IsBusFree()) {
|
||||
logger.Trace("Bus free phase");
|
||||
|
||||
LogTrace("Bus Free phase");
|
||||
SetPhase(phase_t::busfree);
|
||||
|
||||
GetBus().SetREQ(false);
|
||||
@ -98,7 +89,7 @@ void ScsiController::BusFree()
|
||||
GetBus().SetBSY(false);
|
||||
|
||||
// Initialize status and message
|
||||
SetStatus(status::GOOD);
|
||||
SetStatus(status::good);
|
||||
SetMessage(0x00);
|
||||
|
||||
// Initialize ATN message reception status
|
||||
@ -108,39 +99,6 @@ void ScsiController::BusFree()
|
||||
|
||||
SetByteTransfer(false);
|
||||
|
||||
if (shutdown_mode != piscsi_shutdown_mode::NONE) {
|
||||
// Prepare the shutdown by flushing all caches
|
||||
for (const auto& device : GetControllerManager()->GetAllDevices()) {
|
||||
device->FlushCache();
|
||||
}
|
||||
}
|
||||
|
||||
// When the bus is free PiSCSI or the Pi may be shut down.
|
||||
// This code has to be executed in the bus free phase and thus has to be located in the controller.
|
||||
switch(shutdown_mode) {
|
||||
case piscsi_shutdown_mode::STOP_PISCSI:
|
||||
logger.Info("PiSCSI shutdown requested");
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
case piscsi_shutdown_mode::STOP_PI:
|
||||
logger.Info("Raspberry Pi shutdown requested");
|
||||
if (system("init 0") == -1) {
|
||||
logger.Error("Raspberry Pi shutdown failed: " + string(strerror(errno)));
|
||||
}
|
||||
break;
|
||||
|
||||
case piscsi_shutdown_mode::RESTART_PI:
|
||||
logger.Info("Raspberry Pi restart requested");
|
||||
if (system("init 6") == -1) {
|
||||
logger.Error("Raspberry Pi restart failed: " + string(strerror(errno)));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -153,18 +111,7 @@ void ScsiController::BusFree()
|
||||
void ScsiController::Selection()
|
||||
{
|
||||
if (!IsSelection()) {
|
||||
// A different device controller was selected
|
||||
if (int id = 1 << GetTargetId(); (static_cast<int>(GetBus().GetDAT()) & id) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort if there is no LUN for this controller
|
||||
if (!GetLunCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.Trace("Selection phase");
|
||||
|
||||
LogTrace("Selection phase");
|
||||
SetPhase(phase_t::selection);
|
||||
|
||||
// Raise BSY and respond
|
||||
@ -174,6 +121,8 @@ void ScsiController::Selection()
|
||||
|
||||
// Selection completed
|
||||
if (!GetBus().GetSEL() && GetBus().GetBSY()) {
|
||||
LogTrace("Selection completed");
|
||||
|
||||
// Message out phase if ATN=1, otherwise command phase
|
||||
if (GetBus().GetATN()) {
|
||||
MsgOut();
|
||||
@ -186,8 +135,7 @@ void ScsiController::Selection()
|
||||
void ScsiController::Command()
|
||||
{
|
||||
if (!IsCommand()) {
|
||||
logger.Trace("Command phase");
|
||||
|
||||
LogTrace("Command phase");
|
||||
SetPhase(phase_t::command);
|
||||
|
||||
GetBus().SetMSG(false);
|
||||
@ -198,9 +146,9 @@ void ScsiController::Command()
|
||||
if (actual_count == 0) {
|
||||
stringstream s;
|
||||
s << "Received unknown command: $" << setfill('0') << setw(2) << hex << GetBuffer()[0];
|
||||
logger.Trace(s.str());
|
||||
LogTrace(s.str());
|
||||
|
||||
Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
Error(sense_key::illegal_request, asc::invalid_command_operation_code);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -210,16 +158,16 @@ void ScsiController::Command()
|
||||
if (actual_count != command_byte_count) {
|
||||
stringstream s;
|
||||
s << "Command byte count mismatch for command $" << setfill('0') << setw(2) << hex << GetBuffer()[0];
|
||||
logger.Error(s.str() + ": expected " + to_string(command_byte_count) + " bytes, received"
|
||||
LogError(s.str() + ": expected " + to_string(command_byte_count) + " bytes, received"
|
||||
+ to_string(actual_count) + " byte(s)");
|
||||
Error(sense_key::ABORTED_COMMAND);
|
||||
Error(sense_key::aborted_command);
|
||||
return;
|
||||
}
|
||||
|
||||
// Command data transfer
|
||||
AllocateCmd(command_byte_count);
|
||||
for (int i = 0; i < command_byte_count; i++) {
|
||||
GetCmd()[i] = GetBuffer()[i];
|
||||
SetCmdByte(i, GetBuffer()[i]);
|
||||
}
|
||||
|
||||
SetLength(0);
|
||||
@ -234,9 +182,9 @@ void ScsiController::Execute()
|
||||
s << "Controller is executing " << command_mapping.find(GetOpcode())->second.second << ", CDB $"
|
||||
<< setfill('0') << hex;
|
||||
for (int i = 0; i < BUS::GetCommandByteCount(static_cast<uint8_t>(GetOpcode())); i++) {
|
||||
s << setw(2) << GetCmd(i);
|
||||
s << setw(2) << GetCmdByte(i);
|
||||
}
|
||||
logger.Debug(s.str());
|
||||
LogDebug(s.str());
|
||||
|
||||
// Initialization for data transfer
|
||||
ResetOffset();
|
||||
@ -245,15 +193,15 @@ void ScsiController::Execute()
|
||||
|
||||
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE
|
||||
if (GetOpcode() != scsi_command::eCmdRequestSense) {
|
||||
SetStatus(status::GOOD);
|
||||
SetStatus(status::good);
|
||||
}
|
||||
|
||||
int lun = GetEffectiveLun();
|
||||
if (!HasDeviceForLun(lun)) {
|
||||
if (GetOpcode() != scsi_command::eCmdInquiry && GetOpcode() != scsi_command::eCmdRequestSense) {
|
||||
logger.Trace("Invalid LUN " + to_string(lun));
|
||||
LogTrace("Invalid LUN " + to_string(lun));
|
||||
|
||||
Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN);
|
||||
Error(sense_key::illegal_request, asc::invalid_lun);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -265,7 +213,7 @@ void ScsiController::Execute()
|
||||
|
||||
// SCSI-2 4.4.3 Incorrect logical unit handling
|
||||
if (GetOpcode() == scsi_command::eCmdInquiry && !HasDeviceForLun(lun)) {
|
||||
logger.Trace("Reporting LUN" + to_string(GetEffectiveLun()) + " as not supported");
|
||||
LogTrace("Reporting LUN" + to_string(GetEffectiveLun()) + " as not supported");
|
||||
|
||||
GetBuffer().data()[0] = 0x7f;
|
||||
|
||||
@ -279,7 +227,7 @@ void ScsiController::Execute()
|
||||
device->SetStatusCode(0);
|
||||
}
|
||||
|
||||
if (device->CheckReservation(initiator_id, GetOpcode(), GetCmd(4) & 0x01)) {
|
||||
if (device->CheckReservation(initiator_id, GetOpcode(), GetCmdByte(4) & 0x01)) {
|
||||
try {
|
||||
device->Dispatch(GetOpcode());
|
||||
}
|
||||
@ -288,7 +236,7 @@ void ScsiController::Execute()
|
||||
}
|
||||
}
|
||||
else {
|
||||
Error(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION, status::RESERVATION_CONFLICT);
|
||||
Error(sense_key::aborted_command, asc::no_additional_sense_information, status::reservation_conflict);
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,9 +252,8 @@ void ScsiController::Status()
|
||||
}
|
||||
|
||||
stringstream s;
|
||||
s << "Status Phase, status is $" << setfill('0') << setw(2) << hex << static_cast<int>(GetStatus());
|
||||
logger.Trace(s.str());
|
||||
|
||||
s << "Status phase, status is $" << setfill('0') << setw(2) << hex << static_cast<int>(GetStatus());
|
||||
LogTrace(s.str());
|
||||
SetPhase(phase_t::status);
|
||||
|
||||
// Signal line operated by the target
|
||||
@ -329,8 +276,7 @@ void ScsiController::Status()
|
||||
void ScsiController::MsgIn()
|
||||
{
|
||||
if (!IsMsgIn()) {
|
||||
logger.Trace("Message In phase");
|
||||
|
||||
LogTrace("Message In phase");
|
||||
SetPhase(phase_t::msgin);
|
||||
|
||||
GetBus().SetMSG(true);
|
||||
@ -347,8 +293,6 @@ void ScsiController::MsgIn()
|
||||
void ScsiController::MsgOut()
|
||||
{
|
||||
if (!IsMsgOut()) {
|
||||
logger.Trace("Message Out phase");
|
||||
|
||||
// process the IDENTIFY message
|
||||
if (IsSelection()) {
|
||||
scsi.atnmsg = true;
|
||||
@ -356,6 +300,7 @@ void ScsiController::MsgOut()
|
||||
scsi.msb = {};
|
||||
}
|
||||
|
||||
LogTrace("Message Out phase");
|
||||
SetPhase(phase_t::msgout);
|
||||
|
||||
GetBus().SetMSG(true);
|
||||
@ -387,8 +332,7 @@ void ScsiController::DataIn()
|
||||
return;
|
||||
}
|
||||
|
||||
logger.Trace("Entering Data In phase");
|
||||
|
||||
LogTrace("Data In phase");
|
||||
SetPhase(phase_t::datain);
|
||||
|
||||
GetBus().SetMSG(false);
|
||||
@ -417,11 +361,9 @@ void ScsiController::DataOut()
|
||||
return;
|
||||
}
|
||||
|
||||
logger.Trace("Data Out phase");
|
||||
|
||||
LogTrace("Data Out phase");
|
||||
SetPhase(phase_t::dataout);
|
||||
|
||||
// Signal line operated by the target
|
||||
GetBus().SetMSG(false);
|
||||
GetBus().SetCD(false);
|
||||
GetBus().SetIO(false);
|
||||
@ -441,7 +383,6 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
|
||||
// Reset check
|
||||
if (GetBus().GetRST()) {
|
||||
Reset();
|
||||
GetBus().Reset();
|
||||
|
||||
return;
|
||||
}
|
||||
@ -453,9 +394,9 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
|
||||
}
|
||||
|
||||
int lun = GetEffectiveLun();
|
||||
if (!HasDeviceForLun(lun) || asc == asc::INVALID_LUN) {
|
||||
if (!HasDeviceForLun(lun) || asc == asc::invalid_lun) {
|
||||
if (!HasDeviceForLun(0)) {
|
||||
logger.Error("No LUN 0");
|
||||
LogError("No LUN 0");
|
||||
|
||||
SetStatus(status);
|
||||
SetMessage(0x00);
|
||||
@ -468,11 +409,11 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
|
||||
lun = 0;
|
||||
}
|
||||
|
||||
if (sense_key != sense_key::NO_SENSE || asc != asc::NO_ADDITIONAL_SENSE_INFORMATION) {
|
||||
if (sense_key != sense_key::no_sense || asc != asc::no_additional_sense_information) {
|
||||
stringstream s;
|
||||
s << setfill('0') << setw(2) << hex << "Error status: Sense Key $" << static_cast<int>(sense_key)
|
||||
<< ", ASC $" << static_cast<int>(asc);
|
||||
logger.Debug(s.str());
|
||||
LogDebug(s.str());
|
||||
|
||||
// Set Sense Key and ASC for a subsequent REQUEST SENSE
|
||||
GetDeviceForLun(lun)->SetStatusCode((static_cast<int>(sense_key) << 16) | (static_cast<int>(asc) << 8));
|
||||
@ -481,7 +422,7 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
|
||||
SetStatus(status);
|
||||
SetMessage(0x00);
|
||||
|
||||
logger.Trace("Error (to status phase)");
|
||||
LogTrace("Error (to status phase)");
|
||||
|
||||
Status();
|
||||
}
|
||||
@ -492,7 +433,7 @@ void ScsiController::Send()
|
||||
assert(GetBus().GetIO());
|
||||
|
||||
if (HasValidLength()) {
|
||||
logger.Trace("Sending data, offset: " + to_string(GetOffset()) + ", length: " + to_string(GetLength()));
|
||||
LogTrace("Sending data, offset: " + to_string(GetOffset()) + ", length: " + to_string(GetLength()));
|
||||
|
||||
// The delay should be taken from the respective LUN, but as there are no Daynaport drivers for
|
||||
// LUNs other than 0 this work-around works.
|
||||
@ -500,7 +441,7 @@ void ScsiController::Send()
|
||||
HasDeviceForLun(0) ? GetDeviceForLun(0)->GetSendDelay() : 0);
|
||||
len != static_cast<int>(GetLength())) {
|
||||
// If you cannot send all, move to status phase
|
||||
Error(sense_key::ABORTED_COMMAND);
|
||||
Error(sense_key::aborted_command);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -510,33 +451,30 @@ void ScsiController::Send()
|
||||
}
|
||||
|
||||
DecrementBlocks();
|
||||
bool result = true;
|
||||
|
||||
// Processing after data collection (read/data-in only)
|
||||
if (IsDataIn() && GetBlocks() != 0) {
|
||||
if (IsDataIn() && HasBlocks()) {
|
||||
// set next buffer (set offset, length)
|
||||
result = XferIn(GetBuffer());
|
||||
logger.Trace("Processing after data collection. Blocks: " + to_string(GetBlocks()));
|
||||
if (!XferIn(GetBuffer())) {
|
||||
// If result FALSE, move to status phase
|
||||
Error(sense_key::aborted_command);
|
||||
return;
|
||||
}
|
||||
|
||||
LogTrace("Processing after data collection");
|
||||
}
|
||||
|
||||
// If result FALSE, move to status phase
|
||||
if (!result) {
|
||||
Error(sense_key::ABORTED_COMMAND);
|
||||
return;
|
||||
}
|
||||
|
||||
// Continue sending if block !=0
|
||||
if (GetBlocks() != 0){
|
||||
logger.Trace("Continuing to send. Blocks: " + to_string(GetBlocks()));
|
||||
// Continue sending if blocks != 0
|
||||
if (HasBlocks()) {
|
||||
LogTrace("Continuing to send");
|
||||
assert(HasValidLength());
|
||||
assert(GetOffset() == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move to next phase
|
||||
logger.Trace("Moving to next phase: " + string(BUS::GetPhaseStrRaw(GetPhase())));
|
||||
LogTrace("All data transferred, moving to next phase: " + string(BUS::GetPhaseStrRaw(GetPhase())));
|
||||
switch (GetPhase()) {
|
||||
// Message in phase
|
||||
case phase_t::msgin:
|
||||
// Completed sending response to extended message of IDENTIFY message
|
||||
if (scsi.atnmsg) {
|
||||
@ -551,13 +489,11 @@ void ScsiController::Send()
|
||||
}
|
||||
break;
|
||||
|
||||
// Data-in Phase
|
||||
case phase_t::datain:
|
||||
// status phase
|
||||
Status();
|
||||
break;
|
||||
|
||||
// status phase
|
||||
case phase_t::status:
|
||||
// Message in phase
|
||||
SetLength(1);
|
||||
@ -578,13 +514,13 @@ void ScsiController::Receive()
|
||||
assert(!GetBus().GetIO());
|
||||
|
||||
if (HasValidLength()) {
|
||||
logger.Trace("Receiving data, transfer length: " + to_string(GetLength()) + " byte(s)");
|
||||
LogTrace("Receiving data, transfer length: " + to_string(GetLength()) + " byte(s)");
|
||||
|
||||
// If not able to receive all, move to status phase
|
||||
if (uint32_t len = GetBus().ReceiveHandShake(GetBuffer().data() + GetOffset(), GetLength()); len != GetLength()) {
|
||||
logger.Error("Not able to receive " + to_string(GetLength()) + " byte(s) of data, only received "
|
||||
LogError("Not able to receive " + to_string(GetLength()) + " byte(s) of data, only received "
|
||||
+ to_string(len));
|
||||
Error(sense_key::ABORTED_COMMAND);
|
||||
Error(sense_key::aborted_command);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -603,10 +539,10 @@ void ScsiController::Receive()
|
||||
bool result = true;
|
||||
|
||||
// Processing after receiving data (by phase)
|
||||
logger.Trace("Phase: " + string(BUS::GetPhaseStrRaw(GetPhase())));
|
||||
LogTrace("Phase: " + string(BUS::GetPhaseStrRaw(GetPhase())));
|
||||
switch (GetPhase()) {
|
||||
case phase_t::dataout:
|
||||
if (GetBlocks() == 0) {
|
||||
if (!HasBlocks()) {
|
||||
// End with this buffer
|
||||
result = XferOut(false);
|
||||
} else {
|
||||
@ -633,12 +569,12 @@ void ScsiController::Receive()
|
||||
|
||||
// If result FALSE, move to status phase
|
||||
if (!result) {
|
||||
Error(sense_key::ABORTED_COMMAND);
|
||||
Error(sense_key::aborted_command);
|
||||
return;
|
||||
}
|
||||
|
||||
// Continue to receive if block != 0
|
||||
if (GetBlocks() != 0) {
|
||||
// Continue to receive if blocks != 0
|
||||
if (HasBlocks()) {
|
||||
assert(HasValidLength());
|
||||
assert(GetOffset() == 0);
|
||||
return;
|
||||
@ -692,7 +628,7 @@ void ScsiController::ReceiveBytes()
|
||||
bool result = true;
|
||||
|
||||
// Processing after receiving data (by phase)
|
||||
logger.Trace("Phase: " + string(BUS::GetPhaseStrRaw(GetPhase())));
|
||||
LogTrace("Phase: " + string(BUS::GetPhaseStrRaw(GetPhase())));
|
||||
switch (GetPhase()) {
|
||||
case phase_t::dataout:
|
||||
result = XferOut(false);
|
||||
@ -716,7 +652,7 @@ void ScsiController::ReceiveBytes()
|
||||
|
||||
// If result FALSE, move to status phase
|
||||
if (!result) {
|
||||
Error(sense_key::ABORTED_COMMAND);
|
||||
Error(sense_key::aborted_command);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -752,7 +688,7 @@ bool ScsiController::XferOut(bool cont)
|
||||
SetByteTransfer(false);
|
||||
|
||||
auto device = GetDeviceForLun(GetEffectiveLun());
|
||||
return device != nullptr ? device->WriteByteSequence(GetBuffer(), count) : false;
|
||||
return device != nullptr ? device->WriteByteSequence(span(GetBuffer().data(), count)) : false;
|
||||
}
|
||||
|
||||
void ScsiController::DataOutNonBlockOriented()
|
||||
@ -760,7 +696,6 @@ void ScsiController::DataOutNonBlockOriented()
|
||||
assert(IsDataOut());
|
||||
|
||||
switch (GetOpcode()) {
|
||||
// TODO Check why these cases are needed
|
||||
case scsi_command::eCmdWrite6:
|
||||
case scsi_command::eCmdWrite10:
|
||||
case scsi_command::eCmdWrite16:
|
||||
@ -777,7 +712,7 @@ void ScsiController::DataOutNonBlockOriented()
|
||||
device->ModeSelect(GetOpcode(), GetCmd(), GetBuffer(), GetOffset());
|
||||
}
|
||||
else {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_command_operation_code);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -790,7 +725,7 @@ void ScsiController::DataOutNonBlockOriented()
|
||||
stringstream s;
|
||||
s << "Unexpected Data Out phase for command $" << setfill('0') << setw(2) << hex
|
||||
<< static_cast<int>(GetOpcode());
|
||||
logger.Warn(s.str());
|
||||
LogWarn(s.str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -807,9 +742,9 @@ bool ScsiController::XferIn(vector<uint8_t>& buf)
|
||||
|
||||
stringstream s;
|
||||
s << "Command: $" << setfill('0') << setw(2) << hex << static_cast<int>(GetOpcode());
|
||||
logger.Trace(s.str());
|
||||
LogTrace(s.str());
|
||||
|
||||
int lun = GetEffectiveLun();
|
||||
const int lun = GetEffectiveLun();
|
||||
if (!HasDeviceForLun(lun)) {
|
||||
return false;
|
||||
}
|
||||
@ -821,7 +756,7 @@ bool ScsiController::XferIn(vector<uint8_t>& buf)
|
||||
case scsi_command::eCmdRead16:
|
||||
// Read from disk
|
||||
try {
|
||||
SetLength(dynamic_pointer_cast<Disk>(GetDeviceForLun(lun))->Read(GetCmd(), buf, GetNext()));
|
||||
SetLength(dynamic_pointer_cast<Disk>(GetDeviceForLun(lun))->Read(buf, GetNext()));
|
||||
}
|
||||
catch(const scsi_exception&) {
|
||||
// If there is an error, go to the status phase
|
||||
@ -878,13 +813,23 @@ bool ScsiController::XferOutBlockOriented(bool cont)
|
||||
case scsi_command::eCmdWrite6:
|
||||
case scsi_command::eCmdWrite10:
|
||||
case scsi_command::eCmdWrite16:
|
||||
// TODO Verify has to verify, not to write
|
||||
// TODO Verify has to verify, not to write, see https://github.com/PiSCSI/piscsi/issues/807
|
||||
case scsi_command::eCmdVerify10:
|
||||
case scsi_command::eCmdVerify16:
|
||||
{
|
||||
// Special case for SCBR and SCDP
|
||||
if (auto byte_writer = dynamic_pointer_cast<ByteWriter>(device); byte_writer) {
|
||||
if (!byte_writer->WriteBytes(GetCmd(), GetBuffer(), GetLength())) {
|
||||
// TODO Get rid of this special case for SCBR
|
||||
if (auto bridge = dynamic_pointer_cast<SCSIBR>(device); bridge) {
|
||||
if (!bridge->ReadWrite(GetCmd(), GetBuffer())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResetOffset();
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO Get rid of this special case for SCDP
|
||||
if (auto daynaport = dynamic_pointer_cast<SCSIDaynaPort>(device); daynaport) {
|
||||
if (!daynaport->Write(GetCmd(), GetBuffer())) {
|
||||
return false;
|
||||
}
|
||||
|
||||