mirror of https://github.com/akuker/RASCSI.git
commit
796869dde6
|
@ -1 +1 @@
|
|||
* @akuker @erichelgeson @rdmark
|
||||
* @akuker @rdmark
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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 <a href="https://datasift.github.io/gitflow/IntroducingGitFlow.html">Gitflow Workflow</a>. 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 <year>.<month>.<release number> 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.
|
||||
|
|
107
cpp/Makefile
107
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)
|
||||
|
|
|
@ -3,16 +3,22 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include "devices/primary_device.h"
|
||||
#include "abstract_controller.h"
|
||||
#include <ranges>
|
||||
|
||||
using namespace scsi_defs;
|
||||
|
||||
AbstractController::AbstractController(BUS& bus, int target_id, int max_luns) : bus(bus), target_id(target_id), max_luns(max_luns)
|
||||
{
|
||||
device_logger.SetIdAndLun(target_id, -1);
|
||||
}
|
||||
|
||||
void AbstractController::AllocateCmd(size_t size)
|
||||
{
|
||||
if (size > ctrl.cmd.size()) {
|
||||
|
@ -40,9 +46,8 @@ unordered_set<shared_ptr<PrimaryDevice>> AbstractController::GetDevices() const
|
|||
{
|
||||
unordered_set<shared_ptr<PrimaryDevice>> devices;
|
||||
|
||||
for (const auto& [id, lun] : luns) {
|
||||
devices.insert(lun);
|
||||
}
|
||||
// "luns | views:values" is not supported by the bullseye compiler
|
||||
ranges::transform(luns, inserter(devices, devices.begin()), [] (const auto& l) { return l.second; } );
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
@ -56,100 +61,66 @@ void AbstractController::Reset()
|
|||
{
|
||||
SetPhase(phase_t::busfree);
|
||||
|
||||
ctrl.status = status::GOOD;
|
||||
ctrl.message = 0x00;
|
||||
ctrl.blocks = 0;
|
||||
ctrl.next = 0;
|
||||
ctrl.offset = 0;
|
||||
ctrl.length = 0;
|
||||
ctrl = {};
|
||||
|
||||
SetByteTransfer(false);
|
||||
|
||||
// Reset all LUNs
|
||||
for (const auto& [lun, device] : luns) {
|
||||
for (const auto& [_, device] : luns) {
|
||||
device->Reset();
|
||||
}
|
||||
|
||||
GetBus().Reset();
|
||||
}
|
||||
|
||||
void AbstractController::ProcessPhase()
|
||||
void AbstractController::ProcessOnController(int id_data)
|
||||
{
|
||||
switch (GetPhase()) {
|
||||
case phase_t::busfree:
|
||||
BusFree();
|
||||
break;
|
||||
device_logger.SetIdAndLun(GetTargetId(), -1);
|
||||
|
||||
case phase_t::selection:
|
||||
Selection();
|
||||
break;
|
||||
const int initiator_id = ExtractInitiatorId(id_data);
|
||||
if (initiator_id != UNKNOWN_INITIATOR_ID) {
|
||||
LogTrace("++++ Starting processing for initiator ID " + to_string(initiator_id));
|
||||
}
|
||||
else {
|
||||
LogTrace("++++ Starting processing for unknown initiator ID");
|
||||
}
|
||||
|
||||
case phase_t::dataout:
|
||||
DataOut();
|
||||
break;
|
||||
|
||||
case phase_t::datain:
|
||||
DataIn();
|
||||
break;
|
||||
|
||||
case phase_t::command:
|
||||
Command();
|
||||
break;
|
||||
|
||||
case phase_t::status:
|
||||
Status();
|
||||
break;
|
||||
|
||||
case phase_t::msgout:
|
||||
MsgOut();
|
||||
break;
|
||||
|
||||
case phase_t::msgin:
|
||||
MsgIn();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw scsi_exception(sense_key::ABORTED_COMMAND);
|
||||
break;
|
||||
while (Process(initiator_id)) {
|
||||
// Handle bus phases until the bus is free for the next command
|
||||
}
|
||||
}
|
||||
|
||||
bool AbstractController::AddDevice(shared_ptr<PrimaryDevice> device)
|
||||
{
|
||||
if (device->GetLun() < 0 || device->GetLun() >= GetMaxLuns() || HasDeviceForLun(device->GetLun())) {
|
||||
const int lun = device->GetLun();
|
||||
|
||||
if (lun < 0 || lun >= GetMaxLuns() || HasDeviceForLun(lun) || device->GetController()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
luns[device->GetLun()] = device;
|
||||
device->SetController(shared_from_this());
|
||||
luns[lun] = device;
|
||||
device->SetController(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractController::RemoveDevice(shared_ptr<PrimaryDevice> device)
|
||||
bool AbstractController::RemoveDevice(PrimaryDevice& device)
|
||||
{
|
||||
device->SetController(nullptr);
|
||||
device.CleanUp();
|
||||
|
||||
return luns.erase(device->GetLun()) == 1;
|
||||
return luns.erase(device.GetLun()) == 1;
|
||||
}
|
||||
|
||||
bool AbstractController::HasDeviceForLun(int lun) const
|
||||
{
|
||||
return luns.find(lun) != luns.end();
|
||||
return luns.contains(lun);
|
||||
}
|
||||
|
||||
int AbstractController::ExtractInitiatorId(int id_data) const
|
||||
{
|
||||
int initiator_id = UNKNOWN_INITIATOR_ID;
|
||||
|
||||
if (int tmp = id_data - (1 << target_id); tmp) {
|
||||
initiator_id = 0;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
tmp >>= 1;
|
||||
if (tmp) {
|
||||
initiator_id++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (const int id_data_without_target = id_data - (1 << target_id); id_data_without_target) {
|
||||
return static_cast<int>(log2(id_data_without_target & -id_data_without_target));
|
||||
}
|
||||
|
||||
return initiator_id;
|
||||
return UNKNOWN_INITIATOR_ID;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// Base class for device controllers
|
||||
//
|
||||
|
@ -14,17 +14,19 @@
|
|||
#include "shared/scsi.h"
|
||||
#include "hal/bus.h"
|
||||
#include "phase_handler.h"
|
||||
#include "controller_manager.h"
|
||||
#include "devices/device_logger.h"
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class PrimaryDevice;
|
||||
|
||||
class AbstractController : public PhaseHandler, public enable_shared_from_this<AbstractController>
|
||||
class AbstractController : public PhaseHandler
|
||||
{
|
||||
public:
|
||||
|
||||
|
@ -37,19 +39,19 @@ public:
|
|||
RESTART_PI
|
||||
};
|
||||
|
||||
AbstractController(shared_ptr<ControllerManager> controller_manager, int target_id, int max_luns)
|
||||
: controller_manager(controller_manager), target_id(target_id), max_luns(max_luns) {}
|
||||
AbstractController(BUS&, int, int);
|
||||
~AbstractController() override = default;
|
||||
|
||||
virtual void Error(scsi_defs::sense_key, scsi_defs::asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
|
||||
scsi_defs::status = scsi_defs::status::CHECK_CONDITION) = 0;
|
||||
virtual void Error(scsi_defs::sense_key, scsi_defs::asc = scsi_defs::asc::no_additional_sense_information,
|
||||
scsi_defs::status = scsi_defs::status::check_condition) = 0;
|
||||
virtual void Reset();
|
||||
virtual int GetInitiatorId() const = 0;
|
||||
|
||||
// Get requested LUN based on IDENTIFY message, with LUN from the CDB as fallback
|
||||
virtual int GetEffectiveLun() const = 0;
|
||||
|
||||
virtual void ScheduleShutdown(piscsi_shutdown_mode) = 0;
|
||||
void ScheduleShutdown(piscsi_shutdown_mode mode) { shutdown_mode = mode; }
|
||||
piscsi_shutdown_mode GetShutdownMode() const { return shutdown_mode; }
|
||||
|
||||
int GetTargetId() const { return target_id; }
|
||||
int GetMaxLuns() const { return max_luns; }
|
||||
|
@ -58,52 +60,59 @@ public:
|
|||
unordered_set<shared_ptr<PrimaryDevice>> GetDevices() const;
|
||||
shared_ptr<PrimaryDevice> GetDeviceForLun(int) const;
|
||||
bool AddDevice(shared_ptr<PrimaryDevice>);
|
||||
bool RemoveDevice(shared_ptr<PrimaryDevice>);
|
||||
bool RemoveDevice(PrimaryDevice&);
|
||||
bool HasDeviceForLun(int) const;
|
||||
int ExtractInitiatorId(int) const;
|
||||
void ProcessOnController(int);
|
||||
|
||||
// TODO These should probably be extracted into a new TransferHandler class
|
||||
void AllocateBuffer(size_t);
|
||||
vector<uint8_t>& GetBuffer() { return ctrl.buffer; }
|
||||
scsi_defs::status GetStatus() const { return ctrl.status; }
|
||||
auto& GetBuffer() { return ctrl.buffer; }
|
||||
auto GetStatus() const { return ctrl.status; }
|
||||
void SetStatus(scsi_defs::status s) { ctrl.status = s; }
|
||||
uint32_t GetLength() const { return ctrl.length; }
|
||||
auto GetLength() const { return ctrl.length; }
|
||||
void SetLength(uint32_t l) { ctrl.length = l; }
|
||||
uint32_t GetBlocks() const { return ctrl.blocks; }
|
||||
bool HasBlocks() const { return ctrl.blocks; }
|
||||
void SetBlocks(uint32_t b) { ctrl.blocks = b; }
|
||||
void DecrementBlocks() { --ctrl.blocks; }
|
||||
uint64_t GetNext() const { return ctrl.next; }
|
||||
auto GetNext() const { return ctrl.next; }
|
||||
void SetNext(uint64_t n) { ctrl.next = n; }
|
||||
void IncrementNext() { ++ctrl.next; }
|
||||
int GetMessage() const { return ctrl.message; }
|
||||
void SetMessage(int m) { ctrl.message = m; }
|
||||
vector<int>& GetCmd() { return ctrl.cmd; }
|
||||
int GetCmd(int index) const { return ctrl.cmd[index]; }
|
||||
auto GetCmd() const { return ctrl.cmd; }
|
||||
int GetCmdByte(int index) const { return ctrl.cmd[index]; }
|
||||
bool IsByteTransfer() const { return is_byte_transfer; }
|
||||
void SetByteTransfer(bool);
|
||||
uint32_t GetBytesToTransfer() const { return bytes_to_transfer; }
|
||||
auto GetBytesToTransfer() const { return bytes_to_transfer; }
|
||||
void SetBytesToTransfer(uint32_t b) { bytes_to_transfer = b; }
|
||||
|
||||
protected:
|
||||
|
||||
shared_ptr<ControllerManager> GetControllerManager() const { return controller_manager.lock(); }
|
||||
inline BUS& GetBus() const { return controller_manager.lock()->GetBus(); }
|
||||
BUS& GetBus() const { return bus; }
|
||||
|
||||
scsi_defs::scsi_command GetOpcode() const { return static_cast<scsi_defs::scsi_command>(ctrl.cmd[0]); }
|
||||
auto GetOpcode() const { return static_cast<scsi_defs::scsi_command>(ctrl.cmd[0]); }
|
||||
int GetLun() const { return (ctrl.cmd[1] >> 5) & 0x07; }
|
||||
|
||||
void ProcessPhase();
|
||||
|
||||
void AllocateCmd(size_t);
|
||||
|
||||
void SetCmdByte(int index, int value) { ctrl.cmd[index] = value; }
|
||||
|
||||
// TODO These should probably be extracted into a new TransferHandler class
|
||||
bool HasValidLength() const { return ctrl.length != 0; }
|
||||
int GetOffset() const { return ctrl.offset; }
|
||||
void ResetOffset() { ctrl.offset = 0; }
|
||||
void UpdateOffsetAndLength() { ctrl.offset += ctrl.length; ctrl.length = 0; }
|
||||
|
||||
void LogTrace(const string& s) const { device_logger.Trace(s); }
|
||||
void LogDebug(const string& s) const { device_logger.Debug(s); }
|
||||
void LogInfo(const string& s) const { device_logger.Info(s); }
|
||||
void LogWarn(const string& s) const { device_logger.Warn(s); }
|
||||
void LogError(const string& s) const { device_logger.Error(s); }
|
||||
|
||||
private:
|
||||
|
||||
int ExtractInitiatorId(int) const;
|
||||
|
||||
using ctrl_t = struct _ctrl_t {
|
||||
// Command data, dynamically resized if required
|
||||
vector<int> cmd = vector<int>(16);
|
||||
|
@ -121,7 +130,9 @@ private:
|
|||
|
||||
ctrl_t ctrl = {};
|
||||
|
||||
weak_ptr<ControllerManager> controller_manager;
|
||||
BUS& bus;
|
||||
|
||||
DeviceLogger device_logger;
|
||||
|
||||
// Logical units of this controller mapped to their LUN numbers
|
||||
unordered_map<int, shared_ptr<PrimaryDevice>> luns;
|
||||
|
@ -132,4 +143,6 @@ private:
|
|||
|
||||
bool is_byte_transfer = false;
|
||||
uint32_t bytes_to_transfer = 0;
|
||||
|
||||
piscsi_shutdown_mode shutdown_mode = piscsi_shutdown_mode::NONE;
|
||||
};
|
||||
|
|
|
@ -3,29 +3,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<PrimaryDevice> device)
|
||||
shared_ptr<ScsiController> ControllerManager::CreateScsiController(BUS& bus, int id) const
|
||||
{
|
||||
auto controller = FindController(id);
|
||||
if (controller != nullptr) {
|
||||
auto controller = make_shared<ScsiController>(bus, id);
|
||||
controller->Init();
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
bool ControllerManager::AttachToController(BUS& bus, int id, shared_ptr<PrimaryDevice> device)
|
||||
{
|
||||
if (auto controller = FindController(id); controller != nullptr) {
|
||||
if (controller->HasDeviceForLun(device->GetLun())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return controller->AddDevice(device);
|
||||
}
|
||||
|
||||
// If there is no LUN yet the first LUN must be LUN 0
|
||||
if (device->GetLun() == 0) {
|
||||
controller = make_shared<ScsiController>(shared_from_this(), id);
|
||||
|
||||
if (controller->AddDevice(device)) {
|
||||
// If this is LUN 0 create a new controller
|
||||
if (!device->GetLun()) {
|
||||
if (auto controller = CreateScsiController(bus, id); controller->AddDevice(device)) {
|
||||
controllers[id] = controller;
|
||||
|
||||
return true;
|
||||
|
@ -35,20 +43,37 @@ bool ControllerManager::AttachToScsiController(int id, shared_ptr<PrimaryDevice>
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ControllerManager::DeleteController(shared_ptr<AbstractController> controller)
|
||||
bool ControllerManager::DeleteController(const AbstractController& controller)
|
||||
{
|
||||
return controllers.erase(controller->GetTargetId()) == 1;
|
||||
}
|
||||
|
||||
shared_ptr<AbstractController> ControllerManager::IdentifyController(int data) const
|
||||
{
|
||||
for (const auto& [id, controller] : controllers) {
|
||||
if (data & (1 << controller->GetTargetId())) {
|
||||
return controller;
|
||||
}
|
||||
for (const auto& device : controller.GetDevices()) {
|
||||
device->CleanUp();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return controllers.erase(controller.GetTargetId()) == 1;
|
||||
}
|
||||
|
||||
void ControllerManager::DeleteAllControllers()
|
||||
{
|
||||
unordered_set<shared_ptr<AbstractController>> values;
|
||||
ranges::transform(controllers, inserter(values, values.begin()), [] (const auto& controller) { return controller.second; } );
|
||||
|
||||
for (const auto& controller : values) {
|
||||
DeleteController(*controller);
|
||||
}
|
||||
|
||||
assert(controllers.empty());
|
||||
}
|
||||
|
||||
AbstractController::piscsi_shutdown_mode ControllerManager::ProcessOnController(int id_data) const
|
||||
{
|
||||
if (const auto& it = ranges::find_if(controllers, [&] (const auto& c) { return (id_data & (1 << c.first)); } );
|
||||
it != controllers.end()) {
|
||||
(*it).second->ProcessOnController(id_data);
|
||||
|
||||
return (*it).second->GetShutdownMode();
|
||||
}
|
||||
|
||||
return AbstractController::piscsi_shutdown_mode::NONE;
|
||||
}
|
||||
|
||||
shared_ptr<AbstractController> ControllerManager::FindController(int target_id) const
|
||||
|
@ -57,11 +82,15 @@ shared_ptr<AbstractController> ControllerManager::FindController(int target_id)
|
|||
return it == controllers.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
bool ControllerManager::HasController(int target_id) const {
|
||||
return controllers.contains(target_id);
|
||||
}
|
||||
|
||||
unordered_set<shared_ptr<PrimaryDevice>> ControllerManager::GetAllDevices() const
|
||||
{
|
||||
unordered_set<shared_ptr<PrimaryDevice>> devices;
|
||||
|
||||
for (const auto& [id, controller] : controllers) {
|
||||
for (const auto& [_, controller] : controllers) {
|
||||
const auto& d = controller->GetDevices();
|
||||
devices.insert(d.begin(), d.end());
|
||||
}
|
||||
|
@ -69,12 +98,12 @@ unordered_set<shared_ptr<PrimaryDevice>> ControllerManager::GetAllDevices() cons
|
|||
return devices;
|
||||
}
|
||||
|
||||
void ControllerManager::DeleteAllControllers()
|
||||
bool ControllerManager::HasDeviceForIdAndLun(int id, int lun) const
|
||||
{
|
||||
controllers.clear();
|
||||
return GetDeviceForIdAndLun(id, lun) != nullptr;
|
||||
}
|
||||
|
||||
shared_ptr<PrimaryDevice> ControllerManager::GetDeviceByIdAndLun(int id, int lun) const
|
||||
shared_ptr<PrimaryDevice> ControllerManager::GetDeviceForIdAndLun(int id, int lun) const
|
||||
{
|
||||
if (const auto& controller = FindController(id); controller != nullptr) {
|
||||
return controller->GetDeviceForLun(lun);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// Keeps track of and manages the controllers
|
||||
//
|
||||
|
@ -11,36 +11,41 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "hal/bus.h"
|
||||
#include "controllers/abstract_controller.h"
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <memory>
|
||||
#include "hal/bus.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class AbstractController;
|
||||
class ScsiController;
|
||||
class PrimaryDevice;
|
||||
|
||||
class ControllerManager : public enable_shared_from_this<ControllerManager>
|
||||
class ControllerManager
|
||||
{
|
||||
BUS& bus;
|
||||
|
||||
unordered_map<int, shared_ptr<AbstractController>> controllers;
|
||||
|
||||
public:
|
||||
|
||||
explicit ControllerManager(BUS& bus) : bus(bus) {}
|
||||
ControllerManager() = default;
|
||||
~ControllerManager() = default;
|
||||
|
||||
// Maximum number of controller devices
|
||||
static const int DEVICE_MAX = 8;
|
||||
|
||||
inline BUS& GetBus() const { return bus; }
|
||||
bool AttachToScsiController(int, shared_ptr<PrimaryDevice>);
|
||||
bool DeleteController(shared_ptr<AbstractController>);
|
||||
shared_ptr<AbstractController> IdentifyController(int) const;
|
||||
shared_ptr<AbstractController> FindController(int) const;
|
||||
unordered_set<shared_ptr<PrimaryDevice>> GetAllDevices() const;
|
||||
bool AttachToController(BUS&, int, shared_ptr<PrimaryDevice>);
|
||||
bool DeleteController(const AbstractController&);
|
||||
void DeleteAllControllers();
|
||||
shared_ptr<PrimaryDevice> GetDeviceByIdAndLun(int, int) const;
|
||||
AbstractController::piscsi_shutdown_mode ProcessOnController(int) const;
|
||||
shared_ptr<AbstractController> FindController(int) const;
|
||||
bool HasController(int) const;
|
||||
unordered_set<shared_ptr<PrimaryDevice>> GetAllDevices() const;
|
||||
bool HasDeviceForIdAndLun(int, int) const;
|
||||
shared_ptr<PrimaryDevice> GetDeviceForIdAndLun(int, int) const;
|
||||
|
||||
static int GetScsiIdMax() { return 8; }
|
||||
static int GetScsiLunMax() { return 32; }
|
||||
|
||||
private:
|
||||
|
||||
shared_ptr<ScsiController> CreateScsiController(BUS&, int) const;
|
||||
|
||||
// Controllers mapped to their device IDs
|
||||
unordered_map<int, shared_ptr<AbstractController>> controllers;
|
||||
};
|
||||
|
|
|
@ -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(); };
|
||||
}
|
|
@ -3,15 +3,18 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "shared/scsi.h"
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
|
||||
using namespace scsi_defs;
|
||||
using namespace scsi_defs;
|
||||
|
||||
class PhaseHandler
|
||||
{
|
||||
|
@ -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_t, function<void()>> phase_executors;
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
|
@ -16,7 +16,9 @@
|
|||
#include "shared/piscsi_exceptions.h"
|
||||
#include "hal/gpiobus.h"
|
||||
#include "hal/systimer.h"
|
||||
#include "devices/interfaces/byte_writer.h"
|
||||
#include "controllers/controller_manager.h"
|
||||
#include "devices/scsi_host_bridge.h"
|
||||
#include "devices/scsi_daynaport.h"
|
||||
#include "devices/mode_page_device.h"
|
||||
#include "devices/disk.h"
|
||||
#include "scsi_controller.h"
|
||||
|
@ -28,11 +30,8 @@
|
|||
|
||||
using namespace scsi_defs;
|
||||
|
||||
ScsiController::ScsiController(shared_ptr<ControllerManager> controller_manager, int target_id)
|
||||
: AbstractController(controller_manager, target_id, LUN_MAX)
|
||||
ScsiController::ScsiController(BUS& bus, int target_id) : AbstractController(bus, target_id, ControllerManager::GetScsiLunMax())
|
||||
{
|
||||
logger.SetIdAndLun(target_id, -1);
|
||||
|
||||
// The initial buffer size will default to either the default buffer size OR
|
||||
// the size of an Ethernet message, whichever is larger.
|
||||
AllocateBuffer(std::max(DEFAULT_BUFFER_SIZE, ETH_FRAME_LEN + 16 + ETH_FCS_LEN));
|
||||
|
@ -44,26 +43,21 @@ void ScsiController::Reset()
|
|||
|
||||
execstart = 0;
|
||||
identified_lun = -1;
|
||||
initiator_id = UNKNOWN_INITIATOR_ID;
|
||||
|
||||
scsi.atnmsg = false;
|
||||
scsi.msc = 0;
|
||||
scsi.msb = {};
|
||||
|
||||
SetByteTransfer(false);
|
||||
scsi = {};
|
||||
}
|
||||
|
||||
phase_t ScsiController::Process(int id)
|
||||
bool ScsiController::Process(int id)
|
||||
{
|
||||
GetBus().Acquire();
|
||||
|
||||
if (GetBus().GetRST()) {
|
||||
logger.Warn("RESET signal received!");
|
||||
LogWarn("RESET signal received!");
|
||||
|
||||
Reset();
|
||||
|
||||
GetBus().Reset();
|
||||
|
||||
return GetPhase();
|
||||
return false;
|
||||
}
|
||||
|
||||
initiator_id = id;
|
||||
|
@ -72,23 +66,20 @@ phase_t ScsiController::Process(int id)
|
|||
ProcessPhase();
|
||||
}
|
||||
catch(const scsi_exception&) {
|
||||
// Any exception should have been handled during the phase processing
|
||||
logger.Error("Unhandled SCSI error, resetting controller and bus and entering bus free phase");
|
||||
LogError("Unhandled SCSI error, resetting controller and bus and entering bus free phase");
|
||||
|
||||
Reset();
|
||||
GetBus().Reset();
|
||||
|
||||
BusFree();
|
||||
}
|
||||
|
||||
return GetPhase();
|
||||
return !IsBusFree();
|
||||
}
|
||||
|
||||
void ScsiController::BusFree()
|
||||
{
|
||||
if (!IsBusFree()) {
|
||||
logger.Trace("Bus free phase");
|
||||
|
||||
LogTrace("Bus Free phase");
|
||||
SetPhase(phase_t::busfree);
|
||||
|
||||
GetBus().SetREQ(false);
|
||||
|
@ -98,7 +89,7 @@ void ScsiController::BusFree()
|
|||
GetBus().SetBSY(false);
|
||||
|
||||
// Initialize status and message
|
||||
SetStatus(status::GOOD);
|
||||
SetStatus(status::good);
|
||||
SetMessage(0x00);
|
||||
|
||||
// Initialize ATN message reception status
|
||||
|
@ -108,39 +99,6 @@ void ScsiController::BusFree()
|
|||
|
||||
SetByteTransfer(false);
|
||||
|
||||
if (shutdown_mode != piscsi_shutdown_mode::NONE) {
|
||||
// Prepare the shutdown by flushing all caches
|
||||
for (const auto& device : GetControllerManager()->GetAllDevices()) {
|
||||
device->FlushCache();
|
||||
}
|
||||
}
|
||||
|
||||
// When the bus is free PiSCSI or the Pi may be shut down.
|
||||
// This code has to be executed in the bus free phase and thus has to be located in the controller.
|
||||
switch(shutdown_mode) {
|
||||
case piscsi_shutdown_mode::STOP_PISCSI:
|
||||
logger.Info("PiSCSI shutdown requested");
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
|
||||
case piscsi_shutdown_mode::STOP_PI:
|
||||
logger.Info("Raspberry Pi shutdown requested");
|
||||
if (system("init 0") == -1) {
|
||||
logger.Error("Raspberry Pi shutdown failed: " + string(strerror(errno)));
|
||||
}
|
||||
break;
|
||||
|
||||
case piscsi_shutdown_mode::RESTART_PI:
|
||||
logger.Info("Raspberry Pi restart requested");
|
||||
if (system("init 6") == -1) {
|
||||
logger.Error("Raspberry Pi restart failed: " + string(strerror(errno)));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -153,18 +111,7 @@ void ScsiController::BusFree()
|
|||
void ScsiController::Selection()
|
||||
{
|
||||
if (!IsSelection()) {
|
||||
// A different device controller was selected
|
||||
if (int id = 1 << GetTargetId(); (static_cast<int>(GetBus().GetDAT()) & id) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort if there is no LUN for this controller
|
||||
if (!GetLunCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.Trace("Selection phase");
|
||||
|
||||
LogTrace("Selection phase");
|
||||
SetPhase(phase_t::selection);
|
||||
|
||||
// Raise BSY and respond
|
||||
|
@ -174,6 +121,8 @@ void ScsiController::Selection()
|
|||
|
||||
// Selection completed
|
||||
if (!GetBus().GetSEL() && GetBus().GetBSY()) {
|
||||
LogTrace("Selection completed");
|
||||
|
||||
// Message out phase if ATN=1, otherwise command phase
|
||||
if (GetBus().GetATN()) {
|
||||
MsgOut();
|
||||
|
@ -186,8 +135,7 @@ void ScsiController::Selection()
|
|||
void ScsiController::Command()
|
||||
{
|
||||
if (!IsCommand()) {
|
||||
logger.Trace("Command phase");
|
||||
|
||||
LogTrace("Command phase");
|
||||
SetPhase(phase_t::command);
|
||||
|
||||
GetBus().SetMSG(false);
|
||||
|
@ -198,9 +146,9 @@ void ScsiController::Command()
|
|||
if (actual_count == 0) {
|
||||
stringstream s;
|
||||
s << "Received unknown command: $" << setfill('0') << setw(2) << hex << GetBuffer()[0];
|
||||
logger.Trace(s.str());
|
||||
LogTrace(s.str());
|
||||
|
||||
Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
Error(sense_key::illegal_request, asc::invalid_command_operation_code);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -210,16 +158,16 @@ void ScsiController::Command()
|
|||
if (actual_count != command_byte_count) {
|
||||
stringstream s;
|
||||
s << "Command byte count mismatch for command $" << setfill('0') << setw(2) << hex << GetBuffer()[0];
|
||||
logger.Error(s.str() + ": expected " + to_string(command_byte_count) + " bytes, received"
|
||||
LogError(s.str() + ": expected " + to_string(command_byte_count) + " bytes, received"
|
||||
+ to_string(actual_count) + " byte(s)");
|
||||
Error(sense_key::ABORTED_COMMAND);
|
||||
Error(sense_key::aborted_command);
|
||||
return;
|
||||
}
|
||||
|
||||
// Command data transfer
|
||||
AllocateCmd(command_byte_count);
|
||||
for (int i = 0; i < command_byte_count; i++) {
|
||||
GetCmd()[i] = GetBuffer()[i];
|
||||
SetCmdByte(i, GetBuffer()[i]);
|
||||
}
|
||||
|
||||
SetLength(0);
|
||||
|
@ -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<uint8_t>(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<uint8_t>(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<int>(GetStatus());
|
||||
logger.Trace(s.str());
|
||||
|
||||
s << "Status phase, status is $" << setfill('0') << setw(2) << hex << static_cast<int>(GetStatus());
|
||||
LogTrace(s.str());
|
||||
SetPhase(phase_t::status);
|
||||
|
||||
// Signal line operated by the target
|
||||
|
@ -329,8 +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<int>(sense_key)
|
||||
<< ", ASC $" << static_cast<int>(asc);
|
||||
logger.Debug(s.str());
|
||||
s << setfill('0') << hex << "Error status: Sense Key $" << setw(2) << static_cast<int>(sense_key)
|
||||
<< ", ASC $" << setw(2) << static_cast<int>(asc);
|
||||
LogDebug(s.str());
|
||||
|
||||
// Set Sense Key and ASC for a subsequent REQUEST SENSE
|
||||
GetDeviceForLun(lun)->SetStatusCode((static_cast<int>(sense_key) << 16) | (static_cast<int>(asc) << 8));
|
||||
|
@ -481,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<int>(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<ModePageDevice>(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<int>(GetOpcode());
|
||||
logger.Warn(s.str());
|
||||
LogWarn(s.str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -807,9 +727,9 @@ bool ScsiController::XferIn(vector<uint8_t>& buf)
|
|||
|
||||
stringstream s;
|
||||
s << "Command: $" << setfill('0') << setw(2) << hex << static_cast<int>(GetOpcode());
|
||||
logger.Trace(s.str());
|
||||
LogTrace(s.str());
|
||||
|
||||
int lun = GetEffectiveLun();
|
||||
const int lun = GetEffectiveLun();
|
||||
if (!HasDeviceForLun(lun)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -821,7 +741,7 @@ bool ScsiController::XferIn(vector<uint8_t>& buf)
|
|||
case scsi_command::eCmdRead16:
|
||||
// Read from disk
|
||||
try {
|
||||
SetLength(dynamic_pointer_cast<Disk>(GetDeviceForLun(lun))->Read(GetCmd(), buf, GetNext()));
|
||||
SetLength(dynamic_pointer_cast<Disk>(GetDeviceForLun(lun))->Read(buf, GetNext()));
|
||||
}
|
||||
catch(const scsi_exception&) {
|
||||
// If there is an error, go to the status phase
|
||||
|
@ -878,13 +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<ByteWriter>(device); byte_writer) {
|
||||
if (!byte_writer->WriteBytes(GetCmd(), GetBuffer(), GetLength())) {
|
||||
// TODO Get rid of this special case for SCBR
|
||||
if (auto bridge = dynamic_pointer_cast<SCSIBR>(device); bridge) {
|
||||
if (!bridge->ReadWrite(GetCmd(), GetBuffer())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResetOffset();
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO Get rid of this special case for SCDP
|
||||
if (auto daynaport = dynamic_pointer_cast<SCSIDaynaPort>(device); daynaport) {
|
||||
if (!daynaport->Write(GetCmd(), GetBuffer())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -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<Disk>(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<int>(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<int>(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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "shared/scsi.h"
|
||||
#include "controller_manager.h"
|
||||
#include "devices/device_logger.h"
|
||||
#include "abstract_controller.h"
|
||||
#include <array>
|
||||
|
||||
|
@ -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<ControllerManager>, 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;
|
||||
};
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/log.h"
|
||||
#include "cfilesystem.h"
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <iconv.h>
|
||||
#include <utime.h>
|
||||
|
@ -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;
|
||||
|
|
|
@ -13,6 +13,15 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using TCHAR = char;
|
||||
|
||||
static const int FILEPATH_MAX = 260;
|
||||
|
|
|
@ -9,126 +9,71 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/log.h"
|
||||
#include "shared/piscsi_util.h"
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include "shared/network_util.h"
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "ctapdriver.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/epoll.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include <linux/sockios.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace 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<string, string>& const_params)
|
||||
bool CTapDriver::Init(const param_map& const_params)
|
||||
{
|
||||
#ifndef __linux__
|
||||
return false;
|
||||
#else
|
||||
unordered_map<string, string> 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<string, string>& 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<string, string>& 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<string, string>& 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<string, string> 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<uint8_t, ETH_FRAME_LEN> 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<const uint8_t> 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<int>(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<uint32_t>(read(m_hTAP, buf, ETH_FRAME_LEN));
|
||||
if (dwReceived == static_cast<uint32_t>(-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<int>(write(m_hTAP, buf, len));
|
||||
}
|
||||
|
|
|
@ -11,46 +11,59 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <pcap/pcap.h>
|
||||
#include <net/ethernet.h>
|
||||
#include "devices/device.h"
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <span>
|
||||
|
||||
#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<string, string>&);
|
||||
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<const uint8_t>);
|
||||
|
||||
private:
|
||||
|
||||
static string SetUpEth0(int, const string&);
|
||||
static string SetUpNonEth0(int, int, const string&);
|
||||
static pair<string, string> ExtractAddressAndMask(const string&);
|
||||
|
||||
array<byte, 6> m_MacAddr; // MAC Address
|
||||
|
||||
int m_hTAP = -1; // File handle
|
||||
|
||||
pcap_t *m_pcap = nullptr;
|
||||
pcap_dumper_t *m_pcap_dumper = nullptr;
|
||||
|
||||
// Prioritized comma-separated list of interfaces to create the bridge for
|
||||
vector<string> interfaces;
|
||||
|
||||
|
|
|
@ -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 <spdlog/spdlog.h>
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
|
||||
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<string, string>& 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<string, string>& 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 + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <unordered_map>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace piscsi_interface;
|
||||
|
||||
// A combination of device ID and LUN
|
||||
using id_set = pair<int, int>;
|
||||
|
||||
// The map used for storing/passing device parameters
|
||||
using param_map = unordered_map<string, string, piscsi_util::StringHash, equal_to<>>;
|
||||
|
||||
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<string, string> params;
|
||||
|
||||
// The default parameters
|
||||
unordered_map<string, string> 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<string, string>&);
|
||||
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<string, string> GetParams() const { return params; }
|
||||
void SetDefaultParams(const unordered_map<string, string>& p) { default_params = p; }
|
||||
auto GetParams() const { return params; }
|
||||
virtual param_map GetDefaultParams() const { return {}; }
|
||||
|
||||
void SetStatusCode(int s) { status_code = s; }
|
||||
|
||||
|
|
|
@ -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 <ifaddrs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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<PrimaryDevice> DeviceFactory::CreateDevice(PbDeviceType type, int lun
|
|||
if (const string ext = GetExtensionLowerCase(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") {
|
||||
device = make_shared<SCSIHD_NEC>(lun);
|
||||
} else {
|
||||
device = make_shared<SCSIHD>(lun, sector_sizes.find(SCHD)->second, false,
|
||||
ext == "hd1" ? scsi_level::SCSI_1_CCS : scsi_level::SCSI_2);
|
||||
device = make_shared<SCSIHD>(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<PrimaryDevice> DeviceFactory::CreateDevice(PbDeviceType type, int lun
|
|||
}
|
||||
|
||||
case SCRM:
|
||||
device = make_shared<SCSIHD>(lun, sector_sizes.find(SCRM)->second, true);
|
||||
device = make_shared<SCSIHD>(lun, sector_sizes.find(type)->second, true);
|
||||
device->SetProduct("SCSI HD (REM.)");
|
||||
break;
|
||||
|
||||
case SCMO:
|
||||
device = make_shared<SCSIMO>(lun, sector_sizes.find(SCMO)->second);
|
||||
device = make_shared<SCSIMO>(lun, sector_sizes.find(type)->second);
|
||||
device->SetProduct("SCSI MO");
|
||||
break;
|
||||
|
||||
case SCCD:
|
||||
device = make_shared<SCSICD>(lun, sector_sizes.find(SCCD)->second,
|
||||
GetExtensionLowerCase(filename) == "is1" ? scsi_level::SCSI_1_CCS : scsi_level::SCSI_2);
|
||||
device = make_shared<SCSICD>(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<PrimaryDevice> DeviceFactory::CreateDevice(PbDeviceType type, int lun
|
|||
device = make_shared<SCSIBR>(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<PrimaryDevice> 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<PrimaryDevice> DeviceFactory::CreateDevice(PbDeviceType type, int lun
|
|||
case SCLP:
|
||||
device = make_shared<SCSIPrinter>(lun);
|
||||
device->SetProduct("SCSI PRINTER");
|
||||
device->SetDefaultParams(default_params.find(SCLP)->second);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -160,47 +136,14 @@ shared_ptr<PrimaryDevice> DeviceFactory::CreateDevice(PbDeviceType type, int lun
|
|||
return device;
|
||||
}
|
||||
|
||||
const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(PbDeviceType type) const
|
||||
// TODO Move to respective device, which may require changes in the SCSI_HD/SCSIHD_NEC inheritance hierarchy
|
||||
unordered_set<uint32_t> DeviceFactory::GetSectorSizes(PbDeviceType type) const
|
||||
{
|
||||
const auto& it = sector_sizes.find(type);
|
||||
return it != sector_sizes.end() ? it->second : empty_set;
|
||||
}
|
||||
|
||||
const unordered_map<string, string>& DeviceFactory::GetDefaultParams(PbDeviceType type) const
|
||||
{
|
||||
const auto& it = default_params.find(type);
|
||||
return it != default_params.end() ? it->second : empty_map;
|
||||
}
|
||||
|
||||
vector<string> DeviceFactory::GetNetworkInterfaces() const
|
||||
{
|
||||
vector<string> 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;
|
||||
}
|
||||
|
|
|
@ -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 <string>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#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<PrimaryDevice> CreateDevice(PbDeviceType, int, const string&) const;
|
||||
PbDeviceType GetTypeForFile(const string&) const;
|
||||
const unordered_set<uint32_t>& GetSectorSizes(PbDeviceType type) const;
|
||||
const unordered_map<string, string>& GetDefaultParams(PbDeviceType type) const;
|
||||
vector<string> GetNetworkInterfaces() const;
|
||||
const unordered_map<string, PbDeviceType>& GetExtensionMapping() const { return extension_mapping; }
|
||||
unordered_set<uint32_t> GetSectorSizes(PbDeviceType type) const;
|
||||
const auto& GetExtensionMapping() const { return extension_mapping; }
|
||||
|
||||
private:
|
||||
|
||||
unordered_map<PbDeviceType, unordered_set<uint32_t>> sector_sizes;
|
||||
|
||||
unordered_map<PbDeviceType, unordered_map<string, string>> default_params;
|
||||
unordered_map<string, PbDeviceType, piscsi_util::StringHash, equal_to<>> extension_mapping;
|
||||
|
||||
unordered_map<string, PbDeviceType> extension_mapping;
|
||||
|
||||
unordered_map<string, PbDeviceType> device_mapping;
|
||||
|
||||
unordered_set<uint32_t> empty_set;
|
||||
unordered_map<string, string> empty_map;
|
||||
unordered_map<string, PbDeviceType, piscsi_util::StringHash, equal_to<>> device_mapping;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 <string>
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
@ -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<string, string>& params)
|
||||
bool Disk::Init(const param_map& params)
|
||||
{
|
||||
StorageDevice::Init(params);
|
||||
|
||||
|
@ -64,6 +56,13 @@ bool Disk::Init(const unordered_map<string, string>& 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<int>& cdb, vector<uint8_t>& buf) const
|
||||
int Disk::ModeSense6(cdb_t cdb, vector<uint8_t>& buf) const
|
||||
{
|
||||
// Get length, clear buffer
|
||||
const auto length = static_cast<int>(min(buf.size(), static_cast<size_t>(cdb[4])));
|
||||
|
@ -315,7 +317,7 @@ int Disk::ModeSense6(const vector<int>& cdb, vector<uint8_t>& buf) const
|
|||
return size;
|
||||
}
|
||||
|
||||
int Disk::ModeSense10(const vector<int>& cdb, vector<uint8_t>& buf) const
|
||||
int Disk::ModeSense10(cdb_t cdb, vector<uint8_t>& buf) const
|
||||
{
|
||||
// Get length, clear buffer
|
||||
const auto length = static_cast<int>(min(buf.size(), static_cast<size_t>(GetInt16(cdb, 7))));
|
||||
|
@ -497,28 +499,32 @@ void Disk::AddCachePage(map<int, vector<byte>>& pages, bool changeable) const
|
|||
pages[8] = buf;
|
||||
}
|
||||
|
||||
int Disk::Read(const vector<int>&, vector<uint8_t>& buf, uint64_t block)
|
||||
int Disk::Read(span<uint8_t> buf, uint64_t block)
|
||||
{
|
||||
assert(block < GetBlockCount());
|
||||
|
||||
CheckReady();
|
||||
|
||||
if (!cache->ReadSector(buf, static_cast<uint32_t>(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<int>&, const vector<uint8_t>& buf, uint64_t block)
|
||||
void Disk::Write(span<const uint8_t> buf, uint64_t block)
|
||||
{
|
||||
assert(block < GetBlockCount());
|
||||
|
||||
CheckReady();
|
||||
|
||||
if (!cache->WriteSector(buf, static_cast<uint32_t>(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<uint8_t>& 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<uint8_t>& 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<bool, uint64_t, uint32_t> 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<bool, uint64_t, uint32_t> 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<uint32_t> 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<PbStatistics> Disk::GetStatistics() const
|
||||
{
|
||||
vector<PbStatistics> 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;
|
||||
}
|
||||
|
|
|
@ -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 <string>
|
||||
#include <span>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
|
@ -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<string, string>&) override;
|
||||
bool Init(const param_map&) override;
|
||||
void CleanUp() override;
|
||||
|
||||
void Dispatch(scsi_command) override;
|
||||
|
||||
bool Eject(bool) override;
|
||||
|
||||
virtual void Write(const vector<int>&, const vector<uint8_t>&, uint64_t);
|
||||
virtual void Write(span<const uint8_t>, uint64_t);
|
||||
|
||||
virtual int Read(const vector<int>&, vector<uint8_t>& , uint64_t);
|
||||
virtual int Read(span<uint8_t> , uint64_t);
|
||||
|
||||
uint32_t GetSectorSizeInBytes() const;
|
||||
bool IsSectorSizeConfigurable() const { return !sector_sizes.empty(); }
|
||||
bool SetConfiguredSectorSize(const DeviceFactory&, uint32_t);
|
||||
void FlushCache() override;
|
||||
|
||||
vector<PbStatistics> 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<bool, uint64_t, uint32_t> CheckAndGetStartAndCount(access_mode) const;
|
||||
|
||||
int ModeSense6(const vector<int>&, vector<uint8_t>&) const override;
|
||||
int ModeSense10(const vector<int>&, vector<uint8_t>&) const override;
|
||||
int ModeSense6(cdb_t, vector<uint8_t>&) const override;
|
||||
int ModeSense10(cdb_t, vector<uint8_t>&) const override;
|
||||
|
||||
static inline const unordered_map<uint32_t, uint32_t> shift_counts =
|
||||
{ { 512, 9 }, { 1024, 10 }, { 2048, 11 }, { 4096, 12 } };
|
||||
|
|
|
@ -27,11 +27,11 @@ DiskCache::DiskCache(const string& path, int size, uint32_t blocks, off_t imgoff
|
|||
assert(imgoff >= 0);
|
||||
}
|
||||
|
||||
bool DiskCache::Save() const
|
||||
bool DiskCache::Save()
|
||||
{
|
||||
// Save valid tracks
|
||||
return none_of(cache.begin(), cache.end(), [this](const cache_t& c)
|
||||
{ return c.disktrk != nullptr && !c.disktrk->Save(sec_path); });
|
||||
return ranges::none_of(cache.begin(), cache.end(), [this](const cache_t& c)
|
||||
{ return c.disktrk != nullptr && !c.disktrk->Save(sec_path, cache_miss_write_count); });
|
||||
}
|
||||
|
||||
shared_ptr<DiskTrack> DiskCache::GetTrack(uint32_t block)
|
||||
|
@ -46,7 +46,7 @@ shared_ptr<DiskTrack> DiskCache::GetTrack(uint32_t block)
|
|||
return Assign(track);
|
||||
}
|
||||
|
||||
bool DiskCache::ReadSector(vector<uint8_t>& buf, uint32_t block)
|
||||
bool DiskCache::ReadSector(span<uint8_t> buf, uint32_t block)
|
||||
{
|
||||
shared_ptr<DiskTrack> disktrk = GetTrack(block);
|
||||
if (disktrk == nullptr) {
|
||||
|
@ -57,7 +57,7 @@ bool DiskCache::ReadSector(vector<uint8_t>& buf, uint32_t block)
|
|||
return disktrk->ReadSector(buf, block & 0xff);
|
||||
}
|
||||
|
||||
bool DiskCache::WriteSector(const vector<uint8_t>& buf, uint32_t block)
|
||||
bool DiskCache::WriteSector(span<const uint8_t> buf, uint32_t block)
|
||||
{
|
||||
shared_ptr<DiskTrack> disktrk = GetTrack(block);
|
||||
if (disktrk == nullptr) {
|
||||
|
@ -120,7 +120,7 @@ shared_ptr<DiskTrack> DiskCache::Assign(int track)
|
|||
}
|
||||
|
||||
// Save this track
|
||||
if (!cache[c].disktrk->Save(sec_path)) {
|
||||
if (!cache[c].disktrk->Save(sec_path, cache_miss_write_count)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -156,17 +156,16 @@ bool DiskCache::Load(int index, int track, shared_ptr<DiskTrack> disktrk)
|
|||
sectors = 0x100;
|
||||
}
|
||||
|
||||
// Create a disk track
|
||||
if (disktrk == nullptr) {
|
||||
disktrk = make_shared<DiskTrack>();
|
||||
}
|
||||
|
||||
// Initialize disk track
|
||||
disktrk->Init(track, sec_size, sectors, cd_raw, imgoffset);
|
||||
|
||||
// Try loading
|
||||
if (!disktrk->Load(sec_path)) {
|
||||
// Failure
|
||||
if (!disktrk->Load(sec_path, cache_miss_read_count)) {
|
||||
++read_error_count;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -190,3 +189,35 @@ void DiskCache::UpdateSerialNumber()
|
|||
}
|
||||
}
|
||||
|
||||
vector<PbStatistics> DiskCache::GetStatistics(bool is_read_only) const
|
||||
{
|
||||
vector<PbStatistics> statistics;
|
||||
|
||||
PbStatistics s;
|
||||
|
||||
s.set_category(PbStatisticsCategory::CATEGORY_INFO);
|
||||
|
||||
s.set_key(CACHE_MISS_READ_COUNT);
|
||||
s.set_value(cache_miss_read_count);
|
||||
statistics.push_back(s);
|
||||
|
||||
if (!is_read_only) {
|
||||
s.set_key(CACHE_MISS_WRITE_COUNT);
|
||||
s.set_value(cache_miss_write_count);
|
||||
statistics.push_back(s);
|
||||
}
|
||||
|
||||
s.set_category(PbStatisticsCategory::CATEGORY_ERROR);
|
||||
|
||||
s.set_key(READ_ERROR_COUNT);
|
||||
s.set_value(read_error_count);
|
||||
statistics.push_back(s);
|
||||
|
||||
if (!is_read_only) {
|
||||
s.set_key(WRITE_ERROR_COUNT);
|
||||
s.set_value(write_error_count);
|
||||
statistics.push_back(s);
|
||||
}
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
|
|
@ -15,17 +15,30 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "generated/piscsi_interface.pb.h"
|
||||
#include <span>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace piscsi_interface;
|
||||
|
||||
class DiskCache
|
||||
{
|
||||
// Number of tracks to cache
|
||||
static const int CACHE_MAX = 16;
|
||||
|
||||
uint64_t read_error_count = 0;
|
||||
uint64_t write_error_count = 0;
|
||||
uint64_t cache_miss_read_count = 0;
|
||||
uint64_t cache_miss_write_count = 0;
|
||||
|
||||
inline static const string READ_ERROR_COUNT = "read_error_count";
|
||||
inline static const string WRITE_ERROR_COUNT = "write_error_count";
|
||||
inline static const string CACHE_MISS_READ_COUNT = "cache_miss_read_count";
|
||||
inline static const string CACHE_MISS_WRITE_COUNT = "cache_miss_write_count";
|
||||
|
||||
public:
|
||||
|
||||
// Internal data definition
|
||||
|
@ -39,10 +52,11 @@ public:
|
|||
|
||||
void SetRawMode(bool b) { cd_raw = b; } // CD-ROM raw mode setting
|
||||
|
||||
// Access
|
||||
bool Save() const; // Save and release all
|
||||
bool ReadSector(vector<uint8_t>&, uint32_t); // Sector Read
|
||||
bool WriteSector(const vector<uint8_t>&, uint32_t); // Sector Write
|
||||
bool Save(); // Save and release all
|
||||
bool ReadSector(span<uint8_t>, uint32_t); // Sector Read
|
||||
bool WriteSector(span<const uint8_t>, uint32_t); // Sector Write
|
||||
|
||||
vector<PbStatistics> GetStatistics(bool) const;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/log.h"
|
||||
#include "disk_track.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
|
||||
DiskTrack::~DiskTrack()
|
||||
|
@ -46,7 +48,7 @@ void DiskTrack::Init(int track, int size, int sectors, bool raw, off_t imgoff)
|
|||
dt.imgoffset = imgoff;
|
||||
}
|
||||
|
||||
bool DiskTrack::Load(const string& path)
|
||||
bool DiskTrack::Load(const string& path, uint64_t& cache_miss_read_count)
|
||||
{
|
||||
// Not needed if already loaded
|
||||
if (dt.init) {
|
||||
|
@ -54,6 +56,8 @@ bool DiskTrack::Load(const string& path)
|
|||
return true;
|
||||
}
|
||||
|
||||
++cache_miss_read_count;
|
||||
|
||||
// Calculate offset (previous tracks are considered to hold 256 sectors)
|
||||
off_t offset = ((off_t)dt.track << 8);
|
||||
if (dt.raw) {
|
||||
|
@ -75,7 +79,7 @@ bool DiskTrack::Load(const string& path)
|
|||
|
||||
if (dt.buffer == nullptr) {
|
||||
if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) {
|
||||
LOGWARN("posix_memalign failed")
|
||||
spdlog::warn("posix_memalign failed");
|
||||
}
|
||||
dt.length = length;
|
||||
}
|
||||
|
@ -88,14 +92,14 @@ bool DiskTrack::Load(const string& path)
|
|||
if (dt.length != static_cast<uint32_t>(length)) {
|
||||
free(dt.buffer);
|
||||
if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) {
|
||||
LOGWARN("posix_memalign failed")
|
||||
spdlog::warn("posix_memalign failed");
|
||||
}
|
||||
dt.length = length;
|
||||
}
|
||||
|
||||
// Resize and clear changemap
|
||||
dt.changemap.resize(dt.sectors);
|
||||
fill(dt.changemap.begin(), dt.changemap.end(), false);
|
||||
fill(dt.changemap.begin(), dt.changemap.end(), false); //NOSONAR ranges::fill() cannot be applied to vector<bool>
|
||||
|
||||
ifstream in(path, ios::binary);
|
||||
if (in.fail()) {
|
||||
|
@ -136,7 +140,7 @@ bool DiskTrack::Load(const string& path)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DiskTrack::Save(const string& path)
|
||||
bool DiskTrack::Save(const string& path, uint64_t& cache_miss_write_count)
|
||||
{
|
||||
// Not needed if not initialized
|
||||
if (!dt.init) {
|
||||
|
@ -148,6 +152,8 @@ bool DiskTrack::Save(const string& path)
|
|||
return true;
|
||||
}
|
||||
|
||||
++cache_miss_write_count;
|
||||
|
||||
// Need to write
|
||||
assert(dt.buffer);
|
||||
assert((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||||
|
@ -209,13 +215,13 @@ bool DiskTrack::Save(const string& path)
|
|||
}
|
||||
|
||||
// Drop the change flag and exit
|
||||
fill(dt.changemap.begin(), dt.changemap.end(), false);
|
||||
fill(dt.changemap.begin(), dt.changemap.end(), false); //NOSONAR ranges::fill() cannot be applied to vector<bool>
|
||||
dt.changed = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskTrack::ReadSector(vector<uint8_t>& buf, int sec) const
|
||||
bool DiskTrack::ReadSector(span<uint8_t> buf, int sec) const
|
||||
{
|
||||
assert(sec >= 0 && sec < 0x100);
|
||||
|
||||
|
@ -238,7 +244,7 @@ bool DiskTrack::ReadSector(vector<uint8_t>& buf, int sec) const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DiskTrack::WriteSector(const vector<uint8_t>& buf, int sec)
|
||||
bool DiskTrack::WriteSector(span<const uint8_t> buf, int sec)
|
||||
{
|
||||
assert((sec >= 0) && (sec < 0x100));
|
||||
assert(!dt.raw);
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
@ -48,12 +50,11 @@ private:
|
|||
friend class DiskCache;
|
||||
|
||||
void Init(int track, int size, int sectors, bool raw = false, off_t imgoff = 0);
|
||||
bool Load(const string& path);
|
||||
bool Save(const string& path);
|
||||
bool Load(const string& path, uint64_t&);
|
||||
bool Save(const string& path, uint64_t&);
|
||||
|
||||
// Read / Write
|
||||
bool ReadSector(vector<uint8_t>&, int) const; // Sector Read
|
||||
bool WriteSector(const vector<uint8_t>& buf, int); // Sector Write
|
||||
bool ReadSector(span<uint8_t>, int) const; // Sector Read
|
||||
bool WriteSector(span<const uint8_t> buf, int); // Sector Write
|
||||
|
||||
int GetTrack() const { return dt.track; } // Get track
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// Host Services with realtime clock and shutdown support
|
||||
//
|
||||
|
@ -25,11 +25,13 @@
|
|||
#include "scsi_command_util.h"
|
||||
#include "host_services.h"
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::chrono;
|
||||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
bool HostServices::Init(const unordered_map<string, string>& params)
|
||||
bool HostServices::Init(const param_map& params)
|
||||
{
|
||||
ModePageDevice::Init(params);
|
||||
|
||||
|
@ -49,13 +51,13 @@ void HostServices::TestUnitReady()
|
|||
|
||||
vector<uint8_t> HostServices::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(device_type::PROCESSOR, scsi_level::SPC_3, false);
|
||||
return HandleInquiry(device_type::processor, scsi_level::spc_3, false);
|
||||
}
|
||||
|
||||
void HostServices::StartStopUnit() const
|
||||
{
|
||||
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 (!start) {
|
||||
if (load) {
|
||||
|
@ -69,17 +71,17 @@ void HostServices::StartStopUnit() const
|
|||
GetController()->ScheduleShutdown(AbstractController::piscsi_shutdown_mode::RESTART_PI);
|
||||
}
|
||||
else {
|
||||
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();
|
||||
}
|
||||
|
||||
int HostServices::ModeSense6(const vector<int>& cdb, vector<uint8_t>& buf) const
|
||||
int HostServices::ModeSense6(cdb_t cdb, vector<uint8_t>& buf) const
|
||||
{
|
||||
// Block descriptors cannot be returned
|
||||
if (!(cdb[1] & 0x08)) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
const auto length = static_cast<int>(min(buf.size(), static_cast<size_t>(cdb[4])));
|
||||
|
@ -93,11 +95,11 @@ int HostServices::ModeSense6(const vector<int>& cdb, vector<uint8_t>& buf) const
|
|||
return size;
|
||||
}
|
||||
|
||||
int HostServices::ModeSense10(const vector<int>& cdb, vector<uint8_t>& buf) const
|
||||
int HostServices::ModeSense10(cdb_t cdb, vector<uint8_t>& buf) const
|
||||
{
|
||||
// Block descriptors cannot be returned
|
||||
if (!(cdb[1] & 0x08)) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
const auto length = static_cast<int>(min(buf.size(), static_cast<size_t>(GetInt16(cdb, 7))));
|
||||
|
@ -123,7 +125,8 @@ void HostServices::AddRealtimeClockPage(map<int, vector<byte>>& pages, bool chan
|
|||
pages[32] = vector<byte>(10);
|
||||
|
||||
if (!changeable) {
|
||||
time_t t = time(nullptr);
|
||||
const auto now = system_clock::now();
|
||||
const time_t t = system_clock::to_time_t(now);
|
||||
tm localtime;
|
||||
localtime_r(&t, &localtime);
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// Host Services with realtime clock and shutdown support
|
||||
//
|
||||
|
@ -12,6 +12,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "mode_page_device.h"
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
@ -23,7 +24,7 @@ public:
|
|||
explicit HostServices(int lun) : ModePageDevice(SCHS, lun) {}
|
||||
~HostServices() override = default;
|
||||
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
bool Init(const param_map&) override;
|
||||
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
void TestUnitReady() override;
|
||||
|
@ -48,8 +49,8 @@ private:
|
|||
};
|
||||
|
||||
void StartStopUnit() const;
|
||||
int ModeSense6(const vector<int>&, vector<uint8_t>&) const override;
|
||||
int ModeSense10(const vector<int>&, vector<uint8_t>&) const override;
|
||||
int ModeSense6(cdb_t, vector<uint8_t>&) const override;
|
||||
int ModeSense10(cdb_t, vector<uint8_t>&) const override;
|
||||
|
||||
void AddRealtimeClockPage(map<int, vector<byte>>&, bool) const;
|
||||
};
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
//
|
||||
// Abstraction for the DaynaPort and the host bridge, which both have methods for writing byte sequences
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class ByteWriter
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
ByteWriter() = default;
|
||||
virtual ~ByteWriter() = default;
|
||||
|
||||
virtual bool WriteBytes(const vector<int>&, vector<uint8_t>&, uint32_t) = 0;
|
||||
};
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// A basic device with mode page support, to be used for subclassing
|
||||
//
|
||||
|
@ -20,7 +20,7 @@ using namespace std;
|
|||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
bool ModePageDevice::Init(const unordered_map<string, string>& params)
|
||||
bool ModePageDevice::Init(const param_map& params)
|
||||
{
|
||||
PrimaryDevice::Init(params);
|
||||
|
||||
|
@ -32,7 +32,7 @@ bool ModePageDevice::Init(const unordered_map<string, string>& params)
|
|||
return true;
|
||||
}
|
||||
|
||||
int ModePageDevice::AddModePages(const vector<int>& cdb, vector<uint8_t>& buf, int offset, int length, int max_size) const
|
||||
int ModePageDevice::AddModePages(cdb_t cdb, vector<uint8_t>& buf, int offset, int length, int max_size) const
|
||||
{
|
||||
const int max_length = length - offset;
|
||||
if (max_length < 0) {
|
||||
|
@ -46,7 +46,7 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<uint8_t>& buf, i
|
|||
|
||||
stringstream s;
|
||||
s << "Requesting mode page $" << setfill('0') << setw(2) << hex << page;
|
||||
GetLogger().Trace(s.str());
|
||||
LogTrace(s.str());
|
||||
|
||||
// Mode page data mapped to the respective page numbers, C++ maps are ordered by key
|
||||
map<int, vector<byte>> pages;
|
||||
|
@ -54,8 +54,8 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<uint8_t>& buf, i
|
|||
|
||||
if (pages.empty()) {
|
||||
s << "Unsupported mode page $" << page;
|
||||
GetLogger().Trace(s.str());
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
LogTrace(s.str());
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
// Holds all mode page data
|
||||
|
@ -90,7 +90,7 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<uint8_t>& buf, i
|
|||
}
|
||||
|
||||
if (static_cast<int>(result.size()) > max_size) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
const auto size = static_cast<int>(min(static_cast<size_t>(max_length), result.size()));
|
||||
|
@ -114,15 +114,15 @@ void ModePageDevice::ModeSense10() const
|
|||
EnterDataInPhase();
|
||||
}
|
||||
|
||||
void ModePageDevice::ModeSelect(scsi_command, const vector<int>&, const vector<uint8_t>&, int) const
|
||||
void ModePageDevice::ModeSelect(scsi_command, cdb_t, span<const uint8_t>, int) const
|
||||
{
|
||||
// There is no default implementation of MDOE SELECT
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
// There is no default implementation of MODE SELECT
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_command_operation_code);
|
||||
}
|
||||
|
||||
void ModePageDevice::ModeSelect6() const
|
||||
{
|
||||
SaveParametersCheck(GetController()->GetCmd(4));
|
||||
SaveParametersCheck(GetController()->GetCmdByte(4));
|
||||
}
|
||||
|
||||
void ModePageDevice::ModeSelect10() const
|
||||
|
@ -134,8 +134,8 @@ void ModePageDevice::ModeSelect10() const
|
|||
|
||||
void ModePageDevice::SaveParametersCheck(int length) const
|
||||
{
|
||||
if (!SupportsSaveParameters() && (GetController()->GetCmd(1) & 0x01)) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
if (!SupportsSaveParameters() && (GetController()->GetCmdByte(1) & 0x01)) {
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
GetController()->SetLength(length);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -11,25 +11,25 @@
|
|||
|
||||
#include "primary_device.h"
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
// TODO Maybe this should better be a mixin class because not all storage devicess have mode pages
|
||||
class ModePageDevice : public PrimaryDevice
|
||||
{
|
||||
public:
|
||||
|
||||
using PrimaryDevice::PrimaryDevice;
|
||||
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
bool Init(const param_map&) override;
|
||||
|
||||
virtual void ModeSelect(scsi_defs::scsi_command, const vector<int>&, const vector<uint8_t>&, int) const;
|
||||
virtual void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) const;
|
||||
|
||||
protected:
|
||||
|
||||
bool SupportsSaveParameters() const { return supports_save_parameters; }
|
||||
void SupportsSaveParameters(bool b) { supports_save_parameters = b; }
|
||||
int AddModePages(const vector<int>&, vector<uint8_t>&, int, int, int) const;
|
||||
int AddModePages(cdb_t, vector<uint8_t>&, int, int, int) const;
|
||||
virtual void SetUpModePages(map<int, vector<byte>>&, int, bool) const = 0;
|
||||
virtual void AddVendorPage(map<int, vector<byte>>&, int, bool) const {
|
||||
// Nothing to add by default
|
||||
|
@ -39,8 +39,8 @@ private:
|
|||
|
||||
bool supports_save_parameters = false;
|
||||
|
||||
virtual int ModeSense6(const vector<int>&, vector<uint8_t>&) const = 0;
|
||||
virtual int ModeSense10(const vector<int>&, vector<uint8_t>&) const = 0;
|
||||
virtual int ModeSense6(cdb_t, vector<uint8_t>&) const = 0;
|
||||
virtual int ModeSense10(cdb_t, vector<uint8_t>&) const = 0;
|
||||
|
||||
void ModeSense6() const;
|
||||
void ModeSense10() const;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -17,7 +17,7 @@ using namespace std;
|
|||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
bool PrimaryDevice::Init(const unordered_map<string, string>& params)
|
||||
bool PrimaryDevice::Init(const param_map& params)
|
||||
{
|
||||
// Mandatory SCSI primary commands
|
||||
AddCommand(scsi_command::eCmdTestUnitReady, [this] { TestUnitReady(); });
|
||||
|
@ -35,9 +35,9 @@ bool PrimaryDevice::Init(const unordered_map<string, string>& params)
|
|||
return true;
|
||||
}
|
||||
|
||||
void PrimaryDevice::AddCommand(scsi_command opcode, const operation& execute)
|
||||
void PrimaryDevice::AddCommand(scsi_command cmd, const operation& execute)
|
||||
{
|
||||
commands[opcode] = execute;
|
||||
commands[cmd] = execute;
|
||||
}
|
||||
|
||||
void PrimaryDevice::Dispatch(scsi_command cmd)
|
||||
|
@ -46,15 +46,14 @@ void PrimaryDevice::Dispatch(scsi_command cmd)
|
|||
s << "$" << setfill('0') << setw(2) << hex << static_cast<int>(cmd);
|
||||
|
||||
if (const auto& it = commands.find(cmd); it != commands.end()) {
|
||||
GetLogger().Debug("Device is executing " + string(command_mapping.find(cmd)->second.second) +
|
||||
" (" + s.str() + ")");
|
||||
LogDebug("Device is executing " + command_mapping.find(cmd)->second.second + " (" + s.str() + ")");
|
||||
|
||||
it->second();
|
||||
}
|
||||
else {
|
||||
GetLogger().Trace("Received unsupported command: " + s.str());
|
||||
LogTrace("Received unsupported command: " + s.str());
|
||||
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_command_operation_code);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,18 +66,14 @@ void PrimaryDevice::Reset()
|
|||
|
||||
int PrimaryDevice::GetId() const
|
||||
{
|
||||
if (GetController() == nullptr) {
|
||||
GetLogger().Error("Device is missing its controller");
|
||||
}
|
||||
|
||||
return GetController() != nullptr ? GetController()->GetTargetId() : -1;
|
||||
}
|
||||
|
||||
void PrimaryDevice::SetController(shared_ptr<AbstractController> c)
|
||||
void PrimaryDevice::SetController(AbstractController *c)
|
||||
{
|
||||
controller = c;
|
||||
|
||||
logger.SetIdAndLun(c != nullptr ? c->GetTargetId() : -1, GetLun());
|
||||
device_logger.SetIdAndLun(GetId(), GetLun());
|
||||
}
|
||||
|
||||
void PrimaryDevice::TestUnitReady()
|
||||
|
@ -91,11 +86,11 @@ void PrimaryDevice::TestUnitReady()
|
|||
void PrimaryDevice::Inquiry()
|
||||
{
|
||||
// EVPD and page code check
|
||||
if ((GetController()->GetCmd(1) & 0x01) || GetController()->GetCmd(2)) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
if ((GetController()->GetCmdByte(1) & 0x01) || GetController()->GetCmdByte(2)) {
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
vector<uint8_t> buf = InquiryInternal();
|
||||
const vector<uint8_t> buf = InquiryInternal();
|
||||
|
||||
const size_t allocation_length = min(buf.size(), static_cast<size_t>(GetInt16(GetController()->GetCmd(), 3)));
|
||||
|
||||
|
@ -103,8 +98,8 @@ void PrimaryDevice::Inquiry()
|
|||
GetController()->SetLength(static_cast<uint32_t>(allocation_length));
|
||||
|
||||
// Report if the device does not support the requested LUN
|
||||
if (int lun = GetController()->GetEffectiveLun(); !GetController()->HasDeviceForLun(lun)) {
|
||||
GetLogger().Trace("LUN is not available");
|
||||
if (const int lun = GetController()->GetEffectiveLun(); !GetController()->HasDeviceForLun(lun)) {
|
||||
LogTrace("LUN is not available");
|
||||
|
||||
// Signal that the requested LUN does not exist
|
||||
GetController()->GetBuffer().data()[0] = 0x7f;
|
||||
|
@ -116,8 +111,8 @@ void PrimaryDevice::Inquiry()
|
|||
void PrimaryDevice::ReportLuns()
|
||||
{
|
||||
// Only SELECT REPORT mode 0 is supported
|
||||
if (GetController()->GetCmd(2)) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
if (GetController()->GetCmdByte(2)) {
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
const uint32_t allocation_length = GetInt32(GetController()->GetCmd(), 6);
|
||||
|
@ -155,14 +150,14 @@ void PrimaryDevice::RequestSense()
|
|||
lun = 0;
|
||||
|
||||
// Do not raise an exception here because the rest of the code must be executed
|
||||
GetController()->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN);
|
||||
GetController()->Error(sense_key::illegal_request, asc::invalid_lun);
|
||||
|
||||
GetController()->SetStatus(status::GOOD);
|
||||
GetController()->SetStatus(status::good);
|
||||
}
|
||||
|
||||
vector<byte> buf = GetController()->GetDeviceForLun(lun)->HandleRequestSense();
|
||||
|
||||
const size_t allocation_length = min(buf.size(), static_cast<size_t>(GetController()->GetCmd(4)));
|
||||
const size_t allocation_length = min(buf.size(), static_cast<size_t>(GetController()->GetCmdByte(4)));
|
||||
|
||||
memcpy(GetController()->GetBuffer().data(), buf.data(), allocation_length);
|
||||
GetController()->SetLength(static_cast<uint32_t>(allocation_length));
|
||||
|
@ -173,13 +168,13 @@ void PrimaryDevice::RequestSense()
|
|||
void PrimaryDevice::SendDiagnostic()
|
||||
{
|
||||
// Do not support PF bit
|
||||
if (GetController()->GetCmd(1) & 0x10) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
if (GetController()->GetCmdByte(1) & 0x10) {
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
// Do not support parameter list
|
||||
if ((GetController()->GetCmd(3) != 0) || (GetController()->GetCmd(4) != 0)) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
if ((GetController()->GetCmdByte(3) != 0) || (GetController()->GetCmdByte(4) != 0)) {
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
EnterStatusPhase();
|
||||
|
@ -190,24 +185,24 @@ void PrimaryDevice::CheckReady()
|
|||
// Not ready if reset
|
||||
if (IsReset()) {
|
||||
SetReset(false);
|
||||
GetLogger().Trace("Device in reset");
|
||||
throw scsi_exception(sense_key::UNIT_ATTENTION, asc::POWER_ON_OR_RESET);
|
||||
LogTrace("Device in reset");
|
||||
throw scsi_exception(sense_key::unit_attention, asc::power_on_or_reset);
|
||||
}
|
||||
|
||||
// Not ready if it needs attention
|
||||
if (IsAttn()) {
|
||||
SetAttn(false);
|
||||
GetLogger().Trace("Device in needs attention");
|
||||
throw scsi_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
|
||||
LogTrace("Device in needs attention");
|
||||
throw scsi_exception(sense_key::unit_attention, asc::not_ready_to_ready_change);
|
||||
}
|
||||
|
||||
// Return status if not ready
|
||||
if (!IsReady()) {
|
||||
GetLogger().Trace("Device not ready");
|
||||
throw scsi_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT);
|
||||
LogTrace("Device not ready");
|
||||
throw scsi_exception(sense_key::not_ready, asc::medium_not_present);
|
||||
}
|
||||
|
||||
GetLogger().Trace("Device is ready");
|
||||
LogTrace("Device is ready");
|
||||
}
|
||||
|
||||
vector<uint8_t> PrimaryDevice::HandleInquiry(device_type type, scsi_level level, bool is_removable) const
|
||||
|
@ -223,8 +218,8 @@ vector<uint8_t> PrimaryDevice::HandleInquiry(device_type type, scsi_level level,
|
|||
buf[0] = static_cast<uint8_t>(type);
|
||||
buf[1] = is_removable ? 0x80 : 0x00;
|
||||
buf[2] = static_cast<uint8_t>(level);
|
||||
buf[3] = level >= scsi_level::SCSI_2 ?
|
||||
static_cast<uint8_t>(scsi_level::SCSI_2) : static_cast<uint8_t>(scsi_level::SCSI_1_CCS);
|
||||
buf[3] = level >= scsi_level::scsi_2 ?
|
||||
static_cast<uint8_t>(scsi_level::scsi_2) : static_cast<uint8_t>(scsi_level::scsi_1_ccs);
|
||||
buf[4] = 0x1F;
|
||||
|
||||
// Padded vendor, product, revision
|
||||
|
@ -237,7 +232,7 @@ vector<byte> PrimaryDevice::HandleRequestSense() const
|
|||
{
|
||||
// Return not ready only if there are no errors
|
||||
if (!GetStatusCode() && !IsReady()) {
|
||||
throw scsi_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT);
|
||||
throw scsi_exception(sense_key::not_ready, asc::medium_not_present);
|
||||
}
|
||||
|
||||
// Set 18 bytes including extended sense data
|
||||
|
@ -257,14 +252,14 @@ vector<byte> PrimaryDevice::HandleRequestSense() const
|
|||
<< "Status $" << static_cast<int>(GetController()->GetStatus())
|
||||
<< ", Sense Key $" << static_cast<int>(buf[2])
|
||||
<< ", ASC $" << static_cast<int>(buf[12]);
|
||||
GetLogger().Trace(s.str());
|
||||
LogTrace(s.str());
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool PrimaryDevice::WriteByteSequence(vector<uint8_t>&, uint32_t)
|
||||
bool PrimaryDevice::WriteByteSequence(span<const uint8_t>)
|
||||
{
|
||||
GetLogger().Error("Writing bytes is not supported by this device");
|
||||
LogError("Writing bytes is not supported by this device");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -274,10 +269,10 @@ void PrimaryDevice::ReserveUnit()
|
|||
reserving_initiator = GetController()->GetInitiatorId();
|
||||
|
||||
if (reserving_initiator != -1) {
|
||||
GetLogger().Trace("Reserved device for initiator ID " + to_string(reserving_initiator));
|
||||
LogTrace("Reserved device for initiator ID " + to_string(reserving_initiator));
|
||||
}
|
||||
else {
|
||||
GetLogger().Trace("Reserved device for unknown initiator");
|
||||
LogTrace("Reserved device for unknown initiator");
|
||||
}
|
||||
|
||||
EnterStatusPhase();
|
||||
|
@ -286,10 +281,10 @@ void PrimaryDevice::ReserveUnit()
|
|||
void PrimaryDevice::ReleaseUnit()
|
||||
{
|
||||
if (reserving_initiator != -1) {
|
||||
GetLogger().Trace("Released device reserved by initiator ID " + to_string(reserving_initiator));
|
||||
LogTrace("Released device reserved by initiator ID " + to_string(reserving_initiator));
|
||||
}
|
||||
else {
|
||||
GetLogger().Trace("Released device reserved by unknown initiator");
|
||||
LogTrace("Released device reserved by unknown initiator");
|
||||
}
|
||||
|
||||
DiscardReservation();
|
||||
|
@ -313,10 +308,10 @@ bool PrimaryDevice::CheckReservation(int initiator_id, scsi_command cmd, bool pr
|
|||
}
|
||||
|
||||
if (initiator_id != -1) {
|
||||
GetLogger().Trace("Initiator ID " + to_string(initiator_id) + " tries to access reserved device");
|
||||
LogTrace("Initiator ID " + to_string(initiator_id) + " tries to access reserved device");
|
||||
}
|
||||
else {
|
||||
GetLogger().Trace("Unknown initiator tries to access reserved device");
|
||||
LogTrace("Unknown initiator tries to access reserved device");
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// A device implementing mandatory SCSI primary commands, to be used for subclassing
|
||||
//
|
||||
|
@ -18,6 +18,7 @@
|
|||
#include "device_logger.h"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <span>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
|
@ -25,6 +26,8 @@ using namespace scsi_defs;
|
|||
|
||||
class PrimaryDevice: private ScsiPrimaryCommands, public Device
|
||||
{
|
||||
friend class AbstractController;
|
||||
|
||||
using operation = function<void()>;
|
||||
|
||||
public:
|
||||
|
@ -32,15 +35,16 @@ public:
|
|||
PrimaryDevice(PbDeviceType type, int lun) : Device(type, lun) {}
|
||||
~PrimaryDevice() override = default;
|
||||
|
||||
virtual bool Init(const unordered_map<string, string>&);
|
||||
virtual bool Init(const param_map&);
|
||||
virtual void CleanUp() {
|
||||
// Override if cleanup work is required for a derived device
|
||||
};
|
||||
|
||||
virtual void Dispatch(scsi_command);
|
||||
|
||||
int GetId() const override;
|
||||
|
||||
void SetController(shared_ptr<AbstractController>);
|
||||
|
||||
virtual bool WriteByteSequence(vector<uint8_t>&, uint32_t);
|
||||
virtual bool WriteByteSequence(span<const uint8_t>);
|
||||
|
||||
int GetSendDelay() const { return send_delay; }
|
||||
|
||||
|
@ -50,15 +54,19 @@ public:
|
|||
void Reset() override;
|
||||
|
||||
virtual void FlushCache() {
|
||||
// Devices with a cache have to implement this method
|
||||
// Devices with a cache have to override this method
|
||||
}
|
||||
|
||||
virtual vector<PbStatistics> GetStatistics() const {
|
||||
// Devices which provide statistics have to override this method
|
||||
|
||||
return vector<PbStatistics>();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void AddCommand(scsi_command, const operation&);
|
||||
|
||||
const DeviceLogger& GetLogger() const { return logger; }
|
||||
|
||||
vector<uint8_t> HandleInquiry(scsi_defs::device_type, scsi_level, bool) const;
|
||||
virtual vector<uint8_t> InquiryInternal() const = 0;
|
||||
void CheckReady();
|
||||
|
@ -69,16 +77,24 @@ protected:
|
|||
void ReserveUnit() override;
|
||||
void ReleaseUnit() override;
|
||||
|
||||
void EnterStatusPhase() const { controller.lock()->Status(); }
|
||||
void EnterDataInPhase() const { controller.lock()->DataIn(); }
|
||||
void EnterDataOutPhase() const { controller.lock()->DataOut(); }
|
||||
void EnterStatusPhase() const { controller->Status(); }
|
||||
void EnterDataInPhase() const { controller->DataIn(); }
|
||||
void EnterDataOutPhase() const { controller->DataOut(); }
|
||||
|
||||
inline shared_ptr<AbstractController> GetController() const { return controller.lock(); }
|
||||
auto GetController() const { return controller; }
|
||||
|
||||
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:
|
||||
|
||||
static const int NOT_RESERVED = -2;
|
||||
|
||||
void SetController(AbstractController *);
|
||||
|
||||
void TestUnitReady() override;
|
||||
void RequestSense() override;
|
||||
void ReportLuns() override;
|
||||
|
@ -86,9 +102,11 @@ private:
|
|||
|
||||
vector<byte> HandleRequestSense() const;
|
||||
|
||||
DeviceLogger logger;
|
||||
// TODO Try to remove this field and use controller->Log*() methods instead
|
||||
DeviceLogger device_logger;
|
||||
|
||||
weak_ptr<AbstractController> controller;
|
||||
// Owned by the controller manager
|
||||
AbstractController *controller = nullptr;
|
||||
|
||||
unordered_map<scsi_command, operation> commands;
|
||||
|
||||
|
|
|
@ -3,31 +3,31 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include "device_logger.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace scsi_defs;
|
||||
|
||||
void scsi_command_util::ModeSelect(const DeviceLogger& logger, scsi_command cmd, const vector<int>& cdb,
|
||||
const vector<uint8_t>& buf, int length, int sector_size)
|
||||
string scsi_command_util::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length, int sector_size)
|
||||
{
|
||||
assert(cmd == scsi_command::eCmdModeSelect6 || cmd == scsi_command::eCmdModeSelect10);
|
||||
assert(length >= 0);
|
||||
|
||||
string result;
|
||||
|
||||
// PF
|
||||
if (!(cdb[1] & 0x10)) {
|
||||
// Vendor-specific parameters (SCSI-1) are not supported.
|
||||
// Do not report an error in order to support Apple's HD SC Setup.
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Skip block descriptors
|
||||
|
@ -45,9 +45,9 @@ void scsi_command_util::ModeSelect(const DeviceLogger& logger, scsi_command cmd,
|
|||
// Parse the pages
|
||||
while (length > 0) {
|
||||
// Format device page
|
||||
if (int page = buf[offset]; page == 0x03) {
|
||||
if (const int page = buf[offset]; page == 0x03) {
|
||||
if (length < 14) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_parameter_list);
|
||||
}
|
||||
|
||||
// With this page the sector size for a subsequent FORMAT can be selected, but only very few
|
||||
|
@ -56,8 +56,8 @@ void scsi_command_util::ModeSelect(const DeviceLogger& logger, scsi_command cmd,
|
|||
if (GetInt16(buf, offset + 12) != sector_size) {
|
||||
// With piscsi it is not possible to permanently (by formatting) change the sector size,
|
||||
// because the size is an externally configurable setting only
|
||||
logger.Warn("In order to change the sector size use the -b option when launching piscsi");
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
spdlog::warn("In order to change the sector size use the -b option when launching piscsi");
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_parameter_list);
|
||||
}
|
||||
|
||||
has_valid_page_code = true;
|
||||
|
@ -65,7 +65,7 @@ void scsi_command_util::ModeSelect(const DeviceLogger& logger, scsi_command cmd,
|
|||
else {
|
||||
stringstream s;
|
||||
s << "Unknown MODE SELECT page code: $" << setfill('0') << setw(2) << hex << page;
|
||||
logger.Warn(s.str());
|
||||
result = s.str();
|
||||
}
|
||||
|
||||
// Advance to the next page
|
||||
|
@ -76,8 +76,10 @@ void scsi_command_util::ModeSelect(const DeviceLogger& logger, scsi_command cmd,
|
|||
}
|
||||
|
||||
if (!has_valid_page_code) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_parameter_list);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void scsi_command_util::EnrichFormatPage(map<int, vector<byte>>& pages, bool changeable, int sector_size)
|
||||
|
@ -97,33 +99,19 @@ void scsi_command_util::AddAppleVendorModePage(map<int, vector<byte>>& pages, bo
|
|||
|
||||
// No changeable area
|
||||
if (!changeable) {
|
||||
const char APPLE_DATA[] = "APPLE COMPUTER, INC ";
|
||||
constexpr const char APPLE_DATA[] = "APPLE COMPUTER, INC ";
|
||||
memcpy(&pages[48].data()[2], APPLE_DATA, sizeof(APPLE_DATA));
|
||||
}
|
||||
}
|
||||
|
||||
int scsi_command_util::GetInt16(const vector<uint8_t>& buf, int offset)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 1);
|
||||
|
||||
return (static_cast<int>(buf[offset]) << 8) | buf[offset + 1];
|
||||
}
|
||||
|
||||
int scsi_command_util::GetInt16(const vector<int>& buf, int offset)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 1);
|
||||
|
||||
return (buf[offset] << 8) | buf[offset + 1];
|
||||
}
|
||||
|
||||
int scsi_command_util::GetInt24(const vector<int>& buf, int offset)
|
||||
int scsi_command_util::GetInt24(span <const int> buf, int offset)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 2);
|
||||
|
||||
return (buf[offset] << 16) | (buf[offset + 1] << 8) | buf[offset + 2];
|
||||
}
|
||||
|
||||
uint32_t scsi_command_util::GetInt32(const vector<int>& buf, int offset)
|
||||
uint32_t scsi_command_util::GetInt32(span <const int> buf, int offset)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 3);
|
||||
|
||||
|
@ -131,7 +119,7 @@ uint32_t scsi_command_util::GetInt32(const vector<int>& buf, int offset)
|
|||
(static_cast<uint32_t>(buf[offset + 2]) << 8) | static_cast<uint32_t>(buf[offset + 3]);
|
||||
}
|
||||
|
||||
uint64_t scsi_command_util::GetInt64(const vector<int>& buf, int offset)
|
||||
uint64_t scsi_command_util::GetInt64(span<const int> buf, int offset)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 7);
|
||||
|
||||
|
@ -141,42 +129,6 @@ uint64_t scsi_command_util::GetInt64(const vector<int>& buf, int offset)
|
|||
(static_cast<uint64_t>(buf[offset + 6]) << 8) | static_cast<uint64_t>(buf[offset + 7]);
|
||||
}
|
||||
|
||||
void scsi_command_util::SetInt16(vector<byte>& buf, int offset, int value)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 1);
|
||||
|
||||
buf[offset] = static_cast<byte>(value >> 8);
|
||||
buf[offset + 1] = static_cast<byte>(value);
|
||||
}
|
||||
|
||||
void scsi_command_util::SetInt32(vector<byte>& buf, int offset, uint32_t value)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 3);
|
||||
|
||||
buf[offset] = static_cast<byte>(value >> 24);
|
||||
buf[offset + 1] = static_cast<byte>(value >> 16);
|
||||
buf[offset + 2] = static_cast<byte>(value >> 8);
|
||||
buf[offset + 3] = static_cast<byte>(value);
|
||||
}
|
||||
|
||||
void scsi_command_util::SetInt16(vector<uint8_t>& buf, int offset, int value)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 1);
|
||||
|
||||
buf[offset] = static_cast<uint8_t>(value >> 8);
|
||||
buf[offset + 1] = static_cast<uint8_t>(value);
|
||||
}
|
||||
|
||||
void scsi_command_util::SetInt32(vector<uint8_t>& buf, int offset, uint32_t value)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 3);
|
||||
|
||||
buf[offset] = static_cast<uint8_t>(value >> 24);
|
||||
buf[offset + 1] = static_cast<uint8_t>(value >> 16);
|
||||
buf[offset + 2] = static_cast<uint8_t>(value >> 8);
|
||||
buf[offset + 3] = static_cast<uint8_t>(value);
|
||||
}
|
||||
|
||||
void scsi_command_util::SetInt64(vector<uint8_t>& buf, int offset, uint64_t value)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 7);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// Shared code for SCSI command implementations
|
||||
//
|
||||
|
@ -12,27 +12,49 @@
|
|||
#pragma once
|
||||
|
||||
#include "shared/scsi.h"
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class DeviceLogger;
|
||||
|
||||
namespace scsi_command_util
|
||||
{
|
||||
void ModeSelect(const DeviceLogger&, scsi_defs::scsi_command, const vector<int>&, const vector<uint8_t>&, int, int);
|
||||
string ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int, int);
|
||||
void EnrichFormatPage(map<int, vector<byte>>&, bool, int);
|
||||
void AddAppleVendorModePage(map<int, vector<byte>>&, bool);
|
||||
|
||||
int GetInt16(const vector<uint8_t>&, int);
|
||||
int GetInt16(const vector<int>&, int);
|
||||
int GetInt24(const vector<int>&, int);
|
||||
uint32_t GetInt32(const vector<int>&, int);
|
||||
uint64_t GetInt64(const vector<int>&, int);
|
||||
void SetInt16(vector<byte>&, int, int);
|
||||
void SetInt32(vector<byte>&, int, uint32_t);
|
||||
void SetInt16(vector<uint8_t>&, int, int);
|
||||
void SetInt32(vector<uint8_t>&, int, uint32_t);
|
||||
int GetInt16(const auto buf, int offset)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 1);
|
||||
|
||||
return (static_cast<int>(buf[offset]) << 8) | buf[offset + 1];
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void SetInt16(vector<T>& buf, int offset, int value)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 1);
|
||||
|
||||
buf[offset] = static_cast<T>(value >> 8);
|
||||
buf[offset + 1] = static_cast<T>(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SetInt32(vector<T>& buf, int offset, uint32_t value)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 3);
|
||||
|
||||
buf[offset] = static_cast<T>(value >> 24);
|
||||
buf[offset + 1] = static_cast<T>(value >> 16);
|
||||
buf[offset + 2] = static_cast<T>(value >> 8);
|
||||
buf[offset + 3] = static_cast<T>(value);
|
||||
}
|
||||
|
||||
int GetInt24(span<const int>, int);
|
||||
uint32_t GetInt32(span <const int>, int);
|
||||
uint64_t GetInt64(span<const int>, int);
|
||||
void SetInt64(vector<uint8_t>&, int, uint64_t);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ SCSIDaynaPort::SCSIDaynaPort(int lun) : PrimaryDevice(SCDP, lun)
|
|||
SupportsParams(true);
|
||||
}
|
||||
|
||||
bool SCSIDaynaPort::Init(const unordered_map<string, string>& params)
|
||||
bool SCSIDaynaPort::Init(const param_map& params)
|
||||
{
|
||||
PrimaryDevice::Init(params);
|
||||
|
||||
|
@ -54,16 +54,14 @@ bool SCSIDaynaPort::Init(const unordered_map<string, string>& params)
|
|||
// In the MacOS driver, it looks like the driver is doing two "READ" system calls.
|
||||
SetSendDelay(DAYNAPORT_READ_HEADER_SZ);
|
||||
|
||||
m_bTapEnable = m_tap.Init(GetParams());
|
||||
if(!m_bTapEnable){
|
||||
GetLogger().Error("Unable to open the TAP interface");
|
||||
|
||||
tap_enabled = tap.Init(GetParams());
|
||||
if (!tap_enabled) {
|
||||
// Not terminating on regular Linux PCs is helpful for testing
|
||||
#if !defined(__x86_64__) && !defined(__X86__)
|
||||
return false;
|
||||
return false;
|
||||
#endif
|
||||
} else {
|
||||
GetLogger().Trace("Tap interface created");
|
||||
LogTrace("Tap interface created");
|
||||
}
|
||||
|
||||
Reset();
|
||||
|
@ -73,9 +71,14 @@ bool SCSIDaynaPort::Init(const unordered_map<string, string>& params)
|
|||
return true;
|
||||
}
|
||||
|
||||
void SCSIDaynaPort::CleanUp()
|
||||
{
|
||||
tap.CleanUp();
|
||||
}
|
||||
|
||||
vector<uint8_t> SCSIDaynaPort::InquiryInternal() const
|
||||
{
|
||||
vector<uint8_t> buf = HandleInquiry(device_type::PROCESSOR, scsi_level::SCSI_2, false);
|
||||
vector<uint8_t> buf = HandleInquiry(device_type::processor, scsi_level::scsi_2, false);
|
||||
|
||||
// The Daynaport driver for the Mac expects 37 bytes: Increase additional length and
|
||||
// add a vendor-specific byte in order to satisfy this driver.
|
||||
|
@ -116,14 +119,14 @@ vector<uint8_t> SCSIDaynaPort::InquiryInternal() const
|
|||
// - The SCSI/Link apparently has about 6KB buffer space for packets.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIDaynaPort::Read(const vector<int>& cdb, vector<uint8_t>& buf, uint64_t)
|
||||
int SCSIDaynaPort::Read(cdb_t cdb, vector<uint8_t>& buf, uint64_t)
|
||||
{
|
||||
int rx_packet_size = 0;
|
||||
const auto response = (scsi_resp_read_t*)buf.data();
|
||||
|
||||
const int requested_length = cdb[4];
|
||||
|
||||
GetLogger().Trace("Read maximum length: " + to_string(requested_length));
|
||||
LogTrace("Read maximum length: " + to_string(requested_length));
|
||||
|
||||
// At startup the host may send a READ(6) command with a sector count of 1 to read the root sector.
|
||||
// We should respond by going into the status mode with a code of 0x02.
|
||||
|
@ -142,17 +145,19 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<uint8_t>& buf, uint64_t)
|
|||
// The first 2 bytes are reserved for the length of the packet
|
||||
// The next 4 bytes are reserved for a flag field
|
||||
//rx_packet_size = m_tap.Rx(response->data);
|
||||
rx_packet_size = m_tap.Receive(&buf[DAYNAPORT_READ_HEADER_SZ]);
|
||||
rx_packet_size = tap.Receive(&buf[DAYNAPORT_READ_HEADER_SZ]);
|
||||
|
||||
// If we didn't receive anything, return size of 0
|
||||
if (rx_packet_size <= 0) {
|
||||
GetLogger().Trace("No packet received");
|
||||
LogTrace("No packet received");
|
||||
response->length = 0;
|
||||
response->flags = read_data_flags_t::e_no_more_data;
|
||||
return DAYNAPORT_READ_HEADER_SZ;
|
||||
}
|
||||
|
||||
GetLogger().Trace("Packet Size " + to_string(rx_packet_size) + ", read count: " + to_string(read_count));
|
||||
byte_read_count += rx_packet_size;
|
||||
|
||||
LogTrace("Packet Size " + to_string(rx_packet_size) + ", read count: " + to_string(read_count));
|
||||
|
||||
// This is a very basic filter to prevent unnecessary packets from
|
||||
// being sent to the SCSI initiator.
|
||||
|
@ -187,11 +192,11 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<uint8_t>& buf, uint64_t)
|
|||
for (int i = 0 ; i < 6; i++) {
|
||||
s << " $" << static_cast<int>(response->data[i]);
|
||||
}
|
||||
GetLogger().Debug(s.str());
|
||||
LogDebug(s.str());
|
||||
|
||||
// If there are pending packets to be processed, we'll tell the host that the read
|
||||
// length was 0.
|
||||
if (!m_tap.PendingPackets()) {
|
||||
if (!tap.HasPendingPackets()) {
|
||||
response->length = 0;
|
||||
response->flags = read_data_flags_t::e_no_more_data;
|
||||
return DAYNAPORT_READ_HEADER_SZ;
|
||||
|
@ -216,7 +221,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<uint8_t>& buf, uint64_t)
|
|||
size = 64;
|
||||
}
|
||||
SetInt16(buf, 0, size);
|
||||
SetInt32(buf, 2, m_tap.PendingPackets() ? 0x10 : 0x00);
|
||||
SetInt32(buf, 2, tap.HasPendingPackets() ? 0x10 : 0x00);
|
||||
|
||||
// Return the packet size + 2 for the length + 4 for the flag field
|
||||
// The CRC was already appended by the ctapdriver
|
||||
|
@ -249,25 +254,25 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<uint8_t>& buf, uint64_t)
|
|||
// XX XX ... is the actual packet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool SCSIDaynaPort::WriteBytes(const vector<int>& cdb, vector<uint8_t>& buf, uint32_t)
|
||||
bool SCSIDaynaPort::Write(cdb_t cdb, span<const uint8_t> buf)
|
||||
{
|
||||
const int data_format = cdb[5];
|
||||
int data_length = GetInt16(cdb, 3);
|
||||
|
||||
if (data_format == 0x00) {
|
||||
m_tap.Send(buf.data(), data_length);
|
||||
GetLogger().Trace("Transmitted " + to_string(data_length) + " byte(s) (00 format)");
|
||||
if (const int data_format = cdb[5]; data_format == 0x00) {
|
||||
const int data_length = GetInt16(cdb, 3);
|
||||
tap.Send(buf.data(), data_length);
|
||||
byte_write_count += data_length;
|
||||
LogTrace("Transmitted " + to_string(data_length) + " byte(s) (00 format)");
|
||||
}
|
||||
else if (data_format == 0x80) {
|
||||
// The data length is specified in the first 2 bytes of the payload
|
||||
data_length = buf[1] + ((static_cast<int>(buf[0]) & 0xff) << 8);
|
||||
m_tap.Send(&(buf.data()[4]), data_length);
|
||||
GetLogger().Trace("Transmitted " + to_string(data_length) + "byte(s) (80 format)");
|
||||
const int data_length = buf[1] + ((static_cast<int>(buf[0]) & 0xff) << 8);
|
||||
tap.Send(&(buf.data()[4]), data_length);
|
||||
byte_write_count += data_length;
|
||||
LogTrace("Transmitted " + to_string(data_length) + "byte(s) (80 format)");
|
||||
}
|
||||
else {
|
||||
stringstream s;
|
||||
s << "Unknown data format: " << setfill('0') << setw(2) << hex << data_format;
|
||||
GetLogger().Warn(s.str());
|
||||
LogWarn(s.str());
|
||||
}
|
||||
|
||||
GetController()->SetBlocks(0);
|
||||
|
@ -291,7 +296,7 @@ bool SCSIDaynaPort::WriteBytes(const vector<int>& cdb, vector<uint8_t>& buf, uin
|
|||
// - long #3: frames lost
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIDaynaPort::RetrieveStats(const vector<int>& cdb, vector<uint8_t>& buf) const
|
||||
int SCSIDaynaPort::RetrieveStats(cdb_t cdb, vector<uint8_t>& buf) const
|
||||
{
|
||||
memcpy(buf.data(), &m_scsi_link_stats, sizeof(m_scsi_link_stats));
|
||||
|
||||
|
@ -312,17 +317,17 @@ void SCSIDaynaPort::Read6()
|
|||
|
||||
// If any commands have a bogus control value, they were probably not
|
||||
// generated by the DaynaPort driver so ignore them
|
||||
if (GetController()->GetCmd(5) != 0xc0 && GetController()->GetCmd(5) != 0x80) {
|
||||
GetLogger().Trace("Control value: " + to_string(GetController()->GetCmd(5)));
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
if (GetController()->GetCmdByte(5) != 0xc0 && GetController()->GetCmdByte(5) != 0x80) {
|
||||
LogTrace("Control value: " + to_string(GetController()->GetCmdByte(5)));
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
stringstream s;
|
||||
s << "READ(6) command, record: $" << setfill('0') << setw(8) << hex << record;
|
||||
GetLogger().Trace(s.str() + ", blocks: " + to_string(GetController()->GetBlocks()));
|
||||
LogTrace(s.str());
|
||||
|
||||
GetController()->SetLength(Read(GetController()->GetCmd(), GetController()->GetBuffer(), record));
|
||||
GetLogger().Trace("Length is " + to_string(GetController()->GetLength()));
|
||||
LogTrace("Length is " + to_string(GetController()->GetLength()));
|
||||
|
||||
// Set next block
|
||||
GetController()->SetNext(record + 1);
|
||||
|
@ -335,7 +340,7 @@ void SCSIDaynaPort::Write6() const
|
|||
// Ensure a sufficient buffer size (because it is not transfer for each block)
|
||||
GetController()->AllocateBuffer(DAYNAPORT_BUFFER_SIZE);
|
||||
|
||||
const int data_format = GetController()->GetCmd(5);
|
||||
const int data_format = GetController()->GetCmdByte(5);
|
||||
|
||||
if (data_format == 0x00) {
|
||||
GetController()->SetLength(GetInt16(GetController()->GetCmd(), 3));
|
||||
|
@ -346,15 +351,15 @@ void SCSIDaynaPort::Write6() const
|
|||
else {
|
||||
stringstream s;
|
||||
s << "Unknown data format: " << setfill('0') << setw(2) << hex << data_format;
|
||||
GetLogger().Warn(s.str());
|
||||
LogWarn(s.str());
|
||||
}
|
||||
|
||||
stringstream s;
|
||||
s << "Length: " << GetController()->GetLength() << ", format: $" << setfill('0') << setw(2) << hex << data_format;
|
||||
GetLogger().Trace(s.str());
|
||||
LogTrace(s.str());
|
||||
|
||||
if (GetController()->GetLength() <= 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);
|
||||
}
|
||||
|
||||
// Set next block
|
||||
|
@ -406,7 +411,7 @@ void SCSIDaynaPort::SetInterfaceMode() const
|
|||
// Check whether this command is telling us to "Set Interface Mode" or "Set MAC Address"
|
||||
|
||||
GetController()->SetLength(RetrieveStats(GetController()->GetCmd(), GetController()->GetBuffer()));
|
||||
switch(GetController()->GetCmd(5)){
|
||||
switch(GetController()->GetCmdByte(5)){
|
||||
case CMD_SCSILINK_SETMODE:
|
||||
// Not implemented, do nothing
|
||||
EnterStatusPhase();
|
||||
|
@ -419,21 +424,21 @@ void SCSIDaynaPort::SetInterfaceMode() const
|
|||
|
||||
default:
|
||||
stringstream s;
|
||||
s << "Unsupported SetInterface command: " << setfill('0') << setw(2) << hex << GetController()->GetCmd(5);
|
||||
GetLogger().Warn(s.str());
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
s << "Unsupported SetInterface command: " << setfill('0') << setw(2) << hex << GetController()->GetCmdByte(5);
|
||||
LogWarn(s.str());
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_command_operation_code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SCSIDaynaPort::SetMcastAddr() const
|
||||
{
|
||||
GetController()->SetLength(GetController()->GetCmd(4));
|
||||
GetController()->SetLength(GetController()->GetCmdByte(4));
|
||||
if (GetController()->GetLength() == 0) {
|
||||
stringstream s;
|
||||
s << "Unsupported SetMcastAddr command: " << setfill('0') << setw(2) << hex << GetController()->GetCmd(2);
|
||||
GetLogger().Warn(s.str());
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
s << "Unsupported SetMcastAddr command: " << setfill('0') << setw(2) << hex << GetController()->GetCmdByte(2);
|
||||
LogWarn(s.str());
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
EnterDataOutPhase();
|
||||
|
@ -451,29 +456,49 @@ void SCSIDaynaPort::SetMcastAddr() const
|
|||
// seconds
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIDaynaPort::EnableInterface()
|
||||
void SCSIDaynaPort::EnableInterface() const
|
||||
{
|
||||
if (GetController()->GetCmd(5) & 0x80) {
|
||||
if (!m_tap.Enable()) {
|
||||
GetLogger().Warn("Unable to enable the DaynaPort Interface");
|
||||
if (GetController()->GetCmdByte(5) & 0x80) {
|
||||
if (const string error = tap.IpLink(true); !error.empty()) {
|
||||
LogWarn("Unable to enable the DaynaPort Interface: " + error);
|
||||
|
||||
throw scsi_exception(sense_key::ABORTED_COMMAND);
|
||||
throw scsi_exception(sense_key::aborted_command);
|
||||
}
|
||||
|
||||
m_tap.Flush();
|
||||
tap.Flush();
|
||||
|
||||
GetLogger().Info("The DaynaPort interface has been ENABLED");
|
||||
LogInfo("The DaynaPort interface has been ENABLED");
|
||||
}
|
||||
else {
|
||||
if (!m_tap.Disable()) {
|
||||
GetLogger().Warn("Unable to disable the DaynaPort Interface");
|
||||
if (const string error = tap.IpLink(false); !error.empty()) {
|
||||
LogWarn("Unable to disable the DaynaPort Interface: " + error);
|
||||
|
||||
throw scsi_exception(sense_key::ABORTED_COMMAND);
|
||||
throw scsi_exception(sense_key::aborted_command);
|
||||
}
|
||||
|
||||
GetLogger().Info("The DaynaPort interface has been DISABLED");
|
||||
LogInfo("The DaynaPort interface has been DISABLED");
|
||||
}
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
vector<PbStatistics> SCSIDaynaPort::GetStatistics() const
|
||||
{
|
||||
vector<PbStatistics> statistics = PrimaryDevice::GetStatistics();
|
||||
|
||||
PbStatistics s;
|
||||
s.set_id(GetId());
|
||||
s.set_unit(GetLun());
|
||||
|
||||
s.set_category(PbStatisticsCategory::CATEGORY_INFO);
|
||||
|
||||
s.set_key(BYTE_READ_COUNT);
|
||||
s.set_value(byte_read_count);
|
||||
statistics.push_back(s);
|
||||
|
||||
s.set_key(BYTE_WRITE_COUNT);
|
||||
s.set_value(byte_write_count);
|
||||
statistics.push_back(s);
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
|
|
@ -29,10 +29,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "interfaces/byte_writer.h"
|
||||
#include "primary_device.h"
|
||||
#include "ctapdriver.h"
|
||||
#include <net/ethernet.h>
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <array>
|
||||
|
||||
|
@ -41,21 +42,30 @@
|
|||
// DaynaPort SCSI Link
|
||||
//
|
||||
//===========================================================================
|
||||
class SCSIDaynaPort : public PrimaryDevice, public ByteWriter
|
||||
class SCSIDaynaPort : public PrimaryDevice
|
||||
{
|
||||
uint64_t byte_read_count = 0;
|
||||
uint64_t byte_write_count = 0;
|
||||
|
||||
inline static const string BYTE_READ_COUNT = "byte_read_count";
|
||||
inline static const string BYTE_WRITE_COUNT = "byte_write_count";
|
||||
|
||||
public:
|
||||
|
||||
explicit SCSIDaynaPort(int);
|
||||
~SCSIDaynaPort() override = default;
|
||||
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
bool Init(const param_map&) override;
|
||||
void CleanUp() override;
|
||||
|
||||
param_map GetDefaultParams() const override { return tap.GetDefaultParams(); }
|
||||
|
||||
// Commands
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
int Read(const vector<int>&, vector<uint8_t>&, uint64_t);
|
||||
bool WriteBytes(const vector<int>&, vector<uint8_t>&, uint32_t) override;
|
||||
int Read(cdb_t, vector<uint8_t>&, uint64_t);
|
||||
bool Write(cdb_t, span<const uint8_t>);
|
||||
|
||||
int RetrieveStats(const vector<int>&, vector<uint8_t>&) const;
|
||||
int RetrieveStats(cdb_t, vector<uint8_t>&) const;
|
||||
|
||||
void TestUnitReady() override;
|
||||
void Read6();
|
||||
|
@ -63,7 +73,9 @@ public:
|
|||
void RetrieveStatistics() const;
|
||||
void SetInterfaceMode() const;
|
||||
void SetMcastAddr() const;
|
||||
void EnableInterface();
|
||||
void EnableInterface() const;
|
||||
|
||||
vector<PbStatistics> GetStatistics() const override;
|
||||
|
||||
static const int DAYNAPORT_BUFFER_SIZE = 0x1000000;
|
||||
|
||||
|
@ -114,8 +126,7 @@ private:
|
|||
.frames_lost = 0,
|
||||
};
|
||||
|
||||
CTapDriver m_tap;
|
||||
CTapDriver tap;
|
||||
|
||||
// TAP valid flag
|
||||
bool m_bTapEnable = false;
|
||||
bool tap_enabled = false;
|
||||
};
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "scsi_command_util.h"
|
||||
#include "scsi_host_bridge.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <array>
|
||||
|
||||
using namespace std;
|
||||
using namespace scsi_defs;
|
||||
|
@ -31,7 +30,7 @@ SCSIBR::SCSIBR(int lun) : PrimaryDevice(SCBR, lun)
|
|||
SupportsParams(true);
|
||||
}
|
||||
|
||||
bool SCSIBR::Init(const unordered_map<string, string>& params)
|
||||
bool SCSIBR::Init(const param_map& params)
|
||||
{
|
||||
PrimaryDevice::Init(params);
|
||||
|
||||
|
@ -43,16 +42,14 @@ bool SCSIBR::Init(const unordered_map<string, string>& params)
|
|||
AddCommand(scsi_command::eCmdSendMessage10, [this] { SendMessage10(); });
|
||||
|
||||
#ifdef __linux__
|
||||
// TAP Driver Generation
|
||||
m_bTapEnable = tap.Init(GetParams());
|
||||
if (!m_bTapEnable){
|
||||
GetLogger().Error("Unable to open the TAP interface");
|
||||
tap_enabled = tap.Init(GetParams());
|
||||
if (!tap_enabled){
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Generate MAC Address
|
||||
if (m_bTapEnable) {
|
||||
if (tap_enabled) {
|
||||
tap.GetMacAddr(mac_addr.data());
|
||||
mac_addr[5]++;
|
||||
}
|
||||
|
@ -60,19 +57,24 @@ bool SCSIBR::Init(const unordered_map<string, string>& params)
|
|||
// Packet reception flag OFF
|
||||
packet_enable = false;
|
||||
|
||||
SetReady(m_bTapEnable);
|
||||
SetReady(tap_enabled);
|
||||
|
||||
// Not terminating on regular Linux PCs is helpful for testing
|
||||
#if defined(__x86_64__) || defined(__X86__)
|
||||
return true;
|
||||
#else
|
||||
return m_bTapEnable;
|
||||
return tap_enabled;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SCSIBR::CleanUp()
|
||||
{
|
||||
tap.CleanUp();
|
||||
}
|
||||
|
||||
vector<uint8_t> SCSIBR::InquiryInternal() const
|
||||
{
|
||||
vector<uint8_t> buf = HandleInquiry(device_type::COMMUNICATIONS, scsi_level::SCSI_2, false);
|
||||
vector<uint8_t> buf = HandleInquiry(device_type::communications, scsi_level::scsi_2, false);
|
||||
|
||||
// The bridge returns more additional bytes than the other devices
|
||||
buf.resize(0x1F + 8 + 5);
|
||||
|
@ -83,7 +85,7 @@ vector<uint8_t> SCSIBR::InquiryInternal() const
|
|||
buf[36] = '0';
|
||||
|
||||
// TAP Enable
|
||||
if (m_bTapEnable) {
|
||||
if (tap_enabled) {
|
||||
buf[37] = '1';
|
||||
}
|
||||
|
||||
|
@ -99,7 +101,7 @@ void SCSIBR::TestUnitReady()
|
|||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
int SCSIBR::GetMessage10(const vector<int>& cdb, vector<uint8_t>& buf)
|
||||
int SCSIBR::GetMessage10(cdb_t cdb, vector<uint8_t>& buf)
|
||||
{
|
||||
// Type
|
||||
const int type = cdb[2];
|
||||
|
@ -113,7 +115,7 @@ int SCSIBR::GetMessage10(const vector<int>& cdb, vector<uint8_t>& buf)
|
|||
switch (type) {
|
||||
case 1: // Ethernet
|
||||
// Do not process if TAP is invalid
|
||||
if (!m_bTapEnable) {
|
||||
if (!tap_enabled) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -187,7 +189,7 @@ int SCSIBR::GetMessage10(const vector<int>& cdb, vector<uint8_t>& buf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool SCSIBR::WriteBytes(const vector<int>& cdb, vector<uint8_t>& buf, uint32_t)
|
||||
bool SCSIBR::ReadWrite(cdb_t cdb, vector<uint8_t>& buf)
|
||||
{
|
||||
// Type
|
||||
const int type = cdb[2];
|
||||
|
@ -204,7 +206,7 @@ bool SCSIBR::WriteBytes(const vector<int>& cdb, vector<uint8_t>& buf, uint32_t)
|
|||
switch (type) {
|
||||
case 1: // Ethernet
|
||||
// Do not process if TAP is invalid
|
||||
if (!m_bTapEnable) {
|
||||
if (!tap_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -252,7 +254,7 @@ void SCSIBR::GetMessage10()
|
|||
|
||||
GetController()->SetLength(GetMessage10(GetController()->GetCmd(), GetController()->GetBuffer()));
|
||||
if (GetController()->GetLength() <= 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);
|
||||
}
|
||||
|
||||
// Set next block
|
||||
|
@ -273,7 +275,7 @@ void SCSIBR::SendMessage10() const
|
|||
{
|
||||
GetController()->SetLength(GetInt24(GetController()->GetCmd(), 6));
|
||||
if (GetController()->GetLength() <= 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);
|
||||
}
|
||||
|
||||
// Ensure a sufficient buffer size (because it is not a transfer for each block)
|
||||
|
@ -292,7 +294,7 @@ int SCSIBR::GetMacAddr(vector<uint8_t>& mac) const
|
|||
return static_cast<int>(mac_addr.size());
|
||||
}
|
||||
|
||||
void SCSIBR::SetMacAddr(const vector<uint8_t>& mac)
|
||||
void SCSIBR::SetMacAddr(span<const uint8_t> mac)
|
||||
{
|
||||
memcpy(mac_addr.data(), mac.data(), mac_addr.size());
|
||||
}
|
||||
|
@ -339,7 +341,7 @@ void SCSIBR::GetPacketBuf(vector<uint8_t>& buf, int index)
|
|||
packet_enable = false;
|
||||
}
|
||||
|
||||
void SCSIBR::SendPacket(const vector<uint8_t>& buf, int len)
|
||||
void SCSIBR::SendPacket(span<const uint8_t> buf, int len) const
|
||||
{
|
||||
tap.Send(buf.data(), len);
|
||||
}
|
||||
|
@ -837,7 +839,7 @@ void SCSIBR::FS_GetCapacity(vector<uint8_t>& buf)
|
|||
auto dp = (uint32_t*)buf.data();
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
|
||||
Human68k::capacity_t cap;
|
||||
Human68k::capacity_t cap = {};
|
||||
fsresult = fs.GetCapacity(nUnit, &cap);
|
||||
|
||||
cap.freearea = htons(cap.freearea);
|
||||
|
@ -878,7 +880,7 @@ void SCSIBR::FS_GetDPB(vector<uint8_t>& buf)
|
|||
auto dp = (uint32_t*)buf.data();
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
|
||||
Human68k::dpb_t dpb;
|
||||
Human68k::dpb_t dpb = {};
|
||||
fsresult = fs.GetDPB(nUnit, &dpb);
|
||||
|
||||
dpb.sector_size = htons(dpb.sector_size);
|
||||
|
|
|
@ -17,16 +17,16 @@
|
|||
//---------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "interfaces/byte_writer.h"
|
||||
#include "primary_device.h"
|
||||
#include "ctapdriver.h"
|
||||
#include "cfilesystem.h"
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <array>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class SCSIBR : public PrimaryDevice, public ByteWriter
|
||||
class SCSIBR : public PrimaryDevice
|
||||
{
|
||||
static constexpr const array<uint8_t, 6> bcast_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
|
@ -35,12 +35,15 @@ public:
|
|||
explicit SCSIBR(int);
|
||||
~SCSIBR() override = default;
|
||||
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
bool Init(const param_map&) override;
|
||||
void CleanUp() override;
|
||||
|
||||
param_map GetDefaultParams() const override { return tap.GetDefaultParams(); }
|
||||
|
||||
// Commands
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
int GetMessage10(const vector<int>&, vector<uint8_t>&);
|
||||
bool WriteBytes(const vector<int>&, vector<uint8_t>&, uint32_t) override;
|
||||
int GetMessage10(cdb_t, vector<uint8_t>&);
|
||||
bool ReadWrite(cdb_t, vector<uint8_t>&);
|
||||
void TestUnitReady() override;
|
||||
void GetMessage10();
|
||||
void SendMessage10() const;
|
||||
|
@ -48,16 +51,16 @@ public:
|
|||
private:
|
||||
|
||||
int GetMacAddr(vector<uint8_t>&) const; // Get MAC address
|
||||
void SetMacAddr(const vector<uint8_t>&); // Set MAC address
|
||||
void SetMacAddr(span<const uint8_t>); // Set MAC address
|
||||
void ReceivePacket(); // Receive a packet
|
||||
void GetPacketBuf(vector<uint8_t>&, int); // Get a packet
|
||||
void SendPacket(const vector<uint8_t>&, int); // Send a packet
|
||||
void GetPacketBuf(vector<uint8_t>&, int); // Get a packet
|
||||
void SendPacket(span<const uint8_t>, int) const; // Send a packet
|
||||
|
||||
CTapDriver tap; // TAP driver
|
||||
bool m_bTapEnable = false; // TAP valid flag
|
||||
array<uint8_t, 6> mac_addr = {}; // MAC Address
|
||||
bool tap_enabled = false; // TAP valid flag
|
||||
array<uint8_t, 6> mac_addr = {}; // MAC Address
|
||||
int packet_len = 0; // Receive packet size
|
||||
array<uint8_t, 0x1000> packet_buf; // Receive packet buffer
|
||||
array<uint8_t, 0x1000> packet_buf; // Receive packet buffer
|
||||
bool packet_enable = false; // Received packet valid
|
||||
|
||||
int ReadFsResult(vector<uint8_t>&) const; // Read filesystem (result code)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// Implementation of a SCSI printer (see SCSI-2 specification for a command description)
|
||||
//
|
||||
|
@ -32,7 +32,6 @@
|
|||
#include "shared/piscsi_exceptions.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include "scsi_printer.h"
|
||||
#include <sys/stat.h>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace std;
|
||||
|
@ -45,7 +44,7 @@ SCSIPrinter::SCSIPrinter(int lun) : PrimaryDevice(SCLP, lun)
|
|||
SupportsParams(true);
|
||||
}
|
||||
|
||||
bool SCSIPrinter::Init(const unordered_map<string, string>& params)
|
||||
bool SCSIPrinter::Init(const param_map& params)
|
||||
{
|
||||
PrimaryDevice::Init(params);
|
||||
|
||||
|
@ -61,7 +60,7 @@ bool SCSIPrinter::Init(const unordered_map<string, string>& params)
|
|||
AddCommand(scsi_command::eCmdSendDiagnostic, [this] { SendDiagnostic(); });
|
||||
|
||||
if (GetParam("cmd").find("%f") == string::npos) {
|
||||
GetLogger().Trace("Missing filename specifier %f");
|
||||
LogTrace("Missing filename specifier %f");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -74,6 +73,27 @@ bool SCSIPrinter::Init(const unordered_map<string, string>& params)
|
|||
return true;
|
||||
}
|
||||
|
||||
void SCSIPrinter::CleanUp()
|
||||
{
|
||||
PrimaryDevice::CleanUp();
|
||||
|
||||
if (out.is_open()) {
|
||||
out.close();
|
||||
|
||||
error_code error;
|
||||
remove(path(filename), error);
|
||||
|
||||
filename = "";
|
||||
}
|
||||
}
|
||||
|
||||
param_map SCSIPrinter::GetDefaultParams() const
|
||||
{
|
||||
return {
|
||||
{ "cmd", "lp -oraw %f" }
|
||||
};
|
||||
}
|
||||
|
||||
void SCSIPrinter::TestUnitReady()
|
||||
{
|
||||
// The printer is always ready
|
||||
|
@ -82,20 +102,22 @@ void SCSIPrinter::TestUnitReady()
|
|||
|
||||
vector<uint8_t> SCSIPrinter::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(device_type::PRINTER, scsi_level::SCSI_2, false);
|
||||
return HandleInquiry(device_type::printer, scsi_level::scsi_2, false);
|
||||
}
|
||||
|
||||
void SCSIPrinter::Print()
|
||||
{
|
||||
const uint32_t length = GetInt24(GetController()->GetCmd(), 2);
|
||||
|
||||
GetLogger().Trace("Receiving " + to_string(length) + " byte(s) to be printed");
|
||||
LogTrace("Expecting to receive " + to_string(length) + " byte(s) to be printed");
|
||||
|
||||
if (length > GetController()->GetBuffer().size()) {
|
||||
GetLogger().Error("Transfer buffer overflow: Buffer size is " + to_string(GetController()->GetBuffer().size()) +
|
||||
LogError("Transfer buffer overflow: Buffer size is " + to_string(GetController()->GetBuffer().size()) +
|
||||
" bytes, " + to_string(length) + " bytes expected");
|
||||
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
++print_error_count;
|
||||
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
GetController()->SetLength(length);
|
||||
|
@ -107,9 +129,11 @@ void SCSIPrinter::Print()
|
|||
void SCSIPrinter::SynchronizeBuffer()
|
||||
{
|
||||
if (!out.is_open()) {
|
||||
GetLogger().Warn("Nothing to print");
|
||||
LogWarn("Nothing to print");
|
||||
|
||||
throw scsi_exception(sense_key::ABORTED_COMMAND);
|
||||
++print_warning_count;
|
||||
|
||||
throw scsi_exception(sense_key::aborted_command);
|
||||
}
|
||||
|
||||
string cmd = GetParam("cmd");
|
||||
|
@ -118,26 +142,29 @@ void SCSIPrinter::SynchronizeBuffer()
|
|||
cmd.replace(file_position, 2, filename);
|
||||
|
||||
error_code error;
|
||||
LogTrace("Printing file '" + filename + "' with " + to_string(file_size(path(filename), error)) + " byte(s)");
|
||||
|
||||
GetLogger().Trace("Printing file '" + filename + "' with " + to_string(file_size(path(filename), error)) + " byte(s)");
|
||||
|
||||
GetLogger().Debug("Executing '" + cmd + "'");
|
||||
LogDebug("Executing print command '" + cmd + "'");
|
||||
|
||||
if (system(cmd.c_str())) {
|
||||
GetLogger().Error("Printing file '" + filename + "' failed, the printing system might not be configured");
|
||||
LogError("Printing file '" + filename + "' failed, the printing system might not be configured");
|
||||
|
||||
Cleanup();
|
||||
++print_error_count;
|
||||
|
||||
throw scsi_exception(sense_key::ABORTED_COMMAND);
|
||||
CleanUp();
|
||||
|
||||
throw scsi_exception(sense_key::aborted_command);
|
||||
}
|
||||
|
||||
Cleanup();
|
||||
CleanUp();
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
bool SCSIPrinter::WriteByteSequence(vector<uint8_t>& buf, uint32_t length)
|
||||
bool SCSIPrinter::WriteByteSequence(span<const uint8_t> buf)
|
||||
{
|
||||
byte_receive_count += buf.size();
|
||||
|
||||
if (!out.is_open()) {
|
||||
vector<char> f(file_template.begin(), file_template.end());
|
||||
f.push_back(0);
|
||||
|
@ -145,7 +172,10 @@ bool SCSIPrinter::WriteByteSequence(vector<uint8_t>& buf, uint32_t length)
|
|||
// There is no C++ API that generates a file with a unique name
|
||||
const int fd = mkstemp(f.data());
|
||||
if (fd == -1) {
|
||||
GetLogger().Error("Can't create printer output file for pattern '" + filename + "': " + strerror(errno));
|
||||
LogError("Can't create printer output file for pattern '" + filename + "': " + strerror(errno));
|
||||
|
||||
++print_error_count;
|
||||
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
|
@ -154,27 +184,55 @@ bool SCSIPrinter::WriteByteSequence(vector<uint8_t>& buf, uint32_t length)
|
|||
|
||||
out.open(filename, ios::binary);
|
||||
if (out.fail()) {
|
||||
throw scsi_exception(sense_key::ABORTED_COMMAND);
|
||||
++print_error_count;
|
||||
|
||||
throw scsi_exception(sense_key::aborted_command);
|
||||
}
|
||||
|
||||
GetLogger().Trace("Created printer output file '" + filename + "'");
|
||||
LogTrace("Created printer output file '" + filename + "'");
|
||||
}
|
||||
|
||||
GetLogger().Trace("Appending " + to_string(length) + " byte(s) to printer output file ''" + filename + "'");
|
||||
LogTrace("Appending " + to_string(buf.size()) + " byte(s) to printer output file ''" + filename + "'");
|
||||
|
||||
out.write((const char*)buf.data(), length);
|
||||
out.write((const char *)buf.data(), buf.size());
|
||||
|
||||
return !out.fail();
|
||||
const bool status = out.fail();
|
||||
if (!status) {
|
||||
++print_error_count;
|
||||
}
|
||||
|
||||
return !status;
|
||||
}
|
||||
|
||||
void SCSIPrinter::Cleanup()
|
||||
vector<PbStatistics> SCSIPrinter::GetStatistics() const
|
||||
{
|
||||
if (out.is_open()) {
|
||||
out.close();
|
||||
vector<PbStatistics> statistics = PrimaryDevice::GetStatistics();
|
||||
|
||||
error_code error;
|
||||
remove(path(filename), error);
|
||||
PbStatistics s;
|
||||
s.set_id(GetId());
|
||||
s.set_unit(GetLun());
|
||||
|
||||
filename = "";
|
||||
}
|
||||
s.set_category(PbStatisticsCategory::CATEGORY_INFO);
|
||||
|
||||
s.set_key(FILE_PRINT_COUNT);
|
||||
s.set_value(file_print_count);
|
||||
statistics.push_back(s);
|
||||
|
||||
s.set_key(BYTE_RECEIVE_COUNT);
|
||||
s.set_value(byte_receive_count);
|
||||
statistics.push_back(s);
|
||||
|
||||
s.set_category(PbStatisticsCategory::CATEGORY_ERROR);
|
||||
|
||||
s.set_key(PRINT_ERROR_COUNT);
|
||||
s.set_value(print_error_count);
|
||||
statistics.push_back(s);
|
||||
|
||||
s.set_category(PbStatisticsCategory::CATEGORY_WARNING);
|
||||
|
||||
s.set_key(PRINT_WARNING_COUNT);
|
||||
s.set_value(print_warning_count);
|
||||
statistics.push_back(s);
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// Implementation of a SCSI printer (see SCSI-2 specification for a command description)
|
||||
//
|
||||
|
@ -15,25 +15,41 @@
|
|||
#include <fstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <span>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class SCSIPrinter : public PrimaryDevice, private ScsiPrinterCommands
|
||||
{
|
||||
uint64_t file_print_count = 0;
|
||||
uint64_t byte_receive_count = 0;
|
||||
uint64_t print_error_count = 0;
|
||||
uint64_t print_warning_count = 0;
|
||||
|
||||
static const int NOT_RESERVED = -2;
|
||||
|
||||
static constexpr const char *PRINTER_FILE_PATTERN = "/piscsi_sclp-XXXXXX";
|
||||
|
||||
inline static const string FILE_PRINT_COUNT = "file_print_count";
|
||||
inline static const string BYTE_RECEIVE_COUNT = "byte_receive_count";
|
||||
inline static const string PRINT_ERROR_COUNT = "print_error_count";
|
||||
inline static const string PRINT_WARNING_COUNT = "print_warning_count";
|
||||
|
||||
public:
|
||||
|
||||
explicit SCSIPrinter(int);
|
||||
~SCSIPrinter() override = default;
|
||||
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
bool Init(const param_map&) override;
|
||||
void CleanUp() override;
|
||||
|
||||
param_map GetDefaultParams() const override;
|
||||
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
|
||||
bool WriteByteSequence(vector<uint8_t>&, uint32_t) override;
|
||||
bool WriteByteSequence(span<const uint8_t>) override;
|
||||
|
||||
vector<PbStatistics> GetStatistics() const override;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -44,8 +60,6 @@ private:
|
|||
void Print() override;
|
||||
void SynchronizeBuffer();
|
||||
|
||||
void Cleanup();
|
||||
|
||||
string file_template;
|
||||
|
||||
string filename;
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
SCSICD::SCSICD(int lun, const unordered_set<uint32_t>& sector_sizes, scsi_defs::scsi_level level)
|
||||
SCSICD::SCSICD(int lun, const unordered_set<uint32_t>& sector_sizes, scsi_defs::scsi_level level)
|
||||
: Disk(SCCD, lun), scsi_level(level)
|
||||
{
|
||||
SetSectorSizes(sector_sizes);
|
||||
|
@ -31,7 +31,7 @@ SCSICD::SCSICD(int lun, const unordered_set<uint32_t>& sector_sizes, scsi_defs::
|
|||
SetLockable(true);
|
||||
}
|
||||
|
||||
bool SCSICD::Init(const unordered_map<string, string>& params)
|
||||
bool SCSICD::Init(const param_map& params)
|
||||
{
|
||||
Disk::Init(params);
|
||||
|
||||
|
@ -116,7 +116,7 @@ void SCSICD::OpenIso()
|
|||
|
||||
if (rawfile) {
|
||||
if (size % 2536) {
|
||||
GetLogger().Warn("Raw ISO CD-ROM file size is not a multiple of 2536 bytes but is "
|
||||
LogWarn("Raw ISO CD-ROM file size is not a multiple of 2536 bytes but is "
|
||||
+ to_string(size) + " bytes");
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ void SCSICD::ReadToc()
|
|||
|
||||
vector<uint8_t> SCSICD::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(device_type::CD_ROM, scsi_level, true);
|
||||
return HandleInquiry(device_type::cd_rom, scsi_level, true);
|
||||
}
|
||||
|
||||
void SCSICD::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
|
||||
|
@ -216,13 +216,13 @@ void SCSICD::AddVendorPage(map<int, vector<byte>>& pages, int page, bool changea
|
|||
}
|
||||
}
|
||||
|
||||
int SCSICD::Read(const vector<int>& cdb, vector<uint8_t>& buf, uint64_t block)
|
||||
int SCSICD::Read(span<uint8_t> buf, uint64_t block)
|
||||
{
|
||||
CheckReady();
|
||||
|
||||
const int index = SearchTrack(static_cast<int>(block));
|
||||
if (index < 0) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
|
||||
throw scsi_exception(sense_key::illegal_request, asc::lba_out_of_range);
|
||||
}
|
||||
|
||||
assert(tracks[index]);
|
||||
|
@ -241,15 +241,15 @@ int SCSICD::Read(const vector<int>& cdb, vector<uint8_t>& buf, uint64_t block)
|
|||
}
|
||||
|
||||
assert(dataindex >= 0);
|
||||
return Disk::Read(cdb, buf, block);
|
||||
return Disk::Read(buf, block);
|
||||
}
|
||||
|
||||
int SCSICD::ReadTocInternal(const vector<int>& cdb, vector<uint8_t>& buf)
|
||||
int SCSICD::ReadTocInternal(cdb_t cdb, vector<uint8_t>& buf)
|
||||
{
|
||||
CheckReady();
|
||||
|
||||
// If ready, there is at least one track
|
||||
assert(tracks.size() > 0);
|
||||
assert(!tracks.empty());
|
||||
assert(tracks[0]);
|
||||
|
||||
// Get allocation length, clear buffer
|
||||
|
@ -263,7 +263,7 @@ int SCSICD::ReadTocInternal(const vector<int>& cdb, vector<uint8_t>& buf)
|
|||
const int last = tracks[tracks.size() - 1]->GetTrackNo();
|
||||
// Except for AA
|
||||
if (cdb[6] > last && cdb[6] != 0xaa) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
// Check start index
|
||||
|
@ -280,7 +280,7 @@ int SCSICD::ReadTocInternal(const vector<int>& cdb, vector<uint8_t>& buf)
|
|||
// AA if not found or internal error
|
||||
if (!tracks[index]) {
|
||||
if (cdb[6] != 0xaa) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
// Returns the final LBA+1 because it is AA
|
||||
|
|
|
@ -17,20 +17,23 @@
|
|||
#include "cd_track.h"
|
||||
#include "disk.h"
|
||||
#include "interfaces/scsi_mmc_commands.h"
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class SCSICD : public Disk, private ScsiMmcCommands
|
||||
{
|
||||
public:
|
||||
|
||||
SCSICD(int, const unordered_set<uint32_t>&, scsi_defs::scsi_level = scsi_level::SCSI_2);
|
||||
SCSICD(int, const unordered_set<uint32_t>&, scsi_defs::scsi_level = scsi_level::scsi_2);
|
||||
~SCSICD() override = default;
|
||||
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
bool Init(const param_map&) override;
|
||||
|
||||
void Open() override;
|
||||
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
int Read(const vector<int>&, vector<uint8_t>&, uint64_t) override;
|
||||
int Read(span<uint8_t>, uint64_t) override;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -39,7 +42,7 @@ protected:
|
|||
|
||||
private:
|
||||
|
||||
int ReadTocInternal(const vector<int>&, vector<uint8_t>&);
|
||||
int ReadTocInternal(cdb_t, vector<uint8_t>&);
|
||||
|
||||
void AddCDROMPage(map<int, vector<byte>>&, bool) const;
|
||||
void AddCDDAPage(map<int, vector<byte>>&, bool) const;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
|
@ -14,8 +14,8 @@
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include "scsihd.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include "scsihd.h"
|
||||
|
||||
using namespace scsi_command_util;
|
||||
|
||||
|
@ -70,7 +70,7 @@ void SCSIHD::Open()
|
|||
{
|
||||
assert(!IsReady());
|
||||
|
||||
off_t size = GetFileSize();
|
||||
const off_t size = GetFileSize();
|
||||
|
||||
// Sector size (default 512 bytes) and number of blocks
|
||||
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512);
|
||||
|
@ -81,12 +81,15 @@ void SCSIHD::Open()
|
|||
|
||||
vector<uint8_t> SCSIHD::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(device_type::DIRECT_ACCESS, scsi_level, IsRemovable());
|
||||
return HandleInquiry(device_type::direct_access, scsi_level, IsRemovable());
|
||||
}
|
||||
|
||||
void SCSIHD::ModeSelect(scsi_command cmd, const vector<int>& cdb, const vector<uint8_t>& buf, int length) const
|
||||
void SCSIHD::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length) const
|
||||
{
|
||||
scsi_command_util::ModeSelect(GetLogger(), cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount());
|
||||
if (const string result = scsi_command_util::ModeSelect(cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount());
|
||||
!result.empty()) {
|
||||
LogWarn(result);
|
||||
}
|
||||
}
|
||||
|
||||
void SCSIHD::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) const
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
|
@ -18,6 +18,7 @@
|
|||
#include "shared/scsi.h"
|
||||
#include "disk.h"
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
@ -27,7 +28,7 @@ class SCSIHD : public Disk
|
|||
|
||||
public:
|
||||
|
||||
SCSIHD(int, const unordered_set<uint32_t>&, bool, scsi_defs::scsi_level = scsi_level::SCSI_2);
|
||||
SCSIHD(int, const unordered_set<uint32_t>&, bool, scsi_defs::scsi_level = scsi_level::scsi_2);
|
||||
~SCSIHD() override = default;
|
||||
|
||||
void FinalizeSetup(off_t);
|
||||
|
@ -36,7 +37,7 @@ public:
|
|||
|
||||
// Commands
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
void ModeSelect(scsi_defs::scsi_command, const vector<int>&, const vector<uint8_t>&, int) const override;
|
||||
void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) const override;
|
||||
|
||||
void AddFormatPage(map<int, vector<byte>>&, bool) const override;
|
||||
void AddVendorPage(map<int, vector<byte>>&, int, bool) const override;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) 2021-2023 Uwe Seimet
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -47,7 +48,7 @@ void SCSIHD_NEC::Open()
|
|||
FinalizeSetup(image_offset);
|
||||
}
|
||||
|
||||
pair<int, int> SCSIHD_NEC::SetParameters(const array<char, 512>& data, int size)
|
||||
pair<int, int> SCSIHD_NEC::SetParameters(span<const char> data, int size)
|
||||
{
|
||||
array<uint8_t, 512> root_sector = {};
|
||||
memcpy(root_sector.data(), data.data(), root_sector.size());
|
||||
|
@ -107,12 +108,12 @@ pair<int, int> SCSIHD_NEC::SetParameters(const array<char, 512>& data, int size)
|
|||
throw io_exception("Invalid NEC sector size of " + to_string(sector_size) + " byte(s)");
|
||||
}
|
||||
|
||||
return make_pair(image_size, sector_size);
|
||||
return { image_size, sector_size };
|
||||
}
|
||||
|
||||
vector<uint8_t> SCSIHD_NEC::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_1_CCS, false);
|
||||
return HandleInquiry(device_type::direct_access, scsi_level::scsi_1_ccs, false);
|
||||
}
|
||||
|
||||
void SCSIHD_NEC::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) const
|
||||
|
|
|
@ -33,7 +33,7 @@ class SCSIHD_NEC : public SCSIHD //NOSONAR The inheritance hierarchy depth is ac
|
|||
{
|
||||
public:
|
||||
|
||||
explicit SCSIHD_NEC(int lun) : SCSIHD(lun, sector_sizes, false) {}
|
||||
explicit SCSIHD_NEC(int lun) : SCSIHD(lun, { 512 }, false) {}
|
||||
~SCSIHD_NEC() override = default;
|
||||
|
||||
void Open() override;
|
||||
|
@ -47,13 +47,11 @@ protected:
|
|||
|
||||
private:
|
||||
|
||||
pair<int, int> SetParameters(const array<char, 512>&, int);
|
||||
pair<int, int> SetParameters(span<const char>, int);
|
||||
|
||||
static int GetInt16LittleEndian(const uint8_t *);
|
||||
static int GetInt32LittleEndian(const uint8_t *);
|
||||
|
||||
static inline const unordered_set<uint32_t> sector_sizes = { 512 };
|
||||
|
||||
// Image file offset
|
||||
off_t image_offset = 0;
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -23,13 +24,13 @@ SCSIMO::SCSIMO(int lun, const unordered_set<uint32_t>& sector_sizes) : Disk(SCMO
|
|||
SetSectorSizes(sector_sizes);
|
||||
|
||||
// 128 MB, 512 bytes per sector, 248826 sectors
|
||||
geometries[512 * 248826] = make_pair(512, 248826);
|
||||
geometries[512 * 248826] = { 512, 248826 };
|
||||
// 230 MB, 512 bytes per block, 446325 sectors
|
||||
geometries[512 * 446325] = make_pair(512, 446325);
|
||||
geometries[512 * 446325] = { 512, 446325 };
|
||||
// 540 MB, 512 bytes per sector, 1041500 sectors
|
||||
geometries[512 * 1041500] = make_pair(512, 1041500);
|
||||
geometries[512 * 1041500] = { 512, 1041500 };
|
||||
// 640 MB, 20248 bytes per sector, 310352 sectors
|
||||
geometries[2048 * 310352] = make_pair(2048, 310352);
|
||||
geometries[2048 * 310352] = { 2048, 310352 };
|
||||
|
||||
SetProtectable(true);
|
||||
SetRemovable(true);
|
||||
|
@ -61,7 +62,7 @@ void SCSIMO::Open()
|
|||
|
||||
vector<uint8_t> SCSIMO::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(device_type::OPTICAL_MEMORY, scsi_level::SCSI_2, true);
|
||||
return HandleInquiry(device_type::optical_memory, scsi_level::scsi_2, true);
|
||||
}
|
||||
|
||||
void SCSIMO::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
|
||||
|
@ -89,9 +90,12 @@ void SCSIMO::AddOptionPage(map<int, vector<byte>>& pages, bool) const
|
|||
// Do not report update blocks
|
||||
}
|
||||
|
||||
void SCSIMO::ModeSelect(scsi_command cmd, const vector<int>& cdb, const vector<uint8_t>& buf, int length) const
|
||||
void SCSIMO::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length) const
|
||||
{
|
||||
scsi_command_util::ModeSelect(GetLogger(), cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount());
|
||||
if (const string result = scsi_command_util::ModeSelect(cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount());
|
||||
!result.empty()) {
|
||||
LogWarn(result);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
|
@ -15,6 +16,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "disk.h"
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
@ -30,7 +32,7 @@ public:
|
|||
void Open() override;
|
||||
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
void ModeSelect(scsi_defs::scsi_command, const vector<int>&, const vector<uint8_t>&, int) const override;
|
||||
void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) const override;
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -3,15 +3,13 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include "storage_device.h"
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace std;
|
||||
using namespace filesystem;
|
||||
|
@ -22,22 +20,43 @@ StorageDevice::StorageDevice(PbDeviceType type, int lun) : ModePageDevice(type,
|
|||
SetStoppable(true);
|
||||
}
|
||||
|
||||
void StorageDevice::CleanUp()
|
||||
{
|
||||
UnreserveFile();
|
||||
|
||||
ModePageDevice::CleanUp();
|
||||
}
|
||||
|
||||
void StorageDevice::SetFilename(string_view f)
|
||||
{
|
||||
filename = filesystem::path(f);
|
||||
|
||||
// Permanently write-protected
|
||||
SetReadOnly(IsReadOnlyFile());
|
||||
|
||||
SetProtectable(!IsReadOnlyFile());
|
||||
|
||||
if (IsReadOnlyFile()) {
|
||||
SetProtected(false);
|
||||
}
|
||||
}
|
||||
|
||||
void StorageDevice::ValidateFile()
|
||||
{
|
||||
if (blocks == 0) {
|
||||
throw io_exception(string(GetTypeString()) + " device has 0 blocks");
|
||||
}
|
||||
|
||||
if (!exists(path(filename))) {
|
||||
throw file_not_found_exception("Image file '" + filename + "' for " + GetTypeString() + " device does not exist");
|
||||
if (!exists(filename)) {
|
||||
throw file_not_found_exception("Image file '" + filename.string() + "' for " + GetTypeString() + " device does not exist");
|
||||
}
|
||||
|
||||
if (GetFileSize() > 2LL * 1024 * 1024 * 1024 * 1024) {
|
||||
throw io_exception("Drive capacity cannot exceed 2 TiB");
|
||||
throw io_exception("Image files > 2 TiB are not supported");
|
||||
}
|
||||
|
||||
// TODO Check for duplicate handling of these properties (-> piscsi_executor.cpp)
|
||||
if (access(filename.c_str(), W_OK)) {
|
||||
if (IsReadOnlyFile()) {
|
||||
// Permanently write-protected
|
||||
SetReadOnly(true);
|
||||
SetProtectable(false);
|
||||
|
@ -50,28 +69,28 @@ void StorageDevice::ValidateFile()
|
|||
SetReady(true);
|
||||
}
|
||||
|
||||
void StorageDevice::ReserveFile(const string& file, int id, int lun) const
|
||||
void StorageDevice::ReserveFile() const
|
||||
{
|
||||
assert(!file.empty());
|
||||
assert(reserved_files.find(file) == reserved_files.end());
|
||||
assert(!filename.empty());
|
||||
assert(!reserved_files.contains(filename.string()));
|
||||
|
||||
reserved_files[file] = make_pair(id, lun);
|
||||
reserved_files[filename.string()] = { GetId(), GetLun() };
|
||||
}
|
||||
|
||||
void StorageDevice::UnreserveFile()
|
||||
{
|
||||
reserved_files.erase(filename);
|
||||
reserved_files.erase(filename.string());
|
||||
|
||||
filename = "";
|
||||
filename.clear();
|
||||
}
|
||||
|
||||
id_set StorageDevice::GetIdsForReservedFile(const string& file)
|
||||
{
|
||||
if (const auto& it = reserved_files.find(file); it != reserved_files.end()) {
|
||||
return make_pair(it->second.first, it->second.second);
|
||||
return { it->second.first, it->second.second };
|
||||
}
|
||||
|
||||
return make_pair(-1, -1);
|
||||
return { -1, -1 };
|
||||
}
|
||||
|
||||
void StorageDevice::UnreserveAll()
|
||||
|
@ -79,9 +98,9 @@ void StorageDevice::UnreserveAll()
|
|||
reserved_files.clear();
|
||||
}
|
||||
|
||||
bool StorageDevice::FileExists(const string& file)
|
||||
bool StorageDevice::FileExists(string_view file)
|
||||
{
|
||||
return exists(file);
|
||||
return exists(path(file));
|
||||
}
|
||||
|
||||
bool StorageDevice::IsReadOnlyFile() const
|
||||
|
@ -91,10 +110,10 @@ bool StorageDevice::IsReadOnlyFile() const
|
|||
|
||||
off_t StorageDevice::GetFileSize() const
|
||||
{
|
||||
// filesystem::file_size cannot be used here because gcc < 10.3.0 cannot handle more than 2 GiB
|
||||
if (struct stat st; !stat(filename.c_str(), &st)) {
|
||||
return st.st_size;
|
||||
try {
|
||||
return file_size(filename);
|
||||
}
|
||||
catch (const filesystem_error& e) {
|
||||
throw io_exception("Can't get size of '" + filename.string() + "': " + e.what());
|
||||
}
|
||||
|
||||
throw io_exception("Can't get size of '" + filename + "'");
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
// The base class for all mass storage devices with image file support
|
||||
//
|
||||
|
@ -11,14 +11,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "shared/piscsi_util.h"
|
||||
#include "mode_page_device.h"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace std;
|
||||
|
||||
using id_set = pair<int, int>;
|
||||
|
||||
class StorageDevice : public ModePageDevice
|
||||
{
|
||||
public:
|
||||
|
@ -26,24 +26,27 @@ public:
|
|||
StorageDevice(PbDeviceType, int);
|
||||
~StorageDevice() override = default;
|
||||
|
||||
void CleanUp() override;
|
||||
|
||||
virtual void Open() = 0;
|
||||
|
||||
string GetFilename() const { return filename; }
|
||||
void SetFilename(string_view f) { filename = f; }
|
||||
string GetFilename() const { return filename.string(); }
|
||||
void SetFilename(string_view);
|
||||
|
||||
uint64_t GetBlockCount() const { return blocks; }
|
||||
|
||||
void ReserveFile(const string&, int, int) const;
|
||||
void ReserveFile() const;
|
||||
void UnreserveFile();
|
||||
// TODO Remove this method, it is only used by the unit tests
|
||||
static void UnreserveAll();
|
||||
|
||||
static bool FileExists(const string&);
|
||||
bool IsReadOnlyFile() const;
|
||||
static bool FileExists(string_view);
|
||||
|
||||
void SetMediumChanged(bool b) { medium_changed = b; }
|
||||
|
||||
static unordered_map<string, id_set> GetReservedFiles() { return reserved_files; }
|
||||
static void SetReservedFiles(const unordered_map<string, id_set>& r) { reserved_files = r; }
|
||||
static auto GetReservedFiles() { return reserved_files; }
|
||||
static void SetReservedFiles(const unordered_map<string, id_set, piscsi_util::StringHash, equal_to<>>& r)
|
||||
{ reserved_files = r; }
|
||||
static id_set GetIdsForReservedFile(const string&);
|
||||
|
||||
protected:
|
||||
|
@ -58,13 +61,14 @@ protected:
|
|||
|
||||
private:
|
||||
|
||||
// Total number of blocks
|
||||
bool IsReadOnlyFile() const;
|
||||
|
||||
uint64_t blocks = 0;
|
||||
|
||||
string filename;
|
||||
filesystem::path filename;
|
||||
|
||||
bool medium_changed = false;
|
||||
|
||||
// The list of image files in use and the IDs and LUNs using these files
|
||||
static inline unordered_map<string, id_set> reserved_files;
|
||||
static inline unordered_map<string, id_set, piscsi_util::StringHash, equal_to<>> reserved_files;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "hal/pi_defs/bpi-m2p.h"
|
||||
#include <string>
|
||||
|
||||
//
|
||||
|
@ -55,33 +54,3 @@ const static int PIN_CD = 11; // CD
|
|||
const static int PIN_IO = 23; // IO
|
||||
const static int PIN_BSY = 24; // BSY
|
||||
const static int PIN_SEL = 8; // SEL
|
||||
|
||||
// Warning: The Allwinner/Banana Pi GPIO numbers DO NOT correspond to
|
||||
// the Raspberry Pi GPIO numbers.
|
||||
// For example, Pin 7 is GPIO4 on a Raspberry Pi. Its GPIO 6 on a Banana Pi
|
||||
// For Banana Pi, the pins are specified by physical pin number, NOT GPIO number
|
||||
// (The Macro's convert the pin number to logical BPi GPIO number)
|
||||
const static int BPI_PIN_ACT = BPI_M2P_07; // ACTIVE
|
||||
const static int BPI_PIN_ENB = BPI_M2P_11; // ENABLE
|
||||
const static int BPI_PIN_IND = BPI_M2P_13; // INITIATOR CTRL DIRECTION
|
||||
const static int BPI_PIN_TAD = -1; // TARGET CTRL DIRECTION
|
||||
const static int BPI_PIN_DTD = BPI_M2P_12; // DATA DIRECTION
|
||||
|
||||
const static int BPI_PIN_DT0 = BPI_M2P_31; // Data 0
|
||||
const static int BPI_PIN_DT1 = BPI_M2P_32; // Data 1
|
||||
const static int BPI_PIN_DT2 = BPI_M2P_33; // Data 2
|
||||
const static int BPI_PIN_DT3 = BPI_M2P_36; // Data 3
|
||||
const static int BPI_PIN_DT4 = BPI_M2P_35; // Data 4
|
||||
const static int BPI_PIN_DT5 = BPI_M2P_38; // Data 5
|
||||
const static int BPI_PIN_DT6 = BPI_M2P_37; // Data 6
|
||||
const static int BPI_PIN_DT7 = BPI_M2P_40; // Data 7
|
||||
const static int BPI_PIN_DP = BPI_M2P_29; // Data parity
|
||||
const static int BPI_PIN_ATN = BPI_M2P_15; // ATN
|
||||
const static int BPI_PIN_RST = BPI_M2P_22; // RST
|
||||
const static int BPI_PIN_ACK = BPI_M2P_19; // ACK
|
||||
const static int BPI_PIN_REQ = BPI_M2P_26; // REQ
|
||||
const static int BPI_PIN_MSG = BPI_M2P_21; // MSG
|
||||
const static int BPI_PIN_CD = BPI_M2P_23; // CD
|
||||
const static int BPI_PIN_IO = BPI_M2P_16; // IO
|
||||
const static int BPI_PIN_BSY = BPI_M2P_18; // BSY
|
||||
const static int BPI_PIN_SEL = BPI_M2P_24; // SEL
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "hal/pi_defs/bpi-m2p.h"
|
||||
#include <string>
|
||||
|
||||
//
|
||||
|
@ -55,33 +54,3 @@ const static int PIN_CD = 24; // CD
|
|||
const static int PIN_IO = 25; // IO
|
||||
const static int PIN_BSY = 26; // BSY
|
||||
const static int PIN_SEL = 27; // SEL
|
||||
|
||||
// Warning: The Allwinner/Banana Pi GPIO numbers DO NOT correspond to
|
||||
// the Raspberry Pi GPIO numbers.
|
||||
// For example, Pin 7 is GPIO4 on a Raspberry Pi. Its GPIO 6 on a Banana Pi
|
||||
// For Banana Pi, the pins are specified by physical pin number, NOT GPIO number
|
||||
// (The Macro's convert the pin number to logical BPi GPIO number)
|
||||
const static int BPI_PIN_ACT = BPI_M2P_07; // ACTIVE
|
||||
const static int BPI_PIN_ENB = BPI_M2P_29; // ENABLE
|
||||
const static int BPI_PIN_IND = BPI_M2P_31; // INITIATOR CTRL DIRECTION
|
||||
const static int BPI_PIN_TAD = BPI_M2P_26; // TARGET CTRL DIRECTION
|
||||
const static int BPI_PIN_DTD = BPI_M2P_24; // DATA DIRECTION
|
||||
|
||||
const static int BPI_PIN_DT0 = BPI_M2P_19; // Data 0
|
||||
const static int BPI_PIN_DT1 = BPI_M2P_23; // Data 1
|
||||
const static int BPI_PIN_DT2 = BPI_M2P_32; // Data 2
|
||||
const static int BPI_PIN_DT3 = BPI_M2P_33; // Data 3
|
||||
const static int BPI_PIN_DT4 = BPI_M2P_08; // Data 4
|
||||
const static int BPI_PIN_DT5 = BPI_M2P_10; // Data 5
|
||||
const static int BPI_PIN_DT6 = BPI_M2P_36; // Data 6
|
||||
const static int BPI_PIN_DT7 = BPI_M2P_11; // Data 7
|
||||
const static int BPI_PIN_DP = BPI_M2P_12; // Data parity
|
||||
const static int BPI_PIN_ATN = BPI_M2P_35; // ATN
|
||||
const static int BPI_PIN_RST = BPI_M2P_38; // RST
|
||||
const static int BPI_PIN_ACK = BPI_M2P_40; // ACK
|
||||
const static int BPI_PIN_REQ = BPI_M2P_15; // REQ
|
||||
const static int BPI_PIN_MSG = BPI_M2P_16; // MSG
|
||||
const static int BPI_PIN_CD = BPI_M2P_18; // CD
|
||||
const static int BPI_PIN_IO = BPI_M2P_22; // IO
|
||||
const static int BPI_PIN_BSY = BPI_M2P_37; // BSY
|
||||
const static int BPI_PIN_SEL = BPI_M2P_13; // SEL
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "hal/pi_defs/bpi-m2p.h"
|
||||
#include <string>
|
||||
|
||||
//
|
||||
|
@ -55,33 +54,3 @@ const static int PIN_CD = 18; // CD
|
|||
const static int PIN_IO = 4; // IO
|
||||
const static int PIN_BSY = 27; // BSY
|
||||
const static int PIN_SEL = 23; // SEL
|
||||
|
||||
// Warning: The Allwinner/Banana Pi GPIO numbers DO NOT correspond to
|
||||
// the Raspberry Pi GPIO numbers.
|
||||
// For example, Pin 7 is GPIO4 on a Raspberry Pi. Its GPIO 6 on a Banana Pi
|
||||
// For Banana Pi, the pins are specified by physical pin number, NOT GPIO number
|
||||
// (The Macro's convert the pin number to logical BPi GPIO number)
|
||||
const static int BPI_PIN_ACT = BPI_M2P_08; // ACTIVE
|
||||
const static int BPI_PIN_ENB = BPI_M2P_31; // ENABLE
|
||||
const static int BPI_PIN_IND = BPI_M2P_04; // INITIATOR CTRL DIRECTION
|
||||
const static int BPI_PIN_TAD = BPI_M2P_24; // TARGET CTRL DIRECTION
|
||||
const static int BPI_PIN_DTD = BPI_M2P_29; // DATA DIRECTION
|
||||
|
||||
const static int BPI_PIN_DT0 = BPI_M2P_40; // Data 0
|
||||
const static int BPI_PIN_DT1 = BPI_M2P_37; // Data 1
|
||||
const static int BPI_PIN_DT2 = BPI_M2P_38; // Data 2
|
||||
const static int BPI_PIN_DT3 = BPI_M2P_35; // Data 3
|
||||
const static int BPI_PIN_DT4 = BPI_M2P_36; // Data 4
|
||||
const static int BPI_PIN_DT5 = BPI_M2P_33; // Data 5
|
||||
const static int BPI_PIN_DT6 = BPI_M2P_32; // Data 6
|
||||
const static int BPI_PIN_DT7 = BPI_M2P_23; // Data 7
|
||||
const static int BPI_PIN_DP = BPI_M2P_22; // Data parity
|
||||
const static int BPI_PIN_ATN = BPI_M2P_19; // ATN
|
||||
const static int BPI_PIN_RST = BPI_M2P_15; // RST
|
||||
const static int BPI_PIN_ACK = BPI_M2P_18; // ACK
|
||||
const static int BPI_PIN_REQ = BPI_M2P_10; // REQ
|
||||
const static int BPI_PIN_MSG = BPI_M2P_11; // MSG
|
||||
const static int BPI_PIN_CD = BPI_M2P_12; // CD
|
||||
const static int BPI_PIN_IO = BPI_M2P_07; // IO
|
||||
const static int BPI_PIN_BSY = BPI_M2P_13; // BSY
|
||||
const static int BPI_PIN_SEL = BPI_M2P_16; // SEL
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "hal/pi_defs/bpi-m2p.h"
|
||||
#include <string>
|
||||
|
||||
//
|
||||
|
@ -55,33 +54,3 @@ const static int PIN_CD = 24; // CD
|
|||
const static int PIN_IO = 25; // IO
|
||||
const static int PIN_BSY = 26; // BSY
|
||||
const static int PIN_SEL = 27; // SEL
|
||||
|
||||
// Warning: The Allwinner/Banana Pi GPIO numbers DO NOT correspond to
|
||||
// the Raspberry Pi GPIO numbers.
|
||||
// For example, Pin 7 is GPIO4 on a Raspberry Pi. Its GPIO 6 on a Banana Pi
|
||||
// For Banana Pi, the pins are specified by physical pin number, NOT GPIO number
|
||||
// (The Macro's convert the pin number to logical BPi GPIO number)
|
||||
const static int BPI_PIN_ACT = BPI_M2P_07; // ACTIVE
|
||||
const static int BPI_PIN_ENB = BPI_M2P_29; // ENABLE
|
||||
const static int BPI_PIN_IND = -1; // INITIATOR CTRL DIRECTION
|
||||
const static int BPI_PIN_TAD = -1; // TARGET CTRL DIRECTION
|
||||
const static int BPI_PIN_DTD = -1; // DATA DIRECTION
|
||||
|
||||
const static int BPI_PIN_DT0 = BPI_M2P_19; // Data 0
|
||||
const static int BPI_PIN_DT1 = BPI_M2P_23; // Data 1
|
||||
const static int BPI_PIN_DT2 = BPI_M2P_32; // Data 2
|
||||
const static int BPI_PIN_DT3 = BPI_M2P_33; // Data 3
|
||||
const static int BPI_PIN_DT4 = BPI_M2P_08; // Data 4
|
||||
const static int BPI_PIN_DT5 = BPI_M2P_10; // Data 5
|
||||
const static int BPI_PIN_DT6 = BPI_M2P_36; // Data 6
|
||||
const static int BPI_PIN_DT7 = BPI_M2P_11; // Data 7
|
||||
const static int BPI_PIN_DP = BPI_M2P_12; // Data parity
|
||||
const static int BPI_PIN_ATN = BPI_M2P_35; // ATN
|
||||
const static int BPI_PIN_RST = BPI_M2P_38; // RST
|
||||
const static int BPI_PIN_ACK = BPI_M2P_40; // ACK
|
||||
const static int BPI_PIN_REQ = BPI_M2P_15; // REQ
|
||||
const static int BPI_PIN_MSG = BPI_M2P_16; // MSG
|
||||
const static int BPI_PIN_CD = BPI_M2P_18; // CD
|
||||
const static int BPI_PIN_IO = BPI_M2P_22; // IO
|
||||
const static int BPI_PIN_BSY = BPI_M2P_37; // BSY
|
||||
const static int BPI_PIN_SEL = BPI_M2P_13; // SEL
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "shared/scsi.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
using namespace scsi_defs;
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 akuker
|
||||
//
|
||||
// [ SCSI Bus Monitor ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "data_sample_bananam2p.h"
|
||||
#include "hal/sunxi_utils.h"
|
||||
#include <cstdint>
|
||||
|
||||
uint8_t DataSample_BananaM2p::GetDAT() const
|
||||
{
|
||||
uint8_t ret_val = 0;
|
||||
ret_val |= GetSignal(BPI_PIN_DT0) ? 0x01 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
|
||||
ret_val |= GetSignal(BPI_PIN_DT1) ? 0x02 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
|
||||
ret_val |= GetSignal(BPI_PIN_DT2) ? 0x04 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
|
||||
ret_val |= GetSignal(BPI_PIN_DT3) ? 0x08 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
|
||||
ret_val |= GetSignal(BPI_PIN_DT4) ? 0x10 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
|
||||
ret_val |= GetSignal(BPI_PIN_DT5) ? 0x20 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
|
||||
ret_val |= GetSignal(BPI_PIN_DT6) ? 0x40 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
|
||||
ret_val |= GetSignal(BPI_PIN_DT7) ? 0x80 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
bool DataSample_BananaM2p::GetSignal(int pin) const
|
||||
{
|
||||
int bank = SunXI::GPIO_BANK(pin);
|
||||
int num = SunXI::GPIO_NUM(pin);
|
||||
|
||||
return (bool)((data[bank] >> num) & 0x1);
|
||||
}
|
||||
|
||||
// This will return the Banana Pi data in the "Raspberry Pi" data format.
|
||||
uint32_t DataSample_BananaM2p::GetRawCapture() const
|
||||
{
|
||||
uint32_t rpi_data = 0;
|
||||
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_BSY)) << PIN_BSY;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_SEL)) << PIN_SEL;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_ATN)) << PIN_ATN;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_ACK)) << PIN_ACK;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_RST)) << PIN_RST;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_MSG)) << PIN_MSG;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_CD)) << PIN_CD;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_IO)) << PIN_IO;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_REQ)) << PIN_REQ;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_ACT)) << PIN_ACT;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DP)) << PIN_DP;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT0)) << PIN_DT0;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT1)) << PIN_DT1;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT2)) << PIN_DT2;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT3)) << PIN_DT3;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT4)) << PIN_DT4;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT5)) << PIN_DT5;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT6)) << PIN_DT6;
|
||||
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT7)) << PIN_DT7;
|
||||
|
||||
return rpi_data;
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 akuker
|
||||
//
|
||||
// [ Logical representation of a single data sample ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/data_sample.h"
|
||||
#include "shared/scsi.h"
|
||||
#include <array>
|
||||
|
||||
#if defined CONNECT_TYPE_STANDARD
|
||||
#include "hal/connection_type/connection_standard.h"
|
||||
#elif defined CONNECT_TYPE_FULLSPEC
|
||||
#include "hal/connection_type/connection_fullspec.h"
|
||||
#elif defined CONNECT_TYPE_AIBOM
|
||||
#include "hal/connection_type/connection_aibom.h"
|
||||
#elif defined CONNECT_TYPE_GAMERNIUM
|
||||
#include "hal/connection_type/connection_gamernium.h"
|
||||
#else
|
||||
#error Invalid connection type or none specified
|
||||
#endif
|
||||
|
||||
class DataSample_BananaM2p final : public DataSample
|
||||
{
|
||||
public:
|
||||
bool GetSignal(int pin) const override;
|
||||
|
||||
bool GetBSY() const override
|
||||
{
|
||||
return GetSignal(BPI_PIN_BSY);
|
||||
}
|
||||
bool GetSEL() const override
|
||||
{
|
||||
return GetSignal(BPI_PIN_SEL);
|
||||
}
|
||||
bool GetATN() const override
|
||||
{
|
||||
return GetSignal(BPI_PIN_ATN);
|
||||
}
|
||||
bool GetACK() const override
|
||||
{
|
||||
return GetSignal(BPI_PIN_ACK);
|
||||
}
|
||||
bool GetRST() const override
|
||||
{
|
||||
return GetSignal(BPI_PIN_RST);
|
||||
}
|
||||
bool GetMSG() const override
|
||||
{
|
||||
return GetSignal(BPI_PIN_MSG);
|
||||
}
|
||||
bool GetCD() const override
|
||||
{
|
||||
return GetSignal(BPI_PIN_CD);
|
||||
}
|
||||
bool GetIO() const override
|
||||
{
|
||||
return GetSignal(BPI_PIN_IO);
|
||||
}
|
||||
bool GetREQ() const override
|
||||
{
|
||||
return GetSignal(BPI_PIN_REQ);
|
||||
}
|
||||
bool GetACT() const override
|
||||
{
|
||||
return GetSignal(BPI_PIN_ACT);
|
||||
}
|
||||
bool GetDP() const override
|
||||
{
|
||||
return GetSignal(BPI_PIN_DP);
|
||||
}
|
||||
|
||||
uint8_t GetDAT() const override;
|
||||
|
||||
uint32_t GetRawCapture() const override;
|
||||
|
||||
DataSample_BananaM2p(const array<uint32_t, 12> &in_data, uint64_t in_timestamp)
|
||||
: DataSample{in_timestamp}, data{in_data}
|
||||
{
|
||||
}
|
||||
DataSample_BananaM2p() = default;
|
||||
|
||||
~DataSample_BananaM2p() override = default;
|
||||
|
||||
private:
|
||||
array<uint32_t, 12> data = {0};
|
||||
};
|
|
@ -6,15 +6,12 @@
|
|||
// Powered by XM6 TypeG Technology.
|
||||
// Copyright (C) 2016-2020 GIMONS
|
||||
//
|
||||
// [ GPIO-SCSI bus ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "hal/gpiobus.h"
|
||||
#include "hal/sbc_version.h"
|
||||
#include "hal/systimer.h"
|
||||
#include "shared/log.h"
|
||||
#include <array>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
|
@ -41,11 +38,10 @@ bool GPIOBUS::Init(mode_e mode)
|
|||
//---------------------------------------------------------------------------
|
||||
int GPIOBUS::CommandHandShake(vector<uint8_t> &buf)
|
||||
{
|
||||
GPIO_FUNCTION_TRACE
|
||||
// Only works in TARGET mode
|
||||
if (actmode != mode_e::TARGET) {
|
||||
return 0;
|
||||
}
|
||||
assert(actmode == mode_e::TARGET);
|
||||
|
||||
GPIO_FUNCTION_TRACE
|
||||
|
||||
DisableIRQ();
|
||||
|
||||
|
@ -280,8 +276,8 @@ int GPIOBUS::SendHandShake(uint8_t *buf, int count, int delay_after_bytes)
|
|||
if (actmode == mode_e::TARGET) {
|
||||
for (i = 0; i < count; i++) {
|
||||
if (i == delay_after_bytes) {
|
||||
LOGTRACE("%s DELAYING for %dus after %d bytes", __PRETTY_FUNCTION__, SCSI_DELAY_SEND_DATA_DAYNAPORT_US,
|
||||
(int)delay_after_bytes)
|
||||
spdlog::trace("DELAYING for " + to_string(SCSI_DELAY_SEND_DATA_DAYNAPORT_US) + " us after " +
|
||||
to_string(delay_after_bytes) + " bytes");
|
||||
SysTimer::SleepUsec(SCSI_DELAY_SEND_DATA_DAYNAPORT_US);
|
||||
}
|
||||
|
||||
|
@ -324,12 +320,6 @@ int GPIOBUS::SendHandShake(uint8_t *buf, int count, int delay_after_bytes)
|
|||
phase_t phase = GetPhase();
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (i == delay_after_bytes) {
|
||||
LOGTRACE("%s DELAYING for %dus after %d bytes", __PRETTY_FUNCTION__, SCSI_DELAY_SEND_DATA_DAYNAPORT_US,
|
||||
(int)delay_after_bytes)
|
||||
SysTimer::SleepUsec(SCSI_DELAY_SEND_DATA_DAYNAPORT_US);
|
||||
}
|
||||
|
||||
// Set the DATA signals
|
||||
SetDAT(*buf);
|
||||
|
||||
|
@ -341,6 +331,11 @@ int GPIOBUS::SendHandShake(uint8_t *buf, int count, int delay_after_bytes)
|
|||
break;
|
||||
}
|
||||
|
||||
// Signal the last MESSAGE OUT byte
|
||||
if (phase == phase_t::msgout && i == count - 1) {
|
||||
SetATN(false);
|
||||
}
|
||||
|
||||
// Phase error
|
||||
Acquire();
|
||||
if (GetPhase() != phase) {
|
||||
|
@ -392,27 +387,18 @@ bool GPIOBUS::PollSelectEvent()
|
|||
return false;
|
||||
#else
|
||||
GPIO_FUNCTION_TRACE
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
errno = 0;
|
||||
int prev_mode = -1;
|
||||
if (SBC_Version::IsBananaPi()) {
|
||||
prev_mode = GetMode(BPI_PIN_SEL);
|
||||
SetMode(BPI_PIN_SEL, GPIO_IRQ_IN);
|
||||
}
|
||||
errno = 0;
|
||||
|
||||
if (epoll_event epev; epoll_wait(epfd, &epev, 1, -1) <= 0) {
|
||||
LOGWARN("%s epoll_wait failed", __PRETTY_FUNCTION__)
|
||||
spdlog::warn("epoll_wait failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gpioevent_data gpev; read(selevreq.fd, &gpev, sizeof(gpev)) < 0) {
|
||||
LOGWARN("%s read failed", __PRETTY_FUNCTION__)
|
||||
spdlog::warn("read failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SBC_Version::IsBananaPi()) {
|
||||
SetMode(BPI_PIN_SEL, prev_mode);
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
@ -435,10 +421,10 @@ void GPIOBUS::ClearSelectEvent()
|
|||
bool GPIOBUS::WaitSignal(int pin, bool ast)
|
||||
{
|
||||
// Get current time
|
||||
uint32_t now = SysTimer::GetTimerLow();
|
||||
const uint32_t now = SysTimer::GetTimerLow();
|
||||
|
||||
// Calculate timeout (3000ms)
|
||||
uint32_t timeout = 3000 * 1000;
|
||||
const uint32_t timeout = 3000 * 1000;
|
||||
|
||||
do {
|
||||
// Immediately upon receiving a reset
|
||||
|
@ -455,4 +441,4 @@ bool GPIOBUS::WaitSignal(int pin, bool ast)
|
|||
|
||||
// We timed out waiting for the signal
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
// Copyright (C) 2016-2020 GIMONS
|
||||
// [ GPIO-SCSI bus ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -13,7 +12,6 @@
|
|||
|
||||
#include "hal/bus.h"
|
||||
#include "shared/scsi.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
|
@ -176,11 +174,11 @@ class GPIOBUS : public BUS
|
|||
bool Init(mode_e mode = mode_e::TARGET) override;
|
||||
|
||||
// Command receive handshake
|
||||
int CommandHandShake(vector<uint8_t> &) override;
|
||||
int CommandHandShake(vector<uint8_t>&) override;
|
||||
// Data receive handshake
|
||||
int ReceiveHandShake(uint8_t *buf, int count) override;
|
||||
int ReceiveHandShake(uint8_t *, int) override;
|
||||
// Data transmission handshake
|
||||
int SendHandShake(uint8_t *buf, int count, int delay_after_bytes) override;
|
||||
int SendHandShake(uint8_t *, int, int) override;
|
||||
|
||||
// SEL signal event polling
|
||||
bool PollSelectEvent() override;
|
||||
|
@ -208,13 +206,13 @@ class GPIOBUS : public BUS
|
|||
virtual void DrvConfig(uint32_t drive) = 0;
|
||||
|
||||
// Operation mode
|
||||
mode_e actmode = mode_e::TARGET; // NOSONAR: This protected so derived classes can access it
|
||||
mode_e actmode = mode_e::TARGET;
|
||||
|
||||
#ifdef __linux__
|
||||
// SEL signal event request
|
||||
struct gpioevent_request selevreq = {}; // NOSONAR: This protected so derived classes can access it
|
||||
struct gpioevent_request selevreq = {};
|
||||
// epoll file descriptor
|
||||
int epfd; // NOSONAR: This protected so derived classes can access it
|
||||
int epfd = 0;
|
||||
|
||||
#endif
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,204 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
// Copyright (C) 2016-2020 GIMONS
|
||||
// [ GPIO-SCSI bus ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/data_sample_bananam2p.h"
|
||||
#include "hal/gpiobus.h"
|
||||
#include "hal/pi_defs/bpi-gpio.h"
|
||||
#include "hal/sbc_version.h"
|
||||
#include "hal/sunxi_utils.h"
|
||||
#include "shared/log.h"
|
||||
#include "shared/scsi.h"
|
||||
#include <map>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Class definition
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
class GPIOBUS_BananaM2p : public GPIOBUS
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
GPIOBUS_BananaM2p() = default;
|
||||
~GPIOBUS_BananaM2p() override = default;
|
||||
// Destructor
|
||||
bool Init(mode_e mode = mode_e::TARGET) override;
|
||||
|
||||
void Cleanup() override;
|
||||
void Reset() override;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Bus signal acquisition
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
uint32_t Acquire() override;
|
||||
|
||||
void SetENB(bool ast) override;
|
||||
// Set ENB signal
|
||||
|
||||
bool GetBSY() const override;
|
||||
// Get BSY signal
|
||||
void SetBSY(bool ast) override;
|
||||
// Set BSY signal
|
||||
|
||||
bool GetSEL() const override;
|
||||
// Get SEL signal
|
||||
void SetSEL(bool ast) override;
|
||||
// Set SEL signal
|
||||
|
||||
bool GetATN() const override;
|
||||
// Get ATN signal
|
||||
void SetATN(bool ast) override;
|
||||
// Set ATN signal
|
||||
|
||||
bool GetACK() const override;
|
||||
// Get ACK signal
|
||||
void SetACK(bool ast) override;
|
||||
// Set ACK signal
|
||||
|
||||
bool GetACT() const override;
|
||||
// Get ACT signal
|
||||
void SetACT(bool ast) override;
|
||||
// Set ACT signal
|
||||
|
||||
bool GetRST() const override;
|
||||
// Get RST signal
|
||||
void SetRST(bool ast) override;
|
||||
// Set RST signal
|
||||
|
||||
bool GetMSG() const override;
|
||||
// Get MSG signal
|
||||
void SetMSG(bool ast) override;
|
||||
// Set MSG signal
|
||||
|
||||
bool GetCD() const override;
|
||||
// Get CD signal
|
||||
void SetCD(bool ast) override;
|
||||
// Set CD signal
|
||||
|
||||
bool GetIO() override;
|
||||
// Get IO signal
|
||||
void SetIO(bool ast) override;
|
||||
// Set IO signal
|
||||
|
||||
bool GetREQ() const override;
|
||||
// Get REQ signal
|
||||
void SetREQ(bool ast) override;
|
||||
// Set REQ signal
|
||||
|
||||
bool GetDP() const override;
|
||||
|
||||
uint8_t GetDAT() override;
|
||||
// Get DAT signal
|
||||
void SetDAT(uint8_t dat) override;
|
||||
// Set DAT signal
|
||||
|
||||
bool WaitREQ(bool ast) override
|
||||
{
|
||||
return WaitSignal(BPI_PIN_REQ, ast);
|
||||
}
|
||||
bool WaitACK(bool ast) override
|
||||
{
|
||||
return WaitSignal(BPI_PIN_ACK, ast);
|
||||
}
|
||||
|
||||
// TODO: Restore these back to protected
|
||||
// protected:
|
||||
// SCSI I/O signal control
|
||||
void MakeTable() override;
|
||||
// Create work data
|
||||
void SetControl(int pin, bool ast) override;
|
||||
// Set Control Signal
|
||||
void SetMode(int pin, int mode) override;
|
||||
// Set SCSI I/O mode
|
||||
int GetMode(int pin) override;
|
||||
|
||||
inline bool GetSignal(int pin) const override;
|
||||
|
||||
// Set SCSI output signal value
|
||||
void SetSignal(int pin, bool ast) override;
|
||||
|
||||
// Interrupt control
|
||||
// IRQ Disabled
|
||||
void DisableIRQ() override;
|
||||
// IRQ Enabled
|
||||
void EnableIRQ() override;
|
||||
|
||||
// GPIO pin functionality settings
|
||||
void PinConfig(int pin, int mode) override;
|
||||
// GPIO pin direction setting
|
||||
void PullConfig(int pin, int mode) override;
|
||||
// GPIO pin pull up/down resistor setting
|
||||
void PinSetSignal(int pin, bool ast) override;
|
||||
// Set GPIO output signal
|
||||
void DrvConfig(uint32_t drive) override;
|
||||
// Set GPIO drive strength
|
||||
|
||||
unique_ptr<DataSample> GetSample(uint64_t timestamp) override
|
||||
{
|
||||
Acquire();
|
||||
return make_unique<DataSample_BananaM2p>(signals, timestamp);
|
||||
}
|
||||
|
||||
bool SetupSelEvent();
|
||||
|
||||
volatile uint32_t *gpio_map = nullptr;
|
||||
|
||||
// Timer control register
|
||||
volatile uint32_t *tmr_ctrl;
|
||||
array<uint32_t, 12> signals = {0}; // All bus signals
|
||||
|
||||
int sunxi_setup(void);
|
||||
|
||||
void sunxi_set_pullupdn(int gpio, int pud);
|
||||
void sunxi_setup_gpio(int gpio, int direction, int pud);
|
||||
|
||||
void sunxi_output_gpio(int gpio, int value);
|
||||
int sunxi_input_gpio(int gpio) const;
|
||||
|
||||
int bpi_found = -1;
|
||||
|
||||
volatile SunXI::sunxi_gpio_reg_t *pio_map;
|
||||
volatile SunXI::sunxi_gpio_reg_t *r_pio_map;
|
||||
|
||||
volatile uint32_t *r_gpio_map;
|
||||
|
||||
uint8_t *gpio_mmap_reg;
|
||||
uint32_t sunxi_capture_all_gpio();
|
||||
void set_pullupdn(int gpio, int pud);
|
||||
|
||||
// These definitions are from c_gpio.c and should be removed at some point!!
|
||||
const int SETUP_OK = 0;
|
||||
|
||||
SBC_Version::sbc_version_type sbc_version;
|
||||
|
||||
static const array<int, 19> SignalTable;
|
||||
|
||||
void InitializeGpio();
|
||||
std::vector<int> gpio_banks;
|
||||
|
||||
// Note: These MUST be in order from bit 0 to bit 7 with parity as the last item in the array
|
||||
const array<int, 9> pintbl = {BPI_PIN_DT0, BPI_PIN_DT1, BPI_PIN_DT2, BPI_PIN_DT3, BPI_PIN_DT4,
|
||||
BPI_PIN_DT5, BPI_PIN_DT6, BPI_PIN_DT7, BPI_PIN_DP};
|
||||
|
||||
void sunxi_set_all_gpios(array<uint32_t, 12> &mask, array<uint32_t, 12> &value);
|
||||
|
||||
array<uint32_t, 12> gpio_data_masks = {0};
|
||||
|
||||
#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__)
|
||||
// The SEL_EVENT functions need to do something to prevent SonarCloud from
|
||||
// claiming they should be const
|
||||
int dummy_var = 0;
|
||||
#endif
|
||||
};
|
|
@ -1,51 +1,48 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 akuker
|
||||
// [ GPIO bus factory ]
|
||||
// Copyright (C) 2022 akuker
|
||||
// Copyright (C) 2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "hal/gpiobus_bananam2p.h"
|
||||
#include "hal/gpiobus_factory.h"
|
||||
#include "hal/gpiobus_raspberry.h"
|
||||
#include "hal/gpiobus_virtual.h"
|
||||
#include "hal/sbc_version.h"
|
||||
#include "shared/log.h"
|
||||
#include <unistd.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
unique_ptr<BUS> GPIOBUS_Factory::Create(BUS::mode_e mode)
|
||||
{
|
||||
unique_ptr<BUS> return_ptr;
|
||||
unique_ptr<BUS> bus;
|
||||
|
||||
try {
|
||||
// TODO Make the factory a friend of GPIOBUS and make the GPIOBUS constructor private
|
||||
// so that clients cannot use it anymore but have to use the factory.
|
||||
// Also make Init() private.
|
||||
SBC_Version::Init();
|
||||
if (SBC_Version::IsBananaPi()) {
|
||||
LOGTRACE("Creating GPIOBUS_BananaM2p")
|
||||
return_ptr = make_unique<GPIOBUS_BananaM2p>();
|
||||
} else if (SBC_Version::IsRaspberryPi()) {
|
||||
LOGTRACE("Creating GPIOBUS_Raspberry")
|
||||
return_ptr = make_unique<GPIOBUS_Raspberry>();
|
||||
if (SBC_Version::IsRaspberryPi()) {
|
||||
if (getuid()) {
|
||||
spdlog::error("GPIO bus access requires root permissions. Are you running as root?");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bus = make_unique<GPIOBUS_Raspberry>();
|
||||
} else {
|
||||
LOGINFO("Creating Virtual GPIOBUS")
|
||||
return_ptr = make_unique<GPIOBUS_Virtual>();
|
||||
bus = make_unique<GPIOBUS_Virtual>();
|
||||
}
|
||||
if (!return_ptr->Init(mode)) {
|
||||
return nullptr;
|
||||
|
||||
if (bus->Init(mode)) {
|
||||
bus->Reset();
|
||||
}
|
||||
return_ptr->Reset();
|
||||
} catch (const invalid_argument&) {
|
||||
LOGERROR("Exception while trying to initialize GPIO bus. Are you running as root?")
|
||||
return_ptr = nullptr;
|
||||
} catch (const invalid_argument& e) {
|
||||
spdlog::error(string("Exception while trying to initialize GPIO bus: ") + e.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return return_ptr;
|
||||
return bus;
|
||||
}
|
||||
|
|
|
@ -15,13 +15,15 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "hal/gpiobus_raspberry.h"
|
||||
#include "hal/gpiobus.h"
|
||||
#include "hal/systimer.h"
|
||||
#include "shared/log.h"
|
||||
#include <map>
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#ifdef __linux__
|
||||
#include <sys/epoll.h>
|
||||
#endif
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
|
@ -80,14 +82,14 @@ bool GPIOBUS_Raspberry::Init(mode_e mode)
|
|||
// Open /dev/mem
|
||||
int fd = open("/dev/mem", O_RDWR | O_SYNC);
|
||||
if (fd == -1) {
|
||||
LOGERROR("Error: Unable to open /dev/mem. Are you running as root?")
|
||||
spdlog::error("Error: Unable to open /dev/mem. Are you running as root?");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Map peripheral region memory
|
||||
void *map = mmap(NULL, 0x1000100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, baseaddr);
|
||||
if (map == MAP_FAILED) {
|
||||
LOGERROR("Error: Unable to map memory: %s", strerror(errno))
|
||||
spdlog::error("Error: Unable to map memory: "+ string(strerror(errno)));
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
@ -183,7 +185,7 @@ bool GPIOBUS_Raspberry::Init(mode_e mode)
|
|||
// GPIO chip open
|
||||
fd = open("/dev/gpiochip0", 0);
|
||||
if (fd == -1) {
|
||||
LOGERROR("Unable to open /dev/gpiochip0. Is PiSCSI or RaSCSI already running?")
|
||||
spdlog::error("Unable to open /dev/gpiochip0. If PiSCSI is running, please shut it down first.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -199,7 +201,7 @@ bool GPIOBUS_Raspberry::Init(mode_e mode)
|
|||
|
||||
// Get event request
|
||||
if (ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &selevreq) == -1) {
|
||||
LOGERROR("Unable to register event request. Is PiSCSI or RaSCSI already running?")
|
||||
spdlog::error("Unable to register event request. If PiSCSI is running, please shut it down first.");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
#include "hal/data_sample_raspberry.h"
|
||||
#include "hal/gpiobus.h"
|
||||
#include "shared/log.h"
|
||||
#include "shared/scsi.h"
|
||||
#include <map>
|
||||
|
||||
|
@ -163,9 +162,9 @@ class GPIOBUS_Raspberry : public GPIOBUS
|
|||
|
||||
protected:
|
||||
// All bus signals
|
||||
uint32_t signals = 0; // NOSONAR: Must be protected (not private) for testability
|
||||
uint32_t signals = 0;
|
||||
// GPIO input level
|
||||
volatile uint32_t *level = nullptr; // NOSONAR: Must be protected (not private) for testability
|
||||
volatile uint32_t *level = nullptr;
|
||||
|
||||
private:
|
||||
// SCSI I/O signal control
|
||||
|
|
|
@ -6,19 +6,19 @@
|
|||
// Powered by XM6 TypeG Technology.
|
||||
// Copyright (C) 2016-2020 GIMONS
|
||||
//
|
||||
// [ GPIO-SCSI bus ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "hal/gpiobus_virtual.h"
|
||||
#include "hal/gpiobus.h"
|
||||
#include "hal/systimer.h"
|
||||
#include "shared/log.h"
|
||||
#include "hal/log.h"
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/epoll.h>
|
||||
#endif
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
// Copyright (C) 2016-2020 GIMONS
|
||||
// [ GPIO-SCSI bus ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -13,7 +12,6 @@
|
|||
|
||||
#include "hal/data_sample_raspberry.h"
|
||||
#include "hal/gpiobus.h"
|
||||
#include "shared/log.h"
|
||||
#include "shared/scsi.h"
|
||||
|
||||
#include <map>
|
||||
|
@ -171,4 +169,4 @@ class GPIOBUS_Virtual final : public GPIOBUS
|
|||
sem_t *mutex_sem, *buffer_count_sem, *spool_signal_sem;
|
||||
int fd_shm, fd_log;
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
|
|
@ -9,9 +9,11 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// The legacy code in this file is deprecated and can cause a buffer overflow. Use spdlog directly instead.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
static const int LOGBUF_SIZE = 512;
|
||||
|
|
@ -1,356 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2014-2017 Banana Pi
|
||||
Updates Copyright (C) 2022 akuker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
const int GPIO_NOT_USED = -1;
|
||||
|
||||
const int GPIO_PA00 = 0;
|
||||
const int GPIO_PA01 = 1;
|
||||
const int GPIO_PA02 = 2;
|
||||
const int GPIO_PA03 = 3;
|
||||
const int GPIO_PA04 = 4;
|
||||
const int GPIO_PA05 = 5;
|
||||
const int GPIO_PA06 = 6;
|
||||
const int GPIO_PA07 = 7;
|
||||
const int GPIO_PA08 = 8;
|
||||
const int GPIO_PA09 = 9;
|
||||
const int GPIO_PA10 = 10;
|
||||
const int GPIO_PA11 = 11;
|
||||
const int GPIO_PA12 = 12;
|
||||
const int GPIO_PA13 = 13;
|
||||
const int GPIO_PA14 = 14;
|
||||
const int GPIO_PA15 = 15;
|
||||
const int GPIO_PA16 = 16;
|
||||
const int GPIO_PA17 = 17;
|
||||
const int GPIO_PA18 = 18;
|
||||
const int GPIO_PA19 = 19;
|
||||
const int GPIO_PA20 = 20;
|
||||
const int GPIO_PA21 = 21;
|
||||
const int GPIO_PA22 = 22;
|
||||
const int GPIO_PA23 = 23;
|
||||
const int GPIO_PA24 = 24;
|
||||
const int GPIO_PA25 = 25;
|
||||
const int GPIO_PA26 = 26;
|
||||
const int GPIO_PA27 = 27;
|
||||
const int GPIO_PA28 = 28;
|
||||
const int GPIO_PA29 = 29;
|
||||
const int GPIO_PA30 = 30;
|
||||
const int GPIO_PA31 = 31;
|
||||
|
||||
const int GPIO_PB00 = 32;
|
||||
const int GPIO_PB01 = 1 + GPIO_PB00;
|
||||
const int GPIO_PB02 = 2 + GPIO_PB00;
|
||||
const int GPIO_PB03 = 3 + GPIO_PB00;
|
||||
const int GPIO_PB04 = 4 + GPIO_PB00;
|
||||
const int GPIO_PB05 = 5 + GPIO_PB00;
|
||||
const int GPIO_PB06 = 6 + GPIO_PB00;
|
||||
const int GPIO_PB07 = 7 + GPIO_PB00;
|
||||
const int GPIO_PB08 = 8 + GPIO_PB00;
|
||||
const int GPIO_PB09 = 9 + GPIO_PB00;
|
||||
const int GPIO_PB10 = 10 + GPIO_PB00;
|
||||
const int GPIO_PB11 = 11 + GPIO_PB00;
|
||||
const int GPIO_PB12 = 12 + GPIO_PB00;
|
||||
const int GPIO_PB13 = 13 + GPIO_PB00;
|
||||
const int GPIO_PB14 = 14 + GPIO_PB00;
|
||||
const int GPIO_PB15 = 15 + GPIO_PB00;
|
||||
const int GPIO_PB16 = 16 + GPIO_PB00;
|
||||
const int GPIO_PB17 = 17 + GPIO_PB00;
|
||||
const int GPIO_PB18 = 18 + GPIO_PB00;
|
||||
const int GPIO_PB19 = 19 + GPIO_PB00;
|
||||
const int GPIO_PB20 = 20 + GPIO_PB00;
|
||||
const int GPIO_PB21 = 21 + GPIO_PB00;
|
||||
const int GPIO_PB22 = 22 + GPIO_PB00;
|
||||
const int GPIO_PB23 = 23 + GPIO_PB00;
|
||||
const int GPIO_PB24 = 24 + GPIO_PB00;
|
||||
const int GPIO_PB25 = 25 + GPIO_PB00;
|
||||
const int GPIO_PB26 = 26 + GPIO_PB00;
|
||||
const int GPIO_PB27 = 27 + GPIO_PB00;
|
||||
const int GPIO_PB28 = 28 + GPIO_PB00;
|
||||
const int GPIO_PB29 = 29 + GPIO_PB00;
|
||||
const int GPIO_PB30 = 30 + GPIO_PB00;
|
||||
const int GPIO_PB31 = 31 + GPIO_PB00;
|
||||
|
||||
const int GPIO_PC00 = 64;
|
||||
const int GPIO_PC01 = 1 + GPIO_PC00;
|
||||
const int GPIO_PC02 = 2 + GPIO_PC00;
|
||||
const int GPIO_PC03 = 3 + GPIO_PC00;
|
||||
const int GPIO_PC04 = 4 + GPIO_PC00;
|
||||
const int GPIO_PC05 = 5 + GPIO_PC00;
|
||||
const int GPIO_PC06 = 6 + GPIO_PC00;
|
||||
const int GPIO_PC07 = 7 + GPIO_PC00;
|
||||
const int GPIO_PC08 = 8 + GPIO_PC00;
|
||||
const int GPIO_PC09 = 9 + GPIO_PC00;
|
||||
const int GPIO_PC10 = 10 + GPIO_PC00;
|
||||
const int GPIO_PC11 = 11 + GPIO_PC00;
|
||||
const int GPIO_PC12 = 12 + GPIO_PC00;
|
||||
const int GPIO_PC13 = 13 + GPIO_PC00;
|
||||
const int GPIO_PC14 = 14 + GPIO_PC00;
|
||||
const int GPIO_PC15 = 15 + GPIO_PC00;
|
||||
const int GPIO_PC16 = 16 + GPIO_PC00;
|
||||
const int GPIO_PC17 = 17 + GPIO_PC00;
|
||||
const int GPIO_PC18 = 18 + GPIO_PC00;
|
||||
const int GPIO_PC19 = 19 + GPIO_PC00;
|
||||
const int GPIO_PC20 = 20 + GPIO_PC00;
|
||||
const int GPIO_PC21 = 21 + GPIO_PC00;
|
||||
const int GPIO_PC22 = 22 + GPIO_PC00;
|
||||
const int GPIO_PC23 = 23 + GPIO_PC00;
|
||||
const int GPIO_PC24 = 24 + GPIO_PC00;
|
||||
const int GPIO_PC25 = 25 + GPIO_PC00;
|
||||
const int GPIO_PC26 = 26 + GPIO_PC00;
|
||||
const int GPIO_PC27 = 27 + GPIO_PC00;
|
||||
const int GPIO_PC28 = 28 + GPIO_PC00;
|
||||
const int GPIO_PC29 = 29 + GPIO_PC00;
|
||||
const int GPIO_PC30 = 30 + GPIO_PC00;
|
||||
const int GPIO_PC31 = 31 + GPIO_PC00;
|
||||
|
||||
const int GPIO_PD00 = 96;
|
||||
const int GPIO_PD01 = 1 + GPIO_PD00;
|
||||
const int GPIO_PD02 = 2 + GPIO_PD00;
|
||||
const int GPIO_PD03 = 3 + GPIO_PD00;
|
||||
const int GPIO_PD04 = 4 + GPIO_PD00;
|
||||
const int GPIO_PD05 = 5 + GPIO_PD00;
|
||||
const int GPIO_PD06 = 6 + GPIO_PD00;
|
||||
const int GPIO_PD07 = 7 + GPIO_PD00;
|
||||
const int GPIO_PD08 = 8 + GPIO_PD00;
|
||||
const int GPIO_PD09 = 9 + GPIO_PD00;
|
||||
const int GPIO_PD10 = 10 + GPIO_PD00;
|
||||
const int GPIO_PD11 = 11 + GPIO_PD00;
|
||||
const int GPIO_PD12 = 12 + GPIO_PD00;
|
||||
const int GPIO_PD13 = 13 + GPIO_PD00;
|
||||
const int GPIO_PD14 = 14 + GPIO_PD00;
|
||||
const int GPIO_PD15 = 15 + GPIO_PD00;
|
||||
const int GPIO_PD16 = 16 + GPIO_PD00;
|
||||
const int GPIO_PD17 = 17 + GPIO_PD00;
|
||||
const int GPIO_PD18 = 18 + GPIO_PD00;
|
||||
const int GPIO_PD19 = 19 + GPIO_PD00;
|
||||
const int GPIO_PD20 = 20 + GPIO_PD00;
|
||||
const int GPIO_PD21 = 21 + GPIO_PD00;
|
||||
const int GPIO_PD22 = 22 + GPIO_PD00;
|
||||
const int GPIO_PD23 = 23 + GPIO_PD00;
|
||||
const int GPIO_PD24 = 24 + GPIO_PD00;
|
||||
const int GPIO_PD25 = 25 + GPIO_PD00;
|
||||
const int GPIO_PD26 = 26 + GPIO_PD00;
|
||||
const int GPIO_PD27 = 27 + GPIO_PD00;
|
||||
const int GPIO_PD28 = 28 + GPIO_PD00;
|
||||
const int GPIO_PD29 = 29 + GPIO_PD00;
|
||||
const int GPIO_PD30 = 30 + GPIO_PD00;
|
||||
const int GPIO_PD31 = 31 + GPIO_PD00;
|
||||
|
||||
const int GPIO_PE00 = 128;
|
||||
const int GPIO_PE01 = 1 + GPIO_PE00;
|
||||
const int GPIO_PE02 = 2 + GPIO_PE00;
|
||||
const int GPIO_PE03 = 3 + GPIO_PE00;
|
||||
const int GPIO_PE04 = 4 + GPIO_PE00;
|
||||
const int GPIO_PE05 = 5 + GPIO_PE00;
|
||||
const int GPIO_PE06 = 6 + GPIO_PE00;
|
||||
const int GPIO_PE07 = 7 + GPIO_PE00;
|
||||
const int GPIO_PE08 = 8 + GPIO_PE00;
|
||||
const int GPIO_PE09 = 9 + GPIO_PE00;
|
||||
const int GPIO_PE10 = 10 + GPIO_PE00;
|
||||
const int GPIO_PE11 = 11 + GPIO_PE00;
|
||||
const int GPIO_PE12 = 12 + GPIO_PE00;
|
||||
const int GPIO_PE13 = 13 + GPIO_PE00;
|
||||
const int GPIO_PE14 = 14 + GPIO_PE00;
|
||||
const int GPIO_PE15 = 15 + GPIO_PE00;
|
||||
const int GPIO_PE16 = 16 + GPIO_PE00;
|
||||
const int GPIO_PE17 = 17 + GPIO_PE00;
|
||||
const int GPIO_PE18 = 18 + GPIO_PE00;
|
||||
const int GPIO_PE19 = 19 + GPIO_PE00;
|
||||
const int GPIO_PE20 = 20 + GPIO_PE00;
|
||||
const int GPIO_PE21 = 21 + GPIO_PE00;
|
||||
const int GPIO_PE22 = 22 + GPIO_PE00;
|
||||
const int GPIO_PE23 = 23 + GPIO_PE00;
|
||||
const int GPIO_PE24 = 24 + GPIO_PE00;
|
||||
const int GPIO_PE25 = 25 + GPIO_PE00;
|
||||
const int GPIO_PE26 = 26 + GPIO_PE00;
|
||||
const int GPIO_PE27 = 27 + GPIO_PE00;
|
||||
const int GPIO_PE28 = 28 + GPIO_PE00;
|
||||
const int GPIO_PE29 = 29 + GPIO_PE00;
|
||||
const int GPIO_PE30 = 30 + GPIO_PE00;
|
||||
const int GPIO_PE31 = 31 + GPIO_PE00;
|
||||
|
||||
const int GPIO_PG00 = 192;
|
||||
const int GPIO_PG01 = 1 + GPIO_PG00;
|
||||
const int GPIO_PG02 = 2 + GPIO_PG00;
|
||||
const int GPIO_PG03 = 3 + GPIO_PG00;
|
||||
const int GPIO_PG04 = 4 + GPIO_PG00;
|
||||
const int GPIO_PG05 = 5 + GPIO_PG00;
|
||||
const int GPIO_PG06 = 6 + GPIO_PG00;
|
||||
const int GPIO_PG07 = 7 + GPIO_PG00;
|
||||
const int GPIO_PG08 = 8 + GPIO_PG00;
|
||||
const int GPIO_PG09 = 9 + GPIO_PG00;
|
||||
const int GPIO_PG10 = 10 + GPIO_PG00;
|
||||
const int GPIO_PG11 = 11 + GPIO_PG00;
|
||||
const int GPIO_PG12 = 12 + GPIO_PG00;
|
||||
const int GPIO_PG13 = 13 + GPIO_PG00;
|
||||
const int GPIO_PG14 = 14 + GPIO_PG00;
|
||||
const int GPIO_PG15 = 15 + GPIO_PG00;
|
||||
const int GPIO_PG16 = 16 + GPIO_PG00;
|
||||
const int GPIO_PG17 = 17 + GPIO_PG00;
|
||||
const int GPIO_PG18 = 18 + GPIO_PG00;
|
||||
const int GPIO_PG19 = 19 + GPIO_PG00;
|
||||
const int GPIO_PG20 = 20 + GPIO_PG00;
|
||||
const int GPIO_PG21 = 21 + GPIO_PG00;
|
||||
const int GPIO_PG22 = 22 + GPIO_PG00;
|
||||
const int GPIO_PG23 = 23 + GPIO_PG00;
|
||||
const int GPIO_PG24 = 24 + GPIO_PG00;
|
||||
const int GPIO_PG25 = 25 + GPIO_PG00;
|
||||
const int GPIO_PG26 = 26 + GPIO_PG00;
|
||||
const int GPIO_PG27 = 27 + GPIO_PG00;
|
||||
const int GPIO_PG28 = 28 + GPIO_PG00;
|
||||
const int GPIO_PG29 = 29 + GPIO_PG00;
|
||||
const int GPIO_PG30 = 30 + GPIO_PG00;
|
||||
const int GPIO_PG31 = 31 + GPIO_PG00;
|
||||
|
||||
const int GPIO_PH00 = 224;
|
||||
const int GPIO_PH01 = 1 + GPIO_PH00;
|
||||
const int GPIO_PH02 = 2 + GPIO_PH00;
|
||||
const int GPIO_PH03 = 3 + GPIO_PH00;
|
||||
const int GPIO_PH04 = 4 + GPIO_PH00;
|
||||
const int GPIO_PH05 = 5 + GPIO_PH00;
|
||||
const int GPIO_PH06 = 6 + GPIO_PH00;
|
||||
const int GPIO_PH07 = 7 + GPIO_PH00;
|
||||
const int GPIO_PH08 = 8 + GPIO_PH00;
|
||||
const int GPIO_PH09 = 9 + GPIO_PH00;
|
||||
const int GPIO_PH10 = 10 + GPIO_PH00;
|
||||
const int GPIO_PH11 = 11 + GPIO_PH00;
|
||||
const int GPIO_PH12 = 12 + GPIO_PH00;
|
||||
const int GPIO_PH13 = 13 + GPIO_PH00;
|
||||
const int GPIO_PH14 = 14 + GPIO_PH00;
|
||||
const int GPIO_PH15 = 15 + GPIO_PH00;
|
||||
const int GPIO_PH16 = 16 + GPIO_PH00;
|
||||
const int GPIO_PH17 = 17 + GPIO_PH00;
|
||||
const int GPIO_PH18 = 18 + GPIO_PH00;
|
||||
const int GPIO_PH19 = 19 + GPIO_PH00;
|
||||
const int GPIO_PH20 = 20 + GPIO_PH00;
|
||||
const int GPIO_PH21 = 21 + GPIO_PH00;
|
||||
const int GPIO_PH22 = 22 + GPIO_PH00;
|
||||
const int GPIO_PH23 = 23 + GPIO_PH00;
|
||||
const int GPIO_PH24 = 24 + GPIO_PH00;
|
||||
const int GPIO_PH25 = 25 + GPIO_PH00;
|
||||
const int GPIO_PH26 = 26 + GPIO_PH00;
|
||||
const int GPIO_PH27 = 27 + GPIO_PH00;
|
||||
const int GPIO_PH28 = 28 + GPIO_PH00;
|
||||
const int GPIO_PH29 = 29 + GPIO_PH00;
|
||||
const int GPIO_PH30 = 30 + GPIO_PH00;
|
||||
const int GPIO_PH31 = 31 + GPIO_PH00;
|
||||
|
||||
const int GPIO_PI00 = 256;
|
||||
const int GPIO_PI01 = 1 + GPIO_PI00;
|
||||
const int GPIO_PI02 = 2 + GPIO_PI00;
|
||||
const int GPIO_PI03 = 3 + GPIO_PI00;
|
||||
const int GPIO_PI04 = 4 + GPIO_PI00;
|
||||
const int GPIO_PI05 = 5 + GPIO_PI00;
|
||||
const int GPIO_PI06 = 6 + GPIO_PI00;
|
||||
const int GPIO_PI07 = 7 + GPIO_PI00;
|
||||
const int GPIO_PI08 = 8 + GPIO_PI00;
|
||||
const int GPIO_PI09 = 9 + GPIO_PI00;
|
||||
const int GPIO_PI10 = 10 + GPIO_PI00;
|
||||
const int GPIO_PI11 = 11 + GPIO_PI00;
|
||||
const int GPIO_PI12 = 12 + GPIO_PI00;
|
||||
const int GPIO_PI13 = 13 + GPIO_PI00;
|
||||
const int GPIO_PI14 = 14 + GPIO_PI00;
|
||||
const int GPIO_PI15 = 15 + GPIO_PI00;
|
||||
const int GPIO_PI16 = 16 + GPIO_PI00;
|
||||
const int GPIO_PI17 = 17 + GPIO_PI00;
|
||||
const int GPIO_PI18 = 18 + GPIO_PI00;
|
||||
const int GPIO_PI19 = 19 + GPIO_PI00;
|
||||
const int GPIO_PI20 = 20 + GPIO_PI00;
|
||||
const int GPIO_PI21 = 21 + GPIO_PI00;
|
||||
const int GPIO_PI22 = 22 + GPIO_PI00;
|
||||
const int GPIO_PI23 = 23 + GPIO_PI00;
|
||||
const int GPIO_PI24 = 24 + GPIO_PI00;
|
||||
const int GPIO_PI25 = 25 + GPIO_PI00;
|
||||
const int GPIO_PI26 = 26 + GPIO_PI00;
|
||||
const int GPIO_PI27 = 27 + GPIO_PI00;
|
||||
const int GPIO_PI28 = 28 + GPIO_PI00;
|
||||
const int GPIO_PI29 = 29 + GPIO_PI00;
|
||||
const int GPIO_PI30 = 30 + GPIO_PI00;
|
||||
const int GPIO_PI31 = 31 + GPIO_PI00;
|
||||
|
||||
const int GPIO_PL00 = 352;
|
||||
const int GPIO_PL01 = 1 + GPIO_PL00;
|
||||
const int GPIO_PL02 = 2 + GPIO_PL00;
|
||||
const int GPIO_PL03 = 3 + GPIO_PL00;
|
||||
const int GPIO_PL04 = 4 + GPIO_PL00;
|
||||
const int GPIO_PL05 = 5 + GPIO_PL00;
|
||||
const int GPIO_PL06 = 6 + GPIO_PL00;
|
||||
const int GPIO_PL07 = 7 + GPIO_PL00;
|
||||
const int GPIO_PL08 = 8 + GPIO_PL00;
|
||||
const int GPIO_PL09 = 9 + GPIO_PL00;
|
||||
const int GPIO_PL10 = 10 + GPIO_PL00;
|
||||
const int GPIO_PL11 = 11 + GPIO_PL00;
|
||||
const int GPIO_PL12 = 12 + GPIO_PL00;
|
||||
const int GPIO_PL13 = 13 + GPIO_PL00;
|
||||
const int GPIO_PL14 = 14 + GPIO_PL00;
|
||||
const int GPIO_PL15 = 15 + GPIO_PL00;
|
||||
const int GPIO_PL16 = 16 + GPIO_PL00;
|
||||
const int GPIO_PL17 = 17 + GPIO_PL00;
|
||||
const int GPIO_PL18 = 18 + GPIO_PL00;
|
||||
const int GPIO_PL19 = 19 + GPIO_PL00;
|
||||
const int GPIO_PL20 = 20 + GPIO_PL00;
|
||||
const int GPIO_PL21 = 21 + GPIO_PL00;
|
||||
const int GPIO_PL22 = 22 + GPIO_PL00;
|
||||
const int GPIO_PL23 = 23 + GPIO_PL00;
|
||||
const int GPIO_PL24 = 24 + GPIO_PL00;
|
||||
const int GPIO_PL25 = 25 + GPIO_PL00;
|
||||
const int GPIO_PL26 = 26 + GPIO_PL00;
|
||||
const int GPIO_PL27 = 27 + GPIO_PL00;
|
||||
const int GPIO_PL28 = 28 + GPIO_PL00;
|
||||
const int GPIO_PL29 = 29 + GPIO_PL00;
|
||||
const int GPIO_PL30 = 30 + GPIO_PL00;
|
||||
const int GPIO_PL31 = 31 + GPIO_PL00;
|
||||
|
||||
const int GPIO_PM00 = 384;
|
||||
const int GPIO_PM01 = 1 + GPIO_PM00;
|
||||
const int GPIO_PM02 = 2 + GPIO_PM00;
|
||||
const int GPIO_PM03 = 3 + GPIO_PM00;
|
||||
const int GPIO_PM04 = 4 + GPIO_PM00;
|
||||
const int GPIO_PM05 = 5 + GPIO_PM00;
|
||||
const int GPIO_PM06 = 6 + GPIO_PM00;
|
||||
const int GPIO_PM07 = 7 + GPIO_PM00;
|
||||
const int GPIO_PM08 = 8 + GPIO_PM00;
|
||||
const int GPIO_PM09 = 9 + GPIO_PM00;
|
||||
const int GPIO_PM10 = 10 + GPIO_PM00;
|
||||
const int GPIO_PM11 = 11 + GPIO_PM00;
|
||||
const int GPIO_PM12 = 12 + GPIO_PM00;
|
||||
const int GPIO_PM13 = 13 + GPIO_PM00;
|
||||
const int GPIO_PM14 = 14 + GPIO_PM00;
|
||||
const int GPIO_PM15 = 15 + GPIO_PM00;
|
||||
const int GPIO_PM16 = 16 + GPIO_PM00;
|
||||
const int GPIO_PM17 = 17 + GPIO_PM00;
|
||||
const int GPIO_PM18 = 18 + GPIO_PM00;
|
||||
const int GPIO_PM19 = 19 + GPIO_PM00;
|
||||
const int GPIO_PM20 = 20 + GPIO_PM00;
|
||||
const int GPIO_PM21 = 21 + GPIO_PM00;
|
||||
const int GPIO_PM22 = 22 + GPIO_PM00;
|
||||
const int GPIO_PM23 = 23 + GPIO_PM00;
|
||||
const int GPIO_PM24 = 24 + GPIO_PM00;
|
||||
const int GPIO_PM25 = 25 + GPIO_PM00;
|
||||
const int GPIO_PM26 = 26 + GPIO_PM00;
|
||||
const int GPIO_PM27 = 27 + GPIO_PM00;
|
||||
const int GPIO_PM28 = 28 + GPIO_PM00;
|
||||
const int GPIO_PM29 = 29 + GPIO_PM00;
|
||||
const int GPIO_PM30 = 30 + GPIO_PM00;
|
||||
const int GPIO_PM31 = 31 + GPIO_PM00;
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2014-2017 Banana Pi
|
||||
Updates Copyright (C) 2022 akuker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/pi_defs/bpi-gpio.h"
|
||||
|
||||
/* The following define the mapping of the Banana Pi CPU pins to the logical
|
||||
* GPIO numbers. The GPIO numbers are used by the software to set/configure
|
||||
* the CPU registers. */
|
||||
const int BPI_M2P_01 = GPIO_NOT_USED;
|
||||
const int BPI_M2P_03 = GPIO_PA12;
|
||||
const int BPI_M2P_05 = GPIO_PA11;
|
||||
const int BPI_M2P_07 = GPIO_PA06;
|
||||
const int BPI_M2P_09 = GPIO_NOT_USED;
|
||||
const int BPI_M2P_11 = GPIO_PA01;
|
||||
const int BPI_M2P_13 = GPIO_PA00;
|
||||
const int BPI_M2P_15 = GPIO_PA03;
|
||||
const int BPI_M2P_17 = GPIO_NOT_USED;
|
||||
const int BPI_M2P_19 = GPIO_PC00;
|
||||
const int BPI_M2P_21 = GPIO_PC01;
|
||||
const int BPI_M2P_23 = GPIO_PC02;
|
||||
const int BPI_M2P_25 = GPIO_NOT_USED;
|
||||
const int BPI_M2P_27 = GPIO_PA19;
|
||||
const int BPI_M2P_29 = GPIO_PA07;
|
||||
const int BPI_M2P_31 = GPIO_PA08;
|
||||
const int BPI_M2P_33 = GPIO_PA09;
|
||||
const int BPI_M2P_35 = GPIO_PA10;
|
||||
const int BPI_M2P_37 = GPIO_PA17;
|
||||
const int BPI_M2P_39 = GPIO_NOT_USED;
|
||||
|
||||
const int BPI_M2P_02 = GPIO_NOT_USED;
|
||||
const int BPI_M2P_04 = GPIO_NOT_USED;
|
||||
const int BPI_M2P_06 = GPIO_NOT_USED;
|
||||
const int BPI_M2P_08 = GPIO_PA13;
|
||||
const int BPI_M2P_10 = GPIO_PA14;
|
||||
const int BPI_M2P_12 = GPIO_PA16;
|
||||
const int BPI_M2P_14 = GPIO_NOT_USED;
|
||||
const int BPI_M2P_16 = GPIO_PA15;
|
||||
const int BPI_M2P_18 = GPIO_PC04;
|
||||
const int BPI_M2P_20 = GPIO_NOT_USED;
|
||||
const int BPI_M2P_22 = GPIO_PA02;
|
||||
const int BPI_M2P_24 = GPIO_PC03;
|
||||
const int BPI_M2P_26 = GPIO_PC07;
|
||||
const int BPI_M2P_28 = GPIO_PA18;
|
||||
const int BPI_M2P_30 = GPIO_NOT_USED;
|
||||
const int BPI_M2P_32 = GPIO_PL02;
|
||||
const int BPI_M2P_34 = GPIO_NOT_USED;
|
||||
const int BPI_M2P_36 = GPIO_PL04;
|
||||
const int BPI_M2P_38 = GPIO_PA21;
|
||||
const int BPI_M2P_40 = GPIO_PA20;
|
|
@ -10,26 +10,18 @@
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "sbc_version.h"
|
||||
#include "shared/log.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
SBC_Version::sbc_version_type SBC_Version::m_sbc_version = sbc_version_type::sbc_unknown;
|
||||
SBC_Version::sbc_version_type SBC_Version::sbc_version = sbc_version_type::sbc_unknown;
|
||||
|
||||
// TODO: THESE NEED TO BE VALIDATED!!!!
|
||||
const std::string SBC_Version::m_str_raspberry_pi_1 = "Raspberry Pi 1";
|
||||
const std::string SBC_Version::m_str_raspberry_pi_2_3 = "Raspberry Pi 2/3";
|
||||
const std::string SBC_Version::m_str_raspberry_pi_4 = "Raspberry Pi 4";
|
||||
const std::string SBC_Version::m_str_bananapi_m1_plus = "Banana Pi M1 Plus";
|
||||
const std::string SBC_Version::m_str_bananapi_m2_berry = "Banana Pi M2 Berry/Ultra";
|
||||
const std::string SBC_Version::m_str_bananapi_m2_zero = "Banana Pi M2 Zero";
|
||||
const std::string SBC_Version::m_str_bananapi_m2_plus = "Banana Pi BPI-M2-Plus H3";
|
||||
const std::string SBC_Version::m_str_bananapi_m3 = "Banana Pi M3";
|
||||
const std::string SBC_Version::m_str_bananapi_m4 = "Banana Pi M4";
|
||||
const std::string SBC_Version::m_str_bananapi_m5 = "Banana Pi M5";
|
||||
const std::string SBC_Version::m_str_bananapi_m64 = "Banana Pi M64";
|
||||
const std::string SBC_Version::m_str_unknown_sbc = "Unknown SBC";
|
||||
const string SBC_Version::str_raspberry_pi_1 = "Raspberry Pi 1";
|
||||
const string SBC_Version::str_raspberry_pi_2_3 = "Raspberry Pi 2/3";
|
||||
const string SBC_Version::str_raspberry_pi_4 = "Raspberry Pi 4";
|
||||
const string SBC_Version::str_unknown_sbc = "Unknown SBC";
|
||||
|
||||
// The strings in this table should align with the 'model' embedded
|
||||
// in the device tree. This can be aquired by running:
|
||||
|
@ -39,57 +31,41 @@ const std::string SBC_Version::m_str_unknown_sbc = "Unknown SBC";
|
|||
// "Raspberry Pi 4 Model B" will match with both of the following:
|
||||
// - Raspberry Pi 4 Model B Rev 1.4
|
||||
// - Raspberry Pi 4 Model B Rev 1.3
|
||||
const std::map<std::string, SBC_Version::sbc_version_type, std::less<>> SBC_Version::m_proc_device_tree_mapping = {
|
||||
// TODO Is there a better way to detect the Pi type than relying on strings?
|
||||
const map<string, SBC_Version::sbc_version_type, less<>> SBC_Version::proc_device_tree_mapping = {
|
||||
{"Raspberry Pi 1 Model ", sbc_version_type::sbc_raspberry_pi_1},
|
||||
{"Raspberry Pi 2 Model ", sbc_version_type::sbc_raspberry_pi_2_3},
|
||||
{"Raspberry Pi 3 Model ", sbc_version_type::sbc_raspberry_pi_2_3},
|
||||
{"Raspberry Pi 4 Model ", sbc_version_type::sbc_raspberry_pi_4},
|
||||
{"Raspberry Pi 400 ", sbc_version_type::sbc_raspberry_pi_4},
|
||||
{"Raspberry Pi Zero W", sbc_version_type::sbc_raspberry_pi_1},
|
||||
{"Raspberry Pi Zero", sbc_version_type::sbc_raspberry_pi_1},
|
||||
{"Banana Pi BPI-M2-Zero ", sbc_version_type::sbc_bananapi_m2_zero},
|
||||
{"Banana Pi BPI-M2-Ultra ", sbc_version_type::sbc_bananapi_m2_berry},
|
||||
{"Banana Pi BPI-M2-Plus H3", sbc_version_type::sbc_bananapi_m2_plus},
|
||||
{"Banana Pi M2 Berry ", sbc_version_type::sbc_bananapi_m2_berry},
|
||||
// sbc_bananapi_m3, TBD....
|
||||
// sbc_bananapi_m4,
|
||||
{"Raspberry Pi Zero", sbc_version_type::sbc_raspberry_pi_1}
|
||||
};
|
||||
|
||||
const std::string SBC_Version::m_device_tree_model_path = "/proc/device-tree/model";
|
||||
const string SBC_Version::m_device_tree_model_path = "/proc/device-tree/model";
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Convert the SBC Version to a printable string
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
const std::string *SBC_Version::GetString()
|
||||
string SBC_Version::GetAsString()
|
||||
{
|
||||
switch (m_sbc_version) {
|
||||
switch (sbc_version) {
|
||||
case sbc_version_type::sbc_raspberry_pi_1:
|
||||
return &m_str_raspberry_pi_1;
|
||||
return str_raspberry_pi_1;
|
||||
case sbc_version_type::sbc_raspberry_pi_2_3:
|
||||
return &m_str_raspberry_pi_2_3;
|
||||
return str_raspberry_pi_2_3;
|
||||
case sbc_version_type::sbc_raspberry_pi_4:
|
||||
return &m_str_raspberry_pi_4;
|
||||
case sbc_version_type::sbc_bananapi_m2_berry:
|
||||
return &m_str_bananapi_m2_berry;
|
||||
case sbc_version_type::sbc_bananapi_m2_zero:
|
||||
return &m_str_bananapi_m2_zero;
|
||||
case sbc_version_type::sbc_bananapi_m2_plus:
|
||||
return &m_str_bananapi_m2_plus;
|
||||
case sbc_version_type::sbc_bananapi_m3:
|
||||
return &m_str_bananapi_m3;
|
||||
case sbc_version_type::sbc_bananapi_m4:
|
||||
return &m_str_bananapi_m4;
|
||||
return str_raspberry_pi_4;
|
||||
default:
|
||||
LOGERROR("Unknown type of sbc detected: %d", static_cast<int>(m_sbc_version))
|
||||
return &m_str_unknown_sbc;
|
||||
return str_unknown_sbc;
|
||||
}
|
||||
}
|
||||
|
||||
SBC_Version::sbc_version_type SBC_Version::GetSbcVersion()
|
||||
{
|
||||
return m_sbc_version;
|
||||
return sbc_version;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -100,71 +76,43 @@ SBC_Version::sbc_version_type SBC_Version::GetSbcVersion()
|
|||
//---------------------------------------------------------------------------
|
||||
void SBC_Version::Init()
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
std::string device_tree_model;
|
||||
|
||||
const std::ifstream input_stream(SBC_Version::m_device_tree_model_path);
|
||||
ifstream input_stream(SBC_Version::m_device_tree_model_path);
|
||||
|
||||
if (input_stream.fail()) {
|
||||
#if defined(__x86_64__) || defined(__X86__)
|
||||
// We expect this to fail on x86
|
||||
LOGINFO("Detected device %s", GetString()->c_str())
|
||||
m_sbc_version = sbc_version_type::sbc_unknown;
|
||||
spdlog::warn("Detected " + GetAsString());
|
||||
sbc_version = sbc_version_type::sbc_unknown;
|
||||
return;
|
||||
#else
|
||||
LOGERROR("Failed to open %s. Are you running as root?", SBC_Version::m_device_tree_model_path.c_str())
|
||||
throw std::invalid_argument("Failed to open /proc/device-tree/model");
|
||||
spdlog::error("Failed to open " + SBC_Version::m_device_tree_model_path + ". Are you running as root?");
|
||||
throw invalid_argument("Failed to open /proc/device-tree/model");
|
||||
#endif
|
||||
}
|
||||
|
||||
std::stringstream str_buffer;
|
||||
stringstream str_buffer;
|
||||
str_buffer << input_stream.rdbuf();
|
||||
device_tree_model = str_buffer.str();
|
||||
const string device_tree_model = str_buffer.str();
|
||||
|
||||
for (const auto &[key, value] : m_proc_device_tree_mapping) {
|
||||
if (device_tree_model.rfind(key, 0) == 0) {
|
||||
m_sbc_version = value;
|
||||
LOGINFO("Detected device %s", GetString()->c_str())
|
||||
return;
|
||||
}
|
||||
for (const auto& [key, value] : proc_device_tree_mapping) {
|
||||
if (device_tree_model.starts_with(key)) {
|
||||
sbc_version = value;
|
||||
spdlog::info("Detected " + GetAsString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOGERROR("%s Unable to determine single board computer type. Defaulting to Raspberry Pi 4", __PRETTY_FUNCTION__)
|
||||
m_sbc_version = sbc_version_type::sbc_raspberry_pi_4;
|
||||
|
||||
sbc_version = sbc_version_type::sbc_raspberry_pi_4;
|
||||
spdlog::error("Unable to determine single board computer type. Defaulting to Raspberry Pi 4");
|
||||
}
|
||||
|
||||
bool SBC_Version::IsRaspberryPi()
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
switch (m_sbc_version) {
|
||||
switch (sbc_version) {
|
||||
case sbc_version_type::sbc_raspberry_pi_1:
|
||||
case sbc_version_type::sbc_raspberry_pi_2_3:
|
||||
case sbc_version_type::sbc_raspberry_pi_4:
|
||||
return true;
|
||||
case sbc_version_type::sbc_bananapi_m2_berry:
|
||||
case sbc_version_type::sbc_bananapi_m2_zero:
|
||||
case sbc_version_type::sbc_bananapi_m2_plus:
|
||||
case sbc_version_type::sbc_bananapi_m3:
|
||||
case sbc_version_type::sbc_bananapi_m4:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SBC_Version::IsBananaPi()
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
switch (m_sbc_version) {
|
||||
case sbc_version_type::sbc_raspberry_pi_1:
|
||||
case sbc_version_type::sbc_raspberry_pi_2_3:
|
||||
case sbc_version_type::sbc_raspberry_pi_4:
|
||||
return false;
|
||||
case sbc_version_type::sbc_bananapi_m2_berry:
|
||||
case sbc_version_type::sbc_bananapi_m2_zero:
|
||||
case sbc_version_type::sbc_bananapi_m2_plus:
|
||||
case sbc_version_type::sbc_bananapi_m3:
|
||||
case sbc_version_type::sbc_bananapi_m4:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -174,11 +122,10 @@ bool SBC_Version::IsBananaPi()
|
|||
// (imported from bcm_host.c)
|
||||
uint32_t SBC_Version::GetDeviceTreeRanges(const char *filename, uint32_t offset)
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
uint32_t address = ~0;
|
||||
if (FILE *fp = fopen(filename, "rb"); fp) {
|
||||
fseek(fp, offset, SEEK_SET);
|
||||
if (std::array<uint8_t, 4> buf; fread(buf.data(), 1, buf.size(), fp) == buf.size()) {
|
||||
if (array<uint8_t, 4> buf; fread(buf.data(), 1, buf.size(), fp) == buf.size()) {
|
||||
address = (int)buf[0] << 24 | (int)buf[1] << 16 | (int)buf[2] << 8 | (int)buf[3] << 0;
|
||||
}
|
||||
fclose(fp);
|
||||
|
@ -189,15 +136,12 @@ uint32_t SBC_Version::GetDeviceTreeRanges(const char *filename, uint32_t offset)
|
|||
#if defined __linux__
|
||||
uint32_t SBC_Version::GetPeripheralAddress(void)
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
uint32_t address = GetDeviceTreeRanges("/proc/device-tree/soc/ranges", 4);
|
||||
if (address == 0) {
|
||||
address = GetDeviceTreeRanges("/proc/device-tree/soc/ranges", 8);
|
||||
}
|
||||
address = (address == (uint32_t)~0) ? 0x20000000 : address;
|
||||
|
||||
LOGDEBUG("Peripheral address : 0x%8x\n", address)
|
||||
|
||||
return address;
|
||||
}
|
||||
#elif defined __NetBSD__
|
||||
|
@ -215,7 +159,6 @@ uint32_t SBC_Version::GetPeripheralAddress(void)
|
|||
// Use BCM2835 address
|
||||
address = 0x20000000;
|
||||
}
|
||||
LOGDEBUG("Peripheral address : 0x%lx\n", address);
|
||||
return address;
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Single Board Computer Versions
|
||||
|
@ -27,16 +30,7 @@ class SBC_Version
|
|||
sbc_unknown = 0,
|
||||
sbc_raspberry_pi_1,
|
||||
sbc_raspberry_pi_2_3,
|
||||
sbc_raspberry_pi_4,
|
||||
sbc_bananapi_m1_plus,
|
||||
sbc_bananapi_m2_ultra,
|
||||
sbc_bananapi_m2_berry,
|
||||
sbc_bananapi_m2_zero,
|
||||
sbc_bananapi_m2_plus,
|
||||
sbc_bananapi_m3,
|
||||
sbc_bananapi_m4,
|
||||
sbc_bananapi_m5,
|
||||
sbc_bananapi_m64,
|
||||
sbc_raspberry_pi_4
|
||||
};
|
||||
|
||||
SBC_Version() = delete;
|
||||
|
@ -47,32 +41,22 @@ class SBC_Version
|
|||
static sbc_version_type GetSbcVersion();
|
||||
|
||||
static bool IsRaspberryPi();
|
||||
static bool IsBananaPi();
|
||||
|
||||
static const std::string *GetString();
|
||||
static string GetAsString();
|
||||
|
||||
static uint32_t GetPeripheralAddress();
|
||||
|
||||
private:
|
||||
static sbc_version_type m_sbc_version;
|
||||
static sbc_version_type sbc_version;
|
||||
|
||||
static const std::string m_str_raspberry_pi_1;
|
||||
static const std::string m_str_raspberry_pi_2_3;
|
||||
static const std::string m_str_raspberry_pi_4;
|
||||
static const std::string m_str_bananapi_m1_plus;
|
||||
static const std::string m_str_bananapi_m2_ultra;
|
||||
static const std::string m_str_bananapi_m2_berry;
|
||||
static const std::string m_str_bananapi_m2_zero;
|
||||
static const std::string m_str_bananapi_m2_plus;
|
||||
static const std::string m_str_bananapi_m3;
|
||||
static const std::string m_str_bananapi_m4;
|
||||
static const std::string m_str_bananapi_m5;
|
||||
static const std::string m_str_bananapi_m64;
|
||||
static const std::string m_str_unknown_sbc;
|
||||
static const string str_raspberry_pi_1;
|
||||
static const string str_raspberry_pi_2_3;
|
||||
static const string str_raspberry_pi_4;
|
||||
static const string str_unknown_sbc;
|
||||
|
||||
static const std::map<std::string, sbc_version_type, std::less<>> m_proc_device_tree_mapping;
|
||||
static const map<std::string, sbc_version_type, less<>> proc_device_tree_mapping;
|
||||
|
||||
static const std::string m_device_tree_model_path;
|
||||
static const string m_device_tree_model_path;
|
||||
|
||||
static uint32_t GetDeviceTreeRanges(const char *filename, uint32_t offset);
|
||||
};
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 akuker
|
||||
//
|
||||
// [ Utility functions for working with Allwinner CPUs ]
|
||||
//
|
||||
// This should include generic functions that can be applicable to
|
||||
// different variants of the SunXI (Allwinner) SoCs
|
||||
//
|
||||
// Large portions of this functionality were derived from c_gpio.c, which
|
||||
// is part of the RPI.GPIO library available here:
|
||||
// https://github.com/BPI-SINOVOIP/RPi.GPIO/blob/master/source/c_gpio.c
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "hal/sunxi_utils.h"
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const string BLACK = "\033[30m"; /* Black */
|
||||
static const string RED = "\033[31m"; /* Red */
|
||||
static const string GREEN = "\033[32m"; /* Green */
|
||||
static const string YELLOW = "\033[33m"; /* Yellow */
|
||||
static const string BLUE = "\033[34m"; /* Blue */
|
||||
static const string MAGENTA = "\033[35m"; /* Magenta */
|
||||
static const string CYAN = "\033[36m"; /* Cyan */
|
||||
static const string WHITE = "\033[37m"; /* White */
|
||||
|
||||
// TODO: this is only a debug function that will be removed at a later date.....
|
||||
void dump_gpio_registers(const SunXI::sunxi_gpio_reg_t *regs)
|
||||
{
|
||||
printf("%s--- GPIO BANK 0 CFG: %08X %08X %08X %08X\n", CYAN.c_str(), regs->gpio_bank[0].CFG[0],
|
||||
regs->gpio_bank[0].CFG[1], regs->gpio_bank[0].CFG[2], regs->gpio_bank[0].CFG[3]);
|
||||
|
||||
printf("--- Dat: (%08X) DRV: %08X %08X\n", regs->gpio_bank[0].DAT, regs->gpio_bank[0].DRV[0],
|
||||
regs->gpio_bank[0].DRV[1]);
|
||||
printf("--- Pull: %08X %08x\n", regs->gpio_bank[0].PULL[0], regs->gpio_bank[0].PULL[1]);
|
||||
|
||||
printf("--- GPIO INT CFG: %08X %08X %08X\n", regs->gpio_int.CFG[0], regs->gpio_int.CFG[1], regs->gpio_int.CFG[2]);
|
||||
printf("--- CTL: (%08X) STA: %08X DEB: %08X\n %s", regs->gpio_int.CTL, regs->gpio_int.STA, regs->gpio_int.DEB,
|
||||
WHITE.c_str());
|
||||
}
|
|
@ -1,189 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 akuker
|
||||
//
|
||||
// [ Utility functions for working with Allwinner CPUs ]
|
||||
//
|
||||
// This should include generic functions that can be applicable to
|
||||
// different variants of the SunXI (Allwinner) SoCs
|
||||
//
|
||||
// Large portions of this functionality were derived from c_gpio.c, which
|
||||
// is part of the RPI.GPIO library available here:
|
||||
// https://github.com/BPI-SINOVOIP/RPi.GPIO/blob/master/source/c_gpio.c
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#ifndef __arm__
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
class SunXI
|
||||
{
|
||||
public:
|
||||
static inline int GPIO_BANK(int pin)
|
||||
{
|
||||
return (pin >> 5);
|
||||
}
|
||||
static inline int GPIO_NUM(int pin)
|
||||
{
|
||||
return (pin & 0x1F);
|
||||
}
|
||||
static inline int GPIO_CFG_INDEX(int pin)
|
||||
{
|
||||
return ((pin & 0x1F) >> 3);
|
||||
}
|
||||
static inline int GPIO_CFG_OFFSET(int pin)
|
||||
{
|
||||
return (((pin & 0x1F) & 0x7) << 2);
|
||||
}
|
||||
static inline int GPIO_PUL_INDEX(int pin)
|
||||
{
|
||||
return ((pin & 0x1F) >> 4);
|
||||
}
|
||||
static inline int GPIO_PUL_OFFSET(int pin)
|
||||
{
|
||||
return ((pin & 0x0F) << 1);
|
||||
}
|
||||
static inline int GPIO_DRV_INDEX(int pin)
|
||||
{
|
||||
return ((pin & 0x1F) >> 4);
|
||||
}
|
||||
static inline int GPIO_DRV_OFFSET(int pin)
|
||||
{
|
||||
return ((pin & 0x0F) << 1);
|
||||
}
|
||||
|
||||
static inline void short_wait(void)
|
||||
{
|
||||
for (int i = 0; i < 150; i++) {
|
||||
#ifndef __arm__
|
||||
// Timing doesn't really matter if we're not on ARM.
|
||||
// The SunXI SoCs are all ARM-based.
|
||||
const timespec ts = {.tv_sec = 0, .tv_nsec = 1};
|
||||
nanosleep(&ts, nullptr);
|
||||
#else
|
||||
// wait 150 cycles
|
||||
asm volatile("nop");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
enum class gpio_configure_values_e : uint8_t {
|
||||
gpio_input = 0b000,
|
||||
gpio_output = 0b001,
|
||||
gpio_alt_func_1 = 0b010,
|
||||
gpio_alt_func_2 = 0b011,
|
||||
gpio_reserved_1 = 0b100,
|
||||
gpio_reserved_2 = 0b101,
|
||||
gpio_interupt = 0b110,
|
||||
gpio_disable = 0b111
|
||||
};
|
||||
|
||||
struct sunxi_gpio {
|
||||
unsigned int CFG[4]; // NOSONAR: Intentionally using C style arrays for low level register access
|
||||
unsigned int DAT;
|
||||
unsigned int DRV[2]; // NOSONAR: Intentionally using C style arrays for low level register access
|
||||
unsigned int PULL[2]; // NOSONAR: Intentionally using C style arrays for low level register access
|
||||
};
|
||||
using sunxi_gpio_t = struct sunxi_gpio;
|
||||
|
||||
/* gpio interrupt control */
|
||||
struct sunxi_gpio_int {
|
||||
unsigned int CFG[3]; // NOSONAR: Intentionally using C style arrays for low level register access
|
||||
unsigned int CTL;
|
||||
unsigned int STA;
|
||||
unsigned int DEB;
|
||||
};
|
||||
using sunxi_gpio_int_t = struct sunxi_gpio_int;
|
||||
|
||||
struct sunxi_gpio_reg {
|
||||
struct sunxi_gpio gpio_bank[9]; // NOSONAR: Intentionally using C style arrays for low level register access
|
||||
unsigned char res[0xbc]; // NOSONAR: Intentionally using C style arrays for low level register access
|
||||
struct sunxi_gpio_int gpio_int;
|
||||
};
|
||||
using sunxi_gpio_reg_t = struct sunxi_gpio_reg;
|
||||
|
||||
static const uint32_t PAGE_SIZE = (4 * 1024);
|
||||
static const uint32_t BLOCK_SIZE = (4 * 1024);
|
||||
|
||||
static const int SETUP_DEVMEM_FAIL = 1;
|
||||
static const int SETUP_MALLOC_FAIL = 2;
|
||||
static const int SETUP_MMAP_FAIL = 3;
|
||||
static const int SETUP_CPUINFO_FAIL = 4;
|
||||
static const int SETUP_NOT_RPI_FAIL = 5;
|
||||
static const int INPUT = 1;
|
||||
static const int OUTPUT = 0;
|
||||
static const int ALT0 = 4;
|
||||
static const int HIGH = 1;
|
||||
static const int LOW = 0;
|
||||
static const int PUD_OFF = 0;
|
||||
static const int PUD_DOWN = 1;
|
||||
static const int PUD_UP = 2;
|
||||
|
||||
static const uint32_t SUNXI_R_GPIO_BASE = 0x01F02000;
|
||||
static const uint32_t SUNXI_R_GPIO_REG_OFFSET = 0xC00;
|
||||
static const uint32_t SUNXI_GPIO_BASE = 0x01C20000;
|
||||
static const uint32_t SUNXI_GPIO_REG_OFFSET = 0x800;
|
||||
static const uint32_t SUNXI_CFG_OFFSET = 0x00;
|
||||
static const uint32_t SUNXI_DATA_OFFSET = 0x10;
|
||||
static const uint32_t SUNXI_PUD_OFFSET = 0x1C;
|
||||
static const uint32_t SUNXI_BANK_SIZE = 0x24;
|
||||
|
||||
static const uint32_t MAP_SIZE = (4096 * 2);
|
||||
static const uint32_t MAP_MASK = (MAP_SIZE - 1);
|
||||
|
||||
static const int FSEL_OFFSET = 0; // 0x0000
|
||||
static const int SET_OFFSET = 7; // 0x001c / 4
|
||||
static const int CLR_OFFSET = 10; // 0x0028 / 4
|
||||
static const int PINLEVEL_OFFSET = 13; // 0x0034 / 4
|
||||
static const int EVENT_DETECT_OFFSET = 16; // 0x0040 / 4
|
||||
static const int RISING_ED_OFFSET = 19; // 0x004c / 4
|
||||
static const int FALLING_ED_OFFSET = 22; // 0x0058 / 4
|
||||
static const int HIGH_DETECT_OFFSET = 25; // 0x0064 / 4
|
||||
static const int LOW_DETECT_OFFSET = 28; // 0x0070 / 4
|
||||
static const int PULLUPDN_OFFSET = 37; // 0x0094 / 4
|
||||
static const int PULLUPDNCLK_OFFSET = 38; // 0x0098 / 4
|
||||
|
||||
static const uint32_t TMR_REGISTER_BASE = 0x01C20C00;
|
||||
static const uint32_t TMR_IRQ_EN_REG = 0x0; // T imer IRQ Enable Register
|
||||
static const uint32_t TMR_IRQ_STA_REG = 0x4; // Timer Status Register
|
||||
static const uint32_t TMR0_CTRL_REG = 0x10; // Timer 0 Control Register
|
||||
static const uint32_t TMR0_INTV_VALUE_REG = 0x14; // Timer 0 Interval Value Register
|
||||
static const uint32_t TMR0_CUR_VALUE_REG = 0x18; // Timer 0 Current Value Register
|
||||
static const uint32_t TMR1_CTRL_REG = 0x20; // Timer 1 Control Register
|
||||
static const uint32_t TMR1_INTV_VALUE_REG = 0x24; // Timer 1 Interval Value Register
|
||||
static const uint32_t TMR1_CUR_VALUE_REG = 0x28; // Timer 1 Current Value Register
|
||||
static const uint32_t AVS_CNT_CTL_REG = 0x80; // AVS Control Register
|
||||
static const uint32_t AVS_CNT0_REG = 0x84; // AVS Counter 0 Register
|
||||
static const uint32_t AVS_CNT1_REG = 0x88; // AVS Counter 1 Register
|
||||
static const uint32_t AVS_CNT_DIV_REG = 0x8C; // AVS Divisor Register
|
||||
static const uint32_t WDOG0_IRQ_EN_REG = 0xA0; // Watchdog 0 IRQ Enable Register
|
||||
static const uint32_t WDOG0_IRQ_STA_REG = 0xA4; // Watchdog 0 Status Register
|
||||
static const uint32_t WDOG0_CTRL_REG = 0xB0; // Watchdog 0 Control Register
|
||||
static const uint32_t WDOG0_CFG_REG = 0xB4; // Watchdog 0 Configuration Register
|
||||
static const uint32_t WDOG0_MODE_REG = 0xB8; // Watchdog 0 Mode Register
|
||||
};
|
|
@ -12,32 +12,25 @@
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "hal/systimer.h"
|
||||
#include "hal/systimer_allwinner.h"
|
||||
#include "hal/systimer_raspberry.h"
|
||||
#include <sys/mman.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include "hal/gpiobus.h"
|
||||
#include "hal/sbc_version.h"
|
||||
|
||||
#include "shared/log.h"
|
||||
bool SysTimer::initialized = false;
|
||||
bool SysTimer::is_raspberry = false;
|
||||
|
||||
bool SysTimer::initialized = false;
|
||||
bool SysTimer::is_allwinnner = false;
|
||||
bool SysTimer::is_raspberry = false;
|
||||
using namespace std;
|
||||
|
||||
std::unique_ptr<PlatformSpecificTimer> SysTimer::systimer_ptr;
|
||||
unique_ptr<PlatformSpecificTimer> SysTimer::systimer_ptr;
|
||||
|
||||
void SysTimer::Init()
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
|
||||
if (!initialized) {
|
||||
if (SBC_Version::IsRaspberryPi()) {
|
||||
systimer_ptr = make_unique<SysTimer_Raspberry>();
|
||||
is_raspberry = true;
|
||||
} else if (SBC_Version::IsBananaPi()) {
|
||||
systimer_ptr = make_unique<SysTimer_AllWinner>();
|
||||
is_allwinnner = true;
|
||||
}
|
||||
systimer_ptr->Init();
|
||||
initialized = true;
|
||||
|
@ -49,16 +42,19 @@ uint32_t SysTimer::GetTimerLow()
|
|||
{
|
||||
return systimer_ptr->GetTimerLow();
|
||||
}
|
||||
|
||||
// Get system timer high byte
|
||||
uint32_t SysTimer::GetTimerHigh()
|
||||
{
|
||||
return systimer_ptr->GetTimerHigh();
|
||||
}
|
||||
|
||||
// Sleep for N nanoseconds
|
||||
void SysTimer::SleepNsec(uint32_t nsec)
|
||||
{
|
||||
systimer_ptr->SleepNsec(nsec);
|
||||
}
|
||||
|
||||
// Sleep for N microseconds
|
||||
void SysTimer::SleepUsec(uint32_t usec)
|
||||
{
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 akuker
|
||||
//
|
||||
// [ High resolution timer for the Allwinner series of SoC's]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "hal/systimer_allwinner.h"
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "hal/gpiobus.h"
|
||||
|
||||
#include "shared/log.h"
|
||||
|
||||
const std::string SysTimer_AllWinner::dev_mem_filename = "/dev/mem";
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Initialize the system timer
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SysTimer_AllWinner::Init()
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
|
||||
int fd;
|
||||
|
||||
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
|
||||
LOGERROR("I can't open /dev/mem. Are you running as root?")
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
hsitimer_regs =
|
||||
(sun8i_hsitimer_registers *)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, hs_timer_base_address);
|
||||
|
||||
if (hsitimer_regs == MAP_FAILED) {
|
||||
LOGERROR("Unable to map high speed timer registers. Are you running as root?")
|
||||
}
|
||||
|
||||
sysbus_regs = (struct sun8i_sysbus_registers *)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
|
||||
system_bus_base_address);
|
||||
|
||||
if (sysbus_regs == MAP_FAILED) {
|
||||
LOGERROR("Unable to map system bus registers. Are you running as root?")
|
||||
}
|
||||
|
||||
enable_hs_timer();
|
||||
}
|
||||
|
||||
void SysTimer_AllWinner::enable_hs_timer()
|
||||
{
|
||||
// By default, the HSTimer clock gating is masked. When it is necessary to use
|
||||
// the HSTimer, its clock gating should be opened in BUS Clock Gating Register 0
|
||||
// and then de-assert the software reset in BUS Software Reset Register 0 on the
|
||||
// CCU module. If it is not needed to use the HSTimer, both the gating bit and
|
||||
// the software reset bit should be set 0.
|
||||
|
||||
LOGTRACE("%s [Before Enable] CLK GATE: %08X SOFT RST: %08X", __PRETTY_FUNCTION__, sysbus_regs->bus_clk_gating_reg0,
|
||||
sysbus_regs->bus_soft_rst_reg0)
|
||||
|
||||
sysbus_regs->bus_clk_gating_reg0 = sysbus_regs->bus_clk_gating_reg0 | (1 << BUS_CLK_GATING_REG0_HSTMR);
|
||||
sysbus_regs->bus_soft_rst_reg0 = sysbus_regs->bus_soft_rst_reg0 | (1 << BUS_SOFT_RST_REG0_HSTMR);
|
||||
LOGTRACE("%s [After Enable] CLK GATE: %08X SOFT RST: %08X", __PRETTY_FUNCTION__, sysbus_regs->bus_clk_gating_reg0,
|
||||
sysbus_regs->bus_soft_rst_reg0)
|
||||
|
||||
// Set interval value to the maximum value. (its a 52 bit register)
|
||||
hsitimer_regs->hs_tmr_intv_hi_reg = (1 << 20) - 1; //(0xFFFFF)
|
||||
hsitimer_regs->hs_tmr_intv_lo_reg = UINT32_MAX;
|
||||
|
||||
// Select prescale value of 1, continuouse mode
|
||||
hsitimer_regs->hs_tmr_ctrl_reg = HS_TMR_CLK_PRE_SCALE_1;
|
||||
|
||||
// Set reload bit
|
||||
hsitimer_regs->hs_tmr_ctrl_reg |= HS_TMR_RELOAD;
|
||||
|
||||
// Enable HSTimer
|
||||
hsitimer_regs->hs_tmr_ctrl_reg |= HS_TMR_EN;
|
||||
}
|
||||
|
||||
// TODO: According to the data sheet, we should turn off the HS timer when we're done with it. But, its just going to
|
||||
// eat up a little extra power if we leave it running.
|
||||
void SysTimer_AllWinner::disable_hs_timer()
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
|
||||
LOGINFO("[Before Disable] CLK GATE: %08X SOFT RST: %08X", sysbus_regs->bus_clk_gating_reg0,
|
||||
sysbus_regs->bus_soft_rst_reg0)
|
||||
|
||||
sysbus_regs->bus_clk_gating_reg0 = sysbus_regs->bus_clk_gating_reg0 & ~(1 << BUS_CLK_GATING_REG0_HSTMR);
|
||||
sysbus_regs->bus_soft_rst_reg0 = sysbus_regs->bus_soft_rst_reg0 & ~(1 << BUS_SOFT_RST_REG0_HSTMR);
|
||||
|
||||
LOGINFO("[After Disable] CLK GATE: %08X SOFT RST: %08X", sysbus_regs->bus_clk_gating_reg0,
|
||||
sysbus_regs->bus_soft_rst_reg0)
|
||||
}
|
||||
|
||||
uint32_t SysTimer_AllWinner::GetTimerLow()
|
||||
{
|
||||
// PiSCSI expects the timer to count UP, but the Allwinner HS timer counts
|
||||
// down. So, we subtract the current timer value from UINT32_MAX
|
||||
return UINT32_MAX - (hsitimer_regs->hs_tmr_curnt_lo_reg / 200);
|
||||
}
|
||||
|
||||
uint32_t SysTimer_AllWinner::GetTimerHigh()
|
||||
{
|
||||
return (uint32_t)0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Sleep in nanoseconds
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SysTimer_AllWinner::SleepNsec(uint32_t nsec)
|
||||
{
|
||||
// If time is less than one HS timer clock tick, don't do anything
|
||||
if (nsec < 20) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The HS timer receives a 200MHz clock input, which equates to
|
||||
// one clock tick every 5 ns.
|
||||
auto clockticks = (uint32_t)std::ceil(nsec / 5);
|
||||
|
||||
uint32_t enter_time = hsitimer_regs->hs_tmr_curnt_lo_reg;
|
||||
|
||||
// TODO: NEED TO HANDLE COUNTER OVERFLOW
|
||||
LOGTRACE("%s entertime: %08X ns: %d clockticks: %d", __PRETTY_FUNCTION__, enter_time, nsec, clockticks)
|
||||
while ((enter_time - hsitimer_regs->hs_tmr_curnt_lo_reg) < clockticks)
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Sleep in microseconds
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SysTimer_AllWinner::SleepUsec(uint32_t usec)
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__)
|
||||
|
||||
// If time is 0, don't do anything
|
||||
if (usec == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t enter_time = GetTimerLow();
|
||||
while ((GetTimerLow() - enter_time) < usec)
|
||||
;
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 akuker
|
||||
//
|
||||
// [ High resolution timer ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "systimer.h"
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// System timer
|
||||
//
|
||||
//===========================================================================
|
||||
class SysTimer_AllWinner : public PlatformSpecificTimer
|
||||
{
|
||||
public:
|
||||
// Default constructor
|
||||
SysTimer_AllWinner() = default;
|
||||
// Default destructor
|
||||
~SysTimer_AllWinner() override = default;
|
||||
// Initialization
|
||||
void Init() override;
|
||||
// Get system timer low byte
|
||||
uint32_t GetTimerLow() override;
|
||||
// Get system timer high byte
|
||||
uint32_t GetTimerHigh() override;
|
||||
// Sleep for N nanoseconds
|
||||
void SleepNsec(uint32_t nsec) override;
|
||||
// Sleep for N microseconds
|
||||
void SleepUsec(uint32_t usec) override;
|
||||
|
||||
private:
|
||||
void enable_hs_timer();
|
||||
void disable_hs_timer();
|
||||
|
||||
static const std::string dev_mem_filename;
|
||||
|
||||
/* Reference: Allwinner H3 Datasheet, section 4.9.3 */
|
||||
static const uint32_t hs_timer_base_address = 0x01C60000;
|
||||
/* Note: Currently the high speed timer is NOT in the armbian
|
||||
* device tree. If it is ever added, this should be pulled
|
||||
* from there */
|
||||
|
||||
using sun8i_hsitimer_registers = struct {
|
||||
/* 0x00 HS Timer IRQ Enabled Register */
|
||||
uint32_t hs_tmr_irq_en_reg;
|
||||
/* 0x04 HS Timer Status Register */
|
||||
uint32_t hs_tmr_irq_stat_reg;
|
||||
/* 0x08 Unused */
|
||||
uint32_t unused_08;
|
||||
/* 0x0C Unused */
|
||||
uint32_t unused_0C;
|
||||
/* 0x10 HS Timer Control Register */
|
||||
uint32_t hs_tmr_ctrl_reg;
|
||||
/* 0x14 HS Timer Interval Value Low Reg */
|
||||
uint32_t hs_tmr_intv_lo_reg;
|
||||
/* 0x18 HS Timer Interval Value High Register */
|
||||
uint32_t hs_tmr_intv_hi_reg;
|
||||
/* 0x1C HS Timer Current Value Low Register */
|
||||
uint32_t hs_tmr_curnt_lo_reg;
|
||||
/* 0x20 HS Timer Current Value High Register */
|
||||
uint32_t hs_tmr_curnt_hi_reg;
|
||||
};
|
||||
|
||||
/* Constants for the HS Timer IRQ enable Register (section 4.9.4.1) */
|
||||
static const uint32_t HS_TMR_INTERUPT_ENB = (1 << 0);
|
||||
|
||||
/* Constants for the HS Timer Control Register (section 4.9.4.3) */
|
||||
static const uint32_t HS_TMR_EN = (1 << 0);
|
||||
static const uint32_t HS_TMR_RELOAD = (1 << 1);
|
||||
static const uint32_t HS_TMR_CLK_PRE_SCALE_1 = (0 << 4);
|
||||
static const uint32_t HS_TMR_CLK_PRE_SCALE_2 = (1 << 4);
|
||||
static const uint32_t HS_TMR_CLK_PRE_SCALE_4 = (2 << 4);
|
||||
static const uint32_t HS_TMR_CLK_PRE_SCALE_8 = (3 << 4);
|
||||
static const uint32_t HS_TMR_CLK_PRE_SCALE_16 = (4 << 4); // NOSONAR This matches the datasheet
|
||||
static const uint32_t HS_TMR_MODE_SINGLE = (1 << 7);
|
||||
static const uint32_t HS_TMR_TEST_MODE = (1 << 31);
|
||||
|
||||
volatile sun8i_hsitimer_registers *hsitimer_regs;
|
||||
|
||||
/* Reference: Allwinner H3 Datasheet, section 4.3.4 */
|
||||
static const uint32_t system_bus_base_address = 0x01C20000;
|
||||
|
||||
struct sun8i_sysbus_registers {
|
||||
uint32_t pad_00_5C[(0x60 / sizeof(uint32_t))]; // NOSONAR c-style array used for padding
|
||||
/* 0x0060 Bus Clock Gating Register 0 */
|
||||
uint32_t bus_clk_gating_reg0;
|
||||
uint32_t pad_64_2C0[((0x2C0 - 0x64) / sizeof(uint32_t))]; // NOSONAR c-style array used for padding
|
||||
/* 0x2C0 Bus Software Reset Register 0 */
|
||||
uint32_t bus_soft_rst_reg0;
|
||||
};
|
||||
|
||||
/* Bit associated with the HS Timer */
|
||||
static const uint32_t BUS_CLK_GATING_REG0_HSTMR = 19;
|
||||
static const uint32_t BUS_SOFT_RST_REG0_HSTMR = 19;
|
||||
|
||||
struct sun8i_sysbus_registers *sysbus_regs;
|
||||
};
|
|
@ -12,6 +12,7 @@
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "hal/systimer_raspberry.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <memory>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
@ -19,14 +20,14 @@
|
|||
#include "hal/gpiobus.h"
|
||||
#include "hal/sbc_version.h"
|
||||
|
||||
#include "shared/log.h"
|
||||
|
||||
// System timer address
|
||||
volatile uint32_t *SysTimer_Raspberry::systaddr = nullptr;
|
||||
// ARM timer address
|
||||
volatile uint32_t *SysTimer_Raspberry::armtaddr = nullptr;
|
||||
volatile uint32_t SysTimer_Raspberry::corefreq = 0;
|
||||
|
||||
using namespace std;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Initialize the system timer
|
||||
|
@ -35,19 +36,19 @@ volatile uint32_t SysTimer_Raspberry::corefreq = 0;
|
|||
void SysTimer_Raspberry::Init()
|
||||
{
|
||||
// Get the base address
|
||||
auto baseaddr = SBC_Version::GetPeripheralAddress();
|
||||
const auto baseaddr = SBC_Version::GetPeripheralAddress();
|
||||
|
||||
// Open /dev/mem
|
||||
int mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
|
||||
if (mem_fd == -1) {
|
||||
LOGERROR("Error: Unable to open /dev/mem. Are you running as root?")
|
||||
spdlog::error("Error: Unable to open /dev/mem. Are you running as root?");
|
||||
return;
|
||||
}
|
||||
|
||||
// Map peripheral region memory
|
||||
void *map = mmap(nullptr, 0x1000100, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, baseaddr);
|
||||
if (map == MAP_FAILED) {
|
||||
LOGERROR("Error: Unable to map memory")
|
||||
spdlog::error("Error: Unable to map memory");
|
||||
close(mem_fd);
|
||||
return;
|
||||
}
|
||||
|
@ -64,7 +65,7 @@ void SysTimer_Raspberry::Init()
|
|||
//
|
||||
// Clock id
|
||||
// 0x000000004: CORE
|
||||
std::array<uint32_t, 32> maxclock = {32, 0, 0x00030004, 8, 0, 4, 0, 0};
|
||||
const array<uint32_t, 32> maxclock = {32, 0, 0x00030004, 8, 0, 4, 0, 0};
|
||||
|
||||
// Save the base address
|
||||
systaddr = (uint32_t *)map + SYST_OFFSET / sizeof(uint32_t);
|
||||
|
@ -114,7 +115,7 @@ void SysTimer_Raspberry::SleepNsec(uint32_t nsec)
|
|||
}
|
||||
|
||||
// Calculate the timer difference
|
||||
uint32_t diff = corefreq * nsec / 1000;
|
||||
const uint32_t diff = corefreq * nsec / 1000;
|
||||
|
||||
// Return if the difference in time is too small
|
||||
if (diff == 0) {
|
||||
|
@ -122,7 +123,7 @@ void SysTimer_Raspberry::SleepNsec(uint32_t nsec)
|
|||
}
|
||||
|
||||
// Start
|
||||
uint32_t start = armtaddr[ARMT_FREERUN];
|
||||
const uint32_t start = armtaddr[ARMT_FREERUN];
|
||||
|
||||
// Loop until timer has elapsed
|
||||
while ((armtaddr[ARMT_FREERUN] - start) < diff)
|
||||
|
@ -141,7 +142,7 @@ void SysTimer_Raspberry::SleepUsec(uint32_t usec)
|
|||
return;
|
||||
}
|
||||
|
||||
uint32_t now = GetTimerLow();
|
||||
const uint32_t now = GetTimerLow();
|
||||
while ((GetTimerLow() - now) < usec)
|
||||
;
|
||||
}
|
||||
|
|
|
@ -3,24 +3,51 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
// Copyright (C) 2021-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/log.h"
|
||||
#include "generated/piscsi_interface.pb.h"
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include "shared/protobuf_util.h"
|
||||
#include "command_context.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace piscsi_interface;
|
||||
using namespace protobuf_util;
|
||||
|
||||
void CommandContext::Cleanup()
|
||||
bool CommandContext::ReadCommand()
|
||||
{
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
// Read magic string
|
||||
array<byte, 6> magic;
|
||||
if (const size_t bytes_read = ReadBytes(fd, magic); bytes_read) {
|
||||
if (bytes_read != magic.size() || memcmp(magic.data(), "RASCSI", magic.size())) {
|
||||
throw io_exception("Invalid magic");
|
||||
}
|
||||
|
||||
// Fetch the command
|
||||
DeserializeMessage(fd, command);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CommandContext::WriteResult(const PbResult& result) const
|
||||
{
|
||||
// The descriptor is -1 when devices are not attached via the remote interface but by the piscsi tool
|
||||
if (fd != -1) {
|
||||
SerializeMessage(fd, result);
|
||||
}
|
||||
}
|
||||
|
||||
bool CommandContext::WriteSuccessResult(PbResult& result) const
|
||||
{
|
||||
result.set_status(true);
|
||||
WriteResult(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandContext::ReturnLocalizedError(LocalizationKey key, const string& arg1, const string& arg2,
|
||||
|
@ -33,7 +60,13 @@ bool CommandContext::ReturnLocalizedError(LocalizationKey key, PbErrorCode error
|
|||
const string& arg2, const string& arg3) const
|
||||
{
|
||||
// For the logfile always use English
|
||||
LOGERROR("%s", localizer.Localize(key, "en", arg1, arg2, arg3).c_str())
|
||||
// Do not log unknown operations as an error for backward/foward compatibility with old/new clients
|
||||
if (error_code == PbErrorCode::UNKNOWN_OPERATION) {
|
||||
spdlog::trace(localizer.Localize(key, "en", arg1, arg2, arg3));
|
||||
}
|
||||
else {
|
||||
spdlog::error(localizer.Localize(key, "en", arg1, arg2, arg3));
|
||||
}
|
||||
|
||||
return ReturnStatus(false, localizer.Localize(key, locale, arg1, arg2, arg3), error_code, false);
|
||||
}
|
||||
|
@ -42,17 +75,12 @@ bool CommandContext::ReturnStatus(bool status, const string& msg, PbErrorCode er
|
|||
{
|
||||
// Do not log twice if logging has already been done in the localized error handling above
|
||||
if (log && !status && !msg.empty()) {
|
||||
LOGERROR("%s", msg.c_str())
|
||||
spdlog::error(msg);
|
||||
}
|
||||
|
||||
if (fd == -1) {
|
||||
if (!msg.empty()) {
|
||||
if (status) {
|
||||
cerr << "Error: " << msg << endl;
|
||||
}
|
||||
else {
|
||||
cout << msg << endl;
|
||||
}
|
||||
cerr << "Error: " << msg << endl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -60,8 +88,18 @@ bool CommandContext::ReturnStatus(bool status, const string& msg, PbErrorCode er
|
|||
result.set_status(status);
|
||||
result.set_error_code(error_code);
|
||||
result.set_msg(msg);
|
||||
serializer.SerializeMessage(fd, result);
|
||||
WriteResult(result);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool CommandContext::ReturnSuccessStatus() const
|
||||
{
|
||||
return ReturnStatus(true, "", PbErrorCode::NO_ERROR_CODE, true);
|
||||
}
|
||||
|
||||
bool CommandContext::ReturnErrorStatus(const string& msg) const
|
||||
{
|
||||
return ReturnStatus(false, msg, PbErrorCode::NO_ERROR_CODE, true);
|
||||
}
|
||||
|
|
|
@ -3,15 +3,14 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
// Copyright (C) 2021-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "generated/piscsi_interface.pb.h"
|
||||
#include "localizer.h"
|
||||
#include "shared/protobuf_serializer.h"
|
||||
#include "generated/piscsi_interface.pb.h"
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
@ -19,27 +18,36 @@ using namespace piscsi_interface;
|
|||
|
||||
class CommandContext
|
||||
{
|
||||
const ProtobufSerializer serializer;
|
||||
|
||||
const Localizer localizer;
|
||||
|
||||
string locale;
|
||||
|
||||
int fd;
|
||||
|
||||
public:
|
||||
|
||||
CommandContext(const std::string& s = "", int f = -1) : locale(s), fd(f) {}
|
||||
CommandContext(const PbCommand& cmd, string_view f, string_view l) : command(cmd), default_folder(f), locale(l) {}
|
||||
explicit CommandContext(int f) : fd(f) {}
|
||||
~CommandContext() = default;
|
||||
|
||||
void Cleanup();
|
||||
|
||||
const ProtobufSerializer& GetSerializer() const { return serializer; }
|
||||
int GetFd() const { return fd; }
|
||||
void SetFd(int f) { fd = f; }
|
||||
bool IsValid() const { return fd != -1; }
|
||||
string GetDefaultFolder() const { return default_folder; }
|
||||
void SetDefaultFolder(string_view f) { default_folder = f; }
|
||||
bool ReadCommand();
|
||||
void WriteResult(const PbResult&) const;
|
||||
bool WriteSuccessResult(PbResult&) const;
|
||||
const PbCommand& GetCommand() const { return command; }
|
||||
|
||||
bool ReturnLocalizedError(LocalizationKey, const string& = "", const string& = "", const string& = "") const;
|
||||
bool ReturnLocalizedError(LocalizationKey, PbErrorCode, const string& = "", const string& = "", const string& = "") const;
|
||||
bool ReturnStatus(bool = true, const string& = "", PbErrorCode = PbErrorCode::NO_ERROR_CODE, bool = true) const;
|
||||
bool ReturnSuccessStatus() const;
|
||||
bool ReturnErrorStatus(const string&) const;
|
||||
|
||||
private:
|
||||
|
||||
bool ReturnStatus(bool, const string&, PbErrorCode, bool) const;
|
||||
|
||||
const Localizer localizer;
|
||||
|
||||
PbCommand command;
|
||||
|
||||
string default_folder;
|
||||
|
||||
string locale;
|
||||
|
||||
int fd = -1;
|
||||
};
|
||||
|
|
|
@ -3,15 +3,13 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
// Copyright (C) 2021-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "localizer.h"
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -24,19 +22,19 @@ Localizer::Localizer()
|
|||
Add(LocalizationKey::ERROR_AUTHENTICATION, "es", "Fallo de autentificación");
|
||||
Add(LocalizationKey::ERROR_AUTHENTICATION, "zh", "认证失败");
|
||||
|
||||
Add(LocalizationKey::ERROR_OPERATION, "en", "Unknown operation");
|
||||
Add(LocalizationKey::ERROR_OPERATION, "de", "Unbekannte Operation");
|
||||
Add(LocalizationKey::ERROR_OPERATION, "sv", "Okänd operation");
|
||||
Add(LocalizationKey::ERROR_OPERATION, "fr", "Opération inconnue");
|
||||
Add(LocalizationKey::ERROR_OPERATION, "es", "Operación desconocida");
|
||||
Add(LocalizationKey::ERROR_OPERATION, "zh", "未知操作");
|
||||
Add(LocalizationKey::ERROR_OPERATION, "en", "Unknown operation: %1");
|
||||
Add(LocalizationKey::ERROR_OPERATION, "de", "Unbekannte Operation: %1");
|
||||
Add(LocalizationKey::ERROR_OPERATION, "sv", "Okänd operation: %1");
|
||||
Add(LocalizationKey::ERROR_OPERATION, "fr", "Opération inconnue: %1");
|
||||
Add(LocalizationKey::ERROR_OPERATION, "es", "Operación desconocida: %1");
|
||||
Add(LocalizationKey::ERROR_OPERATION, "zh", "未知操作: %1");
|
||||
|
||||
Add(LocalizationKey::ERROR_LOG_LEVEL, "en", "Invalid log level %1");
|
||||
Add(LocalizationKey::ERROR_LOG_LEVEL, "de", "Ungültiger Log-Level %1");
|
||||
Add(LocalizationKey::ERROR_LOG_LEVEL, "sv", "Ogiltig loggnivå %1");
|
||||
Add(LocalizationKey::ERROR_LOG_LEVEL, "fr", "Niveau de journalisation invalide %1");
|
||||
Add(LocalizationKey::ERROR_LOG_LEVEL, "es", "Nivel de registro %1 no válido");
|
||||
Add(LocalizationKey::ERROR_LOG_LEVEL, "zh", "无效的日志级别 %1");
|
||||
Add(LocalizationKey::ERROR_LOG_LEVEL, "en", "Invalid log level '%1'");
|
||||
Add(LocalizationKey::ERROR_LOG_LEVEL, "de", "Ungültiger Log-Level '%1'");
|
||||
Add(LocalizationKey::ERROR_LOG_LEVEL, "sv", "Ogiltig loggnivå '%1'");
|
||||
Add(LocalizationKey::ERROR_LOG_LEVEL, "fr", "Niveau de journalisation invalide '%1'");
|
||||
Add(LocalizationKey::ERROR_LOG_LEVEL, "es", "Nivel de registro '%1' no válido");
|
||||
Add(LocalizationKey::ERROR_LOG_LEVEL, "zh", "无效的日志级别 '%1'");
|
||||
|
||||
Add(LocalizationKey::ERROR_MISSING_DEVICE_ID, "en", "Missing device ID");
|
||||
Add(LocalizationKey::ERROR_MISSING_DEVICE_ID, "de", "Fehlende Geräte-ID");
|
||||
|
@ -51,26 +49,28 @@ Localizer::Localizer()
|
|||
Add(LocalizationKey::ERROR_MISSING_FILENAME, "fr", "Nom de fichier manquant");
|
||||
Add(LocalizationKey::ERROR_MISSING_FILENAME, "es", "Falta el nombre del archivo");
|
||||
Add(LocalizationKey::ERROR_MISSING_FILENAME, "zh", "缺少文件名");
|
||||
|
||||
|
||||
Add(LocalizationKey::ERROR_DEVICE_MISSING_FILENAME, "en", "Device type %1 requires a filename");
|
||||
Add(LocalizationKey::ERROR_DEVICE_MISSING_FILENAME, "de", "Gerätetyp %1 erfordert einen Dateinamen");
|
||||
Add(LocalizationKey::ERROR_DEVICE_MISSING_FILENAME, "sv", "Enhetstypen %1 kräver ett filnamn");
|
||||
Add(LocalizationKey::ERROR_DEVICE_MISSING_FILENAME, "fr", "Périphérique de type %1 à besoin d'un nom de fichier");
|
||||
Add(LocalizationKey::ERROR_DEVICE_MISSING_FILENAME, "es", "El tipo de dispositivo %1 requiere un nombre de archivo");
|
||||
Add(LocalizationKey::ERROR_DEVICE_MISSING_FILENAME, "zh", "设备类型 %1 需要一个文件名");
|
||||
|
||||
Add(LocalizationKey::ERROR_IMAGE_IN_USE, "en", "Image file '%1' is already being used by ID %2, unit %3");
|
||||
Add(LocalizationKey::ERROR_IMAGE_IN_USE, "de", "Image-Datei '%1' wird bereits von ID %2, Einheit %3 benutzt");
|
||||
Add(LocalizationKey::ERROR_IMAGE_IN_USE, "sv", "Skivbildsfilen '%1' används redan av id %2, enhetsnummer %3");
|
||||
Add(LocalizationKey::ERROR_IMAGE_IN_USE, "fr", "Le fichier d'image '%1' est déjà utilisé par l'ID %2, unité %3");
|
||||
Add(LocalizationKey::ERROR_IMAGE_IN_USE, "es", "El archivo de imagen '%1' ya está siendo utilizado por el ID %2, unidad %3");
|
||||
Add(LocalizationKey::ERROR_IMAGE_IN_USE, "zh", "图像文件%1已被 ID %2、单元 %3 使用");
|
||||
|
||||
|
||||
Add(LocalizationKey::ERROR_IMAGE_IN_USE, "en", "Image file '%1' is already being used by device %2");
|
||||
Add(LocalizationKey::ERROR_IMAGE_IN_USE, "de", "Image-Datei '%1' wird bereits von Gerät %2 benutzt");
|
||||
Add(LocalizationKey::ERROR_IMAGE_IN_USE, "sv", "Skivbildsfilen '%1' används redan av nhet %2");
|
||||
Add(LocalizationKey::ERROR_IMAGE_IN_USE, "fr", "Le fichier d'image '%1' est déjà utilisé par périphérique %2");
|
||||
Add(LocalizationKey::ERROR_IMAGE_IN_USE, "es", "El archivo de imagen '%1' ya está siendo utilizado por dispositivo %2");
|
||||
Add(LocalizationKey::ERROR_IMAGE_IN_USE, "zh", "图像文件%1已被 ID %2");
|
||||
|
||||
Add(LocalizationKey::ERROR_IMAGE_FILE_INFO, "en", "Can't create image file info for '%1'");
|
||||
Add(LocalizationKey::ERROR_IMAGE_FILE_INFO, "de", "Image-Datei-Information für '%1' kann nicht erzeugt werden");
|
||||
Add(LocalizationKey::ERROR_IMAGE_FILE_INFO, "sv", "Kunde ej skapa skivbildsfilsinfo för '%1'");
|
||||
Add(LocalizationKey::ERROR_IMAGE_FILE_INFO, "fr", "Ne peux pas créer les informations du fichier image '%1'");
|
||||
Add(LocalizationKey::ERROR_IMAGE_FILE_INFO, "es", "No se puede crear información de archivo de imagen para '%1'");
|
||||
Add(LocalizationKey::ERROR_IMAGE_FILE_INFO, "zh", "无法为'%1'创建图像文件信息");
|
||||
|
||||
|
||||
Add(LocalizationKey::ERROR_RESERVED_ID, "en", "Device ID %1 is reserved");
|
||||
Add(LocalizationKey::ERROR_RESERVED_ID, "de", "Geräte-ID %1 ist reserviert");
|
||||
Add(LocalizationKey::ERROR_RESERVED_ID, "sv", "Enhets-id %1 är reserverat");
|
||||
|
@ -84,21 +84,21 @@ Localizer::Localizer()
|
|||
Add(LocalizationKey::ERROR_NON_EXISTING_DEVICE, "fr", "Commande pour ID %1 non-existant");
|
||||
Add(LocalizationKey::ERROR_NON_EXISTING_DEVICE, "es", "Comando para ID %1 no existente");
|
||||
Add(LocalizationKey::ERROR_NON_EXISTING_DEVICE, "zh", "不存在的 ID %1 的指令");
|
||||
|
||||
|
||||
Add(LocalizationKey::ERROR_NON_EXISTING_UNIT, "en", "Command for non-existing ID %1, unit %2");
|
||||
Add(LocalizationKey::ERROR_NON_EXISTING_UNIT, "de", "Kommando für nicht existente ID %1, Einheit %2");
|
||||
Add(LocalizationKey::ERROR_NON_EXISTING_UNIT, "sv", "Kommando för id %1, enhetsnummer %2 som ej existerar");
|
||||
Add(LocalizationKey::ERROR_NON_EXISTING_UNIT, "fr", "Command pour ID %1, unité %2 non-existant");
|
||||
Add(LocalizationKey::ERROR_NON_EXISTING_UNIT, "es", "Comando para ID %1 inexistente, unidad %2");
|
||||
Add(LocalizationKey::ERROR_NON_EXISTING_UNIT, "zh", "不存在的 ID %1, 单元 %2 的指令");
|
||||
|
||||
|
||||
Add(LocalizationKey::ERROR_UNKNOWN_DEVICE_TYPE, "en", "Unknown device type %1");
|
||||
Add(LocalizationKey::ERROR_UNKNOWN_DEVICE_TYPE, "de", "Unbekannter Gerätetyp %1");
|
||||
Add(LocalizationKey::ERROR_UNKNOWN_DEVICE_TYPE, "sv", "Obekant enhetstyp: %1");
|
||||
Add(LocalizationKey::ERROR_UNKNOWN_DEVICE_TYPE, "fr", "Type de périphérique inconnu %1");
|
||||
Add(LocalizationKey::ERROR_UNKNOWN_DEVICE_TYPE, "es", "Tipo de dispositivo desconocido %1");
|
||||
Add(LocalizationKey::ERROR_UNKNOWN_DEVICE_TYPE, "zh", "未知设备类型 %1");
|
||||
|
||||
|
||||
Add(LocalizationKey::ERROR_MISSING_DEVICE_TYPE, "en", "Device type required for unknown extension of file '%1'");
|
||||
Add(LocalizationKey::ERROR_MISSING_DEVICE_TYPE, "de", "Gerätetyp erforderlich für unbekannte Extension der Datei '%1'");
|
||||
Add(LocalizationKey::ERROR_MISSING_DEVICE_TYPE, "sv", "Man måste ange enhetstyp för obekant filändelse '%1'");
|
||||
|
@ -116,6 +116,7 @@ Localizer::Localizer()
|
|||
Add(LocalizationKey::ERROR_DETACH, "en", "Couldn't detach device");
|
||||
Add(LocalizationKey::ERROR_DETACH, "de", "Geräte konnte nicht entfernt werden");
|
||||
Add(LocalizationKey::ERROR_DETACH, "sv", "Kunde ej koppla ifrån enheten");
|
||||
Add(LocalizationKey::ERROR_DETACH, "fr", "Impossible de détacher le périphérique");
|
||||
Add(LocalizationKey::ERROR_DETACH, "es", "No se ha podido desconectar el dispositivo");
|
||||
Add(LocalizationKey::ERROR_DETACH, "zh", "无法卸载设备");
|
||||
|
||||
|
@ -178,54 +179,63 @@ Localizer::Localizer()
|
|||
Add(LocalizationKey::ERROR_SCSI_CONTROLLER, "en", "Couldn't create SCSI controller");
|
||||
Add(LocalizationKey::ERROR_SCSI_CONTROLLER, "de", "SCSI-Controller konnte nicht erzeugt werden");
|
||||
Add(LocalizationKey::ERROR_SCSI_CONTROLLER, "sv", "Kunde ej skapa SCSI-gränssnitt");
|
||||
Add(LocalizationKey::ERROR_SCSI_CONTROLLER, "fr", "Impossible de créer le contrôleur SCSI");
|
||||
Add(LocalizationKey::ERROR_SCSI_CONTROLLER, "es", "No se ha podido crear el controlador SCSI");
|
||||
Add(LocalizationKey::ERROR_SCSI_CONTROLLER, "zh", "无法创建 SCSI 控制器");
|
||||
|
||||
Add(LocalizationKey::ERROR_INVALID_ID, "en", "Invalid device ID %1 (0-%2)");
|
||||
Add(LocalizationKey::ERROR_INVALID_ID, "de", "Ungültige Geräte-ID %1 (0-%2)");
|
||||
Add(LocalizationKey::ERROR_INVALID_ID, "sv", "Ogiltigt enhets-id %1 (0-%2)");
|
||||
Add(LocalizationKey::ERROR_INVALID_ID, "fr", "ID de périphérique invalide %1 (0-%2)");
|
||||
Add(LocalizationKey::ERROR_INVALID_ID, "es", "ID de dispositivo inválido %1 (0-%2)");
|
||||
Add(LocalizationKey::ERROR_INVALID_ID, "zh", "无效的设备 ID %1 (0-%2)");
|
||||
|
||||
Add(LocalizationKey::ERROR_INVALID_LUN, "en", "Invalid LUN %1 (0-%2)");
|
||||
Add(LocalizationKey::ERROR_INVALID_LUN, "de", "Ungültige LUN %1 (0-%2)");
|
||||
Add(LocalizationKey::ERROR_INVALID_LUN, "sv", "Ogiltigt enhetsnummer %1 (0-%2)");
|
||||
Add(LocalizationKey::ERROR_INVALID_LUN, "fr", "LUN invalide %1 (0-%2)");
|
||||
Add(LocalizationKey::ERROR_INVALID_LUN, "es", "LUN invalido %1 (0-%2)");
|
||||
Add(LocalizationKey::ERROR_INVALID_LUN, "zh", "无效的 LUN %1 (0-%2)");
|
||||
|
||||
Add(LocalizationKey::ERROR_LUN0, "en", "LUN 0 cannot be detached as long as there is still another LUN");
|
||||
Add(LocalizationKey::ERROR_LUN0, "de", "LUN 0 kann nicht entfernt werden, solange noch eine andere LUN existiert");
|
||||
Add(LocalizationKey::ERROR_LUN0, "sv", "Enhetsnummer 0 kan ej bli frånkopplat så länge som andra enhetsnummer är anslutna");
|
||||
Add(LocalizationKey::ERROR_LUN0, "fr", "LUN 0 ne peux pas être détaché tant qu'il y'a un autre LUN");
|
||||
Add(LocalizationKey::ERROR_LUN0, "es", "El LUN 0 no se puede desconectar mientras haya otro LUN");
|
||||
Add(LocalizationKey::ERROR_LUN0, "zh", "LUN 0 无法卸载,因为当前仍有另一个 LUN。");
|
||||
|
||||
Add(LocalizationKey::ERROR_INITIALIZATION, "en", "Initialization of %1 device, ID %2, LUN %3 failed");
|
||||
Add(LocalizationKey::ERROR_INITIALIZATION, "de", "Initialisierung von %1-Gerät, ID %2, LUN %3 fehlgeschlagen");
|
||||
Add(LocalizationKey::ERROR_INITIALIZATION, "sv", "Kunde ej initialisera enheten %1 med id %2 och enhetsnummer %3");
|
||||
Add(LocalizationKey::ERROR_INITIALIZATION, "es", "La inicialización del dispositivo %1, ID %2, LUN %3 falló");
|
||||
Add(LocalizationKey::ERROR_INITIALIZATION, "zh", "%1 设备、ID %2、LUN %3 的初始化失败");
|
||||
Add(LocalizationKey::ERROR_INITIALIZATION, "en", "Initialization of %1 failed");
|
||||
Add(LocalizationKey::ERROR_INITIALIZATION, "de", "Initialisierung von %1 fehlgeschlagen");
|
||||
Add(LocalizationKey::ERROR_INITIALIZATION, "sv", "Kunde ej initialisera %1 ");
|
||||
Add(LocalizationKey::ERROR_INITIALIZATION, "fr", "Echec de l'initialisation de %1");
|
||||
Add(LocalizationKey::ERROR_INITIALIZATION, "es", "La inicialización del %1 falló");
|
||||
Add(LocalizationKey::ERROR_INITIALIZATION, "zh", "%1 的初始化失败");
|
||||
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_STOPPABLE, "en", "%1 operation denied, %2 isn't stoppable");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_STOPPABLE, "de", "%1-Operation verweigert, %2 ist nicht stopbar");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_STOPPABLE, "sv", "Operationen %1 nekades för att %2 inte kan stoppas");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_STOPPABLE, "fr", "Opération %1 refusée, %2 ne peut être stoppé");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_STOPPABLE, "es", "%1 operación denegada, %2 no se puede parar");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_STOPPABLE, "zh", "%1 操作被拒绝,%2 不可停止");
|
||||
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_REMOVABLE, "en", "%1 operation denied, %2 isn't removable");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_REMOVABLE, "de", "%1-Operation verweigert, %2 ist nicht wechselbar");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_REMOVABLE, "sv", "Operationen %1 nekades för att %2 inte är uttagbar(t)");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_REMOVABLE, "fr", "Opération %1 refusée, %2 n'est pas détachable");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_REMOVABLE, "es", "%1 operación denegada, %2 no es removible");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_REMOVABLE, "zh", "%1 操作被拒绝,%2 不可移除");
|
||||
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_PROTECTABLE, "en", "%1 operation denied, %2 isn't protectable");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_PROTECTABLE, "de", "%1-Operation verweigert, %2 ist nicht schützbar");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_PROTECTABLE, "sv", "Operationen %1 nekades för att %2 inte är skyddbar(t)");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_PROTECTABLE, "fr", "Opération %1 refusée, %2 n'est pas protégeable");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_PROTECTABLE, "es", "%1 operación denegada, %2 no es protegible");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_PROTECTABLE, "zh", "%1 操作被拒绝,%2 不可保护");
|
||||
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_READY, "en", "%1 operation denied, %2 isn't ready");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_READY, "de", "%1-Operation verweigert, %2 ist nicht bereit");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_READY, "sv", "Operationen %1 nekades för att %2 inte är redo");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_READY, "fr", "Opération %1 refusée, %2 n'est pas prêt");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_READY, "es", "%1 operación denegada, %2 no está listo");
|
||||
Add(LocalizationKey::ERROR_OPERATION_DENIED_READY, "zh", "%1 操作被拒绝,%2 还没有准备好");
|
||||
}
|
||||
|
@ -235,17 +245,16 @@ void Localizer::Add(LocalizationKey key, const string& locale, string_view value
|
|||
// Safeguards against empty messages, duplicate entries and unsupported locales
|
||||
assert(locale.size());
|
||||
assert(value.size());
|
||||
assert(supported_languages.find(locale) != supported_languages.end());
|
||||
assert(supported_languages.contains(locale));
|
||||
assert(localized_messages[locale][key].empty());
|
||||
|
||||
localized_messages[locale][key] = value;
|
||||
}
|
||||
|
||||
string Localizer::Localize(LocalizationKey key, const string& locale, const string& arg1, const string& arg2,
|
||||
const string &arg3) const
|
||||
{
|
||||
string locale_lower = locale;
|
||||
transform(locale_lower.begin(), locale_lower.end(), locale_lower.begin(), ::tolower);
|
||||
string locale_lower;
|
||||
ranges::transform(locale, back_inserter(locale_lower), ::tolower);
|
||||
|
||||
auto it = localized_messages.find(locale_lower);
|
||||
if (it == localized_messages.end()) {
|
||||
|
@ -268,9 +277,9 @@ string Localizer::Localize(LocalizationKey key, const string& locale, const stri
|
|||
}
|
||||
|
||||
string message = m->second;
|
||||
message = regex_replace(message, regex("%1"), arg1);
|
||||
message = regex_replace(message, regex("%2"), arg2);
|
||||
message = regex_replace(message, regex("%3"), arg3);
|
||||
message = regex_replace(message, regex1, arg1);
|
||||
message = regex_replace(message, regex2, arg2);
|
||||
message = regex_replace(message, regex3, arg3);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
// Copyright (C) 2021-2023 Uwe Seimet
|
||||
//
|
||||
// Message localization support. Currently only for messages with up to 3 string parameters.
|
||||
//
|
||||
|
@ -11,9 +11,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "shared/piscsi_util.h"
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <regex>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -64,8 +66,12 @@ public:
|
|||
private:
|
||||
|
||||
void Add(LocalizationKey, const string&, string_view);
|
||||
unordered_map<string, unordered_map<LocalizationKey, string>> localized_messages;
|
||||
unordered_map<string, unordered_map<LocalizationKey, string>, piscsi_util::StringHash, equal_to<>> localized_messages;
|
||||
|
||||
// Supported locales, always lower case
|
||||
unordered_set<string> supported_languages = { "en", "de", "sv", "fr", "es", "zh" };
|
||||
unordered_set<string, piscsi_util::StringHash, equal_to<>> supported_languages = { "en", "de", "sv", "fr", "es", "zh" };
|
||||
|
||||
const regex regex1 = regex("%1");
|
||||
const regex regex2 = regex("%2");
|
||||
const regex regex3 = regex("%3");
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -13,7 +13,7 @@ using namespace std;
|
|||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const vector<char *> args(argv, argv + argc);
|
||||
vector<char *> args(argv, argv + argc);
|
||||
|
||||
return Piscsi().run(args);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -3,31 +3,30 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "devices/device_logger.h"
|
||||
#include "controllers/controller_manager.h"
|
||||
#include "piscsi/command_context.h"
|
||||
#include "piscsi/piscsi_service.h"
|
||||
#include "piscsi/piscsi_image.h"
|
||||
#include "piscsi/piscsi_response.h"
|
||||
#include "piscsi/piscsi_executor.h"
|
||||
#include "generated/piscsi_interface.pb.h"
|
||||
#include <vector>
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class BUS;
|
||||
class ControllerManager;
|
||||
class PiscsiExecutor;
|
||||
|
||||
class Piscsi
|
||||
{
|
||||
using optargs_type = vector<pair<int, string>>;
|
||||
|
||||
static const int DEFAULT_PORT = 6868;
|
||||
|
||||
public:
|
||||
|
@ -35,46 +34,49 @@ public:
|
|||
Piscsi() = default;
|
||||
~Piscsi() = default;
|
||||
|
||||
int run(const vector<char *>&);
|
||||
int run(span<char *>);
|
||||
|
||||
private:
|
||||
|
||||
void Banner(const vector<char *>&) const;
|
||||
bool InitBus() const;
|
||||
static void Cleanup();
|
||||
void ReadAccessToken(const string&) const;
|
||||
void Banner(span<char *>) const;
|
||||
bool InitBus();
|
||||
void CleanUp();
|
||||
void ReadAccessToken(const path&);
|
||||
void LogDevices(string_view) const;
|
||||
PbDeviceType ParseDeviceType(const string&) const;
|
||||
static void TerminationHandler(int);
|
||||
optargs_type ParseArguments(const vector<char *>&, int&) const;
|
||||
void CreateInitialDevices(const optargs_type&) const;
|
||||
void WaitForNotBusy() const;
|
||||
string ParseArguments(span<char *>, PbCommand&, int&, string&);
|
||||
void Process();
|
||||
bool IsNotBusy() const;
|
||||
|
||||
// TODO Should not be static and should be moved to PiscsiService
|
||||
static bool ExecuteCommand(const CommandContext&, const PbCommand&);
|
||||
bool ShutDown(AbstractController::piscsi_shutdown_mode);
|
||||
bool ShutDown(const CommandContext&, const string&);
|
||||
|
||||
DeviceLogger device_logger;
|
||||
bool ExecuteCommand(const CommandContext&);
|
||||
bool ExecuteWithLock(const CommandContext&);
|
||||
bool HandleDeviceListChange(const CommandContext&, PbOperation) const;
|
||||
|
||||
// A static instance is needed because of the signal handler
|
||||
static inline shared_ptr<BUS> bus;
|
||||
bool SetLogLevel(const string&) const;
|
||||
|
||||
// TODO These fields should not be static
|
||||
const shared_ptr<spdlog::logger> logger = spdlog::stdout_color_mt("piscsi stdout logger");
|
||||
|
||||
static inline PiscsiService service;
|
||||
static PbDeviceType ParseDeviceType(const string&);
|
||||
|
||||
static inline PiscsiImage piscsi_image;
|
||||
mutex execution_locker;
|
||||
|
||||
const static inline PiscsiResponse piscsi_response;
|
||||
string access_token;
|
||||
|
||||
static inline shared_ptr<ControllerManager> controller_manager;
|
||||
PiscsiImage piscsi_image;
|
||||
|
||||
static inline shared_ptr<PiscsiExecutor> executor;
|
||||
PiscsiResponse response;
|
||||
|
||||
// Processing flag
|
||||
static inline volatile bool active;
|
||||
PiscsiService service;
|
||||
|
||||
// Some versions of spdlog do not support get_log_level(), so we have to remember the level
|
||||
static inline string current_log_level = "info";
|
||||
unique_ptr<PiscsiExecutor> executor;
|
||||
|
||||
static inline string access_token;
|
||||
ControllerManager controller_manager;
|
||||
|
||||
unique_ptr<BUS> bus;
|
||||
|
||||
// Required for the termination handler
|
||||
static inline Piscsi *instance;
|
||||
};
|
||||
|
|
|
@ -3,35 +3,28 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
// Copyright (C) 2021-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/log.h"
|
||||
#include "shared/piscsi_util.h"
|
||||
#include "shared/protobuf_util.h"
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include "controllers/controller_manager.h"
|
||||
#include "controllers/scsi_controller.h"
|
||||
#include "devices/device_logger.h"
|
||||
#include "devices/device_factory.h"
|
||||
#include "devices/primary_device.h"
|
||||
#include "devices/disk.h"
|
||||
#include "piscsi_service.h"
|
||||
#include "piscsi_image.h"
|
||||
#include "localizer.h"
|
||||
#include "command_context.h"
|
||||
#include "piscsi_executor.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
|
||||
using namespace spdlog;
|
||||
using namespace protobuf_util;
|
||||
using namespace piscsi_util;
|
||||
|
||||
bool PiscsiExecutor::ProcessDeviceCmd(const CommandContext& context, const PbDeviceDefinition& pb_device,
|
||||
const PbCommand& command, bool dryRun)
|
||||
bool PiscsiExecutor::ProcessDeviceCmd(const CommandContext& context, const PbDeviceDefinition& pb_device, bool dryRun)
|
||||
{
|
||||
PrintCommand(command, pb_device, dryRun);
|
||||
spdlog::info((dryRun ? "Validating: " : "Executing: ") + PrintCommand(context.GetCommand(), pb_device));
|
||||
|
||||
const int id = pb_device.id();
|
||||
const int lun = pb_device.unit();
|
||||
|
@ -40,14 +33,14 @@ bool PiscsiExecutor::ProcessDeviceCmd(const CommandContext& context, const PbDev
|
|||
return false;
|
||||
}
|
||||
|
||||
const PbOperation operation = command.operation();
|
||||
const PbOperation operation = context.GetCommand().operation();
|
||||
|
||||
// For all commands except ATTACH the device and LUN must exist
|
||||
if (operation != ATTACH && !VerifyExistingIdAndLun(context, id, lun)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto device = controller_manager.GetDeviceByIdAndLun(id, lun);
|
||||
auto device = controller_manager.GetDeviceForIdAndLun(id, lun);
|
||||
|
||||
if (!ValidateOperationAgainstDevice(context, *device, operation)) {
|
||||
return false;
|
||||
|
@ -64,7 +57,7 @@ bool PiscsiExecutor::ProcessDeviceCmd(const CommandContext& context, const PbDev
|
|||
return Attach(context, pb_device, dryRun);
|
||||
|
||||
case DETACH:
|
||||
return Detach(context, device, dryRun);
|
||||
return Detach(context, *device, dryRun);
|
||||
|
||||
case INSERT:
|
||||
return Insert(context, pb_device, device, dryRun);
|
||||
|
@ -82,146 +75,68 @@ bool PiscsiExecutor::ProcessDeviceCmd(const CommandContext& context, const PbDev
|
|||
case CHECK_AUTHENTICATION:
|
||||
case NO_OPERATION:
|
||||
// Do nothing, just log
|
||||
LOGTRACE("Received %s command", PbOperation_Name(operation).c_str())
|
||||
spdlog::trace("Received " + PbOperation_Name(operation) + " command");
|
||||
break;
|
||||
|
||||
default:
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_OPERATION);
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_OPERATION, to_string(operation));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PiscsiExecutor::ProcessCmd(const CommandContext& context, const PbCommand& command)
|
||||
bool PiscsiExecutor::ProcessCmd(const CommandContext& context)
|
||||
{
|
||||
const PbCommand& command = context.GetCommand();
|
||||
|
||||
// Handle commands that are not device-specific
|
||||
switch (command.operation()) {
|
||||
case DETACH_ALL:
|
||||
DetachAll();
|
||||
return context.ReturnStatus();
|
||||
return context.ReturnSuccessStatus();
|
||||
|
||||
case RESERVE_IDS: {
|
||||
const string ids = GetParam(command, "ids");
|
||||
if (const string error = SetReservedIds(ids); !error.empty()) {
|
||||
return context.ReturnStatus(false, error);
|
||||
if (const string error = SetReservedIds(GetParam(command, "ids")); !error.empty()) {
|
||||
return context.ReturnErrorStatus(error);
|
||||
}
|
||||
|
||||
return context.ReturnStatus();
|
||||
return context.ReturnSuccessStatus();
|
||||
}
|
||||
|
||||
case CREATE_IMAGE:
|
||||
return piscsi_image.CreateImage(context, command);
|
||||
|
||||
case DELETE_IMAGE:
|
||||
return piscsi_image.DeleteImage(context, command);
|
||||
|
||||
case RENAME_IMAGE:
|
||||
return piscsi_image.RenameImage(context, command);
|
||||
|
||||
case COPY_IMAGE:
|
||||
return piscsi_image.CopyImage(context, command);
|
||||
|
||||
case PROTECT_IMAGE:
|
||||
case UNPROTECT_IMAGE:
|
||||
return piscsi_image.SetImagePermissions(context, command);
|
||||
|
||||
default:
|
||||
// This is a device-specific command handled below
|
||||
break;
|
||||
}
|
||||
|
||||
// Remember the list of reserved files, than run the dry run
|
||||
// Remember the list of reserved files during the dry run
|
||||
const auto& reserved_files = StorageDevice::GetReservedFiles();
|
||||
for (const auto& device : command.devices()) {
|
||||
if (!ProcessDeviceCmd(context, device, command, true)) {
|
||||
// Dry run failed, restore the file list
|
||||
StorageDevice::SetReservedFiles(reserved_files);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the list of reserved files before proceeding
|
||||
const bool isDryRunError = ranges::find_if_not(command.devices(), [&] (const auto& device)
|
||||
{ return ProcessDeviceCmd(context, device, true); }) != command.devices().end();
|
||||
StorageDevice::SetReservedFiles(reserved_files);
|
||||
|
||||
if (const string result = ValidateLunSetup(command); !result.empty()) {
|
||||
return context.ReturnStatus(false, result);
|
||||
}
|
||||
|
||||
for (const auto& device : command.devices()) {
|
||||
if (!ProcessDeviceCmd(context, device, command, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ATTACH and DETACH return the device list
|
||||
if (context.IsValid() && (command.operation() == ATTACH || command.operation() == DETACH)) {
|
||||
// A new command with an empty device list is required here in order to return data for all devices
|
||||
PbCommand cmd;
|
||||
PbResult result;
|
||||
piscsi_response.GetDevicesInfo(controller_manager.GetAllDevices(), result, cmd,
|
||||
piscsi_image.GetDefaultFolder());
|
||||
serializer.SerializeMessage(context.GetFd(), result);
|
||||
return true;
|
||||
}
|
||||
|
||||
return context.ReturnStatus();
|
||||
}
|
||||
|
||||
bool PiscsiExecutor::SetLogLevel(const string& log_level) const
|
||||
{
|
||||
int id = -1;
|
||||
int lun = -1;
|
||||
string level = log_level;
|
||||
|
||||
if (size_t separator_pos = log_level.find(COMPONENT_SEPARATOR); separator_pos != string::npos) {
|
||||
level = log_level.substr(0, separator_pos);
|
||||
|
||||
const string l = log_level.substr(separator_pos + 1);
|
||||
separator_pos = l.find(COMPONENT_SEPARATOR);
|
||||
if (separator_pos != string::npos) {
|
||||
const string error = ProcessId(l, ScsiController::LUN_MAX, id, lun);
|
||||
if (!error.empty()) {
|
||||
LOGWARN("Invalid device ID/LUN specifier '%s'", l.c_str())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!GetAsUnsignedInt(l, id)) {
|
||||
LOGWARN("Invalid device ID specifier '%s'", l.c_str())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto& it = log_level_mapping.find(level); it != log_level_mapping.end()) {
|
||||
set_level(it->second);
|
||||
}
|
||||
else {
|
||||
LOGWARN("Invalid log level '%s'", log_level.c_str())
|
||||
if (isDryRunError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceLogger::SetLogIdAndLun(id, lun);
|
||||
|
||||
if (id != -1) {
|
||||
if (lun == -1) {
|
||||
LOGINFO("Set log level for device ID %d to '%s'", id, level.c_str())
|
||||
}
|
||||
else {
|
||||
LOGINFO("Set log level for device ID %d, LUN %d to '%s'", id, lun, level.c_str())
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGINFO("Set log level to '%s'", level.c_str())
|
||||
if (const string error = EnsureLun0(command); !error.empty()) {
|
||||
return context.ReturnErrorStatus(error);
|
||||
}
|
||||
|
||||
return true;
|
||||
if (ranges::find_if_not(command.devices(), [&] (const auto& device)
|
||||
{ return ProcessDeviceCmd(context, device, false); } ) != command.devices().end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return context.ReturnSuccessStatus();
|
||||
}
|
||||
|
||||
bool PiscsiExecutor::Start(PrimaryDevice& device, bool dryRun) const
|
||||
{
|
||||
if (!dryRun) {
|
||||
LOGINFO("Start requested for %s ID %d, unit %d", device.GetTypeString(), device.GetId(), device.GetLun())
|
||||
spdlog::info("Start requested for " + device.GetIdentifier());
|
||||
|
||||
if (!device.Start()) {
|
||||
LOGWARN("Starting %s ID %d, unit %d failed", device.GetTypeString(), device.GetId(), device.GetLun())
|
||||
spdlog::warn("Starting " + device.GetIdentifier() + " failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,7 +146,7 @@ bool PiscsiExecutor::Start(PrimaryDevice& device, bool dryRun) const
|
|||
bool PiscsiExecutor::Stop(PrimaryDevice& device, bool dryRun) const
|
||||
{
|
||||
if (!dryRun) {
|
||||
LOGINFO("Stop requested for %s ID %d, unit %d", device.GetTypeString(), device.GetId(), device.GetLun())
|
||||
spdlog::info("Stop requested for " + device.GetIdentifier());
|
||||
|
||||
device.Stop();
|
||||
}
|
||||
|
@ -242,10 +157,10 @@ bool PiscsiExecutor::Stop(PrimaryDevice& device, bool dryRun) const
|
|||
bool PiscsiExecutor::Eject(PrimaryDevice& device, bool dryRun) const
|
||||
{
|
||||
if (!dryRun) {
|
||||
LOGINFO("Eject requested for %s ID %d, unit %d", device.GetTypeString(), device.GetId(), device.GetLun())
|
||||
spdlog::info("Eject requested for " + device.GetIdentifier());
|
||||
|
||||
if (!device.Eject(true)) {
|
||||
LOGWARN("Ejecting %s ID %d, unit %d failed", device.GetTypeString(), device.GetId(), device.GetLun())
|
||||
spdlog::warn("Ejecting " + device.GetIdentifier() + " failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,8 +170,7 @@ bool PiscsiExecutor::Eject(PrimaryDevice& device, bool dryRun) const
|
|||
bool PiscsiExecutor::Protect(PrimaryDevice& device, bool dryRun) const
|
||||
{
|
||||
if (!dryRun) {
|
||||
LOGINFO("Write protection requested for %s ID %d, unit %d", device.GetTypeString(), device.GetId(),
|
||||
device.GetLun())
|
||||
spdlog::info("Write protection requested for " + device.GetIdentifier());
|
||||
|
||||
device.SetProtected(true);
|
||||
}
|
||||
|
@ -267,8 +181,7 @@ bool PiscsiExecutor::Protect(PrimaryDevice& device, bool dryRun) const
|
|||
bool PiscsiExecutor::Unprotect(PrimaryDevice& device, bool dryRun) const
|
||||
{
|
||||
if (!dryRun) {
|
||||
LOGINFO("Write unprotection requested for %s ID %d, unit %d", device.GetTypeString(), device.GetId(),
|
||||
device.GetLun())
|
||||
spdlog::info("Write unprotection requested for " + device.GetIdentifier());
|
||||
|
||||
device.SetProtected(false);
|
||||
}
|
||||
|
@ -282,15 +195,16 @@ bool PiscsiExecutor::Attach(const CommandContext& context, const PbDeviceDefinit
|
|||
const int lun = pb_device.unit();
|
||||
const PbDeviceType type = pb_device.type();
|
||||
|
||||
if (lun >= ScsiController::LUN_MAX) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_INVALID_LUN, to_string(lun), to_string(ScsiController::LUN_MAX));
|
||||
if (lun >= ControllerManager::GetScsiLunMax()) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_INVALID_LUN, to_string(lun),
|
||||
to_string(ControllerManager::GetScsiLunMax()));
|
||||
}
|
||||
|
||||
if (controller_manager.GetDeviceByIdAndLun(id, lun) != nullptr) {
|
||||
if (controller_manager.HasDeviceForIdAndLun(id, lun)) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_DUPLICATE_ID, to_string(id), to_string(lun));
|
||||
}
|
||||
|
||||
if (reserved_ids.find(id) != reserved_ids.end()) {
|
||||
if (reserved_ids.contains(id)) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_RESERVED_ID, to_string(id));
|
||||
}
|
||||
|
||||
|
@ -302,8 +216,7 @@ bool PiscsiExecutor::Attach(const CommandContext& context, const PbDeviceDefinit
|
|||
}
|
||||
|
||||
// If no filename was provided the medium is considered not inserted
|
||||
auto storage_device = dynamic_pointer_cast<StorageDevice>(device);
|
||||
device->SetRemoved(storage_device != nullptr ? filename.empty() : false);
|
||||
device->SetRemoved(device->SupportsFile() ? filename.empty() : false);
|
||||
|
||||
if (!SetProductData(context, pb_device, *device)) {
|
||||
return false;
|
||||
|
@ -313,14 +226,14 @@ bool PiscsiExecutor::Attach(const CommandContext& context, const PbDeviceDefinit
|
|||
return false;
|
||||
}
|
||||
|
||||
string full_path;
|
||||
const auto storage_device = dynamic_pointer_cast<StorageDevice>(device);
|
||||
if (device->SupportsFile()) {
|
||||
// Only with removable media drives, CD and MO the medium (=file) may be inserted later
|
||||
if (!device->IsRemovable() && filename.empty()) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_MISSING_FILENAME, PbDeviceType_Name(type));
|
||||
}
|
||||
|
||||
if (!ValidateImageFile(context, *storage_device, filename, full_path)) {
|
||||
if (!ValidateImageFile(context, *storage_device, filename)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -336,25 +249,24 @@ bool PiscsiExecutor::Attach(const CommandContext& context, const PbDeviceDefinit
|
|||
return true;
|
||||
}
|
||||
|
||||
unordered_map<string, string> params = { pb_device.params().begin(), pb_device.params().end() };
|
||||
param_map params = { pb_device.params().begin(), pb_device.params().end() };
|
||||
if (!device->SupportsFile()) {
|
||||
// Clients like scsictl might have sent both "file" and "interfaces"
|
||||
params.erase("file");
|
||||
}
|
||||
|
||||
if (!device->Init(params)) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_INITIALIZATION, PbDeviceType_Name(device->GetType()),
|
||||
to_string(id), to_string(lun));
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_INITIALIZATION, device->GetIdentifier());
|
||||
}
|
||||
|
||||
if (storage_device != nullptr) {
|
||||
storage_device->ReserveFile(full_path, id, lun);
|
||||
}
|
||||
|
||||
if (!controller_manager.AttachToScsiController(id, device)) {
|
||||
if (!controller_manager.AttachToController(bus, id, device)) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_SCSI_CONTROLLER);
|
||||
}
|
||||
|
||||
if (storage_device != nullptr && !storage_device->IsRemoved()) {
|
||||
storage_device->ReserveFile();
|
||||
}
|
||||
|
||||
string msg = "Attached ";
|
||||
if (device->IsReadOnly()) {
|
||||
msg += "read-only ";
|
||||
|
@ -362,8 +274,8 @@ bool PiscsiExecutor::Attach(const CommandContext& context, const PbDeviceDefinit
|
|||
else if (device->IsProtectable() && device->IsProtected()) {
|
||||
msg += "protected ";
|
||||
}
|
||||
msg += string(device->GetTypeString()) + " device, ID " + to_string(id) + ", unit " + to_string(lun);
|
||||
LOGINFO("%s", msg.c_str())
|
||||
msg += device->GetIdentifier();
|
||||
spdlog::info(msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -371,12 +283,11 @@ bool PiscsiExecutor::Attach(const CommandContext& context, const PbDeviceDefinit
|
|||
bool PiscsiExecutor::Insert(const CommandContext& context, const PbDeviceDefinition& pb_device,
|
||||
const shared_ptr<PrimaryDevice>& device, bool dryRun) const
|
||||
{
|
||||
auto storage_device = dynamic_pointer_cast<StorageDevice>(device);
|
||||
if (storage_device == nullptr) {
|
||||
if (!device->SupportsFile()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!storage_device->IsRemoved()) {
|
||||
if (!device->IsRemoved()) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_EJECT_REQUIRED);
|
||||
}
|
||||
|
||||
|
@ -394,56 +305,51 @@ bool PiscsiExecutor::Insert(const CommandContext& context, const PbDeviceDefinit
|
|||
return true;
|
||||
}
|
||||
|
||||
LOGINFO("Insert %sfile '%s' requested into %s ID %d, unit %d", pb_device.protected_() ? "protected " : "",
|
||||
filename.c_str(), storage_device->GetTypeString(), pb_device.id(), pb_device.unit())
|
||||
spdlog::info("Insert " + string(pb_device.protected_() ? "protected " : "") + "file '" + filename +
|
||||
"' requested into " + device->GetIdentifier());
|
||||
|
||||
if (!SetSectorSize(context, storage_device, pb_device.block_size())) {
|
||||
if (!SetSectorSize(context, device, pb_device.block_size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string full_path;
|
||||
if (!ValidateImageFile(context, *storage_device, filename, full_path)) {
|
||||
auto storage_device = dynamic_pointer_cast<StorageDevice>(device);
|
||||
if (!ValidateImageFile(context, *storage_device, filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
storage_device->SetProtected(pb_device.protected_());
|
||||
storage_device->ReserveFile(full_path, storage_device->GetId(), storage_device->GetLun());
|
||||
storage_device->ReserveFile();
|
||||
storage_device->SetMediumChanged(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PiscsiExecutor::Detach(const CommandContext& context, const shared_ptr<PrimaryDevice>& device, bool dryRun) const
|
||||
bool PiscsiExecutor::Detach(const CommandContext& context, PrimaryDevice& device, bool dryRun)
|
||||
{
|
||||
auto controller = controller_manager.FindController(device->GetId());
|
||||
auto controller = controller_manager.FindController(device.GetId());
|
||||
if (controller == nullptr) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_DETACH);
|
||||
}
|
||||
|
||||
// LUN 0 can only be detached if there is no other LUN anymore
|
||||
if (!device->GetLun() && controller->GetLunCount() > 1) {
|
||||
if (!device.GetLun() && controller->GetLunCount() > 1) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_LUN0);
|
||||
}
|
||||
|
||||
if (!dryRun) {
|
||||
// Remember the ID before it gets invalid when removing the device
|
||||
const int id = device->GetId();
|
||||
// Remember the device identifier for the log message before the device data become invalid on removal
|
||||
const string identifier = device.GetIdentifier();
|
||||
|
||||
if (!controller->RemoveDevice(device)) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_DETACH);
|
||||
}
|
||||
|
||||
// If no LUN is left also delete the controller
|
||||
if (!controller->GetLunCount() && !controller_manager.DeleteController(controller)) {
|
||||
if (!controller->GetLunCount() && !controller_manager.DeleteController(*controller)) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_DETACH);
|
||||
}
|
||||
|
||||
if (auto storage_device = dynamic_pointer_cast<StorageDevice>(device); storage_device != nullptr) {
|
||||
storage_device->UnreserveFile();
|
||||
}
|
||||
|
||||
LOGINFO("%s", ("Detached " + string(device->GetTypeString()) + " device with ID " + to_string(id)
|
||||
+ ", unit " + to_string(device->GetLun())).c_str())
|
||||
spdlog::info("Detached " + identifier);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -452,177 +358,96 @@ bool PiscsiExecutor::Detach(const CommandContext& context, const shared_ptr<Prim
|
|||
void PiscsiExecutor::DetachAll()
|
||||
{
|
||||
controller_manager.DeleteAllControllers();
|
||||
StorageDevice::UnreserveAll();
|
||||
|
||||
LOGINFO("Detached all devices")
|
||||
spdlog::info("Detached all devices");
|
||||
}
|
||||
|
||||
bool PiscsiExecutor::ShutDown(const CommandContext& context, const string& mode) {
|
||||
if (mode.empty()) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_SHUTDOWN_MODE_MISSING);
|
||||
}
|
||||
|
||||
PbResult result;
|
||||
result.set_status(true);
|
||||
|
||||
// The PiSCSI shutdown mode is "rascsi" instead of "piscsi" for backwards compatibility
|
||||
if (mode == "rascsi") {
|
||||
LOGINFO("PiSCSI shutdown requested")
|
||||
|
||||
serializer.SerializeMessage(context.GetFd(), result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mode != "system" && mode != "reboot") {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_SHUTDOWN_MODE_INVALID, mode);
|
||||
}
|
||||
|
||||
// The root user has UID 0
|
||||
if (getuid()) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_SHUTDOWN_PERMISSION);
|
||||
}
|
||||
|
||||
if (mode == "system") {
|
||||
LOGINFO("System shutdown requested")
|
||||
|
||||
serializer.SerializeMessage(context.GetFd(), result);
|
||||
|
||||
DetachAll();
|
||||
|
||||
if (system("init 0") == -1) {
|
||||
LOGERROR("System shutdown failed: %s", strerror(errno))
|
||||
}
|
||||
}
|
||||
else if (mode == "reboot") {
|
||||
LOGINFO("System reboot requested")
|
||||
|
||||
serializer.SerializeMessage(context.GetFd(), result);
|
||||
|
||||
DetachAll();
|
||||
|
||||
if (system("init 6") == -1) {
|
||||
LOGERROR("System reboot failed: %s", strerror(errno))
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
string PiscsiExecutor::SetReservedIds(string_view ids)
|
||||
{
|
||||
list<string> ids_to_reserve;
|
||||
set<int> ids_to_reserve;
|
||||
stringstream ss(ids.data());
|
||||
string id;
|
||||
while (getline(ss, id, ',')) {
|
||||
if (!id.empty()) {
|
||||
ids_to_reserve.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
set<int> reserved;
|
||||
for (const string& id_to_reserve : ids_to_reserve) {
|
||||
int res_id;
|
||||
if (!GetAsUnsignedInt(id_to_reserve, res_id) || res_id > 7) {
|
||||
return "Invalid ID " + id_to_reserve;
|
||||
}
|
||||
|
||||
if (controller_manager.FindController(res_id) != nullptr) {
|
||||
return "ID " + id_to_reserve + " is currently in use";
|
||||
}
|
||||
|
||||
reserved.insert(res_id);
|
||||
}
|
||||
|
||||
reserved_ids = { reserved.begin(), reserved.end() };
|
||||
|
||||
if (!reserved_ids.empty()) {
|
||||
string s;
|
||||
bool isFirst = true;
|
||||
for (const auto& reserved_id : reserved) {
|
||||
if (!isFirst) {
|
||||
s += ", ";
|
||||
}
|
||||
isFirst = false;
|
||||
s += to_string(reserved_id);
|
||||
if (!GetAsUnsignedInt(id, res_id) || res_id > 7) {
|
||||
return "Invalid ID " + id;
|
||||
}
|
||||
|
||||
LOGINFO("Reserved ID(s) set to %s", s.c_str())
|
||||
if (controller_manager.HasController(res_id)) {
|
||||
return "ID " + id + " is currently in use";
|
||||
}
|
||||
|
||||
ids_to_reserve.insert(res_id);
|
||||
}
|
||||
|
||||
reserved_ids = { ids_to_reserve.begin(), ids_to_reserve.end() };
|
||||
|
||||
if (!ids_to_reserve.empty()) {
|
||||
spdlog::info("Reserved ID(s) set to " + Join(ids_to_reserve));
|
||||
}
|
||||
else {
|
||||
LOGINFO("Cleared reserved ID(s)")
|
||||
spdlog::info("Cleared reserved ID(s)");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool PiscsiExecutor::ValidateImageFile(const CommandContext& context, StorageDevice& storage_device,
|
||||
const string& filename, string& full_path) const
|
||||
const string& filename) const
|
||||
{
|
||||
if (filename.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (const auto [id1, lun1] = StorageDevice::GetIdsForReservedFile(filename); id1 != -1 || lun1 != -1) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_IMAGE_IN_USE, filename,
|
||||
to_string(id1), to_string(lun1));
|
||||
if (!CheckForReservedFile(context, filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string effective_filename = filename;
|
||||
storage_device.SetFilename(filename);
|
||||
|
||||
if (!StorageDevice::FileExists(filename)) {
|
||||
// If the file does not exist search for it in the default image folder
|
||||
effective_filename = piscsi_image.GetDefaultFolder() + "/" + filename;
|
||||
const string effective_filename = context.GetDefaultFolder() + "/" + filename;
|
||||
|
||||
if (const auto [id2, lun2] = StorageDevice::GetIdsForReservedFile(effective_filename); id2 != -1 || lun2 != -1) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_IMAGE_IN_USE, filename,
|
||||
to_string(id2), to_string(lun2));
|
||||
if (!CheckForReservedFile(context, effective_filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!StorageDevice::FileExists(effective_filename)) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_FILE_OPEN, effective_filename);
|
||||
}
|
||||
}
|
||||
|
||||
storage_device.SetFilename(effective_filename);
|
||||
|
||||
if (storage_device.IsReadOnlyFile()) {
|
||||
// Permanently write-protected
|
||||
storage_device.SetReadOnly(true);
|
||||
storage_device.SetProtectable(false);
|
||||
}
|
||||
else {
|
||||
storage_device.SetReadOnly(false);
|
||||
storage_device.SetProtectable(true);
|
||||
storage_device.SetFilename(effective_filename);
|
||||
}
|
||||
|
||||
try {
|
||||
storage_device.Open();
|
||||
}
|
||||
catch(const io_exception&) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_FILE_OPEN, effective_filename);
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_FILE_OPEN, storage_device.GetFilename());
|
||||
}
|
||||
|
||||
full_path = effective_filename;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PiscsiExecutor::PrintCommand(const PbCommand& command, const PbDeviceDefinition& pb_device, bool dryRun) const
|
||||
bool PiscsiExecutor::CheckForReservedFile(const CommandContext& context, const string& filename)
|
||||
{
|
||||
if (const auto [id, lun] = StorageDevice::GetIdsForReservedFile(filename); id != -1) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_IMAGE_IN_USE, filename,
|
||||
to_string(id) + ":" + to_string(lun));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string PiscsiExecutor::PrintCommand(const PbCommand& command, const PbDeviceDefinition& pb_device) const
|
||||
{
|
||||
const map<string, string, less<>> params = { command.params().begin(), command.params().end() };
|
||||
|
||||
ostringstream s;
|
||||
s << (dryRun ? "Validating" : "Executing");
|
||||
s << ": operation=" << PbOperation_Name(command.operation());
|
||||
s << "operation=" << PbOperation_Name(command.operation());
|
||||
|
||||
if (!params.empty()) {
|
||||
s << ", command params=";
|
||||
bool isFirst = true;
|
||||
for (const auto& [key, value]: params) {
|
||||
for (const auto& [key, value] : params) {
|
||||
if (!isFirst) {
|
||||
s << ", ";
|
||||
}
|
||||
|
@ -632,8 +457,7 @@ void PiscsiExecutor::PrintCommand(const PbCommand& command, const PbDeviceDefini
|
|||
}
|
||||
}
|
||||
|
||||
s << ", device id=" << pb_device.id() << ", lun=" << pb_device.unit() << ", type="
|
||||
<< PbDeviceType_Name(pb_device.type());
|
||||
s << ", device=" << pb_device.id() << ":" << pb_device.unit() << ", type=" << PbDeviceType_Name(pb_device.type());
|
||||
|
||||
if (pb_device.params_size()) {
|
||||
s << ", device params=";
|
||||
|
@ -649,13 +473,14 @@ void PiscsiExecutor::PrintCommand(const PbCommand& command, const PbDeviceDefini
|
|||
|
||||
s << ", vendor='" << pb_device.vendor() << "', product='" << pb_device.product()
|
||||
<< "', revision='" << pb_device.revision() << "', block size=" << pb_device.block_size();
|
||||
LOGINFO("%s", s.str().c_str())
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
string PiscsiExecutor::ValidateLunSetup(const PbCommand& command) const
|
||||
string PiscsiExecutor::EnsureLun0(const PbCommand& command) const
|
||||
{
|
||||
// Mapping of available LUNs (bit vector) to devices
|
||||
unordered_map<uint32_t, uint32_t> luns;
|
||||
unordered_map<int32_t, int32_t> luns;
|
||||
|
||||
// Collect LUN bit vectors of new devices
|
||||
for (const auto& device : command.devices()) {
|
||||
|
@ -667,23 +492,17 @@ string PiscsiExecutor::ValidateLunSetup(const PbCommand& command) const
|
|||
luns[device->GetId()] |= 1 << device->GetLun();
|
||||
}
|
||||
|
||||
// LUN 0 must exist for all devices
|
||||
for (const auto& [id, lun]: luns) {
|
||||
if (!(lun & 0x01)) {
|
||||
return "LUN 0 is missing for device ID " + to_string(id);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
const auto& it = ranges::find_if_not(luns, [] (const auto& l) { return l.second & 0x01; } );
|
||||
return it == luns.end() ? "" : "LUN 0 is missing for device ID " + to_string((*it).first);
|
||||
}
|
||||
|
||||
bool PiscsiExecutor::VerifyExistingIdAndLun(const CommandContext& context, int id, int lun) const
|
||||
{
|
||||
if (controller_manager.FindController(id) == nullptr) {
|
||||
if (!controller_manager.HasController(id)) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_NON_EXISTING_DEVICE, to_string(id));
|
||||
}
|
||||
|
||||
if (controller_manager.GetDeviceByIdAndLun(id, lun) == nullptr) {
|
||||
if (!controller_manager.HasDeviceForIdAndLun(id, lun)) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_NON_EXISTING_UNIT, to_string(id), to_string(lun));
|
||||
}
|
||||
|
||||
|
@ -706,13 +525,13 @@ shared_ptr<PrimaryDevice> PiscsiExecutor::CreateDevice(const CommandContext& con
|
|||
return device;
|
||||
}
|
||||
|
||||
bool PiscsiExecutor::SetSectorSize(const CommandContext& context, shared_ptr<PrimaryDevice> device, int block_size) const
|
||||
bool PiscsiExecutor::SetSectorSize(const CommandContext& context, shared_ptr<PrimaryDevice> device, int size) const
|
||||
{
|
||||
if (block_size) {
|
||||
auto disk = dynamic_pointer_cast<Disk>(device);
|
||||
if (size) {
|
||||
const auto disk = dynamic_pointer_cast<Disk>(device);
|
||||
if (disk != nullptr && disk->IsSectorSizeConfigurable()) {
|
||||
if (!disk->SetConfiguredSectorSize(device_factory, block_size)) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_BLOCK_SIZE, to_string(block_size));
|
||||
if (!disk->SetConfiguredSectorSize(device_factory, size)) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_BLOCK_SIZE, to_string(size));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -725,22 +544,26 @@ bool PiscsiExecutor::SetSectorSize(const CommandContext& context, shared_ptr<Pri
|
|||
}
|
||||
|
||||
bool PiscsiExecutor::ValidateOperationAgainstDevice(const CommandContext& context, const PrimaryDevice& device,
|
||||
const PbOperation& operation)
|
||||
PbOperation operation)
|
||||
{
|
||||
if ((operation == START || operation == STOP) && !device.IsStoppable()) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_OPERATION_DENIED_STOPPABLE, device.GetTypeString());
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_OPERATION_DENIED_STOPPABLE, PbOperation_Name(operation),
|
||||
device.GetTypeString());
|
||||
}
|
||||
|
||||
if ((operation == INSERT || operation == EJECT) && !device.IsRemovable()) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_OPERATION_DENIED_REMOVABLE, device.GetTypeString());
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_OPERATION_DENIED_REMOVABLE, PbOperation_Name(operation),
|
||||
device.GetTypeString());
|
||||
}
|
||||
|
||||
if ((operation == PROTECT || operation == UNPROTECT) && !device.IsProtectable()) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_OPERATION_DENIED_PROTECTABLE, device.GetTypeString());
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_OPERATION_DENIED_PROTECTABLE, PbOperation_Name(operation),
|
||||
device.GetTypeString());
|
||||
}
|
||||
|
||||
if ((operation == PROTECT || operation == UNPROTECT) && !device.IsReady()) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_OPERATION_DENIED_READY, device.GetTypeString());
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_OPERATION_DENIED_READY, PbOperation_Name(operation),
|
||||
device.GetTypeString());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -748,15 +571,16 @@ bool PiscsiExecutor::ValidateOperationAgainstDevice(const CommandContext& contex
|
|||
|
||||
bool PiscsiExecutor::ValidateIdAndLun(const CommandContext& context, int id, int lun)
|
||||
{
|
||||
// Validate the device ID and LUN
|
||||
if (id < 0) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_MISSING_DEVICE_ID);
|
||||
}
|
||||
if (id >= ControllerManager::DEVICE_MAX) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_INVALID_ID, to_string(id), to_string(ControllerManager::DEVICE_MAX - 1));
|
||||
if (id >= ControllerManager::GetScsiIdMax()) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_INVALID_ID, to_string(id),
|
||||
to_string(ControllerManager::GetScsiIdMax() - 1));
|
||||
}
|
||||
if (lun < 0 || lun >= ScsiController::LUN_MAX) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_INVALID_LUN, to_string(lun), to_string(ScsiController::LUN_MAX - 1));
|
||||
if (lun < 0 || lun >= ControllerManager::GetScsiLunMax()) {
|
||||
return context.ReturnLocalizedError(LocalizationKey::ERROR_INVALID_LUN, to_string(lun),
|
||||
to_string(ControllerManager::GetScsiLunMax() - 1));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -777,7 +601,7 @@ bool PiscsiExecutor::SetProductData(const CommandContext& context, const PbDevic
|
|||
}
|
||||
}
|
||||
catch(const invalid_argument& e) {
|
||||
return context.ReturnStatus(false, e.what());
|
||||
return context.ReturnErrorStatus(e.what());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -3,39 +3,34 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
// Copyright (C) 2021-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "shared/protobuf_serializer.h"
|
||||
#include "piscsi/piscsi_response.h"
|
||||
#include "hal/bus.h"
|
||||
#include "controllers/controller_manager.h"
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
class PiscsiImage;
|
||||
class DeviceFactory;
|
||||
class ControllerManager;
|
||||
class PrimaryDevice;
|
||||
class StorageDevice;
|
||||
class CommandContext;
|
||||
|
||||
using namespace spdlog;
|
||||
|
||||
class PiscsiExecutor
|
||||
{
|
||||
public:
|
||||
|
||||
PiscsiExecutor(PiscsiImage& piscsi_image, ControllerManager& controller_manager)
|
||||
: piscsi_image(piscsi_image), controller_manager(controller_manager) {}
|
||||
PiscsiExecutor(BUS& bus, ControllerManager& controller_manager) : bus(bus), controller_manager(controller_manager) {}
|
||||
~PiscsiExecutor() = default;
|
||||
|
||||
unordered_set<int> GetReservedIds() const { return reserved_ids; }
|
||||
// TODO At least some of these methods should be private, currently they are directly called by the unit tests
|
||||
|
||||
bool ProcessDeviceCmd(const CommandContext&, const PbDeviceDefinition&, const PbCommand&, bool);
|
||||
bool ProcessCmd(const CommandContext&, const PbCommand&);
|
||||
bool SetLogLevel(const string&) const;
|
||||
auto GetReservedIds() const { return reserved_ids; }
|
||||
|
||||
bool ProcessDeviceCmd(const CommandContext&, const PbDeviceDefinition&, bool);
|
||||
bool ProcessCmd(const CommandContext&);
|
||||
bool Start(PrimaryDevice&, bool) const;
|
||||
bool Stop(PrimaryDevice&, bool) const;
|
||||
bool Eject(PrimaryDevice&, bool) const;
|
||||
|
@ -43,41 +38,29 @@ public:
|
|||
bool Unprotect(PrimaryDevice&, bool) const;
|
||||
bool Attach(const CommandContext&, const PbDeviceDefinition&, bool);
|
||||
bool Insert(const CommandContext&, const PbDeviceDefinition&, const shared_ptr<PrimaryDevice>&, bool) const;
|
||||
bool Detach(const CommandContext&, const shared_ptr<PrimaryDevice>&, bool) const;
|
||||
bool Detach(const CommandContext&, PrimaryDevice&, bool);
|
||||
void DetachAll();
|
||||
bool ShutDown(const CommandContext&, const string&);
|
||||
string SetReservedIds(string_view);
|
||||
bool ValidateImageFile(const CommandContext&, StorageDevice&, const string&, string&) const;
|
||||
void PrintCommand(const PbCommand&, const PbDeviceDefinition&, bool) const;
|
||||
string ValidateLunSetup(const PbCommand&) const;
|
||||
bool ValidateImageFile(const CommandContext&, StorageDevice&, const string&) const;
|
||||
string PrintCommand(const PbCommand&, const PbDeviceDefinition&) const;
|
||||
string EnsureLun0(const PbCommand&) const;
|
||||
bool VerifyExistingIdAndLun(const CommandContext&, int, int) const;
|
||||
shared_ptr<PrimaryDevice> CreateDevice(const CommandContext&, const PbDeviceType, int, const string&) const;
|
||||
bool SetSectorSize(const CommandContext&, shared_ptr<PrimaryDevice>, int) const;
|
||||
|
||||
static bool ValidateOperationAgainstDevice(const CommandContext&, const PrimaryDevice&, const PbOperation&);
|
||||
static bool ValidateOperationAgainstDevice(const CommandContext&, const PrimaryDevice&, PbOperation);
|
||||
static bool ValidateIdAndLun(const CommandContext&, int, int);
|
||||
static bool SetProductData(const CommandContext&, const PbDeviceDefinition&, PrimaryDevice&);
|
||||
|
||||
private:
|
||||
|
||||
const PiscsiResponse piscsi_response;
|
||||
static bool CheckForReservedFile(const CommandContext&, const string&);
|
||||
|
||||
PiscsiImage& piscsi_image;
|
||||
BUS& bus;
|
||||
|
||||
ControllerManager& controller_manager;
|
||||
|
||||
const DeviceFactory device_factory;
|
||||
|
||||
const ProtobufSerializer serializer;
|
||||
|
||||
unordered_set<int> reserved_ids;
|
||||
|
||||
static inline const unordered_map<string, level::level_enum> log_level_mapping = {
|
||||
{ "trace", level::trace },
|
||||
{ "debug", level::debug },
|
||||
{ "info", level::info },
|
||||
{ "warn", level::warn },
|
||||
{ "err", level::err },
|
||||
{ "off", level::off }
|
||||
};
|
||||
};
|
||||
|
|
|
@ -3,21 +3,18 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
// Copyright (C) 2021-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/log.h"
|
||||
#include "shared/protobuf_util.h"
|
||||
#include "devices/disk.h"
|
||||
#include "command_context.h"
|
||||
#include "piscsi_image.h"
|
||||
#include "shared/protobuf_util.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace std;
|
||||
using namespace filesystem;
|
||||
|
@ -32,14 +29,12 @@ PiscsiImage::PiscsiImage()
|
|||
|
||||
bool PiscsiImage::CheckDepth(string_view filename) const
|
||||
{
|
||||
return count(filename.begin(), filename.end(), '/') <= depth;
|
||||
return ranges::count(filename, '/') <= depth;
|
||||
}
|
||||
|
||||
bool PiscsiImage::CreateImageFolder(const CommandContext& context, const string& filename) const
|
||||
bool PiscsiImage::CreateImageFolder(const CommandContext& context, string_view filename) const
|
||||
{
|
||||
if (const size_t filename_start = filename.rfind('/'); filename_start != string::npos) {
|
||||
const auto folder = path(filename.substr(0, filename_start));
|
||||
|
||||
if (const auto folder = path(filename).parent_path(); !folder.string().empty()) {
|
||||
// Checking for existence first prevents an error if the top-level folder is a softlink
|
||||
if (error_code error; exists(folder, error)) {
|
||||
return true;
|
||||
|
@ -51,67 +46,64 @@ bool PiscsiImage::CreateImageFolder(const CommandContext& context, const string&
|
|||
return ChangeOwner(context, folder, false);
|
||||
}
|
||||
catch(const filesystem_error& e) {
|
||||
return context.ReturnStatus(false, "Can't create image folder '" + string(folder) + "': " + e.what());
|
||||
return context.ReturnErrorStatus("Can't create image folder '" + folder.string() + "': " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string PiscsiImage::SetDefaultFolder(const string& f)
|
||||
string PiscsiImage::SetDefaultFolder(string_view f)
|
||||
{
|
||||
if (f.empty()) {
|
||||
return "Can't set default image folder: Missing folder name";
|
||||
}
|
||||
|
||||
string folder = f;
|
||||
|
||||
// If a relative path is specified, the path is assumed to be relative to the user's home directory
|
||||
if (folder[0] != '/') {
|
||||
folder = GetHomeDir() + "/" + folder;
|
||||
path folder(f);
|
||||
if (folder.is_relative()) {
|
||||
folder = path(GetHomeDir() + "/" + folder.string());
|
||||
}
|
||||
else {
|
||||
if (folder.find("/home/") != 0) {
|
||||
return "Default image folder must be located in '/home/'";
|
||||
}
|
||||
|
||||
if (path home_root = path(GetHomeDir()).parent_path(); !folder.string().starts_with(home_root.string())) {
|
||||
return "Default image folder must be located in '" + home_root.string() + "'";
|
||||
}
|
||||
|
||||
// Resolve a potential symlink
|
||||
auto p = path(folder);
|
||||
if (error_code error; is_symlink(p, error)) {
|
||||
p = read_symlink(p);
|
||||
if (error_code error; is_symlink(folder, error)) {
|
||||
folder = read_symlink(folder);
|
||||
}
|
||||
|
||||
if (error_code error; !is_directory(p, error)) {
|
||||
return "'" + string(p) + "' is not a valid folder";
|
||||
if (error_code error; !is_directory(folder)) {
|
||||
return string("'") + folder.string() + "' is not a valid image folder";
|
||||
}
|
||||
|
||||
default_folder = string(p);
|
||||
default_folder = folder.string();
|
||||
|
||||
LOGINFO("Default image folder set to '%s'", default_folder.c_str())
|
||||
spdlog::info("Default image folder set to '" + default_folder + "'");
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool PiscsiImage::CreateImage(const CommandContext& context, const PbCommand& command) const
|
||||
bool PiscsiImage::CreateImage(const CommandContext& context) const
|
||||
{
|
||||
const string filename = GetParam(command, "file");
|
||||
const string filename = GetParam(context.GetCommand(), "file");
|
||||
if (filename.empty()) {
|
||||
return context.ReturnStatus(false, "Can't create image file: Missing image filename");
|
||||
return context.ReturnErrorStatus("Missing image filename");
|
||||
}
|
||||
|
||||
if (!CheckDepth(filename)) {
|
||||
return context.ReturnStatus(false, ("Invalid folder hierarchy depth '" + filename + "'").c_str());
|
||||
return context.ReturnErrorStatus(("Invalid folder hierarchy depth '" + filename + "'").c_str());
|
||||
}
|
||||
|
||||
const string full_filename = GetFullName(filename);
|
||||
if (!IsValidDstFilename(full_filename)) {
|
||||
return context.ReturnStatus(false, "Can't create image file: '" + full_filename + "': File already exists");
|
||||
return context.ReturnErrorStatus("Can't create image file: '" + full_filename + "': File already exists");
|
||||
}
|
||||
|
||||
const string size = GetParam(command, "size");
|
||||
const string size = GetParam(context.GetCommand(), "size");
|
||||
if (size.empty()) {
|
||||
return context.ReturnStatus(false, "Can't create image file '" + full_filename + "': Missing file size");
|
||||
return context.ReturnErrorStatus("Can't create image file '" + full_filename + "': Missing file size");
|
||||
}
|
||||
|
||||
off_t len;
|
||||
|
@ -119,20 +111,20 @@ bool PiscsiImage::CreateImage(const CommandContext& context, const PbCommand& co
|
|||
len = stoull(size);
|
||||
}
|
||||
catch(const invalid_argument&) {
|
||||
return context.ReturnStatus(false, "Can't create image file '" + full_filename + "': Invalid file size " + size);
|
||||
return context.ReturnErrorStatus("Can't create image file '" + full_filename + "': Invalid file size " + size);
|
||||
}
|
||||
catch(const out_of_range&) {
|
||||
return context.ReturnStatus(false, "Can't create image file '" + full_filename + "': Invalid file size " + size);
|
||||
return context.ReturnErrorStatus("Can't create image file '" + full_filename + "': Invalid file size " + size);
|
||||
}
|
||||
if (len < 512 || (len & 0x1ff)) {
|
||||
return context.ReturnStatus(false, "Invalid image file size " + to_string(len) + " (not a multiple of 512)");
|
||||
return context.ReturnErrorStatus("Invalid image file size " + to_string(len) + " (not a multiple of 512)");
|
||||
}
|
||||
|
||||
if (!CreateImageFolder(context, full_filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool read_only = GetParam(command, "read_only") == "true";
|
||||
const bool read_only = GetParam(context.GetCommand(), "read_only") == "true";
|
||||
|
||||
error_code error;
|
||||
path file(full_filename);
|
||||
|
@ -149,40 +141,37 @@ bool PiscsiImage::CreateImage(const CommandContext& context, const PbCommand& co
|
|||
catch(const filesystem_error& e) {
|
||||
remove(file, error);
|
||||
|
||||
return context.ReturnStatus(false, "Can't create image file '" + full_filename + "': " + e.what());
|
||||
return context.ReturnErrorStatus("Can't create image file '" + full_filename + "': " + e.what());
|
||||
}
|
||||
|
||||
LOGINFO("%s", string("Created " + string(read_only ? "read-only " : "") + "image file '" + full_filename +
|
||||
"' with a size of " + to_string(len) + " bytes").c_str())
|
||||
spdlog::info("Created " + string(read_only ? "read-only " : "") + "image file '" + full_filename +
|
||||
"' with a size of " + to_string(len) + " bytes");
|
||||
|
||||
return context.ReturnStatus();
|
||||
return context.ReturnSuccessStatus();
|
||||
}
|
||||
|
||||
bool PiscsiImage::DeleteImage(const CommandContext& context, const PbCommand& command) const
|
||||
bool PiscsiImage::DeleteImage(const CommandContext& context) const
|
||||
{
|
||||
const string filename = GetParam(command, "file");
|
||||
const string filename = GetParam(context.GetCommand(), "file");
|
||||
if (filename.empty()) {
|
||||
return context.ReturnStatus(false, "Missing image filename");
|
||||
return context.ReturnErrorStatus("Missing image filename");
|
||||
}
|
||||
|
||||
if (!CheckDepth(filename)) {
|
||||
return context.ReturnStatus(false, ("Invalid folder hierarchy depth '" + filename + "'").c_str());
|
||||
return context.ReturnErrorStatus("Invalid folder hierarchy depth '" + filename + "'");
|
||||
}
|
||||
|
||||
const auto full_filename = path(GetFullName(filename));
|
||||
|
||||
if (!exists(full_filename)) {
|
||||
return context.ReturnStatus(false, "Image file '" + string(full_filename) + "' does not exist");
|
||||
return context.ReturnErrorStatus("Image file '" + full_filename.string() + "' does not exist");
|
||||
}
|
||||
|
||||
const auto [id, lun] = StorageDevice::GetIdsForReservedFile(full_filename);
|
||||
if (id != -1 || lun != -1) {
|
||||
return context.ReturnStatus(false, "Can't delete image file '" + string(full_filename) +
|
||||
"', it is currently being used by device ID " + to_string(id) + ", LUN " + to_string(lun));
|
||||
if (!IsReservedFile(context, full_filename, "delete")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (error_code error; !remove(full_filename, error)) {
|
||||
return context.ReturnStatus(false, "Can't delete image file '" + string(full_filename) + "'");
|
||||
return context.ReturnErrorStatus("Can't delete image file '" + full_filename.string() + "'");
|
||||
}
|
||||
|
||||
// Delete empty subfolders
|
||||
|
@ -196,32 +185,22 @@ bool PiscsiImage::DeleteImage(const CommandContext& context, const PbCommand& co
|
|||
}
|
||||
|
||||
if (error_code error; !remove(full_folder)) {
|
||||
return context.ReturnStatus(false, "Can't delete empty image folder '" + string(full_folder) + "'");
|
||||
return context.ReturnErrorStatus("Can't delete empty image folder '" + full_folder.string() + "'");
|
||||
}
|
||||
|
||||
last_slash = folder.rfind('/');
|
||||
}
|
||||
|
||||
LOGINFO("Deleted image file '%s'", full_filename.c_str())
|
||||
spdlog::info("Deleted image file '" + full_filename.string() + "'");
|
||||
|
||||
return context.ReturnStatus();
|
||||
return context.ReturnSuccessStatus();
|
||||
}
|
||||
|
||||
bool PiscsiImage::RenameImage(const CommandContext& context, const PbCommand& command) const
|
||||
bool PiscsiImage::RenameImage(const CommandContext& context) const
|
||||
{
|
||||
string from;
|
||||
string to;
|
||||
if (!ValidateParams(context, command, "rename/move", from, to)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto [id, lun] = StorageDevice::GetIdsForReservedFile(from);
|
||||
if (id != -1 || lun != -1) {
|
||||
return context.ReturnStatus(false, "Can't rename/move image file '" + from +
|
||||
"', it is currently being used by device ID " + to_string(id) + ", LUN " + to_string(lun));
|
||||
}
|
||||
|
||||
if (!CreateImageFolder(context, to)) {
|
||||
if (!ValidateParams(context, "rename/move", from, to)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -229,33 +208,19 @@ bool PiscsiImage::RenameImage(const CommandContext& context, const PbCommand& co
|
|||
rename(path(from), path(to));
|
||||
}
|
||||
catch(const filesystem_error& e) {
|
||||
return context.ReturnStatus(false, "Can't rename/move image file '" + from + "' to '" + to + "': " + e.what());
|
||||
return context.ReturnErrorStatus("Can't rename/move image file '" + from + "': " + e.what());
|
||||
}
|
||||
|
||||
LOGINFO("Renamed/Moved image file '%s' to '%s'", from.c_str(), to.c_str())
|
||||
spdlog::info("Renamed/Moved image file '" + from + "' to '" + to + "'");
|
||||
|
||||
return context.ReturnStatus();
|
||||
return context.ReturnSuccessStatus();
|
||||
}
|
||||
|
||||
bool PiscsiImage::CopyImage(const CommandContext& context, const PbCommand& command) const
|
||||
bool PiscsiImage::CopyImage(const CommandContext& context) const
|
||||
{
|
||||
string from;
|
||||
string to;
|
||||
if (!ValidateParams(context, command, "copy", from, to)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (access(from.c_str(), R_OK)) {
|
||||
return context.ReturnStatus(false, "Can't read source image file '" + from + "'");
|
||||
}
|
||||
|
||||
const auto [id, lun] = StorageDevice::GetIdsForReservedFile(from);
|
||||
if (id != -1 || lun != -1) {
|
||||
return context.ReturnStatus(false, "Can't copy image file '" + from +
|
||||
"', it is currently being used by device ID " + to_string(id) + ", LUN " + to_string(lun));
|
||||
}
|
||||
|
||||
if (!CreateImageFolder(context, to)) {
|
||||
if (!ValidateParams(context, "copy", from, to)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -268,119 +233,138 @@ bool PiscsiImage::CopyImage(const CommandContext& context, const PbCommand& comm
|
|||
copy_symlink(f, t);
|
||||
}
|
||||
catch(const filesystem_error& e) {
|
||||
return context.ReturnStatus(false, "Can't copy image file symlink '" + from + "': " + e.what());
|
||||
return context.ReturnErrorStatus("Can't copy image file symlink '" + from + "': " + e.what());
|
||||
}
|
||||
|
||||
LOGINFO("Copied image file symlink '%s' to '%s'", from.c_str(), to.c_str())
|
||||
spdlog::info("Copied image file symlink '" + from + "' to '" + to + "'");
|
||||
|
||||
return context.ReturnStatus();
|
||||
return context.ReturnSuccessStatus();
|
||||
}
|
||||
|
||||
try {
|
||||
copy_file(f, t);
|
||||
|
||||
permissions(t, GetParam(command, "read_only") == "true" ?
|
||||
permissions(t, GetParam(context.GetCommand(), "read_only") == "true" ?
|
||||
perms::owner_read | perms::group_read | perms::others_read :
|
||||
perms::owner_read | perms::group_read | perms::others_read |
|
||||
perms::owner_write | perms::group_write);
|
||||
}
|
||||
catch(const filesystem_error& e) {
|
||||
return context.ReturnStatus(false, "Can't copy image file '" + from + "' to '" + to + "': " + e.what());
|
||||
return context.ReturnErrorStatus("Can't copy image file '" + from + "': " + e.what());
|
||||
}
|
||||
|
||||
LOGINFO("Copied image file '%s' to '%s'", from.c_str(), to.c_str())
|
||||
spdlog::info("Copied image file '" + from + "' to '" + to + "'");
|
||||
|
||||
return context.ReturnStatus();
|
||||
return context.ReturnSuccessStatus();
|
||||
}
|
||||
|
||||
bool PiscsiImage::SetImagePermissions(const CommandContext& context, const PbCommand& command) const
|
||||
bool PiscsiImage::SetImagePermissions(const CommandContext& context) const
|
||||
{
|
||||
string filename = GetParam(command, "file");
|
||||
const string filename = GetParam(context.GetCommand(), "file");
|
||||
if (filename.empty()) {
|
||||
return context.ReturnStatus(false, "Missing image filename");
|
||||
return context.ReturnErrorStatus("Missing image filename");
|
||||
}
|
||||
|
||||
if (!CheckDepth(filename)) {
|
||||
return context.ReturnStatus(false, ("Invalid folder hierarchy depth '" + filename + "'").c_str());
|
||||
return context.ReturnErrorStatus("Invalid folder hierarchy depth '" + filename + "'");
|
||||
}
|
||||
|
||||
filename = GetFullName(filename);
|
||||
if (!IsValidSrcFilename(filename)) {
|
||||
return context.ReturnStatus(false, "Can't modify image file '" + filename + "': Invalid name or type");
|
||||
const string full_filename = GetFullName(filename);
|
||||
if (!IsValidSrcFilename(full_filename)) {
|
||||
return context.ReturnErrorStatus("Can't modify image file '" + full_filename + "': Invalid name or type");
|
||||
}
|
||||
|
||||
const bool protect = command.operation() == PROTECT_IMAGE;
|
||||
const bool protect = context.GetCommand().operation() == PROTECT_IMAGE;
|
||||
|
||||
if (protect && !IsReservedFile(context, full_filename, "protect")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
permissions(path(filename), protect ?
|
||||
permissions(path(full_filename), protect ?
|
||||
perms::owner_read | perms::group_read | perms::others_read :
|
||||
perms::owner_read | perms::group_read | perms::others_read |
|
||||
perms::owner_write | perms::group_write);
|
||||
}
|
||||
catch(const filesystem_error& e) {
|
||||
return context.ReturnStatus(false, "Can't " + string(protect ? "protect" : "unprotect") + " image file '" +
|
||||
filename + "': " + e.what());
|
||||
return context.ReturnErrorStatus("Can't " + string(protect ? "protect" : "unprotect") + " image file '" +
|
||||
full_filename + "': " + e.what());
|
||||
}
|
||||
|
||||
if (protect) {
|
||||
LOGINFO("Protected image file '%s'", filename.c_str())
|
||||
}
|
||||
else {
|
||||
LOGINFO("Unprotected image file '%s'", filename.c_str())
|
||||
}
|
||||
spdlog::info((protect ? "Protected" : "Unprotected") + string(" image file '") + full_filename + "'");
|
||||
|
||||
return context.ReturnStatus();
|
||||
return context.ReturnSuccessStatus();
|
||||
}
|
||||
|
||||
bool PiscsiImage::ValidateParams(const CommandContext& context, const PbCommand& command, const string& operation,
|
||||
string& from, string& to) const
|
||||
bool PiscsiImage::IsReservedFile(const CommandContext& context, const string& file, const string& op)
|
||||
{
|
||||
from = GetParam(command, "from");
|
||||
if (from.empty()) {
|
||||
return context.ReturnStatus(false, "Can't " + operation + " image file: Missing source filename");
|
||||
}
|
||||
|
||||
if (!CheckDepth(from)) {
|
||||
return context.ReturnStatus(false, ("Invalid folder hierarchy depth '" + from + "'").c_str());
|
||||
}
|
||||
|
||||
from = GetFullName(from);
|
||||
if (!IsValidSrcFilename(from)) {
|
||||
return context.ReturnStatus(false, "Can't " + operation + " image file: '" + from + "': Invalid name or type");
|
||||
}
|
||||
|
||||
to = GetParam(command, "to");
|
||||
if (to.empty()) {
|
||||
return context.ReturnStatus(false, "Can't " + operation + " image file '" + from + "': Missing destination filename");
|
||||
}
|
||||
|
||||
if (!CheckDepth(to)) {
|
||||
return context.ReturnStatus(false, ("Invalid folder hierarchy depth '" + to + "'").c_str());
|
||||
}
|
||||
|
||||
to = GetFullName(to);
|
||||
if (!IsValidDstFilename(to)) {
|
||||
return context.ReturnStatus(false, "Can't " + operation + " image file '" + from + "' to '" + to + "': File already exists");
|
||||
const auto [id, lun] = StorageDevice::GetIdsForReservedFile(file);
|
||||
if (id != -1) {
|
||||
return context.ReturnErrorStatus("Can't " + op + " image file '" + file +
|
||||
"', it is currently being used by device " + to_string(id) + ":" + to_string(lun));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PiscsiImage::IsValidSrcFilename(const string& filename)
|
||||
bool PiscsiImage::ValidateParams(const CommandContext& context, const string& op, string& from, string& to) const
|
||||
{
|
||||
from = GetParam(context.GetCommand(), "from");
|
||||
if (from.empty()) {
|
||||
return context.ReturnErrorStatus("Can't " + op + " image file: Missing source filename");
|
||||
}
|
||||
|
||||
if (!CheckDepth(from)) {
|
||||
return context.ReturnErrorStatus("Invalid folder hierarchy depth '" + from + "'");
|
||||
}
|
||||
|
||||
to = GetParam(context.GetCommand(), "to");
|
||||
if (to.empty()) {
|
||||
return context.ReturnErrorStatus("Can't " + op + " image file '" + from + "': Missing destination filename");
|
||||
}
|
||||
|
||||
if (!CheckDepth(to)) {
|
||||
return context.ReturnErrorStatus("Invalid folder hierarchy depth '" + to + "'");
|
||||
}
|
||||
|
||||
from = GetFullName(from);
|
||||
if (!IsValidSrcFilename(from)) {
|
||||
return context.ReturnErrorStatus("Can't " + op + " image file: '" + from + "': Invalid name or type");
|
||||
}
|
||||
|
||||
to = GetFullName(to);
|
||||
if (!IsValidDstFilename(to)) {
|
||||
return context.ReturnErrorStatus("Can't " + op + " image file '" + from + "' to '" + to + "': File already exists");
|
||||
}
|
||||
|
||||
if (!IsReservedFile(context, from, op)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateImageFolder(context, to)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PiscsiImage::IsValidSrcFilename(string_view filename)
|
||||
{
|
||||
// Source file must exist and must be a regular file or a symlink
|
||||
path file(filename);
|
||||
return is_regular_file(file) || is_symlink(file);
|
||||
|
||||
error_code error;
|
||||
return is_regular_file(file, error) || is_symlink(file, error);
|
||||
}
|
||||
|
||||
bool PiscsiImage::IsValidDstFilename(const string& filename)
|
||||
bool PiscsiImage::IsValidDstFilename(string_view filename)
|
||||
{
|
||||
// Destination file must not yet exist
|
||||
try {
|
||||
return !exists(path(filename));
|
||||
}
|
||||
catch(const filesystem_error&) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -394,7 +378,7 @@ bool PiscsiImage::ChangeOwner(const CommandContext& context, const path& filenam
|
|||
error_code error;
|
||||
remove(filename, error);
|
||||
|
||||
return context.ReturnStatus(false, "Can't change ownership of '" + string(filename) + "': " + strerror(e));
|
||||
return context.ReturnErrorStatus("Can't change ownership of '" + filename.string() + "': " + strerror(e));
|
||||
}
|
||||
|
||||
permissions(filename, read_only ?
|
||||
|
@ -437,5 +421,5 @@ pair<int, int> PiscsiImage::GetUidAndGid()
|
|||
gid = pwd.pw_gid;
|
||||
}
|
||||
|
||||
return make_pair(uid, gid);
|
||||
return { uid, gid };
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
// Copyright (C) 2021-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -28,22 +28,23 @@ public:
|
|||
void SetDepth(int d) { depth = d; }
|
||||
int GetDepth() const { return depth; }
|
||||
string GetDefaultFolder() const { return default_folder; }
|
||||
string SetDefaultFolder(const string&);
|
||||
bool CreateImage(const CommandContext&, const PbCommand&) const;
|
||||
bool DeleteImage(const CommandContext&, const PbCommand&) const;
|
||||
bool RenameImage(const CommandContext&, const PbCommand&) const;
|
||||
bool CopyImage(const CommandContext&, const PbCommand&) const;
|
||||
bool SetImagePermissions(const CommandContext&, const PbCommand&) const;
|
||||
string SetDefaultFolder(string_view);
|
||||
bool CreateImage(const CommandContext&) const;
|
||||
bool DeleteImage(const CommandContext&) const;
|
||||
bool RenameImage(const CommandContext&) const;
|
||||
bool CopyImage(const CommandContext&) const;
|
||||
bool SetImagePermissions(const CommandContext&) const;
|
||||
|
||||
private:
|
||||
|
||||
bool CheckDepth(string_view) const;
|
||||
string GetFullName(const string& filename) const { return default_folder + "/" + filename; }
|
||||
bool CreateImageFolder(const CommandContext&, const string&) const;
|
||||
bool ValidateParams(const CommandContext&, const PbCommand&, const string&, string&, string&) const;
|
||||
bool CreateImageFolder(const CommandContext&, string_view) const;
|
||||
static bool IsReservedFile(const CommandContext&, const string&, const string&);
|
||||
bool ValidateParams(const CommandContext&, const string&, string&, string&) const;
|
||||
|
||||
static bool IsValidSrcFilename(const string&);
|
||||
static bool IsValidDstFilename(const string&);
|
||||
static bool IsValidSrcFilename(string_view);
|
||||
static bool IsValidDstFilename(string_view);
|
||||
static bool ChangeOwner(const CommandContext&, const path&, bool);
|
||||
static string GetHomeDir();
|
||||
static pair<int, int> GetUidAndGid();
|
||||
|
|
|
@ -3,50 +3,48 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
// Copyright (C) 2021-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/log.h"
|
||||
#include "controllers/controller_manager.h"
|
||||
#include "shared/protobuf_util.h"
|
||||
#include "shared/network_util.h"
|
||||
#include "shared/piscsi_util.h"
|
||||
#include "shared/piscsi_version.h"
|
||||
#include "devices/disk.h"
|
||||
#include "devices/device_factory.h"
|
||||
#include "generated/piscsi_interface.pb.h"
|
||||
#include "piscsi_response.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace std;
|
||||
using namespace filesystem;
|
||||
using namespace piscsi_interface;
|
||||
using namespace piscsi_util;
|
||||
using namespace network_util;
|
||||
using namespace protobuf_util;
|
||||
|
||||
unique_ptr<PbDeviceProperties> PiscsiResponse::GetDeviceProperties(const Device& device) const
|
||||
void PiscsiResponse::GetDeviceProperties(const Device& device, PbDeviceProperties& properties) const
|
||||
{
|
||||
auto properties = make_unique<PbDeviceProperties>();
|
||||
|
||||
// Currently there is only a SCSI controller, i.e. there can always be 32 LUNs
|
||||
properties->set_luns(32);
|
||||
properties->set_read_only(device.IsReadOnly());
|
||||
properties->set_protectable(device.IsProtectable());
|
||||
properties->set_stoppable(device.IsStoppable());
|
||||
properties->set_removable(device.IsRemovable());
|
||||
properties->set_lockable(device.IsLockable());
|
||||
properties->set_supports_file(device.SupportsFile());
|
||||
properties->set_supports_params(device.SupportsParams());
|
||||
properties.set_luns(ControllerManager::GetScsiLunMax());
|
||||
properties.set_read_only(device.IsReadOnly());
|
||||
properties.set_protectable(device.IsProtectable());
|
||||
properties.set_stoppable(device.IsStoppable());
|
||||
properties.set_removable(device.IsRemovable());
|
||||
properties.set_lockable(device.IsLockable());
|
||||
properties.set_supports_file(device.SupportsFile());
|
||||
properties.set_supports_params(device.SupportsParams());
|
||||
|
||||
if (device.SupportsParams()) {
|
||||
for (const auto& [key, value] : device_factory.GetDefaultParams(device.GetType())) {
|
||||
auto& map = *properties->mutable_default_params();
|
||||
for (const auto& [key, value] : device.GetDefaultParams()) {
|
||||
auto& map = *properties.mutable_default_params();
|
||||
map[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& block_size : device_factory.GetSectorSizes(device.GetType())) {
|
||||
properties->add_block_sizes(block_size);
|
||||
properties.add_block_sizes(block_size);
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
void PiscsiResponse::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_info, PbDeviceType type) const
|
||||
|
@ -54,10 +52,10 @@ void PiscsiResponse::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_inf
|
|||
auto type_properties = device_types_info.add_properties();
|
||||
type_properties->set_type(type);
|
||||
const auto device = device_factory.CreateDevice(type, 0, "");
|
||||
type_properties->set_allocated_properties(GetDeviceProperties(*device).release());
|
||||
} //NOSONAR The allocated memory is managed by protobuf
|
||||
GetDeviceProperties(*device, *type_properties->mutable_properties());
|
||||
}
|
||||
|
||||
void PiscsiResponse::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info) const
|
||||
void PiscsiResponse::GetDeviceTypesInfo(PbDeviceTypesInfo& device_types_info) const
|
||||
{
|
||||
// Start with 2 instead of 1. 1 was the removed SASI drive type.
|
||||
int ordinal = 2;
|
||||
|
@ -78,16 +76,15 @@ void PiscsiResponse::GetDevice(const Device& device, PbDevice& pb_device, const
|
|||
pb_device.set_revision(device.GetRevision());
|
||||
pb_device.set_type(device.GetType());
|
||||
|
||||
pb_device.set_allocated_properties(GetDeviceProperties(device).release());
|
||||
GetDeviceProperties(device, *pb_device.mutable_properties());
|
||||
|
||||
auto status = make_unique<PbDeviceStatus>().release(); //NOSONAR The allocated memory is managed by protobuf
|
||||
pb_device.set_allocated_status(status);
|
||||
auto status = pb_device.mutable_status();
|
||||
status->set_protected_(device.IsProtected());
|
||||
status->set_stopped(device.IsStopped());
|
||||
status->set_removed(device.IsRemoved());
|
||||
status->set_locked(device.IsLocked());
|
||||
|
||||
if (device.SupportsParams()) { //NOSONAR The allocated memory is managed by protobuf
|
||||
if (device.SupportsParams()) {
|
||||
for (const auto& [key, value] : device.GetParams()) {
|
||||
SetParam(pb_device, key, value);
|
||||
}
|
||||
|
@ -100,11 +97,9 @@ void PiscsiResponse::GetDevice(const Device& device, PbDevice& pb_device, const
|
|||
|
||||
const auto storage_device = dynamic_cast<const StorageDevice *>(&device);
|
||||
if (storage_device != nullptr) {
|
||||
auto image_file = make_unique<PbImageFile>().release();
|
||||
GetImageFile(*image_file, default_folder, device.IsReady() ? storage_device->GetFilename() : "");
|
||||
pb_device.set_allocated_file(image_file);
|
||||
GetImageFile(*pb_device.mutable_file(), default_folder, device.IsReady() ? storage_device->GetFilename() : "");
|
||||
}
|
||||
} //NOSONAR The allocated memory is managed by protobuf
|
||||
}
|
||||
|
||||
bool PiscsiResponse::GetImageFile(PbImageFile& image_file, const string& default_folder, const string& filename) const
|
||||
{
|
||||
|
@ -112,13 +107,13 @@ bool PiscsiResponse::GetImageFile(PbImageFile& image_file, const string& default
|
|||
image_file.set_name(filename);
|
||||
image_file.set_type(device_factory.GetTypeForFile(filename));
|
||||
|
||||
const string f = filename[0] == '/' ? filename : default_folder + "/" + filename;
|
||||
const path p(filename[0] == '/' ? filename : default_folder + "/" + filename);
|
||||
|
||||
image_file.set_read_only(access(f.c_str(), W_OK));
|
||||
image_file.set_read_only(access(p.c_str(), W_OK));
|
||||
|
||||
// filesystem::file_size cannot be used here because gcc < 10.3.0 cannot handle files of more than 2 GiB
|
||||
if (struct stat st; !stat(f.c_str(), &st) && !S_ISDIR(st.st_mode)) {
|
||||
image_file.set_size(st.st_size);
|
||||
error_code error;
|
||||
if (is_regular_file(p, error) || (is_symlink(p, error) && !is_block_file(p, error))) {
|
||||
image_file.set_size(file_size(p));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -127,92 +122,68 @@ bool PiscsiResponse::GetImageFile(PbImageFile& image_file, const string& default
|
|||
}
|
||||
|
||||
void PiscsiResponse::GetAvailableImages(PbImageFilesInfo& image_files_info, const string& default_folder,
|
||||
const string& folder, const string& folder_pattern, const string& file_pattern, int scan_depth) const
|
||||
const string& folder_pattern, const string& file_pattern, int scan_depth) const
|
||||
{
|
||||
if (scan_depth-- < 0) {
|
||||
const path default_path(default_folder);
|
||||
if (!is_directory(default_path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string folder_pattern_lower = folder_pattern;
|
||||
transform(folder_pattern_lower.begin(), folder_pattern_lower.end(), folder_pattern_lower.begin(), ::tolower);
|
||||
string folder_pattern_lower;
|
||||
ranges::transform(folder_pattern, back_inserter(folder_pattern_lower), ::tolower);
|
||||
|
||||
string file_pattern_lower = file_pattern;
|
||||
transform(file_pattern_lower.begin(), file_pattern_lower.end(), file_pattern_lower.begin(), ::tolower);
|
||||
string file_pattern_lower;
|
||||
ranges::transform(file_pattern, back_inserter(file_pattern_lower), ::tolower);
|
||||
|
||||
DIR *d = opendir(folder.c_str());
|
||||
if (d == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// C++ filesystem cannot be used here because gcc < 10.3.0 cannot handle files of more than 2 GiB
|
||||
const dirent *dir;
|
||||
while ((dir = readdir(d))) {
|
||||
string filename = GetNextImageFile(dir, folder);
|
||||
if (filename.empty()) {
|
||||
for (auto it = recursive_directory_iterator(default_path, directory_options::follow_directory_symlink);
|
||||
it != recursive_directory_iterator(); it++) {
|
||||
if (it.depth() > scan_depth) {
|
||||
it.disable_recursion_pending();
|
||||
continue;
|
||||
}
|
||||
|
||||
string name_lower = dir->d_name;
|
||||
if (!file_pattern.empty()) {
|
||||
transform(name_lower.begin(), name_lower.end(), name_lower.begin(), ::tolower);
|
||||
}
|
||||
const string parent = it->path().parent_path().string();
|
||||
|
||||
if (dir->d_type == DT_DIR) {
|
||||
if (folder_pattern_lower.empty() || name_lower.find(folder_pattern_lower) != string::npos) {
|
||||
GetAvailableImages(image_files_info, default_folder, filename, folder_pattern,
|
||||
file_pattern, scan_depth);
|
||||
}
|
||||
const string folder = parent.size() > default_folder.size() ? parent.substr(default_folder.size() + 1) : "";
|
||||
|
||||
if (!FilterMatches(folder, folder_pattern_lower) || !FilterMatches(it->path().filename().string(), file_pattern_lower)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file_pattern_lower.empty() || name_lower.find(file_pattern_lower) != string::npos) {
|
||||
if (auto image_file = make_unique<PbImageFile>(); GetImageFile(*image_file.get(), default_folder, filename)) {
|
||||
GetImageFile(*image_files_info.add_image_files(), default_folder,
|
||||
filename.substr(default_folder.length() + 1));
|
||||
}
|
||||
if (!ValidateImageFile(it->path())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const string filename = folder.empty() ?
|
||||
it->path().filename().string() : folder + "/" + it->path().filename().string();
|
||||
if (PbImageFile image_file; GetImageFile(image_file, default_folder, filename)) {
|
||||
GetImageFile(*image_files_info.add_image_files(), default_folder, filename);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
unique_ptr<PbImageFilesInfo> PiscsiResponse::GetAvailableImages(PbResult& result, const string& default_folder,
|
||||
void PiscsiResponse::GetImageFilesInfo(PbImageFilesInfo& image_files_info, const string& default_folder,
|
||||
const string& folder_pattern, const string& file_pattern, int scan_depth) const
|
||||
{
|
||||
auto image_files_info = make_unique<PbImageFilesInfo>();
|
||||
image_files_info.set_default_image_folder(default_folder);
|
||||
image_files_info.set_depth(scan_depth);
|
||||
|
||||
image_files_info->set_default_image_folder(default_folder);
|
||||
image_files_info->set_depth(scan_depth);
|
||||
|
||||
GetAvailableImages(*image_files_info, default_folder, default_folder, folder_pattern,
|
||||
file_pattern, scan_depth);
|
||||
|
||||
result.set_status(true);
|
||||
|
||||
return image_files_info;
|
||||
GetAvailableImages(image_files_info, default_folder, folder_pattern, file_pattern, scan_depth);
|
||||
}
|
||||
|
||||
void PiscsiResponse::GetAvailableImages(PbResult& result, PbServerInfo& server_info, const string& default_folder,
|
||||
void PiscsiResponse::GetAvailableImages(PbServerInfo& server_info, const string& default_folder,
|
||||
const string& folder_pattern, const string& file_pattern, int scan_depth) const
|
||||
{
|
||||
auto image_files_info = GetAvailableImages(result, default_folder, folder_pattern, file_pattern, scan_depth);
|
||||
image_files_info->set_default_image_folder(default_folder);
|
||||
server_info.set_allocated_image_files_info(image_files_info.release());
|
||||
server_info.mutable_image_files_info()->set_default_image_folder(default_folder);
|
||||
|
||||
result.set_status(true); //NOSONAR The allocated memory is managed by protobuf
|
||||
GetImageFilesInfo(*server_info.mutable_image_files_info(), default_folder, folder_pattern, file_pattern, scan_depth);
|
||||
}
|
||||
|
||||
unique_ptr<PbReservedIdsInfo> PiscsiResponse::GetReservedIds(PbResult& result, const unordered_set<int>& ids) const
|
||||
void PiscsiResponse::GetReservedIds(PbReservedIdsInfo& reserved_ids_info, const unordered_set<int>& ids) const
|
||||
{
|
||||
auto reserved_ids_info = make_unique<PbReservedIdsInfo>();
|
||||
for (const int id : ids) {
|
||||
reserved_ids_info->add_ids(id);
|
||||
reserved_ids_info.add_ids(id);
|
||||
}
|
||||
|
||||
result.set_status(true);
|
||||
|
||||
return reserved_ids_info;
|
||||
}
|
||||
|
||||
void PiscsiResponse::GetDevices(const unordered_set<shared_ptr<PrimaryDevice>>& devices, PbServerInfo& server_info,
|
||||
|
@ -232,7 +203,7 @@ void PiscsiResponse::GetDevicesInfo(const unordered_set<shared_ptr<PrimaryDevice
|
|||
// If no device list was provided in the command get information on all devices
|
||||
if (!command.devices_size()) {
|
||||
for (const auto& device : devices) {
|
||||
id_sets.insert(make_pair(device->GetId(), device->GetLun()));
|
||||
id_sets.insert({ device->GetId(), device->GetLun() });
|
||||
}
|
||||
}
|
||||
// Otherwise get information on the devices provided in the command
|
||||
|
@ -243,8 +214,7 @@ void PiscsiResponse::GetDevicesInfo(const unordered_set<shared_ptr<PrimaryDevice
|
|||
}
|
||||
}
|
||||
|
||||
auto devices_info = make_unique<PbDevicesInfo>();
|
||||
|
||||
auto devices_info = result.mutable_devices_info();
|
||||
for (const auto& [id, lun] : id_sets) {
|
||||
for (const auto& d : devices) {
|
||||
if (d->GetId() == id && d->GetLun() == lun) {
|
||||
|
@ -254,244 +224,239 @@ void PiscsiResponse::GetDevicesInfo(const unordered_set<shared_ptr<PrimaryDevice
|
|||
}
|
||||
}
|
||||
|
||||
result.set_allocated_devices_info(devices_info.release());
|
||||
result.set_status(true);
|
||||
}
|
||||
|
||||
unique_ptr<PbDeviceTypesInfo> PiscsiResponse::GetDeviceTypesInfo(PbResult& result) const
|
||||
void PiscsiResponse::GetServerInfo(PbServerInfo& server_info, const PbCommand& command,
|
||||
const unordered_set<shared_ptr<PrimaryDevice>>& devices, const unordered_set<int>& reserved_ids,
|
||||
const string& default_folder, int scan_depth) const
|
||||
{
|
||||
auto device_types_info = make_unique<PbDeviceTypesInfo>();
|
||||
|
||||
GetAllDeviceTypeProperties(*device_types_info);
|
||||
|
||||
result.set_status(true);
|
||||
|
||||
return device_types_info;
|
||||
}
|
||||
|
||||
unique_ptr<PbServerInfo> PiscsiResponse::GetServerInfo(const unordered_set<shared_ptr<PrimaryDevice>>& devices,
|
||||
PbResult& result, const unordered_set<int>& reserved_ids, const string& current_log_level,
|
||||
const string& default_folder, const string& folder_pattern, const string& file_pattern, int scan_depth) const
|
||||
{
|
||||
auto server_info = make_unique<PbServerInfo>();
|
||||
|
||||
server_info->set_allocated_version_info(GetVersionInfo(result).release());
|
||||
server_info->set_allocated_log_level_info(GetLogLevelInfo(result, current_log_level).release()); //NOSONAR The allocated memory is managed by protobuf
|
||||
GetAllDeviceTypeProperties(*server_info->mutable_device_types_info()); //NOSONAR The allocated memory is managed by protobuf
|
||||
GetAvailableImages(result, *server_info, default_folder, folder_pattern, file_pattern, scan_depth);
|
||||
server_info->set_allocated_network_interfaces_info(GetNetworkInterfacesInfo(result).release());
|
||||
server_info->set_allocated_mapping_info(GetMappingInfo(result).release()); //NOSONAR The allocated memory is managed by protobuf
|
||||
GetDevices(devices, *server_info, default_folder); //NOSONAR The allocated memory is managed by protobuf
|
||||
server_info->set_allocated_reserved_ids_info(GetReservedIds(result, reserved_ids).release());
|
||||
server_info->set_allocated_operation_info(GetOperationInfo(result, scan_depth).release()); //NOSONAR The allocated memory is managed by protobuf
|
||||
|
||||
result.set_status(true);
|
||||
|
||||
return server_info;
|
||||
}
|
||||
|
||||
unique_ptr<PbVersionInfo> PiscsiResponse::GetVersionInfo(PbResult& result) const
|
||||
{
|
||||
auto version_info = make_unique<PbVersionInfo>();
|
||||
|
||||
version_info->set_major_version(piscsi_major_version);
|
||||
version_info->set_minor_version(piscsi_minor_version);
|
||||
version_info->set_patch_version(piscsi_patch_version);
|
||||
|
||||
result.set_status(true);
|
||||
|
||||
return version_info;
|
||||
}
|
||||
|
||||
unique_ptr<PbLogLevelInfo> PiscsiResponse::GetLogLevelInfo(PbResult& result, const string& current_log_level) const
|
||||
{
|
||||
auto log_level_info = make_unique<PbLogLevelInfo>();
|
||||
|
||||
for (const auto& log_level : log_levels) {
|
||||
log_level_info->add_log_levels(log_level);
|
||||
const vector<string> command_operations = Split(GetParam(command, "operations"), ',');
|
||||
set<string, less<>> operations;
|
||||
for (const string& operation : command_operations) {
|
||||
string op;
|
||||
ranges::transform(operation, back_inserter(op), ::toupper);
|
||||
operations.insert(op);
|
||||
}
|
||||
|
||||
log_level_info->set_current_log_level(current_log_level);
|
||||
|
||||
result.set_status(true);
|
||||
|
||||
return log_level_info;
|
||||
}
|
||||
|
||||
unique_ptr<PbNetworkInterfacesInfo> PiscsiResponse::GetNetworkInterfacesInfo(PbResult& result) const
|
||||
{
|
||||
auto network_interfaces_info = make_unique<PbNetworkInterfacesInfo>();
|
||||
|
||||
for (const auto& network_interface : device_factory.GetNetworkInterfaces()) {
|
||||
network_interfaces_info->add_name(network_interface);
|
||||
if (!operations.empty()) {
|
||||
spdlog::trace("Requested operation(s): " + Join(operations, ","));
|
||||
}
|
||||
|
||||
result.set_status(true);
|
||||
if (HasOperation(operations, PbOperation::VERSION_INFO)) {
|
||||
GetVersionInfo(*server_info.mutable_version_info());
|
||||
}
|
||||
|
||||
return network_interfaces_info;
|
||||
if (HasOperation(operations, PbOperation::LOG_LEVEL_INFO)) {
|
||||
GetLogLevelInfo(*server_info.mutable_log_level_info());
|
||||
}
|
||||
|
||||
if (HasOperation(operations, PbOperation::DEVICE_TYPES_INFO)) {
|
||||
GetDeviceTypesInfo(*server_info.mutable_device_types_info());
|
||||
}
|
||||
|
||||
if (HasOperation(operations, PbOperation::DEFAULT_IMAGE_FILES_INFO)) {
|
||||
GetAvailableImages(server_info, default_folder, GetParam(command, "folder_pattern"),
|
||||
GetParam(command, "file_pattern"), scan_depth);
|
||||
}
|
||||
|
||||
if (HasOperation(operations, PbOperation::NETWORK_INTERFACES_INFO)) {
|
||||
GetNetworkInterfacesInfo(*server_info.mutable_network_interfaces_info());
|
||||
}
|
||||
|
||||
if (HasOperation(operations, PbOperation::MAPPING_INFO)) {
|
||||
GetMappingInfo(*server_info.mutable_mapping_info());
|
||||
}
|
||||
|
||||
if (HasOperation(operations, PbOperation::STATISTICS_INFO)) {
|
||||
GetStatisticsInfo(*server_info.mutable_statistics_info(), devices);
|
||||
}
|
||||
|
||||
if (HasOperation(operations, PbOperation::DEVICES_INFO)) {
|
||||
GetDevices(devices, server_info, default_folder);
|
||||
}
|
||||
|
||||
if (HasOperation(operations, PbOperation::RESERVED_IDS_INFO)) {
|
||||
GetReservedIds(*server_info.mutable_reserved_ids_info(), reserved_ids);
|
||||
}
|
||||
|
||||
if (HasOperation(operations, PbOperation::OPERATION_INFO)) {
|
||||
GetOperationInfo(*server_info.mutable_operation_info(), scan_depth);
|
||||
}
|
||||
}
|
||||
|
||||
unique_ptr<PbMappingInfo> PiscsiResponse::GetMappingInfo(PbResult& result) const
|
||||
void PiscsiResponse::GetVersionInfo(PbVersionInfo& version_info) const
|
||||
{
|
||||
auto mapping_info = make_unique<PbMappingInfo>();
|
||||
version_info.set_major_version(piscsi_major_version);
|
||||
version_info.set_minor_version(piscsi_minor_version);
|
||||
version_info.set_patch_version(piscsi_patch_version);
|
||||
}
|
||||
|
||||
void PiscsiResponse::GetLogLevelInfo(PbLogLevelInfo& log_level_info) const
|
||||
{
|
||||
for (const auto& log_level : spdlog::level::level_string_views) {
|
||||
log_level_info.add_log_levels(log_level.data());
|
||||
}
|
||||
|
||||
log_level_info.set_current_log_level(spdlog::level::level_string_views[spdlog::get_level()].data());
|
||||
}
|
||||
|
||||
void PiscsiResponse::GetNetworkInterfacesInfo(PbNetworkInterfacesInfo& network_interfaces_info) const
|
||||
{
|
||||
for (const auto& network_interface : GetNetworkInterfaces()) {
|
||||
network_interfaces_info.add_name(network_interface);
|
||||
}
|
||||
}
|
||||
|
||||
void PiscsiResponse::GetMappingInfo(PbMappingInfo& mapping_info) const
|
||||
{
|
||||
for (const auto& [name, type] : device_factory.GetExtensionMapping()) {
|
||||
(*mapping_info->mutable_mapping())[name] = type;
|
||||
(*mapping_info.mutable_mapping())[name] = type;
|
||||
}
|
||||
|
||||
result.set_status(true);
|
||||
|
||||
return mapping_info;
|
||||
}
|
||||
|
||||
unique_ptr<PbOperationInfo> PiscsiResponse::GetOperationInfo(PbResult& result, int depth) const
|
||||
void PiscsiResponse::GetStatisticsInfo(PbStatisticsInfo& statistics_info,
|
||||
const unordered_set<shared_ptr<PrimaryDevice>>& devices) const
|
||||
{
|
||||
auto operation_info = make_unique<PbOperationInfo>();
|
||||
|
||||
auto operation = CreateOperation(*operation_info, ATTACH, "Attach device, device-specific parameters are required");
|
||||
AddOperationParameter(*operation, "name", "Image file name in case of a mass storage device").release();
|
||||
AddOperationParameter(*operation, "interface", "Comma-separated prioritized network interface list").release();
|
||||
AddOperationParameter(*operation, "inet", "IP address and netmask of the network bridge").release();
|
||||
AddOperationParameter(*operation, "cmd", "Print command for the printer device").release();
|
||||
operation.release();
|
||||
|
||||
CreateOperation(*operation_info, DETACH, "Detach device, device-specific parameters are required").release();
|
||||
|
||||
CreateOperation(*operation_info, DETACH_ALL, "Detach all devices").release();
|
||||
|
||||
CreateOperation(*operation_info, START, "Start device, device-specific parameters are required").release();
|
||||
|
||||
CreateOperation(*operation_info, STOP, "Stop device, device-specific parameters are required").release();
|
||||
|
||||
operation = CreateOperation(*operation_info, INSERT, "Insert medium, device-specific parameters are required");
|
||||
AddOperationParameter(*operation, "file", "Image file name", "", true).release();
|
||||
operation.release();
|
||||
|
||||
CreateOperation(*operation_info, EJECT, "Eject medium, device-specific parameters are required").release();
|
||||
|
||||
CreateOperation(*operation_info, PROTECT, "Protect medium, device-specific parameters are required").release();
|
||||
|
||||
CreateOperation(*operation_info, UNPROTECT, "Unprotect medium, device-specific parameters are required").release();
|
||||
|
||||
operation = CreateOperation(*operation_info, SERVER_INFO, "Get piscsi server information");
|
||||
if (depth) {
|
||||
AddOperationParameter(*operation, "folder_pattern", "Pattern for filtering image folder names").release();
|
||||
for (const auto& device : devices) {
|
||||
for (const auto& statistics : device->GetStatistics()) {
|
||||
auto s = statistics_info.add_statistics();
|
||||
s->set_id(statistics.id());
|
||||
s->set_unit(statistics.unit());
|
||||
s->set_category(statistics.category());
|
||||
s->set_key(statistics.key());
|
||||
s->set_value(statistics.value());
|
||||
}
|
||||
}
|
||||
AddOperationParameter(*operation, "file_pattern", "Pattern for filtering image file names").release();
|
||||
operation.release();
|
||||
|
||||
CreateOperation(*operation_info, VERSION_INFO, "Get piscsi server version").release();
|
||||
|
||||
CreateOperation(*operation_info, DEVICES_INFO, "Get information on attached devices").release();
|
||||
|
||||
CreateOperation(*operation_info, DEVICE_TYPES_INFO, "Get device properties by device type").release();
|
||||
|
||||
operation = CreateOperation(*operation_info, DEFAULT_IMAGE_FILES_INFO, "Get information on available image files");
|
||||
if (depth) {
|
||||
AddOperationParameter(*operation, "folder_pattern", "Pattern for filtering image folder names").release();
|
||||
}
|
||||
AddOperationParameter(*operation, "file_pattern", "Pattern for filtering image file names").release();
|
||||
operation.release();
|
||||
|
||||
operation = CreateOperation(*operation_info, IMAGE_FILE_INFO, "Get information on image file");
|
||||
AddOperationParameter(*operation, "file", "Image file name", "", true).release();
|
||||
operation.release();
|
||||
|
||||
CreateOperation(*operation_info, LOG_LEVEL_INFO, "Get log level information").release();
|
||||
|
||||
CreateOperation(*operation_info, NETWORK_INTERFACES_INFO, "Get the available network interfaces").release();
|
||||
|
||||
CreateOperation(*operation_info, MAPPING_INFO, "Get mapping of extensions to device types").release();
|
||||
|
||||
CreateOperation(*operation_info, RESERVED_IDS_INFO, "Get list of reserved device IDs").release();
|
||||
|
||||
operation = CreateOperation(*operation_info, DEFAULT_FOLDER, "Set default image file folder");
|
||||
AddOperationParameter(*operation, "folder", "Default image file folder name", "", true).release();
|
||||
operation.release();
|
||||
|
||||
operation = CreateOperation(*operation_info, LOG_LEVEL, "Set log level");
|
||||
AddOperationParameter(*operation, "level", "New log level", "", true).release();
|
||||
operation.release();
|
||||
|
||||
operation = CreateOperation(*operation_info, RESERVE_IDS, "Reserve device IDs");
|
||||
AddOperationParameter(*operation, "ids", "Comma-separated device ID list", "", true).release();
|
||||
operation.release();
|
||||
|
||||
operation = CreateOperation(*operation_info, SHUT_DOWN, "Shut down or reboot");
|
||||
auto parameter = AddOperationParameter(*operation, "mode", "Shutdown mode", "", true).release();
|
||||
parameter->add_permitted_values("piscsi");
|
||||
// System shutdown/reboot requires root permissions
|
||||
if (!getuid()) {
|
||||
parameter->add_permitted_values("system");
|
||||
parameter->add_permitted_values("reboot");
|
||||
}
|
||||
operation.release();
|
||||
|
||||
operation = CreateOperation(*operation_info, CREATE_IMAGE, "Create an image file");
|
||||
AddOperationParameter(*operation, "file", "Image file name", "", true).release();
|
||||
AddOperationParameter(*operation, "size", "Image file size in bytes", "", true).release();
|
||||
parameter = AddOperationParameter(*operation, "read_only", "Read-only flag", "false").release();
|
||||
parameter->add_permitted_values("true");
|
||||
parameter->add_permitted_values("false");
|
||||
operation.release();
|
||||
|
||||
operation = CreateOperation(*operation_info, DELETE_IMAGE, "Delete image file");
|
||||
AddOperationParameter(*operation, "file", "Image file name", "", true).release();
|
||||
operation.release();
|
||||
|
||||
operation = CreateOperation(*operation_info, RENAME_IMAGE, "Rename image file");
|
||||
AddOperationParameter(*operation, "from", "Source image file name", "", true).release();
|
||||
AddOperationParameter(*operation, "to", "Destination image file name", "", true).release();
|
||||
operation.release();
|
||||
|
||||
operation = CreateOperation(*operation_info, COPY_IMAGE, "Copy image file");
|
||||
AddOperationParameter(*operation, "from", "Source image file name", "", true).release();
|
||||
AddOperationParameter(*operation, "to", "Destination image file name", "", true).release();
|
||||
parameter = AddOperationParameter(*operation, "read_only", "Read-only flag", "false").release();
|
||||
parameter->add_permitted_values("true");
|
||||
parameter->add_permitted_values("false");
|
||||
operation.release();
|
||||
|
||||
operation = CreateOperation(*operation_info, PROTECT_IMAGE, "Write-protect image file");
|
||||
AddOperationParameter(*operation, "file", "Image file name", "", true).release();
|
||||
operation.release();
|
||||
|
||||
operation = CreateOperation(*operation_info, UNPROTECT_IMAGE, "Make image file writable");
|
||||
AddOperationParameter(*operation, "file", "Image file name", "", true).release();
|
||||
operation.release();
|
||||
|
||||
operation = CreateOperation(*operation_info, CHECK_AUTHENTICATION, "Check whether an authentication token is valid");
|
||||
AddOperationParameter(*operation, "token", "Authentication token to be checked", "", true).release();
|
||||
operation.release();
|
||||
|
||||
CreateOperation(*operation_info, OPERATION_INFO, "Get operation meta data").release();
|
||||
|
||||
result.set_status(true);
|
||||
|
||||
return operation_info;
|
||||
}
|
||||
|
||||
unique_ptr<PbOperationMetaData> PiscsiResponse::CreateOperation(PbOperationInfo& operation_info, const PbOperation& operation,
|
||||
void PiscsiResponse::GetOperationInfo(PbOperationInfo& operation_info, int depth) const
|
||||
{
|
||||
auto operation = CreateOperation(operation_info, ATTACH, "Attach device, device-specific parameters are required");
|
||||
AddOperationParameter(*operation, "name", "Image file name in case of a mass storage device");
|
||||
AddOperationParameter(*operation, "interface", "Comma-separated prioritized network interface list");
|
||||
AddOperationParameter(*operation, "inet", "IP address and netmask of the network bridge");
|
||||
AddOperationParameter(*operation, "cmd", "Print command for the printer device");
|
||||
|
||||
CreateOperation(operation_info, DETACH, "Detach device, device-specific parameters are required");
|
||||
|
||||
CreateOperation(operation_info, DETACH_ALL, "Detach all devices");
|
||||
|
||||
CreateOperation(operation_info, START, "Start device, device-specific parameters are required");
|
||||
|
||||
CreateOperation(operation_info, STOP, "Stop device, device-specific parameters are required");
|
||||
|
||||
operation = CreateOperation(operation_info, INSERT, "Insert medium, device-specific parameters are required");
|
||||
AddOperationParameter(*operation, "file", "Image file name", "", true);
|
||||
|
||||
CreateOperation(operation_info, EJECT, "Eject medium, device-specific parameters are required");
|
||||
|
||||
CreateOperation(operation_info, PROTECT, "Protect medium, device-specific parameters are required");
|
||||
|
||||
CreateOperation(operation_info, UNPROTECT, "Unprotect medium, device-specific parameters are required");
|
||||
|
||||
operation = CreateOperation(operation_info, SERVER_INFO, "Get piscsi server information");
|
||||
if (depth) {
|
||||
AddOperationParameter(*operation, "folder_pattern", "Pattern for filtering image folder names");
|
||||
}
|
||||
AddOperationParameter(*operation, "file_pattern", "Pattern for filtering image file names");
|
||||
|
||||
CreateOperation(operation_info, VERSION_INFO, "Get piscsi server version");
|
||||
|
||||
CreateOperation(operation_info, DEVICES_INFO, "Get information on attached devices");
|
||||
|
||||
CreateOperation(operation_info, DEVICE_TYPES_INFO, "Get device properties by device type");
|
||||
|
||||
operation = CreateOperation(operation_info, DEFAULT_IMAGE_FILES_INFO, "Get information on available image files");
|
||||
if (depth) {
|
||||
AddOperationParameter(*operation, "folder_pattern", "Pattern for filtering image folder names");
|
||||
}
|
||||
AddOperationParameter(*operation, "file_pattern", "Pattern for filtering image file names");
|
||||
|
||||
operation = CreateOperation(operation_info, IMAGE_FILE_INFO, "Get information on image file");
|
||||
AddOperationParameter(*operation, "file", "Image file name", "", true);
|
||||
|
||||
CreateOperation(operation_info, LOG_LEVEL_INFO, "Get log level information");
|
||||
|
||||
CreateOperation(operation_info, NETWORK_INTERFACES_INFO, "Get the available network interfaces");
|
||||
|
||||
CreateOperation(operation_info, MAPPING_INFO, "Get mapping of extensions to device types");
|
||||
|
||||
CreateOperation(operation_info, STATISTICS_INFO, "Get statistics");
|
||||
|
||||
CreateOperation(operation_info, RESERVED_IDS_INFO, "Get list of reserved device IDs");
|
||||
|
||||
operation = CreateOperation(operation_info, DEFAULT_FOLDER, "Set default image file folder");
|
||||
AddOperationParameter(*operation, "folder", "Default image file folder name", "", true);
|
||||
|
||||
operation = CreateOperation(operation_info, LOG_LEVEL, "Set log level");
|
||||
AddOperationParameter(*operation, "level", "New log level", "", true);
|
||||
|
||||
operation = CreateOperation(operation_info, RESERVE_IDS, "Reserve device IDs");
|
||||
AddOperationParameter(*operation, "ids", "Comma-separated device ID list", "", true);
|
||||
|
||||
operation = CreateOperation(operation_info, SHUT_DOWN, "Shut down or reboot");
|
||||
if (getuid()) {
|
||||
AddOperationParameter(*operation, "mode", "Shutdown mode", "", true, { "rascsi" } );
|
||||
}
|
||||
else {
|
||||
// System shutdown/reboot requires root permissions
|
||||
AddOperationParameter(*operation, "mode", "Shutdown mode", "", true, { "rascsi", "system", "reboot" } );
|
||||
}
|
||||
|
||||
operation = CreateOperation(operation_info, CREATE_IMAGE, "Create an image file");
|
||||
AddOperationParameter(*operation, "file", "Image file name", "", true);
|
||||
AddOperationParameter(*operation, "size", "Image file size in bytes", "", true);
|
||||
AddOperationParameter(*operation, "read_only", "Read-only flag", "false", false, { "true", "false" } );
|
||||
|
||||
operation = CreateOperation(operation_info, DELETE_IMAGE, "Delete image file");
|
||||
AddOperationParameter(*operation, "file", "Image file name", "", true);
|
||||
|
||||
operation = CreateOperation(operation_info, RENAME_IMAGE, "Rename image file");
|
||||
AddOperationParameter(*operation, "from", "Source image file name", "", true);
|
||||
AddOperationParameter(*operation, "to", "Destination image file name", "", true);
|
||||
|
||||
operation = CreateOperation(operation_info, COPY_IMAGE, "Copy image file");
|
||||
AddOperationParameter(*operation, "from", "Source image file name", "", true);
|
||||
AddOperationParameter(*operation, "to", "Destination image file name", "", true);
|
||||
AddOperationParameter(*operation, "read_only", "Read-only flag", "false", false, { "true", "false" } );
|
||||
|
||||
operation = CreateOperation(operation_info, PROTECT_IMAGE, "Write-protect image file");
|
||||
AddOperationParameter(*operation, "file", "Image file name", "", true);
|
||||
|
||||
operation = CreateOperation(operation_info, UNPROTECT_IMAGE, "Make image file writable");
|
||||
AddOperationParameter(*operation, "file", "Image file name", "", true);
|
||||
|
||||
operation = CreateOperation(operation_info, CHECK_AUTHENTICATION, "Check whether an authentication token is valid");
|
||||
AddOperationParameter(*operation, "token", "Authentication token to be checked", "", true);
|
||||
|
||||
CreateOperation(operation_info, OPERATION_INFO, "Get operation meta data");
|
||||
}
|
||||
|
||||
// This method returns a raw pointer because protobuf does not have support for smart pointers
|
||||
PbOperationMetaData *PiscsiResponse::CreateOperation(PbOperationInfo& operation_info, const PbOperation& operation,
|
||||
const string& description) const
|
||||
{
|
||||
auto meta_data = make_unique<PbOperationMetaData>();
|
||||
meta_data->set_server_side_name(PbOperation_Name(operation));
|
||||
meta_data->set_description(description);
|
||||
PbOperationMetaData meta_data;
|
||||
meta_data.set_server_side_name(PbOperation_Name(operation));
|
||||
meta_data.set_description(description);
|
||||
int ordinal = PbOperation_descriptor()->FindValueByName(PbOperation_Name(operation))->index();
|
||||
(*operation_info.mutable_operations())[ordinal] = *meta_data.release();
|
||||
return unique_ptr<PbOperationMetaData>(&(*operation_info.mutable_operations())[ordinal]);
|
||||
(*operation_info.mutable_operations())[ordinal] = meta_data;
|
||||
return &(*operation_info.mutable_operations())[ordinal];
|
||||
}
|
||||
|
||||
unique_ptr<PbOperationParameter> PiscsiResponse::AddOperationParameter(PbOperationMetaData& meta_data,
|
||||
const string& name, const string& description, const string& default_value, bool is_mandatory) const
|
||||
void PiscsiResponse::AddOperationParameter(PbOperationMetaData& meta_data, const string& name,
|
||||
const string& description, const string& default_value, bool is_mandatory,
|
||||
const vector<string>& permitted_values) const
|
||||
{
|
||||
auto parameter = unique_ptr<PbOperationParameter>(meta_data.add_parameters());
|
||||
auto parameter = meta_data.add_parameters();
|
||||
parameter->set_name(name);
|
||||
parameter->set_description(description);
|
||||
parameter->set_default_value(default_value);
|
||||
parameter->set_is_mandatory(is_mandatory);
|
||||
|
||||
return parameter;
|
||||
for (const auto& permitted_value : permitted_values) {
|
||||
parameter->add_permitted_values(permitted_value);
|
||||
}
|
||||
}
|
||||
|
||||
set<id_set> PiscsiResponse::MatchDevices(const unordered_set<shared_ptr<PrimaryDevice>>& devices, PbResult& result,
|
||||
|
@ -503,7 +468,7 @@ set<id_set> PiscsiResponse::MatchDevices(const unordered_set<shared_ptr<PrimaryD
|
|||
bool has_device = false;
|
||||
for (const auto& d : devices) {
|
||||
if (d->GetId() == device.id() && d->GetLun() == device.unit()) {
|
||||
id_sets.insert(make_pair(device.id(), device.unit()));
|
||||
id_sets.insert({ device.id(), device.unit() });
|
||||
has_device = true;
|
||||
break;
|
||||
}
|
||||
|
@ -513,7 +478,7 @@ set<id_set> PiscsiResponse::MatchDevices(const unordered_set<shared_ptr<PrimaryD
|
|||
id_sets.clear();
|
||||
|
||||
result.set_status(false);
|
||||
result.set_msg("No device for ID " + to_string(device.id()) + ", unit " + to_string(device.unit()));
|
||||
result.set_msg("No device for " + to_string(device.id()) + ":" + to_string(device.unit()));
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -522,30 +487,50 @@ set<id_set> PiscsiResponse::MatchDevices(const unordered_set<shared_ptr<PrimaryD
|
|||
return id_sets;
|
||||
}
|
||||
|
||||
string PiscsiResponse::GetNextImageFile(const dirent *dir, const string& folder)
|
||||
bool PiscsiResponse::ValidateImageFile(const path& path)
|
||||
{
|
||||
// Ignore unknown folder types and folder names starting with '.'
|
||||
if ((dir->d_type != DT_REG && dir->d_type != DT_DIR && dir->d_type != DT_LNK && dir->d_type != DT_BLK)
|
||||
|| dir->d_name[0] == '.') {
|
||||
return "";
|
||||
if (path.filename().string().starts_with(".")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const string filename = folder + "/" + dir->d_name;
|
||||
filesystem::path p(path);
|
||||
|
||||
const bool file_exists = exists(path(filename));
|
||||
|
||||
// filesystem::file_size cannot be used here because gcc < 10.3.0 cannot handle files of more than 2 GiB
|
||||
struct stat st;
|
||||
stat(filename.c_str(), &st);
|
||||
if (dir->d_type == DT_REG && file_exists && !st.st_size) {
|
||||
LOGWARN("File '%s' in image folder '%s' is empty", dir->d_name, folder.c_str())
|
||||
return "";
|
||||
// Follow symlink
|
||||
if (is_symlink(p)) {
|
||||
p = read_symlink(p);
|
||||
if (!exists(p)) {
|
||||
spdlog::warn("Image file symlink '" + path.string() + "' is broken");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dir->d_type == DT_LNK && !file_exists) {
|
||||
LOGWARN("Symlink '%s' in image folder '%s' is broken", dir->d_name, folder.c_str())
|
||||
return "";
|
||||
if (is_directory(p) || (is_other(p) && !is_block_file(p))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return filename;
|
||||
if (!is_block_file(p) && file_size(p) < 256) {
|
||||
spdlog::warn("Image file '" + p.string() + "' is invalid");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PiscsiResponse::FilterMatches(const string& input, string_view pattern_lower)
|
||||
{
|
||||
if (!pattern_lower.empty()) {
|
||||
string name_lower;
|
||||
ranges::transform(input, back_inserter(name_lower), ::tolower);
|
||||
|
||||
if (name_lower.find(pattern_lower) == string::npos) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PiscsiResponse::HasOperation(const set<string, less<>>& operations, PbOperation operation)
|
||||
{
|
||||
return operations.empty() || operations.contains(PbOperation_Name(operation));
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
// Copyright (C) 2021-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -11,53 +11,58 @@
|
|||
|
||||
#include "devices/device_factory.h"
|
||||
#include "devices/primary_device.h"
|
||||
#include "shared/piscsi_util.h"
|
||||
#include "generated/piscsi_interface.pb.h"
|
||||
#include <dirent.h>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <set>
|
||||
|
||||
using namespace std;
|
||||
using namespace filesystem;
|
||||
using namespace piscsi_interface;
|
||||
|
||||
class PiscsiResponse
|
||||
{
|
||||
using id_set = pair<int, int>;
|
||||
|
||||
public:
|
||||
|
||||
PiscsiResponse() = default;
|
||||
~PiscsiResponse() = default;
|
||||
|
||||
bool GetImageFile(PbImageFile&, const string&, const string&) const;
|
||||
unique_ptr<PbImageFilesInfo> GetAvailableImages(PbResult&, const string&, const string&, const string&, int) const;
|
||||
unique_ptr<PbReservedIdsInfo> GetReservedIds(PbResult&, const unordered_set<int>&) const;
|
||||
void GetImageFilesInfo(PbImageFilesInfo&, const string&, const string&, const string&, int) const;
|
||||
void GetReservedIds(PbReservedIdsInfo&, const unordered_set<int>&) const;
|
||||
void GetDevices(const unordered_set<shared_ptr<PrimaryDevice>>&, PbServerInfo&, const string&) const;
|
||||
void GetDevicesInfo(const unordered_set<shared_ptr<PrimaryDevice>>&, PbResult&, const PbCommand&, const string&) const;
|
||||
unique_ptr<PbDeviceTypesInfo> GetDeviceTypesInfo(PbResult&) const;
|
||||
unique_ptr<PbVersionInfo> GetVersionInfo(PbResult&) const;
|
||||
unique_ptr<PbServerInfo> GetServerInfo(const unordered_set<shared_ptr<PrimaryDevice>>&, PbResult&, const unordered_set<int>&,
|
||||
const string&, const string&, const string&, const string&, int) const;
|
||||
unique_ptr<PbNetworkInterfacesInfo> GetNetworkInterfacesInfo(PbResult&) const;
|
||||
unique_ptr<PbMappingInfo> GetMappingInfo(PbResult&) const;
|
||||
unique_ptr<PbLogLevelInfo> GetLogLevelInfo(PbResult&, const string&) const;
|
||||
unique_ptr<PbOperationInfo> GetOperationInfo(PbResult&, int) const;
|
||||
void GetDeviceTypesInfo(PbDeviceTypesInfo&) const;
|
||||
void GetVersionInfo(PbVersionInfo&) const;
|
||||
void GetServerInfo(PbServerInfo&, const PbCommand&, const unordered_set<shared_ptr<PrimaryDevice>>&,
|
||||
const unordered_set<int>&, const string&, int) const;
|
||||
void GetNetworkInterfacesInfo(PbNetworkInterfacesInfo&) const;
|
||||
void GetMappingInfo(PbMappingInfo&) const;
|
||||
void GetLogLevelInfo(PbLogLevelInfo&) const;
|
||||
void GetStatisticsInfo(PbStatisticsInfo&, const unordered_set<shared_ptr<PrimaryDevice>>&) const;
|
||||
void GetOperationInfo(PbOperationInfo&, int) const;
|
||||
|
||||
private:
|
||||
|
||||
DeviceFactory device_factory;
|
||||
inline static const vector<string> EMPTY_VECTOR;
|
||||
|
||||
const inline static array<string, 6> log_levels = { "trace", "debug", "info", "warn", "err", "off" };
|
||||
// TODO Try to get rid of this field by having the device instead of the factory providing the device data
|
||||
const DeviceFactory device_factory;
|
||||
|
||||
unique_ptr<PbDeviceProperties> GetDeviceProperties(const Device&) const;
|
||||
void GetDeviceProperties(const Device&, PbDeviceProperties&) const;
|
||||
void GetDevice(const Device&, PbDevice&, const string&) const;
|
||||
void GetAllDeviceTypeProperties(PbDeviceTypesInfo&) const;
|
||||
void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType) const;
|
||||
void GetAvailableImages(PbImageFilesInfo&, const string&, const string&, const string&, const string&, int) const;
|
||||
void GetAvailableImages(PbResult& result, PbServerInfo&, const string&, const string&, const string&, int) const;
|
||||
unique_ptr<PbOperationMetaData> CreateOperation(PbOperationInfo&, const PbOperation&, const string&) const;
|
||||
unique_ptr<PbOperationParameter> AddOperationParameter(PbOperationMetaData&, const string&, const string&,
|
||||
const string& = "", bool = false) const;
|
||||
void GetAvailableImages(PbImageFilesInfo&, const string&, const string&, const string&, int) const;
|
||||
void GetAvailableImages(PbServerInfo&, const string&, const string&, const string&, int) const;
|
||||
PbOperationMetaData *CreateOperation(PbOperationInfo&, const PbOperation&, const string&) const;
|
||||
void AddOperationParameter(PbOperationMetaData&, const string&, const string&,
|
||||
const string& = "", bool = false, const vector<string>& = EMPTY_VECTOR) const;
|
||||
set<id_set> MatchDevices(const unordered_set<shared_ptr<PrimaryDevice>>&, PbResult&, const PbCommand&) const;
|
||||
|
||||
static string GetNextImageFile(const dirent *, const string&);
|
||||
static bool ValidateImageFile(const path&);
|
||||
|
||||
static bool FilterMatches(const string&, string_view);
|
||||
|
||||
static bool HasOperation(const set<string, less<>>&, PbOperation);
|
||||
};
|
||||
|
|
|
@ -3,140 +3,114 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/log.h"
|
||||
#include "shared/piscsi_util.h"
|
||||
#include "shared/protobuf_serializer.h"
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include "command_context.h"
|
||||
#include "localizer.h"
|
||||
#include "piscsi_service.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <csignal>
|
||||
#include <cassert>
|
||||
|
||||
using namespace piscsi_interface;
|
||||
using namespace piscsi_util;
|
||||
|
||||
void PiscsiService::Cleanup() const
|
||||
string PiscsiService::Init(const callback& cb, int port)
|
||||
{
|
||||
running = false;
|
||||
assert(service_socket == -1);
|
||||
|
||||
if (service_socket != -1) {
|
||||
close(service_socket);
|
||||
}
|
||||
}
|
||||
|
||||
bool PiscsiService::Init(const callback& cb, int port)
|
||||
{
|
||||
if (port <= 0 || port > 65535) {
|
||||
return false;
|
||||
return "Invalid port number " + to_string(port);
|
||||
}
|
||||
|
||||
// Create socket for monitor
|
||||
sockaddr_in server = {};
|
||||
service_socket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (service_socket == -1) {
|
||||
LOGERROR("Unable to create socket")
|
||||
return false;
|
||||
return "Unable to create service socket: " + string(strerror(errno));
|
||||
}
|
||||
|
||||
if (const int yes = 1; setsockopt(service_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
|
||||
Stop();
|
||||
return "Can't reuse address";
|
||||
}
|
||||
|
||||
sockaddr_in server = {};
|
||||
server.sin_family = PF_INET;
|
||||
server.sin_port = htons((uint16_t)port);
|
||||
server.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
// Allow address reuse
|
||||
if (int yes = 1; setsockopt(service_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
|
||||
return false;
|
||||
server.sin_addr.s_addr = INADDR_ANY;
|
||||
if (bind(service_socket, reinterpret_cast<const sockaddr *>(&server), sizeof(sockaddr_in)) < 0) { //NOSONAR bit_cast is not supported by the bullseye compiler
|
||||
Stop();
|
||||
return "Port " + to_string(port) + " is in use, is piscsi already running?";
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
if (bind(service_socket, (sockaddr *)&server, sizeof(sockaddr_in)) < 0) {
|
||||
cerr << "Error: Port " << port << " is in use, is piscsi or rascsi already running?" << endl;
|
||||
return false;
|
||||
if (listen(service_socket, 2) == -1) {
|
||||
Stop();
|
||||
return "Can't listen to service socket: " + string(strerror(errno));
|
||||
}
|
||||
|
||||
execute = cb;
|
||||
|
||||
monthread = thread(&PiscsiService::Execute, this);
|
||||
monthread.detach();
|
||||
return "";
|
||||
}
|
||||
|
||||
// Interrupt handler settings
|
||||
return signal(SIGINT, KillHandler) != SIG_ERR && signal(SIGHUP, KillHandler) != SIG_ERR
|
||||
&& signal(SIGTERM, KillHandler) != SIG_ERR;
|
||||
void PiscsiService::Start()
|
||||
{
|
||||
assert(service_socket != -1);
|
||||
|
||||
service_thread = jthread([this] () { Execute(); } );
|
||||
}
|
||||
|
||||
void PiscsiService::Stop()
|
||||
{
|
||||
assert(service_socket != -1);
|
||||
|
||||
shutdown(service_socket, SHUT_RD);
|
||||
close(service_socket);
|
||||
|
||||
service_socket = -1;
|
||||
}
|
||||
|
||||
void PiscsiService::Execute() const
|
||||
{
|
||||
#ifdef __linux__
|
||||
// Scheduler Settings
|
||||
sched_param schedparam;
|
||||
schedparam.sched_priority = 0;
|
||||
// Run this thread with very low priority
|
||||
sched_param schedparam = { .sched_priority = 0 };
|
||||
sched_setscheduler(0, SCHED_IDLE, &schedparam);
|
||||
#endif
|
||||
|
||||
// Set the affinity to a specific processor core
|
||||
FixCpu(2);
|
||||
|
||||
// Wait for the execution to start
|
||||
const timespec ts = { .tv_sec = 0, .tv_nsec = 1000};
|
||||
while (!running) {
|
||||
nanosleep(&ts, nullptr);
|
||||
}
|
||||
|
||||
// Set up the monitor socket to receive commands
|
||||
listen(service_socket, 1);
|
||||
|
||||
while (true) {
|
||||
CommandContext context;
|
||||
|
||||
try {
|
||||
PbCommand command = ReadCommand(context);
|
||||
if (context.IsValid()) {
|
||||
execute(context, command);
|
||||
}
|
||||
// TODO Accept more than one command instead of closing the socket after a single command
|
||||
while (service_socket != -1) {
|
||||
const int fd = accept(service_socket, nullptr, nullptr);
|
||||
if (fd != -1) {
|
||||
ExecuteCommand(fd);
|
||||
close(fd);
|
||||
}
|
||||
catch(const io_exception& e) {
|
||||
LOGWARN("%s", e.what())
|
||||
|
||||
// Fall through
|
||||
}
|
||||
|
||||
context.Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
PbCommand PiscsiService::ReadCommand(CommandContext& context) const
|
||||
void PiscsiService::ExecuteCommand(int fd) const
|
||||
{
|
||||
// Wait for connection
|
||||
sockaddr client = {};
|
||||
socklen_t socklen = sizeof(client);
|
||||
const int fd = accept(service_socket, &client, &socklen);
|
||||
if (fd == -1) {
|
||||
throw io_exception("accept() failed");
|
||||
CommandContext context(fd);
|
||||
try {
|
||||
if (context.ReadCommand()) {
|
||||
execute(context);
|
||||
}
|
||||
}
|
||||
catch(const io_exception& e) {
|
||||
spdlog::warn(e.what());
|
||||
|
||||
PbCommand command;
|
||||
|
||||
// Read magic string
|
||||
vector<byte> magic(6);
|
||||
const size_t bytes_read = context.GetSerializer().ReadBytes(fd, magic);
|
||||
if (!bytes_read) {
|
||||
return command;
|
||||
// Try to return an error message (this may fail if the exception was caused when returning the actual result)
|
||||
PbResult result;
|
||||
result.set_msg(e.what());
|
||||
try {
|
||||
context.WriteResult(result);
|
||||
}
|
||||
catch(const io_exception&) { //NOSONAR Not handled on purpose
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes_read != magic.size() || memcmp(magic.data(), "RASCSI", magic.size())) {
|
||||
throw io_exception("Invalid magic");
|
||||
}
|
||||
|
||||
// Fetch the command
|
||||
context.GetSerializer().DeserializeMessage(fd, command);
|
||||
|
||||
context.SetFd(fd);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,49 +3,42 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
// Copyright (C) 2022-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "generated/piscsi_interface.pb.h"
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
|
||||
class CommandContext;
|
||||
|
||||
using namespace std;
|
||||
using namespace piscsi_interface;
|
||||
|
||||
class PiscsiService
|
||||
{
|
||||
using callback = function<bool(const CommandContext&, piscsi_interface::PbCommand&)>;
|
||||
|
||||
callback execute;
|
||||
|
||||
int service_socket = -1;
|
||||
|
||||
thread monthread;
|
||||
|
||||
static inline volatile bool running = false;
|
||||
using callback = function<bool(CommandContext&)>;
|
||||
|
||||
public:
|
||||
|
||||
PiscsiService() = default;
|
||||
~PiscsiService() = default;
|
||||
|
||||
bool Init(const callback&, int);
|
||||
void Cleanup() const;
|
||||
|
||||
bool IsRunning() const { return running; }
|
||||
void SetRunning(bool b) const { running = b; }
|
||||
string Init(const callback&, int);
|
||||
void Start();
|
||||
void Stop();
|
||||
bool IsRunning() const { return service_socket != -1 && service_thread.joinable(); }
|
||||
|
||||
private:
|
||||
|
||||
void Execute() const;
|
||||
void ExecuteCommand(int) const;
|
||||
|
||||
PbCommand ReadCommand(CommandContext&) const;
|
||||
callback execute;
|
||||
|
||||
static void KillHandler(int) { running = false; }
|
||||
jthread service_thread;
|
||||
|
||||
int service_socket = -1;
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
// Copyright (C) 2021-2023 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -23,7 +23,7 @@ package piscsi_interface;
|
|||
// The available device types
|
||||
enum PbDeviceType {
|
||||
UNDEFINED = 0;
|
||||
// Non-removable SASI drive, not supported anymore
|
||||
// Non-removable SASI drive, not supported anymore but must not be removed because of backwards compatibility
|
||||
SAHD = 1 [deprecated = true];
|
||||
// Non-removable SCSI drive
|
||||
SCHD = 2;
|
||||
|
@ -82,9 +82,12 @@ enum PbOperation {
|
|||
// Make medium writable (not possible for read-only media)
|
||||
UNPROTECT = 9;
|
||||
|
||||
// Gets the server information (PbServerInfo). Calling this operation should be avoided because it
|
||||
// may return a lot of data. More specific other operations should be used instead.
|
||||
// Gets the server information (PbServerInfo). Calling this operation without a list of operations should
|
||||
// be avoided because this may return a lot of data. More specific other operations should be used instead.
|
||||
// Parameters:
|
||||
// "operations": Optional case insensitive comma-separated list of operation names to return data for,
|
||||
// e.g. "version_info,log_level_info". Unknown operation names are ignored. If this parameter is missing
|
||||
// the full set of data supported by PbServerInfo is returned.
|
||||
// "folder_pattern": Optional filter, only folder names containing the case-insensitive pattern are returned
|
||||
// "file_pattern": Optional filter, only filenames containing the case-insensitive pattern are returned
|
||||
SERVER_INFO = 10;
|
||||
|
@ -190,6 +193,9 @@ enum PbOperation {
|
|||
|
||||
// Get operation meta data (PbOperationInfo)
|
||||
OPERATION_INFO = 31;
|
||||
|
||||
// Get statistics (PbStatisticsInfo)
|
||||
STATISTICS_INFO = 32;
|
||||
}
|
||||
|
||||
// The operation parameter meta data. The parameter data type is provided by the protobuf API.
|
||||
|
@ -286,7 +292,7 @@ message PbImageFile {
|
|||
string name = 1;
|
||||
// The assumed device type, based on the filename extension
|
||||
PbDeviceType type = 2;
|
||||
// The file size in bytes, 0 for block devices
|
||||
// The file size in bytes, 0 for block devices in /dev
|
||||
uint64 size = 3;
|
||||
bool read_only = 4;
|
||||
}
|
||||
|
@ -311,6 +317,41 @@ message PbNetworkInterfacesInfo {
|
|||
repeated string name = 1;
|
||||
}
|
||||
|
||||
// Statistics categories ordered by increasing severity
|
||||
enum PbStatisticsCategory {
|
||||
CATEGORY_NONE = 0;
|
||||
CATEGORY_INFO = 1;
|
||||
CATEGORY_WARNING = 2;
|
||||
CATEGORY_ERROR = 3;
|
||||
}
|
||||
|
||||
message PbStatistics {
|
||||
PbStatisticsCategory category = 1;
|
||||
// The device ID and LUN for this statistics item. Both are -1 if the item is not device specific.
|
||||
int32 id = 2;
|
||||
int32 unit = 3;
|
||||
// A symbolic unique item name, may be used for I18N. Supported values and their categories:
|
||||
// "read_error_count" (ERROR, SCHD/SCRM/SCMO/SCCD)
|
||||
// "write_error_count" (ERROR, SCHD/SCRM/SCMO)
|
||||
// "cache_miss_read_count" (INFO, SCHD/SCRM/SCMO/SCCD)
|
||||
// "cache_miss_write_count" (INFO, SCHD/SCRM/SCMO)
|
||||
// "sector_read_count" (INFO, SCHD/SCRM/SCMO/SCCD)
|
||||
// "sector_write_count" (INFO, SCHD/SCRM/SCMO)
|
||||
// "byte_read_count" (INFO, SCDP)
|
||||
// "byte_write_count" (INFO, SCDP)
|
||||
// "print_error_count" (ERROR, SCLP)
|
||||
// "print_warning_count" (WARNING, SCLP)
|
||||
// "file_print_count" (INFO, SCLP)
|
||||
// "byte_receive_count" (INFO, SCLP)
|
||||
string key = 4;
|
||||
uint64 value = 5;
|
||||
}
|
||||
|
||||
// The information on collected statistics
|
||||
message PbStatisticsInfo {
|
||||
repeated PbStatistics statistics = 1;
|
||||
}
|
||||
|
||||
// The device definition, sent from the client to the server
|
||||
message PbDeviceDefinition {
|
||||
int32 id = 1;
|
||||
|
@ -407,6 +448,8 @@ message PbResult {
|
|||
PbReservedIdsInfo reserved_ids_info = 12;
|
||||
// The result of an OPERATION_INFO command
|
||||
PbOperationInfo operation_info = 13;
|
||||
// The result of a STATISTICS_INFO command
|
||||
PbStatisticsInfo statistics_info = 15;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,4 +473,6 @@ message PbServerInfo {
|
|||
PbDevicesInfo devices_info = 8;
|
||||
// The operation meta data
|
||||
PbOperationInfo operation_info = 9;
|
||||
// The statistics
|
||||
PbStatisticsInfo statistics_info = 10;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue