diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 20e3089e..202a2fdf 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1 +1 @@
-* @akuker @erichelgeson @rdmark
+* @akuker @rdmark
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 0dc35b82..c828582e 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -4,6 +4,7 @@
- Which github revision of software:
- Which board version:
- Which computer is the PiSCSI connected to:
+- Which OS you are using (output of 'lsb_release -a'):
# Describe the issue
diff --git a/.github/workflows/build_code.yml b/.github/workflows/build_code.yml
index 0970e9f5..e0c38852 100644
--- a/.github/workflows/build_code.yml
+++ b/.github/workflows/build_code.yml
@@ -7,6 +7,19 @@ on:
- 'cpp/**'
- '.github/workflows/build_code.yml'
- '.github/workflows/arm_cross_compile.yml'
+ pull_request:
+ paths:
+ - 'cpp/**'
+ - '.github/workflows/build_code.yml'
+ - '.github/workflows/arm_cross_compile.yml'
+ types:
+ - assigned
+ - opened
+ - synchronize
+ - reopened
+ branches:
+ - 'develop'
+ - 'main'
jobs:
fullspec:
diff --git a/.github/workflows/cpp.yml b/.github/workflows/cpp.yml
index 33a84537..7c3eac8c 100644
--- a/.github/workflows/cpp.yml
+++ b/.github/workflows/cpp.yml
@@ -1,11 +1,20 @@
-name: C++ Tests/Analysis
+name: C++ Tests; Full Static Analysis
on:
workflow_dispatch:
push:
paths:
- 'cpp/**'
+ - 'python/**'
- '.github/workflows/cpp.yml'
+ pull_request:
+ paths:
+ - 'cpp/**'
+ - 'python/**'
+ - '.github/workflows/cpp.yml'
+ branches:
+ - 'develop'
+ - 'main'
env:
APT_PACKAGES: libspdlog-dev libpcap-dev libevdev2 libev-dev protobuf-compiler libgtest-dev libgmock-dev
@@ -40,10 +49,10 @@ jobs:
env:
SOURCES: cpp
BUILD_WRAPPER_OUT_DIR: "$HOME/.build_wrapper_out" # Directory where build-wrapper output will be placed
- SONAR_SCANNER_VERSION: 4.7.0.2747
+ SONAR_SCANNER_VERSION: 5.0.1.3006
SONAR_SERVER_URL: "https://sonarcloud.io"
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- SONAR_PROJECT_KEY: "akuker_PISCSI"
+ SONAR_PROJECT_KEY: "akuker-PISCSI"
SONAR_ORGANIZATION: "piscsi"
steps:
- uses: actions/checkout@v3
@@ -106,4 +115,4 @@ jobs:
--define sonar.coverage.exclusions="cpp/**/test/**"
--define sonar.cpd.exclusions="cpp/**/test/**"
--define sonar.inclusions="cpp/**,python/**"
- --define sonar.python.version=3.7,3.9
+ --define sonar.python.version=3.9,3.11
diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml
index 69da8317..c2f0c086 100644
--- a/.github/workflows/web.yml
+++ b/.github/workflows/web.yml
@@ -8,6 +8,15 @@ on:
- 'python/common/**'
- '.github/workflows/web.yml'
- 'easyinstall.sh'
+ pull_request:
+ paths:
+ - 'python/web/**'
+ - 'python/common/**'
+ - '.github/workflows/web.yml'
+ - 'easyinstall.sh'
+ branches:
+ - 'develop'
+ - 'main'
jobs:
backend_checks:
@@ -123,8 +132,12 @@ jobs:
id: npm
- name: Stylelint
- run: npx stylelint src/static/themes/modern/style.css
+ run: |
+ npx stylelint src/static/themes/modern/style.css
+ npx stylelint src/static/themes/classic/style.css
- name: Prettier
- run: npx prettier --check src/static/themes/modern/style.css
+ run: |
+ npx prettier --check src/static/themes/modern/style.css
+ npx prettier --check src/static/themes/classic/style.css
if: success() || failure() && steps.npm.outcome == 'success'
diff --git a/README.md b/README.md
index f58de439..3747d6f4 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ Please check out the full story with much more detail on the [wiki](https://gith
# How do I contribute?
PiSCSI is using the Gitflow Workflow. A quick overview:
-- The *master* branch should always reflect the contents of the last stable release
+- The *main* branch should always reflect the contents of the last stable release
- The *develop* branch should contain the latest tested & approved updates. Pull requests should be used to merge changes into develop.
- The rest of the feature branches are for developing new features
- A tag will be created for each "release". The releases will be named .. where the release number is incremented for each subsequent release tagged in the same calendar month. The first release of the month of January 2021 is called "21.01.01", the second one in the same month "21.01.02" and so on.
diff --git a/cpp/Makefile b/cpp/Makefile
index 1a5f95b0..c5d0b83d 100644
--- a/cpp/Makefile
+++ b/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,
@@ -17,29 +15,20 @@ DEBUG ?= 0
ifeq ($(DEBUG), 1)
# Debug compiler flags
CXXFLAGS += -O0 -g -Wall -Wextra -DDEBUG
- BUILD_TYPE = Debug
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
@@ -50,7 +39,7 @@ endif
CONNECT_TYPE ?= FULLSPEC
ifdef CONNECT_TYPE
-CXXFLAGS += -DCONNECT_TYPE_$(CONNECT_TYPE)
+ CXXFLAGS += -DCONNECT_TYPE_$(CONNECT_TYPE)
endif
PISCSI = piscsi
@@ -78,54 +67,55 @@ BIN_ALL = \
$(BINDIR)/$(PISCSI) \
$(BINDIR)/$(SCSICTL) \
$(BINDIR)/$(SCSIMON) \
- $(BINDIR)/$(SCSIDUMP) \
$(BINDIR)/$(SCSILOOP)
+# scsidump requires initiator support
+ifeq ($(CONNECT_TYPE), FULLSPEC)
+ BIN_ALL += $(BINDIR)/$(SCSIDUMP)
+endif
+
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 ./piscsi -name '*.cpp' | grep -v piscsi.cpp)
SRC_PISCSI_CORE += $(shell find ./controllers -name '*.cpp')
SRC_PISCSI_CORE += $(shell find ./devices -name '*.cpp')
SRC_PISCSI_CORE += $(shell find ./hal -name '*.cpp')
-SRC_PISCSI = piscsi.cpp
+SRC_PISCSI = piscsi/piscsi.cpp
-SRC_SCSIMON = scsimon.cpp
-SRC_SCSIMON += $(shell find ./monitor -name '*.cpp')
+SRC_SCSIMON = scsimon/scsimon.cpp
+SRC_SCSIMON += $(shell find ./scsimon -name '*.cpp' | grep -v scsimon.cpp)
SRC_SCSIMON += $(shell find ./hal -name '*.cpp')
-SRC_SCSICTL_CORE = $(shell find ./scsictl -name '*.cpp')
+SRC_SCSICTL_CORE = $(shell find ./scsictl -name '*.cpp' | grep -v scsictl.cpp)
-SRC_SCSICTL = scsictl.cpp
+SRC_SCSICTL = scsictl/scsictl.cpp
-SRC_SCSIDUMP = scsidump.cpp
-SRC_SCSIDUMP += $(shell find ./scsidump -name '*.cpp')
+SRC_SCSIDUMP = scsidump/scsidump.cpp
+SRC_SCSIDUMP += $(shell find ./scsidump -name '*.cpp' | grep -v scsidump.cpp)
SRC_SCSIDUMP += $(shell find ./hal -name '*.cpp')
SRC_PISCSI_TEST = $(shell find ./test -name '*.cpp')
-SRC_PISCSI_TEST += $(shell find ./scsidump -name '*.cpp')
-SRC_PISCSI_TEST += $(shell find ./monitor -name '*.cpp')
+SRC_PISCSI_TEST += $(shell find ./scsidump -name '*.cpp' | grep -v scsidump.cpp)
-SRC_SCSILOOP = scsiloop.cpp
-SRC_SCSILOOP += $(shell find ./scsiloop -name '*.cpp')
+SRC_SCSILOOP = scsiloop/scsiloop.cpp
+SRC_SCSILOOP += $(shell find ./scsiloop -name '*.cpp' | grep -v scsiloop.cpp)
SRC_SCSILOOP += $(shell find ./hal -name '*.cpp')
-vpath %.h ./ ./shared ./controllers ./devices ./monitor ./hal \
- ./hal/boards ./hal/pi_defs ./piscsi ./scsictl ./scsidump \
- ./scsiloop
-vpath %.cpp ./ ./shared ./controllers ./devices ./monitor ./hal \
- ./hal/boards ./hal/pi_defs ./piscsi ./scsictl ./scsidump \
- ./scsiloop ./test
+vpath %.h ./shared ./controllers ./devices ./scsimon ./hal \
+ ./hal/pi_defs ./piscsi ./scsictl ./scsidump ./scsiloop
+vpath %.cpp ./shared ./controllers ./devices ./scsimon ./hal \
+ ./hal/pi_defs ./piscsi ./scsictl ./scsidump ./scsiloop ./test
vpath %.o ./$(OBJDIR)
vpath ./$(BINDIR)
@@ -142,6 +132,22 @@ OBJ_SHARED := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SHARED:%.cpp=%.o)))
OBJ_PROTOBUF := $(addprefix $(OBJDIR)/,$(notdir $(SRC_PROTOBUF:%.cpp=%.o)))
OBJ_GENERATED := $(addprefix $(OBJDIR)/,$(notdir $(SRC_GENERATED:%.cpp=%.o)))
+BINARIES = $(USR_LOCAL_BIN)/$(SCSICTL) \
+ $(USR_LOCAL_BIN)/$(PISCSI) \
+ $(USR_LOCAL_BIN)/$(SCSIMON) \
+ $(USR_LOCAL_BIN)/$(SCSILOOP)
+ifeq ($(CONNECT_TYPE), FULLSPEC)
+ BINARIES += $(USR_LOCAL_BIN)/$(SCSIDUMP)
+endif
+
+MAN_PAGES = $(MAN_PAGE_DIR)/piscsi.1 \
+ $(MAN_PAGE_DIR)/scsictl.1 \
+ $(MAN_PAGE_DIR)/scsimon.1 \
+ $(MAN_PAGE_DIR)/scsiloop.1
+ifeq ($(CONNECT_TYPE), FULLSPEC)
+ MAN_PAGES += $(MAN_PAGE_DIR)/scsidump.1
+endif
+
GENERATED_DIR := generated
# For the unit tests, the following functions will be "wrapped" by the linker, meaning the
@@ -167,7 +173,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 +186,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 +205,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 +220,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)
@@ -245,16 +252,8 @@ clean:
## * sudo systemctl start piscsi
.PHONY: install
install: \
- $(MAN_PAGE_DIR)/piscsi.1 \
- $(MAN_PAGE_DIR)/scsictl.1 \
- $(MAN_PAGE_DIR)/scsimon.1 \
- $(MAN_PAGE_DIR)/scsiloop.1 \
- $(MAN_PAGE_DIR)/scsidump.1 \
- $(USR_LOCAL_BIN)/$(SCSICTL) \
- $(USR_LOCAL_BIN)/$(PISCSI) \
- $(USR_LOCAL_BIN)/$(SCSIMON) \
- $(USR_LOCAL_BIN)/$(SCSILOOP) \
- $(USR_LOCAL_BIN)/$(SCSIDUMP) \
+ $(MAN_PAGES) \
+ $(BINARIES) \
$(SYSTEMD_CONF) \
$(RSYSLOG_CONF) \
$(RSYSLOG_LOG)
diff --git a/cpp/controllers/abstract_controller.cpp b/cpp/controllers/abstract_controller.cpp
index 17e91984..36242a6c 100644
--- a/cpp/controllers/abstract_controller.cpp
+++ b/cpp/controllers/abstract_controller.cpp
@@ -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
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> AbstractController::GetDevices() const
{
unordered_set> 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 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 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(log2(id_data_without_target & -id_data_without_target));
}
- return initiator_id;
+ return UNKNOWN_INITIATOR_ID;
}
diff --git a/cpp/controllers/abstract_controller.h b/cpp/controllers/abstract_controller.h
index ae718bbf..820b98f3 100644
--- a/cpp/controllers/abstract_controller.h
+++ b/cpp/controllers/abstract_controller.h
@@ -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
#include
+#include
#include
#include
+#include
using namespace std;
class PrimaryDevice;
-class AbstractController : public PhaseHandler, public enable_shared_from_this
+class AbstractController : public PhaseHandler
{
public:
@@ -37,19 +39,19 @@ public:
RESTART_PI
};
- AbstractController(shared_ptr 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> GetDevices() const;
shared_ptr GetDeviceForLun(int) const;
bool AddDevice(shared_ptr);
- bool RemoveDevice(shared_ptr);
+ 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& 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& 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 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(ctrl.cmd[0]); }
+ auto GetOpcode() const { return static_cast(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 cmd = vector(16);
@@ -121,7 +130,9 @@ private:
ctrl_t ctrl = {};
- weak_ptr controller_manager;
+ BUS& bus;
+
+ DeviceLogger device_logger;
// Logical units of this controller mapped to their LUN numbers
unordered_map> 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;
};
diff --git a/cpp/controllers/controller_manager.cpp b/cpp/controllers/controller_manager.cpp
index 9f877cdb..3b55833d 100644
--- a/cpp/controllers/controller_manager.cpp
+++ b/cpp/controllers/controller_manager.cpp
@@ -3,29 +3,37 @@
// 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 device)
+shared_ptr ControllerManager::CreateScsiController(BUS& bus, int id) const
{
- auto controller = FindController(id);
- if (controller != nullptr) {
+ auto controller = make_shared(bus, id);
+ controller->Init();
+
+ return controller;
+}
+
+bool ControllerManager::AttachToController(BUS& bus, int id, shared_ptr 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(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 +43,37 @@ bool ControllerManager::AttachToScsiController(int id, shared_ptr
return false;
}
-bool ControllerManager::DeleteController(shared_ptr controller)
+bool ControllerManager::DeleteController(const AbstractController& controller)
{
- return controllers.erase(controller->GetTargetId()) == 1;
-}
-
-shared_ptr 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> 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 ControllerManager::FindController(int target_id) const
@@ -57,11 +82,15 @@ shared_ptr 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> ControllerManager::GetAllDevices() const
{
unordered_set> 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 +98,12 @@ unordered_set> 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 ControllerManager::GetDeviceByIdAndLun(int id, int lun) const
+shared_ptr ControllerManager::GetDeviceForIdAndLun(int id, int lun) const
{
if (const auto& controller = FindController(id); controller != nullptr) {
return controller->GetDeviceForLun(lun);
diff --git a/cpp/controllers/controller_manager.h b/cpp/controllers/controller_manager.h
index dd145e07..43f6177c 100644
--- a/cpp/controllers/controller_manager.h
+++ b/cpp/controllers/controller_manager.h
@@ -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
#include
#include
-#include "hal/bus.h"
using namespace std;
-class AbstractController;
+class ScsiController;
class PrimaryDevice;
-class ControllerManager : public enable_shared_from_this
+class ControllerManager
{
- BUS& bus;
-
- unordered_map> 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);
- bool DeleteController(shared_ptr);
- shared_ptr IdentifyController(int) const;
- shared_ptr FindController(int) const;
- unordered_set> GetAllDevices() const;
+ bool AttachToController(BUS&, int, shared_ptr);
+ bool DeleteController(const AbstractController&);
void DeleteAllControllers();
- shared_ptr GetDeviceByIdAndLun(int, int) const;
+ AbstractController::piscsi_shutdown_mode ProcessOnController(int) const;
+ shared_ptr FindController(int) const;
+ bool HasController(int) const;
+ unordered_set> GetAllDevices() const;
+ bool HasDeviceForIdAndLun(int, int) const;
+ shared_ptr GetDeviceForIdAndLun(int, int) const;
+
+ static int GetScsiIdMax() { return 8; }
+ static int GetScsiLunMax() { return 32; }
+
+private:
+
+ shared_ptr CreateScsiController(BUS&, int) const;
+
+ // Controllers mapped to their device IDs
+ unordered_map> controllers;
};
diff --git a/cpp/controllers/phase_handler.cpp b/cpp/controllers/phase_handler.cpp
new file mode 100644
index 00000000..664bd095
--- /dev/null
+++ b/cpp/controllers/phase_handler.cpp
@@ -0,0 +1,22 @@
+//---------------------------------------------------------------------------
+//
+// SCSI Target Emulator PiSCSI
+// for Raspberry Pi
+//
+// Copyright (C) 2023 Uwe Seimet
+//
+//---------------------------------------------------------------------------
+
+#include "phase_handler.h"
+
+void PhaseHandler::Init()
+{
+ phase_executors[phase_t::busfree] = [this] () { BusFree(); };
+ phase_executors[phase_t::selection] = [this] () { Selection(); };
+ phase_executors[phase_t::dataout] = [this] () { DataOut(); };
+ phase_executors[phase_t::datain] = [this] () { DataIn(); };
+ phase_executors[phase_t::command] = [this] () { Command(); };
+ phase_executors[phase_t::status] = [this] () { Status(); };
+ phase_executors[phase_t::msgout] = [this] () { MsgOut(); };
+ phase_executors[phase_t::msgin] = [this] () { MsgIn(); };
+}
diff --git a/cpp/controllers/phase_handler.h b/cpp/controllers/phase_handler.h
index a50adf39..949c122d 100644
--- a/cpp/controllers/phase_handler.h
+++ b/cpp/controllers/phase_handler.h
@@ -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
+#include
- using namespace scsi_defs;
+using namespace scsi_defs;
class PhaseHandler
{
@@ -22,6 +25,8 @@ public:
PhaseHandler() = default;
virtual ~PhaseHandler() = default;
+ void Init();
+
virtual void BusFree() = 0;
virtual void Selection() = 0;
virtual void Command() = 0;
@@ -31,7 +36,7 @@ public:
virtual void MsgIn() = 0;
virtual void MsgOut() = 0;
- virtual phase_t Process(int) = 0;
+ virtual bool Process(int) = 0;
protected:
@@ -45,4 +50,18 @@ 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:
+
+ unordered_map> phase_executors;
};
diff --git a/cpp/controllers/scsi_controller.cpp b/cpp/controllers/scsi_controller.cpp
index 20aaa136..09752595 100644
--- a/cpp/controllers/scsi_controller.cpp
+++ b/cpp/controllers/scsi_controller.cpp
@@ -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 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(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);
@@ -230,13 +178,15 @@ void ScsiController::Command()
void ScsiController::Execute()
{
- stringstream s;
- s << "Controller is executing " << command_mapping.find(GetOpcode())->second.second << ", CDB $"
- << setfill('0') << hex;
- for (int i = 0; i < BUS::GetCommandByteCount(static_cast(GetOpcode())); i++) {
- s << setw(2) << GetCmd(i);
- }
- logger.Debug(s.str());
+ if (spdlog::get_level() == spdlog::level::trace) {
+ stringstream s;
+ s << "Controller is executing " << command_mapping.find(GetOpcode())->second.second << ", CDB $"
+ << setfill('0') << hex;
+ for (int i = 0; i < BUS::GetCommandByteCount(static_cast(GetOpcode())); i++) {
+ s << setw(2) << GetCmdByte(i);
+ }
+ LogTrace(s.str());
+ }
// Initialization for data transfer
ResetOffset();
@@ -245,15 +195,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 +215,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 +229,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 +238,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 +254,8 @@ void ScsiController::Status()
}
stringstream s;
- s << "Status Phase, status is $" << setfill('0') << setw(2) << hex << static_cast(GetStatus());
- logger.Trace(s.str());
-
+ s << "Status phase, status is $" << setfill('0') << setw(2) << hex << static_cast(GetStatus());
+ LogTrace(s.str());
SetPhase(phase_t::status);
// Signal line operated by the target
@@ -329,8 +278,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 +295,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 +302,7 @@ void ScsiController::MsgOut()
scsi.msb = {};
}
+ LogTrace("Message Out phase");
SetPhase(phase_t::msgout);
GetBus().SetMSG(true);
@@ -387,8 +334,7 @@ void ScsiController::DataIn()
return;
}
- logger.Trace("Entering Data In phase");
-
+ LogTrace("Data In phase");
SetPhase(phase_t::datain);
GetBus().SetMSG(false);
@@ -417,11 +363,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 +385,6 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
// Reset check
if (GetBus().GetRST()) {
Reset();
- GetBus().Reset();
return;
}
@@ -453,9 +396,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 +411,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(sense_key)
- << ", ASC $" << static_cast(asc);
- logger.Debug(s.str());
+ s << setfill('0') << hex << "Error status: Sense Key $" << setw(2) << static_cast(sense_key)
+ << ", ASC $" << setw(2) << static_cast(asc);
+ LogDebug(s.str());
// Set Sense Key and ASC for a subsequent REQUEST SENSE
GetDeviceForLun(lun)->SetStatusCode((static_cast(sense_key) << 16) | (static_cast(asc) << 8));
@@ -481,8 +424,6 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
SetStatus(status);
SetMessage(0x00);
- logger.Trace("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(GetLength())) {
// If you cannot send all, move to status phase
- Error(sense_key::ABORTED_COMMAND);
+ Error(sense_key::aborted_command);
return;
}
@@ -510,56 +451,46 @@ 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) {
- // flag off
scsi.atnmsg = false;
- // command phase
Command();
} else {
- // Bus free phase
BusFree();
}
break;
- // Data-in Phase
case phase_t::datain:
- // status phase
Status();
break;
- // status phase
case phase_t::status:
- // Message in phase
SetLength(1);
SetBlocks(1);
GetBuffer()[0] = (uint8_t)GetMessage();
@@ -578,13 +509,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 +534,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 +564,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 +623,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 +647,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,15 +683,14 @@ 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()
+void ScsiController::DataOutNonBlockOriented() const
{
assert(IsDataOut());
switch (GetOpcode()) {
- // TODO Check why these cases are needed
case scsi_command::eCmdWrite6:
case scsi_command::eCmdWrite10:
case scsi_command::eCmdWrite16:
@@ -768,18 +698,8 @@ void ScsiController::DataOutNonBlockOriented()
case scsi_command::eCmdWriteLong16:
case scsi_command::eCmdVerify10:
case scsi_command::eCmdVerify16:
- break;
-
case scsi_command::eCmdModeSelect6:
- case scsi_command::eCmdModeSelect10: {
- if (auto device = dynamic_pointer_cast(GetDeviceForLun(GetEffectiveLun()));
- device != nullptr) {
- device->ModeSelect(GetOpcode(), GetCmd(), GetBuffer(), GetOffset());
- }
- else {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
- }
- }
+ case scsi_command::eCmdModeSelect10:
break;
case scsi_command::eCmdSetMcastAddr:
@@ -790,7 +710,7 @@ void ScsiController::DataOutNonBlockOriented()
stringstream s;
s << "Unexpected Data Out phase for command $" << setfill('0') << setw(2) << hex
<< static_cast(GetOpcode());
- logger.Warn(s.str());
+ LogWarn(s.str());
break;
}
}
@@ -807,9 +727,9 @@ bool ScsiController::XferIn(vector& buf)
stringstream s;
s << "Command: $" << setfill('0') << setw(2) << hex << static_cast(GetOpcode());
- logger.Trace(s.str());
+ LogTrace(s.str());
- int lun = GetEffectiveLun();
+ const int lun = GetEffectiveLun();
if (!HasDeviceForLun(lun)) {
return false;
}
@@ -821,7 +741,7 @@ bool ScsiController::XferIn(vector& buf)
case scsi_command::eCmdRead16:
// Read from disk
try {
- SetLength(dynamic_pointer_cast(GetDeviceForLun(lun))->Read(GetCmd(), buf, GetNext()));
+ SetLength(dynamic_pointer_cast(GetDeviceForLun(lun))->Read(buf, GetNext()));
}
catch(const scsi_exception&) {
// If there is an error, go to the status phase
@@ -878,13 +798,20 @@ 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
- case scsi_command::eCmdVerify10:
- case scsi_command::eCmdVerify16:
{
- // Special case for SCBR and SCDP
- if (auto byte_writer = dynamic_pointer_cast(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(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(device); daynaport) {
+ if (!daynaport->Write(GetCmd(), GetBuffer())) {
return false;
}
@@ -898,7 +825,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
}
try {
- disk->Write(GetCmd(), GetBuffer(), GetNext() - 1);
+ disk->Write(GetBuffer(), GetNext() - 1);
}
catch(const scsi_exception& e) {
Error(e.get_sense_key(), e.get_asc());
@@ -908,24 +835,41 @@ bool ScsiController::XferOutBlockOriented(bool cont)
// If you do not need the next block, end here
IncrementNext();
- if (!cont) {
- break;
+ if (cont) {
+ SetLength(disk->GetSectorSizeInBytes());
+ ResetOffset();
+ }
+
+ break;
+ }
+
+ case scsi_command::eCmdVerify10:
+ case scsi_command::eCmdVerify16:
+ {
+ auto disk = dynamic_pointer_cast(device);
+ if (disk == nullptr) {
+ return false;
+ }
+
+ // If you do not need the next block, end here
+ IncrementNext();
+ if (cont) {
+ SetLength(disk->GetSectorSizeInBytes());
+ ResetOffset();
}
- SetLength(disk->GetSectorSizeInBytes());
- ResetOffset();
break;
}
case scsi_command::eCmdSetMcastAddr:
- logger.Trace("Done with DaynaPort Set Multicast Address");
+ LogTrace("Done with DaynaPort Set Multicast Address");
break;
default:
stringstream s;
s << "Received an unexpected command ($" << setfill('0') << setw(2) << hex
<< static_cast(GetOpcode()) << ")";
- logger.Warn(s.str());
+ LogWarn(s.str());
break;
}
@@ -934,15 +878,15 @@ bool ScsiController::XferOutBlockOriented(bool cont)
void ScsiController::ProcessCommand()
{
- uint32_t len = GPIOBUS::GetCommandByteCount(GetBuffer()[0]);
+ const uint32_t len = GPIOBUS::GetCommandByteCount(GetBuffer()[0]);
stringstream s;
s << "CDB=$" << setfill('0') << setw(2) << hex;
for (uint32_t i = 0; i < len; i++) {
- GetCmd()[i] = GetBuffer()[i];
- s << GetCmd(i);
+ SetCmdByte(i, GetBuffer()[i]);
+ s << GetCmdByte(i);
}
- logger.Trace(s.str());
+ LogTrace(s.str());
Execute();
}
@@ -954,13 +898,13 @@ void ScsiController::ParseMessage()
const uint8_t message_type = scsi.msb[i];
if (message_type == 0x06) {
- logger.Trace("Received ABORT message");
+ LogTrace("Received ABORT message");
BusFree();
return;
}
if (message_type == 0x0C) {
- logger.Trace("Received BUS DEVICE RESET message");
+ LogTrace("Received BUS DEVICE RESET message");
scsi.syncoffset = 0;
if (auto device = GetDeviceForLun(identified_lun); device != nullptr) {
device->DiscardReservation();
@@ -971,11 +915,11 @@ void ScsiController::ParseMessage()
if (message_type >= 0x80) {
identified_lun = static_cast(message_type) & 0x1F;
- logger.Trace("Received IDENTIFY message for LUN " + to_string(identified_lun));
+ LogTrace("Received IDENTIFY message for LUN " + to_string(identified_lun));
}
if (message_type == 0x01) {
- logger.Trace("Received EXTENDED MESSAGE");
+ LogTrace("Received EXTENDED MESSAGE");
// Check only when synchronous transfer is possible
if (!scsi.syncenable || scsi.msb[i + 2] != 0x01) {
@@ -1046,4 +990,4 @@ void ScsiController::Sleep()
SysTimer::SleepUsec(MIN_EXEC_TIME - time);
}
execstart = 0;
-}
\ No newline at end of file
+}
diff --git a/cpp/controllers/scsi_controller.h b/cpp/controllers/scsi_controller.h
index fd5093bc..2fe3d525 100644
--- a/cpp/controllers/scsi_controller.h
+++ b/cpp/controllers/scsi_controller.h
@@ -15,8 +15,6 @@
#pragma once
#include "shared/scsi.h"
-#include "controller_manager.h"
-#include "devices/device_logger.h"
#include "abstract_controller.h"
#include
@@ -52,20 +50,17 @@ class ScsiController : public AbstractController
public:
- // Maximum number of logical units
- static inline const int LUN_MAX = 32;
-
- explicit ScsiController(shared_ptr, int);
+ ScsiController(BUS&, int);
~ScsiController() override = default;
void Reset() override;
- phase_t Process(int) override;
+ bool Process(int) override;
int GetEffectiveLun() const override;
- void Error(scsi_defs::sense_key sense_key, scsi_defs::asc asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
- scsi_defs::status status = scsi_defs::status::CHECK_CONDITION) override;
+ void Error(scsi_defs::sense_key sense_key, scsi_defs::asc asc = scsi_defs::asc::no_additional_sense_information,
+ scsi_defs::status status = scsi_defs::status::check_condition) override;
int GetInitiatorId() const override { return initiator_id; }
@@ -79,16 +74,8 @@ public:
void DataIn() override;
void DataOut() override;
- // TODO Make non-virtual private as soon as SysTimer calls do not segfault anymore on a regular PC,
- // e.g. by using ifdef __arm__. Currently the unit tests require this method to be public.
- virtual void Execute();
-
- void ScheduleShutdown(piscsi_shutdown_mode mode) override { shutdown_mode = mode; }
-
private:
- DeviceLogger logger;
-
// Execution start time
uint32_t execstart = 0;
@@ -106,9 +93,12 @@ private:
bool XferOutBlockOriented(bool);
void ReceiveBytes();
- void DataOutNonBlockOriented();
+ void DataOutNonBlockOriented() const;
void Receive();
+ // TODO Make non-virtual as soon as SysTimer calls do not segfault anymore on a regular PC, e.g. by using ifdef __arm__.
+ virtual void Execute();
+
void ProcessCommand();
void ParseMessage();
void ProcessMessage();
@@ -116,7 +106,5 @@ private:
void Sleep();
scsi_t scsi = {};
-
- AbstractController::piscsi_shutdown_mode shutdown_mode = AbstractController::piscsi_shutdown_mode::NONE;
};
diff --git a/cpp/devices/cd_track.h b/cpp/devices/cd_track.h
index cd712484..02302323 100644
--- a/cpp/devices/cd_track.h
+++ b/cpp/devices/cd_track.h
@@ -14,6 +14,7 @@
#pragma once
+#include
#include
using namespace std;
diff --git a/cpp/devices/cfilesystem.cpp b/cpp/devices/cfilesystem.cpp
index 7531c70a..381ed933 100644
--- a/cpp/devices/cfilesystem.cpp
+++ b/cpp/devices/cfilesystem.cpp
@@ -17,8 +17,8 @@
//
//---------------------------------------------------------------------------
-#include "shared/log.h"
#include "cfilesystem.h"
+#include
#include
#include
#include
@@ -3507,18 +3507,18 @@ int CFileSys::GetDPB(uint32_t nUnit, Human68k::dpb_t* pDpb) const
// Acquire sector data
if (!m_cEntry.GetCapacityCache(nUnit, &cap)) {
// Carry out an extra media check here because it may be skipped when doing a manual eject
- if (!m_cEntry.isEnable(nUnit))
- goto none;
-
// Media check
- if (m_cEntry.isMediaOffline(nUnit))
- goto none;
-
- // Get drive status
- m_cEntry.GetCapacity(nUnit, &cap);
+ if (!m_cEntry.isEnable(nUnit) || m_cEntry.isMediaOffline(nUnit)) {
+ cap.clusters = 4; // This is totally fine, right?
+ cap.sectors = 64;
+ cap.bytes = 512;
+ }
+ else {
+ // Get drive status
+ m_cEntry.GetCapacity(nUnit, &cap);
+ }
}
} else {
- none:
cap.clusters = 4; // This is totally fine, right?
cap.sectors = 64;
cap.bytes = 512;
diff --git a/cpp/devices/cfilesystem.h b/cpp/devices/cfilesystem.h
index 8c4c8703..e44339a2 100644
--- a/cpp/devices/cfilesystem.h
+++ b/cpp/devices/cfilesystem.h
@@ -13,6 +13,15 @@
#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
using TCHAR = char;
static const int FILEPATH_MAX = 260;
diff --git a/cpp/devices/ctapdriver.cpp b/cpp/devices/ctapdriver.cpp
index 2c868e51..85fc2adc 100644
--- a/cpp/devices/ctapdriver.cpp
+++ b/cpp/devices/ctapdriver.cpp
@@ -9,126 +9,71 @@
//
//---------------------------------------------------------------------------
-#include "shared/log.h"
#include "shared/piscsi_util.h"
-#include "shared/piscsi_exceptions.h"
+#include "shared/network_util.h"
#include
#include
#include
#include "ctapdriver.h"
+#include
#include
#include
#include
#ifdef __linux__
-#include
-#include
#include
#include
#endif
using namespace std;
using namespace piscsi_util;
+using namespace network_util;
-//---------------------------------------------------------------------------
-//
-// Initialization
-//
-//---------------------------------------------------------------------------
-static bool br_setif(int br_socket_fd, const char* bridgename, const char* ifname, bool add) {
+const string CTapDriver::BRIDGE_NAME = "piscsi_bridge";
+
+static string br_setif(int br_socket_fd, const string& bridgename, const string& ifname, bool add) {
#ifndef __linux__
- return false;
+ return "if_nametoindex: Linux is required";
#else
ifreq ifr;
- ifr.ifr_ifindex = if_nametoindex(ifname);
+ ifr.ifr_ifindex = if_nametoindex(ifname.c_str());
if (ifr.ifr_ifindex == 0) {
- LOGERROR("Can't if_nametoindex %s: %s", ifname, strerror(errno))
- return false;
+ return "Can't if_nametoindex " + ifname;
}
- strncpy(ifr.ifr_name, bridgename, IFNAMSIZ - 1);
+ strncpy(ifr.ifr_name, bridgename.c_str(), IFNAMSIZ - 1); //NOSONAR Using strncpy is safe
if (ioctl(br_socket_fd, add ? SIOCBRADDIF : SIOCBRDELIF, &ifr) < 0) {
- LOGERROR("Can't ioctl %s: %s", add ? "SIOCBRADDIF" : "SIOCBRDELIF", strerror(errno))
- return false;
+ return "Can't ioctl " + string(add ? "SIOCBRADDIF" : "SIOCBRDELIF");
}
- return true;
+ return "";
#endif
}
-CTapDriver::~CTapDriver()
-{
- if (m_hTAP != -1) {
- if (int br_socket_fd; (br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
- LOGERROR("Can't open bridge socket: %s", strerror(errno))
- } else {
- LOGDEBUG("brctl delif %s piscsi0", BRIDGE_NAME)
- if (!br_setif(br_socket_fd, BRIDGE_NAME, "piscsi0", false)) { //NOSONAR No exception is raised here
- LOGWARN("Warning: Removing piscsi0 from the bridge failed.")
- LOGWARN("You may need to manually remove the piscsi0 tap device from the bridge")
- }
- close(br_socket_fd);
- }
-
- // Release TAP defice
- close(m_hTAP);
- }
-
- if (m_pcap_dumper != nullptr) {
- pcap_dump_close(m_pcap_dumper);
- }
-
- if (m_pcap != nullptr) {
- pcap_close(m_pcap);
- }
-}
-
-static bool ip_link(int fd, const char* ifname, bool up) {
+string ip_link(int fd, const char* ifname, bool up) {
#ifndef __linux__
- return false;
+ return "Can't ip_link: Linux is required";
#else
ifreq ifr;
- strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1); // Need to save room for null terminator
- int err = ioctl(fd, SIOCGIFFLAGS, &ifr);
- if (err) {
- LOGERROR("Can't ioctl SIOCGIFFLAGS: %s", strerror(errno))
- return false;
+ strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); //NOSONAR Using strncpy is safe
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
+ return "Can't ioctl SIOCGIFFLAGS";
}
ifr.ifr_flags &= ~IFF_UP;
if (up) {
ifr.ifr_flags |= IFF_UP;
}
- err = ioctl(fd, SIOCSIFFLAGS, &ifr);
- if (err) {
- LOGERROR("Can't ioctl SIOCSIFFLAGS: %s", strerror(errno))
- return false;
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
+ return "Can't ioctl SIOCSIFFLAGS";
}
- return true;
+ return "";
#endif
}
-static bool is_interface_up(string_view interface) {
- string file = "/sys/class/net/";
- file += interface;
- file += "/carrier";
-
- bool status = true;
- FILE *fp = fopen(file.c_str(), "r");
- if (!fp || fgetc(fp) != '1') {
- status = false;
- }
-
- if (fp) {
- fclose(fp);
- }
-
- return status;
-}
-
-bool CTapDriver::Init(const unordered_map& const_params)
+bool CTapDriver::Init(const param_map& const_params)
{
#ifndef __linux__
return false;
#else
- unordered_map params = const_params;
+ param_map params = const_params;
stringstream s(params["interface"]);
string interface;
while (getline(s, interface, ',')) {
@@ -136,36 +81,33 @@ bool CTapDriver::Init(const unordered_map& const_params)
}
inet = params["inet"];
- LOGTRACE("Opening tap device")
+ spdlog::trace("Opening tap device");
// TAP device initilization
if ((m_hTAP = open("/dev/net/tun", O_RDWR)) < 0) {
- LOGERROR("Can't open tun: %s", strerror(errno))
+ LogErrno("Can't open tun");
return false;
}
- LOGTRACE("Opened tap device %d", m_hTAP)
-
// IFF_NO_PI for no extra packet information
ifreq ifr = {};
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
- string dev = "piscsi0";
- strncpy(ifr.ifr_name, dev.c_str(), IFNAMSIZ - 1);
+ strncpy(ifr.ifr_name, "piscsi0", IFNAMSIZ - 1); //NOSONAR Using strncpy is safe
- LOGTRACE("Going to open %s", ifr.ifr_name)
+ spdlog::trace("Going to open " + string(ifr.ifr_name));
- int ret = ioctl(m_hTAP, TUNSETIFF, (void *)&ifr);
+ const int ret = ioctl(m_hTAP, TUNSETIFF, (void *)&ifr);
if (ret < 0) {
- LOGERROR("Can't ioctl TUNSETIFF: %s", strerror(errno))
+ LogErrno("Can't ioctl TUNSETIFF");
close(m_hTAP);
return false;
}
- LOGTRACE("Return code from ioctl was %d", ret)
+ spdlog::trace("Return code from ioctl was " + to_string(ret));
const int ip_fd = socket(PF_INET, SOCK_DGRAM, 0);
if (ip_fd < 0) {
- LOGERROR("Can't open ip socket: %s", strerror(errno))
+ LogErrno("Can't open ip socket");
close(m_hTAP);
return false;
@@ -173,179 +115,72 @@ bool CTapDriver::Init(const unordered_map& const_params)
const int br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (br_socket_fd < 0) {
- LOGERROR("Can't open bridge socket: %s", strerror(errno))
+ LogErrno("Can't open bridge socket");
close(m_hTAP);
close(ip_fd);
return false;
}
+ auto cleanUp = [&] (const string& error) {
+ LogErrno(error);
+ close(m_hTAP);
+ close(ip_fd);
+ close(br_socket_fd);
+ return false;
+ };
+
// Check if the bridge has already been created
- string sys_file = "/sys/class/net/";
- sys_file += BRIDGE_NAME;
- if (access(sys_file.c_str(), F_OK)) {
- LOGINFO("%s is not yet available", BRIDGE_NAME)
+ // TODO Find an alternative to accessing a file, there is most likely a system call/ioctl
+ if (access(string("/sys/class/net/" + BRIDGE_NAME).c_str(), F_OK)) {
+ spdlog::trace("Checking which interface is available for creating the bridge " + BRIDGE_NAME);
- LOGTRACE("Checking which interface is available for creating the bridge")
-
- string bridge_interface;
- for (const string& iface : interfaces) {
- if (is_interface_up(iface)) {
- LOGTRACE("%s", string("Interface " + iface + " is up").c_str())
-
- bridge_interface = iface;
- break;
- }
- else {
- LOGTRACE("%s", string("Interface " + iface + " is not available or is not up").c_str())
- }
+ const auto& it = ranges::find_if(interfaces, [] (const string& iface) { return IsInterfaceUp(iface); } );
+ if (it == interfaces.end()) {
+ return cleanUp("No interface is up, not creating bridge " + BRIDGE_NAME);
}
- if (bridge_interface.empty()) {
- LOGERROR("No interface is up, not creating bridge")
- return false;
- }
+ const string bridge_interface = *it;
- LOGINFO("Creating %s for interface %s", BRIDGE_NAME, bridge_interface.c_str())
+ spdlog::info("Creating " + BRIDGE_NAME + " for interface " + bridge_interface);
if (bridge_interface == "eth0") {
- LOGTRACE("brctl addbr %s", BRIDGE_NAME)
-
- if (ioctl(br_socket_fd, SIOCBRADDBR, BRIDGE_NAME) < 0) {
- LOGERROR("Can't ioctl SIOCBRADDBR: %s", strerror(errno))
-
- close(m_hTAP);
- close(ip_fd);
- close(br_socket_fd);
- return false;
- }
-
- LOGTRACE("brctl addif %s %s", BRIDGE_NAME, bridge_interface.c_str())
-
- if (!br_setif(br_socket_fd, BRIDGE_NAME, bridge_interface.c_str(), true)) {
- close(m_hTAP);
- close(ip_fd);
- close(br_socket_fd);
- return false;
+ if (const string error = SetUpEth0(br_socket_fd, bridge_interface); !error.empty()) {
+ return cleanUp(error);
}
}
- else {
- string address = inet;
- string netmask = "255.255.255.0"; //NOSONAR This hardcoded IP address is safe
- if (size_t separatorPos = inet.find('/'); separatorPos != string::npos) {
- address = inet.substr(0, separatorPos);
-
- int m;
- if (!GetAsUnsignedInt(inet.substr(separatorPos + 1), m) || m < 8 || m > 32) {
- LOGERROR("Invalid CIDR netmask notation '%s'", inet.substr(separatorPos + 1).c_str())
-
- close(m_hTAP);
- close(ip_fd);
- close(br_socket_fd);
- return false;
- }
-
- // long long is required for compatibility with 32 bit platforms
- const auto mask = (long long)(pow(2, 32) - (1 << (32 - m)));
- netmask = to_string((mask >> 24) & 0xff) + '.' + to_string((mask >> 16) & 0xff) + '.' +
- to_string((mask >> 8) & 0xff) + '.' + to_string(mask & 0xff);
-
- }
-
- LOGTRACE("brctl addbr %s", BRIDGE_NAME)
-
- if (ioctl(br_socket_fd, SIOCBRADDBR, BRIDGE_NAME) < 0) {
- LOGERROR("Can't ioctl SIOCBRADDBR: %s", strerror(errno))
-
- close(m_hTAP);
- close(ip_fd);
- close(br_socket_fd);
- return false;
- }
-
- ifreq ifr_a;
- ifr_a.ifr_addr.sa_family = AF_INET;
- strncpy(ifr_a.ifr_name, BRIDGE_NAME, IFNAMSIZ);
- if (auto addr = (sockaddr_in*)&ifr_a.ifr_addr;
- inet_pton(AF_INET, address.c_str(), &addr->sin_addr) != 1) {
- LOGERROR("Can't convert '%s' into a network address: %s", address.c_str(), strerror(errno))
-
- close(m_hTAP);
- close(ip_fd);
- close(br_socket_fd);
- return false;
- }
-
- ifreq ifr_n;
- ifr_n.ifr_addr.sa_family = AF_INET;
- strncpy(ifr_n.ifr_name, BRIDGE_NAME, IFNAMSIZ);
- if (auto mask = (sockaddr_in*)&ifr_n.ifr_addr;
- inet_pton(AF_INET, netmask.c_str(), &mask->sin_addr) != 1) {
- LOGERROR("Can't convert '%s' into a netmask: %s", netmask.c_str(), strerror(errno))
-
- close(m_hTAP);
- close(ip_fd);
- close(br_socket_fd);
- return false;
- }
-
- LOGTRACE("ip address add %s dev %s", inet.c_str(), BRIDGE_NAME)
-
- if (ioctl(ip_fd, SIOCSIFADDR, &ifr_a) < 0 || ioctl(ip_fd, SIOCSIFNETMASK, &ifr_n) < 0) {
- LOGERROR("Can't ioctl SIOCSIFADDR or SIOCSIFNETMASK: %s", strerror(errno))
-
- close(m_hTAP);
- close(ip_fd);
- close(br_socket_fd);
- return false;
- }
+ else if (const string error = SetUpNonEth0(br_socket_fd, ip_fd, inet); !error.empty()) {
+ return cleanUp(error);
}
- LOGTRACE("ip link set dev %s up", BRIDGE_NAME)
+ spdlog::trace(">ip link set dev " + BRIDGE_NAME + " up");
- if (!ip_link(ip_fd, BRIDGE_NAME, true)) {
- close(m_hTAP);
- close(ip_fd);
- close(br_socket_fd);
- return false;
+ if (const string error = ip_link(ip_fd, BRIDGE_NAME.c_str(), true); !error.empty()) {
+ return cleanUp(error);
}
}
- else
- {
- LOGINFO("%s is already available", BRIDGE_NAME)
+ else {
+ spdlog::info(BRIDGE_NAME + " is already available");
}
- LOGTRACE("ip link set piscsi0 up")
+ spdlog::trace(">ip link set piscsi0 up");
- if (!ip_link(ip_fd, "piscsi0", true)) {
- close(m_hTAP);
- close(ip_fd);
- close(br_socket_fd);
- return false;
+ if (const string error = ip_link(ip_fd, "piscsi0", true); !error.empty()) {
+ return cleanUp(error);
}
- LOGTRACE("brctl addif %s piscsi0", BRIDGE_NAME)
+ spdlog::trace(">brctl addif " + BRIDGE_NAME + " piscsi0");
- if (!br_setif(br_socket_fd, BRIDGE_NAME, "piscsi0", true)) {
- close(m_hTAP);
- close(ip_fd);
- close(br_socket_fd);
- return false;
+ if (const string error = br_setif(br_socket_fd, BRIDGE_NAME, "piscsi0", true); !error.empty()) {
+ return cleanUp(error);
}
- // Get MAC address
- LOGTRACE("Getting the MAC address")
+ spdlog::trace("Getting the MAC address");
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(m_hTAP, SIOCGIFHWADDR, &ifr) < 0) {
- LOGERROR("Can't ioctl SIOCGIFHWADDR: %s", strerror(errno))
-
- close(m_hTAP);
- close(ip_fd);
- close(br_socket_fd);
- return false;
+ return cleanUp("Can't ioctl SIOCGIFHWADDR");
}
- LOGTRACE("Got the MAC")
// Save MAC address
memcpy(m_MacAddr.data(), ifr.ifr_hwaddr.sa_data, m_MacAddr.size());
@@ -353,50 +188,132 @@ bool CTapDriver::Init(const unordered_map& const_params)
close(ip_fd);
close(br_socket_fd);
- LOGINFO("Tap device %s created", ifr.ifr_name)
+ spdlog::info("Tap device " + string(ifr.ifr_name) + " created");
return true;
#endif
}
-void CTapDriver::OpenDump(const string& path) {
- if (m_pcap == nullptr) {
- m_pcap = pcap_open_dead(DLT_EN10MB, 65535);
- }
- if (m_pcap_dumper != nullptr) {
- pcap_dump_close(m_pcap_dumper);
- }
- m_pcap_dumper = pcap_dump_open(m_pcap, path.c_str());
- if (m_pcap_dumper == nullptr) {
- LOGERROR("Can't open pcap file: %s", pcap_geterr(m_pcap))
- throw io_exception("Can't open pcap file");
- }
+void CTapDriver::CleanUp() const
+{
+ if (m_hTAP != -1) {
+ if (const int br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0); br_socket_fd < 0) {
+ LogErrno("Can't open bridge socket");
+ } else {
+ spdlog::trace(">brctl delif " + BRIDGE_NAME + " piscsi0");
+ if (const string error = br_setif(br_socket_fd, BRIDGE_NAME, "piscsi0", false); !error.empty()) {
+ spdlog::warn("Warning: Removing piscsi0 from the bridge failed: " + error);
+ spdlog::warn("You may need to manually remove the piscsi0 tap device from the bridge");
+ }
+ close(br_socket_fd);
+ }
- LOGTRACE("%s Opened %s for dumping", __PRETTY_FUNCTION__, path.c_str())
+ // Release TAP device
+ close(m_hTAP);
+ }
}
-bool CTapDriver::Enable() const
+param_map CTapDriver::GetDefaultParams() const
+{
+ return {
+ { "interface", Join(GetNetworkInterfaces(), ",") },
+ { "inet", DEFAULT_IP }
+ };
+}
+
+pair CTapDriver::ExtractAddressAndMask(const string& s)
+{
+ string address = s;
+ string netmask = "255.255.255.0"; //NOSONAR This hardcoded IP address is safe
+ if (const auto& components = Split(s, '/', 2); components.size() == 2) {
+ address = components[0];
+
+ int m;
+ if (!GetAsUnsignedInt(components[1], m) || m < 8 || m > 32) {
+ spdlog::error("Invalid CIDR netmask notation '" + components[1] + "'");
+ return { "", "" };
+ }
+
+ // long long is required for compatibility with 32 bit platforms
+ const auto mask = (long long)(pow(2, 32) - (1 << (32 - m)));
+ netmask = to_string((mask >> 24) & 0xff) + '.' + to_string((mask >> 16) & 0xff) + '.' +
+ to_string((mask >> 8) & 0xff) + '.' + to_string(mask & 0xff);
+ }
+
+ return { address, netmask };
+}
+
+string CTapDriver::SetUpEth0(int socket_fd, const string& bridge_interface)
+{
+#ifdef __linux__
+ spdlog::trace(">brctl addbr " + BRIDGE_NAME);
+
+ if (ioctl(socket_fd, SIOCBRADDBR, BRIDGE_NAME.c_str()) < 0) {
+ return "Can't ioctl SIOCBRADDBR";
+ }
+
+ spdlog::trace(">brctl addif " + BRIDGE_NAME + " " + bridge_interface);
+
+ if (const string error = br_setif(socket_fd, BRIDGE_NAME, bridge_interface, true); !error.empty()) {
+ return error;
+ }
+#endif
+
+ return "";
+}
+
+string CTapDriver::SetUpNonEth0(int socket_fd, int ip_fd, const string& s)
+{
+#ifdef __linux__
+ const auto [address, netmask] = ExtractAddressAndMask(s);
+ if (address.empty() || netmask.empty()) {
+ return "Error extracting inet address and netmask";
+ }
+
+ spdlog::trace(">brctl addbr " + BRIDGE_NAME);
+
+ if (ioctl(socket_fd, SIOCBRADDBR, BRIDGE_NAME.c_str()) < 0) {
+ return "Can't ioctl SIOCBRADDBR";
+ }
+
+ ifreq ifr_a;
+ ifr_a.ifr_addr.sa_family = AF_INET;
+ strncpy(ifr_a.ifr_name, BRIDGE_NAME.c_str(), IFNAMSIZ - 1); //NOSONAR Using strncpy is safe
+ if (auto addr = (sockaddr_in*)&ifr_a.ifr_addr;
+ inet_pton(AF_INET, address.c_str(), &addr->sin_addr) != 1) {
+ return "Can't convert '" + address + "' into a network address";
+ }
+
+ ifreq ifr_n;
+ ifr_n.ifr_addr.sa_family = AF_INET;
+ strncpy(ifr_n.ifr_name, BRIDGE_NAME.c_str(), IFNAMSIZ - 1); //NOSONAR Using strncpy is safe
+ if (auto mask = (sockaddr_in*)&ifr_n.ifr_addr;
+ inet_pton(AF_INET, netmask.c_str(), &mask->sin_addr) != 1) {
+ return "Can't convert '" + netmask + "' into a netmask";
+ }
+
+ spdlog::trace(">ip address add " + s + " dev " + BRIDGE_NAME);
+
+ if (ioctl(ip_fd, SIOCSIFADDR, &ifr_a) < 0 || ioctl(ip_fd, SIOCSIFNETMASK, &ifr_n) < 0) {
+ return "Can't ioctl SIOCSIFADDR or SIOCSIFNETMASK";
+ }
+#endif
+
+ return "";
+}
+
+string CTapDriver::IpLink(bool enable) const
{
const int fd = socket(PF_INET, SOCK_DGRAM, 0);
- LOGDEBUG("%s: ip link set piscsi0 up", __PRETTY_FUNCTION__)
- const bool result = ip_link(fd, "piscsi0", true);
+ spdlog::trace(string(">ip link set piscsi0 ") + (enable ? "up" : "down"));
+ const string result = ip_link(fd, "piscsi0", enable);
close(fd);
return result;
}
-bool CTapDriver::Disable() const
+void CTapDriver::Flush() const
{
- const int fd = socket(PF_INET, SOCK_DGRAM, 0);
- LOGDEBUG("%s: ip link set piscsi0 down", __PRETTY_FUNCTION__)
- const bool result = ip_link(fd, "piscsi0", false);
- close(fd);
- return result;
-}
-
-void CTapDriver::Flush()
-{
- LOGTRACE("%s", __PRETTY_FUNCTION__)
- while (PendingPackets()) {
+ while (HasPendingPackets()) {
array m_garbage_buffer;
(void)Receive(m_garbage_buffer.data());
}
@@ -409,12 +326,7 @@ void CTapDriver::GetMacAddr(uint8_t *mac) const
memcpy(mac, m_MacAddr.data(), m_MacAddr.size());
}
-//---------------------------------------------------------------------------
-//
-// Receive
-//
-//---------------------------------------------------------------------------
-bool CTapDriver::PendingPackets() const
+bool CTapDriver::HasPendingPackets() const
{
assert(m_hTAP != -1);
@@ -424,20 +336,16 @@ bool CTapDriver::PendingPackets() const
fds.events = POLLIN | POLLERR;
fds.revents = 0;
poll(&fds, 1, 0);
- LOGTRACE("%s %u revents", __PRETTY_FUNCTION__, fds.revents)
- if (!(fds.revents & POLLIN)) {
- return false;
- } else {
- return true;
- }
+ spdlog::trace(to_string(fds.revents) + " revents");
+ return fds.revents & POLLIN;
}
// See https://stackoverflow.com/questions/21001659/crc32-algorithm-implementation-in-c-without-a-look-up-table-and-with-a-public-li
-uint32_t CTapDriver::Crc32(const uint8_t *buf, int length) {
+uint32_t CTapDriver::Crc32(span data) {
uint32_t crc = 0xffffffff;
- for (int i = 0; i < length; i++) {
- crc ^= buf[i];
- for (int j = 0; j < 8; j++) {
+ for (const auto d: data) {
+ crc ^= d;
+ for (int i = 0; i < 8; i++) {
const uint32_t mask = -(static_cast(crc) & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask);
}
@@ -445,19 +353,19 @@ uint32_t CTapDriver::Crc32(const uint8_t *buf, int length) {
return ~crc;
}
-int CTapDriver::Receive(uint8_t *buf)
+int CTapDriver::Receive(uint8_t *buf) const
{
assert(m_hTAP != -1);
// Check if there is data that can be received
- if (!PendingPackets()) {
+ if (!HasPendingPackets()) {
return 0;
}
// Receive
auto dwReceived = static_cast(read(m_hTAP, buf, ETH_FRAME_LEN));
if (dwReceived == static_cast(-1)) {
- LOGWARN("%s Error occured while receiving a packet", __PRETTY_FUNCTION__)
+ spdlog::warn("Error occured while receiving a packet");
return 0;
}
@@ -466,49 +374,28 @@ int CTapDriver::Receive(uint8_t *buf)
// We need to add the Frame Check Status (FCS) CRC back onto the end of the packet.
// The Linux network subsystem removes it, since most software apps shouldn't ever
// need it.
- const int crc = Crc32(buf, dwReceived);
+ const int crc = Crc32(span(buf, dwReceived));
buf[dwReceived + 0] = (uint8_t)((crc >> 0) & 0xFF);
buf[dwReceived + 1] = (uint8_t)((crc >> 8) & 0xFF);
buf[dwReceived + 2] = (uint8_t)((crc >> 16) & 0xFF);
buf[dwReceived + 3] = (uint8_t)((crc >> 24) & 0xFF);
- LOGDEBUG("%s CRC is %08X - %02X %02X %02X %02X\n", __PRETTY_FUNCTION__, crc, buf[dwReceived+0], buf[dwReceived+1], buf[dwReceived+2], buf[dwReceived+3])
+ spdlog::trace("CRC is " + to_string(crc) + " - " + to_string(buf[dwReceived+0]) + " " + to_string(buf[dwReceived+1]) +
+ " " + to_string(buf[dwReceived+2]) + " " + to_string(buf[dwReceived+3]));
// Add FCS size to the received message size
dwReceived += 4;
}
- if (m_pcap_dumper != nullptr) {
- pcap_pkthdr h = {
- .ts = {},
- .caplen = dwReceived,
- .len = dwReceived
- };
- gettimeofday(&h.ts, nullptr);
- pcap_dump((u_char*)m_pcap_dumper, &h, buf);
- LOGTRACE("%s Dumped %d byte packet (first byte: %02x last byte: %02x)", __PRETTY_FUNCTION__, (unsigned int)dwReceived, buf[0], buf[dwReceived-1])
- }
-
// Return the number of bytes
return dwReceived;
}
-int CTapDriver::Send(const uint8_t *buf, int len)
+int CTapDriver::Send(const uint8_t *buf, int len) const
{
assert(m_hTAP != -1);
- if (m_pcap_dumper != nullptr) {
- pcap_pkthdr h = {
- .ts = {},
- .caplen = (bpf_u_int32)len,
- .len = (bpf_u_int32)len,
- };
- gettimeofday(&h.ts, nullptr);
- pcap_dump((u_char*)m_pcap_dumper, &h, buf);
- LOGTRACE("%s Dumped %d byte packet (first byte: %02x last byte: %02x)", __PRETTY_FUNCTION__, (unsigned int)h.len, buf[0], buf[h.len-1])
- }
-
// Start sending
return static_cast(write(m_hTAP, buf, len));
}
diff --git a/cpp/devices/ctapdriver.h b/cpp/devices/ctapdriver.h
index bb3bc371..1e0eff68 100644
--- a/cpp/devices/ctapdriver.h
+++ b/cpp/devices/ctapdriver.h
@@ -11,46 +11,59 @@
#pragma once
-#include
-#include
+#include "devices/device.h"
#include
#include
#include
#include
+#include
+
+#ifndef ETH_FRAME_LEN
+static const int ETH_FRAME_LEN = 1514;
+#endif
+#ifndef ETH_FCS_LEN
+static const int ETH_FCS_LEN = 4;
+#endif
using namespace std;
class CTapDriver
{
- static constexpr const char *BRIDGE_NAME = "piscsi_bridge";
+ static const string BRIDGE_NAME;
+
+ const inline static string DEFAULT_IP = "10.10.20.1/24"; //NOSONAR This hardcoded IP address is safe
public:
CTapDriver() = default;
- ~CTapDriver();
+ ~CTapDriver() = default;
CTapDriver(CTapDriver&) = default;
CTapDriver& operator=(const CTapDriver&) = default;
- bool Init(const unordered_map&);
- void OpenDump(const string& path); // Capture packets
- void GetMacAddr(uint8_t *mac) const;
- int Receive(uint8_t *buf);
- int Send(const uint8_t *buf, int len);
- bool PendingPackets() const; // Check if there are IP packets available
- bool Enable() const; // Enable the piscsi0 interface
- bool Disable() const; // Disable the piscsi0 interface
- void Flush(); // Purge all of the packets that are waiting to be processed
+ bool Init(const param_map&);
+ void CleanUp() const;
- static uint32_t Crc32(const uint8_t *, int);
+ param_map GetDefaultParams() const;
+
+ void GetMacAddr(uint8_t *) const;
+ int Receive(uint8_t *) const;
+ int Send(const uint8_t *, int) const;
+ bool HasPendingPackets() const; // Check if there are IP packets available
+ string IpLink(bool) const; // Enable/Disable the piscsi0 interface
+ void Flush() const; // Purge all of the packets that are waiting to be processed
+
+ static uint32_t Crc32(span);
private:
+
+ static string SetUpEth0(int, const string&);
+ static string SetUpNonEth0(int, int, const string&);
+ static pair ExtractAddressAndMask(const string&);
+
array m_MacAddr; // MAC Address
int m_hTAP = -1; // File handle
- pcap_t *m_pcap = nullptr;
- pcap_dumper_t *m_pcap_dumper = nullptr;
-
// Prioritized comma-separated list of interfaces to create the bridge for
vector interfaces;
diff --git a/cpp/devices/device.cpp b/cpp/devices/device.cpp
index 2aedf5d9..6c764ec3 100644
--- a/cpp/devices/device.cpp
+++ b/cpp/devices/device.cpp
@@ -3,22 +3,24 @@
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
-// Copyright (C) 2021-2022 Uwe Seimet
+// Copyright (C) 2021-2023 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "shared/piscsi_version.h"
#include "device.h"
+#include
#include
#include
#include
+#include
using namespace std;
Device::Device(PbDeviceType type, int lun) : type(type), lun(lun)
{
ostringstream os;
- os << setw(2) << setfill('0') << piscsi_major_version << setw(2) << setfill('0') << piscsi_minor_version;
+ os << setfill('0') << setw(2) << piscsi_major_version << setw(2) << piscsi_minor_version;
revision = os.str();
}
@@ -39,7 +41,7 @@ void Device::SetProtected(bool b)
void Device::SetVendor(const string& v)
{
if (v.empty() || v.length() > 8) {
- throw invalid_argument("Vendor '" + v + "' must be between 1 and 8 characters");
+ throw invalid_argument("Vendor '" + v + "' must have between 1 and 8 characters");
}
vendor = v;
@@ -48,7 +50,7 @@ void Device::SetVendor(const string& v)
void Device::SetProduct(const string& p, bool force)
{
if (p.empty() || p.length() > 16) {
- throw invalid_argument("Product '" + p + "' must be between 1 and 16 characters");
+ throw invalid_argument("Product '" + p + "' must have between 1 and 16 characters");
}
// Changing vital product data is not SCSI compliant
@@ -62,7 +64,7 @@ void Device::SetProduct(const string& p, bool force)
void Device::SetRevision(const string& r)
{
if (r.empty() || r.length() > 4) {
- throw invalid_argument("Revision '" + r + "' must be between 1 and 4 characters");
+ throw invalid_argument("Revision '" + r + "' must have between 1 and 4 characters");
}
revision = r;
@@ -85,9 +87,9 @@ string Device::GetParam(const string& key) const
return it == params.end() ? "" : it->second;
}
-void Device::SetParams(const unordered_map& set_params)
+void Device::SetParams(const param_map& set_params)
{
- params = default_params;
+ params = GetDefaultParams();
// Devices with image file support implicitly support the "file" parameter
if (SupportsFile()) {
@@ -96,11 +98,11 @@ void Device::SetParams(const unordered_map& set_params)
for (const auto& [key, value] : set_params) {
// It is assumed that there are default parameters for all supported parameters
- if (params.find(key) != params.end()) {
+ if (params.contains(key)) {
params[key] = value;
}
else {
- LOGWARN("%s", string("Ignored unknown parameter '" + key + "'").c_str())
+ spdlog::warn("Ignored unknown parameter '" + key + "'");
}
}
}
diff --git a/cpp/devices/device.h b/cpp/devices/device.h
index f971bc9d..edff1d13 100644
--- a/cpp/devices/device.h
+++ b/cpp/devices/device.h
@@ -3,20 +3,26 @@
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
-// Copyright (C) 2021-2022 Uwe Seimet
+// Copyright (C) 2021-2023 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
-#include "shared/log.h"
#include "generated/piscsi_interface.pb.h"
+#include "shared/piscsi_util.h"
#include
#include
using namespace std;
using namespace piscsi_interface;
+// A combination of device ID and LUN
+using id_set = pair;
+
+// The map used for storing/passing device parameters
+using param_map = unordered_map>;
+
class Device //NOSONAR The number of fields and methods is justified, the complexity is low
{
const string DEFAULT_VENDOR = "PiSCSI";
@@ -60,10 +66,7 @@ class Device //NOSONAR The number of fields and methods is justified, the comple
string revision;
// The parameters the device was created with
- unordered_map params;
-
- // The default parameters
- unordered_map default_params;
+ param_map params;
// Sense Key and ASC
// MSB Reserved (0x00)
@@ -90,14 +93,15 @@ protected:
int GetStatusCode() const { return status_code; }
string GetParam(const string&) const;
- void SetParams(const unordered_map&);
+ void SetParams(const param_map&);
public:
virtual ~Device() = default;
PbDeviceType GetType() const { return type; }
- const char *GetTypeString() const { return PbDeviceType_Name(type).c_str(); }
+ string GetTypeString() const { return PbDeviceType_Name(type); }
+ string GetIdentifier() const { return GetTypeString() + " " + to_string(GetId()) + ":" + to_string(lun); }
bool IsReady() const { return ready; }
virtual void Reset();
@@ -131,8 +135,8 @@ public:
bool SupportsFile() const { return supports_file; }
void SupportsParams(bool b) { supports_params = b; }
void SupportsFile(bool b) { supports_file = b; }
- unordered_map GetParams() const { return params; }
- void SetDefaultParams(const unordered_map& p) { default_params = p; }
+ auto GetParams() const { return params; }
+ virtual param_map GetDefaultParams() const { return {}; }
void SetStatusCode(int s) { status_code = s; }
diff --git a/cpp/devices/device_factory.cpp b/cpp/devices/device_factory.cpp
index 321162f7..f951d645 100644
--- a/cpp/devices/device_factory.cpp
+++ b/cpp/devices/device_factory.cpp
@@ -3,11 +3,11 @@
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
-// Copyright (C) 2021-2022 Uwe Seimet
+// Copyright (C) 2021-2023 Uwe Seimet
//
//---------------------------------------------------------------------------
-#include "shared/piscsi_util.h"
+#include "shared/network_util.h"
#include "scsihd.h"
#include "scsihd_nec.h"
#include "scsimo.h"
@@ -17,15 +17,10 @@
#include "scsi_daynaport.h"
#include "host_services.h"
#include "device_factory.h"
-#include
-#include
-#include
-#include
-#include
using namespace std;
-using namespace piscsi_interface;
using namespace piscsi_util;
+using namespace network_util;
DeviceFactory::DeviceFactory()
{
@@ -34,22 +29,6 @@ DeviceFactory::DeviceFactory()
sector_sizes[SCMO] = { 512, 1024, 2048, 4096 };
sector_sizes[SCCD] = { 512, 2048};
- string network_interfaces;
- for (const auto& network_interface : GetNetworkInterfaces()) {
- if (network_interface.rfind("dummy", 0) == string::npos) {
- if (!network_interfaces.empty()) {
- network_interfaces += ",";
- }
- network_interfaces += network_interface;
- }
- }
-
- default_params[SCBR]["interface"] = network_interfaces;
- default_params[SCBR]["inet"] = DEFAULT_IP;
- default_params[SCDP]["interface"] = network_interfaces;
- default_params[SCDP]["inet"] = DEFAULT_IP;
- default_params[SCLP]["cmd"] = "lp -oraw %f";
-
extension_mapping["hd1"] = SCHD;
extension_mapping["hds"] = SCHD;
extension_mapping["hda"] = SCHD;
@@ -96,8 +75,8 @@ shared_ptr DeviceFactory::CreateDevice(PbDeviceType type, int lun
if (const string ext = GetExtensionLowerCase(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") {
device = make_shared(lun);
} else {
- device = make_shared(lun, sector_sizes.find(SCHD)->second, false,
- ext == "hd1" ? scsi_level::SCSI_1_CCS : scsi_level::SCSI_2);
+ device = make_shared(lun, sector_sizes.find(type)->second, false,
+ ext == "hd1" ? scsi_level::scsi_1_ccs : scsi_level::scsi_2);
// Some Apple tools require a particular drive identification
if (ext == "hda") {
@@ -109,18 +88,18 @@ shared_ptr DeviceFactory::CreateDevice(PbDeviceType type, int lun
}
case SCRM:
- device = make_shared(lun, sector_sizes.find(SCRM)->second, true);
+ device = make_shared(lun, sector_sizes.find(type)->second, true);
device->SetProduct("SCSI HD (REM.)");
break;
case SCMO:
- device = make_shared(lun, sector_sizes.find(SCMO)->second);
+ device = make_shared(lun, sector_sizes.find(type)->second);
device->SetProduct("SCSI MO");
break;
case SCCD:
- device = make_shared(lun, sector_sizes.find(SCCD)->second,
- GetExtensionLowerCase(filename) == "is1" ? scsi_level::SCSI_1_CCS : scsi_level::SCSI_2);
+ device = make_shared(lun, sector_sizes.find(type)->second,
+ GetExtensionLowerCase(filename) == "is1" ? scsi_level::scsi_1_ccs : scsi_level::scsi_2);
device->SetProduct("SCSI CD-ROM");
break;
@@ -128,7 +107,6 @@ shared_ptr DeviceFactory::CreateDevice(PbDeviceType type, int lun
device = make_shared(lun);
// Since this is an emulation for a specific driver the product name has to be set accordingly
device->SetProduct("RASCSI BRIDGE");
- device->SetDefaultParams(default_params.find(SCBR)->second);
break;
case SCDP:
@@ -137,7 +115,6 @@ shared_ptr DeviceFactory::CreateDevice(PbDeviceType type, int lun
device->SetVendor("Dayna");
device->SetProduct("SCSI/Link");
device->SetRevision("1.4a");
- device->SetDefaultParams(default_params.find(SCDP)->second);
break;
case SCHS:
@@ -150,7 +127,6 @@ shared_ptr DeviceFactory::CreateDevice(PbDeviceType type, int lun
case SCLP:
device = make_shared(lun);
device->SetProduct("SCSI PRINTER");
- device->SetDefaultParams(default_params.find(SCLP)->second);
break;
default:
@@ -160,47 +136,14 @@ shared_ptr DeviceFactory::CreateDevice(PbDeviceType type, int lun
return device;
}
-const unordered_set& DeviceFactory::GetSectorSizes(PbDeviceType type) const
+// TODO Move to respective device, which may require changes in the SCSI_HD/SCSIHD_NEC inheritance hierarchy
+unordered_set DeviceFactory::GetSectorSizes(PbDeviceType type) const
{
const auto& it = sector_sizes.find(type);
- return it != sector_sizes.end() ? it->second : empty_set;
-}
-
-const unordered_map& DeviceFactory::GetDefaultParams(PbDeviceType type) const
-{
- const auto& it = default_params.find(type);
- return it != default_params.end() ? it->second : empty_map;
-}
-
-vector DeviceFactory::GetNetworkInterfaces() const
-{
- vector network_interfaces;
-
-#ifdef __linux__
- ifaddrs *addrs;
- getifaddrs(&addrs);
- ifaddrs *tmp = addrs;
-
- while (tmp) {
- if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET &&
- strcmp(tmp->ifa_name, "lo") && strcmp(tmp->ifa_name, "piscsi_bridge")) {
- const int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
-
- ifreq ifr = {};
- strcpy(ifr.ifr_name, tmp->ifa_name); //NOSONAR Using strcpy is safe here
- // Only list interfaces that are up
- if (!ioctl(fd, SIOCGIFFLAGS, &ifr) && (ifr.ifr_flags & IFF_UP)) {
- network_interfaces.emplace_back(tmp->ifa_name);
- }
-
- close(fd);
- }
-
- tmp = tmp->ifa_next;
+ if (it != sector_sizes.end()) {
+ return it->second;
+ }
+ else {
+ return {};
}
-
- freeifaddrs(addrs);
-#endif
-
- return network_interfaces;
}
diff --git a/cpp/devices/device_factory.h b/cpp/devices/device_factory.h
index a2865d88..fef1c17d 100644
--- a/cpp/devices/device_factory.h
+++ b/cpp/devices/device_factory.h
@@ -3,7 +3,7 @@
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
-// Copyright (C) 2021-2022 Uwe Seimet
+// Copyright (C) 2021-2023 Uwe Seimet
//
// The DeviceFactory creates devices based on their type and the image file extension
//
@@ -11,8 +11,8 @@
#pragma once
+#include "shared/piscsi_util.h"
#include
-#include
#include
#include
#include "generated/piscsi_interface.pb.h"
@@ -24,7 +24,6 @@ class PrimaryDevice;
class DeviceFactory
{
- const inline static string DEFAULT_IP = "10.10.20.1/24"; //NOSONAR This hardcoded IP address is safe
public:
@@ -33,21 +32,14 @@ public:
shared_ptr CreateDevice(PbDeviceType, int, const string&) const;
PbDeviceType GetTypeForFile(const string&) const;
- const unordered_set& GetSectorSizes(PbDeviceType type) const;
- const unordered_map& GetDefaultParams(PbDeviceType type) const;
- vector GetNetworkInterfaces() const;
- const unordered_map& GetExtensionMapping() const { return extension_mapping; }
+ unordered_set GetSectorSizes(PbDeviceType type) const;
+ const auto& GetExtensionMapping() const { return extension_mapping; }
private:
unordered_map> sector_sizes;
- unordered_map> default_params;
+ unordered_map> extension_mapping;
- unordered_map extension_mapping;
-
- unordered_map device_mapping;
-
- unordered_set empty_set;
- unordered_map empty_map;
+ unordered_map> device_mapping;
};
diff --git a/cpp/devices/device_logger.cpp b/cpp/devices/device_logger.cpp
index 98c6d8d3..65afb129 100644
--- a/cpp/devices/device_logger.cpp
+++ b/cpp/devices/device_logger.cpp
@@ -3,63 +3,52 @@
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
-// Copyright (C) 2022 Uwe Seimet
+// Copyright (C) 2022-2023 Uwe Seimet
//
//---------------------------------------------------------------------------
-#include "shared/log.h"
#include "device_logger.h"
using namespace std;
+using namespace spdlog;
void DeviceLogger::Trace(const string& message) const
{
- if (const string m = GetLogMessage(message); !m.empty()) {
- LOGTRACE("%s", m.c_str())
- }
+ Log(level::trace, message);
}
void DeviceLogger::Debug(const string& message) const
{
- if (const string m = GetLogMessage(message); !m.empty()) {
- LOGDEBUG("%s", m.c_str())
- }
+ Log(level::debug, message);
}
void DeviceLogger::Info(const string& message) const
{
- if (const string m = GetLogMessage(message); !m.empty()) {
- LOGINFO("%s", m.c_str())
- }
+ Log(level::info, message);
}
void DeviceLogger::Warn(const string& message) const
{
- if (const string m = GetLogMessage(message); !m.empty()) {
- LOGWARN("%s", m.c_str())
- }
+ Log(level::warn, message);
}
void DeviceLogger::Error(const string& message) const
{
- if (const string m = GetLogMessage(message); !m.empty()) {
- LOGERROR("%s", m.c_str())
- }
+ Log(level::err, message);
}
-string DeviceLogger::GetLogMessage(const string& message) const
+void DeviceLogger::Log(level::level_enum level, const string& message) const
{
- if (log_device_id == -1 || (log_device_id == id && (log_device_lun == -1 || log_device_lun == lun)))
- {
+ if (!message.empty() &&
+ (log_device_id == -1 ||
+ (log_device_id == id && (log_device_lun == -1 || log_device_lun == lun)))) {
if (lun == -1) {
- return "(ID " + to_string(id) + ") - " + message;
+ log(level, "(ID " + to_string(id) + ") - " + message);
}
else {
- return "(ID:LUN " + to_string(id) + ":" + to_string(lun) + ") - " + message;
+ log(level, "(ID:LUN " + to_string(id) + ":" + to_string(lun) + ") - " + message);
}
}
-
- return "";
}
void DeviceLogger::SetIdAndLun(int i, int l)
diff --git a/cpp/devices/device_logger.h b/cpp/devices/device_logger.h
index fb5b8f7a..dc662718 100644
--- a/cpp/devices/device_logger.h
+++ b/cpp/devices/device_logger.h
@@ -3,12 +3,13 @@
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
-// Copyright (C) 2022 Uwe Seimet
+// Copyright (C) 2022-2023 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
+#include "spdlog/spdlog.h"
#include
using namespace std;
@@ -27,14 +28,13 @@ public:
void Warn(const string&) const;
void Error(const string&) const;
- string GetLogMessage(const string&) const;
-
void SetIdAndLun(int, int);
-
static void SetLogIdAndLun(int, int);
private:
+ void Log(spdlog::level::level_enum, const string&) const;
+
int id = -1;
int lun = -1;
diff --git a/cpp/devices/disk.cpp b/cpp/devices/disk.cpp
index 8649103f..bc8b53d0 100644
--- a/cpp/devices/disk.cpp
+++ b/cpp/devices/disk.cpp
@@ -23,15 +23,7 @@
using namespace scsi_defs;
using namespace scsi_command_util;
-Disk::~Disk()
-{
- // Save disk cache, only if ready
- if (IsReady() && cache != nullptr) {
- cache->Save();
- }
-}
-
-bool Disk::Init(const unordered_map& params)
+bool Disk::Init(const param_map& params)
{
StorageDevice::Init(params);
@@ -64,6 +56,13 @@ bool Disk::Init(const unordered_map& params)
return true;
}
+void Disk::CleanUp()
+{
+ FlushCache();
+
+ StorageDevice::CleanUp();
+}
+
void Disk::Dispatch(scsi_command cmd)
{
// Media changes must be reported on the next access, i.e. not only for TEST UNIT READY
@@ -72,7 +71,7 @@ void Disk::Dispatch(scsi_command cmd)
SetMediumChanged(false);
- GetController()->Error(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
+ GetController()->Error(sense_key::unit_attention, asc::not_ready_to_ready_change);
}
else {
PrimaryDevice::Dispatch(cmd);
@@ -93,7 +92,7 @@ void Disk::ResizeCache(const string& path, bool raw)
void Disk::FlushCache()
{
- if (cache != nullptr) {
+ if (cache != nullptr && IsReady()) {
cache->Save();
}
}
@@ -103,8 +102,8 @@ void Disk::FormatUnit()
CheckReady();
// FMTDATA=1 is not supported (but OK if there is no DEFECT LIST)
- if ((GetController()->GetCmd(1) & 0x10) != 0 && GetController()->GetCmd(4) != 0) {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
+ if ((GetController()->GetCmdByte(1) & 0x10) != 0 && GetController()->GetCmdByte(4) != 0) {
+ throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
}
EnterStatusPhase();
@@ -115,9 +114,9 @@ void Disk::Read(access_mode mode)
const auto& [valid, start, blocks] = CheckAndGetStartAndCount(mode);
if (valid) {
GetController()->SetBlocks(blocks);
- GetController()->SetLength(Read(GetController()->GetCmd(), GetController()->GetBuffer(), start));
+ GetController()->SetLength(Read(GetController()->GetBuffer(), start));
- GetLogger().Trace("Length is " + to_string(GetController()->GetLength()));
+ LogTrace("Length is " + to_string(GetController()->GetLength()));
// Set next block
GetController()->SetNext(start + 1);
@@ -135,7 +134,7 @@ void Disk::ReadWriteLong10() const
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
if (GetInt16(GetController()->GetCmd(), 7) != 0) {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
+ throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
}
EnterStatusPhase();
@@ -147,7 +146,7 @@ void Disk::ReadWriteLong16() const
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
if (GetInt16(GetController()->GetCmd(), 12) != 0) {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
+ throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
}
EnterStatusPhase();
@@ -156,7 +155,7 @@ void Disk::ReadWriteLong16() const
void Disk::Write(access_mode mode) const
{
if (IsProtected()) {
- throw scsi_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
+ throw scsi_exception(sense_key::data_protect, asc::write_protected);
}
const auto& [valid, start, blocks] = CheckAndGetStartAndCount(mode);
@@ -179,14 +178,14 @@ void Disk::Verify(access_mode mode)
const auto& [valid, start, blocks] = CheckAndGetStartAndCount(mode);
if (valid) {
// if BytChk=0
- if ((GetController()->GetCmd(1) & 0x02) == 0) {
+ if ((GetController()->GetCmdByte(1) & 0x02) == 0) {
Seek();
return;
}
// Test reading
GetController()->SetBlocks(blocks);
- GetController()->SetLength(Read(GetController()->GetCmd(), GetController()->GetBuffer(), start));
+ GetController()->SetLength(Read(GetController()->GetBuffer(), start));
// Set next block
GetController()->SetNext(start + 1);
@@ -200,14 +199,14 @@ void Disk::Verify(access_mode mode)
void Disk::StartStopUnit()
{
- const bool start = GetController()->GetCmd(4) & 0x01;
- const bool load = GetController()->GetCmd(4) & 0x02;
+ const bool start = GetController()->GetCmdByte(4) & 0x01;
+ const bool load = GetController()->GetCmdByte(4) & 0x02;
if (load) {
- GetLogger().Trace(start ? "Loading medium" : "Ejecting medium");
+ LogTrace(start ? "Loading medium" : "Ejecting medium");
}
else {
- GetLogger().Trace(start ? "Starting unit" : "Stopping unit");
+ LogTrace(start ? "Starting unit" : "Stopping unit");
SetStopped(!start);
}
@@ -217,12 +216,12 @@ void Disk::StartStopUnit()
if (load) {
if (IsLocked()) {
// Cannot be ejected because it is locked
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
+ throw scsi_exception(sense_key::illegal_request, asc::load_or_eject_failed);
}
// Eject
if (!Eject(false)) {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
+ throw scsi_exception(sense_key::illegal_request, asc::load_or_eject_failed);
}
}
else {
@@ -237,9 +236,9 @@ void Disk::PreventAllowMediumRemoval()
{
CheckReady();
- const bool lock = GetController()->GetCmd(4) & 0x01;
+ const bool lock = GetController()->GetCmdByte(4) & 0x01;
- GetLogger().Trace(lock ? "Locking medium" : "Unlocking medium");
+ LogTrace(lock ? "Locking medium" : "Unlocking medium");
SetLocked(lock);
@@ -274,12 +273,15 @@ bool Disk::Eject(bool force)
// The image file for this drive is not in use anymore
UnreserveFile();
+
+ sector_read_count = 0;
+ sector_write_count = 0;
}
return status;
}
-int Disk::ModeSense6(const vector& cdb, vector& buf) const
+int Disk::ModeSense6(cdb_t cdb, vector& buf) const
{
// Get length, clear buffer
const auto length = static_cast(min(buf.size(), static_cast(cdb[4])));
@@ -315,7 +317,7 @@ int Disk::ModeSense6(const vector& cdb, vector& buf) const
return size;
}
-int Disk::ModeSense10(const vector& cdb, vector& buf) const
+int Disk::ModeSense10(cdb_t cdb, vector& buf) const
{
// Get length, clear buffer
const auto length = static_cast(min(buf.size(), static_cast(GetInt16(cdb, 7))));
@@ -497,28 +499,32 @@ void Disk::AddCachePage(map>& pages, bool changeable) const
pages[8] = buf;
}
-int Disk::Read(const vector&, vector& buf, uint64_t block)
+int Disk::Read(span buf, uint64_t block)
{
assert(block < GetBlockCount());
CheckReady();
if (!cache->ReadSector(buf, static_cast(block))) {
- throw scsi_exception(sense_key::MEDIUM_ERROR, asc::READ_FAULT);
+ throw scsi_exception(sense_key::medium_error, asc::read_fault);
}
+ ++sector_read_count;
+
return GetSectorSizeInBytes();
}
-void Disk::Write(const vector&, const vector& buf, uint64_t block)
+void Disk::Write(span buf, uint64_t block)
{
assert(block < GetBlockCount());
CheckReady();
if (!cache->WriteSector(buf, static_cast(block))) {
- throw scsi_exception(sense_key::MEDIUM_ERROR, asc::WRITE_FAULT);
+ throw scsi_exception(sense_key::medium_error, asc::write_fault);
}
+
+ ++sector_write_count;
}
void Disk::Seek()
@@ -553,7 +559,7 @@ void Disk::ReadCapacity10()
CheckReady();
if (GetBlockCount() == 0) {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
+ throw scsi_exception(sense_key::illegal_request, asc::medium_not_present);
}
vector& buf = GetController()->GetBuffer();
@@ -580,7 +586,7 @@ void Disk::ReadCapacity16()
CheckReady();
if (GetBlockCount() == 0) {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
+ throw scsi_exception(sense_key::illegal_request, asc::medium_not_present);
}
vector& buf = GetController()->GetBuffer();
@@ -604,7 +610,7 @@ void Disk::ReadCapacity16()
void Disk::ReadCapacity16_ReadLong16()
{
// The service action determines the actual command
- switch (GetController()->GetCmd(1) & 0x1f) {
+ switch (GetController()->GetCmdByte(1) & 0x1f) {
case 0x10:
ReadCapacity16();
break;
@@ -614,7 +620,7 @@ void Disk::ReadCapacity16_ReadLong16()
break;
default:
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
+ throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
break;
}
}
@@ -624,9 +630,9 @@ void Disk::ValidateBlockAddress(access_mode mode) const
const uint64_t block = mode == RW16 ? GetInt64(GetController()->GetCmd(), 2) : GetInt32(GetController()->GetCmd(), 2);
if (block > GetBlockCount()) {
- GetLogger().Trace("Capacity of " + to_string(GetBlockCount()) + " block(s) exceeded: Trying to access block "
+ LogTrace("Capacity of " + to_string(GetBlockCount()) + " block(s) exceeded: Trying to access block "
+ to_string(block));
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
+ throw scsi_exception(sense_key::illegal_request, asc::lba_out_of_range);
}
}
@@ -638,7 +644,7 @@ tuple Disk::CheckAndGetStartAndCount(access_mode mode)
if (mode == RW6 || mode == SEEK6) {
start = GetInt24(GetController()->GetCmd(), 1);
- count = GetController()->GetCmd(4);
+ count = GetController()->GetCmdByte(4);
if (!count) {
count= 0x100;
}
@@ -659,13 +665,13 @@ tuple Disk::CheckAndGetStartAndCount(access_mode mode)
stringstream s;
s << "READ/WRITE/VERIFY/SEEK, start block: $" << setfill('0') << setw(8) << hex << start;
- GetLogger().Trace(s.str() + ", blocks: " + to_string(count));
+ LogTrace(s.str() + ", blocks: " + to_string(count));
// Check capacity
if (uint64_t capacity = GetBlockCount(); !capacity || start > capacity || start + count > capacity) {
- GetLogger().Trace("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ LogTrace("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ to_string(start) + ", block count " + to_string(count));
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
+ throw scsi_exception(sense_key::illegal_request, asc::lba_out_of_range);
}
// Do not process 0 blocks
@@ -689,10 +695,8 @@ uint32_t Disk::GetSectorSizeInBytes() const
void Disk::SetSectorSizeInBytes(uint32_t size_in_bytes)
{
- DeviceFactory device_factory;
- if (const auto& sizes = device_factory.GetSectorSizes(GetType());
- !sizes.empty() && sizes.find(size_in_bytes) == sizes.end()) {
- throw io_exception("Invalid sector size of " + to_string(size_in_bytes) + " byte(s)");
+ if (DeviceFactory device_factory; !device_factory.GetSectorSizes(GetType()).contains(size_in_bytes)) {
+ throw io_exception("Invalid sector size of " + to_string(size_in_bytes) + " byte(s)");
}
size_shift_count = CalculateShiftCount(size_in_bytes);
@@ -706,12 +710,43 @@ uint32_t Disk::GetConfiguredSectorSize() const
bool Disk::SetConfiguredSectorSize(const DeviceFactory& device_factory, uint32_t configured_size)
{
- if (unordered_set sizes = device_factory.GetSectorSizes(GetType());
- sizes.find(configured_size) == sizes.end()) {
+ if (!device_factory.GetSectorSizes(GetType()).contains(configured_size)) {
return false;
}
- configured_sector_size = configured_size;
+ configured_sector_size = configured_size;
return true;
}
+
+vector Disk::GetStatistics() const
+{
+ vector statistics = PrimaryDevice::GetStatistics();
+
+ // Enrich cache statistics with device information before adding them to device statistics
+ if (cache) {
+ for (auto& s : cache->GetStatistics(IsReadOnly())) {
+ s.set_id(GetId());
+ s.set_unit(GetLun());
+ statistics.push_back(s);
+ }
+ }
+
+ PbStatistics s;
+ s.set_id(GetId());
+ s.set_unit(GetLun());
+
+ s.set_category(PbStatisticsCategory::CATEGORY_INFO);
+
+ s.set_key(SECTOR_READ_COUNT);
+ s.set_value(sector_read_count);
+ statistics.push_back(s);
+
+ if (!IsReadOnly()) {
+ s.set_key(SECTOR_WRITE_COUNT);
+ s.set_value(sector_write_count);
+ statistics.push_back(s);
+ }
+
+ return statistics;
+}
diff --git a/cpp/devices/disk.h b/cpp/devices/disk.h
index 75af2697..7364ad08 100644
--- a/cpp/devices/disk.h
+++ b/cpp/devices/disk.h
@@ -8,7 +8,6 @@
// XM6i
// Copyright (C) 2010-2015 isaki@NetBSD.org
//
-// Imported sava's Anex86/T98Next image and MO format support patch.
// Comments translated to english by akuker.
//
//---------------------------------------------------------------------------
@@ -16,12 +15,14 @@
#pragma once
#include "shared/scsi.h"
+#include "shared/piscsi_util.h"
#include "device_factory.h"
#include "disk_track.h"
#include "disk_cache.h"
#include "interfaces/scsi_block_commands.h"
#include "storage_device.h"
#include
+#include
#include
#include
#include
@@ -41,26 +42,34 @@ class Disk : public StorageDevice, private ScsiBlockCommands
// Sector size shift count (9=512, 10=1024, 11=2048, 12=4096)
uint32_t size_shift_count = 0;
+ uint64_t sector_read_count = 0;
+ uint64_t sector_write_count = 0;
+
+ inline static const string SECTOR_READ_COUNT = "sector_read_count";
+ inline static const string SECTOR_WRITE_COUNT = "sector_write_count";
+
public:
- Disk(PbDeviceType type, int lun) : StorageDevice(type, lun) {}
- ~Disk() override;
+ using StorageDevice::StorageDevice;
- bool Init(const unordered_map&) override;
+ bool Init(const param_map&) override;
+ void CleanUp() override;
void Dispatch(scsi_command) override;
bool Eject(bool) override;
- virtual void Write(const vector&, const vector&, uint64_t);
+ virtual void Write(span, uint64_t);
- virtual int Read(const vector&, vector& , uint64_t);
+ virtual int Read(span , uint64_t);
uint32_t GetSectorSizeInBytes() const;
bool IsSectorSizeConfigurable() const { return !sector_sizes.empty(); }
bool SetConfiguredSectorSize(const DeviceFactory&, uint32_t);
void FlushCache() override;
+ vector GetStatistics() const override;
+
private:
// Commands covered by the SCSI specifications (see https://www.t10.org/drafts.htm)
@@ -92,8 +101,8 @@ private:
void ValidateBlockAddress(access_mode) const;
tuple CheckAndGetStartAndCount(access_mode) const;
- int ModeSense6(const vector&, vector&) const override;
- int ModeSense10(const vector&, vector&) const override;
+ int ModeSense6(cdb_t, vector&) const override;
+ int ModeSense10(cdb_t, vector