From eb71c31cf1b5d1fb70f12a18c459e17e16813f30 Mon Sep 17 00:00:00 2001 From: akuker <34318535+akuker@users.noreply.github.com> Date: Fri, 2 Dec 2022 22:20:27 -0600 Subject: [PATCH] Initial merge of (incomplete) Banana Pi updates (#993) --- cpp/Makefile | 34 +- cpp/controllers/abstract_controller.cpp | 20 +- cpp/controllers/controller_manager.h | 2 +- cpp/controllers/phase_handler.h | 26 +- cpp/controllers/scsi_controller.cpp | 46 +- cpp/controllers/scsi_controller.h | 2 +- cpp/hal/bus.cpp | 7 +- cpp/hal/bus.h | 177 +- cpp/hal/connection_type/connection_aibom.h | 87 + cpp/hal/connection_type/connection_fullspec.h | 87 + .../connection_type/connection_gamernium.h | 87 + cpp/hal/connection_type/connection_standard.h | 87 + cpp/hal/data_sample.cpp | 40 + cpp/hal/data_sample.h | 54 + cpp/hal/data_sample_bananam2p.cpp | 64 + cpp/hal/data_sample_bananam2p.h | 94 + cpp/hal/data_sample_raspberry.cpp | 14 + cpp/hal/data_sample_raspberry.h | 109 ++ cpp/hal/gpiobus.cpp | 1543 ++++------------- cpp/hal/gpiobus.h | 434 +---- cpp/hal/gpiobus_aibom.h | 56 - cpp/hal/gpiobus_bananam2p.cpp | 1105 ++++++++++++ cpp/hal/gpiobus_bananam2p.h | 221 +++ cpp/hal/gpiobus_factory.cpp | 39 +- cpp/hal/gpiobus_factory.h | 6 +- cpp/hal/gpiobus_fullspec.h | 56 - cpp/hal/gpiobus_gamernium.h | 57 - cpp/hal/gpiobus_raspberry.cpp | 988 +++++++++++ cpp/hal/gpiobus_raspberry.h | 295 ++++ cpp/hal/gpiobus_standard.h | 56 - cpp/hal/gpiobus_virtual.cpp | 564 ++++++ cpp/hal/gpiobus_virtual.h | 174 ++ cpp/hal/pi_defs/bpi-gpio.h | 356 ++++ cpp/hal/pi_defs/bpi-m2p.h | 71 + cpp/hal/pin_control.h | 68 + cpp/hal/sbc_version.cpp | 226 +++ cpp/hal/sbc_version.h | 78 + cpp/hal/sunxi_utils.cpp | 65 + cpp/hal/sunxi_utils.h | 189 ++ cpp/hal/systimer.cpp | 144 +- cpp/hal/systimer.h | 55 +- cpp/hal/systimer_allwinner.cpp | 155 ++ cpp/hal/systimer_allwinner.h | 106 ++ cpp/hal/systimer_raspberry.cpp | 147 ++ cpp/hal/systimer_raspberry.h | 70 + cpp/monitor/data_sample.cpp | 39 - cpp/monitor/data_sample.h | 49 - cpp/monitor/sm_core.cpp | 105 +- cpp/monitor/sm_core.h | 56 +- cpp/monitor/sm_html_report.cpp | 32 +- cpp/monitor/sm_json_report.cpp | 71 +- cpp/monitor/sm_reports.h | 11 +- cpp/monitor/sm_vcd_report.cpp | 202 +-- cpp/rascsi/rascsi_core.cpp | 11 +- cpp/rasdump/rasdump_core.cpp | 12 +- cpp/rasdump/rasdump_core.h | 2 +- cpp/scsiloop.cpp | 20 + cpp/scsiloop/scsiloop_core.cpp | 175 ++ cpp/scsiloop/scsiloop_core.h | 32 + cpp/scsiloop/scsiloop_cout.cpp | 40 + cpp/scsiloop/scsiloop_cout.h | 33 + cpp/scsiloop/scsiloop_gpio.cpp | 592 +++++++ cpp/scsiloop/scsiloop_gpio.h | 81 + cpp/scsiloop/scsiloop_timer.cpp | 91 + cpp/scsiloop/scsiloop_timer.h | 19 + cpp/shared/scsi.h | 313 ++-- cpp/test/abstract_controller_test.cpp | 24 +- cpp/test/bus_test.cpp | 198 +-- cpp/test/gpiobus_raspberry_test.cpp | 234 +++ cpp/test/linux_os_stubs.cpp | 59 + cpp/test/linux_os_stubs.h | 21 + cpp/test/mocks.h | 15 +- cpp/test/phase_handler_test.cpp | 16 +- cpp/test/scsi_controller_test.cpp | 98 +- cpp/test/test_shared.cpp | 150 +- cpp/test/test_shared.h | 10 + doc/scsiloop.1 | 35 + doc/scsiloop_man_page.txt | 41 + 78 files changed, 8367 insertions(+), 2881 deletions(-) create mode 100644 cpp/hal/connection_type/connection_aibom.h create mode 100644 cpp/hal/connection_type/connection_fullspec.h create mode 100644 cpp/hal/connection_type/connection_gamernium.h create mode 100644 cpp/hal/connection_type/connection_standard.h create mode 100644 cpp/hal/data_sample.cpp create mode 100644 cpp/hal/data_sample.h create mode 100644 cpp/hal/data_sample_bananam2p.cpp create mode 100644 cpp/hal/data_sample_bananam2p.h create mode 100644 cpp/hal/data_sample_raspberry.cpp create mode 100644 cpp/hal/data_sample_raspberry.h delete mode 100644 cpp/hal/gpiobus_aibom.h create mode 100644 cpp/hal/gpiobus_bananam2p.cpp create mode 100644 cpp/hal/gpiobus_bananam2p.h delete mode 100644 cpp/hal/gpiobus_fullspec.h delete mode 100644 cpp/hal/gpiobus_gamernium.h create mode 100644 cpp/hal/gpiobus_raspberry.cpp create mode 100644 cpp/hal/gpiobus_raspberry.h delete mode 100644 cpp/hal/gpiobus_standard.h create mode 100644 cpp/hal/gpiobus_virtual.cpp create mode 100644 cpp/hal/gpiobus_virtual.h create mode 100644 cpp/hal/pi_defs/bpi-gpio.h create mode 100755 cpp/hal/pi_defs/bpi-m2p.h create mode 100644 cpp/hal/pin_control.h create mode 100644 cpp/hal/sbc_version.cpp create mode 100644 cpp/hal/sbc_version.h create mode 100644 cpp/hal/sunxi_utils.cpp create mode 100644 cpp/hal/sunxi_utils.h create mode 100644 cpp/hal/systimer_allwinner.cpp create mode 100644 cpp/hal/systimer_allwinner.h create mode 100644 cpp/hal/systimer_raspberry.cpp create mode 100644 cpp/hal/systimer_raspberry.h delete mode 100644 cpp/monitor/data_sample.cpp delete mode 100644 cpp/monitor/data_sample.h create mode 100644 cpp/scsiloop.cpp create mode 100644 cpp/scsiloop/scsiloop_core.cpp create mode 100644 cpp/scsiloop/scsiloop_core.h create mode 100644 cpp/scsiloop/scsiloop_cout.cpp create mode 100644 cpp/scsiloop/scsiloop_cout.h create mode 100644 cpp/scsiloop/scsiloop_gpio.cpp create mode 100644 cpp/scsiloop/scsiloop_gpio.h create mode 100644 cpp/scsiloop/scsiloop_timer.cpp create mode 100644 cpp/scsiloop/scsiloop_timer.h create mode 100644 cpp/test/gpiobus_raspberry_test.cpp create mode 100644 cpp/test/linux_os_stubs.cpp create mode 100644 cpp/test/linux_os_stubs.h create mode 100644 doc/scsiloop.1 create mode 100644 doc/scsiloop_man_page.txt diff --git a/cpp/Makefile b/cpp/Makefile index 2eb22067..4640b41f 100644 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -58,6 +58,7 @@ RASCTL = rasctl RASDUMP = rasdump SCSIMON = scsimon RASCSI_TEST = rascsi_test +SCSILOOP = scsiloop SYSTEMD_CONF = /etc/systemd/system/rascsi.service RSYSLOG_CONF = /etc/rsyslog.d/rascsi.conf @@ -77,7 +78,8 @@ BIN_ALL = \ $(BINDIR)/$(RASCSI) \ $(BINDIR)/$(RASCTL) \ $(BINDIR)/$(SCSIMON) \ - $(BINDIR)/$(RASDUMP) + $(BINDIR)/$(RASDUMP) \ + $(BINDIR)/$(SCSILOOP) SRC_PROTOC = rascsi_interface.proto @@ -114,8 +116,16 @@ SRC_RASCSI_TEST = $(shell find ./test -name '*.cpp') SRC_RASCSI_TEST += $(shell find ./rasdump -name '*.cpp') SRC_RASCSI_TEST += $(shell find ./monitor -name '*.cpp') -vpath %.h ./ ./shared ./controllers ./devices ./monitor ./hal ./rascsi ./rasctl ./rasdump -vpath %.cpp ./ ./shared ./controllers ./devices ./monitor ./hal ./rascsi ./rasctl ./rasdump ./test +SRC_SCSILOOP = scsiloop.cpp +SRC_SCSILOOP += $(shell find ./scsiloop -name '*.cpp') +SRC_SCSILOOP += $(shell find ./hal -name '*.cpp') + +vpath %.h ./ ./shared ./controllers ./devices ./monitor ./hal \ + ./hal/boards ./hal/pi_defs ./rascsi ./rasctl ./rasdump \ + ./scsiloop +vpath %.cpp ./ ./shared ./controllers ./devices ./monitor ./hal \ + ./hal/boards ./hal/pi_defs ./rascsi ./rasctl ./rasdump \ + ./scsiloop ./test vpath %.o ./$(OBJDIR) vpath ./$(BINDIR) @@ -127,16 +137,21 @@ OBJ_RASCTL := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCTL:%.cpp=%.o))) OBJ_RASDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASDUMP:%.cpp=%.o))) OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o))) OBJ_RASCSI_TEST := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI_TEST:%.cpp=%.o))) +OBJ_SCSILOOP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSILOOP:%.cpp=%.o))) 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))) GENERATED_DIR := generated +# For the unit tests, the following functions will be "wrapped" by the linker, meaning the +# __wrap_xxxx() function will be called instead of the real function. These linker flags +# should only be used for testing purposes! +TEST_WRAPS = -Wl,--wrap=fopen64 # The following will include all of the auto-generated dependency files (*.d) # if they exist. This will trigger a rebuild of a source file if a header changes -ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI_CORE) $(OBJ_RASCTL_CORE) $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SCSIMON) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_RASCSI_TEST)) +ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI_CORE) $(OBJ_RASCTL_CORE) $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SCSIMON) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_RASCSI_TEST) $(OBJ_SCSILOOP)) -include $(ALL_DEPS) $(OBJDIR) $(BINDIR): @@ -179,7 +194,7 @@ lcov: test lcov -q -c -d . --include '*/cpp/*' -o $(COVERAGE_FILE) --exclude '*/test/*' --exclude '*/interfaces/*' --exclude '*/rascsi_interface.pb*' genhtml -q -o $(COVERAGE_DIR) --legend $(COVERAGE_FILE) -docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt $(DOC_DIR)/rasdump_man_page.txt +docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt $(DOC_DIR)/rasdump_man_page.txt $(DOC_DIR)/scsiloop_man_page.txt $(SRC_RASCSI_CORE) $(SRC_RASCTL_CORE) : $(OBJ_GENERATED) @@ -196,16 +211,19 @@ $(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) $(OBJ_SHARED) | $(BINDIR) $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) $(OBJ_SHARED) $(BINDIR)/$(RASCSI_TEST): $(SRC_GENERATED) $(OBJ_RASCSI_CORE) $(OBJ_RASCTL_CORE) $(OBJ_RASCSI_TEST) $(OBJ_RASCTL_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) | $(BINDIR) - $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCTL_CORE) $(OBJ_RASCSI_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) -lpthread -lpcap -lprotobuf -lstdc++fs -lgmock -lgtest + $(CXX) $(CXXFLAGS) $(TEST_WRAPS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCTL_CORE) $(OBJ_RASCSI_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(OBJ_GENERATED) -lpthread -lpcap -lprotobuf -lstdc++fs -lgmock -lgtest +$(BINDIR)/$(SCSILOOP): $(OBJ_SHARED) $(OBJ_SCSILOOP) | $(BINDIR) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SHARED) $(OBJ_SCSILOOP) # Phony rules for building individual utilities -.PHONY: $(RASCSI) $(RASCTL) $(RASDUMP) $(SCSIMON) $(RASCSI_TEST) +.PHONY: $(RASCSI) $(RASCTL) $(RASDUMP) $(SCSIMON) $(RASCSI_TEST) $(SCSILOOP) $(RASCSI) : $(BINDIR)/$(RASCSI) $(RASCTL) : $(BINDIR)/$(RASCTL) $(RASDUMP) : $(BINDIR)/$(RASDUMP) $(SCSIMON) : $(BINDIR)/$(SCSIMON) $(RASCSI_TEST): $(BINDIR)/$(RASCSI_TEST) +$(SCSILOOP) : $(BINDIR)/$(SCSILOOP) ## clean : Remove all of the object files, intermediate ## compiler files and executable files @@ -230,10 +248,12 @@ install: \ $(MAN_PAGE_DIR)/rascsi.1 \ $(MAN_PAGE_DIR)/rasctl.1 \ $(MAN_PAGE_DIR)/scsimon.1 \ + $(MAN_PAGE_DIR)/scsiloop.1 \ $(MAN_PAGE_DIR)/rasdump.1 \ $(USR_LOCAL_BIN)/$(RASCTL) \ $(USR_LOCAL_BIN)/$(RASCSI) \ $(USR_LOCAL_BIN)/$(SCSIMON) \ + $(USR_LOCAL_BIN)/$(SCSILOOP) \ $(USR_LOCAL_BIN)/$(RASDUMP) \ $(SYSTEMD_CONF) \ $(RSYSLOG_CONF) \ diff --git a/cpp/controllers/abstract_controller.cpp b/cpp/controllers/abstract_controller.cpp index b113cc9c..8b26e9e3 100644 --- a/cpp/controllers/abstract_controller.cpp +++ b/cpp/controllers/abstract_controller.cpp @@ -11,6 +11,8 @@ #include "devices/primary_device.h" #include "abstract_controller.h" +using namespace scsi_defs; + void AbstractController::AllocateCmd(size_t size) { if (size > ctrl.cmd.size()) { @@ -52,7 +54,7 @@ shared_ptr AbstractController::GetDeviceForLun(int lun) const { void AbstractController::Reset() { - SetPhase(BUS::phase_t::busfree); + SetPhase(phase_t::busfree); ctrl.status = status::GOOD; ctrl.message = 0x00; @@ -70,35 +72,35 @@ void AbstractController::Reset() void AbstractController::ProcessPhase() { switch (GetPhase()) { - case BUS::phase_t::busfree: + case phase_t::busfree: BusFree(); break; - case BUS::phase_t::selection: + case phase_t::selection: Selection(); break; - case BUS::phase_t::dataout: + case phase_t::dataout: DataOut(); break; - case BUS::phase_t::datain: + case phase_t::datain: DataIn(); break; - case BUS::phase_t::command: + case phase_t::command: Command(); break; - case BUS::phase_t::status: + case phase_t::status: Status(); break; - case BUS::phase_t::msgout: + case phase_t::msgout: MsgOut(); break; - case BUS::phase_t::msgin: + case phase_t::msgin: MsgIn(); break; diff --git a/cpp/controllers/controller_manager.h b/cpp/controllers/controller_manager.h index 640eaade..ff55d6c2 100644 --- a/cpp/controllers/controller_manager.h +++ b/cpp/controllers/controller_manager.h @@ -14,10 +14,10 @@ #include #include #include +#include "hal/bus.h" using namespace std; -class BUS; class AbstractController; class PrimaryDevice; diff --git a/cpp/controllers/phase_handler.h b/cpp/controllers/phase_handler.h index 47ae864e..2952aa79 100644 --- a/cpp/controllers/phase_handler.h +++ b/cpp/controllers/phase_handler.h @@ -11,9 +11,11 @@ #include "shared/scsi.h" + using namespace scsi_defs; + class PhaseHandler { - BUS::phase_t phase = BUS::phase_t::busfree; + phase_t phase = phase_t::busfree; public: @@ -29,18 +31,18 @@ public: virtual void MsgIn() = 0; virtual void MsgOut() = 0; - virtual BUS::phase_t Process(int) = 0; + virtual phase_t Process(int) = 0; protected: - BUS::phase_t GetPhase() const { return phase; } - void SetPhase(BUS::phase_t p) { phase = p; } - bool IsSelection() const { return phase == BUS::phase_t::selection; } - bool IsBusFree() const { return phase == BUS::phase_t::busfree; } - bool IsCommand() const { return phase == BUS::phase_t::command; } - bool IsStatus() const { return phase == BUS::phase_t::status; } - bool IsDataIn() const { return phase == BUS::phase_t::datain; } - bool IsDataOut() const { return phase == BUS::phase_t::dataout; } - bool IsMsgIn() const { return phase == BUS::phase_t::msgin; } - bool IsMsgOut() const { return phase == BUS::phase_t::msgout; } + phase_t GetPhase() const { return phase; } + void SetPhase(phase_t p) { phase = p; } + bool IsSelection() const { return phase == phase_t::selection; } + bool IsBusFree() const { return phase == phase_t::busfree; } + bool IsCommand() const { return phase == phase_t::command; } + bool IsStatus() const { return phase == phase_t::status; } + bool IsDataIn() const { return phase == phase_t::datain; } + bool IsDataOut() const { return phase == phase_t::dataout; } + bool IsMsgIn() const { return phase == phase_t::msgin; } + bool IsMsgOut() const { return phase == phase_t::msgout; } }; diff --git a/cpp/controllers/scsi_controller.cpp b/cpp/controllers/scsi_controller.cpp index a47620ac..81bb2891 100644 --- a/cpp/controllers/scsi_controller.cpp +++ b/cpp/controllers/scsi_controller.cpp @@ -52,7 +52,7 @@ void ScsiController::Reset() SetByteTransfer(false); } -BUS::phase_t ScsiController::Process(int id) +phase_t ScsiController::Process(int id) { GetBus().Acquire(); @@ -89,7 +89,7 @@ void ScsiController::BusFree() if (!IsBusFree()) { logger.Trace("Bus free phase"); - SetPhase(BUS::phase_t::busfree); + SetPhase(phase_t::busfree); GetBus().SetREQ(false); GetBus().SetMSG(false); @@ -165,7 +165,7 @@ void ScsiController::Selection() logger.Trace("Selection phase"); - SetPhase(BUS::phase_t::selection); + SetPhase(phase_t::selection); // Raise BSY and respond GetBus().SetBSY(true); @@ -188,7 +188,7 @@ void ScsiController::Command() if (!IsCommand()) { logger.Trace("Command phase"); - SetPhase(BUS::phase_t::command); + SetPhase(phase_t::command); GetBus().SetMSG(false); GetBus().SetCD(true); @@ -307,7 +307,7 @@ void ScsiController::Status() s << "Status Phase, status is $" << setfill('0') << setw(2) << hex << static_cast(GetStatus()); logger.Trace(s.str()); - SetPhase(BUS::phase_t::status); + SetPhase(phase_t::status); // Signal line operated by the target GetBus().SetMSG(false); @@ -331,7 +331,7 @@ void ScsiController::MsgIn() if (!IsMsgIn()) { logger.Trace("Message In phase"); - SetPhase(BUS::phase_t::msgin); + SetPhase(phase_t::msgin); GetBus().SetMSG(true); GetBus().SetCD(true); @@ -356,7 +356,7 @@ void ScsiController::MsgOut() scsi.msb = {}; } - SetPhase(BUS::phase_t::msgout); + SetPhase(phase_t::msgout); GetBus().SetMSG(true); GetBus().SetCD(true); @@ -389,7 +389,7 @@ void ScsiController::DataIn() logger.Trace("Entering Data In phase"); - SetPhase(BUS::phase_t::datain); + SetPhase(phase_t::datain); GetBus().SetMSG(false); GetBus().SetCD(false); @@ -419,7 +419,7 @@ void ScsiController::DataOut() logger.Trace("Data Out phase"); - SetPhase(BUS::phase_t::dataout); + SetPhase(phase_t::dataout); // Signal line operated by the target GetBus().SetMSG(false); @@ -537,7 +537,7 @@ void ScsiController::Send() logger.Trace("Moving to next phase: " + string(BUS::GetPhaseStrRaw(GetPhase()))); switch (GetPhase()) { // Message in phase - case BUS::phase_t::msgin: + case phase_t::msgin: // Completed sending response to extended message of IDENTIFY message if (scsi.atnmsg) { // flag off @@ -552,13 +552,13 @@ void ScsiController::Send() break; // Data-in Phase - case BUS::phase_t::datain: + case phase_t::datain: // status phase Status(); break; // status phase - case BUS::phase_t::status: + case phase_t::status: // Message in phase SetLength(1); SetBlocks(1); @@ -605,7 +605,7 @@ void ScsiController::Receive() // Processing after receiving data (by phase) logger.Trace("Phase: " + string(BUS::GetPhaseStrRaw(GetPhase()))); switch (GetPhase()) { - case BUS::phase_t::dataout: + case phase_t::dataout: if (GetBlocks() == 0) { // End with this buffer result = XferOut(false); @@ -615,7 +615,7 @@ void ScsiController::Receive() } break; - case BUS::phase_t::msgout: + case phase_t::msgout: SetMessage(GetBuffer()[0]); if (!XferMsg(GetMessage())) { // Immediately free the bus if message output fails @@ -646,15 +646,15 @@ void ScsiController::Receive() // Move to next phase switch (GetPhase()) { - case BUS::phase_t::command: + case phase_t::command: ProcessCommand(); break; - case BUS::phase_t::msgout: + case phase_t::msgout: ProcessMessage(); break; - case BUS::phase_t::dataout: + case phase_t::dataout: // Block-oriented data have been handled above DataOutNonBlockOriented(); @@ -694,11 +694,11 @@ void ScsiController::ReceiveBytes() // Processing after receiving data (by phase) logger.Trace("Phase: " + string(BUS::GetPhaseStrRaw(GetPhase()))); switch (GetPhase()) { - case BUS::phase_t::dataout: + case phase_t::dataout: result = XferOut(false); break; - case BUS::phase_t::msgout: + case phase_t::msgout: SetMessage(GetBuffer()[0]); if (!XferMsg(GetMessage())) { // Immediately free the bus if message output fails @@ -722,15 +722,15 @@ void ScsiController::ReceiveBytes() // Move to next phase switch (GetPhase()) { - case BUS::phase_t::command: + case phase_t::command: ProcessCommand(); break; - case BUS::phase_t::msgout: + case phase_t::msgout: ProcessMessage(); break; - case BUS::phase_t::dataout: + case phase_t::dataout: Status(); break; @@ -1046,4 +1046,4 @@ void ScsiController::Sleep() SysTimer::SleepUsec(MIN_EXEC_TIME - time); } execstart = 0; -} +} \ No newline at end of file diff --git a/cpp/controllers/scsi_controller.h b/cpp/controllers/scsi_controller.h index 11fa3128..07e339d1 100644 --- a/cpp/controllers/scsi_controller.h +++ b/cpp/controllers/scsi_controller.h @@ -60,7 +60,7 @@ public: void Reset() override; - BUS::phase_t Process(int) override; + phase_t Process(int) override; int GetEffectiveLun() const override; diff --git a/cpp/hal/bus.cpp b/cpp/hal/bus.cpp index 19dfe926..ff2edd1b 100644 --- a/cpp/hal/bus.cpp +++ b/cpp/hal/bus.cpp @@ -5,6 +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 akuker // //--------------------------------------------------------------------------- @@ -30,7 +31,7 @@ int BUS::GetCommandByteCount(uint8_t opcode) // Phase Acquisition // //--------------------------------------------------------------------------- -BUS::phase_t BUS::GetPhase() +phase_t BUS::GetPhase() { // Selection Phase if (GetSEL()) { @@ -66,7 +67,7 @@ const char* BUS::GetPhaseStrRaw(phase_t current_phase) { // This determines the phase based upon the Msg, C/D and I/O signals. // //--------------------------------------------------------------------------- -const array BUS::phase_table = { +const array BUS::phase_table = { // | MSG|C/D|I/O | phase_t::dataout, // | 0 | 0 | 0 | phase_t::datain, // | 0 | 0 | 1 | @@ -83,7 +84,7 @@ const array BUS::phase_table = { // Phase string to phase mapping // //--------------------------------------------------------------------------- -const unordered_map BUS::phase_str_mapping = { +const unordered_map BUS::phase_str_mapping = { { phase_t::busfree, "busfree" }, { phase_t::arbitration, "arbitration" }, { phase_t::selection, "selection" }, diff --git a/cpp/hal/bus.h b/cpp/hal/bus.h index ee7fa54a..23740fe2 100644 --- a/cpp/hal/bus.h +++ b/cpp/hal/bus.h @@ -9,116 +9,105 @@ #pragma once +#include "hal/data_sample.h" +#include "hal/pin_control.h" #include "shared/config.h" #include "shared/scsi.h" -#include #include -#include +#include +#include #include - +#include +#include using namespace std; -class BUS +//--------------------------------------------------------------------------- +// +// Constant declarations (bus control timing) +// +//--------------------------------------------------------------------------- +// SCSI Bus timings taken from: +// https://www.staff.uni-mainz.de/tacke/scsi/SCSI2-05.html +const static int SCSI_DELAY_ARBITRATION_DELAY_NS = 2400; +const static int SCSI_DELAY_ASSERTION_PERIOD_NS = 90; +const static int SCSI_DELAY_BUS_CLEAR_DELAY_NS = 800; +const static int SCSI_DELAY_BUS_FREE_DELAY_NS = 800; +const static int SCSI_DELAY_BUS_SET_DELAY_NS = 1800; +const static int SCSI_DELAY_BUS_SETTLE_DELAY_NS = 400; +const static int SCSI_DELAY_CABLE_SKEW_DELAY_NS = 10; +const static int SCSI_DELAY_DATA_RELEASE_DELAY_NS = 400; +const static int SCSI_DELAY_DESKEW_DELAY_NS = 45; +const static int SCSI_DELAY_DISCONNECTION_DELAY_US = 200; +const static int SCSI_DELAY_HOLD_TIME_NS = 45; +const static int SCSI_DELAY_NEGATION_PERIOD_NS = 90; +const static int SCSI_DELAY_POWER_ON_TO_SELECTION_TIME_S = 10; // (recommended) +const static int SCSI_DELAY_RESET_TO_SELECTION_TIME_US = 250 * 1000; // (recommended) +const static int SCSI_DELAY_RESET_HOLD_TIME_US = 25; +const static int SCSI_DELAY_SELECTION_ABORT_TIME_US = 200; +const static int SCSI_DELAY_SELECTION_TIMEOUT_DELAY_NS = 250 * 1000; // (recommended) +const static int SCSI_DELAY_FAST_ASSERTION_PERIOD_NS = 30; +const static int SCSI_DELAY_FAST_CABLE_SKEW_DELAY_NS = 5; +const static int SCSI_DELAY_FAST_DESKEW_DELAY_NS = 20; +const static int SCSI_DELAY_FAST_HOLD_TIME_NS = 10; +const static int SCSI_DELAY_FAST_NEGATION_PERIOD_NS = 30; + +// The DaynaPort SCSI Link do a short delay in the middle of transfering +// a packet. This is the number of uS that will be delayed between the +// header and the actual data. +const static int SCSI_DELAY_SEND_DATA_DAYNAPORT_US = 100; + + +class bus_exception : public runtime_error { -public: - // Operation modes definition - enum class mode_e { - TARGET = 0, - INITIATOR = 1, - MONITOR = 2, - }; + using runtime_error::runtime_error; +}; - // Phase definitions - enum class phase_t : int { - busfree, - arbitration, - selection, - reselection, - command, - datain, - dataout, - status, - msgin, - msgout, - reserved - }; - - BUS() = default; - virtual ~BUS() = default; +class BUS : public PinControl +{ + public: + // Operation modes definition + enum class mode_e { + TARGET = 0, + INITIATOR = 1, + MONITOR = 2, + }; static int GetCommandByteCount(uint8_t); - virtual bool Init(mode_e mode) = 0; - virtual void Reset() = 0; - virtual void Cleanup() = 0; - phase_t GetPhase(); + virtual bool Init(mode_e mode) = 0; + virtual void Reset() = 0; + virtual void Cleanup() = 0; + phase_t GetPhase(); - static phase_t GetPhase(int mci) - { - return phase_table[mci]; - } + static phase_t GetPhase(int mci) + { + return phase_table[mci]; + } - // Get the string phase name, based upon the raw data - static const char* GetPhaseStrRaw(phase_t current_phase); + // Get the string phase name, based upon the raw data + static const char *GetPhaseStrRaw(phase_t current_phase); + virtual int GetMode(int pin) = 0; - // Extract as specific pin field from a raw data capture - static inline uint32_t GetPinRaw(uint32_t raw_data, uint32_t pin_num) - { - return ((raw_data >> pin_num) & 1); - } + virtual uint32_t Acquire() = 0; + virtual unique_ptr GetSample(uint64_t timestamp = 0) = 0; + virtual int CommandHandShake(vector &) = 0; + virtual int ReceiveHandShake(uint8_t *buf, int count) = 0; + virtual int SendHandShake(uint8_t *buf, int count, int delay_after_bytes) = 0; - virtual bool GetBSY() const = 0; - virtual void SetBSY(bool ast) = 0; + // SEL signal event polling + virtual bool PollSelectEvent() = 0; - virtual bool GetSEL() const = 0; - virtual void SetSEL(bool ast) = 0; + // Clear SEL signal event + virtual void ClearSelectEvent() = 0; - virtual bool GetATN() const = 0; - virtual void SetATN(bool ast) = 0; + virtual bool GetSignal(int pin) const = 0; + // Get SCSI input signal value + virtual void SetSignal(int pin, bool ast) = 0; + // Set SCSI output signal value + static const int SEND_NO_DELAY = -1; + // Passed into SendHandShake when we don't want to delay + private: + static const array phase_table; - virtual bool GetACK() const = 0; - virtual void SetACK(bool ast) = 0; - - virtual bool GetRST() const = 0; - virtual void SetRST(bool ast) = 0; - - virtual bool GetMSG() const = 0; - virtual void SetMSG(bool ast) = 0; - - virtual bool GetCD() const = 0; - virtual void SetCD(bool ast) = 0; - - virtual bool GetIO() = 0; - virtual void SetIO(bool ast) = 0; - - virtual bool GetREQ() const = 0; - virtual void SetREQ(bool ast) = 0; - - virtual uint8_t GetDAT() = 0; - virtual void SetDAT(uint8_t dat) = 0; - virtual bool GetDP() const = 0; // Get parity signal - - virtual uint32_t Acquire() = 0; - virtual int CommandHandShake(vector&) = 0; - virtual int ReceiveHandShake(uint8_t *buf, int count) = 0; - virtual int SendHandShake(uint8_t *buf, int count, int delay_after_bytes) = 0; - -#ifdef USE_SEL_EVENT_ENABLE - // SEL signal event polling - virtual bool PollSelectEvent() = 0; - - // Clear SEL signal event - virtual void ClearSelectEvent() = 0; -#endif - - virtual bool GetSignal(int pin) const = 0; - // Get SCSI input signal value - virtual void SetSignal(int pin, bool ast) = 0; - // Set SCSI output signal value - static const int SEND_NO_DELAY = -1; - // Passed into SendHandShake when we don't want to delay -private: - static const array phase_table; - - static const unordered_map phase_str_mapping; + static const unordered_map phase_str_mapping; }; diff --git a/cpp/hal/connection_type/connection_aibom.h b/cpp/hal/connection_type/connection_aibom.h new file mode 100644 index 00000000..ad7723e4 --- /dev/null +++ b/cpp/hal/connection_type/connection_aibom.h @@ -0,0 +1,87 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "hal/pi_defs/bpi-m2p.h" +#include + +// +// RaSCSI Adapter Aibom version +// + +const std::string CONNECT_DESC = "AIBOM PRODUCTS version"; // Startup message + +// Select signal control mode +const static int SIGNAL_CONTROL_MODE = 2; // SCSI positive logic specification + +// Control signal output logic +#define ACT_ON ON // ACTIVE SIGNAL ON +#define ENB_ON ON // ENABLE SIGNAL ON +#define IND_IN OFF // INITIATOR SIGNAL INPUT +#define TAD_IN OFF // TARGET SIGNAL INPUT +#define DTD_IN OFF // DATA SIGNAL INPUT + +// Control signal pin assignment (-1 means no control) +const static int PIN_ACT = 4; // ACTIVE +const static int PIN_ENB = 17; // ENABLE +const static int PIN_IND = 27; // INITIATOR CTRL DIRECTION +const static int PIN_TAD = -1; // TARGET CTRL DIRECTION +const static int PIN_DTD = 18; // DATA DIRECTION + +// SCSI signal pin assignment +const static int PIN_DT0 = 6; // Data 0 +const static int PIN_DT1 = 12; // Data 1 +const static int PIN_DT2 = 13; // Data 2 +const static int PIN_DT3 = 16; // Data 3 +const static int PIN_DT4 = 19; // Data 4 +const static int PIN_DT5 = 20; // Data 5 +const static int PIN_DT6 = 26; // Data 6 +const static int PIN_DT7 = 21; // Data 7 +const static int PIN_DP = 5; // Data parity +const static int PIN_ATN = 22; // ATN +const static int PIN_RST = 25; // RST +const static int PIN_ACK = 10; // ACK +const static int PIN_REQ = 7; // REQ +const static int PIN_MSG = 9; // MSG +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 \ No newline at end of file diff --git a/cpp/hal/connection_type/connection_fullspec.h b/cpp/hal/connection_type/connection_fullspec.h new file mode 100644 index 00000000..298d3f77 --- /dev/null +++ b/cpp/hal/connection_type/connection_fullspec.h @@ -0,0 +1,87 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "hal/pi_defs/bpi-m2p.h" +#include + +// +// RaSCSI standard (SCSI logic, standard pin assignment) +// + +const std::string CONNECT_DESC = "FULLSPEC"; // Startup message + +// Select signal control mode +const static int SIGNAL_CONTROL_MODE = 0; // SCSI logical specification + +// Control signal pin assignment (-1 means no control) +const static int PIN_ACT = 4; // ACTIVE +const static int PIN_ENB = 5; // ENABLE +const static int PIN_IND = 6; // INITIATOR CTRL DIRECTION +const static int PIN_TAD = 7; // TARGET CTRL DIRECTION +const static int PIN_DTD = 8; // DATA DIRECTION + +// Control signal output logic +#define ACT_ON ON // ACTIVE SIGNAL ON +#define ENB_ON ON // ENABLE SIGNAL ON +#define IND_IN OFF // INITIATOR SIGNAL INPUT +#define TAD_IN OFF // TARGET SIGNAL INPUT +#define DTD_IN ON // DATA SIGNAL INPUT + +// SCSI signal pin assignment +const static int PIN_DT0 = 10; // Data 0 +const static int PIN_DT1 = 11; // Data 1 +const static int PIN_DT2 = 12; // Data 2 +const static int PIN_DT3 = 13; // Data 3 +const static int PIN_DT4 = 14; // Data 4 +const static int PIN_DT5 = 15; // Data 5 +const static int PIN_DT6 = 16; // Data 6 +const static int PIN_DT7 = 17; // Data 7 +const static int PIN_DP = 18; // Data parity +const static int PIN_ATN = 19; // ATN +const static int PIN_RST = 20; // RST +const static int PIN_ACK = 21; // ACK +const static int PIN_REQ = 22; // REQ +const static int PIN_MSG = 23; // MSG +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 diff --git a/cpp/hal/connection_type/connection_gamernium.h b/cpp/hal/connection_type/connection_gamernium.h new file mode 100644 index 00000000..452fd0ac --- /dev/null +++ b/cpp/hal/connection_type/connection_gamernium.h @@ -0,0 +1,87 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "hal/pi_defs/bpi-m2p.h" +#include + +// +// RaSCSI Adapter GAMERnium.com version +// + +const std::string CONNECT_DESC = "GAMERnium.com version"; // Startup message + +// Select signal control mode +const static int SIGNAL_CONTROL_MODE = 0; // SCSI logical specification + +// Control signal output logic +#define ACT_ON ON // ACTIVE SIGNAL ON +#define ENB_ON ON // ENABLE SIGNAL ON +#define IND_IN OFF // INITIATOR SIGNAL INPUT +#define TAD_IN OFF // TARGET SIGNAL INPUT +#define DTD_IN ON // DATA SIGNAL INPUT + +// Control signal pin assignment (-1 means no control) +const static int PIN_ACT = 14; // ACTIVE +const static int PIN_ENB = 6; // ENABLE +const static int PIN_IND = 7; // INITIATOR CTRL DIRECTION +const static int PIN_TAD = 8; // TARGET CTRL DIRECTION +const static int PIN_DTD = 5; // DATA DIRECTION + +// SCSI signal pin assignment +const static int PIN_DT0 = 21; // Data 0 +const static int PIN_DT1 = 26; // Data 1 +const static int PIN_DT2 = 20; // Data 2 +const static int PIN_DT3 = 19; // Data 3 +const static int PIN_DT4 = 16; // Data 4 +const static int PIN_DT5 = 13; // Data 5 +const static int PIN_DT6 = 12; // Data 6 +const static int PIN_DT7 = 11; // Data 7 +const static int PIN_DP = 25; // Data parity +const static int PIN_ATN = 10; // ATN +const static int PIN_RST = 22; // RST +const static int PIN_ACK = 24; // ACK +const static int PIN_REQ = 15; // REQ +const static int PIN_MSG = 17; // MSG +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 diff --git a/cpp/hal/connection_type/connection_standard.h b/cpp/hal/connection_type/connection_standard.h new file mode 100644 index 00000000..d3c2ed0d --- /dev/null +++ b/cpp/hal/connection_type/connection_standard.h @@ -0,0 +1,87 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "hal/pi_defs/bpi-m2p.h" +#include + +// +// RaSCSI standard (SCSI logic, standard pin assignment) +// + +const std::string CONNECT_DESC = "STANDARD"; // Startup message + +// Select signal control mode +const static int SIGNAL_CONTROL_MODE = 0; // SCSI logical specification + +// Control signal pin assignment (-1 means no control) +const static int PIN_ACT = 4; // ACTIVE +const static int PIN_ENB = 5; // ENABLE +const static int PIN_IND = -1; // INITIATOR CTRL DIRECTION +const static int PIN_TAD = -1; // TARGET CTRL DIRECTION +const static int PIN_DTD = -1; // DATA DIRECTION + +// Control signal output logic +#define ACT_ON ON // ACTIVE SIGNAL ON +#define ENB_ON ON // ENABLE SIGNAL ON +#define IND_IN OFF // INITIATOR SIGNAL INPUT +#define TAD_IN OFF // TARGET SIGNAL INPUT +#define DTD_IN ON // DATA SIGNAL INPUT + +// SCSI signal pin assignment +const static int PIN_DT0 = 10; // Data 0 +const static int PIN_DT1 = 11; // Data 1 +const static int PIN_DT2 = 12; // Data 2 +const static int PIN_DT3 = 13; // Data 3 +const static int PIN_DT4 = 14; // Data 4 +const static int PIN_DT5 = 15; // Data 5 +const static int PIN_DT6 = 16; // Data 6 +const static int PIN_DT7 = 17; // Data 7 +const static int PIN_DP = 18; // Data parity +const static int PIN_ATN = 19; // ATN +const static int PIN_RST = 20; // RST +const static int PIN_ACK = 21; // ACK +const static int PIN_REQ = 22; // REQ +const static int PIN_MSG = 23; // MSG +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 diff --git a/cpp/hal/data_sample.cpp b/cpp/hal/data_sample.cpp new file mode 100644 index 00000000..9582e995 --- /dev/null +++ b/cpp/hal/data_sample.cpp @@ -0,0 +1,40 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 akuker +// +// [ SCSI Bus Monitor ] +// +//--------------------------------------------------------------------------- + +#include "hal/bus.h" +#include "hal/data_sample.h" +#include + +using namespace std; + +string DataSample::GetPhaseStr() const +{ + return BUS::GetPhaseStrRaw(GetPhase()); +} + +phase_t DataSample::GetPhase() const +{ + // Selection Phase + if (GetSEL()) { + return phase_t::selection; + } + + // Bus busy phase + if (!GetBSY()) { + return phase_t::busfree; + } + + // Get target phase from bus signal line + uint32_t mci = GetMSG() ? 0x04 : 0x00; + mci |= GetCD() ? 0x02 : 0x00; + mci |= GetIO() ? 0x01 : 0x00; + return BUS::GetPhase(mci); +} diff --git a/cpp/hal/data_sample.h b/cpp/hal/data_sample.h new file mode 100644 index 00000000..f3361029 --- /dev/null +++ b/cpp/hal/data_sample.h @@ -0,0 +1,54 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 akuker +// +// [ Logical representation of a single data sample ] +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "shared/scsi.h" +#include + +using namespace scsi_defs; + +class DataSample +{ + public: + virtual bool GetBSY() const = 0; + virtual bool GetSEL() const = 0; + virtual bool GetATN() const = 0; + virtual bool GetACK() const = 0; + virtual bool GetRST() const = 0; + virtual bool GetMSG() const = 0; + virtual bool GetCD() const = 0; + virtual bool GetIO() const = 0; + virtual bool GetREQ() const = 0; + virtual bool GetACT() const = 0; + virtual uint8_t GetDAT() const = 0; + virtual bool GetDP() const = 0; + + virtual uint32_t GetRawCapture() const = 0; + + phase_t GetPhase() const; + virtual bool GetSignal(int pin) const = 0; + + uint64_t GetTimestamp() const + { + return timestamp; + } + + string GetPhaseStr() const; + + virtual ~DataSample() = default; + + explicit DataSample(uint64_t in_timestamp) : timestamp{in_timestamp} {} + DataSample() = default; + + private: + uint64_t timestamp = 0; +}; diff --git a/cpp/hal/data_sample_bananam2p.cpp b/cpp/hal/data_sample_bananam2p.cpp new file mode 100644 index 00000000..2e329f83 --- /dev/null +++ b/cpp/hal/data_sample_bananam2p.cpp @@ -0,0 +1,64 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 akuker +// +// [ SCSI Bus Monitor ] +// +//--------------------------------------------------------------------------- + +#include "data_sample_bananam2p.h" +#include "hal/sunxi_utils.h" +#include + +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; +} \ No newline at end of file diff --git a/cpp/hal/data_sample_bananam2p.h b/cpp/hal/data_sample_bananam2p.h new file mode 100644 index 00000000..983f2acb --- /dev/null +++ b/cpp/hal/data_sample_bananam2p.h @@ -0,0 +1,94 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// 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 + +#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 &in_data, uint64_t in_timestamp) + : DataSample{in_timestamp}, data{in_data} + { + } + DataSample_BananaM2p() = default; + + ~DataSample_BananaM2p() override = default; + + private: + array data = {0}; +}; \ No newline at end of file diff --git a/cpp/hal/data_sample_raspberry.cpp b/cpp/hal/data_sample_raspberry.cpp new file mode 100644 index 00000000..523e1f87 --- /dev/null +++ b/cpp/hal/data_sample_raspberry.cpp @@ -0,0 +1,14 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 akuker +// +// [ SCSI Bus Monitor ] +// +//--------------------------------------------------------------------------- + +#include "shared/scsi.h" +#include "data_sample.h" + diff --git a/cpp/hal/data_sample_raspberry.h b/cpp/hal/data_sample_raspberry.h new file mode 100644 index 00000000..9a40a958 --- /dev/null +++ b/cpp/hal/data_sample_raspberry.h @@ -0,0 +1,109 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// 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" + +#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_Raspberry final : public DataSample +{ + public: + bool GetSignal(int pin) const override + { + return (bool)((data >> pin) & 1); + }; + + bool GetBSY() const override + { + return GetSignal(PIN_BSY); + } + bool GetSEL() const override + { + return GetSignal(PIN_SEL); + } + bool GetATN() const override + { + return GetSignal(PIN_ATN); + } + bool GetACK() const override + { + return GetSignal(PIN_ACK); + } + bool GetRST() const override + { + return GetSignal(PIN_RST); + } + bool GetMSG() const override + { + return GetSignal(PIN_MSG); + } + bool GetCD() const override + { + return GetSignal(PIN_CD); + } + bool GetIO() const override + { + return GetSignal(PIN_IO); + } + bool GetREQ() const override + { + return GetSignal(PIN_REQ); + } + bool GetACT() const override + { + return GetSignal(PIN_ACT); + } + bool GetDP() const override + { + return GetSignal(PIN_DP); + } + uint8_t GetDAT() const override + { + uint8_t ret_val = 0; + ret_val |= (data >> (PIN_DT0 - 0)) & 0x01; // NOSONAR: GCC 10 doesn't support shift operations on std::byte + ret_val |= (data >> (PIN_DT1 - 1)) & 0x02; // NOSONAR: GCC 10 doesn't support shift operations on std::byte + ret_val |= (data >> (PIN_DT2 - 2)) & 0x04; // NOSONAR: GCC 10 doesn't support shift operations on std::byte + ret_val |= (data >> (PIN_DT3 - 3)) & 0x08; // NOSONAR: GCC 10 doesn't support shift operations on std::byte + ret_val |= (data >> (PIN_DT4 - 4)) & 0x10; // NOSONAR: GCC 10 doesn't support shift operations on std::byte + ret_val |= (data >> (PIN_DT5 - 5)) & 0x20; // NOSONAR: GCC 10 doesn't support shift operations on std::byte + ret_val |= (data >> (PIN_DT6 - 6)) & 0x40; // NOSONAR: GCC 10 doesn't support shift operations on std::byte + ret_val |= (data >> (PIN_DT7 - 7)) & 0x80; // NOSONAR: GCC 10 doesn't support shift operations on std::byte + return ret_val; + } + + uint32_t GetRawCapture() const override + { + return data; + } + + DataSample_Raspberry(const uint32_t in_data, const uint64_t in_timestamp) : DataSample{in_timestamp}, data{in_data} + { + } + DataSample_Raspberry() = default; + + ~DataSample_Raspberry() override = default; + + private: + uint32_t data = 0; +}; \ No newline at end of file diff --git a/cpp/hal/gpiobus.cpp b/cpp/hal/gpiobus.cpp index 18c7130c..2e36dc2e 100644 --- a/cpp/hal/gpiobus.cpp +++ b/cpp/hal/gpiobus.cpp @@ -10,668 +10,28 @@ // //--------------------------------------------------------------------------- -#include -#include -#include #include "hal/gpiobus.h" +#include "hal/sbc_version.h" #include "hal/systimer.h" -#include "shared/config.h" #include "shared/log.h" #include +#include +#include +#include #ifdef __linux__ #include #endif using namespace std; -#ifdef __linux__ -//--------------------------------------------------------------------------- -// -// imported from bcm_host.c -// -//--------------------------------------------------------------------------- -static uint32_t get_dt_ranges(const char *filename, uint32_t offset) -{ - uint32_t address = ~0; - if (FILE *fp = fopen(filename, "rb"); fp) { - fseek(fp, offset, SEEK_SET); - if (array 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); - } - return address; -} - -uint32_t bcm_host_get_peripheral_address() -{ - uint32_t address = get_dt_ranges("/proc/device-tree/soc/ranges", 4); - if (address == 0) { - address = get_dt_ranges("/proc/device-tree/soc/ranges", 8); - } - address = (address == (uint32_t)~0) ? 0x20000000 : address; - return address; -} -#endif - -#ifdef __NetBSD__ -// Assume the Raspberry Pi series and estimate the address from CPU -uint32_t bcm_host_get_peripheral_address() -{ - array buf; - size_t len = buf.size(); - uint32_t address; - - if (sysctlbyname("hw.model", buf.data(), &len, NULL, 0) || - strstr(buf, "ARM1176JZ-S") != buf.data()) { - // Failed to get CPU model || Not BCM2835 - // use the address of BCM283[67] - address = 0x3f000000; - } else { - // Use BCM2835 address - address = 0x20000000; - } - printf("Peripheral address : 0x%lx\n", address); - return address; -} -#endif - bool GPIOBUS::Init(mode_e mode) { - // Save operation mode - actmode = mode; + GPIO_FUNCTION_TRACE -#if defined(__x86_64__) || defined(__X86__) - return true; -#else - int i; -#ifdef USE_SEL_EVENT_ENABLE - epoll_event ev = {}; -#endif + // Save operation mode + actmode = mode; - // Get the base address - baseaddr = (uint32_t)bcm_host_get_peripheral_address(); - - // 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?") - 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") - close(fd); - return false; - } - - // Determine the type of raspberry pi from the base address - if (baseaddr == 0xfe000000) { - rpitype = 4; - } else if (baseaddr == 0x3f000000) { - rpitype = 2; - } else { - rpitype = 1; - } - - // GPIO - gpio = (uint32_t *)map; - gpio += GPIO_OFFSET / sizeof(uint32_t); - level = &gpio[GPIO_LEV_0]; - - // PADS - pads = (uint32_t *)map; - pads += PADS_OFFSET / sizeof(uint32_t); - - // System timer - SysTimer::Init( - (uint32_t *)map + SYST_OFFSET / sizeof(uint32_t), - (uint32_t *)map + ARMT_OFFSET / sizeof(uint32_t)); - - // Interrupt controller - irpctl = (uint32_t *)map; - irpctl += IRPT_OFFSET / sizeof(uint32_t); - - // Quad-A7 control - qa7regs = (uint32_t *)map; - qa7regs += QA7_OFFSET / sizeof(uint32_t); - - // Map GIC memory - if (rpitype == 4) { - map = mmap(NULL, 8192, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, ARM_GICD_BASE); - if (map == MAP_FAILED) { - close(fd); - return false; - } - gicd = (uint32_t *)map; - gicc = (uint32_t *)map; - gicc += (ARM_GICC_BASE - ARM_GICD_BASE) / sizeof(uint32_t); - } else { - gicd = NULL; - gicc = NULL; - } - close(fd); - - // Set Drive Strength to 16mA - DrvConfig(7); - - // Set pull up/pull down -#if SIGNAL_CONTROL_MODE == 0 - int pullmode = GPIO_PULLNONE; -#elif SIGNAL_CONTROL_MODE == 1 - int pullmode = GPIO_PULLUP; -#else - int pullmode = GPIO_PULLDOWN; -#endif - - // Initialize all signals - for (i = 0; SignalTable[i] >= 0; i++) { - int j = SignalTable[i]; - PinSetSignal(j, OFF); - PinConfig(j, GPIO_INPUT); - PullConfig(j, pullmode); - } - - // Set control signals - PinSetSignal(PIN_ACT, OFF); - PinSetSignal(PIN_TAD, OFF); - PinSetSignal(PIN_IND, OFF); - PinSetSignal(PIN_DTD, OFF); - PinConfig(PIN_ACT, GPIO_OUTPUT); - PinConfig(PIN_TAD, GPIO_OUTPUT); - PinConfig(PIN_IND, GPIO_OUTPUT); - PinConfig(PIN_DTD, GPIO_OUTPUT); - - // Set the ENABLE signal - // This is used to show that the application is running - PinSetSignal(PIN_ENB, ENB_OFF); - PinConfig(PIN_ENB, GPIO_OUTPUT); - - // GPFSEL backup - gpfsel[0] = gpio[GPIO_FSEL_0]; - gpfsel[1] = gpio[GPIO_FSEL_1]; - gpfsel[2] = gpio[GPIO_FSEL_2]; - gpfsel[3] = gpio[GPIO_FSEL_3]; - - // Initialize SEL signal interrupt -#ifdef USE_SEL_EVENT_ENABLE - // GPIO chip open - fd = open("/dev/gpiochip0", 0); - if (fd == -1) { - LOGERROR("Unable to open /dev/gpiochip0. Is RaSCSI already running?") - return false; - } - - // Event request setting - strcpy(selevreq.consumer_label, "RaSCSI"); - selevreq.lineoffset = PIN_SEL; - selevreq.handleflags = GPIOHANDLE_REQUEST_INPUT; -#if SIGNAL_CONTROL_MODE < 2 - selevreq.eventflags = GPIOEVENT_REQUEST_FALLING_EDGE; -#else - selevreq.eventflags = GPIOEVENT_REQUEST_RISING_EDGE; -#endif // SIGNAL_CONTROL_MODE - - //Get event request - if (ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &selevreq) == -1) { - LOGERROR("Unable to register event request. Is RaSCSI already running?") - close(fd); - return false; - } - - // Close GPIO chip file handle - close(fd); - - // epoll initialization - epfd = epoll_create(1); - ev.events = EPOLLIN | EPOLLPRI; - ev.data.fd = selevreq.fd; - epoll_ctl(epfd, EPOLL_CTL_ADD, selevreq.fd, &ev); -#else - // Edge detection setting -#if SIGNAL_CONTROL_MODE == 2 - gpio[GPIO_AREN_0] = 1 << PIN_SEL; -#else - gpio[GPIO_AFEN_0] = 1 << PIN_SEL; -#endif // SIGNAL_CONTROL_MODE - - // Clear event - gpio[GPIO_EDS_0] = 1 << PIN_SEL; - - // Register interrupt handler - setIrqFuncAddress(IrqHandler); - - // GPIO interrupt setting - if (rpitype == 4) { - // GIC Invalid - gicd[GICD_CTLR] = 0; - - // Route all interupts to core 0 - for (i = 0; i < 8; i++) { - gicd[GICD_ICENABLER0 + i] = 0xffffffff; - gicd[GICD_ICPENDR0 + i] = 0xffffffff; - gicd[GICD_ICACTIVER0 + i] = 0xffffffff; - } - for (i = 0; i < 64; i++) { - gicd[GICD_IPRIORITYR0 + i] = 0xa0a0a0a0; - gicd[GICD_ITARGETSR0 + i] = 0x01010101; - } - - // Set all interrupts as level triggers - for (i = 0; i < 16; i++) { - gicd[GICD_ICFGR0 + i] = 0; - } - - // GIC Invalid - gicd[GICD_CTLR] = 1; - - // Enable CPU interface for core 0 - gicc[GICC_PMR] = 0xf0; - gicc[GICC_CTLR] = 1; - - // Enable interrupts - gicd[GICD_ISENABLER0 + (GIC_GPIO_IRQ / 32)] = - 1 << (GIC_GPIO_IRQ % 32); - } else { - // Enable interrupts - irpctl[IRPT_ENB_IRQ_2] = (1 << (GPIO_IRQ % 32)); - } -#endif // USE_SEL_EVENT_ENABLE - - // Create work table - MakeTable(); - - // Finally, enable ENABLE - // Show the user that this app is running - SetControl(PIN_ENB, ENB_ON); - - return true; -#endif // ifdef __x86_64__ || __X86__ - -} - -void GPIOBUS::Cleanup() -{ -#if defined(__x86_64__) || defined(__X86__) - return; -#else - // Release SEL signal interrupt -#ifdef USE_SEL_EVENT_ENABLE - close(selevreq.fd); -#endif // USE_SEL_EVENT_ENABLE - - // Set control signals - PinSetSignal(PIN_ENB, OFF); - PinSetSignal(PIN_ACT, OFF); - PinSetSignal(PIN_TAD, OFF); - PinSetSignal(PIN_IND, OFF); - PinSetSignal(PIN_DTD, OFF); - PinConfig(PIN_ACT, GPIO_INPUT); - PinConfig(PIN_TAD, GPIO_INPUT); - PinConfig(PIN_IND, GPIO_INPUT); - PinConfig(PIN_DTD, GPIO_INPUT); - - // Initialize all signals - for (int i = 0; SignalTable[i] >= 0; i++) { - int pin = SignalTable[i]; - PinSetSignal(pin, OFF); - PinConfig(pin, GPIO_INPUT); - PullConfig(pin, GPIO_PULLNONE); - } - - // Set drive strength back to 8mA - DrvConfig(3); -#endif // ifdef __x86_64__ || __X86__ -} - -void GPIOBUS::Reset() -{ -#if defined(__x86_64__) || defined(__X86__) - return; -#else - int i; - int j; - - // Turn off active signal - SetControl(PIN_ACT, ACT_OFF); - - // Set all signals to off - for (i = 0;; i++) { - j = SignalTable[i]; - if (j < 0) { - break; - } - - SetSignal(j, OFF); - } - - if (actmode == mode_e::TARGET) { - // Target mode - - // Set target signal to input - SetControl(PIN_TAD, TAD_IN); - SetMode(PIN_BSY, IN); - SetMode(PIN_MSG, IN); - SetMode(PIN_CD, IN); - SetMode(PIN_REQ, IN); - SetMode(PIN_IO, IN); - - // Set the initiator signal to input - SetControl(PIN_IND, IND_IN); - SetMode(PIN_SEL, IN); - SetMode(PIN_ATN, IN); - SetMode(PIN_ACK, IN); - SetMode(PIN_RST, IN); - - // Set data bus signals to input - SetControl(PIN_DTD, DTD_IN); - SetMode(PIN_DT0, IN); - SetMode(PIN_DT1, IN); - SetMode(PIN_DT2, IN); - SetMode(PIN_DT3, IN); - SetMode(PIN_DT4, IN); - SetMode(PIN_DT5, IN); - SetMode(PIN_DT6, IN); - SetMode(PIN_DT7, IN); - SetMode(PIN_DP, IN); - } else { - // Initiator mode - - // Set target signal to input - SetControl(PIN_TAD, TAD_IN); - SetMode(PIN_BSY, IN); - SetMode(PIN_MSG, IN); - SetMode(PIN_CD, IN); - SetMode(PIN_REQ, IN); - SetMode(PIN_IO, IN); - - // Set the initiator signal to output - SetControl(PIN_IND, IND_OUT); - SetMode(PIN_SEL, OUT); - SetMode(PIN_ATN, OUT); - SetMode(PIN_ACK, OUT); - SetMode(PIN_RST, OUT); - - // Set the data bus signals to output - SetControl(PIN_DTD, DTD_OUT); - SetMode(PIN_DT0, OUT); - SetMode(PIN_DT1, OUT); - SetMode(PIN_DT2, OUT); - SetMode(PIN_DT3, OUT); - SetMode(PIN_DT4, OUT); - SetMode(PIN_DT5, OUT); - SetMode(PIN_DT6, OUT); - SetMode(PIN_DT7, OUT); - SetMode(PIN_DP, OUT); - } - - // Initialize all signals - signals = 0; -#endif // ifdef __x86_64__ || __X86__ -} - -void GPIOBUS::SetENB(bool ast) -{ - PinSetSignal(PIN_ENB, ast ? ENB_ON : ENB_OFF); -} - -bool GPIOBUS::GetBSY() const -{ - return GetSignal(PIN_BSY); -} - -void GPIOBUS::SetBSY(bool ast) -{ - // Set BSY signal - SetSignal(PIN_BSY, ast); - - if (actmode == mode_e::TARGET) { - if (ast) { - // Turn on ACTIVE signal - SetControl(PIN_ACT, ACT_ON); - - // Set Target signal to output - SetControl(PIN_TAD, TAD_OUT); - - SetMode(PIN_BSY, OUT); - SetMode(PIN_MSG, OUT); - SetMode(PIN_CD, OUT); - SetMode(PIN_REQ, OUT); - SetMode(PIN_IO, OUT); - } else { - // Turn off the ACTIVE signal - SetControl(PIN_ACT, ACT_OFF); - - // Set the target signal to input - SetControl(PIN_TAD, TAD_IN); - - SetMode(PIN_BSY, IN); - SetMode(PIN_MSG, IN); - SetMode(PIN_CD, IN); - SetMode(PIN_REQ, IN); - SetMode(PIN_IO, IN); - } - } -} - -bool GPIOBUS::GetSEL() const -{ - return GetSignal(PIN_SEL); -} - -void GPIOBUS::SetSEL(bool ast) -{ - if (actmode == mode_e::INITIATOR && ast) { - // Turn on ACTIVE signal - SetControl(PIN_ACT, ACT_ON); - } - - // Set SEL signal - SetSignal(PIN_SEL, ast); -} - -bool GPIOBUS::GetATN() const -{ - return GetSignal(PIN_ATN); -} - -void GPIOBUS::SetATN(bool ast) -{ - SetSignal(PIN_ATN, ast); -} - -bool GPIOBUS::GetACK() const -{ - return GetSignal(PIN_ACK); -} - -void GPIOBUS::SetACK(bool ast) -{ - SetSignal(PIN_ACK, ast); -} - -bool GPIOBUS::GetACT() const -{ - return GetSignal(PIN_ACT); -} - -void GPIOBUS::SetACT(bool ast) -{ - SetSignal(PIN_ACT, ast); -} - -bool GPIOBUS::GetRST() const -{ - return GetSignal(PIN_RST); -} - -void GPIOBUS::SetRST(bool ast) -{ - SetSignal(PIN_RST, ast); -} - -bool GPIOBUS::GetMSG() const -{ - return GetSignal(PIN_MSG); -} - -void GPIOBUS::SetMSG(bool ast) -{ - SetSignal(PIN_MSG, ast); -} - -bool GPIOBUS::GetCD() const -{ - return GetSignal(PIN_CD); -} - -void GPIOBUS::SetCD(bool ast) -{ - SetSignal(PIN_CD, ast); -} - -bool GPIOBUS::GetIO() -{ - bool ast = GetSignal(PIN_IO); - - if (actmode == mode_e::INITIATOR) { - // Change the data input/output direction by IO signal - if (ast) { - SetControl(PIN_DTD, DTD_IN); - SetMode(PIN_DT0, IN); - SetMode(PIN_DT1, IN); - SetMode(PIN_DT2, IN); - SetMode(PIN_DT3, IN); - SetMode(PIN_DT4, IN); - SetMode(PIN_DT5, IN); - SetMode(PIN_DT6, IN); - SetMode(PIN_DT7, IN); - SetMode(PIN_DP, IN); - } else { - SetControl(PIN_DTD, DTD_OUT); - SetMode(PIN_DT0, OUT); - SetMode(PIN_DT1, OUT); - SetMode(PIN_DT2, OUT); - SetMode(PIN_DT3, OUT); - SetMode(PIN_DT4, OUT); - SetMode(PIN_DT5, OUT); - SetMode(PIN_DT6, OUT); - SetMode(PIN_DT7, OUT); - SetMode(PIN_DP, OUT); - } - } - - return ast; -} - -void GPIOBUS::SetIO(bool ast) -{ - SetSignal(PIN_IO, ast); - - if (actmode == mode_e::TARGET) { - // Change the data input/output direction by IO signal - if (ast) { - SetControl(PIN_DTD, DTD_OUT); - SetDAT(0); - SetMode(PIN_DT0, OUT); - SetMode(PIN_DT1, OUT); - SetMode(PIN_DT2, OUT); - SetMode(PIN_DT3, OUT); - SetMode(PIN_DT4, OUT); - SetMode(PIN_DT5, OUT); - SetMode(PIN_DT6, OUT); - SetMode(PIN_DT7, OUT); - SetMode(PIN_DP, OUT); - } else { - SetControl(PIN_DTD, DTD_IN); - SetMode(PIN_DT0, IN); - SetMode(PIN_DT1, IN); - SetMode(PIN_DT2, IN); - SetMode(PIN_DT3, IN); - SetMode(PIN_DT4, IN); - SetMode(PIN_DT5, IN); - SetMode(PIN_DT6, IN); - SetMode(PIN_DT7, IN); - SetMode(PIN_DP, IN); - } - } -} - -bool GPIOBUS::GetREQ() const -{ - return GetSignal(PIN_REQ); -} - -void GPIOBUS::SetREQ(bool ast) -{ - SetSignal(PIN_REQ, ast); -} - -//--------------------------------------------------------------------------- -// -// Get data signals -// -//--------------------------------------------------------------------------- -uint8_t GPIOBUS::GetDAT() -{ - uint32_t data = Acquire(); - data = - ((data >> (PIN_DT0 - 0)) & (1 << 0)) | - ((data >> (PIN_DT1 - 1)) & (1 << 1)) | - ((data >> (PIN_DT2 - 2)) & (1 << 2)) | - ((data >> (PIN_DT3 - 3)) & (1 << 3)) | - ((data >> (PIN_DT4 - 4)) & (1 << 4)) | - ((data >> (PIN_DT5 - 5)) & (1 << 5)) | - ((data >> (PIN_DT6 - 6)) & (1 << 6)) | - ((data >> (PIN_DT7 - 7)) & (1 << 7)); - - return (uint8_t)data; -} - -//--------------------------------------------------------------------------- -// -// Set data signals -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetDAT(uint8_t dat) -{ - // Write to port -#if SIGNAL_CONTROL_MODE == 0 - uint32_t fsel = gpfsel[0]; - fsel &= tblDatMsk[0][dat]; - fsel |= tblDatSet[0][dat]; - if (fsel != gpfsel[0]) { - gpfsel[0] = fsel; - gpio[GPIO_FSEL_0] = fsel; - } - - fsel = gpfsel[1]; - fsel &= tblDatMsk[1][dat]; - fsel |= tblDatSet[1][dat]; - if (fsel != gpfsel[1]) { - gpfsel[1] = fsel; - gpio[GPIO_FSEL_1] = fsel; - } - - fsel = gpfsel[2]; - fsel &= tblDatMsk[2][dat]; - fsel |= tblDatSet[2][dat]; - if (fsel != gpfsel[2]) { - gpfsel[2] = fsel; - gpio[GPIO_FSEL_2] = fsel; - } -#else - gpio[GPIO_CLR_0] = tblDatMsk[dat]; - gpio[GPIO_SET_0] = tblDatSet[dat]; -#endif // SIGNAL_CONTROL_MODE -} - -bool GPIOBUS::GetDP() const -{ - return GetSignal(PIN_DP); + return true; } //--------------------------------------------------------------------------- @@ -679,124 +39,125 @@ bool GPIOBUS::GetDP() const // Receive command handshake // //--------------------------------------------------------------------------- -int GPIOBUS::CommandHandShake(vector& buf) +int GPIOBUS::CommandHandShake(vector &buf) { - // Only works in TARGET mode - if (actmode != mode_e::TARGET) { - return 0; - } + GPIO_FUNCTION_TRACE + // Only works in TARGET mode + if (actmode != mode_e::TARGET) { + return 0; + } - DisableIRQ(); + DisableIRQ(); - // Assert REQ signal - SetSignal(PIN_REQ, ON); + // Assert REQ signal + SetREQ(ON); - // Wait for ACK signal - bool ret = WaitSignal(PIN_ACK, ON); + // Wait for ACK signal + bool ret = WaitACK(ON); - // Wait until the signal line stabilizes - SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); + // Wait until the signal line stabilizes + SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); - // Get data - buf[0] = GetDAT(); + // Get data + buf[0] = GetDAT(); - // Disable REQ signal - SetSignal(PIN_REQ, OFF); + // Disable REQ signal + SetREQ(OFF); - // Timeout waiting for ACK assertion - if (!ret) { - EnableIRQ(); - return 0; - } + // Timeout waiting for ACK assertion + if (!ret) { + EnableIRQ(); + return 0; + } - // Wait for ACK to clear - ret = WaitSignal(PIN_ACK, OFF); + // Wait for ACK to clear + ret = WaitACK(OFF); - // Timeout waiting for ACK to clear - if (!ret) { - EnableIRQ(); - return 0; - } + // Timeout waiting for ACK to clear + if (!ret) { + EnableIRQ(); + return 0; + } - // The ICD AdSCSI ST, AdSCSI Plus ST and AdSCSI Micro ST host adapters allow SCSI devices to be connected - // to the ACSI bus of Atari ST/TT computers and some clones. ICD-aware drivers prepend a $1F byte in front - // of the CDB (effectively resulting in a custom SCSI command) in order to get access to the full SCSI - // command set. Native ACSI is limited to the low SCSI command classes with command bytes < $20. - // Most other host adapters (e.g. LINK96/97 and the one by Inventronik) and also several devices (e.g. - // UltraSatan or GigaFile) that can directly be connected to the Atari's ACSI port also support ICD - // semantics. I fact, these semantics have become a standard in the Atari world. + // The ICD AdSCSI ST, AdSCSI Plus ST and AdSCSI Micro ST host adapters allow SCSI devices to be connected + // to the ACSI bus of Atari ST/TT computers and some clones. ICD-aware drivers prepend a $1F byte in front + // of the CDB (effectively resulting in a custom SCSI command) in order to get access to the full SCSI + // command set. Native ACSI is limited to the low SCSI command classes with command bytes < $20. + // Most other host adapters (e.g. LINK96/97 and the one by Inventronik) and also several devices (e.g. + // UltraSatan or GigaFile) that can directly be connected to the Atari's ACSI port also support ICD + // semantics. I fact, these semantics have become a standard in the Atari world. - // RaSCSI becomes ICD compatible by ignoring the prepended $1F byte before processing the CDB. - if (buf[0] == 0x1F) { - SetSignal(PIN_REQ, ON); + // RaSCSI becomes ICD compatible by ignoring the prepended $1F byte before processing the CDB. + if (buf[0] == 0x1F) { + SetREQ(ON); - ret = WaitSignal(PIN_ACK, ON); + ret = WaitACK(ON); - SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); + SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); - // Get the actual SCSI command - buf[0] = GetDAT(); + // Get the actual SCSI command + buf[0] = GetDAT(); - SetSignal(PIN_REQ, OFF); + SetREQ(OFF); - if (!ret) { - EnableIRQ(); - return 0; - } + if (!ret) { + EnableIRQ(); + return 0; + } - WaitSignal(PIN_ACK, OFF); + WaitACK(OFF); - if (!ret) { - EnableIRQ(); - return 0; - } - } + if (!ret) { + EnableIRQ(); + return 0; + } + } - const int command_byte_count = GetCommandByteCount(buf[0]); - if (command_byte_count == 0) { - EnableIRQ(); + const int command_byte_count = GetCommandByteCount(buf[0]); + if (command_byte_count == 0) { + EnableIRQ(); - return 0; - } + return 0; + } - int offset = 0; + int offset = 0; - int bytes_received; - for (bytes_received = 1; bytes_received < command_byte_count; bytes_received++) { - ++offset; + int bytes_received; + for (bytes_received = 1; bytes_received < command_byte_count; bytes_received++) { + ++offset; - // Assert REQ signal - SetSignal(PIN_REQ, ON); + // Assert REQ signal + SetREQ(ON); - // Wait for ACK signal - ret = WaitSignal(PIN_ACK, ON); + // Wait for ACK signal + ret = WaitACK(ON); - // Wait until the signal line stabilizes - SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); + // Wait until the signal line stabilizes + SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); - // Get data - buf[offset] = GetDAT(); + // Get data + buf[offset] = GetDAT(); - // Clear the REQ signal - SetSignal(PIN_REQ, OFF); + // Clear the REQ signal + SetREQ(OFF); - // Check for timeout waiting for ACK assertion - if (!ret) { - break; - } + // Check for timeout waiting for ACK assertion + if (!ret) { + break; + } - // Wait for ACK to clear - ret = WaitSignal(PIN_ACK, OFF); + // Wait for ACK to clear + ret = WaitACK(OFF); - // Check for timeout waiting for ACK to clear - if (!ret) { - break; - } - } + // Check for timeout waiting for ACK to clear + if (!ret) { + break; + } + } - EnableIRQ(); + EnableIRQ(); - return bytes_received; + return bytes_received; } //--------------------------------------------------------------------------- @@ -806,97 +167,101 @@ int GPIOBUS::CommandHandShake(vector& buf) //--------------------------------------------------------------------------- int GPIOBUS::ReceiveHandShake(uint8_t *buf, int count) { - int i; + GPIO_FUNCTION_TRACE + int i; - // Disable IRQs - DisableIRQ(); + // Disable IRQs + DisableIRQ(); - if (actmode == mode_e::TARGET) { - for (i = 0; i < count; i++) { - // Assert the REQ signal - SetSignal(PIN_REQ, ON); + if (actmode == mode_e::TARGET) { + for (i = 0; i < count; i++) { + // Assert the REQ signal + SetREQ(ON); - // Wait for ACK - bool ret = WaitSignal(PIN_ACK, ON); + // Wait for ACK + bool ret = WaitACK(ON); - // Wait until the signal line stabilizes - SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); + // Wait until the signal line stabilizes + SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); - // Get data - *buf = GetDAT(); + // Get data + *buf = GetDAT(); - // Clear the REQ signal - SetSignal(PIN_REQ, OFF); + // Clear the REQ signal + SetREQ(OFF); - // Check for timeout waiting for ACK signal - if (!ret) { - break; - } + // Check for timeout waiting for ACK signal + if (!ret) { + break; + } - // Wait for ACK to clear - ret = WaitSignal(PIN_ACK, OFF); + // Wait for ACK to clear + ret = WaitACK(OFF); - // Check for timeout waiting for ACK to clear - if (!ret) { - break; - } + // Check for timeout waiting for ACK to clear + if (!ret) { + break; + } - // Advance the buffer pointer to receive the next byte - buf++; - } - } else { - // Get phase - uint32_t phase = Acquire() & GPIO_MCI; + // Advance the buffer pointer to receive the next byte + buf++; + } + } else { + // Get phase + Acquire(); + phase_t phase = GetPhase(); - for (i = 0; i < count; i++) { - // Wait for the REQ signal to be asserted - bool ret = WaitSignal(PIN_REQ, ON); + for (i = 0; i < count; i++) { + // Wait for the REQ signal to be asserted + bool ret = WaitREQ(ON); - // Check for timeout waiting for REQ signal - if (!ret) { - break; - } + // Check for timeout waiting for REQ signal + if (!ret) { + break; + } - // Phase error - if ((signals & GPIO_MCI) != phase) { - break; - } + // Phase error + Acquire(); + if (GetPhase() != phase) { + break; + } - // Wait until the signal line stabilizes - SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); + // Wait until the signal line stabilizes + SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); - // Get data - *buf = GetDAT(); + // Get data + *buf = GetDAT(); - // Assert the ACK signal - SetSignal(PIN_ACK, ON); + // Assert the ACK signal + SetACK(ON); - // Wait for REQ to clear - ret = WaitSignal(PIN_REQ, OFF); + // Wait for REQ to clear + ret = WaitREQ(OFF); - // Clear the ACK signal - SetSignal(PIN_ACK, OFF); + // Clear the ACK signal + SetACK(OFF); - // Check for timeout waiting for REQ to clear - if (!ret) { - break; - } + // Check for timeout waiting for REQ to clear + if (!ret) { + break; + } - // Phase error - if ((signals & GPIO_MCI) != phase) { - break; - } + // Phase error + Acquire(); + if (GetPhase() != phase) { + break; + } - // Advance the buffer pointer to receive the next byte - buf++; - } - } + // Advance the buffer pointer to receive the next byte + buf++; + } + } - // Re-enable IRQ - EnableIRQ(); + // Re-enable IRQ + EnableIRQ(); - // Return the number of bytes received - return i; + // Return the number of bytes received + return i; } //--------------------------------------------------------------------------- @@ -906,111 +271,116 @@ int GPIOBUS::ReceiveHandShake(uint8_t *buf, int count) //--------------------------------------------------------------------------- int GPIOBUS::SendHandShake(uint8_t *buf, int count, int delay_after_bytes) { - int i; + GPIO_FUNCTION_TRACE + int i; - // Disable IRQs - DisableIRQ(); + // Disable IRQs + DisableIRQ(); - 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) - SysTimer::SleepUsec(SCSI_DELAY_SEND_DATA_DAYNAPORT_US); - } + 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) + SysTimer::SleepUsec(SCSI_DELAY_SEND_DATA_DAYNAPORT_US); + } - // Set the DATA signals - SetDAT(*buf); + // Set the DATA signals + SetDAT(*buf); - // Wait for ACK to clear - bool ret = WaitSignal(PIN_ACK, OFF); + // Wait for ACK to clear + bool ret = WaitACK(OFF); - // Check for timeout waiting for ACK to clear - if (!ret) { - break; - } + // Check for timeout waiting for ACK to clear + if (!ret) { + break; + } - // Already waiting for ACK to clear + // Already waiting for ACK to clear - // Assert the REQ signal - SetSignal(PIN_REQ, ON); + // Assert the REQ signal + SetREQ(ON); - // Wait for ACK - ret = WaitSignal(PIN_ACK, ON); + // Wait for ACK + ret = WaitACK(ON); - // Clear REQ signal - SetSignal(PIN_REQ, OFF); + // Clear REQ signal + SetREQ(OFF); - // Check for timeout waiting for ACK to clear - if (!ret) { - break; - } + // Check for timeout waiting for ACK to clear + if (!ret) { + break; + } - // Advance the data buffer pointer to receive the next byte - buf++; - } + // Advance the data buffer pointer to receive the next byte + buf++; + } - // Wait for ACK to clear - WaitSignal(PIN_ACK, OFF); - } else { - // Get Phase - uint32_t phase = Acquire() & GPIO_MCI; + // Wait for ACK to clear + WaitACK(OFF); + } else { + // Get Phase + Acquire(); + 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); - } + 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); + // Set the DATA signals + SetDAT(*buf); - // Wait for REQ to be asserted - bool ret = WaitSignal(PIN_REQ, ON); + // Wait for REQ to be asserted + bool ret = WaitREQ(ON); - // Check for timeout waiting for REQ to be asserted - if (!ret) { - break; - } + // Check for timeout waiting for REQ to be asserted + if (!ret) { + break; + } - // Phase error - if ((signals & GPIO_MCI) != phase) { - break; - } + // Phase error + Acquire(); + if (GetPhase() != phase) { + break; + } - // Already waiting for REQ assertion + // Already waiting for REQ assertion - // Assert the ACK signal - SetSignal(PIN_ACK, ON); + // Assert the ACK signal + SetACK(ON); - // Wait for REQ to clear - ret = WaitSignal(PIN_REQ, OFF); + // Wait for REQ to clear + ret = WaitREQ(OFF); - // Clear the ACK signal - SetSignal(PIN_ACK, OFF); + // Clear the ACK signal + SetACK(OFF); - // Check for timeout waiting for REQ to clear - if (!ret) { - break; - } + // Check for timeout waiting for REQ to clear + if (!ret) { + break; + } - // Phase error - if ((signals & GPIO_MCI) != phase) { - break; - } + // Phase error + Acquire(); + if (GetPhase() != phase) { + break; + } - // Advance the data buffer pointer to receive the next byte - buf++; - } - } + // Advance the data buffer pointer to receive the next byte + buf++; + } + } - // Re-enable IRQ - EnableIRQ(); + // Re-enable IRQ + EnableIRQ(); - // Return number of transmissions - return i; + // Return number of transmissions + return i; } -#ifdef USE_SEL_EVENT_ENABLE //--------------------------------------------------------------------------- // // SEL signal event polling @@ -1018,19 +388,33 @@ int GPIOBUS::SendHandShake(uint8_t *buf, int count, int delay_after_bytes) //--------------------------------------------------------------------------- bool GPIOBUS::PollSelectEvent() { - errno = 0; +#ifndef USE_SEL_EVENT_ENABLE + 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); + } - if (epoll_event epev; epoll_wait(epfd, &epev, 1, -1) <= 0) { - LOGWARN("%s epoll_wait failed", __PRETTY_FUNCTION__) - return false; - } - - if (gpioevent_data gpev; read(selevreq.fd, &gpev, sizeof(gpev)) < 0) { - LOGWARN("%s read failed", __PRETTY_FUNCTION__) + if (epoll_event epev; epoll_wait(epfd, &epev, 1, -1) <= 0) { + LOGWARN("%s epoll_wait failed", __PRETTY_FUNCTION__) return false; - } + } - return true; + if (gpioevent_data gpev; read(selevreq.fd, &gpev, sizeof(gpev)) < 0) { + LOGWARN("%s read failed", __PRETTY_FUNCTION__) + return false; + } + + if (SBC_Version::IsBananaPi()) { + SetMode(BPI_PIN_SEL, prev_mode); + } + return true; +#endif } //--------------------------------------------------------------------------- @@ -1040,191 +424,7 @@ bool GPIOBUS::PollSelectEvent() //--------------------------------------------------------------------------- void GPIOBUS::ClearSelectEvent() { -} -#endif // USE_SEL_EVENT_ENABLE - -//--------------------------------------------------------------------------- -// -// Signal table -// -//--------------------------------------------------------------------------- -const array GPIOBUS::SignalTable = { - PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, - PIN_DT4, PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP, - PIN_SEL,PIN_ATN, PIN_RST, PIN_ACK, - PIN_BSY, PIN_MSG, PIN_CD, PIN_IO, PIN_REQ, - -1 -}; - -//--------------------------------------------------------------------------- -// -// Create work table -// -//--------------------------------------------------------------------------- -void GPIOBUS::MakeTable(void) -{ - const array pintbl = { - PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, - PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP - }; - - array tblParity; - - // Create parity table - for (uint32_t i = 0; i < 0x100; i++) { - uint32_t bits = i; - uint32_t parity = 0; - for (int j = 0; j < 8; j++) { - parity ^= bits & 1; - bits >>= 1; - } - parity = ~parity; - tblParity[i] = parity & 1; - } - -#if SIGNAL_CONTROL_MODE == 0 - // Mask and setting data generation - for (auto& tbl : tblDatMsk) { - tbl.fill(-1); - } - for (auto& tbl : tblDatSet) { - tbl.fill(0); - } - - for (uint32_t i = 0; i < 0x100; i++) { - // Bit string for inspection - uint32_t bits = i; - - // Get parity - if (tblParity[i]) { - bits |= (1 << 8); - } - - // Bit check - for (int j = 0; j < 9; j++) { - // Index and shift amount calculation - int index = pintbl[j] / 10; - int shift = (pintbl[j] % 10) * 3; - - // Mask data - tblDatMsk[index][i] &= ~(0x7 << shift); - - // Setting data - if (bits & 1) { - tblDatSet[index][i] |= (1 << shift); - } - - bits >>= 1; - } - } -#else - for (uint32_t i = 0; i < 0x100; i++) { - // Bit string for inspection - uint32_t bits = i; - - // Get parity - if (tblParity[i]) { - bits |= (1 << 8); - } - -#if SIGNAL_CONTROL_MODE == 1 - // Negative logic is inverted - bits = ~bits; -#endif - - // Create GPIO register information - uint32_t gpclr = 0; - uint32_t gpset = 0; - for (int j = 0; j < 9; j++) { - if (bits & 1) { - gpset |= (1 << pintbl[j]); - } else { - gpclr |= (1 << pintbl[j]); - } - bits >>= 1; - } - - tblDatMsk[i] = gpclr; - tblDatSet[i] = gpset; - } -#endif -} - -//--------------------------------------------------------------------------- -// -// Control signal setting -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetControl(int pin, bool ast) -{ - PinSetSignal(pin, ast); -} - -//--------------------------------------------------------------------------- -// -// Input/output mode setting -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetMode(int pin, int mode) -{ -#if SIGNAL_CONTROL_MODE == 0 - if (mode == OUT) { - return; - } -#endif // SIGNAL_CONTROL_MODE - - int index = pin / 10; - int shift = (pin % 10) * 3; - uint32_t data = gpfsel[index]; - data &= ~(0x7 << shift); - if (mode == OUT) { - data |= (1 << shift); - } - gpio[index] = data; - gpfsel[index] = data; -} - -//--------------------------------------------------------------------------- -// -// Get input signal value -// -//--------------------------------------------------------------------------- -bool GPIOBUS::GetSignal(int pin) const -{ - return (signals >> pin) & 1; -} - -//--------------------------------------------------------------------------- -// -// Set output signal value -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetSignal(int pin, bool ast) -{ -#if SIGNAL_CONTROL_MODE == 0 - int index = pin / 10; - int shift = (pin % 10) * 3; - uint32_t data = gpfsel[index]; - if (ast) { - data |= (1 << shift); - } else { - data &= ~(0x7 << shift); - } - gpio[index] = data; - gpfsel[index] = data; -#elif SIGNAL_CONTROL_MODE == 1 - if (ast) { - gpio[GPIO_CLR_0] = 0x1 << pin; - } else { - gpio[GPIO_SET_0] = 0x1 << pin; - } -#elif SIGNAL_CONTROL_MODE == 2 - if (ast) { - gpio[GPIO_SET_0] = 0x1 << pin; - } else { - gpio[GPIO_CLR_0] = 0x1 << pin; - } -#endif // SIGNAL_CONTROL_MODE + GPIO_FUNCTION_TRACE } //--------------------------------------------------------------------------- @@ -1232,186 +432,27 @@ void GPIOBUS::SetSignal(int pin, bool ast) // Wait for signal change // //--------------------------------------------------------------------------- -bool GPIOBUS::WaitSignal(int pin, int ast) +bool GPIOBUS::WaitSignal(int pin, bool ast) { - // Get current time - uint32_t now = SysTimer::GetTimerLow(); + // Get current time + uint32_t now = SysTimer::GetTimerLow(); - // Calculate timeout (3000ms) - uint32_t timeout = 3000 * 1000; + // Calculate timeout (3000ms) + uint32_t timeout = 3000 * 1000; - // end immediately if the signal has changed - do { - // Immediately upon receiving a reset - Acquire(); - if (GetRST()) { - return false; - } + do { + // Immediately upon receiving a reset + Acquire(); + if (GetRST()) { + return false; + } - // Check for the signal edge - if (((signals >> pin) ^ ~ast) & 1) { - return true; - } - } while ((SysTimer::GetTimerLow() - now) < timeout); + // Check for the signal edge + if (GetSignal(pin) == ast) { + return true; + } + } while ((SysTimer::GetTimerLow() - now) < timeout); - // We timed out waiting for the signal - return false; -} - -void GPIOBUS::DisableIRQ() -{ -#ifdef __linux__ - if (rpitype == 4) { - // RPI4 is disabled by GICC - giccpmr = gicc[GICC_PMR]; - gicc[GICC_PMR] = 0; - } else if (rpitype == 2) { - // RPI2,3 disable core timer IRQ - tintcore = sched_getcpu() + QA7_CORE0_TINTC; - tintctl = qa7regs[tintcore]; - qa7regs[tintcore] = 0; - } else { - // Stop system timer interrupt with interrupt controller - irptenb = irpctl[IRPT_ENB_IRQ_1]; - irpctl[IRPT_DIS_IRQ_1] = irptenb & 0xf; - } -#else - (void)0; -#endif -} - -void GPIOBUS::EnableIRQ() -{ - if (rpitype == 4) { - // RPI4 enables interrupts via the GICC - gicc[GICC_PMR] = giccpmr; - } else if (rpitype == 2) { - // RPI2,3 re-enable core timer IRQ - qa7regs[tintcore] = tintctl; - } else { - // Restart the system timer interrupt with the interrupt controller - irpctl[IRPT_ENB_IRQ_1] = irptenb & 0xf; - } -} - -//--------------------------------------------------------------------------- -// -// Pin direction setting (input/output) -// -//--------------------------------------------------------------------------- -void GPIOBUS::PinConfig(int pin, int mode) -{ - // Check for invalid pin - if (pin < 0) { - return; - } - - int index = pin / 10; - uint32_t mask = ~(0x7 << ((pin % 10) * 3)); - gpio[index] = (gpio[index] & mask) | ((mode & 0x7) << ((pin % 10) * 3)); -} - -//--------------------------------------------------------------------------- -// -// Pin pull-up/pull-down setting -// -//--------------------------------------------------------------------------- -void GPIOBUS::PullConfig(int pin, int mode) -{ - uint32_t pull; - - // Check for invalid pin - if (pin < 0) { - return; - } - - if (rpitype == 4) { - switch (mode) { - case GPIO_PULLNONE: - pull = 0; - break; - case GPIO_PULLUP: - pull = 1; - break; - case GPIO_PULLDOWN: - pull = 2; - break; - default: - return; - } - - pin &= 0x1f; - int shift = (pin & 0xf) << 1; - uint32_t bits = gpio[GPIO_PUPPDN0 + (pin >> 4)]; - bits &= ~(3 << shift); - bits |= (pull << shift); - gpio[GPIO_PUPPDN0 + (pin >> 4)] = bits; - } else { - pin &= 0x1f; - gpio[GPIO_PUD] = mode & 0x3; - SysTimer::SleepUsec(2); - gpio[GPIO_CLK_0] = 0x1 << pin; - SysTimer::SleepUsec(2); - gpio[GPIO_PUD] = 0; - gpio[GPIO_CLK_0] = 0; - } -} - -//--------------------------------------------------------------------------- -// -// Set output pin -// -//--------------------------------------------------------------------------- -void GPIOBUS::PinSetSignal(int pin, bool ast) -{ - // Check for invalid pin - if (pin < 0) { - return; - } - - if (ast) { - gpio[GPIO_SET_0] = 0x1 << pin; - } else { - gpio[GPIO_CLR_0] = 0x1 << pin; - } -} - -//--------------------------------------------------------------------------- -// -// Set the signal drive strength -// -//--------------------------------------------------------------------------- -void GPIOBUS::DrvConfig(uint32_t drive) -{ - uint32_t data = pads[PAD_0_27]; - pads[PAD_0_27] = (0xFFFFFFF8 & data) | drive | 0x5a000000; -} - - -//--------------------------------------------------------------------------- -// -// Generic Phase Acquisition (Doesn't read GPIO) -// -//--------------------------------------------------------------------------- -BUS::phase_t GPIOBUS::GetPhaseRaw(uint32_t raw_data) -{ - // Selection Phase - if (GetPinRaw(raw_data, PIN_SEL)) { - if(GetPinRaw(raw_data, PIN_IO)) { - return BUS::phase_t::reselection; - } else{ - return BUS::phase_t::selection; - } - } - - // Bus busy phase - if (!GetPinRaw(raw_data, PIN_BSY)) { - return BUS::phase_t::busfree; - } - - // Get target phase from bus signal line - int mci = GetPinRaw(raw_data, PIN_MSG) ? 0x04 : 0x00; - mci |= GetPinRaw(raw_data, PIN_CD) ? 0x02 : 0x00; - mci |= GetPinRaw(raw_data, PIN_IO) ? 0x01 : 0x00; - return GetPhase(mci); -} + // We timed out waiting for the signal + return false; +} \ No newline at end of file diff --git a/cpp/hal/gpiobus.h b/cpp/hal/gpiobus.h index 037bc098..cb8086c1 100644 --- a/cpp/hal/gpiobus.h +++ b/cpp/hal/gpiobus.h @@ -11,10 +11,11 @@ #pragma once -#include "shared/config.h" +#include "hal/bus.h" #include "shared/scsi.h" -#include "bus.h" #include +#include +#include #ifdef __linux__ #include @@ -31,18 +32,25 @@ //#define CONNECT_TYPE_GAMERNIUM // GAMERnium.com version (standard logic, unique pin assignment) #if defined CONNECT_TYPE_STANDARD -#include "hal/gpiobus_standard.h" +#include "hal/connection_type/connection_standard.h" #elif defined CONNECT_TYPE_FULLSPEC -#include "hal/gpiobus_fullspec.h" +#include "hal/connection_type/connection_fullspec.h" #elif defined CONNECT_TYPE_AIBOM -#include "hal/gpiobus_aibom.h" +#include "hal/connection_type/connection_aibom.h" #elif defined CONNECT_TYPE_GAMERNIUM -#include "hal/gpiobus_gamernium.h" +#include "hal/connection_type/connection_gamernium.h" #else #error Invalid connection type or none specified #endif -using namespace std; //NOSONAR Not relevant for rascsi +// #define ENABLE_GPIO_TRACE +#ifdef ENABLE_GPIO_TRACE +#define GPIO_FUNCTION_TRACE LOGTRACE("%s", __PRETTY_FUNCTION__) +#else +#define GPIO_FUNCTION_TRACE +#endif + +using namespace std; //--------------------------------------------------------------------------- // @@ -118,397 +126,95 @@ using namespace std; //NOSONAR Not relevant for rascsi // //--------------------------------------------------------------------------- -#define ALL_SCSI_PINS \ - ((1< &) override; + // Data receive handshake + int ReceiveHandShake(uint8_t *buf, int count) override; + // Data transmission handshake + int SendHandShake(uint8_t *buf, int count, int delay_after_bytes) override; - #if SIGNAL_CONTROL_MODE < 2 - // Invert if negative logic (internal processing is unified to positive logic) - signals = ~signals; - #endif // SIGNAL_CONTROL_MODE + // SEL signal event polling + bool PollSelectEvent() override; + // Clear SEL signal event + void ClearSelectEvent() override; - return signals; - #endif // ifdef __x86_64__ || __X86__ - } + protected: + virtual void MakeTable() = 0; - void SetENB(bool ast); - // Set ENB signal + bool GetSignal(int pin) const override = 0; + void SetSignal(int pin, bool ast) override = 0; + bool WaitSignal(int pin, bool ast); - bool GetBSY() const override; - // Get BSY signal - void SetBSY(bool ast) override; - // Set BSY signal + // Wait for a signal to change + virtual bool WaitREQ(bool ast) = 0; + virtual bool WaitACK(bool ast) = 0; - bool GetSEL() const override; - // Get SEL signal - void SetSEL(bool ast) override; - // Set SEL signal + // Interrupt control + virtual void EnableIRQ() = 0; + virtual void DisableIRQ() = 0; - bool GetATN() const override; - // Get ATN signal - void SetATN(bool ast) override; - // Set ATN signal + // Set GPIO output signal + virtual void PinSetSignal(int pin, bool ast) = 0; + // Set GPIO drive strength + virtual void DrvConfig(uint32_t drive) = 0; - bool GetACK() const override; - // Get ACK signal - void SetACK(bool ast) override; - // Set ACK signal + // Operation mode + mode_e actmode = mode_e::TARGET; // NOSONAR: This protected so derived classes can access it - bool GetACT() const; - // Get ACT signal - void SetACT(bool ast); - // Set ACT signal +#ifdef __linux__ + // SEL signal event request + struct gpioevent_request selevreq = {}; // NOSONAR: This protected so derived classes can access it + // epoll file descriptor + int epfd; // NOSONAR: This protected so derived classes can access it - 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 - - uint8_t GetDAT() override; - // Get DAT signal - void SetDAT(uint8_t dat) override; - // Set DAT signal - bool GetDP() const override; - // Get Data parity signal - int CommandHandShake(vector&) override; - // Command receive handshake - int ReceiveHandShake(uint8_t *buf, int count) override; - // Data receive handshake - int SendHandShake(uint8_t *buf, int count, int delay_after_bytes) override; - // Data transmission handshake - - static BUS::phase_t GetPhaseRaw(uint32_t raw_data); - // Get the phase based on raw data - -#ifdef USE_SEL_EVENT_ENABLE - // SEL signal interrupt - bool PollSelectEvent() override; - // SEL signal event polling - void ClearSelectEvent() override; - // Clear SEL signal event #endif - -private: - // SCSI I/O signal control - void MakeTable(); - // Create work data - void SetControl(int pin, bool ast); - // Set Control Signal - void SetMode(int pin, int mode); - // Set SCSI I/O mode - bool GetSignal(int pin) const override; - // Get SCSI input signal value - void SetSignal(int pin, bool ast) override; - // Set SCSI output signal value - bool WaitSignal(int pin, int ast); - // Wait for a signal to change - // Interrupt control - void DisableIRQ(); - // IRQ Disabled - void EnableIRQ(); - // IRQ Enabled - - // GPIO pin functionality settings - void PinConfig(int pin, int mode); - // GPIO pin direction setting - void PullConfig(int pin, int mode); - // GPIO pin pull up/down resistor setting - void PinSetSignal(int pin, bool ast); - // Set GPIO output signal - void DrvConfig(uint32_t drive); - // Set GPIO drive strength - - - mode_e actmode = mode_e::TARGET; // Operation mode - -#if !defined(__x86_64__) && !defined(__X86__) - uint32_t baseaddr = 0; // Base address -#endif - - int rpitype = 0; // Type of Raspberry Pi - - volatile uint32_t *gpio = nullptr; // GPIO register - - volatile uint32_t *pads = nullptr; // PADS register - -#if !defined(__x86_64__) && !defined(__X86__) - volatile uint32_t *level = nullptr; // GPIO input level -#endif - - volatile uint32_t *irpctl = nullptr; // Interrupt control register - - volatile uint32_t irptenb; // Interrupt enabled state - - volatile uint32_t *qa7regs = nullptr; // QA7 register - - volatile int tintcore; // Interupt control target CPU. - - volatile uint32_t tintctl; // Interupt control - - volatile uint32_t giccpmr; // GICC priority setting - -#if !defined(__x86_64__) && !defined(__X86__) - volatile uint32_t *gicd = nullptr; // GIC Interrupt distributor register -#endif - - volatile uint32_t *gicc = nullptr; // GIC CPU interface register - - array gpfsel; // GPFSEL0-4 backup values - - uint32_t signals = 0; // All bus signals - -#ifdef USE_SEL_EVENT_ENABLE - struct gpioevent_request selevreq = {}; // SEL signal event request - - int epfd; // epoll file descriptor -#endif // USE_SEL_EVENT_ENABLE - -#if SIGNAL_CONTROL_MODE == 0 - array, 3> tblDatMsk; // Data mask table - - array, 3> tblDatSet; // Data setting table -#else - array tblDatMsk = {}; // Data mask table - - array tblDatSet = {}; // Table setting table -#endif - - static const array SignalTable; // signal table }; - diff --git a/cpp/hal/gpiobus_aibom.h b/cpp/hal/gpiobus_aibom.h deleted file mode 100644 index 01f1f9e6..00000000 --- a/cpp/hal/gpiobus_aibom.h +++ /dev/null @@ -1,56 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI Target Emulator RaSCSI Reloaded -// for Raspberry Pi -// -// Powered by XM6 TypeG Technology. -// Copyright (C) 2016-2020 GIMONS -// -//--------------------------------------------------------------------------- - -#pragma once - -#include - -// -// RaSCSI Adapter Aibom version -// - -const std::string CONNECT_DESC = "AIBOM PRODUCTS version"; // Startup message - -// Select signal control mode -const static int SIGNAL_CONTROL_MODE = 2; // SCSI positive logic specification - -// Control signal output logic -#define ACT_ON ON // ACTIVE SIGNAL ON -#define ENB_ON ON // ENABLE SIGNAL ON -#define IND_IN OFF // INITIATOR SIGNAL INPUT -#define TAD_IN OFF // TARGET SIGNAL INPUT -#define DTD_IN OFF // DATA SIGNAL INPUT - -// Control signal pin assignment (-1 means no control) -const static int PIN_ACT = 4; // ACTIVE -const static int PIN_ENB = 17; // ENABLE -const static int PIN_IND = 27; // INITIATOR CTRL DIRECTION -const static int PIN_TAD = -1; // TARGET CTRL DIRECTION -const static int PIN_DTD = 18; // DATA DIRECTION - -// SCSI signal pin assignment -const static int PIN_DT0 = 6; // Data 0 -const static int PIN_DT1 = 12; // Data 1 -const static int PIN_DT2 = 13; // Data 2 -const static int PIN_DT3 = 16; // Data 3 -const static int PIN_DT4 = 19; // Data 4 -const static int PIN_DT5 = 20; // Data 5 -const static int PIN_DT6 = 26; // Data 6 -const static int PIN_DT7 = 21; // Data 7 -const static int PIN_DP = 5; // Data parity -const static int PIN_ATN = 22; // ATN -const static int PIN_RST = 25; // RST -const static int PIN_ACK = 10; // ACK -const static int PIN_REQ = 7; // REQ -const static int PIN_MSG = 9; // MSG -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 diff --git a/cpp/hal/gpiobus_bananam2p.cpp b/cpp/hal/gpiobus_bananam2p.cpp new file mode 100644 index 00000000..352ae657 --- /dev/null +++ b/cpp/hal/gpiobus_bananam2p.cpp @@ -0,0 +1,1105 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi (And Banana Pi) +// +// Copyright (c) 2012-2015 Ben Croston +// Copyright (C) 2022 akuker +// +// 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 +#include +#include +#include +#include +#include +#include + +#include "hal/gpiobus.h" +#include "hal/gpiobus_bananam2p.h" +#include "hal/pi_defs/bpi-gpio.h" +#include "hal/sunxi_utils.h" +#include "hal/systimer.h" +#include "shared/log.h" + +#define ARRAY_SIZE(x) (sizeof(x) / (sizeof(x[0]))) + +bool GPIOBUS_BananaM2p::Init(mode_e mode) +{ + GPIO_FUNCTION_TRACE + GPIOBUS::Init(mode); + SysTimer::Init(); + + sbc_version = SBC_Version::GetSbcVersion(); + + for (auto const gpio_num : SignalTable) { + if (gpio_num == -1) { + break; + } + + int gpio_bank = SunXI::GPIO_BANK(gpio_num); + + if (std::find(gpio_banks.begin(), gpio_banks.end(), gpio_bank) != gpio_banks.end()) { + LOGTRACE("Duplicate bank: %d", gpio_bank) + + } else { + LOGDEBUG("New bank: %d", gpio_bank) + gpio_banks.push_back(gpio_bank); + } + } + + if (int result = sunxi_setup(); result != SETUP_OK) { + return false; + } + + InitializeGpio(); + + // SetupSelEvent needs to be called AFTER Initialize GPIO. This function + // reconfigures the SEL signal. + if (!SetupSelEvent()) { + LOGERROR("Failed to setup SELECT poll event") + return false; + } + LOGTRACE("SetupSelEvent OK!") + + // Set drive strength to maximum + DrvConfig(3); + + return true; +} + +void GPIOBUS_BananaM2p::InitializeGpio() +{ + GPIO_FUNCTION_TRACE + + // Set pull up/pull down +#if SIGNAL_CONTROL_MODE == 0 + int pullmode = GPIO_PULLNONE; +#elif SIGNAL_CONTROL_MODE == 1 + int pullmode = GPIO_PULLUP; +#else + int pullmode = GPIO_PULLDOWN; +#endif + + // Initialize all signals + for (int i = 0; SignalTable[i] >= 0; i++) { + int j = SignalTable[i]; + PinConfig(j, GPIO_INPUT); + PullConfig(j, pullmode); + PinSetSignal(j, OFF); + } + + // Set control signals + PinConfig(BPI_PIN_ACT, GPIO_OUTPUT); + PinConfig(BPI_PIN_TAD, GPIO_OUTPUT); + PinConfig(BPI_PIN_IND, GPIO_OUTPUT); + PinConfig(BPI_PIN_DTD, GPIO_OUTPUT); + PinSetSignal(BPI_PIN_ACT, OFF); + PinSetSignal(BPI_PIN_TAD, OFF); + PinSetSignal(BPI_PIN_IND, OFF); + PinSetSignal(BPI_PIN_DTD, OFF); + + // Set the ENABLE signal + // This is used to show that the application is running + PinConfig(BPI_PIN_ENB, GPIO_OUTPUT); + PinSetSignal(BPI_PIN_ENB, ON); +} + +void GPIOBUS_BananaM2p::Cleanup() +{ + GPIO_FUNCTION_TRACE +#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__) + dummy_var--; // Need to do something to prevent Sonar from claiming this should be a const function + return; +#else + +#ifdef USE_SEL_EVENT_ENABLE + // Release SEL signal interrupt + close(selevreq.fd); +#endif // USE_SEL_EVENT_ENABLE + + // Set control signals + PinConfig(BPI_PIN_ACT, GPIO_INPUT); + PinConfig(BPI_PIN_TAD, GPIO_INPUT); + PinConfig(BPI_PIN_IND, GPIO_INPUT); + PinConfig(BPI_PIN_DTD, GPIO_INPUT); + PinSetSignal(BPI_PIN_ENB, OFF); + PinSetSignal(BPI_PIN_ACT, OFF); + PinSetSignal(BPI_PIN_TAD, OFF); + PinSetSignal(BPI_PIN_IND, OFF); + PinSetSignal(BPI_PIN_DTD, OFF); + + // Initialize all signals + for (int i = 0; SignalTable[i] >= 0; i++) { + int pin = SignalTable[i]; + PinConfig(pin, GPIO_INPUT); + PullConfig(pin, GPIO_PULLNONE); + PinSetSignal(pin, OFF); + } + + // Set drive strength back to Default (Level 1) + DrvConfig(1); + + munmap((void *)gpio_map, SunXI::BLOCK_SIZE); + munmap((void *)r_gpio_map, SunXI::BLOCK_SIZE); + +#endif +} + +void GPIOBUS_BananaM2p::Reset() +{ +#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__) + dummy_var++; + return; +#else + int i; + int j; + + // Turn off active signal + SetControl(BPI_PIN_ACT, ACT_OFF); + + // Set all signals to off + for (i = 0;; i++) { + j = SignalTable[i]; + if (j < 0) { + break; + } + + SetSignal(j, OFF); + } + + if (actmode == mode_e::TARGET) { + // Target mode + + // Set target signal to input + SetControl(BPI_PIN_TAD, TAD_IN); + SetMode(BPI_PIN_BSY, IN); + SetMode(BPI_PIN_MSG, IN); + SetMode(BPI_PIN_CD, IN); + SetMode(BPI_PIN_REQ, IN); + SetMode(BPI_PIN_IO, IN); + + // Set the initiator signal to input + SetControl(BPI_PIN_IND, IND_IN); + SetMode(BPI_PIN_SEL, IN); + SetMode(BPI_PIN_ATN, IN); + SetMode(BPI_PIN_ACK, IN); + SetMode(BPI_PIN_RST, IN); + + // Set data bus signals to input + SetControl(BPI_PIN_DTD, DTD_IN); + SetMode(BPI_PIN_DT0, IN); + SetMode(BPI_PIN_DT1, IN); + SetMode(BPI_PIN_DT2, IN); + SetMode(BPI_PIN_DT3, IN); + SetMode(BPI_PIN_DT4, IN); + SetMode(BPI_PIN_DT5, IN); + SetMode(BPI_PIN_DT6, IN); + SetMode(BPI_PIN_DT7, IN); + SetMode(BPI_PIN_DP, IN); + } else { + // Initiator mode + + // Set target signal to input + SetControl(BPI_PIN_TAD, TAD_IN); + SetMode(BPI_PIN_BSY, IN); + SetMode(BPI_PIN_MSG, IN); + SetMode(BPI_PIN_CD, IN); + SetMode(BPI_PIN_REQ, IN); + SetMode(BPI_PIN_IO, IN); + + // Set the initiator signal to output + SetControl(BPI_PIN_IND, IND_OUT); + SetMode(BPI_PIN_SEL, OUT); + SetMode(BPI_PIN_ATN, OUT); + SetMode(BPI_PIN_ACK, OUT); + SetMode(BPI_PIN_RST, OUT); + + // Set the data bus signals to output + SetControl(BPI_PIN_DTD, DTD_OUT); + SetMode(BPI_PIN_DT0, OUT); + SetMode(BPI_PIN_DT1, OUT); + SetMode(BPI_PIN_DT2, OUT); + SetMode(BPI_PIN_DT3, OUT); + SetMode(BPI_PIN_DT4, OUT); + SetMode(BPI_PIN_DT5, OUT); + SetMode(BPI_PIN_DT6, OUT); + SetMode(BPI_PIN_DT7, OUT); + SetMode(BPI_PIN_DP, OUT); + } + + // Initialize all signals + // TODO!! For now, just re-run Acquire + Acquire(); +#endif // ifdef __x86_64__ || __X86__ +} + +bool GPIOBUS_BananaM2p::SetupSelEvent() +{ +#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__) + dummy_var += 2; // Need to do something to prevent Sonar from claiming this should be a const function + return false; +#else + GPIO_FUNCTION_TRACE + int gpio_pin = BPI_PIN_SEL; + + // GPIO chip open + LOGTRACE("%s GPIO chip open [%d]", __PRETTY_FUNCTION__, gpio_pin) + std::string gpio_dev = "/dev/gpiochip0"; + if (SunXI::GPIO_BANK(gpio_pin) >= 11) { + gpio_dev = "/dev/gpiochip1"; + LOGWARN("gpiochip1 support isn't implemented yet....") + LOGWARN("THIS PROBABLY WONT WORK!") + } + + int gpio_fd = open(gpio_dev.c_str(), 0); + if (gpio_fd == -1) { + LOGERROR("Unable to open /dev/gpiochip0. Is RaSCSI already running?") + return false; + } + + // Event request setting + LOGTRACE("%s Event request setting (pin sel: %d)", __PRETTY_FUNCTION__, gpio_pin) + strncpy(selevreq.consumer_label, "RaSCSI", ARRAY_SIZE(selevreq.consumer_label)); + selevreq.lineoffset = gpio_pin; + selevreq.handleflags = GPIOHANDLE_REQUEST_INPUT; +#if SIGNAL_CONTROL_MODE < 2 + selevreq.eventflags = GPIOEVENT_REQUEST_FALLING_EDGE; + LOGTRACE("%s eventflags = GPIOEVENT_REQUEST_FALLING_EDGE", __PRETTY_FUNCTION__) +#else + selevreq.eventflags = GPIOEVENT_REQUEST_RISING_EDGE; + LOGTRACE("%s eventflags = GPIOEVENT_REQUEST_RISING_EDGE", __PRETTY_FUNCTION__) +#endif // SIGNAL_CONTROL_MODE + + // Get event request + if (ioctl(gpio_fd, GPIO_GET_LINEEVENT_IOCTL, &selevreq) == -1) { + LOGERROR("selevreq.fd = %d %08X", selevreq.fd, (unsigned int)selevreq.fd) + LOGERROR("Unable to register event request. Is RaSCSI already running?") + LOGERROR("[%08X] %s", errno, strerror(errno)) + close(gpio_fd); + return false; + } + + // Close GPIO chip file handle + LOGTRACE("%s Close GPIO chip file handle", __PRETTY_FUNCTION__) + close(gpio_fd); + + // epoll initialization + LOGTRACE("%s epoll initialization", __PRETTY_FUNCTION__) + epfd = epoll_create(1); + if (epfd == -1) { + LOGERROR("Unable to create the epoll event") + return false; + } + epoll_event ev = {}; + memset(&ev, 0, sizeof(ev)); + ev.events = EPOLLIN | EPOLLPRI; + ev.data.fd = selevreq.fd; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, selevreq.fd, &ev) < 0) { + return false; + } + + return true; +#endif +} + +void GPIOBUS_BananaM2p::SetENB(bool ast) +{ + PinSetSignal(BPI_PIN_ENB, ast ? ENB_ON : ENB_OFF); +} + +bool GPIOBUS_BananaM2p::GetBSY() const +{ + return GetSignal(BPI_PIN_BSY); +} + +void GPIOBUS_BananaM2p::SetBSY(bool ast) +{ + if (actmode == mode_e::TARGET) { + if (ast) { + // Turn on ACTIVE signal + SetControl(BPI_PIN_ACT, ACT_ON); + + // Set Target signal to output + SetControl(BPI_PIN_TAD, TAD_OUT); + + SetMode(BPI_PIN_BSY, OUT); + SetMode(BPI_PIN_MSG, OUT); + SetMode(BPI_PIN_CD, OUT); + SetMode(BPI_PIN_REQ, OUT); + SetMode(BPI_PIN_IO, OUT); + + // Set BSY signal + SetSignal(BPI_PIN_BSY, ast); + + } else { + // Turn off the ACTIVE signal + SetControl(BPI_PIN_ACT, ACT_OFF); + + // Set the target signal to input + SetControl(BPI_PIN_TAD, TAD_IN); + + SetMode(BPI_PIN_BSY, IN); + SetMode(BPI_PIN_MSG, IN); + SetMode(BPI_PIN_CD, IN); + SetMode(BPI_PIN_REQ, IN); + SetMode(BPI_PIN_IO, IN); + } + } else { + // Set BSY signal + SetSignal(BPI_PIN_BSY, ast); + } +} + +bool GPIOBUS_BananaM2p::GetSEL() const +{ + return GetSignal(BPI_PIN_SEL); +} + +void GPIOBUS_BananaM2p::SetSEL(bool ast) +{ + if (actmode == mode_e::INITIATOR && ast) { + // Turn on ACTIVE signal + SetControl(BPI_PIN_ACT, ACT_ON); + } + + // Set SEL signal + SetSignal(BPI_PIN_SEL, ast); +} + +bool GPIOBUS_BananaM2p::GetATN() const +{ + return GetSignal(BPI_PIN_ATN); +} + +void GPIOBUS_BananaM2p::SetATN(bool ast) +{ + SetSignal(BPI_PIN_ATN, ast); +} + +bool GPIOBUS_BananaM2p::GetACK() const +{ + return GetSignal(BPI_PIN_ACK); +} + +void GPIOBUS_BananaM2p::SetACK(bool ast) +{ + SetSignal(BPI_PIN_ACK, ast); +} + +bool GPIOBUS_BananaM2p::GetACT() const +{ + return GetSignal(BPI_PIN_ACT); +} + +void GPIOBUS_BananaM2p::SetACT(bool ast) +{ + SetSignal(BPI_PIN_ACT, ast); +} + +bool GPIOBUS_BananaM2p::GetRST() const +{ + return GetSignal(BPI_PIN_RST); +} + +void GPIOBUS_BananaM2p::SetRST(bool ast) +{ + SetSignal(BPI_PIN_RST, ast); +} + +bool GPIOBUS_BananaM2p::GetMSG() const +{ + return GetSignal(BPI_PIN_MSG); +} + +void GPIOBUS_BananaM2p::SetMSG(bool ast) +{ + SetSignal(BPI_PIN_MSG, ast); +} + +bool GPIOBUS_BananaM2p::GetCD() const +{ + return GetSignal(BPI_PIN_CD); +} + +void GPIOBUS_BananaM2p::SetCD(bool ast) +{ + SetSignal(BPI_PIN_CD, ast); +} + +bool GPIOBUS_BananaM2p::GetIO() +{ + bool ast = GetSignal(BPI_PIN_IO); + + if (actmode == mode_e::INITIATOR) { + // Change the data input/output direction by IO signal + if (ast) { + SetControl(BPI_PIN_DTD, DTD_IN); + SetMode(BPI_PIN_DT0, IN); + SetMode(BPI_PIN_DT1, IN); + SetMode(BPI_PIN_DT2, IN); + SetMode(BPI_PIN_DT3, IN); + SetMode(BPI_PIN_DT4, IN); + SetMode(BPI_PIN_DT5, IN); + SetMode(BPI_PIN_DT6, IN); + SetMode(BPI_PIN_DT7, IN); + SetMode(BPI_PIN_DP, IN); + } else { + SetControl(BPI_PIN_DTD, DTD_OUT); + SetMode(BPI_PIN_DT0, OUT); + SetMode(BPI_PIN_DT1, OUT); + SetMode(BPI_PIN_DT2, OUT); + SetMode(BPI_PIN_DT3, OUT); + SetMode(BPI_PIN_DT4, OUT); + SetMode(BPI_PIN_DT5, OUT); + SetMode(BPI_PIN_DT6, OUT); + SetMode(BPI_PIN_DT7, OUT); + SetMode(BPI_PIN_DP, OUT); + } + } + + return ast; +} + +void GPIOBUS_BananaM2p::SetIO(bool ast) +{ + if (actmode == mode_e::TARGET) { + // Change the data input/output direction by IO signal + if (ast) { + SetControl(BPI_PIN_DTD, DTD_OUT); + SetMode(BPI_PIN_DT0, OUT); + SetMode(BPI_PIN_DT1, OUT); + SetMode(BPI_PIN_DT2, OUT); + SetMode(BPI_PIN_DT3, OUT); + SetMode(BPI_PIN_DT4, OUT); + SetMode(BPI_PIN_DT5, OUT); + SetMode(BPI_PIN_DT6, OUT); + SetMode(BPI_PIN_DT7, OUT); + SetMode(BPI_PIN_DP, OUT); + + SetDAT(0); + SetSignal(BPI_PIN_IO, ast); + + } else { + SetControl(BPI_PIN_DTD, DTD_IN); + SetMode(BPI_PIN_DT0, IN); + SetMode(BPI_PIN_DT1, IN); + SetMode(BPI_PIN_DT2, IN); + SetMode(BPI_PIN_DT3, IN); + SetMode(BPI_PIN_DT4, IN); + SetMode(BPI_PIN_DT5, IN); + SetMode(BPI_PIN_DT6, IN); + SetMode(BPI_PIN_DT7, IN); + SetMode(BPI_PIN_DP, IN); + } + } else { + SetSignal(BPI_PIN_IO, ast); + } +} + +bool GPIOBUS_BananaM2p::GetREQ() const +{ + return GetSignal(BPI_PIN_REQ); +} + +void GPIOBUS_BananaM2p::SetREQ(bool ast) +{ + SetSignal(BPI_PIN_REQ, ast); +} + +uint8_t GPIOBUS_BananaM2p::GetDAT() +{ + GPIO_FUNCTION_TRACE + // TODO: This is inefficient, but it works... + uint32_t data = + ((GetSignal(BPI_PIN_DT0) ? 0x01 : 0x00) << 0) | ((GetSignal(BPI_PIN_DT1) ? 0x01 : 0x00) << 1) | + ((GetSignal(BPI_PIN_DT2) ? 0x01 : 0x00) << 2) | ((GetSignal(BPI_PIN_DT3) ? 0x01 : 0x00) << 3) | + ((GetSignal(BPI_PIN_DT4) ? 0x01 : 0x00) << 4) | ((GetSignal(BPI_PIN_DT5) ? 0x01 : 0x00) << 5) | + ((GetSignal(BPI_PIN_DT6) ? 0x01 : 0x00) << 6) | + ((GetSignal(BPI_PIN_DT7) ? 0x01 : 0x00) << 7); // NOSONAR: GCC 10 doesn't support shift operations on std::byte + + return (uint8_t)(data & 0xFF); +} + +void GPIOBUS_BananaM2p::SetDAT(uint8_t dat) +{ + GPIO_FUNCTION_TRACE + + SetMode(BPI_PIN_DT0, OUT); + SetMode(BPI_PIN_DT1, OUT); + SetMode(BPI_PIN_DT2, OUT); + SetMode(BPI_PIN_DT3, OUT); + SetMode(BPI_PIN_DT4, OUT); + SetMode(BPI_PIN_DT5, OUT); + SetMode(BPI_PIN_DT6, OUT); + SetMode(BPI_PIN_DT7, OUT); + SetMode(BPI_PIN_DP, OUT); + + // TODO: This is inefficient, but it works... + PinSetSignal(BPI_PIN_DT0, (dat & (1 << 0)) == 0); // NOSONAR: GCC 10 doesn't support shift operations on std::byte + PinSetSignal(BPI_PIN_DT1, (dat & (1 << 1)) == 0); // NOSONAR: GCC 10 doesn't support shift operations on std::byte + PinSetSignal(BPI_PIN_DT2, (dat & (1 << 2)) == 0); // NOSONAR: GCC 10 doesn't support shift operations on std::byte + PinSetSignal(BPI_PIN_DT3, (dat & (1 << 3)) == 0); // NOSONAR: GCC 10 doesn't support shift operations on std::byte + PinSetSignal(BPI_PIN_DT4, (dat & (1 << 4)) == 0); // NOSONAR: GCC 10 doesn't support shift operations on std::byte + PinSetSignal(BPI_PIN_DT5, (dat & (1 << 5)) == 0); // NOSONAR: GCC 10 doesn't support shift operations on std::byte + PinSetSignal(BPI_PIN_DT6, (dat & (1 << 6)) == 0); // NOSONAR: GCC 10 doesn't support shift operations on std::byte + PinSetSignal(BPI_PIN_DT7, (dat & (1 << 7)) == 0); // NOSONAR: GCC 10 doesn't support shift operations on std::byte + + PinSetSignal(BPI_PIN_DP, __builtin_parity(dat) == 1); +} + +//--------------------------------------------------------------------------- +// +// Signal table +// +//--------------------------------------------------------------------------- +const array GPIOBUS_BananaM2p::SignalTable = {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, BPI_PIN_SEL, + BPI_PIN_ATN, BPI_PIN_RST, BPI_PIN_ACK, BPI_PIN_BSY, BPI_PIN_MSG, + BPI_PIN_CD, BPI_PIN_IO, BPI_PIN_REQ, -1}; + +//--------------------------------------------------------------------------- +// +// Create work table +// +//--------------------------------------------------------------------------- +void GPIOBUS_BananaM2p::MakeTable(void) +{ + const array 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}; + + array tblParity; + + // Create parity table + for (uint32_t i = 0; i < 0x100; i++) { + uint32_t bits = i; + uint32_t parity = 0; + for (int j = 0; j < 8; j++) { + parity ^= bits & 1; + bits >>= 1; + } + parity = ~parity; + tblParity[i] = parity & 1; + } + +#if SIGNAL_CONTROL_MODE == 0 + // Mask and setting data generation + for (auto &tbl : tblDatMsk) { + tbl.fill(-1); + } + for (auto &tbl : tblDatSet) { + tbl.fill(0); + } + + for (uint32_t i = 0; i < 0x100; i++) { + // Bit string for inspection + uint32_t bits = i; + + // Get parity + if (tblParity[i]) { + bits |= (1 << 8); + } + + // Bit check + for (int j = 0; j < 9; j++) { + // Index and shift amount calculation + int index = pintbl[j] / 10; + int shift = (pintbl[j] % 10) * 3; + + // Mask data + tblDatMsk[index][i] &= ~(0x7 << shift); + + // Setting data + if (bits & 1) { + tblDatSet[index][i] |= (1 << shift); + } + + bits >>= 1; + } + } +#else + for (uint32_t i = 0; i < 0x100; i++) { + // Bit string for inspection + uint32_t bits = i; + + // Get parity + if (tblParity[i]) { + bits |= (1 << 8); + } + +#if SIGNAL_CONTROL_MODE == 1 + // Negative logic is inverted + bits = ~bits; +#endif + + // Create GPIO register information + uint32_t gpclr = 0; + uint32_t gpset = 0; + for (int j = 0; j < 9; j++) { + if (bits & 1) { + gpset |= (1 << pintbl[j]); + } else { + gpclr |= (1 << pintbl[j]); + } + bits >>= 1; + } + + tblDatMsk[i] = gpclr; + tblDatSet[i] = gpset; + } +#endif +} + +bool GPIOBUS_BananaM2p::GetDP() const +{ + return GetSignal(BPI_PIN_DP); +} + +void GPIOBUS_BananaM2p::SetControl(int pin, bool ast) +{ + GPIO_FUNCTION_TRACE + PinSetSignal(pin, ast); +} + +// Set direction +int GPIOBUS_BananaM2p::GetMode(int pin) +{ + GPIO_FUNCTION_TRACE + + uint32_t regval = 0; + int bank = SunXI::GPIO_BANK(pin); + int index = SunXI::GPIO_CFG_INDEX(pin); + int offset = SunXI::GPIO_CFG_OFFSET(pin); + + volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]); + /* DK, for PL and PM */ + if (bank >= 11) { + bank -= 11; + pio = &(r_pio_map->gpio_bank[bank]); + } + + regval = pio->CFG[0 + index]; + + // Extract the CFG field + regval &= (0x7 << offset); // 0xf? + // Shift it down to the LSB + regval >>= offset; + return (int)regval; +} + +// Set direction +void GPIOBUS_BananaM2p::SetMode(int pin, int mode) +{ + GPIO_FUNCTION_TRACE + int direction = mode; + + uint32_t regval = 0; + int bank = SunXI::GPIO_BANK(pin); // gpio >> 5 + int index = SunXI::GPIO_CFG_INDEX(pin); // (gpio & 0x1F) >> 3 + int offset = SunXI::GPIO_CFG_OFFSET(pin); // ((gpio & 0x1F) & 0x7) << 2 + LOGTRACE("%s gpio(%d) bank(%d) index(%d) offset(%d) dir(%s)", __PRETTY_FUNCTION__, pin, bank, index, offset, + (GPIO_INPUT == direction) ? "IN" + : (GPIO_IRQ_IN == direction) ? "IRQ" + : "OUT") + + volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]); + /* DK, for PL and PM */ + if (bank >= 11) { + bank -= 11; + pio = &(r_pio_map->gpio_bank[bank]); + } + + regval = pio->CFG[0 + index]; + + // Clear the cfg field + regval &= ~(0x7 << offset); // 0xf? + if (GPIO_INPUT == direction) { + regval |= (((uint32_t)SunXI::gpio_configure_values_e::gpio_input) << offset); + } else if (GPIO_OUTPUT == direction) { + regval |= (((uint32_t)SunXI::gpio_configure_values_e::gpio_output) << offset); + } else if (GPIO_IRQ_IN == direction) { + regval |= (((uint32_t)SunXI::gpio_configure_values_e::gpio_interupt) << offset); + } else { + LOGERROR("line:%d gpio number error %d", __LINE__, pin) + } + pio->CFG[0 + index] = regval; +} + +bool GPIOBUS_BananaM2p::GetSignal(int pin) const +{ + GPIO_FUNCTION_TRACE + int gpio_num = pin; + + uint32_t regval = 0; + int bank = SunXI::GPIO_BANK(gpio_num); // gpio >> 5 + int num = SunXI::GPIO_NUM(gpio_num); // gpio & 0x1F + + regval = (signals[bank] >> num) & 0x1; + return regval != 0; +} + +void GPIOBUS_BananaM2p::SetSignal(int pin, bool ast) +{ + GPIO_FUNCTION_TRACE + int gpio_num = pin; + +#if SIGNAL_CONTROL_MODE == 0 + // True : 0V + // False : Open collector output (disconnect from bus) + int sunxi_gpio_state = (ast == true) ? SunXI::HIGH : SunXI::LOW; +#elif SIGNAL_CONTROL_MODE == 1 + // True : 0V -> (CONVERT) -> 0V + // False : 3.3V -> (CONVERT) -> Open collector output + LOGWARN("%s:%d THIS LOGIC NEEDS TO BE VALIDATED/TESTED", __PRETTY_FUNCTION__, __LINE__) + int sunxi_gpio_state = (ast == true) ? SunXI::HIGH : SunXI::LOW; +#elif SIGNAL_CONTROL_MODE == 2 + // True : 3.3V -> (CONVERT) -> 0V + // False : 0V -> (CONVERT) -> Open collector output + LOGWARN("%s:%d THIS LOGIC NEEDS TO BE VALIDATED/TESTED", __PRETTY_FUNCTION__, __LINE__) + int sunxi_gpio_state = (ast == true) ? SunXI::LOW : SunXI::HIGH; +#endif // SIGNAL_CONTROL_MODE + + LOGTRACE("gpio(%d) sunxi_state(%d)", gpio_num, sunxi_gpio_state) + + int bank = SunXI::GPIO_BANK(gpio_num); // gpio >> 5 + int num = SunXI::GPIO_NUM(gpio_num); // gpio & 0x1F + + volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]); + + /* DK, for PL and PM */ + if (bank >= 11) { + LOGTRACE("bank > 11") + bank -= 11; + pio = &(r_pio_map->gpio_bank[bank]); + } + + if (sunxi_gpio_state == SunXI::HIGH) + pio->DAT &= ~(1 << num); + else + pio->DAT |= (1 << num); +} + +void GPIOBUS_BananaM2p::DisableIRQ() +{ + *tmr_ctrl = 0b00; +} + +void GPIOBUS_BananaM2p::EnableIRQ() +{ + *tmr_ctrl = 0b11; +} + +void GPIOBUS_BananaM2p::PinConfig(int pin, int mode) +{ + GPIO_FUNCTION_TRACE + int gpio_num = pin; + int sunxi_direction = (mode == GPIO_INPUT) ? SunXI::INPUT : SunXI::OUTPUT; + + sunxi_setup_gpio(gpio_num, sunxi_direction, -1); +} + +void GPIOBUS_BananaM2p::PullConfig(int pin, int mode) +{ + GPIO_FUNCTION_TRACE +#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__) + dummy_var++; // Need to do something to prevent Sonar from claiming this should be a const function + (void)pin; + (void)mode; + return; +#else + + // Note: this will throw an exception if an invalid pin is specified + int gpio_num = pin; + int pull_up_down_state = 0; + + switch (mode) { + case GPIO_PULLNONE: + pull_up_down_state = SunXI::PUD_OFF; + break; + case GPIO_PULLUP: + pull_up_down_state = SunXI::PUD_UP; + break; + case GPIO_PULLDOWN: + pull_up_down_state = SunXI::PUD_DOWN; + break; + default: + LOGERROR("%s INVALID PIN MODE", __PRETTY_FUNCTION__); + return; + } + + uint32_t regval = 0; + int bank = SunXI::GPIO_BANK(gpio_num); // gpio >> 5 + int index = SunXI::GPIO_PUL_INDEX(gpio_num); // (gpio & 0x1f) >> 4 + int offset = SunXI::GPIO_PUL_OFFSET(gpio_num); // (gpio) & 0x0F) << 1 + LOGTRACE("%s gpio(%d) bank(%d) index(%d) offset(%d)", __PRETTY_FUNCTION__, gpio_num, bank, index, offset) + + volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]); + /* DK, for PL and PM */ + if (bank >= 11) { + bank -= 11; + pio = &(r_pio_map->gpio_bank[bank]); + } + + regval = *(&pio->PULL[0] + index); + regval &= ~(3 << offset); + regval |= pull_up_down_state << offset; + pio->PULL[0 + index] = regval; +#endif +} + +void GPIOBUS_BananaM2p::PinSetSignal(int pin, bool ast) +{ + GPIO_FUNCTION_TRACE + int gpio_num = pin; + + int sunxi_gpio_state = (ast == true) ? SunXI::HIGH : SunXI::LOW; + sunxi_output_gpio(gpio_num, sunxi_gpio_state); +} + +void GPIOBUS_BananaM2p::DrvConfig(uint32_t drive) +{ + GPIO_FUNCTION_TRACE + + for (auto pin : SignalTable) { + if (pin == -1) { + continue; + } + + LOGTRACE("Configuring GPIO %d to drive strength %d", pin, drive) + + uint32_t regval = 0; + int bank = SunXI::GPIO_BANK(pin); // gpio >> 5 + int index = SunXI::GPIO_DRV_INDEX(pin); // (gpio & 0x1F) >> 3 + int offset = SunXI::GPIO_DRV_OFFSET(pin); // ((gpio & 0x1F) & 0x7) << 2 + LOGTRACE("%s gpio(%d) bank(%d) index(%d) offset(%d)", __PRETTY_FUNCTION__, pin, bank, index, offset) + + volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]); + /* DK, for PL and PM */ + if (bank >= 11) { + bank -= 11; + pio = &(r_pio_map->gpio_bank[bank]); + } + + // Get current register value + regval = pio->DRV[0 + index]; + // Clear the DRV value for that gpio + regval &= ~(0x7 << offset); // 0xf? + // Set the new DRV strength + regval |= (drive & 0b11) << offset; + // Save back to the register + pio->DRV[0 + index] = regval; + } + + // #endif // if __arm__ +} + +uint32_t GPIOBUS_BananaM2p::Acquire() +{ + GPIO_FUNCTION_TRACE + + for (auto bank : gpio_banks) { + volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]); + /* DK, for PL and PM */ + if (bank >= 11) { + pio = &(r_pio_map->gpio_bank[bank - 11]); + } + + uint32_t regval = pio->DAT; + +#if SIGNAL_CONTROL_MODE < 2 + // Invert if negative logic (internal processing is unified to positive logic) + regval = ~regval; +#endif // SIGNAL_CONTROL_MODE + signals[bank] = regval; + } + // TODO: This should do something someday.... + return 0; +} + +int GPIOBUS_BananaM2p::sunxi_setup(void) +{ + GPIO_FUNCTION_TRACE +#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__) + dummy_var++; // Need to do something to prevent Sonar from claiming this should be a const function + return SunXI::SETUP_MMAP_FAIL; +#else + int mem_fd; + uint8_t *gpio_mem; + + // mmap the GPIO memory registers + if ((mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) { + LOGERROR("Error: Unable to open /dev/mem. Are you running as root?") + LOGDEBUG("errno: [%08X] %s", errno, strerror(errno)); + return SunXI::SETUP_DEVMEM_FAIL; + } + + if ((gpio_mem = (uint8_t *)malloc(SunXI::BLOCK_SIZE + (SunXI::PAGE_SIZE - 1))) == NULL) { + LOGERROR("Error: Unable to allocate gpio memory. Are you running as root?") + LOGDEBUG("errno: [%08X] %s", errno, strerror(errno)); + return SunXI::SETUP_DEVMEM_FAIL; + } + + if ((uint32_t)gpio_mem % SunXI::PAGE_SIZE) + gpio_mem += SunXI::PAGE_SIZE - ((uint32_t)gpio_mem % SunXI::PAGE_SIZE); + + gpio_map = (uint32_t *)mmap((caddr_t)gpio_mem, SunXI::BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, + mem_fd, SunXI::SUNXI_GPIO_BASE); + if ((void *)gpio_map == MAP_FAILED) { + LOGERROR("Error: Unable to map gpio memory. Are you running as root?") + LOGDEBUG("errno: [%08X] %s", errno, strerror(errno)); + return SunXI::SETUP_MMAP_FAIL; + } + pio_map = (volatile SunXI::sunxi_gpio_reg *)(gpio_map + (SunXI::SUNXI_GPIO_REG_OFFSET >> 2)); + LOGTRACE("gpio_mem[%p] gpio_map[%p] pio_map[%p]", gpio_mem, gpio_map, pio_map) + // R_PIO GPIO LMN + r_gpio_map = (uint32_t *)mmap((caddr_t)0, SunXI::BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, + SunXI::SUNXI_R_GPIO_BASE); + if ((void *)r_gpio_map == MAP_FAILED) { + LOGERROR("Error: Unable to map r_gpio memory. Are you running as root?") + LOGDEBUG("errno: [%08X] %s", errno, strerror(errno)); + return SunXI::SETUP_MMAP_FAIL; + } + r_pio_map = (volatile SunXI::sunxi_gpio_reg *)(r_gpio_map + (SunXI::SUNXI_R_GPIO_REG_OFFSET >> 2)); + LOGTRACE("r_gpio_map[%p] r_pio_map[%p]", r_gpio_map, r_pio_map) + + tmr_ctrl = gpio_map + ((SunXI::TMR_REGISTER_BASE - SunXI::SUNXI_GPIO_BASE) >> 2); + // LOGINFO("tmr_ctrl offset: %08X value: %08X", (TMR_REGISTER_BASE - SUNXI_GPIO_BASE), *tmr_ctrl); + + close(mem_fd); + return SETUP_OK; +#endif +} + +void GPIOBUS_BananaM2p::sunxi_setup_gpio(int pin, int direction, int pud) +{ + GPIO_FUNCTION_TRACE +#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__) + dummy_var++; // Need to do something to prevent Sonar from claiming this should be a const function + (void)pin; + (void)direction; + (void)pud; + return; +#else + uint32_t regval = 0; + int bank = SunXI::GPIO_BANK(pin); // gpio >> 5 + int index = SunXI::GPIO_CFG_INDEX(pin); // (gpio & 0x1F) >> 3 + int offset = SunXI::GPIO_CFG_OFFSET(pin); // ((gpio & 0x1F) & 0x7) << 2 + LOGTRACE("%s gpio(%d) bank(%d) index(%d) offset(%d)", __PRETTY_FUNCTION__, pin, bank, index, offset) + + volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]); + /* DK, for PL and PM */ + if (bank >= 11) { + bank -= 11; + pio = &(r_pio_map->gpio_bank[bank]); + } + + if (pud != -1) { + set_pullupdn(pin, pud); + } + regval = *(&pio->CFG[0 + index]); + regval &= ~(0x7 << offset); // 0xf? + if (SunXI::INPUT == direction) { + pio->CFG[0 + index] = regval; + } else if (SunXI::OUTPUT == direction) { + regval |= (1 << offset); + pio->CFG[0 + index] = regval; + } else { + LOGERROR("line:%d gpio number error %d", __LINE__, pin) + } +#endif +} + +void GPIOBUS_BananaM2p::sunxi_output_gpio(int pin, int value) +{ + GPIO_FUNCTION_TRACE + if (pin < 0) { + LOGWARN("Invalid GPIO Num") + return; + } + int bank = SunXI::GPIO_BANK(pin); // gpio >> 5 + int num = SunXI::GPIO_NUM(pin); // gpio & 0x1F + + LOGTRACE("%s gpio(%d) bank(%d) num(%d) value(%d)", __PRETTY_FUNCTION__, pin, bank, num, value) + volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]); + + /* DK, for PL and PM */ + if (bank >= 11) { + bank -= 11; + pio = &(r_pio_map->gpio_bank[bank]); + } + + if (value == 0) + pio->DAT &= ~(1 << num); + else + pio->DAT |= (1 << num); +} + +int GPIOBUS_BananaM2p::sunxi_input_gpio(int pin) const +{ + GPIO_FUNCTION_TRACE + uint32_t regval = 0; + int bank = SunXI::GPIO_BANK(pin); // gpio >> 5 + int num = SunXI::GPIO_NUM(pin); // gpio & 0x1F + + LOGTRACE("%s gpio(%d) bank(%d) num(%d)", __PRETTY_FUNCTION__, pin, bank, num) + volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]); + /* DK, for PL and PM */ + if (bank >= 11) { + bank -= 11; + pio = &(r_pio_map->gpio_bank[bank]); + } + + regval = pio->DAT; + regval = regval >> num; + regval &= 1; + return regval; +} + +void GPIOBUS_BananaM2p::set_pullupdn(int pin, int pud) +{ + GPIO_FUNCTION_TRACE + int clk_offset = SunXI::PULLUPDNCLK_OFFSET + (pin / 32); + int shift = (pin % 32); + +#ifdef BPI + if (bpi_found == 1) { + gpio = *(pinTobcm_BP + pin); + return sunxi_set_pullupdn(pin, pud); + } +#endif + if (pud == SunXI::PUD_DOWN) + *(gpio_map + SunXI::PULLUPDN_OFFSET) = (*(gpio_map + SunXI::PULLUPDN_OFFSET) & ~3) | SunXI::PUD_DOWN; + else if (pud == SunXI::PUD_UP) + *(gpio_map + SunXI::PULLUPDN_OFFSET) = (*(gpio_map + SunXI::PULLUPDN_OFFSET) & ~3) | SunXI::PUD_UP; + else // pud == PUD_OFF + *(gpio_map + SunXI::PULLUPDN_OFFSET) &= ~3; + + SunXI::short_wait(); + *(gpio_map + clk_offset) = 1 << shift; + SunXI::short_wait(); + *(gpio_map + SunXI::PULLUPDN_OFFSET) &= ~3; + *(gpio_map + clk_offset) = 0; +} diff --git a/cpp/hal/gpiobus_bananam2p.h b/cpp/hal/gpiobus_bananam2p.h new file mode 100644 index 00000000..629950fa --- /dev/null +++ b/cpp/hal/gpiobus_bananam2p.h @@ -0,0 +1,221 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// 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 + +//--------------------------------------------------------------------------- +// +// 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; + + 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 GetSample(uint64_t timestamp) override + { + Acquire(); + return make_unique(signals, timestamp); + } + + bool SetupSelEvent(); + +#if !defined(__x86_64__) && !defined(__X86__) + uint32_t baseaddr = 0; // Base address +#endif + + volatile uint32_t *gpio_map = nullptr; + + // Timer control register + volatile uint32_t *tmr_ctrl; + +#if !defined(__x86_64__) && !defined(__X86__) + volatile uint32_t *gicd = nullptr; // GIC Interrupt distributor register +#endif + + volatile uint32_t *gicc = nullptr; // GIC CPU interface register + + array gpfsel; // GPFSEL0-4 backup values + + array signals = {0}; // All bus signals + +#if SIGNAL_CONTROL_MODE == 0 + array, 3> tblDatMsk; // Data mask table + + array, 3> tblDatSet; // Data setting table +#else + array tblDatMsk = {}; // Data mask table + + array tblDatSet = {}; // Table setting table +#endif + + 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; + + SunXI::sunxi_gpio_reg_t saved_gpio_config; + + static const array SignalTable; + + void InitializeGpio(); + std::vector gpio_banks; + +#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 +}; diff --git a/cpp/hal/gpiobus_factory.cpp b/cpp/hal/gpiobus_factory.cpp index a6305c30..46a442ee 100644 --- a/cpp/hal/gpiobus_factory.cpp +++ b/cpp/hal/gpiobus_factory.cpp @@ -4,24 +4,41 @@ // for Raspberry Pi // // Copyright (C) 2022 akuker +// [ GPIO bus factory ] // //--------------------------------------------------------------------------- +#include + +#include "hal/gpiobus_bananam2p.h" #include "hal/gpiobus_factory.h" -#include "hal/gpiobus.h" +#include "hal/gpiobus_raspberry.h" +#include "hal/gpiobus_virtual.h" +#include "hal/sbc_version.h" +#include "shared/log.h" using namespace std; unique_ptr GPIOBUS_Factory::Create(BUS::mode_e mode) { - // 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. - if (auto bus = make_unique(); bus->Init(mode)) { - bus->Reset(); - - return bus; - } - - return nullptr; + // 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. + unique_ptr return_ptr; + SBC_Version::Init(); + if (SBC_Version::IsBananaPi()) { + LOGTRACE("Creating GPIOBUS_BananaM2p") + return_ptr = make_unique(); + } else if (SBC_Version::IsRaspberryPi()) { + LOGTRACE("Creating GPIOBUS_Raspberry") + return_ptr = make_unique(); + } else { + LOGINFO("Creating Virtual GPIOBUS") + return_ptr = make_unique(); + } + if (!return_ptr->Init(mode)) { + return nullptr; + } + return_ptr->Reset(); + return return_ptr; } diff --git a/cpp/hal/gpiobus_factory.h b/cpp/hal/gpiobus_factory.h index a62f47a6..6e173ad7 100644 --- a/cpp/hal/gpiobus_factory.h +++ b/cpp/hal/gpiobus_factory.h @@ -9,14 +9,12 @@ #pragma once -#include "bus.h" +#include "hal/bus.h" #include -using namespace std; - class GPIOBUS_Factory { public: - static unique_ptr Create(BUS::mode_e); + static unique_ptr Create(BUS::mode_e mode); }; diff --git a/cpp/hal/gpiobus_fullspec.h b/cpp/hal/gpiobus_fullspec.h deleted file mode 100644 index ee62c6d5..00000000 --- a/cpp/hal/gpiobus_fullspec.h +++ /dev/null @@ -1,56 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI Target Emulator RaSCSI Reloaded -// for Raspberry Pi -// -// Powered by XM6 TypeG Technology. -// Copyright (C) 2016-2020 GIMONS -// -//--------------------------------------------------------------------------- - -#pragma once - -#include - -// -// RaSCSI standard (SCSI logic, standard pin assignment) -// - -const std::string CONNECT_DESC = "FULLSPEC"; // Startup message - -// Select signal control mode -const static int SIGNAL_CONTROL_MODE = 0; // SCSI logical specification - -// Control signal pin assignment (-1 means no control) -const static int PIN_ACT = 4; // ACTIVE -const static int PIN_ENB = 5; // ENABLE -const static int PIN_IND = 6; // INITIATOR CTRL DIRECTION -const static int PIN_TAD = 7; // TARGET CTRL DIRECTION -const static int PIN_DTD = 8; // DATA DIRECTION - -// Control signal output logic -#define ACT_ON ON // ACTIVE SIGNAL ON -#define ENB_ON ON // ENABLE SIGNAL ON -#define IND_IN OFF // INITIATOR SIGNAL INPUT -#define TAD_IN OFF // TARGET SIGNAL INPUT -#define DTD_IN ON // DATA SIGNAL INPUT - -// SCSI signal pin assignment -const static int PIN_DT0 = 10; // Data 0 -const static int PIN_DT1 = 11; // Data 1 -const static int PIN_DT2 = 12; // Data 2 -const static int PIN_DT3 = 13; // Data 3 -const static int PIN_DT4 = 14; // Data 4 -const static int PIN_DT5 = 15; // Data 5 -const static int PIN_DT6 = 16; // Data 6 -const static int PIN_DT7 = 17; // Data 7 -const static int PIN_DP = 18; // Data parity -const static int PIN_ATN = 19; // ATN -const static int PIN_RST = 20; // RST -const static int PIN_ACK = 21; // ACK -const static int PIN_REQ = 22; // REQ -const static int PIN_MSG = 23; // MSG -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 diff --git a/cpp/hal/gpiobus_gamernium.h b/cpp/hal/gpiobus_gamernium.h deleted file mode 100644 index bf295284..00000000 --- a/cpp/hal/gpiobus_gamernium.h +++ /dev/null @@ -1,57 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI Target Emulator RaSCSI Reloaded -// for Raspberry Pi -// -// Powered by XM6 TypeG Technology. -// Copyright (C) 2016-2020 GIMONS -// -//--------------------------------------------------------------------------- - -#pragma once - -#include - -// -// RaSCSI Adapter GAMERnium.com version -// - -const std::string CONNECT_DESC = "GAMERnium.com version"; // Startup message - -// Select signal control mode -const static int SIGNAL_CONTROL_MODE = 0; // SCSI logical specification - -// Control signal output logic -#define ACT_ON ON // ACTIVE SIGNAL ON -#define ENB_ON ON // ENABLE SIGNAL ON -#define IND_IN OFF // INITIATOR SIGNAL INPUT -#define TAD_IN OFF // TARGET SIGNAL INPUT -#define DTD_IN ON // DATA SIGNAL INPUT - -// Control signal pin assignment (-1 means no control) -const static int PIN_ACT = 14; // ACTIVE -const static int PIN_ENB = 6; // ENABLE -const static int PIN_IND = 7; // INITIATOR CTRL DIRECTION -const static int PIN_TAD = 8; // TARGET CTRL DIRECTION -const static int PIN_DTD = 5; // DATA DIRECTION - -// SCSI signal pin assignment -const static int PIN_DT0 = 21; // Data 0 -const static int PIN_DT1 = 26; // Data 1 -const static int PIN_DT2 = 20; // Data 2 -const static int PIN_DT3 = 19; // Data 3 -const static int PIN_DT4 = 16; // Data 4 -const static int PIN_DT5 = 13; // Data 5 -const static int PIN_DT6 = 12; // Data 6 -const static int PIN_DT7 = 11; // Data 7 -const static int PIN_DP = 25; // Data parity -const static int PIN_ATN = 10; // ATN -const static int PIN_RST = 22; // RST -const static int PIN_ACK = 24; // ACK -const static int PIN_REQ = 15; // REQ -const static int PIN_MSG = 17; // MSG -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 - diff --git a/cpp/hal/gpiobus_raspberry.cpp b/cpp/hal/gpiobus_raspberry.cpp new file mode 100644 index 00000000..d020bcdb --- /dev/null +++ b/cpp/hal/gpiobus_raspberry.cpp @@ -0,0 +1,988 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// +// [ GPIO-SCSI bus ] +// +// Raspberry Pi 4: +// https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf +// Raspberry Pi Zero: +// https://datasheets.raspberrypi.com/bcm2835/bcm2835-peripherals.pdf +// +//--------------------------------------------------------------------------- + +#include "hal/gpiobus_raspberry.h" +#include "hal/gpiobus.h" +#include "hal/systimer.h" +#include "shared/log.h" +#include +#include +#include +#include +#include +#include + +//--------------------------------------------------------------------------- +// +// imported from bcm_host.c +// +//--------------------------------------------------------------------------- +uint32_t GPIOBUS_Raspberry::get_dt_ranges(const char *filename, uint32_t offset) +{ + GPIO_FUNCTION_TRACE + uint32_t address = ~0; + if (FILE *fp = fopen(filename, "rb"); fp) { + fseek(fp, offset, SEEK_SET); + if (array 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); + } + return address; +} + +uint32_t GPIOBUS_Raspberry::bcm_host_get_peripheral_address() +{ + GPIO_FUNCTION_TRACE +#ifdef __linux__ + uint32_t address = get_dt_ranges("/proc/device-tree/soc/ranges", 4); + if (address == 0) { + address = get_dt_ranges("/proc/device-tree/soc/ranges", 8); + } + address = (address == (uint32_t)~0) ? 0x20000000 : address; + return address; +#else + return 0; +#endif +} + +bool GPIOBUS_Raspberry::Init(mode_e mode) +{ + GPIOBUS::Init(mode); + +#if defined(__x86_64__) || defined(__X86__) + (void)baseaddr; + level = new uint32_t(); + return true; +#else + int i; +#ifdef USE_SEL_EVENT_ENABLE + epoll_event ev = {}; +#endif + + // Get the base address + baseaddr = (uint32_t)bcm_host_get_peripheral_address(); + + // 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?") + 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") + close(fd); + return false; + } + + // Determine the type of raspberry pi from the base address + if (baseaddr == 0xfe000000) { + rpitype = 4; + } else if (baseaddr == 0x3f000000) { + rpitype = 2; + } else { + rpitype = 1; + } + + // GPIO + gpio = (uint32_t *)map; + gpio += GPIO_OFFSET / sizeof(uint32_t); + level = &gpio[GPIO_LEV_0]; + + // PADS + pads = (uint32_t *)map; + pads += PADS_OFFSET / sizeof(uint32_t); + + // System timer + SysTimer::Init(); + + // Interrupt controller + irpctl = (uint32_t *)map; + irpctl += IRPT_OFFSET / sizeof(uint32_t); + + // Quad-A7 control + qa7regs = (uint32_t *)map; + qa7regs += QA7_OFFSET / sizeof(uint32_t); + + // Map GIC memory + if (rpitype == 4) { + map = mmap(NULL, 8192, PROT_READ | PROT_WRITE, MAP_SHARED, fd, ARM_GICD_BASE); + if (map == MAP_FAILED) { + close(fd); + return false; + } + gicd = (uint32_t *)map; + gicc = (uint32_t *)map; + gicc += (ARM_GICC_BASE - ARM_GICD_BASE) / sizeof(uint32_t); + } else { + gicd = NULL; + gicc = NULL; + } + close(fd); + + // Set Drive Strength to 16mA + DrvConfig(7); + + // Set pull up/pull down +#if SIGNAL_CONTROL_MODE == 0 + int pullmode = GPIO_PULLNONE; +#elif SIGNAL_CONTROL_MODE == 1 + int pullmode = GPIO_PULLUP; +#else + int pullmode = GPIO_PULLDOWN; +#endif + + // Initialize all signals + for (i = 0; SignalTable[i] >= 0; i++) { + int j = SignalTable[i]; + PinSetSignal(j, OFF); + PinConfig(j, GPIO_INPUT); + PullConfig(j, pullmode); + } + + // Set control signals + PinSetSignal(PIN_ACT, OFF); + PinSetSignal(PIN_TAD, OFF); + PinSetSignal(PIN_IND, OFF); + PinSetSignal(PIN_DTD, OFF); + PinConfig(PIN_ACT, GPIO_OUTPUT); + PinConfig(PIN_TAD, GPIO_OUTPUT); + PinConfig(PIN_IND, GPIO_OUTPUT); + PinConfig(PIN_DTD, GPIO_OUTPUT); + + // Set the ENABLE signal + // This is used to show that the application is running + PinSetSignal(PIN_ENB, ENB_OFF); + PinConfig(PIN_ENB, GPIO_OUTPUT); + + // GPIO Function Select (GPFSEL) registers backup + gpfsel[0] = gpio[GPIO_FSEL_0]; + gpfsel[1] = gpio[GPIO_FSEL_1]; + gpfsel[2] = gpio[GPIO_FSEL_2]; + gpfsel[3] = gpio[GPIO_FSEL_3]; + + // Initialize SEL signal interrupt +#ifdef USE_SEL_EVENT_ENABLE + // GPIO chip open + fd = open("/dev/gpiochip0", 0); + if (fd == -1) { + LOGERROR("Unable to open /dev/gpiochip0. Is RaSCSI already running?") + return false; + } + + // Event request setting + strcpy(selevreq.consumer_label, "RaSCSI"); + selevreq.lineoffset = PIN_SEL; + selevreq.handleflags = GPIOHANDLE_REQUEST_INPUT; +#if SIGNAL_CONTROL_MODE < 2 + selevreq.eventflags = GPIOEVENT_REQUEST_FALLING_EDGE; +#else + selevreq.eventflags = GPIOEVENT_REQUEST_RISING_EDGE; +#endif // SIGNAL_CONTROL_MODE + + // Get event request + if (ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &selevreq) == -1) { + LOGERROR("Unable to register event request. Is RaSCSI already running?") + close(fd); + return false; + } + + // Close GPIO chip file handle + close(fd); + + // epoll initialization + epfd = epoll_create(1); + ev.events = EPOLLIN | EPOLLPRI; + ev.data.fd = selevreq.fd; + epoll_ctl(epfd, EPOLL_CTL_ADD, selevreq.fd, &ev); +#else + // Edge detection setting +#if SIGNAL_CONTROL_MODE == 2 + gpio[GPIO_AREN_0] = 1 << PIN_SEL; +#else + gpio[GPIO_AFEN_0] = 1 << PIN_SEL; +#endif // SIGNAL_CONTROL_MODE + + // Clear event - GPIO Pin Event Detect Status + gpio[GPIO_EDS_0] = 1 << PIN_SEL; + + // Register interrupt handler + setIrqFuncAddress(IrqHandler); + + // GPIO interrupt setting + if (rpitype == 4) { + // GIC Invalid + gicd[GICD_CTLR] = 0; + + // Route all interupts to core 0 + for (i = 0; i < 8; i++) { + gicd[GICD_ICENABLER0 + i] = 0xffffffff; + gicd[GICD_ICPENDR0 + i] = 0xffffffff; + gicd[GICD_ICACTIVER0 + i] = 0xffffffff; + } + for (i = 0; i < 64; i++) { + gicd[GICD_IPRIORITYR0 + i] = 0xa0a0a0a0; + gicd[GICD_ITARGETSR0 + i] = 0x01010101; + } + + // Set all interrupts as level triggers + for (i = 0; i < 16; i++) { + gicd[GICD_ICFGR0 + i] = 0; + } + + // GIC Invalid + gicd[GICD_CTLR] = 1; + + // Enable CPU interface for core 0 + gicc[GICC_PMR] = 0xf0; + gicc[GICC_CTLR] = 1; + + // Enable interrupts + gicd[GICD_ISENABLER0 + (GIC_GPIO_IRQ / 32)] = 1 << (GIC_GPIO_IRQ % 32); + } else { + // Enable interrupts + irpctl[IRPT_ENB_IRQ_2] = (1 << (GPIO_IRQ % 32)); + } +#endif // USE_SEL_EVENT_ENABLE + + // Create work table + MakeTable(); + + // Finally, enable ENABLE + // Show the user that this app is running + SetControl(PIN_ENB, ENB_ON); + + return true; +#endif // ifdef __x86_64__ || __X86__ +} + +void GPIOBUS_Raspberry::Cleanup() +{ +#if defined(__x86_64__) || defined(__X86__) + return; +#else + // Release SEL signal interrupt +#ifdef USE_SEL_EVENT_ENABLE + close(selevreq.fd); +#endif // USE_SEL_EVENT_ENABLE + + // Set control signals + PinSetSignal(PIN_ENB, OFF); + PinSetSignal(PIN_ACT, OFF); + PinSetSignal(PIN_TAD, OFF); + PinSetSignal(PIN_IND, OFF); + PinSetSignal(PIN_DTD, OFF); + PinConfig(PIN_ACT, GPIO_INPUT); + PinConfig(PIN_TAD, GPIO_INPUT); + PinConfig(PIN_IND, GPIO_INPUT); + PinConfig(PIN_DTD, GPIO_INPUT); + + // Initialize all signals + for (int i = 0; SignalTable[i] >= 0; i++) { + int pin = SignalTable[i]; + PinSetSignal(pin, OFF); + PinConfig(pin, GPIO_INPUT); + PullConfig(pin, GPIO_PULLNONE); + } + + // Set drive strength back to 8mA + DrvConfig(3); +#endif // ifdef __x86_64__ || __X86__ +} + +void GPIOBUS_Raspberry::Reset() +{ +#if defined(__x86_64__) || defined(__X86__) + return; +#else + int i; + int j; + + // Turn off active signal + SetControl(PIN_ACT, ACT_OFF); + + // Set all signals to off + for (i = 0;; i++) { + j = SignalTable[i]; + if (j < 0) { + break; + } + + SetSignal(j, OFF); + } + + if (actmode == mode_e::TARGET) { + // Target mode + + // Set target signal to input + SetControl(PIN_TAD, TAD_IN); + SetMode(PIN_BSY, IN); + SetMode(PIN_MSG, IN); + SetMode(PIN_CD, IN); + SetMode(PIN_REQ, IN); + SetMode(PIN_IO, IN); + + // Set the initiator signal to input + SetControl(PIN_IND, IND_IN); + SetMode(PIN_SEL, IN); + SetMode(PIN_ATN, IN); + SetMode(PIN_ACK, IN); + SetMode(PIN_RST, IN); + + // Set data bus signals to input + SetControl(PIN_DTD, DTD_IN); + SetMode(PIN_DT0, IN); + SetMode(PIN_DT1, IN); + SetMode(PIN_DT2, IN); + SetMode(PIN_DT3, IN); + SetMode(PIN_DT4, IN); + SetMode(PIN_DT5, IN); + SetMode(PIN_DT6, IN); + SetMode(PIN_DT7, IN); + SetMode(PIN_DP, IN); + } else { + // Initiator mode + + // Set target signal to input + SetControl(PIN_TAD, TAD_IN); + SetMode(PIN_BSY, IN); + SetMode(PIN_MSG, IN); + SetMode(PIN_CD, IN); + SetMode(PIN_REQ, IN); + SetMode(PIN_IO, IN); + + // Set the initiator signal to output + SetControl(PIN_IND, IND_OUT); + SetMode(PIN_SEL, OUT); + SetMode(PIN_ATN, OUT); + SetMode(PIN_ACK, OUT); + SetMode(PIN_RST, OUT); + + // Set the data bus signals to output + SetControl(PIN_DTD, DTD_OUT); + SetMode(PIN_DT0, OUT); + SetMode(PIN_DT1, OUT); + SetMode(PIN_DT2, OUT); + SetMode(PIN_DT3, OUT); + SetMode(PIN_DT4, OUT); + SetMode(PIN_DT5, OUT); + SetMode(PIN_DT6, OUT); + SetMode(PIN_DT7, OUT); + SetMode(PIN_DP, OUT); + } + + // Initialize all signals + signals = 0; +#endif // ifdef __x86_64__ || __X86__ +} + +void GPIOBUS_Raspberry::SetENB(bool ast) +{ + PinSetSignal(PIN_ENB, ast ? ENB_ON : ENB_OFF); +} + +bool GPIOBUS_Raspberry::GetBSY() const +{ + return GetSignal(PIN_BSY); +} + +void GPIOBUS_Raspberry::SetBSY(bool ast) +{ + // Set BSY signal + SetSignal(PIN_BSY, ast); + + if (actmode == mode_e::TARGET) { + if (ast) { + // Turn on ACTIVE signal + SetControl(PIN_ACT, ACT_ON); + + // Set Target signal to output + SetControl(PIN_TAD, TAD_OUT); + + SetMode(PIN_BSY, OUT); + SetMode(PIN_MSG, OUT); + SetMode(PIN_CD, OUT); + SetMode(PIN_REQ, OUT); + SetMode(PIN_IO, OUT); + } else { + // Turn off the ACTIVE signal + SetControl(PIN_ACT, ACT_OFF); + + // Set the target signal to input + SetControl(PIN_TAD, TAD_IN); + + SetMode(PIN_BSY, IN); + SetMode(PIN_MSG, IN); + SetMode(PIN_CD, IN); + SetMode(PIN_REQ, IN); + SetMode(PIN_IO, IN); + } + } +} + +bool GPIOBUS_Raspberry::GetSEL() const +{ + return GetSignal(PIN_SEL); +} + +void GPIOBUS_Raspberry::SetSEL(bool ast) +{ + if (actmode == mode_e::INITIATOR && ast) { + // Turn on ACTIVE signal + SetControl(PIN_ACT, ACT_ON); + } + + // Set SEL signal + SetSignal(PIN_SEL, ast); +} + +bool GPIOBUS_Raspberry::GetATN() const +{ + return GetSignal(PIN_ATN); +} + +void GPIOBUS_Raspberry::SetATN(bool ast) +{ + SetSignal(PIN_ATN, ast); +} + +bool GPIOBUS_Raspberry::GetACK() const +{ + return GetSignal(PIN_ACK); +} + +void GPIOBUS_Raspberry::SetACK(bool ast) +{ + SetSignal(PIN_ACK, ast); +} + +bool GPIOBUS_Raspberry::GetACT() const +{ + return GetSignal(PIN_ACT); +} + +void GPIOBUS_Raspberry::SetACT(bool ast) +{ + SetSignal(PIN_ACT, ast); +} + +bool GPIOBUS_Raspberry::GetRST() const +{ + return GetSignal(PIN_RST); +} + +void GPIOBUS_Raspberry::SetRST(bool ast) +{ + SetSignal(PIN_RST, ast); +} + +bool GPIOBUS_Raspberry::GetMSG() const +{ + return GetSignal(PIN_MSG); +} + +void GPIOBUS_Raspberry::SetMSG(bool ast) +{ + SetSignal(PIN_MSG, ast); +} + +bool GPIOBUS_Raspberry::GetCD() const +{ + return GetSignal(PIN_CD); +} + +void GPIOBUS_Raspberry::SetCD(bool ast) +{ + SetSignal(PIN_CD, ast); +} + +bool GPIOBUS_Raspberry::GetIO() +{ + bool ast = GetSignal(PIN_IO); + + if (actmode == mode_e::INITIATOR) { + // Change the data input/output direction by IO signal + if (ast) { + SetControl(PIN_DTD, DTD_IN); + SetMode(PIN_DT0, IN); + SetMode(PIN_DT1, IN); + SetMode(PIN_DT2, IN); + SetMode(PIN_DT3, IN); + SetMode(PIN_DT4, IN); + SetMode(PIN_DT5, IN); + SetMode(PIN_DT6, IN); + SetMode(PIN_DT7, IN); + SetMode(PIN_DP, IN); + } else { + SetControl(PIN_DTD, DTD_OUT); + SetMode(PIN_DT0, OUT); + SetMode(PIN_DT1, OUT); + SetMode(PIN_DT2, OUT); + SetMode(PIN_DT3, OUT); + SetMode(PIN_DT4, OUT); + SetMode(PIN_DT5, OUT); + SetMode(PIN_DT6, OUT); + SetMode(PIN_DT7, OUT); + SetMode(PIN_DP, OUT); + } + } + + return ast; +} + +void GPIOBUS_Raspberry::SetIO(bool ast) +{ + SetSignal(PIN_IO, ast); + + if (actmode == mode_e::TARGET) { + // Change the data input/output direction by IO signal + if (ast) { + SetControl(PIN_DTD, DTD_OUT); + SetDAT(0); + SetMode(PIN_DT0, OUT); + SetMode(PIN_DT1, OUT); + SetMode(PIN_DT2, OUT); + SetMode(PIN_DT3, OUT); + SetMode(PIN_DT4, OUT); + SetMode(PIN_DT5, OUT); + SetMode(PIN_DT6, OUT); + SetMode(PIN_DT7, OUT); + SetMode(PIN_DP, OUT); + } else { + SetControl(PIN_DTD, DTD_IN); + SetMode(PIN_DT0, IN); + SetMode(PIN_DT1, IN); + SetMode(PIN_DT2, IN); + SetMode(PIN_DT3, IN); + SetMode(PIN_DT4, IN); + SetMode(PIN_DT5, IN); + SetMode(PIN_DT6, IN); + SetMode(PIN_DT7, IN); + SetMode(PIN_DP, IN); + } + } +} + +bool GPIOBUS_Raspberry::GetREQ() const +{ + return GetSignal(PIN_REQ); +} + +void GPIOBUS_Raspberry::SetREQ(bool ast) +{ + SetSignal(PIN_REQ, ast); +} + +//--------------------------------------------------------------------------- +// +// Get data signals +// +//--------------------------------------------------------------------------- +uint8_t GPIOBUS_Raspberry::GetDAT() +{ + uint32_t data = Acquire(); + data = ((data >> (PIN_DT0 - 0)) & (1 << 0)) | ((data >> (PIN_DT1 - 1)) & (1 << 1)) | + ((data >> (PIN_DT2 - 2)) & (1 << 2)) | ((data >> (PIN_DT3 - 3)) & (1 << 3)) | + ((data >> (PIN_DT4 - 4)) & (1 << 4)) | ((data >> (PIN_DT5 - 5)) & (1 << 5)) | + ((data >> (PIN_DT6 - 6)) & (1 << 6)) | ((data >> (PIN_DT7 - 7)) & (1 << 7)); + + return (uint8_t)data; +} + +//--------------------------------------------------------------------------- +// +// Set data signals +// +//--------------------------------------------------------------------------- +void GPIOBUS_Raspberry::SetDAT(uint8_t dat) +{ + // Write to port +#if SIGNAL_CONTROL_MODE == 0 + uint32_t fsel = gpfsel[0]; + fsel &= tblDatMsk[0][dat]; + fsel |= tblDatSet[0][dat]; + if (fsel != gpfsel[0]) { + gpfsel[0] = fsel; + gpio[GPIO_FSEL_0] = fsel; + } + + fsel = gpfsel[1]; + fsel &= tblDatMsk[1][dat]; + fsel |= tblDatSet[1][dat]; + if (fsel != gpfsel[1]) { + gpfsel[1] = fsel; + gpio[GPIO_FSEL_1] = fsel; + } + + fsel = gpfsel[2]; + fsel &= tblDatMsk[2][dat]; + fsel |= tblDatSet[2][dat]; + if (fsel != gpfsel[2]) { + gpfsel[2] = fsel; + gpio[GPIO_FSEL_2] = fsel; + } +#else + gpio[GPIO_CLR_0] = tblDatMsk[dat]; + gpio[GPIO_SET_0] = tblDatSet[dat]; +#endif // SIGNAL_CONTROL_MODE +} + +bool GPIOBUS_Raspberry::GetDP() const +{ + return GetSignal(PIN_DP); +} + +//--------------------------------------------------------------------------- +// +// Create work table +// +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// Signal table +// +//--------------------------------------------------------------------------- +const array GPIOBUS_Raspberry::SignalTable = {PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, + PIN_DT7, PIN_DP, PIN_SEL, PIN_ATN, PIN_RST, PIN_ACK, PIN_BSY, + PIN_MSG, PIN_CD, PIN_IO, PIN_REQ, -1}; + +//--------------------------------------------------------------------------- +// +// Create work table +// +//--------------------------------------------------------------------------- +void GPIOBUS_Raspberry::MakeTable(void) +{ + const array pintbl = {PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP}; + + array tblParity; + + // Create parity table + for (uint32_t i = 0; i < 0x100; i++) { + uint32_t bits = i; + uint32_t parity = 0; + for (int j = 0; j < 8; j++) { + parity ^= bits & 1; + bits >>= 1; + } + parity = ~parity; + tblParity[i] = parity & 1; + } + +#if SIGNAL_CONTROL_MODE == 0 + // Mask and setting data generation + for (auto &tbl : tblDatMsk) { + tbl.fill(-1); + } + for (auto &tbl : tblDatSet) { + tbl.fill(0); + } + + for (uint32_t i = 0; i < 0x100; i++) { + // Bit string for inspection + uint32_t bits = i; + + // Get parity + if (tblParity[i]) { + bits |= (1 << 8); + } + + // Bit check + for (int j = 0; j < 9; j++) { + // Index and shift amount calculation + int index = pintbl[j] / 10; + int shift = (pintbl[j] % 10) * 3; + + // Mask data + tblDatMsk[index][i] &= ~(0x7 << shift); + + // Setting data + if (bits & 1) { + tblDatSet[index][i] |= (1 << shift); + } + + bits >>= 1; + } + } +#else + for (uint32_t i = 0; i < 0x100; i++) { + // Bit string for inspection + uint32_t bits = i; + + // Get parity + if (tblParity[i]) { + bits |= (1 << 8); + } + +#if SIGNAL_CONTROL_MODE == 1 + // Negative logic is inverted + bits = ~bits; +#endif + + // Create GPIO register information + uint32_t gpclr = 0; + uint32_t gpset = 0; + for (int j = 0; j < 9; j++) { + if (bits & 1) { + gpset |= (1 << pintbl[j]); + } else { + gpclr |= (1 << pintbl[j]); + } + bits >>= 1; + } + + tblDatMsk[i] = gpclr; + tblDatSet[i] = gpset; + } +#endif +} + +//--------------------------------------------------------------------------- +// +// Control signal setting +// +//--------------------------------------------------------------------------- +void GPIOBUS_Raspberry::SetControl(int pin, bool ast) +{ + PinSetSignal(pin, ast); +} + +//--------------------------------------------------------------------------- +// +// Input/output mode setting +// +// Set direction fo pin (IN / OUT) +// Used with: TAD, BSY, MSG, CD, REQ, O, SEL, IND, ATN, ACK, RST, DT* +// +//--------------------------------------------------------------------------- +void GPIOBUS_Raspberry::SetMode(int pin, int mode) +{ +#if SIGNAL_CONTROL_MODE == 0 + if (mode == OUT) { + return; + } +#endif // SIGNAL_CONTROL_MODE + + int index = pin / 10; + int shift = (pin % 10) * 3; + uint32_t data = gpfsel[index]; + data &= ~(0x7 << shift); + if (mode == OUT) { + data |= (1 << shift); + } + gpio[index] = data; + gpfsel[index] = data; +} + +//--------------------------------------------------------------------------- +// +// Get input signal value +// +//--------------------------------------------------------------------------- +bool GPIOBUS_Raspberry::GetSignal(int pin) const +{ + return (signals >> pin) & 1; +} + +//--------------------------------------------------------------------------- +// +// Set output signal value +// +// Sets the output value. Used with: +// PIN_ENB, ACT, TAD, IND, DTD, BSY, SignalTable +// +//--------------------------------------------------------------------------- +void GPIOBUS_Raspberry::SetSignal(int pin, bool ast) +{ +#if SIGNAL_CONTROL_MODE == 0 + int index = pin / 10; + int shift = (pin % 10) * 3; + uint32_t data = gpfsel[index]; + if (ast) { + data |= (1 << shift); + } else { + data &= ~(0x7 << shift); + } + gpio[index] = data; + gpfsel[index] = data; +#elif SIGNAL_CONTROL_MODE == 1 + if (ast) { + gpio[GPIO_CLR_0] = 0x1 << pin; + } else { + gpio[GPIO_SET_0] = 0x1 << pin; + } +#elif SIGNAL_CONTROL_MODE == 2 + if (ast) { + gpio[GPIO_SET_0] = 0x1 << pin; + } else { + gpio[GPIO_CLR_0] = 0x1 << pin; + } +#endif // SIGNAL_CONTROL_MODE +} + +void GPIOBUS_Raspberry::DisableIRQ() +{ +#ifdef __linux__ + if (rpitype == 4) { + // RPI4 is disabled by GICC + giccpmr = gicc[GICC_PMR]; + gicc[GICC_PMR] = 0; + } else if (rpitype == 2) { + // RPI2,3 disable core timer IRQ + tintcore = sched_getcpu() + QA7_CORE0_TINTC; + tintctl = qa7regs[tintcore]; + qa7regs[tintcore] = 0; + } else { + // Stop system timer interrupt with interrupt controller + irptenb = irpctl[IRPT_ENB_IRQ_1]; + irpctl[IRPT_DIS_IRQ_1] = irptenb & 0xf; + } +#else + (void)0; +#endif +} + +void GPIOBUS_Raspberry::EnableIRQ() +{ + if (rpitype == 4) { + // RPI4 enables interrupts via the GICC + gicc[GICC_PMR] = giccpmr; + } else if (rpitype == 2) { + // RPI2,3 re-enable core timer IRQ + qa7regs[tintcore] = tintctl; + } else { + // Restart the system timer interrupt with the interrupt controller + irpctl[IRPT_ENB_IRQ_1] = irptenb & 0xf; + } +} + +//--------------------------------------------------------------------------- +// +// Pin direction setting (input/output) +// +// Used in Init() for ACT, TAD, IND, DTD, ENB to set direction (GPIO_OUTPUT vs GPIO_INPUT) +// Also used on SignalTable +// Only used in Init and Cleanup. Reset uses SetMode +//--------------------------------------------------------------------------- +void GPIOBUS_Raspberry::PinConfig(int pin, int mode) +{ + // Check for invalid pin + if (pin < 0) { + return; + } + + int index = pin / 10; + uint32_t mask = ~(0x7 << ((pin % 10) * 3)); + gpio[index] = (gpio[index] & mask) | ((mode & 0x7) << ((pin % 10) * 3)); +} + +//--------------------------------------------------------------------------- +// +// Pin pull-up/pull-down setting +// +//--------------------------------------------------------------------------- +void GPIOBUS_Raspberry::PullConfig(int pin, int mode) +{ + uint32_t pull; + + // Check for invalid pin + if (pin < 0) { + return; + } + + if (rpitype == 4) { + switch (mode) { + case GPIO_PULLNONE: + pull = 0; + break; + case GPIO_PULLUP: + pull = 1; + break; + case GPIO_PULLDOWN: + pull = 2; + break; + default: + return; + } + + pin &= 0x1f; + int shift = (pin & 0xf) << 1; + uint32_t bits = gpio[GPIO_PUPPDN0 + (pin >> 4)]; + bits &= ~(3 << shift); + bits |= (pull << shift); + gpio[GPIO_PUPPDN0 + (pin >> 4)] = bits; + } else { + pin &= 0x1f; + gpio[GPIO_PUD] = mode & 0x3; + SysTimer::SleepUsec(2); + gpio[GPIO_CLK_0] = 0x1 << pin; + SysTimer::SleepUsec(2); + gpio[GPIO_PUD] = 0; + gpio[GPIO_CLK_0] = 0; + } +} + +//--------------------------------------------------------------------------- +// +// Set output pin +// +//--------------------------------------------------------------------------- +void GPIOBUS_Raspberry::PinSetSignal(int pin, bool ast) +{ + // Check for invalid pin + if (pin < 0) { + return; + } + + if (ast) { + gpio[GPIO_SET_0] = 0x1 << pin; + } else { + gpio[GPIO_CLR_0] = 0x1 << pin; + } +} + +//--------------------------------------------------------------------------- +// +// Set the signal drive strength +// +//--------------------------------------------------------------------------- +void GPIOBUS_Raspberry::DrvConfig(uint32_t drive) +{ + uint32_t data = pads[PAD_0_27]; + pads[PAD_0_27] = (0xFFFFFFF8 & data) | drive | 0x5a000000; +} + +//--------------------------------------------------------------------------- +// +// Bus signal acquisition +// +//--------------------------------------------------------------------------- +uint32_t GPIOBUS_Raspberry::Acquire() +{ + signals = *level; + +#if SIGNAL_CONTROL_MODE < 2 + // Invert if negative logic (internal processing is unified to positive logic) + signals = ~signals; +#endif // SIGNAL_CONTROL_MODE + + return signals; +} \ No newline at end of file diff --git a/cpp/hal/gpiobus_raspberry.h b/cpp/hal/gpiobus_raspberry.h new file mode 100644 index 00000000..0e037672 --- /dev/null +++ b/cpp/hal/gpiobus_raspberry.h @@ -0,0 +1,295 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// [ GPIO-SCSI bus ] +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "hal/data_sample_raspberry.h" +#include "hal/gpiobus.h" +#include "shared/log.h" +#include "shared/scsi.h" +#include + +//--------------------------------------------------------------------------- +// +// SCSI signal pin assignment setting +// GPIO pin mapping table for SCSI signals. +// PIN_DT0~PIN_SEL +// +//--------------------------------------------------------------------------- + +#define ALL_SCSI_PINS \ + ((1 << PIN_DT0) | (1 << PIN_DT1) | (1 << PIN_DT2) | (1 << PIN_DT3) | (1 << PIN_DT4) | (1 << PIN_DT5) | \ + (1 << PIN_DT6) | (1 << PIN_DT7) | (1 << PIN_DP) | (1 << PIN_ATN) | (1 << PIN_RST) | (1 << PIN_ACK) | \ + (1 << PIN_REQ) | (1 << PIN_MSG) | (1 << PIN_CD) | (1 << PIN_IO) | (1 << PIN_BSY) | (1 << PIN_SEL)) + +#define GPIO_INEDGE ((1 << PIN_BSY) | (1 << PIN_SEL) | (1 << PIN_ATN) | (1 << PIN_ACK) | (1 << PIN_RST)) + +#define GPIO_MCI ((1 << PIN_MSG) | (1 << PIN_CD) | (1 << PIN_IO)) + +//--------------------------------------------------------------------------- +// +// Constant declarations (GIC) +// +//--------------------------------------------------------------------------- +const static uint32_t ARM_GICD_BASE = 0xFF841000; +const static uint32_t ARM_GICC_BASE = 0xFF842000; +const static uint32_t ARM_GIC_END = 0xFF847FFF; +const static int GICD_CTLR = 0x000; +const static int GICD_IGROUPR0 = 0x020; +const static int GICD_ISENABLER0 = 0x040; +const static int GICD_ICENABLER0 = 0x060; +const static int GICD_ISPENDR0 = 0x080; +const static int GICD_ICPENDR0 = 0x0A0; +const static int GICD_ISACTIVER0 = 0x0C0; +const static int GICD_ICACTIVER0 = 0x0E0; +const static int GICD_IPRIORITYR0 = 0x100; +const static int GICD_ITARGETSR0 = 0x200; +const static int GICD_ICFGR0 = 0x300; +const static int GICD_SGIR = 0x3C0; +const static int GICC_CTLR = 0x000; +const static int GICC_PMR = 0x001; +const static int GICC_IAR = 0x003; +const static int GICC_EOIR = 0x004; + +//--------------------------------------------------------------------------- +// +// Constant declarations (GIC IRQ) +// +//--------------------------------------------------------------------------- +const static int GIC_IRQLOCAL0 = (16 + 14); +const static int GIC_GPIO_IRQ = (32 + 116); // GPIO3 + +//--------------------------------------------------------------------------- +// +// Class definition +// +//--------------------------------------------------------------------------- +class GPIOBUS_Raspberry : public GPIOBUS +{ + public: + GPIOBUS_Raspberry() = default; + ~GPIOBUS_Raspberry() override = default; + bool Init(mode_e mode = mode_e::TARGET) override; + + void Reset() override; + void Cleanup() override; + + // Bus signal acquisition + uint32_t Acquire() override; + + // Set ENB signal + void SetENB(bool ast) override; + + // Get BSY signal + bool GetBSY() const override; + // Set BSY signal + void SetBSY(bool ast) override; + + // Get SEL signal + bool GetSEL() const override; + // Set SEL signal + void SetSEL(bool ast) override; + + // Get ATN signal + bool GetATN() const override; + // Set ATN signal + void SetATN(bool ast) override; + + // Get ACK signal + bool GetACK() const override; + // Set ACK signal + void SetACK(bool ast) override; + + // Get ACT signal + bool GetACT() const override; + // Set ACT signal + void SetACT(bool ast) override; + + // Get RST signal + bool GetRST() const override; + // Set RST signal + void SetRST(bool ast) override; + + // Get MSG signal + bool GetMSG() const override; + // Set MSG signal + void SetMSG(bool ast) override; + + // Get CD signal + bool GetCD() const override; + // Set CD signal + void SetCD(bool ast) override; + + // Get IO signal + bool GetIO() override; + // Set IO signal + void SetIO(bool ast) override; + + // Get REQ signal + bool GetREQ() const override; + // Set REQ signal + void SetREQ(bool ast) override; + + bool GetDP() const override; + + // Get DAT signal + uint8_t GetDAT() override; + // Set DAT signal + void SetDAT(uint8_t dat) override; + + bool WaitREQ(bool ast) override + { + return WaitSignal(PIN_REQ, ast); + } + bool WaitACK(bool ast) override + { + return WaitSignal(PIN_ACK, ast); + } + static uint32_t bcm_host_get_peripheral_address(); + + unique_ptr GetSample(uint64_t timestamp) override + { + Acquire(); + return make_unique(signals, timestamp); + } + + protected: + // All bus signals + uint32_t signals = 0; // NOSONAR: Must be protected (not private) for testability + // GPIO input level + volatile uint32_t *level = nullptr; // NOSONAR: Must be protected (not private) for testability + + private: + // 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 + { + // Not implemented (or needed for thist gpio bus type) + (void)pin; + return -1; + } + bool GetSignal(int pin) const override; + // Get SCSI input signal value + void SetSignal(int pin, bool ast) override; + // Set SCSI output signal value + + // Interrupt control + void DisableIRQ() override; + // IRQ Disabled + void EnableIRQ() override; + // IRQ Enabled + + // 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 + + static uint32_t get_dt_ranges(const char *filename, uint32_t offset); + + uint32_t baseaddr = 0; // Base address + + int rpitype = 0; // Type of Raspberry Pi + + // GPIO register + volatile uint32_t *gpio = nullptr; // NOSONAR: volatile needed for register access + // PADS register + volatile uint32_t *pads = nullptr; // NOSONAR: volatile needed for register access + + // Interrupt control register + volatile uint32_t *irpctl = nullptr; + + // Interrupt enabled state + volatile uint32_t irptenb; // NOSONAR: volatile needed for register access + + // QA7 register + volatile uint32_t *qa7regs = nullptr; + // Interupt control target CPU. + volatile int tintcore; // NOSONAR: volatile needed for register access + + // Interupt control + volatile uint32_t tintctl; // NOSONAR: volatile needed for register access + // GICC priority setting + volatile uint32_t giccpmr; // NOSONAR: volatile needed for register access + +#if !defined(__x86_64__) && !defined(__X86__) + // GIC Interrupt distributor register + volatile uint32_t *gicd = nullptr; +#endif + // GIC CPU interface register + volatile uint32_t *gicc = nullptr; + + // RAM copy of GPFSEL0-4 values (GPIO Function Select) + array gpfsel; + +#if SIGNAL_CONTROL_MODE == 0 + // Data mask table + array, 3> tblDatMsk; + // Data setting table + array, 3> tblDatSet; +#else + // Data mask table + array tblDatMsk = {}; + // Table setting table + array tblDatSet = {}; +#endif + + static const array SignalTable; + + const static int GPIO_FSEL_0 = 0; + const static int GPIO_FSEL_1 = 1; + const static int GPIO_FSEL_2 = 2; + const static int GPIO_FSEL_3 = 3; + const static int GPIO_SET_0 = 7; + const static int GPIO_CLR_0 = 10; + const static int GPIO_LEV_0 = 13; + const static int GPIO_EDS_0 = 16; + const static int GPIO_REN_0 = 19; + const static int GPIO_FEN_0 = 22; + const static int GPIO_HEN_0 = 25; + const static int GPIO_LEN_0 = 28; + const static int GPIO_AREN_0 = 31; + const static int GPIO_AFEN_0 = 34; + const static int GPIO_PUD = 37; + const static int GPIO_CLK_0 = 38; + const static int GPIO_GPPINMUXSD = 52; + const static int GPIO_PUPPDN0 = 57; + const static int GPIO_PUPPDN1 = 58; + const static int GPIO_PUPPDN3 = 59; + const static int GPIO_PUPPDN4 = 60; + const static int PAD_0_27 = 11; + const static int IRPT_PND_IRQ_B = 0; + const static int IRPT_PND_IRQ_1 = 1; + const static int IRPT_PND_IRQ_2 = 2; + const static int IRPT_FIQ_CNTL = 3; + const static int IRPT_ENB_IRQ_1 = 4; + const static int IRPT_ENB_IRQ_2 = 5; + const static int IRPT_ENB_IRQ_B = 6; + const static int IRPT_DIS_IRQ_1 = 7; + const static int IRPT_DIS_IRQ_2 = 8; + const static int IRPT_DIS_IRQ_B = 9; + const static int QA7_CORE0_TINTC = 16; + const static int GPIO_IRQ = (32 + 20); // GPIO3 + + const static uint32_t IRPT_OFFSET = 0x0000B200; + const static uint32_t PADS_OFFSET = 0x00100000; + const static uint32_t GPIO_OFFSET = 0x00200000; + const static uint32_t QA7_OFFSET = 0x01000000; +}; diff --git a/cpp/hal/gpiobus_standard.h b/cpp/hal/gpiobus_standard.h deleted file mode 100644 index 502d1e99..00000000 --- a/cpp/hal/gpiobus_standard.h +++ /dev/null @@ -1,56 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI Target Emulator RaSCSI Reloaded -// for Raspberry Pi -// -// Powered by XM6 TypeG Technology. -// Copyright (C) 2016-2020 GIMONS -// -//--------------------------------------------------------------------------- - -#pragma once - -#include - -// -// RaSCSI standard (SCSI logic, standard pin assignment) -// - -const std::string CONNECT_DESC = "STANDARD"; // Startup message - -// Select signal control mode -const static int SIGNAL_CONTROL_MODE = 0; // SCSI logical specification - -// Control signal pin assignment (-1 means no control) -const static int PIN_ACT = 4; // ACTIVE -const static int PIN_ENB = 5; // ENABLE -const static int PIN_IND = -1; // INITIATOR CTRL DIRECTION -const static int PIN_TAD = -1; // TARGET CTRL DIRECTION -const static int PIN_DTD = -1; // DATA DIRECTION - -// Control signal output logic -#define ACT_ON ON // ACTIVE SIGNAL ON -#define ENB_ON ON // ENABLE SIGNAL ON -#define IND_IN OFF // INITIATOR SIGNAL INPUT -#define TAD_IN OFF // TARGET SIGNAL INPUT -#define DTD_IN ON // DATA SIGNAL INPUT - -// SCSI signal pin assignment -const static int PIN_DT0 = 10; // Data 0 -const static int PIN_DT1 = 11; // Data 1 -const static int PIN_DT2 = 12; // Data 2 -const static int PIN_DT3 = 13; // Data 3 -const static int PIN_DT4 = 14; // Data 4 -const static int PIN_DT5 = 15; // Data 5 -const static int PIN_DT6 = 16; // Data 6 -const static int PIN_DT7 = 17; // Data 7 -const static int PIN_DP = 18; // Data parity -const static int PIN_ATN = 19; // ATN -const static int PIN_RST = 20; // RST -const static int PIN_ACK = 21; // ACK -const static int PIN_REQ = 22; // REQ -const static int PIN_MSG = 23; // MSG -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 diff --git a/cpp/hal/gpiobus_virtual.cpp b/cpp/hal/gpiobus_virtual.cpp new file mode 100644 index 00000000..4dc98f14 --- /dev/null +++ b/cpp/hal/gpiobus_virtual.cpp @@ -0,0 +1,564 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// 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 +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +bool GPIOBUS_Virtual::Init(mode_e mode) +{ + GPIO_FUNCTION_TRACE + GPIOBUS::Init(mode); + +#ifdef SHARED_MEMORY_GPIO + // Create a shared memory region that can be accessed as a virtual "SCSI bus" + // mutual exclusion semaphore, mutex_sem with an initial value 0. + if ((mutex_sem = sem_open(SHARED_MEM_MUTEX_NAME.c_str(), O_CREAT, 0660, 0)) == SEM_FAILED) { + LOGERROR("Unable to open shared memory semaphore %s. Are you running as root?", SHARED_MEM_MUTEX_NAME.c_str()); + } + // Get shared memory + if ((fd_shm = shm_open(SHARED_MEM_NAME.c_str(), O_RDWR | O_CREAT | O_EXCL, 0660)) == -1) { + LOGERROR("Unable to open shared memory %s. Are you running as root?", SHARED_MEM_NAME.c_str()); + sem_close(mutex_sem); + } + if (ftruncate(fd_shm, sizeof(uint32_t)) == -1) { + LOGERROR("Unable to read shared memory"); + sem_close(mutex_sem); + shm_unlink(SHARED_MEM_NAME.c_str()); + return false; + } + + signals = static_cast(mmap(NULL, sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd_shm, 0)); + if (static_cast(signals) == MAP_FAILED) { + LOGERROR("Unabled to map shared memory"); + sem_close(mutex_sem); + shm_unlink(SHARED_MEM_NAME.c_str()); + } +#else + signals = make_shared(0); +#endif + + return true; +} + +void GPIOBUS_Virtual::Cleanup() +{ + // Set control signals + PinSetSignal(PIN_ENB, OFF); + PinSetSignal(PIN_ACT, OFF); + PinSetSignal(PIN_TAD, OFF); + PinSetSignal(PIN_IND, OFF); + PinSetSignal(PIN_DTD, OFF); + PinConfig(PIN_ACT, GPIO_INPUT); + PinConfig(PIN_TAD, GPIO_INPUT); + PinConfig(PIN_IND, GPIO_INPUT); + PinConfig(PIN_DTD, GPIO_INPUT); + + // Initialize all signals + for (int i = 0; SignalTable[i] >= 0; i++) { + int pin = SignalTable[i]; + PinSetSignal(pin, OFF); + PinConfig(pin, GPIO_INPUT); + PullConfig(pin, GPIO_PULLNONE); + } + +#ifdef SHARED_MEMORY_GPIO + munmap(static_cast(signals), sizeof(uint32_t)); + shm_unlink(SHARED_MEM_NAME.c_str()); + sem_close(mutex_sem); +#endif +} + +void GPIOBUS_Virtual::Reset() +{ +#if defined(__x86_64__) || defined(__X86__) + return; +#else + int i; + int j; + + // Turn off active signal + SetControl(PIN_ACT, ACT_OFF); + + // Set all signals to off + for (i = 0;; i++) { + j = SignalTable[i]; + if (j < 0) { + break; + } + + SetSignal(j, OFF); + } + + if (actmode == mode_e::TARGET) { + // Target mode + + // Set target signal to input + SetControl(PIN_TAD, TAD_IN); + SetMode(PIN_BSY, IN); + SetMode(PIN_MSG, IN); + SetMode(PIN_CD, IN); + SetMode(PIN_REQ, IN); + SetMode(PIN_IO, IN); + + // Set the initiator signal to input + SetControl(PIN_IND, IND_IN); + SetMode(PIN_SEL, IN); + SetMode(PIN_ATN, IN); + SetMode(PIN_ACK, IN); + SetMode(PIN_RST, IN); + + // Set data bus signals to input + SetControl(PIN_DTD, DTD_IN); + SetMode(PIN_DT0, IN); + SetMode(PIN_DT1, IN); + SetMode(PIN_DT2, IN); + SetMode(PIN_DT3, IN); + SetMode(PIN_DT4, IN); + SetMode(PIN_DT5, IN); + SetMode(PIN_DT6, IN); + SetMode(PIN_DT7, IN); + SetMode(PIN_DP, IN); + } else { + // Initiator mode + + // Set target signal to input + SetControl(PIN_TAD, TAD_IN); + SetMode(PIN_BSY, IN); + SetMode(PIN_MSG, IN); + SetMode(PIN_CD, IN); + SetMode(PIN_REQ, IN); + SetMode(PIN_IO, IN); + + // Set the initiator signal to output + SetControl(PIN_IND, IND_OUT); + SetMode(PIN_SEL, OUT); + SetMode(PIN_ATN, OUT); + SetMode(PIN_ACK, OUT); + SetMode(PIN_RST, OUT); + + // Set the data bus signals to output + SetControl(PIN_DTD, DTD_OUT); + SetMode(PIN_DT0, OUT); + SetMode(PIN_DT1, OUT); + SetMode(PIN_DT2, OUT); + SetMode(PIN_DT3, OUT); + SetMode(PIN_DT4, OUT); + SetMode(PIN_DT5, OUT); + SetMode(PIN_DT6, OUT); + SetMode(PIN_DT7, OUT); + SetMode(PIN_DP, OUT); + } + + // Initialize all signals + signals = 0; +#endif // ifdef __x86_64__ || __X86__ +} + +void GPIOBUS_Virtual::SetENB(bool ast) +{ + PinSetSignal(PIN_ENB, ast ? ENB_ON : ENB_OFF); +} + +bool GPIOBUS_Virtual::GetBSY() const +{ + return GetSignal(PIN_BSY); +} + +void GPIOBUS_Virtual::SetBSY(bool ast) +{ + // Set BSY signal + SetSignal(PIN_BSY, ast); + + if (actmode == mode_e::TARGET) { + if (ast) { + // Turn on ACTIVE signal + SetControl(PIN_ACT, ACT_ON); + + // Set Target signal to output + SetControl(PIN_TAD, TAD_OUT); + + SetMode(PIN_BSY, OUT); + SetMode(PIN_MSG, OUT); + SetMode(PIN_CD, OUT); + SetMode(PIN_REQ, OUT); + SetMode(PIN_IO, OUT); + } else { + // Turn off the ACTIVE signal + SetControl(PIN_ACT, ACT_OFF); + + // Set the target signal to input + SetControl(PIN_TAD, TAD_IN); + + SetMode(PIN_BSY, IN); + SetMode(PIN_MSG, IN); + SetMode(PIN_CD, IN); + SetMode(PIN_REQ, IN); + SetMode(PIN_IO, IN); + } + } +} + +bool GPIOBUS_Virtual::GetSEL() const +{ + return GetSignal(PIN_SEL); +} + +void GPIOBUS_Virtual::SetSEL(bool ast) +{ + if (actmode == mode_e::INITIATOR && ast) { + // Turn on ACTIVE signal + SetControl(PIN_ACT, ACT_ON); + } + + // Set SEL signal + SetSignal(PIN_SEL, ast); +} + +bool GPIOBUS_Virtual::GetATN() const +{ + return GetSignal(PIN_ATN); +} + +void GPIOBUS_Virtual::SetATN(bool ast) +{ + SetSignal(PIN_ATN, ast); +} + +bool GPIOBUS_Virtual::GetACK() const +{ + return GetSignal(PIN_ACK); +} + +void GPIOBUS_Virtual::SetACK(bool ast) +{ + SetSignal(PIN_ACK, ast); +} + +bool GPIOBUS_Virtual::GetACT() const +{ + return GetSignal(PIN_ACT); +} + +void GPIOBUS_Virtual::SetACT(bool ast) +{ + SetSignal(PIN_ACT, ast); +} + +bool GPIOBUS_Virtual::GetRST() const +{ + return GetSignal(PIN_RST); +} + +void GPIOBUS_Virtual::SetRST(bool ast) +{ + SetSignal(PIN_RST, ast); +} + +bool GPIOBUS_Virtual::GetMSG() const +{ + return GetSignal(PIN_MSG); +} + +void GPIOBUS_Virtual::SetMSG(bool ast) +{ + SetSignal(PIN_MSG, ast); +} + +bool GPIOBUS_Virtual::GetCD() const +{ + return GetSignal(PIN_CD); +} + +void GPIOBUS_Virtual::SetCD(bool ast) +{ + SetSignal(PIN_CD, ast); +} + +bool GPIOBUS_Virtual::GetIO() +{ + bool ast = GetSignal(PIN_IO); + + if (actmode == mode_e::INITIATOR) { + // Change the data input/output direction by IO signal + if (ast) { + SetControl(PIN_DTD, DTD_IN); + SetMode(PIN_DT0, IN); + SetMode(PIN_DT1, IN); + SetMode(PIN_DT2, IN); + SetMode(PIN_DT3, IN); + SetMode(PIN_DT4, IN); + SetMode(PIN_DT5, IN); + SetMode(PIN_DT6, IN); + SetMode(PIN_DT7, IN); + SetMode(PIN_DP, IN); + } else { + SetControl(PIN_DTD, DTD_OUT); + SetMode(PIN_DT0, OUT); + SetMode(PIN_DT1, OUT); + SetMode(PIN_DT2, OUT); + SetMode(PIN_DT3, OUT); + SetMode(PIN_DT4, OUT); + SetMode(PIN_DT5, OUT); + SetMode(PIN_DT6, OUT); + SetMode(PIN_DT7, OUT); + SetMode(PIN_DP, OUT); + } + } + + return ast; +} + +void GPIOBUS_Virtual::SetIO(bool ast) +{ + SetSignal(PIN_IO, ast); + + if (actmode == mode_e::TARGET) { + // Change the data input/output direction by IO signal + if (ast) { + SetControl(PIN_DTD, DTD_OUT); + SetDAT(0); + SetMode(PIN_DT0, OUT); + SetMode(PIN_DT1, OUT); + SetMode(PIN_DT2, OUT); + SetMode(PIN_DT3, OUT); + SetMode(PIN_DT4, OUT); + SetMode(PIN_DT5, OUT); + SetMode(PIN_DT6, OUT); + SetMode(PIN_DT7, OUT); + SetMode(PIN_DP, OUT); + } else { + SetControl(PIN_DTD, DTD_IN); + SetMode(PIN_DT0, IN); + SetMode(PIN_DT1, IN); + SetMode(PIN_DT2, IN); + SetMode(PIN_DT3, IN); + SetMode(PIN_DT4, IN); + SetMode(PIN_DT5, IN); + SetMode(PIN_DT6, IN); + SetMode(PIN_DT7, IN); + SetMode(PIN_DP, IN); + } + } +} + +bool GPIOBUS_Virtual::GetREQ() const +{ + return GetSignal(PIN_REQ); +} + +void GPIOBUS_Virtual::SetREQ(bool ast) +{ + SetSignal(PIN_REQ, ast); +} + +bool GPIOBUS_Virtual::GetDP() const +{ + return GetSignal(PIN_DP); +} + +//--------------------------------------------------------------------------- +// +// Get data signals +// +//--------------------------------------------------------------------------- +uint8_t GPIOBUS_Virtual::GetDAT() +{ + GPIO_FUNCTION_TRACE + uint32_t data = Acquire(); + data = ((data >> (PIN_DT0 - 0)) & (1 << 0)) | ((data >> (PIN_DT1 - 1)) & (1 << 1)) | + ((data >> (PIN_DT2 - 2)) & (1 << 2)) | ((data >> (PIN_DT3 - 3)) & (1 << 3)) | + ((data >> (PIN_DT4 - 4)) & (1 << 4)) | ((data >> (PIN_DT5 - 5)) & (1 << 5)) | + ((data >> (PIN_DT6 - 6)) & (1 << 6)) | ((data >> (PIN_DT7 - 7)) & (1 << 7)); + return (uint8_t)data; +} + +//--------------------------------------------------------------------------- +// +// Set data signals +// +//--------------------------------------------------------------------------- +void GPIOBUS_Virtual::SetDAT(uint8_t dat) +{ + GPIO_FUNCTION_TRACE + + auto new_dat = static_cast(dat); + + PinSetSignal(PIN_DT0, (new_dat & ((byte)1 << 0)) != (byte)0); + PinSetSignal(PIN_DT1, (new_dat & ((byte)1 << 1)) != (byte)0); + PinSetSignal(PIN_DT2, (new_dat & ((byte)1 << 2)) != (byte)0); + PinSetSignal(PIN_DT3, (new_dat & ((byte)1 << 3)) != (byte)0); + PinSetSignal(PIN_DT4, (new_dat & ((byte)1 << 4)) != (byte)0); + PinSetSignal(PIN_DT5, (new_dat & ((byte)1 << 5)) != (byte)0); + PinSetSignal(PIN_DT6, (new_dat & ((byte)1 << 6)) != (byte)0); + PinSetSignal(PIN_DT7, (new_dat & ((byte)1 << 7)) != (byte)0); +} + +//--------------------------------------------------------------------------- +// +// Create work table +// +//--------------------------------------------------------------------------- +void GPIOBUS_Virtual::MakeTable(void) +{ + GPIO_FUNCTION_TRACE +} + +//--------------------------------------------------------------------------- +// +// Control signal setting +// +//--------------------------------------------------------------------------- +void GPIOBUS_Virtual::SetControl(int pin, bool ast) +{ + LOGTRACE("%s hwpin: %d", __PRETTY_FUNCTION__, (int)pin) + PinSetSignal(pin, ast); +} + +//--------------------------------------------------------------------------- +// +// Input/output mode setting +// +//--------------------------------------------------------------------------- +void GPIOBUS_Virtual::SetMode(int hw_pin, int mode) +{ + // Doesn't do anything for virtual gpio bus + (void)hw_pin; + (void)mode; +} + +//--------------------------------------------------------------------------- +// +// Get input signal value +// +//--------------------------------------------------------------------------- +bool GPIOBUS_Virtual::GetSignal(int hw_pin) const +{ + GPIO_FUNCTION_TRACE + + uint32_t signal_value = 0; +#ifdef SHARED_MEMORY_GPIO + if (sem_wait(mutex_sem) == -1) { + LOGERROR("Unable to lock the shared memory") + return false; + } +#endif + signal_value = *signals; +#ifdef SHARED_MEMORY_GPIO + if (sem_post(mutex_sem) == -1) { + LOGERROR("Unable to release the shared memory") + return false; + } +#endif + return (signal_value >> hw_pin) & 1; +} + +//--------------------------------------------------------------------------- +// +// Set output signal value +// +//--------------------------------------------------------------------------- +void GPIOBUS_Virtual::SetSignal(int hw_pin, bool ast) +{ + GPIO_FUNCTION_TRACE + PinSetSignal(hw_pin, ast); +} + +void GPIOBUS_Virtual::DisableIRQ() +{ + GPIO_FUNCTION_TRACE + // Nothing to do for virtual gpio bus +} + +void GPIOBUS_Virtual::EnableIRQ() +{ + GPIO_FUNCTION_TRACE + // Nothing to do for virtual gpio bus +} + +//--------------------------------------------------------------------------- +// +// Set output pin +// +//--------------------------------------------------------------------------- +void GPIOBUS_Virtual::PinSetSignal(int hw_pin, bool ast) +{ + LOGTRACE("%s hwpin: %d", __PRETTY_FUNCTION__, (int)hw_pin) + + // Check for invalid pin + if (hw_pin < 0) { + return; + } +#ifdef SHARED_MEMORY_GPIO + if (sem_wait(mutex_sem) == -1) { + LOGERROR("Unable to lock the shared memory") + return; + } +#endif + if (ast) { + // Set the "gpio" bit + *signals |= 0x1 << hw_pin; + } else { + // Clear the "gpio" bit + *signals ^= ~(0x1 << hw_pin); + } +#ifdef SHARED_MEMORY_GPIO + if (sem_post(mutex_sem) == -1) { + LOGERROR("Unable to release the shared memory") + return; + } +#endif +} + +//--------------------------------------------------------------------------- +// +// Set the signal drive strength +// +//--------------------------------------------------------------------------- +void GPIOBUS_Virtual::DrvConfig(uint32_t drive) +{ + (void)drive; + // Nothing to do for virtual GPIO +} + +uint32_t GPIOBUS_Virtual::Acquire() +{ + GPIO_FUNCTION_TRACE; + + uint32_t signal_value = 0; +#ifdef SHARED_MEMORY_GPIO + if (sem_wait(mutex_sem) == -1) { + LOGERROR("Unable to lock the shared memory") + return false; + } +#endif + signal_value = *signals; +#ifdef SHARED_MEMORY_GPIO + if (sem_post(mutex_sem) == -1) { + LOGERROR("Unable to release the shared memory") + return false; + } +#endif + return signal_value; +} + diff --git a/cpp/hal/gpiobus_virtual.h b/cpp/hal/gpiobus_virtual.h new file mode 100644 index 00000000..feec3d8c --- /dev/null +++ b/cpp/hal/gpiobus_virtual.h @@ -0,0 +1,174 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// [ GPIO-SCSI bus ] +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "hal/data_sample_raspberry.h" +#include "hal/gpiobus.h" +#include "shared/log.h" +#include "shared/scsi.h" + +#include +#include + +//--------------------------------------------------------------------------- +// +// Class definition +// +//--------------------------------------------------------------------------- +class GPIOBUS_Virtual final : public GPIOBUS +{ + public: + // Basic Functions + GPIOBUS_Virtual() = default; + ~GPIOBUS_Virtual() override = default; + // Destructor + bool Init(mode_e mode = mode_e::TARGET) override; + + // // Initialization + void Reset() override; + // Reset + void Cleanup() override; + // Cleanup + + //--------------------------------------------------------------------------- + // + // 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; + + bool WaitREQ(bool ast) override + { + return WaitSignal(PIN_REQ, ast); + } + bool WaitACK(bool ast) override + { + return WaitSignal(PIN_ACK, ast); + } + + uint8_t GetDAT() override; + // Get DAT signal + void SetDAT(uint8_t dat) override; + // Set DAT signal + private: + // 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 + { + // Not implemented (or needed for thist gpio bus type) + (void)pin; + return -1; + } + bool GetSignal(int pin) const override; + // Get SCSI input signal value + void SetSignal(int pin, bool ast) override; + // Wait for a signal to change + // Interrupt control + void DisableIRQ() override; + // IRQ Disabled + void EnableIRQ() override; + // IRQ Enabled + + // GPIO pin functionality settings + void PinConfig(int pin, int mode) override + { + (void)pin; + (void)mode; + } + // GPIO pin direction setting + void PullConfig(int pin, int mode) override + { + (void)mode; // Put these in opposite order so Sonar doesn't complain + (void)pin; // That PinConfig and PullConfig are identical + } + // 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 + + array SignalTable; + shared_ptr signals; // All bus signals + + unique_ptr GetSample(uint64_t timestamp) override + { + return make_unique(*signals, timestamp); + } + +#ifdef SHARED_MEMORY_GPIO + inline static const string SHARED_MEM_MUTEX_NAME = "/sem-mutex"; + inline static const string SHARED_MEM_NAME = "/posix-shared-mem-example"; + + sem_t *mutex_sem, *buffer_count_sem, *spool_signal_sem; + int fd_shm, fd_log; +#endif +}; \ No newline at end of file diff --git a/cpp/hal/pi_defs/bpi-gpio.h b/cpp/hal/pi_defs/bpi-gpio.h new file mode 100644 index 00000000..89541fe8 --- /dev/null +++ b/cpp/hal/pi_defs/bpi-gpio.h @@ -0,0 +1,356 @@ +/* +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; diff --git a/cpp/hal/pi_defs/bpi-m2p.h b/cpp/hal/pi_defs/bpi-m2p.h new file mode 100755 index 00000000..096fd260 --- /dev/null +++ b/cpp/hal/pi_defs/bpi-m2p.h @@ -0,0 +1,71 @@ +/* +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; diff --git a/cpp/hal/pin_control.h b/cpp/hal/pin_control.h new file mode 100644 index 00000000..70ce6365 --- /dev/null +++ b/cpp/hal/pin_control.h @@ -0,0 +1,68 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 akuker +// +//--------------------------------------------------------------------------- + +#include + +#pragma once + +// Virtual functions that must be implemented by the derived gpiobus classes +// to control the GPIO pins +class PinControl +{ + public: + virtual bool GetBSY() const = 0; + virtual void SetBSY(bool ast) = 0; + + virtual bool GetSEL() const = 0; + virtual void SetSEL(bool ast) = 0; + + virtual bool GetATN() const = 0; + virtual void SetATN(bool ast) = 0; + + virtual bool GetACK() const = 0; + virtual void SetACK(bool ast) = 0; + + virtual bool GetRST() const = 0; + virtual void SetRST(bool ast) = 0; + + virtual bool GetMSG() const = 0; + virtual void SetMSG(bool ast) = 0; + + virtual bool GetCD() const = 0; + virtual void SetCD(bool ast) = 0; + + virtual bool GetIO() = 0; + virtual void SetIO(bool ast) = 0; + + virtual bool GetREQ() const = 0; + virtual void SetREQ(bool ast) = 0; + + virtual bool GetACT() const = 0; + virtual void SetACT(bool ast) = 0; + + virtual uint8_t GetDAT() = 0; + virtual void SetDAT(uint8_t dat) = 0; + + // Set ENB signal + virtual void SetENB(bool ast) = 0; + + // Get parity signal + virtual bool GetDP() const = 0; + + // GPIO pin direction setting + virtual void PinConfig(int pin, int mode) = 0; + // GPIO pin pull up/down resistor setting + virtual void PullConfig(int pin, int mode) = 0; + + virtual void SetControl(int pin, bool ast) = 0; + virtual void SetMode(int pin, int mode) = 0; + + PinControl() = default; + virtual ~PinControl() = default; +}; \ No newline at end of file diff --git a/cpp/hal/sbc_version.cpp b/cpp/hal/sbc_version.cpp new file mode 100644 index 00000000..2d5237ca --- /dev/null +++ b/cpp/hal/sbc_version.cpp @@ -0,0 +1,226 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 akuker +// +// [ Hardware version detection routines ] +// +//--------------------------------------------------------------------------- + +#include "sbc_version.h" +#include "shared/log.h" +#include +#include +#include + +SBC_Version::sbc_version_type SBC_Version::m_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"; + +// The strings in this table should align with the 'model' embedded +// in the device tree. This can be aquired by running: +// cat /proc/device-tree/model +// Only the first part of the string is checked. Anything following +// will be ignored. For example: +// "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> SBC_Version::m_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, +}; + +const std::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() +{ + switch (m_sbc_version) { + case sbc_version_type::sbc_raspberry_pi_1: + return &m_str_raspberry_pi_1; + case sbc_version_type::sbc_raspberry_pi_2_3: + return &m_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; + default: + LOGERROR("Unknown type of sbc detected: %d", static_cast(m_sbc_version)) + return &m_str_unknown_sbc; + } +} + +SBC_Version::sbc_version_type SBC_Version::GetSbcVersion() +{ + return m_sbc_version; +} + +//--------------------------------------------------------------------------- +// +// Determine which version of single board computer (Pi) is being used +// based upon the device tree model string. +// +//--------------------------------------------------------------------------- +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); + + 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; + 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"); +#endif + } + + std::stringstream str_buffer; + str_buffer << input_stream.rdbuf(); + 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; + } + } + 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; +} + +bool SBC_Version::IsRaspberryPi() +{ + 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 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; + } +} + +// The following functions are only used on the Raspberry Pi +// (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 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); + } + return address; +} + +#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__ +uint32_t SBC_Version::GetPeripheralAddress(void) +{ + char buf[1024]; + size_t len = sizeof(buf); + uint32_t address; + + if (sysctlbyname("hw.model", buf, &len, NULL, 0) || strstr(buf, "ARM1176JZ-S") != buf) { + // Failed to get CPU model || Not BCM2835 + // use the address of BCM283[67] + address = 0x3f000000; + } else { + // Use BCM2835 address + address = 0x20000000; + } + LOGDEBUG("Peripheral address : 0x%lx\n", address); + return address; +} +#else +uint32_t SBC_Version::GetPeripheralAddress(void) +{ + return 0; +} +#endif diff --git a/cpp/hal/sbc_version.h b/cpp/hal/sbc_version.h new file mode 100644 index 00000000..34f12a6c --- /dev/null +++ b/cpp/hal/sbc_version.h @@ -0,0 +1,78 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 akuker +// +// [ Hardware version detection routines ] +// +//--------------------------------------------------------------------------- + +#pragma once + +#include +#include + +//=========================================================================== +// +// Single Board Computer Versions +// +//=========================================================================== +class SBC_Version +{ + public: + // Type of Single Board Computer + enum class sbc_version_type : uint8_t { + 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_Version() = delete; + ~SBC_Version() = delete; + + static void Init(); + + static sbc_version_type GetSbcVersion(); + + static bool IsRaspberryPi(); + static bool IsBananaPi(); + + static const std::string *GetString(); + + static uint32_t GetPeripheralAddress(); + + private: + static sbc_version_type m_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 std::map> m_proc_device_tree_mapping; + + static const std::string m_device_tree_model_path; + + static uint32_t GetDeviceTreeRanges(const char *filename, uint32_t offset); +}; diff --git a/cpp/hal/sunxi_utils.cpp b/cpp/hal/sunxi_utils.cpp new file mode 100644 index 00000000..16ad4ecc --- /dev/null +++ b/cpp/hal/sunxi_utils.cpp @@ -0,0 +1,65 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// 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 +#include + +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()); +} \ No newline at end of file diff --git a/cpp/hal/sunxi_utils.h b/cpp/hal/sunxi_utils.h new file mode 100644 index 00000000..26a1016b --- /dev/null +++ b/cpp/hal/sunxi_utils.h @@ -0,0 +1,189 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// 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 +#ifndef __arm__ +#include +#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 +}; \ No newline at end of file diff --git a/cpp/hal/systimer.cpp b/cpp/hal/systimer.cpp index bdcec4e2..7804a1e9 100644 --- a/cpp/hal/systimer.cpp +++ b/cpp/hal/systimer.cpp @@ -12,129 +12,55 @@ //--------------------------------------------------------------------------- #include "hal/systimer.h" -#include "hal/gpiobus.h" -#include "shared/config.h" +#include "hal/systimer_allwinner.h" +#include "hal/systimer_raspberry.h" #include -#include -#include -#include -#include -//--------------------------------------------------------------------------- -// -// System timer address -// -//--------------------------------------------------------------------------- -volatile uint32_t* SysTimer::systaddr; +#include "hal/gpiobus.h" +#include "hal/sbc_version.h" -//--------------------------------------------------------------------------- -// -// ARM timer address -// -//--------------------------------------------------------------------------- -volatile uint32_t* SysTimer::armtaddr; +#include "shared/log.h" -//--------------------------------------------------------------------------- -// -// Core frequency -// -//--------------------------------------------------------------------------- -volatile uint32_t SysTimer::corefreq; +bool SysTimer::initialized = false; +bool SysTimer::is_allwinnner = false; +bool SysTimer::is_raspberry = false; -//--------------------------------------------------------------------------- -// -// Initialize the system timer -// -//--------------------------------------------------------------------------- -void SysTimer::Init(uint32_t *syst, uint32_t *armt) +std::unique_ptr SysTimer::systimer_ptr; + +void SysTimer::Init() { - // RPI Mailbox property interface - // Get max clock rate - // Tag: 0x00030004 - // - // Request: Length: 4 - // Value: u32: clock id - // Response: Length: 8 - // Value: u32: clock id, u32: rate (in Hz) - // - // Clock id - // 0x000000004: CORE - array maxclock = { 32, 0, 0x00030004, 8, 0, 4, 0, 0 }; + LOGTRACE("%s", __PRETTY_FUNCTION__) - // Save the base address - systaddr = syst; - armtaddr = armt; - - // Change the ARM timer to free run mode - armtaddr[ARMT_CTRL] = 0x00000282; - - // Get the core frequency - corefreq = 0; - int fd = open("/dev/vcio", O_RDONLY); - if (fd >= 0) { - ioctl(fd, _IOWR(100, 0, char *), maxclock); - corefreq = maxclock[6] / 1000000; - } - close(fd); + if (!initialized) { + if (SBC_Version::IsRaspberryPi()) { + systimer_ptr = make_unique(); + is_raspberry = true; + } else if (SBC_Version::IsBananaPi()) { + systimer_ptr = make_unique(); + is_allwinnner = true; + } + systimer_ptr->Init(); + initialized = true; + } } -//--------------------------------------------------------------------------- -// -// Get system timer low byte -// -//--------------------------------------------------------------------------- -uint32_t SysTimer::GetTimerLow() { - return systaddr[SYST_CLO]; +// Get system timer low byte +uint32_t SysTimer::GetTimerLow() +{ + return systimer_ptr->GetTimerLow(); } - -//--------------------------------------------------------------------------- -// -// Get system timer high byte -// -//--------------------------------------------------------------------------- -uint32_t SysTimer::GetTimerHigh() { - return systaddr[SYST_CHI]; +// Get system timer high byte +uint32_t SysTimer::GetTimerHigh() +{ + return systimer_ptr->GetTimerHigh(); } - -//--------------------------------------------------------------------------- -// -// Sleep in nanoseconds -// -//--------------------------------------------------------------------------- +// Sleep for N nanoseconds void SysTimer::SleepNsec(uint32_t nsec) { - // If time is 0, don't do anything - if (nsec == 0) { - return; - } - - // Calculate the timer difference - uint32_t diff = corefreq * nsec / 1000; - - // Return if the difference in time is too small - if (diff == 0) { - return; - } - - // Start - uint32_t start = armtaddr[ARMT_FREERUN]; - - // Loop until timer has elapsed - while ((armtaddr[ARMT_FREERUN] - start) < diff); + systimer_ptr->SleepNsec(nsec); } - -//--------------------------------------------------------------------------- -// -// Sleep in microseconds -// -//--------------------------------------------------------------------------- +// Sleep for N microseconds void SysTimer::SleepUsec(uint32_t usec) { - // If time is 0, don't do anything - if (usec == 0) { - return; - } - - uint32_t now = GetTimerLow(); - while ((GetTimerLow() - now) < usec); + systimer_ptr->SleepUsec(usec); } diff --git a/cpp/hal/systimer.h b/cpp/hal/systimer.h index 7ff5f459..f1cbd49e 100644 --- a/cpp/hal/systimer.h +++ b/cpp/hal/systimer.h @@ -13,11 +13,30 @@ #pragma once +#include #include -#include "shared/config.h" #include "shared/scsi.h" +class PlatformSpecificTimer +{ + public: + // Default constructor + PlatformSpecificTimer() = default; + // Default destructor + virtual ~PlatformSpecificTimer() = default; + // Initialization + virtual void Init() = 0; + // Get system timer low byte + virtual uint32_t GetTimerLow() = 0; + // Get system timer high byte + virtual uint32_t GetTimerHigh() = 0; + // Sleep for N nanoseconds + virtual void SleepNsec(uint32_t nsec) = 0; + // Sleep for N microseconds + virtual void SleepUsec(uint32_t usec) = 0; +}; + //=========================================================================== // // System timer @@ -25,23 +44,21 @@ //=========================================================================== class SysTimer { -public: - static void Init(uint32_t *syst, uint32_t *armt); - // Initialization - static uint32_t GetTimerLow(); - // Get system timer low byte - static uint32_t GetTimerHigh(); - // Get system timer high byte - static void SleepNsec(uint32_t nsec); - // Sleep for N nanoseconds - static void SleepUsec(uint32_t usec); - // Sleep for N microseconds + public: + static void Init(); + // Get system timer low byte + static uint32_t GetTimerLow(); + // Get system timer high byte + static uint32_t GetTimerHigh(); + // Sleep for N nanoseconds + static void SleepNsec(uint32_t nsec); + // Sleep for N microseconds + static void SleepUsec(uint32_t usec); -private: - static volatile uint32_t *systaddr; - // System timer address - static volatile uint32_t *armtaddr; - // ARM timer address - static volatile uint32_t corefreq; - // Core frequency + private: + static bool initialized; + static bool is_allwinnner; + static bool is_raspberry; + + static std::unique_ptr systimer_ptr; }; diff --git a/cpp/hal/systimer_allwinner.cpp b/cpp/hal/systimer_allwinner.cpp new file mode 100644 index 00000000..8cbe64fa --- /dev/null +++ b/cpp/hal/systimer_allwinner.cpp @@ -0,0 +1,155 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 akuker +// +// [ High resolution timer for the Allwinner series of SoC's] +// +//--------------------------------------------------------------------------- + +#include "hal/systimer_allwinner.h" +#include + +#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() +{ + // RaSCSI 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) + ; +} diff --git a/cpp/hal/systimer_allwinner.h b/cpp/hal/systimer_allwinner.h new file mode 100644 index 00000000..ffd81b19 --- /dev/null +++ b/cpp/hal/systimer_allwinner.h @@ -0,0 +1,106 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 akuker +// +// [ High resolution timer ] +// +//--------------------------------------------------------------------------- +#pragma once + +#include "systimer.h" +#include +#include + +//=========================================================================== +// +// 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; +}; diff --git a/cpp/hal/systimer_raspberry.cpp b/cpp/hal/systimer_raspberry.cpp new file mode 100644 index 00000000..5d2966db --- /dev/null +++ b/cpp/hal/systimer_raspberry.cpp @@ -0,0 +1,147 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// Copyright (C) 2022 akuker +// +// [ High resolution timer ] +// +//--------------------------------------------------------------------------- + +#include "hal/systimer_raspberry.h" +#include +#include +#include + +#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; + +//--------------------------------------------------------------------------- +// +// Initialize the system timer +// +//--------------------------------------------------------------------------- +void SysTimer_Raspberry::Init() +{ + // Get the base address + 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?") + 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") + close(mem_fd); + return; + } + close(mem_fd); + + // RPI Mailbox property interface + // Get max clock rate + // Tag: 0x00030004 + // + // Request: Length: 4 + // Value: u32: clock id + // Response: Length: 8 + // Value: u32: clock id, u32: rate (in Hz) + // + // Clock id + // 0x000000004: CORE + std::array maxclock = {32, 0, 0x00030004, 8, 0, 4, 0, 0}; + + // Save the base address + systaddr = (uint32_t *)map + SYST_OFFSET / sizeof(uint32_t); + armtaddr = (uint32_t *)map + ARMT_OFFSET / sizeof(uint32_t); + + // Change the ARM timer to free run mode + armtaddr[ARMT_CTRL] = 0x00000282; + + // Get the core frequency + if (int vcio_fd = open("/dev/vcio", O_RDONLY); vcio_fd >= 0) { + ioctl(vcio_fd, _IOWR(100, 0, char *), maxclock.data()); + corefreq = maxclock[6] / 1000000; + close(vcio_fd); + } +} + +//--------------------------------------------------------------------------- +// +// Get system timer low byte +// +//--------------------------------------------------------------------------- +uint32_t SysTimer_Raspberry::GetTimerLow() +{ + return systaddr[SYST_CLO]; +} + +//--------------------------------------------------------------------------- +// +// Get system timer high byte +// +//--------------------------------------------------------------------------- +uint32_t SysTimer_Raspberry::GetTimerHigh() +{ + return systaddr[SYST_CHI]; +} + +//--------------------------------------------------------------------------- +// +// Sleep in nanoseconds +// +//--------------------------------------------------------------------------- +void SysTimer_Raspberry::SleepNsec(uint32_t nsec) +{ + // If time is 0, don't do anything + if (nsec == 0) { + return; + } + + // Calculate the timer difference + uint32_t diff = corefreq * nsec / 1000; + + // Return if the difference in time is too small + if (diff == 0) { + return; + } + + // Start + uint32_t start = armtaddr[ARMT_FREERUN]; + + // Loop until timer has elapsed + while ((armtaddr[ARMT_FREERUN] - start) < diff) + ; +} + +//--------------------------------------------------------------------------- +// +// Sleep in microseconds +// +//--------------------------------------------------------------------------- +void SysTimer_Raspberry::SleepUsec(uint32_t usec) +{ + // If time is 0, don't do anything + if (usec == 0) { + return; + } + + uint32_t now = GetTimerLow(); + while ((GetTimerLow() - now) < usec) + ; +} diff --git a/cpp/hal/systimer_raspberry.h b/cpp/hal/systimer_raspberry.h new file mode 100644 index 00000000..7efa125c --- /dev/null +++ b/cpp/hal/systimer_raspberry.h @@ -0,0 +1,70 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// Copyright (C) 2022 akuker +// +// [ High resolution timer ] +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "systimer.h" +#include + +//=========================================================================== +// +// System timer +// +//=========================================================================== +class SysTimer_Raspberry : public PlatformSpecificTimer +{ + public: + // Default constructor + SysTimer_Raspberry() = default; + // Default destructor + ~SysTimer_Raspberry() 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: + // System timer address + static volatile uint32_t *systaddr; + // ARM timer address + static volatile uint32_t *armtaddr; + // Core frequency + static volatile uint32_t corefreq; + + const static int ARMT_LOAD = 0; + const static int ARMT_VALUE = 1; + const static int ARMT_CTRL = 2; + const static int ARMT_CLRIRQ = 3; + const static int ARMT_RAWIRQ = 4; + const static int ARMT_MSKIRQ = 5; + const static int ARMT_RELOAD = 6; + const static int ARMT_PREDIV = 7; + const static int ARMT_FREERUN = 8; + + const static int SYST_CS = 0; + const static int SYST_CLO = 1; + const static int SYST_CHI = 2; + const static int SYST_C0 = 3; + const static int SYST_C1 = 4; + const static int SYST_C2 = 5; + const static int SYST_C3 = 6; + + const static uint32_t SYST_OFFSET = 0x00003000; + const static uint32_t ARMT_OFFSET = 0x0000B400; +}; diff --git a/cpp/monitor/data_sample.cpp b/cpp/monitor/data_sample.cpp deleted file mode 100644 index 370f25fd..00000000 --- a/cpp/monitor/data_sample.cpp +++ /dev/null @@ -1,39 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI Target Emulator RaSCSI Reloaded -// for Raspberry Pi -// -// Copyright (C) 2020-2021 akuker -// -// [ SCSI Bus Monitor ] -// -//--------------------------------------------------------------------------- - -#include "shared/scsi.h" -#include "data_sample.h" - -const char *GetPhaseStr(const data_capture *sample) -{ - return BUS::GetPhaseStrRaw(GetPhase(sample)); -} - -BUS::phase_t GetPhase(const data_capture *sample) -{ - // Selection Phase - if (GetSel(sample)) - { - return BUS::phase_t::selection; - } - - // Bus busy phase - if (!GetBsy(sample)) - { - return BUS::phase_t::busfree; - } - - // Get target phase from bus signal line - uint32_t mci = GetMsg(sample) ? 0x04 : 0x00; - mci |= GetCd(sample) ? 0x02 : 0x00; - mci |= GetIo(sample) ? 0x01 : 0x00; - return BUS::GetPhase(mci); -} diff --git a/cpp/monitor/data_sample.h b/cpp/monitor/data_sample.h deleted file mode 100644 index 81ad2dcf..00000000 --- a/cpp/monitor/data_sample.h +++ /dev/null @@ -1,49 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI Target Emulator RaSCSI Reloaded -// for Raspberry Pi -// -// Copyright (C) 2020-2021 akuker -// -// [ SCSI Bus Monitor ] -// -//--------------------------------------------------------------------------- - -#pragma once - -#include "shared/scsi.h" -#include "hal/gpiobus.h" - -using data_capture_t = struct data_capture -{ - uint32_t data; - uint64_t timestamp; -}; - -#define GET_PIN(SAMPLE, PIN) ((bool)((SAMPLE->data >> PIN) & 1)) - -inline bool GetBsy(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_BSY); } -inline bool GetSel(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_SEL); } -inline bool GetAtn(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_ATN); } -inline bool GetAck(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_ACK); } -inline bool GetRst(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_RST); } -inline bool GetMsg(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_MSG); } -inline bool GetCd(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_CD); } -inline bool GetIo(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_IO); } -inline bool GetReq(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_REQ); } -inline bool GetDp(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_DP); } -inline uint8_t GetData(const data_capture *sample) -{ - uint32_t data = sample->data; - return (uint8_t)((data >> (PIN_DT0 - 0)) & (1 << 0)) | - ((data >> (PIN_DT1 - 1)) & (1 << 1)) | - ((data >> (PIN_DT2 - 2)) & (1 << 2)) | - ((data >> (PIN_DT3 - 3)) & (1 << 3)) | - ((data >> (PIN_DT4 - 4)) & (1 << 4)) | - ((data >> (PIN_DT5 - 5)) & (1 << 5)) | - ((data >> (PIN_DT6 - 6)) & (1 << 6)) | - ((data >> (PIN_DT7 - 7)) & (1 << 7)); -} - -const char *GetPhaseStr(const data_capture *sample); -BUS::phase_t GetPhase(const data_capture *sample); diff --git a/cpp/monitor/sm_core.cpp b/cpp/monitor/sm_core.cpp index aeebd67e..df90390f 100644 --- a/cpp/monitor/sm_core.cpp +++ b/cpp/monitor/sm_core.cpp @@ -5,23 +5,24 @@ // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS +// Copyright (C) 2021-2022 akuker // //--------------------------------------------------------------------------- -#include "shared/log.h" -#include "shared/rasutil.h" -#include "shared/rascsi_version.h" -#include "hal/gpiobus_factory.h" -#include "hal/gpiobus.h" -#include "monitor/sm_reports.h" #include "monitor/sm_core.h" -#include "monitor/data_sample.h" -#include +#include "hal/data_sample.h" +#include "hal/gpiobus.h" +#include "hal/gpiobus_factory.h" +#include "monitor/sm_reports.h" +#include "shared/log.h" +#include "shared/rascsi_version.h" +#include "shared/rasutil.h" #include #include -#include #include +#include #include +#include using namespace std; using namespace ras_util; @@ -31,7 +32,7 @@ void ScsiMon::KillHandler(int) running = false; } -void ScsiMon::ParseArguments(const vector& args) +void ScsiMon::ParseArguments(const vector &args) { int opt; @@ -46,11 +47,11 @@ void ScsiMon::ParseArguments(const vector& args) buff_size = atoi(optarg); break; case 'i': - input_file_name = optarg; - import_data = true; + input_file_name = optarg; + import_data = true; break; case 1: - file_base_name = optarg; + file_base_name = optarg; break; default: cout << "default: " << optarg << endl; @@ -60,8 +61,10 @@ void ScsiMon::ParseArguments(const vector& args) /* Process any remaining command line arguments (not options). */ if (optind < static_cast(args.size())) { - while (optind < static_cast(args.size())) - file_base_name = args[optind++]; + while (optind < static_cast(args.size())) { + file_base_name = args[optind]; + optind++; + } } vcd_file_name = file_base_name; @@ -72,7 +75,7 @@ void ScsiMon::ParseArguments(const vector& args) html_file_name += ".html"; } -void ScsiMon::PrintHelpText(const vector& args) const +void ScsiMon::PrintHelpText(const vector &args) const { LOGINFO("%s -i [input file json] -b [buffer size] [output file]", args[0]) LOGINFO(" -i [input file json] - scsimon will parse the json file instead of capturing new data") @@ -86,8 +89,7 @@ void ScsiMon::Banner() const { if (import_data) { LOGINFO("Reading input file: %s", input_file_name.c_str()) - } - else { + } else { LOGINFO("Reading live data from the GPIO pins") LOGINFO(" Connection type : %s", CONNECT_DESC.c_str()) } @@ -112,7 +114,7 @@ bool ScsiMon::Init() return false; } - bus = GPIOBUS_Factory::Create(BUS::mode_e::TARGET); + bus = GPIOBUS_Factory::Create(BUS::mode_e::TARGET); if (bus == nullptr) { LOGERROR("Unable to intiailize the GPIO bus. Exiting....") return false; @@ -130,11 +132,11 @@ void ScsiMon::Cleanup() const } LOGINFO(" ") LOGINFO("Generating %s...", vcd_file_name.c_str()) - scsimon_generate_value_change_dump(vcd_file_name.c_str(), data_buffer, data_idx); + scsimon_generate_value_change_dump(vcd_file_name, data_buffer); LOGINFO("Generating %s...", json_file_name.c_str()) - scsimon_generate_json(json_file_name.c_str(), data_buffer, data_idx); + scsimon_generate_json(json_file_name, data_buffer); LOGINFO("Generating %s...", html_file_name.c_str()) - scsimon_generate_html(html_file_name.c_str(), data_buffer, data_idx); + scsimon_generate_html(html_file_name, data_buffer); bus->Cleanup(); } @@ -144,12 +146,7 @@ void ScsiMon::Reset() const bus->Reset(); } -#ifdef DEBUG -static uint32_t high_bits = 0x0; -static uint32_t low_bits = 0xFFFFFFFF; -#endif - -int ScsiMon::run(const vector& args) +int ScsiMon::run(const vector &args) { #ifdef DEBUG spdlog::set_level(spdlog::level::trace); @@ -162,12 +159,8 @@ int ScsiMon::run(const vector& args) ParseArguments(args); -#ifdef DEBUG - uint32_t prev_high = high_bits; - uint32_t prev_low = low_bits; -#endif - uint32_t prev_sample = 0xFFFFFFFF; - uint32_t this_sample = 0; + shared_ptr prev_sample = nullptr; + shared_ptr this_sample = nullptr; timeval start_time; timeval stop_time; uint64_t loop_count = 0; @@ -181,12 +174,11 @@ int ScsiMon::run(const vector& args) Banner(); - data_buffer = (data_capture *)calloc(buff_size, sizeof(data_capture_t)); + data_buffer.reserve(buff_size); if (import_data) { - data_idx = scsimon_read_json(input_file_name.c_str(), data_buffer, buff_size); - if (data_idx > 0) - { + data_idx = scsimon_read_json(input_file_name, data_buffer); + if (data_idx > 0) { LOGDEBUG("Read %d samples from %s", data_idx, input_file_name.c_str()) Cleanup(); } @@ -223,12 +215,8 @@ int ScsiMon::run(const vector& args) (void)gettimeofday(&start_time, nullptr); - LOGDEBUG("ALL_SCSI_PINS %08X\n", ALL_SCSI_PINS) - // Main Loop while (running) { - // Work initialization - this_sample = (bus->Acquire() & ALL_SCSI_PINS); loop_count++; if (loop_count > LLONG_MAX - 1) { LOGINFO("Maximum amount of time has elapsed. SCSIMON is terminating.") @@ -240,23 +228,9 @@ int ScsiMon::run(const vector& args) running = false; } - if (this_sample != prev_sample) { -#ifdef DEBUG - // This is intended to be a debug check to see if every pin is set - // high and low at some point. - high_bits |= this_sample; - low_bits &= this_sample; - if ((high_bits != prev_high) || (low_bits != prev_low)) { - LOGDEBUG(" %08X %08X\n", high_bits, low_bits) - } - prev_high = high_bits; - prev_low = low_bits; - if ((data_idx % 1000) == 0) { - LOGDEBUG("%s", ("Collected " + to_string(data_idx) + " samples...").c_str()) - } -#endif - data_buffer[data_idx].data = this_sample; - data_buffer[data_idx].timestamp = loop_count; + this_sample = bus->GetSample(loop_count); + if ((prev_sample == nullptr) || (this_sample->GetRawCapture() != prev_sample->GetRawCapture())) { + data_buffer.push_back(this_sample); data_idx++; prev_sample = this_sample; } @@ -266,8 +240,7 @@ int ScsiMon::run(const vector& args) // Collect one last sample, otherwise it looks like the end of the data was cut off if (data_idx < buff_size) { - data_buffer[data_idx].data = this_sample; - data_buffer[data_idx].timestamp = loop_count; + data_buffer.push_back(bus->GetSample(loop_count)); data_idx++; } @@ -276,13 +249,15 @@ int ScsiMon::run(const vector& args) timersub(&stop_time, &start_time, &time_diff); elapsed_us = ((time_diff.tv_sec * 1000000) + time_diff.tv_usec); - LOGINFO("%s", ("Elapsed time: " + to_string(elapsed_us) + " microseconds (" + to_string(elapsed_us / 1000000) - + " seconds").c_str()) + LOGINFO("%s", ("Elapsed time: " + to_string(elapsed_us) + " microseconds (" + to_string(elapsed_us / 1000000) + + " seconds") + .c_str()) LOGINFO("%s", ("Collected " + to_string(data_idx) + " changes").c_str()) ns_per_loop = (double)(elapsed_us * 1000) / (double)loop_count; - LOGINFO("%s", ("Read the SCSI bus " + to_string(loop_count) + " times with an average of " - + to_string(ns_per_loop) + " ns for each read").c_str()) + LOGINFO("%s", ("Read the SCSI bus " + to_string(loop_count) + " times with an average of " + + to_string(ns_per_loop) + " ns for each read") + .c_str()) Cleanup(); diff --git a/cpp/monitor/sm_core.h b/cpp/monitor/sm_core.h index a7cfdb9e..2d1ba464 100644 --- a/cpp/monitor/sm_core.h +++ b/cpp/monitor/sm_core.h @@ -10,52 +10,50 @@ #pragma once #include "hal/bus.h" -#include "monitor/data_sample.h" -#include +#include "hal/data_sample.h" #include +#include using namespace std; // TODO Make static fields/methods non-static class ScsiMon { -public: + public: + ScsiMon() = default; + ~ScsiMon() = default; - ScsiMon() = default; - ~ScsiMon() = default; + int run(const vector &); - int run(const vector&); + inline static double ns_per_loop; - inline static double ns_per_loop; + private: + void ParseArguments(const vector &); + void PrintHelpText(const vector &) const; + void Banner() const; + bool Init(); + void Cleanup() const; + void Reset() const; -private: + static void KillHandler(int); - void ParseArguments(const vector&); - void PrintHelpText(const vector&) const; - void Banner() const; - bool Init(); - void Cleanup() const; - void Reset() const; + static inline volatile bool running; - static void KillHandler(int); + shared_ptr bus; - static inline volatile bool running; + uint32_t buff_size = 1000000; - unique_ptr bus; + vector> data_buffer; - uint32_t buff_size = 1000000; + uint32_t data_idx = 0; - data_capture *data_buffer = nullptr; + bool print_help = false; - uint32_t data_idx = 0; + bool import_data = false; - bool print_help = false; - - bool import_data = false; - - string file_base_name = "log"; - string vcd_file_name; - string json_file_name; - string html_file_name; - string input_file_name; + string file_base_name = "log"; + string vcd_file_name; + string json_file_name; + string html_file_name; + string input_file_name; }; diff --git a/cpp/monitor/sm_html_report.cpp b/cpp/monitor/sm_html_report.cpp index 375ad859..8fcbe2da 100644 --- a/cpp/monitor/sm_html_report.cpp +++ b/cpp/monitor/sm_html_report.cpp @@ -5,6 +5,7 @@ // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS +// Copyright (C) 2021-2022 akuker // //--------------------------------------------------------------------------- @@ -96,13 +97,13 @@ for (i = 0; i < coll.length; i++) { )"; -static void print_html_data(ofstream& html_fp, const data_capture *data_capture_array, uint32_t capture_count) +static void print_html_data(ofstream& html_fp, const vector> &data_capture_array) { - const data_capture *data; + shared_ptr data; bool prev_data_valid = false; bool curr_data_valid; uint32_t selected_id = 0; - BUS::phase_t prev_phase = BUS::phase_t::busfree; + phase_t prev_phase = phase_t::busfree; bool close_row = false; int data_space_count = 0; bool collapsible_div_active = false; @@ -110,12 +111,11 @@ static void print_html_data(ofstream& html_fp, const data_capture *data_capture_ html_fp << "" << endl; - for (uint32_t idx = 0; idx < capture_count; idx++) { - data = &data_capture_array[idx]; - curr_data_valid = GetAck(data) && GetReq(data); - BUS::phase_t phase = GetPhase(data); - if (phase == BUS::phase_t::selection && !GetBsy(data)) { - selected_id = GetData(data); + for (auto data: data_capture_array) { + curr_data_valid = data->GetACK() && data->GetREQ(); + phase_t phase = data->GetPhase(); + if (phase == phase_t::selection && !data->GetBSY()) { + selected_id = data->GetDAT(); } if (prev_phase != phase) { if (close_row) { @@ -137,8 +137,8 @@ static void print_html_data(ofstream& html_fp, const data_capture *data_capture_ } html_fp << ""; close_row = true; // Close the row the next time around - html_fp << ""; - html_fp << ""; + html_fp << ""; + html_fp << ""; html_fp << ""; html_fp << "
" << (double)data->timestamp / 100000 << "" << GetPhaseStr(data) << "" << (double)data->GetTimestamp() / 100000 << "" << data->GetPhaseStr() << "" << std::hex << selected_id << ""; } @@ -151,7 +151,7 @@ static void print_html_data(ofstream& html_fp, const data_capture *data_capture_ html_fp << std::hex << data_space_count << ": "; } - html_fp << fmt::format("{0:02X}", GetData(data)); + html_fp << fmt::format("{0:02X}", data->GetDAT()); data_space_count++; if ((data_space_count % 4) == 0) { @@ -171,17 +171,17 @@ static void print_html_data(ofstream& html_fp, const data_capture *data_capture_ } } -void scsimon_generate_html(const char *filename, const data_capture *data_capture_array, uint32_t capture_count) +void scsimon_generate_html(const string &filename, const vector> &data_capture_array) { - LOGINFO("Creating HTML report file (%s)", filename) + LOGINFO("Creating HTML report file (%s)", filename.c_str()) ofstream html_ofstream; - html_ofstream.open(filename, ios::out); + html_ofstream.open(filename.c_str(), ios::out); html_ofstream << html_header; print_copyright_info(html_ofstream); - print_html_data(html_ofstream, data_capture_array, capture_count); + print_html_data(html_ofstream, data_capture_array); html_ofstream << html_footer; diff --git a/cpp/monitor/sm_json_report.cpp b/cpp/monitor/sm_json_report.cpp index 62ed70c8..770e9ba2 100644 --- a/cpp/monitor/sm_json_report.cpp +++ b/cpp/monitor/sm_json_report.cpp @@ -5,60 +5,59 @@ // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS +// Copyright (C) 2021-2022 akuker // //--------------------------------------------------------------------------- +#include "hal/data_sample_raspberry.h" #include "shared/log.h" #include "sm_reports.h" #include "string.h" -#include #include +#include using namespace std; -const char timestamp_label[] = "\"timestamp\":\"0x"; -const char data_label[] = "\"data\":\"0x"; +const string timestamp_label = "\"timestamp\":\"0x"; +const string data_label = "\"data\":\"0x"; -uint32_t scsimon_read_json(const char *json_filename, data_capture *data_capture_array, uint32_t max_sz) +uint32_t scsimon_read_json(const string &json_filename, vector> &data_capture_array) { - char str_buf[1024]; - FILE *fp = fopen(json_filename, "r"); + std::ifstream json_file(json_filename); uint32_t sample_count = 0; - while (fgets(str_buf, sizeof(str_buf), fp)) { - char timestamp[1024]; - char data[1024]; + while (json_file) { + string str_buf; + std::getline(json_file, str_buf); + string timestamp; + string data; uint64_t timestamp_uint; uint32_t data_uint; char *ptr; - const char *timestamp_str = strstr(str_buf, timestamp_label); - if (!timestamp_str) + size_t timestamp_pos = str_buf.find(timestamp_label); + if (timestamp_pos == string::npos) continue; - strncpy(timestamp, ×tamp_str[strlen(timestamp_label)], 16); - timestamp[16] = '\0'; - timestamp_uint = strtoull(timestamp, &ptr, 16); + timestamp = str_buf.substr(timestamp_pos + timestamp_label.length(), 16); + timestamp_uint = strtoull(timestamp.c_str(), &ptr, 16); - const char *data_str = strstr(str_buf, data_label); - if (!data_str) + size_t data_pos = str_buf.find(data_label); + if (data_pos == string::npos) continue; - strncpy(data, &data_str[strlen(data_label)], 8); - data[8] = '\0'; - data_uint = static_cast(strtoul(data, &ptr, 16)); + data = str_buf.substr(data_pos + data_label.length(), 8); + data_uint = static_cast(strtoul(data.c_str(), &ptr, 16)); + + // For reading in JSON files, we'll just assume raspberry pi data types + data_capture_array.push_back(make_unique(data_uint, timestamp_uint)); - data_capture_array[sample_count].timestamp = timestamp_uint; - data_capture_array[sample_count].data = data_uint; sample_count++; - if (sample_count >= max_sz) { - LOGWARN("File exceeds maximum buffer size. Some data may be missing.") - LOGWARN("Try re-running the tool with a larger buffer size") + if (sample_count == UINT32_MAX) { + LOGWARN("Maximum number of samples read. Some data may not be included.") break; } } - if (fp != nullptr) { - fclose(fp); - } + json_file.close(); return sample_count; } @@ -68,20 +67,21 @@ uint32_t scsimon_read_json(const char *json_filename, data_capture *data_capture // Generate JSON Output File // //--------------------------------------------------------------------------- -void scsimon_generate_json(const char *filename, const data_capture *data_capture_array, uint32_t capture_count) +void scsimon_generate_json(const string &filename, const vector> &data_capture_array) { - LOGTRACE("Creating JSON file (%s)", filename) + LOGTRACE("Creating JSON file (%s)", filename.c_str()) ofstream json_ofstream; - json_ofstream.open(filename, ios::out); + json_ofstream.open(filename.c_str(), ios::out); json_ofstream << "[" << endl; - uint32_t i = 0; - while (i < capture_count) { - json_ofstream << fmt::format("{{\"id\": \"{0:d}\", \"timestamp\":\"{1:#016x}\", \"data\":\"{2:#08x}\"}}", i, data_capture_array[i].timestamp, data_capture_array[i].data); + size_t i = 0; + size_t capture_count = data_capture_array.size(); + for (auto data : data_capture_array) { + json_ofstream << fmt::format("{{\"id\": \"{0:d}\", \"timestamp\":\"{1:#016x}\", \"data\":\"{2:#08x}\"}}", i, + data->GetTimestamp(), data->GetRawCapture()); - if (i != (capture_count - 1)) - { + if (i != (capture_count - 1)) { json_ofstream << ","; } json_ofstream << endl; @@ -89,5 +89,4 @@ void scsimon_generate_json(const char *filename, const data_capture *data_captur } json_ofstream << "]" << endl; json_ofstream.close(); - } diff --git a/cpp/monitor/sm_reports.h b/cpp/monitor/sm_reports.h index fd5dd71a..61373cd2 100644 --- a/cpp/monitor/sm_reports.h +++ b/cpp/monitor/sm_reports.h @@ -9,10 +9,11 @@ // //--------------------------------------------------------------------------- -#include "data_sample.h" +#include "hal/data_sample.h" +#include -uint32_t scsimon_read_json(const char *json_filename, data_capture *data_capture_array, uint32_t max_sz); +uint32_t scsimon_read_json(const string &json_filename, vector> &data_capture_array); -void scsimon_generate_html(const char *filename, const data_capture *data_capture_array, uint32_t capture_count); -void scsimon_generate_json(const char *filename, const data_capture *data_capture_array, uint32_t capture_count); -void scsimon_generate_value_change_dump(const char *filename, const data_capture *data_capture_array, uint32_t capture_count); +void scsimon_generate_html(const string &filename, const vector> &data_capture_array); +void scsimon_generate_json(const string &filename, const vector> &data_capture_array); +void scsimon_generate_value_change_dump(const string &filename, const vector> &data_capture_array); diff --git a/cpp/monitor/sm_vcd_report.cpp b/cpp/monitor/sm_vcd_report.cpp index a0d747fd..1cea97f1 100644 --- a/cpp/monitor/sm_vcd_report.cpp +++ b/cpp/monitor/sm_vcd_report.cpp @@ -9,14 +9,14 @@ // //--------------------------------------------------------------------------- -#include "shared/log.h" +#include "hal/data_sample.h" #include "hal/gpiobus.h" -#include "data_sample.h" +#include "shared/log.h" #include "sm_core.h" #include "sm_reports.h" -#include -#include #include +#include +#include using namespace std; @@ -29,16 +29,16 @@ using namespace std; // Symbol definition for the VCD file // These are just arbitrary symbols. They can be anything allowed by the VCD file format, // as long as they're consistently used. -const char SYMBOL_PIN_DAT = '#'; -const char SYMBOL_PIN_ATN = '+'; -const char SYMBOL_PIN_RST = '$'; -const char SYMBOL_PIN_ACK = '%'; -const char SYMBOL_PIN_REQ = '^'; -const char SYMBOL_PIN_MSG = '&'; -const char SYMBOL_PIN_CD = '*'; -const char SYMBOL_PIN_IO = '('; -const char SYMBOL_PIN_BSY = ')'; -const char SYMBOL_PIN_SEL = '-'; +const char SYMBOL_PIN_DAT = '#'; +const char SYMBOL_PIN_ATN = '+'; +const char SYMBOL_PIN_RST = '$'; +const char SYMBOL_PIN_ACK = '%'; +const char SYMBOL_PIN_REQ = '^'; +const char SYMBOL_PIN_MSG = '&'; +const char SYMBOL_PIN_CD = '*'; +const char SYMBOL_PIN_IO = '('; +const char SYMBOL_PIN_BSY = ')'; +const char SYMBOL_PIN_SEL = '-'; const char SYMBOL_PIN_PHASE = '='; // We'll use position 0 in the prev_value array to store the previous phase @@ -49,133 +49,99 @@ const int PIN_PHASE = 0; // Variable declarations // //--------------------------------------------------------------------------- -static uint8_t prev_value[32] = {0xFF}; +static array prev_value = {0xFF}; -static uint8_t get_pin_value(uint32_t data, int pin) +static void vcd_output_if_changed_phase(ofstream &fp, phase_t data, int pin, char symbol) { - return (data >> pin) & 1; -} - -static uint8_t get_data_field(uint32_t data) -{ - const uint32_t data_out = - ((data >> (PIN_DT0 - 0)) & (1 << 7)) | - ((data >> (PIN_DT1 - 1)) & (1 << 6)) | - ((data >> (PIN_DT2 - 2)) & (1 << 5)) | - ((data >> (PIN_DT3 - 3)) & (1 << 4)) | - ((data >> (PIN_DT4 - 4)) & (1 << 3)) | - ((data >> (PIN_DT5 - 5)) & (1 << 2)) | - ((data >> (PIN_DT6 - 6)) & (1 << 1)) | - ((data >> (PIN_DT7 - 7)) & (1 << 0)); - - return (uint8_t)data_out; -} - -static void vcd_output_if_changed_phase(ofstream& fp, uint32_t data, int pin, char symbol) -{ - const BUS::phase_t new_value = GPIOBUS::GetPhaseRaw(data); - if (prev_value[pin] != static_cast(new_value)) { - prev_value[pin] = static_cast(new_value); - fp << "s" << GPIOBUS::GetPhaseStrRaw(new_value) << " " << symbol << endl; + if (prev_value[pin] != static_cast(data)) { + prev_value[pin] = static_cast(data); + fp << "s" << BUS::GetPhaseStrRaw(data) << " " << symbol << endl; } } -static void vcd_output_if_changed_bool(ofstream& fp, uint32_t data, int pin, char symbol) +static void vcd_output_if_changed_bool(ofstream &fp, bool data, int pin, char symbol) { - const uint8_t new_value = get_pin_value(data, pin); - if (prev_value[pin] != new_value) { - prev_value[pin] = new_value; - fp << new_value << symbol << endl; + if (prev_value[pin] != data) { + prev_value[pin] = data; + fp << data << symbol << endl; } } -static void vcd_output_if_changed_byte(ofstream& fp, uint32_t data, int pin, char symbol) +static void vcd_output_if_changed_byte(ofstream &fp, uint8_t data, int pin, char symbol) { - const uint8_t new_value = get_data_field(data); - if (prev_value[pin] != new_value) { - prev_value[pin] = new_value; - fp << "b" - << fmt::format("{0:b}", get_pin_value(data, PIN_DT7)) - << fmt::format("{0:b}", get_pin_value(data, PIN_DT6)) - << fmt::format("{0:b}", get_pin_value(data, PIN_DT5)) - << fmt::format("{0:b}", get_pin_value(data, PIN_DT4)) - << fmt::format("{0:b}", get_pin_value(data, PIN_DT3)) - << fmt::format("{0:b}", get_pin_value(data, PIN_DT2)) - << fmt::format("{0:b}", get_pin_value(data, PIN_DT1)) - << fmt::format("{0:b}", get_pin_value(data, PIN_DT0)) - << " " << symbol << endl; + if (prev_value[pin] != data) { + prev_value[pin] = data; + fp << "b" << fmt::format("{0:8b}", data) << " " << symbol << endl; } } -void scsimon_generate_value_change_dump(const char *filename, const data_capture *data_capture_array, uint32_t capture_count) +void scsimon_generate_value_change_dump(const string &filename, const vector> &data_capture_array) { - LOGTRACE("Creating Value Change Dump file (%s)", filename) + LOGTRACE("Creating Value Change Dump file (%s)", filename.c_str()) ofstream vcd_ofstream; - vcd_ofstream.open(filename, ios::out); + vcd_ofstream.open(filename.c_str(), ios::out); // Get the current time time_t rawtime; time(&rawtime); - const struct tm *timeinfo = localtime(&rawtime); - char timestamp[256]; - strftime(timestamp, sizeof(timestamp), "%d-%m-%Y %H-%M-%S", timeinfo); + struct tm timeinfo; + localtime_r(&rawtime, &timeinfo); + string timestamp; + timestamp.resize(256); + strftime(×tamp[0], timestamp.size(), "%d-%m-%Y %H-%M-%S", &timeinfo); - vcd_ofstream - << "$date" << endl - << timestamp << endl - << "$end" << endl - << "$version" << endl - << " VCD generator tool version info text." << endl - << "$end" << endl - << "$comment" << endl - << " Tool build date:" << __TIMESTAMP__ << endl - << "$end" << endl - << "$timescale 1 ns $end" << endl - << "$scope module logic $end" << endl - << "$var wire 1 " << SYMBOL_PIN_BSY << " BSY $end" << endl - << "$var wire 1 " << SYMBOL_PIN_SEL << " SEL $end" << endl - << "$var wire 1 " << SYMBOL_PIN_CD << " CD $end" << endl - << "$var wire 1 " << SYMBOL_PIN_IO << " IO $end"<< endl - << "$var wire 1 " << SYMBOL_PIN_MSG << " MSG $end"<< endl - << "$var wire 1 " << SYMBOL_PIN_REQ << " REQ $end" << endl - << "$var wire 1 " << SYMBOL_PIN_ACK << " ACK $end" << endl - << "$var wire 1 " << SYMBOL_PIN_ATN << " ATN $end" << endl - << "$var wire 1 " << SYMBOL_PIN_RST << " RST $end" << endl - << "$var wire 8 " << SYMBOL_PIN_DAT << " data $end" << endl - << "$var string 1 " << SYMBOL_PIN_PHASE << " phase $end" << endl - << "$upscope $end" << endl - << "$enddefinitions $end" << endl; + vcd_ofstream << "$date" << endl + << timestamp << endl + << "$end" << endl + << "$version" << endl + << " VCD generator tool version info text." << endl + << "$end" << endl + << "$comment" << endl + << " Tool build date:" << __TIMESTAMP__ << endl + << "$end" << endl + << "$timescale 1 ns $end" << endl + << "$scope module logic $end" << endl + << "$var wire 1 " << SYMBOL_PIN_BSY << " BSY $end" << endl + << "$var wire 1 " << SYMBOL_PIN_SEL << " SEL $end" << endl + << "$var wire 1 " << SYMBOL_PIN_CD << " CD $end" << endl + << "$var wire 1 " << SYMBOL_PIN_IO << " IO $end" << endl + << "$var wire 1 " << SYMBOL_PIN_MSG << " MSG $end" << endl + << "$var wire 1 " << SYMBOL_PIN_REQ << " REQ $end" << endl + << "$var wire 1 " << SYMBOL_PIN_ACK << " ACK $end" << endl + << "$var wire 1 " << SYMBOL_PIN_ATN << " ATN $end" << endl + << "$var wire 1 " << SYMBOL_PIN_RST << " RST $end" << endl + << "$var wire 8 " << SYMBOL_PIN_DAT << " data $end" << endl + << "$var string 1 " << SYMBOL_PIN_PHASE << " phase $end" << endl + << "$upscope $end" << endl + << "$enddefinitions $end" << endl; // Initial values - default to zeros - vcd_ofstream - << "$dumpvars" << endl - << "0" << SYMBOL_PIN_BSY << endl - << "0" << SYMBOL_PIN_SEL << endl - << "0" << SYMBOL_PIN_CD << endl - << "0" << SYMBOL_PIN_IO << endl - << "0" << SYMBOL_PIN_MSG << endl - << "0" << SYMBOL_PIN_REQ << endl - << "0" << SYMBOL_PIN_ACK << endl - << "0" << SYMBOL_PIN_ATN << endl - << "0" << SYMBOL_PIN_RST << endl - << "b00000000 " << SYMBOL_PIN_DAT << endl - << "$end" << endl; + vcd_ofstream << "$dumpvars" << endl + << "0" << SYMBOL_PIN_BSY << endl + << "0" << SYMBOL_PIN_SEL << endl + << "0" << SYMBOL_PIN_CD << endl + << "0" << SYMBOL_PIN_IO << endl + << "0" << SYMBOL_PIN_MSG << endl + << "0" << SYMBOL_PIN_REQ << endl + << "0" << SYMBOL_PIN_ACK << endl + << "0" << SYMBOL_PIN_ATN << endl + << "0" << SYMBOL_PIN_RST << endl + << "b00000000 " << SYMBOL_PIN_DAT << endl + << "$end" << endl; - uint32_t i = 0; - while (i < capture_count) { - vcd_ofstream << "#" << (uint64_t)((double)data_capture_array[i].timestamp * ScsiMon::ns_per_loop) << endl; - vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_BSY, SYMBOL_PIN_BSY); - vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_SEL, SYMBOL_PIN_SEL); - vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_CD, SYMBOL_PIN_CD); - vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_IO, SYMBOL_PIN_IO); - vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_MSG, SYMBOL_PIN_MSG); - vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_REQ, SYMBOL_PIN_REQ); - vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_ACK, SYMBOL_PIN_ACK); - vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_ATN, SYMBOL_PIN_ATN); - vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_RST, SYMBOL_PIN_RST); - vcd_output_if_changed_byte(vcd_ofstream, data_capture_array[i].data, PIN_DT0, SYMBOL_PIN_DAT); - vcd_output_if_changed_phase(vcd_ofstream, data_capture_array[i].data, PIN_PHASE, SYMBOL_PIN_PHASE); - i++; + for (shared_ptr cur_sample : data_capture_array) { + vcd_ofstream << "#" << (double)cur_sample->GetTimestamp() * ScsiMon::ns_per_loop << endl; + vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetBSY(), PIN_BSY, SYMBOL_PIN_BSY); + vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetSEL(), PIN_SEL, SYMBOL_PIN_SEL); + vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetCD(), PIN_CD, SYMBOL_PIN_CD); + vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetIO(), PIN_IO, SYMBOL_PIN_IO); + vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetMSG(), PIN_MSG, SYMBOL_PIN_MSG); + vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetREQ(), PIN_REQ, SYMBOL_PIN_REQ); + vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetACK(), PIN_ACK, SYMBOL_PIN_ACK); + vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetATN(), PIN_ATN, SYMBOL_PIN_ATN); + vcd_output_if_changed_bool(vcd_ofstream, cur_sample->GetRST(), PIN_RST, SYMBOL_PIN_RST); + vcd_output_if_changed_byte(vcd_ofstream, cur_sample->GetDAT(), PIN_DT0, SYMBOL_PIN_DAT); + vcd_output_if_changed_phase(vcd_ofstream, cur_sample->GetPhase(), PIN_PHASE, SYMBOL_PIN_PHASE); } vcd_ofstream.close(); } diff --git a/cpp/rascsi/rascsi_core.cpp b/cpp/rascsi/rascsi_core.cpp index 80c24e48..54d08ac6 100644 --- a/cpp/rascsi/rascsi_core.cpp +++ b/cpp/rascsi/rascsi_core.cpp @@ -41,6 +41,7 @@ using namespace spdlog; using namespace rascsi_interface; using namespace ras_util; using namespace protobuf_util; +using namespace scsi_defs; void Rascsi::Banner(const vector& args) const { @@ -584,7 +585,7 @@ int Rascsi::run(const vector& args) // The initiator and target ID const uint8_t id_data = bus->GetDAT(); - BUS::phase_t phase = BUS::phase_t::busfree; + phase_t phase = phase_t::busfree; // Identify the responsible controller auto controller = controller_manager->IdentifyController(id_data); @@ -600,13 +601,13 @@ int Rascsi::run(const vector& args) device_logger.Trace("++++ Starting processing for unknown initiator ID"); } - if (controller->Process(initiator_id) == BUS::phase_t::selection) { - phase = BUS::phase_t::selection; + if (controller->Process(initiator_id) == phase_t::selection) { + phase = phase_t::selection; } } // Return to bus monitoring if the selection phase has not started - if (phase != BUS::phase_t::selection) { + if (phase != phase_t::selection) { continue; } @@ -625,7 +626,7 @@ int Rascsi::run(const vector& args) phase = controller->Process(initiator_id); // End when the bus is free - if (phase == BUS::phase_t::busfree) { + if (phase == phase_t::busfree) { break; } } diff --git a/cpp/rasdump/rasdump_core.cpp b/cpp/rasdump/rasdump_core.cpp index fe9cd13c..fff03819 100644 --- a/cpp/rasdump/rasdump_core.cpp +++ b/cpp/rasdump/rasdump_core.cpp @@ -139,7 +139,7 @@ void RasDump::ParseArguments(const vector& args) buffer = vector(buffer_size); } -void RasDump::WaitPhase(BUS::phase_t phase) const +void RasDump::WaitPhase(phase_t phase) const { LOGDEBUG("Waiting for %s phase", BUS::GetPhaseStrRaw(phase)) @@ -176,7 +176,7 @@ void RasDump::Command(scsi_command cmd, vector& cdb) const Selection(); - WaitPhase(BUS::phase_t::command); + WaitPhase(phase_t::command); cdb[0] = static_cast(cmd); cdb[1] = static_cast(static_cast(cdb[1]) | static_cast(target_lun << 5)); @@ -189,7 +189,7 @@ void RasDump::Command(scsi_command cmd, vector& cdb) const void RasDump::DataIn(int length) { - WaitPhase(BUS::phase_t::datain); + WaitPhase(phase_t::datain); if (!bus->ReceiveHandShake(buffer.data(), length)) { throw parser_exception("DATA IN failed"); @@ -198,7 +198,7 @@ void RasDump::DataIn(int length) void RasDump::DataOut(int length) { - WaitPhase(BUS::phase_t::dataout); + WaitPhase(phase_t::dataout); if (!bus->SendHandShake(buffer.data(), length, BUS::SEND_NO_DELAY)) { throw parser_exception("DATA OUT failed"); @@ -207,7 +207,7 @@ void RasDump::DataOut(int length) void RasDump::Status() const { - WaitPhase(BUS::phase_t::status); + WaitPhase(phase_t::status); if (array buf; bus->ReceiveHandShake(buf.data(), 1) != 1) { throw parser_exception("STATUS failed"); @@ -216,7 +216,7 @@ void RasDump::Status() const void RasDump::MessageIn() const { - WaitPhase(BUS::phase_t::msgin); + WaitPhase(phase_t::msgin); if (array buf; bus->ReceiveHandShake(buf.data(), 1) != 1) { throw parser_exception("MESSAGE IN failed"); diff --git a/cpp/rasdump/rasdump_core.h b/cpp/rasdump/rasdump_core.h index 79a2a0c6..8997f18c 100644 --- a/cpp/rasdump/rasdump_core.h +++ b/cpp/rasdump/rasdump_core.h @@ -36,7 +36,7 @@ private: void ParseArguments(const vector&); int DumpRestore(); pair GetDeviceInfo(); - void WaitPhase(BUS::phase_t) const; + void WaitPhase(phase_t) const; void Selection() const; void Command(scsi_defs::scsi_command, vector&) const; void DataIn(int); diff --git a/cpp/scsiloop.cpp b/cpp/scsiloop.cpp new file mode 100644 index 00000000..b81158f9 --- /dev/null +++ b/cpp/scsiloop.cpp @@ -0,0 +1,20 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// Copyright (C) 2022 akuker +// +//--------------------------------------------------------------------------- + +#include "scsiloop/scsiloop_core.h" + +using namespace std; + +int main(int argc, char *argv[]) +{ + const vector args(argv, argv + argc); + + return ScsiLoop().run(args); +} diff --git a/cpp/scsiloop/scsiloop_core.cpp b/cpp/scsiloop/scsiloop_core.cpp new file mode 100644 index 00000000..f6b0c378 --- /dev/null +++ b/cpp/scsiloop/scsiloop_core.cpp @@ -0,0 +1,175 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded for Raspberry Pi +// Loopback tester utility +// +// Copyright (C) 2022 akuker +// +// [ Loopback tester utility ] +// +// For more information, see: +// https://github.com/akuker/RASCSI/wiki/Troubleshooting#Loopback_Testing +// +//--------------------------------------------------------------------------- + +#include "shared/log.h" +#include "shared/rascsi_version.h" +#include "shared/rasutil.h" +#include "spdlog/sinks/stdout_color_sinks.h" + +#include "scsiloop/scsiloop_core.h" +#include "scsiloop/scsiloop_cout.h" +#include "scsiloop/scsiloop_gpio.h" +#include "scsiloop/scsiloop_timer.h" + +#include +#include + +#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 + +using namespace std; +using namespace spdlog; + +string current_log_level = "unknown"; // Some versions of spdlog do not support get_log_level() + +void ScsiLoop::Banner(const vector &args) const +{ + cout << ras_util::Banner("SCSI Loopback Test"); + cout << "Connect type: " << CONNECT_DESC << '\n' << flush; + + if ((args.size() > 1 && strcmp(args[1], "-h") == 0) || (args.size() > 1 && strcmp(args[1], "--help") == 0)) { + cout << "\nUsage: " << args[0] << " [-L log_level] ...\n\n"; + exit(EXIT_SUCCESS); + } +} + +bool ScsiLoop::SetLogLevel(const string &log_level) +{ + if (log_level == "trace") { + set_level(level::trace); + } else if (log_level == "debug") { + set_level(level::debug); + } else if (log_level == "info") { + set_level(level::info); + } else if (log_level == "warn") { + set_level(level::warn); + } else if (log_level == "err") { + set_level(level::err); + } else if (log_level == "critical") { + set_level(level::critical); + } else if (log_level == "off") { + set_level(level::off); + } else { + return false; + } + + current_log_level = log_level; + + LOGINFO("Set log level to '%s'", current_log_level.c_str()) + + return true; +} + +void ScsiLoop::TerminationHandler(int signum) +{ + exit(signum); +} + +bool ScsiLoop::ParseArgument(const vector &args) +{ + string name; + string log_level; + + const char *locale = setlocale(LC_MESSAGES, ""); + if (locale == nullptr || !strcmp(locale, "C")) { + locale = "en"; + } + + opterr = 1; + int opt; + + while ((opt = getopt(static_cast(args.size()), args.data(), "-L:")) != -1) { + switch (opt) { + case 'L': + log_level = optarg; + continue; + + default: + return false; + } + + if (optopt) { + return false; + } + } + + if (!log_level.empty()) { + SetLogLevel(log_level); + } + + return true; +} + +int ScsiLoop::run(const vector &args) +{ + // added setvbuf to override stdout buffering, so logs are written immediately and not when the process exits. + setvbuf(stdout, nullptr, _IONBF, 0); + + // Output the Banner + Banner(args); + + // ParseArgument() requires the bus to have been initialized first, which requires the root user. + // The -v option should be available for any user, which requires special handling. + for (auto this_arg : args) { + if (!strcasecmp(this_arg, "-v")) { + cout << rascsi_get_version_string() << endl; + return 0; + } + } + + // Create a thread-safe stdout logger to process the log messages + const auto logger = stdout_color_mt("scsiloop stdout logger"); + set_level(level::info); + current_log_level = "info"; + + vector error_list; + + if (!ParseArgument(args)) { + return -1; + } + + // Signal handler to detach all devices on a KILL or TERM signal + struct sigaction termination_handler; + termination_handler.sa_handler = TerminationHandler; + sigemptyset(&termination_handler.sa_mask); + termination_handler.sa_flags = 0; + sigaction(SIGINT, &termination_handler, nullptr); + sigaction(SIGTERM, &termination_handler, nullptr); + + // This must be executed before the timer test, since this initializes the timer + ScsiLoop_GPIO gpio_test; + + int result = ScsiLoop_Timer::RunTimerTest(error_list); + result += gpio_test.RunLoopbackTest(error_list); + + if (result == 0) { + // Only test the dat inputs/outputs if the loopback test passed. + result += gpio_test.RunDataInputTest(error_list); + result += gpio_test.RunDataOutputTest(error_list); + } + + ScsiLoop_Cout::PrintErrors(error_list); + gpio_test.Cleanup(); + + return 0; +} diff --git a/cpp/scsiloop/scsiloop_core.h b/cpp/scsiloop/scsiloop_core.h new file mode 100644 index 00000000..cb407e4f --- /dev/null +++ b/cpp/scsiloop/scsiloop_core.h @@ -0,0 +1,32 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// Copyright (C) 2022 akuker +// +//--------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include + +using namespace std; + +class ScsiLoop +{ + public: + ScsiLoop() = default; + ~ScsiLoop() = default; + + int run(const vector &args); + + private: + void Banner(const vector &) const; + static void TerminationHandler(int signum); + bool ParseArgument(const vector &); + bool SetLogLevel(const string &); +}; diff --git a/cpp/scsiloop/scsiloop_cout.cpp b/cpp/scsiloop/scsiloop_cout.cpp new file mode 100644 index 00000000..9700b22c --- /dev/null +++ b/cpp/scsiloop/scsiloop_cout.cpp @@ -0,0 +1,40 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded for Raspberry Pi +// Loopback tester utility +// +// Copyright (C) 2022 akuker +// +//--------------------------------------------------------------------------- + +#include "scsiloop_cout.h" +#include + +using namespace std; + +void ScsiLoop_Cout::StartTest(const string &test_name) +{ + cout << CYAN << "Testing " << test_name << ":" << WHITE; +} +void ScsiLoop_Cout::PrintUpdate() +{ + cout << "."; +} + +void ScsiLoop_Cout::FinishTest(const string &test_name, int failures) +{ + if (failures == 0) { + cout << GREEN << "OK!" << WHITE << endl; + } else { + cout << RED << test_name << " FAILED! - " << failures << " errors!" << WHITE << endl; + } +} + +void ScsiLoop_Cout::PrintErrors(vector &test_errors) +{ + if (test_errors.size() > 0) { + for (auto err_string : test_errors) { + cout << RED << err_string << endl; + } + } +} \ No newline at end of file diff --git a/cpp/scsiloop/scsiloop_cout.h b/cpp/scsiloop/scsiloop_cout.h new file mode 100644 index 00000000..90fb448a --- /dev/null +++ b/cpp/scsiloop/scsiloop_cout.h @@ -0,0 +1,33 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded for Raspberry Pi +// Loopback tester utility +// +// Copyright (C) 2022 akuker +// +//--------------------------------------------------------------------------- + +#include +#include + +using namespace std; + +class ScsiLoop_Cout +{ + public: + static void StartTest(const string &test_name); + static void PrintUpdate(); + static void FinishTest(const string &test_name, int failures); + static void PrintErrors(vector &test_errors); + + private: + const static inline string RESET = "\033[0m"; + const static inline string BLACK = "\033[30m"; /* Black */ + const static inline string RED = "\033[31m"; /* Red */ + const static inline string GREEN = "\033[32m"; /* Green */ + const static inline string YELLOW = "\033[33m"; /* Yellow */ + const static inline string BLUE = "\033[34m"; /* Blue */ + const static inline string MAGENTA = "\033[35m"; /* Magenta */ + const static inline string CYAN = "\033[36m"; /* Cyan */ + const static inline string WHITE = "\033[37m"; /* White */ +}; \ No newline at end of file diff --git a/cpp/scsiloop/scsiloop_gpio.cpp b/cpp/scsiloop/scsiloop_gpio.cpp new file mode 100644 index 00000000..f7f6a3b5 --- /dev/null +++ b/cpp/scsiloop/scsiloop_gpio.cpp @@ -0,0 +1,592 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded for Raspberry Pi +// Loopback tester utility +// +// Copyright (C) 2022 akuker +// +//--------------------------------------------------------------------------- + +#include "scsiloop/scsiloop_gpio.h" +#include "hal/gpiobus_factory.h" +#include "hal/sbc_version.h" +#include "scsiloop/scsiloop_cout.h" +#include "shared/log.h" + +#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 + +ScsiLoop_GPIO::ScsiLoop_GPIO() +{ + LOGTRACE("%s", __PRETTY_FUNCTION__); + + bus = GPIOBUS_Factory::Create(BUS::mode_e::TARGET); + if (bus == nullptr) { + throw bus_exception("Unable to create bus"); + } + + // At compile time, we don't know which type of board we're using. So, we'll build these at runtime. + // TODO: This logic should be re-structured when/if other variants of SBCs are added in. + if (SBC_Version::IsRaspberryPi()) { + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_DT0, .connected_pin = PIN_ACK, .dir_ctrl_pin = PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_DT1, .connected_pin = PIN_SEL, .dir_ctrl_pin = PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_DT2, .connected_pin = PIN_ATN, .dir_ctrl_pin = PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_DT3, .connected_pin = PIN_RST, .dir_ctrl_pin = PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_DT4, .connected_pin = PIN_CD, .dir_ctrl_pin = PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_DT5, .connected_pin = PIN_IO, .dir_ctrl_pin = PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_DT6, .connected_pin = PIN_MSG, .dir_ctrl_pin = PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_DT7, .connected_pin = PIN_REQ, .dir_ctrl_pin = PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_DP, .connected_pin = PIN_BSY, .dir_ctrl_pin = PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_ATN, .connected_pin = PIN_DT2, .dir_ctrl_pin = PIN_IND}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_RST, .connected_pin = PIN_DT3, .dir_ctrl_pin = PIN_IND}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_ACK, .connected_pin = PIN_DT0, .dir_ctrl_pin = PIN_IND}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_REQ, .connected_pin = PIN_DT7, .dir_ctrl_pin = PIN_TAD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_MSG, .connected_pin = PIN_DT6, .dir_ctrl_pin = PIN_TAD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_CD, .connected_pin = PIN_DT4, .dir_ctrl_pin = PIN_TAD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_IO, .connected_pin = PIN_DT5, .dir_ctrl_pin = PIN_TAD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_BSY, .connected_pin = PIN_DP, .dir_ctrl_pin = PIN_TAD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = PIN_SEL, .connected_pin = PIN_DT1, .dir_ctrl_pin = PIN_IND}); + pin_name_lookup[PIN_DT0] = " d0"; + pin_name_lookup[PIN_DT1] = " d1"; + pin_name_lookup[PIN_DT2] = " d2"; + pin_name_lookup[PIN_DT3] = " d3"; + pin_name_lookup[PIN_DT4] = " d4"; + pin_name_lookup[PIN_DT5] = " d5"; + pin_name_lookup[PIN_DT6] = " d6"; + pin_name_lookup[PIN_DT7] = " d7"; + pin_name_lookup[PIN_DP] = " dp"; + pin_name_lookup[PIN_ATN] = "atn"; + pin_name_lookup[PIN_RST] = "rst"; + pin_name_lookup[PIN_ACK] = "ack"; + pin_name_lookup[PIN_REQ] = "req"; + pin_name_lookup[PIN_MSG] = "msg"; + pin_name_lookup[PIN_CD] = " cd"; + pin_name_lookup[PIN_IO] = " io"; + pin_name_lookup[PIN_BSY] = "bsy"; + pin_name_lookup[PIN_SEL] = "sel"; + pin_name_lookup[PIN_IND] = "ind"; + pin_name_lookup[PIN_TAD] = "tad"; + pin_name_lookup[PIN_DTD] = "dtd"; + + local_pin_dtd = PIN_DTD; + local_pin_tad = PIN_TAD; + local_pin_ind = PIN_IND; + local_pin_ack = PIN_ACK; + local_pin_sel = PIN_SEL; + local_pin_atn = PIN_ATN; + local_pin_rst = PIN_RST; + local_pin_cd = PIN_CD; + local_pin_io = PIN_IO; + local_pin_msg = PIN_MSG; + local_pin_req = PIN_REQ; + local_pin_bsy = PIN_BSY; + local_pin_dt0 = PIN_DT0; + local_pin_dt1 = PIN_DT1; + local_pin_dt2 = PIN_DT2; + local_pin_dt3 = PIN_DT3; + local_pin_dt4 = PIN_DT4; + local_pin_dt5 = PIN_DT5; + local_pin_dt6 = PIN_DT6; + local_pin_dt7 = PIN_DT7; + local_pin_dp = PIN_DP; + + } else if (SBC_Version::GetSbcVersion() == SBC_Version::sbc_version_type::sbc_bananapi_m2_plus) { + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_DT0, .connected_pin = BPI_PIN_ACK, .dir_ctrl_pin = BPI_PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_DT1, .connected_pin = BPI_PIN_SEL, .dir_ctrl_pin = BPI_PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_DT2, .connected_pin = BPI_PIN_ATN, .dir_ctrl_pin = BPI_PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_DT3, .connected_pin = BPI_PIN_RST, .dir_ctrl_pin = BPI_PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_DT4, .connected_pin = BPI_PIN_CD, .dir_ctrl_pin = BPI_PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_DT5, .connected_pin = BPI_PIN_IO, .dir_ctrl_pin = BPI_PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_DT6, .connected_pin = BPI_PIN_MSG, .dir_ctrl_pin = BPI_PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_DT7, .connected_pin = BPI_PIN_REQ, .dir_ctrl_pin = BPI_PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_DP, .connected_pin = BPI_PIN_BSY, .dir_ctrl_pin = BPI_PIN_DTD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_ATN, .connected_pin = BPI_PIN_DT2, .dir_ctrl_pin = BPI_PIN_IND}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_RST, .connected_pin = BPI_PIN_DT3, .dir_ctrl_pin = BPI_PIN_IND}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_ACK, .connected_pin = BPI_PIN_DT0, .dir_ctrl_pin = BPI_PIN_IND}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_REQ, .connected_pin = BPI_PIN_DT7, .dir_ctrl_pin = BPI_PIN_TAD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_MSG, .connected_pin = BPI_PIN_DT6, .dir_ctrl_pin = BPI_PIN_TAD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_CD, .connected_pin = BPI_PIN_DT4, .dir_ctrl_pin = BPI_PIN_TAD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_IO, .connected_pin = BPI_PIN_DT5, .dir_ctrl_pin = BPI_PIN_TAD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_BSY, .connected_pin = BPI_PIN_DP, .dir_ctrl_pin = BPI_PIN_TAD}); + loopback_conn_table.push_back( + loopback_connection{.this_pin = BPI_PIN_SEL, .connected_pin = BPI_PIN_DT1, .dir_ctrl_pin = BPI_PIN_IND}); + + pin_name_lookup[BPI_PIN_DT0] = " d0"; + pin_name_lookup[BPI_PIN_DT1] = " d1"; + pin_name_lookup[BPI_PIN_DT2] = " d2"; + pin_name_lookup[BPI_PIN_DT3] = " d3"; + pin_name_lookup[BPI_PIN_DT4] = " d4"; + pin_name_lookup[BPI_PIN_DT5] = " d5"; + pin_name_lookup[BPI_PIN_DT6] = " d6"; + pin_name_lookup[BPI_PIN_DT7] = " d7"; + pin_name_lookup[BPI_PIN_DP] = " dp"; + pin_name_lookup[BPI_PIN_ATN] = "atn"; + pin_name_lookup[BPI_PIN_RST] = "rst"; + pin_name_lookup[BPI_PIN_ACK] = "ack"; + pin_name_lookup[BPI_PIN_REQ] = "req"; + pin_name_lookup[BPI_PIN_MSG] = "msg"; + pin_name_lookup[BPI_PIN_CD] = " cd"; + pin_name_lookup[BPI_PIN_IO] = " io"; + pin_name_lookup[BPI_PIN_BSY] = "bsy"; + pin_name_lookup[BPI_PIN_SEL] = "sel"; + pin_name_lookup[BPI_PIN_IND] = "ind"; + pin_name_lookup[BPI_PIN_TAD] = "tad"; + pin_name_lookup[BPI_PIN_DTD] = "dtd"; + + local_pin_dtd = BPI_PIN_DTD; + local_pin_tad = BPI_PIN_TAD; + local_pin_ind = BPI_PIN_IND; + local_pin_ack = BPI_PIN_ACK; + local_pin_sel = BPI_PIN_SEL; + local_pin_atn = BPI_PIN_ATN; + local_pin_rst = BPI_PIN_RST; + local_pin_cd = BPI_PIN_CD; + local_pin_io = BPI_PIN_IO; + local_pin_msg = BPI_PIN_MSG; + local_pin_req = BPI_PIN_REQ; + local_pin_bsy = BPI_PIN_BSY; + local_pin_dt0 = BPI_PIN_DT0; + local_pin_dt1 = BPI_PIN_DT1; + local_pin_dt2 = BPI_PIN_DT2; + local_pin_dt3 = BPI_PIN_DT3; + local_pin_dt4 = BPI_PIN_DT4; + local_pin_dt5 = BPI_PIN_DT5; + local_pin_dt6 = BPI_PIN_DT6; + local_pin_dt7 = BPI_PIN_DT7; + local_pin_dp = BPI_PIN_DP; + + } else { + LOGERROR("Unsupported board version: %s", SBC_Version::GetString()->c_str()); + } +} + +void ScsiLoop_GPIO::Cleanup() +{ + bus->Cleanup(); +} + +// Set transceivers IC1 and IC2 to OUTPUT +void ScsiLoop_GPIO::set_dtd_out() +{ + LOGTRACE("%s", __PRETTY_FUNCTION__); + bus->SetControl(local_pin_dtd, DTD_OUT); +} + +// Set transceivers IC1 and IC2 to INPUT +void ScsiLoop_GPIO::set_dtd_in() +{ + LOGTRACE("%s", __PRETTY_FUNCTION__); + bus->SetControl(local_pin_dtd, DTD_IN); +} +// Set transceiver IC4 to OUTPUT +void ScsiLoop_GPIO::set_ind_out() +{ + LOGTRACE("%s", __PRETTY_FUNCTION__); + bus->SetControl(local_pin_ind, IND_OUT); +} +// Set transceiver IC4 to INPUT +void ScsiLoop_GPIO::set_ind_in() +{ + LOGTRACE("%s", __PRETTY_FUNCTION__); + bus->SetControl(local_pin_ind, IND_IN); +} +// Set transceiver IC3 to OUTPUT +void ScsiLoop_GPIO::set_tad_out() +{ + LOGTRACE("%s", __PRETTY_FUNCTION__); + bus->SetControl(local_pin_tad, TAD_OUT); +} +// Set transceiver IC3 to INPUT +void ScsiLoop_GPIO::set_tad_in() +{ + LOGTRACE("%s", __PRETTY_FUNCTION__); + bus->SetControl(local_pin_tad, TAD_IN); +} + +// Set the specified transciever to an OUTPUT. All of the other transceivers +// will be set to inputs. If a non-existent direction gpio is specified, this +// will set all of the transceivers to inputs. +void ScsiLoop_GPIO::set_output_channel(int out_gpio) +{ + LOGTRACE("%s tad: %d dtd: %d ind: %d", CONNECT_DESC.c_str(), (int)local_pin_tad, (int)local_pin_dtd, + (int)local_pin_ind); + if (out_gpio == local_pin_tad) + set_tad_out(); + else + set_tad_in(); + if (out_gpio == local_pin_dtd) + set_dtd_out(); + else + set_dtd_in(); + if (out_gpio == local_pin_ind) + set_ind_out(); + else + set_ind_in(); +} + +// Initialize the GPIO library, set all of the gpios associated with SCSI signals to outputs and set +// all of the direction control gpios to outputs +void ScsiLoop_GPIO::loopback_setup() +{ + LOGTRACE("%s", __PRETTY_FUNCTION__); + + for (loopback_connection cur_gpio : loopback_conn_table) { + if (cur_gpio.this_pin == -1) { + continue; + } + bus->PinConfig(cur_gpio.this_pin, GPIO_OUTPUT); + bus->PullConfig(cur_gpio.this_pin, GPIO_PULLNONE); + } + + // Setup direction control + if (local_pin_ind != -1) { + bus->PinConfig(local_pin_ind, GPIO_OUTPUT); + } + if (local_pin_tad != -1) { + bus->PinConfig(local_pin_tad, GPIO_OUTPUT); + } + if (local_pin_dtd != -1) { + bus->PinConfig(local_pin_dtd, GPIO_OUTPUT); + } +} + +// Main test procedure.This will execute for each of the SCSI pins to make sure its connected +// properly. +int ScsiLoop_GPIO::test_gpio_pin(loopback_connection &gpio_rec, vector &error_list, bool &loopback_adapter_missing) +{ + LOGTRACE("%s", __PRETTY_FUNCTION__); + + int err_count = 0; + int sleep_time = 1000; + + LOGTRACE("dir ctrl pin: %d", (int)gpio_rec.dir_ctrl_pin); + set_output_channel(gpio_rec.dir_ctrl_pin); + usleep(sleep_time); + + // Set all GPIOs high (initialize to a known state) + for (auto cur_gpio : loopback_conn_table) { + // bus->SetSignal(cur_gpio.this_pin, OFF); + bus->SetMode(cur_gpio.this_pin, GPIO_INPUT); + } + + usleep(sleep_time); + bus->Acquire(); + + // ############################################ + // set the test gpio low + bus->SetMode(gpio_rec.this_pin, GPIO_OUTPUT); + bus->SetSignal(gpio_rec.this_pin, ON); + + usleep(sleep_time); + + bus->Acquire(); + // loop through all of the gpios + for (auto cur_gpio : loopback_conn_table) { + printf("."); + // all of the gpios should be high except for the test gpio and the connected gpio + LOGTRACE("calling bus->GetSignal(%d)", (int)cur_gpio.this_pin); + auto cur_val = bus->GetSignal(cur_gpio.this_pin); + LOGDEBUG("%d [%s] is %d", (int)cur_gpio.this_pin, pin_name_lookup.at(cur_gpio.this_pin).c_str(), (int)cur_val); + + if (cur_gpio.this_pin == gpio_rec.this_pin) { + if (cur_val != ON) { + error_list.push_back( + fmt::format("Loopback test: Test commanded GPIO {} [{}] to be low, but it did not respond", + cur_gpio.this_pin, pin_name_lookup.at(cur_gpio.this_pin))); + err_count++; + } + } else if (cur_gpio.this_pin == gpio_rec.connected_pin) { + if (cur_val != ON) { + error_list.push_back( + fmt::format("Loopback test: GPIO {} [{}] should be driven low, but {} [{}] did not affect it", + cur_gpio.this_pin, pin_name_lookup.at(cur_gpio.this_pin), cur_gpio.connected_pin, + pin_name_lookup.at(cur_gpio.connected_pin))); + + err_count++; + } + else{ + loopback_adapter_missing = false; + } + } else { + if (cur_val != OFF) { + error_list.push_back( + fmt::format("Loopback test: GPIO {} [{}] was incorrectly pulled low, when it shouldn't be", + cur_gpio.this_pin, pin_name_lookup.at(cur_gpio.this_pin))); + err_count++; + } + } + } + + // ############################################ + // set the transceivers to input + set_output_channel(-1); + + usleep(sleep_time); + + bus->Acquire(); + // # loop through all of the gpios + for (auto cur_gpio : loopback_conn_table) { + printf("."); + // all of the gpios should be high except for the test gpio + LOGTRACE("calling bus->GetSignal(%d)", (int)cur_gpio.this_pin); + auto cur_val = bus->GetSignal(cur_gpio.this_pin); + LOGDEBUG("%d [%s] is %d", (int)cur_gpio.this_pin, pin_name_lookup.at(cur_gpio.this_pin).c_str(), (int)cur_val); + + if (cur_gpio.this_pin == gpio_rec.this_pin) { + if (cur_val != ON) { + error_list.push_back( + fmt::format("Loopback test: Test commanded GPIO {} [{}] to be low, but it did not respond", + cur_gpio.this_pin, pin_name_lookup.at(cur_gpio.this_pin))); + err_count++; + } + } else { + if (cur_val != OFF) { + error_list.push_back( + fmt::format("Loopback test: GPIO {} [{}] was incorrectly pulled low, when it shouldn't be", + cur_gpio.this_pin, pin_name_lookup.at(cur_gpio.this_pin))); + err_count++; + } + } + } + + // Set the transceiver back to output + set_output_channel(gpio_rec.dir_ctrl_pin); + usleep(sleep_time); + + // ############################################# + // set the test gpio high + bus->SetMode(gpio_rec.this_pin, GPIO_OUTPUT); + bus->SetSignal(gpio_rec.this_pin, OFF); + + usleep(sleep_time); + + bus->Acquire(); + // loop through all of the gpios + for (auto cur_gpio : loopback_conn_table) { + printf("."); + + auto cur_val = bus->GetSignal(cur_gpio.this_pin); + LOGTRACE("%d [%s] is %d", (int)cur_gpio.this_pin, pin_name_lookup.at(cur_gpio.this_pin).c_str(), (int)cur_val); + + if (cur_gpio.this_pin == gpio_rec.this_pin) { + if (cur_val != OFF) { + error_list.push_back( + fmt::format("Loopback test: Test commanded GPIO {} [{}] to be high, but it did not respond", + cur_gpio.this_pin, pin_name_lookup.at(cur_gpio.this_pin))); + err_count++; + } + } else { + if (cur_val != OFF) { + error_list.push_back( + fmt::format("Loopback test: GPIO {} [{}] was incorrectly pulled low, when it shouldn't be", + cur_gpio.this_pin, pin_name_lookup.at(cur_gpio.this_pin))); + err_count++; + } + } + } + + ScsiLoop_Cout::FinishTest(fmt::format("GPIO {:<3} [{}]", gpio_rec.this_pin, pin_name_lookup.at(gpio_rec.this_pin)), + err_count); + return err_count; +} + +int ScsiLoop_GPIO::RunLoopbackTest(vector &error_list) +{ + int errors = 0; + bool loopback_adapter_missing = true; + LOGTRACE("%s", __PRETTY_FUNCTION__); + loopback_setup(); + + for (auto cur_gpio : loopback_conn_table) { + ScsiLoop_Cout::StartTest( + fmt::format("GPIO {:<3}[{}]", cur_gpio.this_pin, pin_name_lookup.at(cur_gpio.this_pin).c_str())); + + errors += test_gpio_pin(cur_gpio, error_list, loopback_adapter_missing); + } + + if(loopback_adapter_missing){ + error_list.push_back("All of the loop-backed signals failed. Is the loopback adapter missing? (A special cable is required to use scsiloop)"); + } + return errors; +} + +void ScsiLoop_GPIO::dat_input_test_setup() +{ + LOGTRACE("%s", __PRETTY_FUNCTION__); + + for (loopback_connection cur_gpio : loopback_conn_table) { + if (cur_gpio.this_pin == -1) { + continue; + } + bus->PinConfig(cur_gpio.this_pin, GPIO_OUTPUT); + bus->PullConfig(cur_gpio.this_pin, GPIO_PULLNONE); + } + + bus->PinConfig(local_pin_dt0, GPIO_INPUT); + bus->PinConfig(local_pin_dt1, GPIO_INPUT); + bus->PinConfig(local_pin_dt2, GPIO_INPUT); + bus->PinConfig(local_pin_dt3, GPIO_INPUT); + bus->PinConfig(local_pin_dt4, GPIO_INPUT); + bus->PinConfig(local_pin_dt5, GPIO_INPUT); + bus->PinConfig(local_pin_dt6, GPIO_INPUT); + bus->PinConfig(local_pin_dt7, GPIO_INPUT); + + set_dtd_in(); + set_tad_out(); + set_ind_out(); +} + +void ScsiLoop_GPIO::dat_output_test_setup() +{ + LOGTRACE("%s", __PRETTY_FUNCTION__); + + for (loopback_connection cur_gpio : loopback_conn_table) { + if (cur_gpio.this_pin == -1) { + continue; + } + bus->PinConfig(cur_gpio.this_pin, GPIO_INPUT); + bus->PullConfig(cur_gpio.this_pin, GPIO_PULLNONE); + } + + bus->PinConfig(local_pin_dt0, GPIO_OUTPUT); + bus->PinConfig(local_pin_dt1, GPIO_OUTPUT); + bus->PinConfig(local_pin_dt2, GPIO_OUTPUT); + bus->PinConfig(local_pin_dt3, GPIO_OUTPUT); + bus->PinConfig(local_pin_dt4, GPIO_OUTPUT); + bus->PinConfig(local_pin_dt5, GPIO_OUTPUT); + bus->PinConfig(local_pin_dt6, GPIO_OUTPUT); + bus->PinConfig(local_pin_dt7, GPIO_OUTPUT); + + set_dtd_out(); + set_tad_in(); + set_ind_in(); +} + +void ScsiLoop_GPIO::set_dat_inputs_loop(uint8_t value) +{ + bus->SetSignal(local_pin_ack, (value >> 0) & 0x1); + bus->SetSignal(local_pin_sel, (value >> 1) & 0x1); + bus->SetSignal(local_pin_atn, (value >> 2) & 0x1); + bus->SetSignal(local_pin_rst, (value >> 3) & 0x1); + bus->SetSignal(local_pin_cd, (value >> 4) & 0x1); + bus->SetSignal(local_pin_io, (value >> 5) & 0x1); + bus->SetSignal(local_pin_msg, (value >> 6) & 0x1); + bus->SetSignal(local_pin_req, (value >> 7) & 0x1); +} + +uint8_t ScsiLoop_GPIO::get_dat_outputs_loop() +{ + uint8_t value = 0; + value |= ((bus->GetSignal(local_pin_ack) & 0x1) << 0); + value |= ((bus->GetSignal(local_pin_sel) & 0x1) << 1); + value |= ((bus->GetSignal(local_pin_atn) & 0x1) << 2); + value |= ((bus->GetSignal(local_pin_rst) & 0x1) << 3); + value |= ((bus->GetSignal(local_pin_cd) & 0x1) << 4); + value |= ((bus->GetSignal(local_pin_io) & 0x1) << 5); + value |= ((bus->GetSignal(local_pin_msg) & 0x1) << 6); + value |= ((bus->GetSignal(local_pin_req) & 0x1) << 7); + return value; +} + +int ScsiLoop_GPIO::RunDataInputTest(vector &error_list) +{ + int err_count = 0; + int delay_time_us = 1000; + dat_input_test_setup(); + + ScsiLoop_Cout::StartTest("data inputs "); + + for (uint32_t val = 0; val < UINT8_MAX; val++) { + set_dat_inputs_loop(val); + usleep(delay_time_us); + + bus->Acquire(); + uint8_t read_val = bus->GetDAT(); + + if (read_val != (uint8_t)(val & 0xFF)) { + error_list.push_back(fmt::format("DAT Inputs: Expected value {} but got {}", val, read_val)); + err_count++; + } + if ((val % 0x7) == 0) { + printf("."); + } + } + + ScsiLoop_Cout::FinishTest("DAT Inputs", err_count); + + set_dat_inputs_loop(0); + return err_count; +} + +int ScsiLoop_GPIO::RunDataOutputTest(vector &error_list) +{ + int err_count = 0; + int delay_time_us = 1000; + dat_output_test_setup(); + + ScsiLoop_Cout::StartTest("data outputs "); + + for (uint32_t val = 0; val < UINT8_MAX; val++) { + bus->SetDAT(val); + usleep(delay_time_us); + + bus->Acquire(); + uint8_t read_val = get_dat_outputs_loop(); + + if (read_val != (uint8_t)(val & 0xFF)) { + error_list.push_back(fmt::format("DAT Outputs: Expected value {} but got {}", val, read_val)); + err_count++; + } + if ((val % 0x7) == 0) { + printf("."); + } + } + + ScsiLoop_Cout::FinishTest("DAT Outputs", err_count); + + return err_count; +} \ No newline at end of file diff --git a/cpp/scsiloop/scsiloop_gpio.h b/cpp/scsiloop/scsiloop_gpio.h new file mode 100644 index 00000000..9e5c217b --- /dev/null +++ b/cpp/scsiloop/scsiloop_gpio.h @@ -0,0 +1,81 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded for Raspberry Pi +// Loopback tester utility +// +// Copyright (C) 2022 akuker +// +//--------------------------------------------------------------------------- + +#include +#include +#include + +#include "hal/gpiobus.h" + +using namespace std; + +class ScsiLoop_GPIO +{ + public: + ScsiLoop_GPIO(); + ~ScsiLoop_GPIO() = default; + int RunLoopbackTest(vector &error_list); + int RunDataInputTest(vector &error_list); + int RunDataOutputTest(vector &error_list); + void Cleanup(); + + private: + struct loopback_connections_struct { + int this_pin; + int connected_pin; + int dir_ctrl_pin; + }; + typedef loopback_connections_struct loopback_connection; + + std::map pin_name_lookup; + std::vector loopback_conn_table; + + void set_dtd_out(); + void set_dtd_in(); + void set_ind_out(); + void set_ind_in(); + void set_tad_out(); + void set_tad_in(); + void set_output_channel(int out_gpio); + void loopback_setup(); + + int test_gpio_pin(loopback_connection &gpio_rec, vector &error_list, bool &loopback_adapter_missing); + + void set_dat_inputs_loop(uint8_t value); + + void dat_input_test_setup(); + void dat_output_test_setup(); + + uint8_t get_dat_outputs_loop(); + + int local_pin_dtd = -1; + int local_pin_tad = -1; + int local_pin_ind = -1; + int local_pin_ack = -1; + int local_pin_sel = -1; + int local_pin_atn = -1; + int local_pin_rst = -1; + int local_pin_cd = -1; + int local_pin_io = -1; + int local_pin_msg = -1; + int local_pin_req = -1; + int local_pin_bsy = -1; + + int local_pin_dt0 = -1; + int local_pin_dt1 = -1; + int local_pin_dt2 = -1; + int local_pin_dt3 = -1; + int local_pin_dt4 = -1; + int local_pin_dt5 = -1; + int local_pin_dt6 = -1; + int local_pin_dt7 = -1; + int local_pin_dp = -1; + + shared_ptr bus; +}; \ No newline at end of file diff --git a/cpp/scsiloop/scsiloop_timer.cpp b/cpp/scsiloop/scsiloop_timer.cpp new file mode 100644 index 00000000..73982684 --- /dev/null +++ b/cpp/scsiloop/scsiloop_timer.cpp @@ -0,0 +1,91 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded for Raspberry Pi +// Loopback tester utility +// +// Copyright (C) 2022 akuker +// +//--------------------------------------------------------------------------- + +#include "scsiloop_timer.h" +#include "hal/systimer.h" +#include "scsiloop/scsiloop_cout.h" +#include "shared/log.h" + +int ScsiLoop_Timer::RunTimerTest(vector &error_list) +{ + uint32_t timer_test_failures = 0; + + ScsiLoop_Cout::StartTest("hardware timer"); + + // Allow +/- 2% tolerance when testing the timers + double timer_tolerance_percent = 0.02; + const uint32_t one_second_in_ns = 1000000; + + //------------------------------------------------------ + // Test SysTimer::GetTimerLow() + LOGDEBUG("++ Testing SysTimer::GetTimerLow()") + uint32_t before = SysTimer::GetTimerLow(); + for (int i = 0; i < 10; i++) { + usleep(100000); + ScsiLoop_Cout::PrintUpdate(); + } + uint32_t after = SysTimer::GetTimerLow(); + + uint32_t elapsed_nanosecs = after - before; + + LOGDEBUG("Elapsed time: %d %08X", elapsed_nanosecs, elapsed_nanosecs); + + if ((elapsed_nanosecs > (one_second_in_ns * (1.0 + timer_tolerance_percent))) || + (elapsed_nanosecs < (one_second_in_ns * (1.0 - timer_tolerance_percent)))) { + error_list.push_back(fmt::format("SysTimer::GetTimerLow() test: Expected time approx: {}, but actually {}", + one_second_in_ns, elapsed_nanosecs)); + timer_test_failures++; + } else { + ScsiLoop_Cout::PrintUpdate(); + } + + //------------------------------------------------------ + // Test SysTimer::SleepUsec() + LOGDEBUG("++ Testing SysTimer::SleepUsec()") + + uint32_t expected_usec_result = 1000 * 100; + before = SysTimer::GetTimerLow(); + for (int i = 0; i < 100; i++) { + SysTimer::SleepUsec(1000); + } + after = SysTimer::GetTimerLow(); + elapsed_nanosecs = after - before; + LOGDEBUG("SysTimer::SleepUsec() Average %d", (uint32_t)(elapsed_nanosecs / 100)); + + if ((elapsed_nanosecs > expected_usec_result * (1.0 + timer_tolerance_percent)) || + (elapsed_nanosecs < expected_usec_result * (1.0 - timer_tolerance_percent))) { + error_list.push_back(fmt::format("SysTimer::SleepUsec Test: Expected time approx: {}, but actually {}", + expected_usec_result, elapsed_nanosecs)); + timer_test_failures++; + } else { + ScsiLoop_Cout::PrintUpdate(); + } + + //------------------------------------------------------ + // Test SysTimer::SleepNsec() + LOGDEBUG("++ Testing SysTimer::SleepNsec()") + + before = SysTimer::GetTimerLow(); + SysTimer::SleepNsec(1000000); + after = SysTimer::GetTimerLow(); + LOGDEBUG("SysTimer::SleepNSec: %d (expected ~1000)", (uint32_t)(after - before)); + + elapsed_nanosecs = after - before; + if ((elapsed_nanosecs > (1000 * (1.0 + timer_tolerance_percent))) || + (elapsed_nanosecs < (1000 * (1.0 - timer_tolerance_percent)))) { + error_list.push_back( + fmt::format("SysTimer::SleepNsec Test: Expected time approx: 1000, but actually {}", elapsed_nanosecs)); + timer_test_failures++; + } else { + ScsiLoop_Cout::PrintUpdate(); + } + + ScsiLoop_Cout::FinishTest("hardware timer", timer_test_failures); + return timer_test_failures; +} diff --git a/cpp/scsiloop/scsiloop_timer.h b/cpp/scsiloop/scsiloop_timer.h new file mode 100644 index 00000000..96394242 --- /dev/null +++ b/cpp/scsiloop/scsiloop_timer.h @@ -0,0 +1,19 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded for Raspberry Pi +// Loopback tester utility +// +// Copyright (C) 2022 akuker +// +//--------------------------------------------------------------------------- + +#include +#include + +using namespace std; + +class ScsiLoop_Timer +{ + public: + static int RunTimerTest(vector &error_list); +}; \ No newline at end of file diff --git a/cpp/shared/scsi.h b/cpp/shared/scsi.h index c4309bdb..c8a4279b 100644 --- a/cpp/shared/scsi.h +++ b/cpp/shared/scsi.h @@ -13,155 +13,166 @@ using namespace std; -namespace scsi_defs { - enum class scsi_level : int { - SCSI_1_CCS = 1, - SCSI_2 = 2, - SPC = 3, - SPC_2 = 4, - SPC_3 = 5, - SPC_4 = 6, - SPC_5 = 7, - SPC_6 = 8 - }; - - enum class device_type : int { - DIRECT_ACCESS = 0, - PRINTER = 2, - PROCESSOR = 3, - CD_ROM = 5, - OPTICAL_MEMORY = 7, - COMMUNICATIONS = 9 - }; - - enum class scsi_command : int { - eCmdTestUnitReady = 0x00, - eCmdRezero = 0x01, - eCmdRequestSense = 0x03, - eCmdFormatUnit = 0x04, - eCmdReassignBlocks = 0x07, - eCmdRead6 = 0x08, - // Bridge specific command - eCmdGetMessage10 = 0x08, - // DaynaPort specific command - eCmdRetrieveStats = 0x09, - eCmdWrite6 = 0x0A, - // Bridge specific ommand - eCmdSendMessage10 = 0x0A, - eCmdPrint = 0x0A, - eCmdSeek6 = 0x0B, - // DaynaPort specific command - eCmdSetIfaceMode = 0x0C, - // DaynaPort specific command - eCmdSetMcastAddr = 0x0D, - // DaynaPort specific command - eCmdEnableInterface = 0x0E, - eCmdSynchronizeBuffer = 0x10, - eCmdInquiry = 0x12, - eCmdModeSelect6 = 0x15, - eCmdReserve6 = 0x16, - eCmdRelease6 = 0x17, - eCmdModeSense6 = 0x1A, - eCmdStartStop = 0x1B, - eCmdStopPrint = 0x1B, - eCmdSendDiagnostic = 0x1D, - eCmdPreventAllowMediumRemoval = 0x1E, - eCmdReadCapacity10 = 0x25, - eCmdRead10 = 0x28, - eCmdWrite10 = 0x2A, - eCmdSeek10 = 0x2B, - eCmdVerify10 = 0x2F, - eCmdSynchronizeCache10 = 0x35, - eCmdReadDefectData10 = 0x37, - eCmdReadLong10 = 0x3E, - eCmdWriteLong10 = 0x3F, - eCmdReadToc = 0x43, - eCmdGetEventStatusNotification = 0x4A, - eCmdModeSelect10 = 0x55, - eCmdModeSense10 = 0x5A, - eCmdRead16 = 0x88, - eCmdWrite16 = 0x8A, - eCmdVerify16 = 0x8F, - eCmdSynchronizeCache16 = 0x91, - eCmdReadCapacity16_ReadLong16 = 0x9E, - eCmdWriteLong16 = 0x9F, - eCmdReportLuns = 0xA0 - }; - - enum class status : int { - GOOD = 0x00, - CHECK_CONDITION = 0x02, - RESERVATION_CONFLICT = 0x18 - }; - - enum class sense_key : int { - NO_SENSE = 0x00, - NOT_READY = 0x02, - MEDIUM_ERROR = 0x03, - ILLEGAL_REQUEST = 0x05, - UNIT_ATTENTION = 0x06, - DATA_PROTECT = 0x07, - ABORTED_COMMAND = 0x0b - }; - - enum class asc : int { - NO_ADDITIONAL_SENSE_INFORMATION = 0x00, - WRITE_FAULT = 0x03, - READ_FAULT = 0x11, - INVALID_COMMAND_OPERATION_CODE = 0x20, - LBA_OUT_OF_RANGE = 0x21, - INVALID_FIELD_IN_CDB = 0x24, - INVALID_LUN = 0x25, - INVALID_FIELD_IN_PARAMETER_LIST = 0x26, - WRITE_PROTECTED = 0x27, - NOT_READY_TO_READY_CHANGE = 0x28, - POWER_ON_OR_RESET = 0x29, - MEDIUM_NOT_PRESENT = 0x3a, - LOAD_OR_EJECT_FAILED = 0x53 - }; - - static const unordered_map> command_mapping = { - { scsi_command::eCmdTestUnitReady, make_pair(6, "TestUnitReady") }, - { scsi_command::eCmdRezero, make_pair(6, "Rezero") }, - { scsi_command::eCmdRequestSense, make_pair(6, "RequestSense") }, - { scsi_command::eCmdFormatUnit, make_pair(6, "FormatUnit") }, - { scsi_command::eCmdReassignBlocks, make_pair(6, "ReassignBlocks") }, - { scsi_command::eCmdRead6, make_pair(6, "Read6/GetMessage10") }, - { scsi_command::eCmdRetrieveStats,make_pair( 6, "RetrieveStats") }, - { scsi_command::eCmdWrite6, make_pair(6, "Write6/Print/SendMessage10") }, - { scsi_command::eCmdSeek6, make_pair(6, "Seek6") }, - { scsi_command::eCmdSetIfaceMode, make_pair(6, "SetIfaceMode") }, - { scsi_command::eCmdSetMcastAddr, make_pair(6, "SetMcastAddr") }, - { scsi_command::eCmdEnableInterface, make_pair(6, "EnableInterface") }, - { scsi_command::eCmdSynchronizeBuffer, make_pair(6, "SynchronizeBuffer") }, - { scsi_command::eCmdInquiry, make_pair(6, "Inquiry") }, - { scsi_command::eCmdModeSelect6, make_pair(6, "ModeSelect6") }, - { scsi_command::eCmdReserve6, make_pair(6, "Reserve6") }, - { scsi_command::eCmdRelease6, make_pair(6, "Release6") }, - { scsi_command::eCmdModeSense6, make_pair(6, "ModeSense6") }, - { scsi_command::eCmdStartStop, make_pair(6, "StartStop") }, - { scsi_command::eCmdStopPrint, make_pair(6, "StopPrint") }, - { scsi_command::eCmdSendDiagnostic, make_pair(6, "SendDiagnostic") }, - { scsi_command::eCmdPreventAllowMediumRemoval, make_pair(6, "PreventAllowMediumRemoval") }, - { scsi_command::eCmdReadCapacity10, make_pair(10, "ReadCapacity10") }, - { scsi_command::eCmdRead10, make_pair(10, "Read10") }, - { scsi_command::eCmdWrite10, make_pair(10, "Write10") }, - { scsi_command::eCmdSeek10, make_pair(10, "Seek10") }, - { scsi_command::eCmdVerify10, make_pair(10, "Verify10") }, - { scsi_command::eCmdSynchronizeCache10, make_pair(10, "SynchronizeCache10") }, - { scsi_command::eCmdReadDefectData10, make_pair(10, "ReadDefectData10") }, - { scsi_command::eCmdReadLong10, make_pair(10, "ReadLong10") }, - { scsi_command::eCmdWriteLong10, make_pair(10, "WriteLong10") }, - { scsi_command::eCmdReadToc, make_pair(10, "ReadToc") }, - { scsi_command::eCmdGetEventStatusNotification, make_pair(10, "GetEventStatusNotification") }, - { scsi_command::eCmdModeSelect10, make_pair(10, "ModeSelect10") }, - { scsi_command::eCmdModeSense10, make_pair(10, "ModeSense10") }, - { scsi_command::eCmdRead16, make_pair(16, "Read16") }, - { scsi_command::eCmdWrite16, make_pair(16, "Write16") }, - { scsi_command::eCmdVerify16, make_pair(16, "Verify16") }, - { scsi_command::eCmdSynchronizeCache16, make_pair(16, "SynchronizeCache16") }, - { scsi_command::eCmdReadCapacity16_ReadLong16, make_pair(16, "ReadCapacity16/ReadLong16") }, - { scsi_command::eCmdWriteLong16, make_pair(16, "WriteLong16") }, - { scsi_command::eCmdReportLuns, make_pair(12, "ReportLuns") } - }; +namespace scsi_defs +{ +enum class scsi_level : int { + SCSI_1_CCS = 1, + SCSI_2 = 2, + SPC = 3, + SPC_2 = 4, + SPC_3 = 5, + SPC_4 = 6, + SPC_5 = 7, + SPC_6 = 8 }; + +// Phase definitions +enum class phase_t : int { + busfree, + arbitration, + selection, + reselection, + command, + datain, + dataout, + status, + msgin, + msgout, + reserved +}; + +enum class device_type : int { + DIRECT_ACCESS = 0, + PRINTER = 2, + PROCESSOR = 3, + CD_ROM = 5, + OPTICAL_MEMORY = 7, + COMMUNICATIONS = 9 +}; + +enum class scsi_command : int { + eCmdTestUnitReady = 0x00, + eCmdRezero = 0x01, + eCmdRequestSense = 0x03, + eCmdFormatUnit = 0x04, + eCmdReassignBlocks = 0x07, + eCmdRead6 = 0x08, + // Bridge specific command + eCmdGetMessage10 = 0x08, + // DaynaPort specific command + eCmdRetrieveStats = 0x09, + eCmdWrite6 = 0x0A, + // Bridge specific ommand + eCmdSendMessage10 = 0x0A, + eCmdPrint = 0x0A, + eCmdSeek6 = 0x0B, + // DaynaPort specific command + eCmdSetIfaceMode = 0x0C, + // DaynaPort specific command + eCmdSetMcastAddr = 0x0D, + // DaynaPort specific command + eCmdEnableInterface = 0x0E, + eCmdSynchronizeBuffer = 0x10, + eCmdInquiry = 0x12, + eCmdModeSelect6 = 0x15, + eCmdReserve6 = 0x16, + eCmdRelease6 = 0x17, + eCmdModeSense6 = 0x1A, + eCmdStartStop = 0x1B, + eCmdStopPrint = 0x1B, + eCmdSendDiagnostic = 0x1D, + eCmdPreventAllowMediumRemoval = 0x1E, + eCmdReadCapacity10 = 0x25, + eCmdRead10 = 0x28, + eCmdWrite10 = 0x2A, + eCmdSeek10 = 0x2B, + eCmdVerify10 = 0x2F, + eCmdSynchronizeCache10 = 0x35, + eCmdReadDefectData10 = 0x37, + eCmdReadLong10 = 0x3E, + eCmdWriteLong10 = 0x3F, + eCmdReadToc = 0x43, + eCmdGetEventStatusNotification = 0x4A, + eCmdModeSelect10 = 0x55, + eCmdModeSense10 = 0x5A, + eCmdRead16 = 0x88, + eCmdWrite16 = 0x8A, + eCmdVerify16 = 0x8F, + eCmdSynchronizeCache16 = 0x91, + eCmdReadCapacity16_ReadLong16 = 0x9E, + eCmdWriteLong16 = 0x9F, + eCmdReportLuns = 0xA0 +}; + +enum class status : int { GOOD = 0x00, CHECK_CONDITION = 0x02, RESERVATION_CONFLICT = 0x18 }; + +enum class sense_key : int { + NO_SENSE = 0x00, + NOT_READY = 0x02, + MEDIUM_ERROR = 0x03, + ILLEGAL_REQUEST = 0x05, + UNIT_ATTENTION = 0x06, + DATA_PROTECT = 0x07, + ABORTED_COMMAND = 0x0b +}; + +enum class asc : int { + NO_ADDITIONAL_SENSE_INFORMATION = 0x00, + WRITE_FAULT = 0x03, + READ_FAULT = 0x11, + INVALID_COMMAND_OPERATION_CODE = 0x20, + LBA_OUT_OF_RANGE = 0x21, + INVALID_FIELD_IN_CDB = 0x24, + INVALID_LUN = 0x25, + INVALID_FIELD_IN_PARAMETER_LIST = 0x26, + WRITE_PROTECTED = 0x27, + NOT_READY_TO_READY_CHANGE = 0x28, + POWER_ON_OR_RESET = 0x29, + MEDIUM_NOT_PRESENT = 0x3a, + LOAD_OR_EJECT_FAILED = 0x53 +}; + +static const unordered_map> command_mapping = { + {scsi_command::eCmdTestUnitReady, make_pair(6, "TestUnitReady")}, + {scsi_command::eCmdRezero, make_pair(6, "Rezero")}, + {scsi_command::eCmdRequestSense, make_pair(6, "RequestSense")}, + {scsi_command::eCmdFormatUnit, make_pair(6, "FormatUnit")}, + {scsi_command::eCmdReassignBlocks, make_pair(6, "ReassignBlocks")}, + {scsi_command::eCmdRead6, make_pair(6, "Read6/GetMessage10")}, + {scsi_command::eCmdRetrieveStats, make_pair(6, "RetrieveStats")}, + {scsi_command::eCmdWrite6, make_pair(6, "Write6/Print/SendMessage10")}, + {scsi_command::eCmdSeek6, make_pair(6, "Seek6")}, + {scsi_command::eCmdSetIfaceMode, make_pair(6, "SetIfaceMode")}, + {scsi_command::eCmdSetMcastAddr, make_pair(6, "SetMcastAddr")}, + {scsi_command::eCmdEnableInterface, make_pair(6, "EnableInterface")}, + {scsi_command::eCmdSynchronizeBuffer, make_pair(6, "SynchronizeBuffer")}, + {scsi_command::eCmdInquiry, make_pair(6, "Inquiry")}, + {scsi_command::eCmdModeSelect6, make_pair(6, "ModeSelect6")}, + {scsi_command::eCmdReserve6, make_pair(6, "Reserve6")}, + {scsi_command::eCmdRelease6, make_pair(6, "Release6")}, + {scsi_command::eCmdModeSense6, make_pair(6, "ModeSense6")}, + {scsi_command::eCmdStartStop, make_pair(6, "StartStop")}, + {scsi_command::eCmdStopPrint, make_pair(6, "StopPrint")}, + {scsi_command::eCmdSendDiagnostic, make_pair(6, "SendDiagnostic")}, + {scsi_command::eCmdPreventAllowMediumRemoval, make_pair(6, "PreventAllowMediumRemoval")}, + {scsi_command::eCmdReadCapacity10, make_pair(10, "ReadCapacity10")}, + {scsi_command::eCmdRead10, make_pair(10, "Read10")}, + {scsi_command::eCmdWrite10, make_pair(10, "Write10")}, + {scsi_command::eCmdSeek10, make_pair(10, "Seek10")}, + {scsi_command::eCmdVerify10, make_pair(10, "Verify10")}, + {scsi_command::eCmdSynchronizeCache10, make_pair(10, "SynchronizeCache10")}, + {scsi_command::eCmdReadDefectData10, make_pair(10, "ReadDefectData10")}, + {scsi_command::eCmdReadLong10, make_pair(10, "ReadLong10")}, + {scsi_command::eCmdWriteLong10, make_pair(10, "WriteLong10")}, + {scsi_command::eCmdReadToc, make_pair(10, "ReadToc")}, + {scsi_command::eCmdGetEventStatusNotification, make_pair(10, "GetEventStatusNotification")}, + {scsi_command::eCmdModeSelect10, make_pair(10, "ModeSelect10")}, + {scsi_command::eCmdModeSense10, make_pair(10, "ModeSense10")}, + {scsi_command::eCmdRead16, make_pair(16, "Read16")}, + {scsi_command::eCmdWrite16, make_pair(16, "Write16")}, + {scsi_command::eCmdVerify16, make_pair(16, "Verify16")}, + {scsi_command::eCmdSynchronizeCache16, make_pair(16, "SynchronizeCache16")}, + {scsi_command::eCmdReadCapacity16_ReadLong16, make_pair(16, "ReadCapacity16/ReadLong16")}, + {scsi_command::eCmdWriteLong16, make_pair(16, "WriteLong16")}, + {scsi_command::eCmdReportLuns, make_pair(12, "ReportLuns")}}; +}; // namespace scsi_defs diff --git a/cpp/test/abstract_controller_test.cpp b/cpp/test/abstract_controller_test.cpp index 61245aff..b6daa7c3 100644 --- a/cpp/test/abstract_controller_test.cpp +++ b/cpp/test/abstract_controller_test.cpp @@ -45,8 +45,8 @@ TEST(AbstractControllerTest, Reset) controller->AddDevice(device); - controller->SetPhase(BUS::phase_t::status); - EXPECT_EQ(BUS::phase_t::status, controller->GetPhase()); + controller->SetPhase(phase_t::status); + EXPECT_EQ(phase_t::status, controller->GetPhase()); controller->Reset(); EXPECT_TRUE(controller->IsBusFree()); EXPECT_EQ(status::GOOD, controller->GetStatus()); @@ -124,44 +124,44 @@ TEST(AbstractControllerTest, ProcessPhase) auto controller_manager = make_shared(*bus); MockAbstractController controller(controller_manager, 0); - controller.SetPhase(BUS::phase_t::selection); + controller.SetPhase(phase_t::selection); EXPECT_CALL(controller, Selection); controller.ProcessPhase(); - controller.SetPhase(BUS::phase_t::busfree); + controller.SetPhase(phase_t::busfree); EXPECT_CALL(controller, BusFree); controller.ProcessPhase(); - controller.SetPhase(BUS::phase_t::datain); + controller.SetPhase(phase_t::datain); EXPECT_CALL(controller, DataIn); controller.ProcessPhase(); - controller.SetPhase(BUS::phase_t::dataout); + controller.SetPhase(phase_t::dataout); EXPECT_CALL(controller, DataOut); controller.ProcessPhase(); - controller.SetPhase(BUS::phase_t::command); + controller.SetPhase(phase_t::command); EXPECT_CALL(controller, Command); controller.ProcessPhase(); - controller.SetPhase(BUS::phase_t::status); + controller.SetPhase(phase_t::status); EXPECT_CALL(controller, Status); controller.ProcessPhase(); - controller.SetPhase(BUS::phase_t::msgin); + controller.SetPhase(phase_t::msgin); EXPECT_CALL(controller, MsgIn); controller.ProcessPhase(); - controller.SetPhase(BUS::phase_t::msgout); + controller.SetPhase(phase_t::msgout); EXPECT_CALL(controller, MsgOut); controller.ProcessPhase(); - controller.SetPhase(BUS::phase_t::reselection); + controller.SetPhase(phase_t::reselection); EXPECT_THAT([&] { controller.ProcessPhase(); }, Throws(AllOf( Property(&scsi_exception::get_sense_key, sense_key::ABORTED_COMMAND), Property(&scsi_exception::get_asc, asc::NO_ADDITIONAL_SENSE_INFORMATION)))); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); EXPECT_THAT([&] { controller.ProcessPhase(); }, Throws(AllOf( Property(&scsi_exception::get_sense_key, sense_key::ABORTED_COMMAND), Property(&scsi_exception::get_asc, asc::NO_ADDITIONAL_SENSE_INFORMATION)))); diff --git a/cpp/test/bus_test.cpp b/cpp/test/bus_test.cpp index 8b0a0822..4b711dd6 100644 --- a/cpp/test/bus_test.cpp +++ b/cpp/test/bus_test.cpp @@ -7,130 +7,122 @@ // //--------------------------------------------------------------------------- -#include "mocks.h" #include "hal/bus.h" +#include "mocks.h" TEST(BusTest, GetCommandByteCount) { - EXPECT_EQ(41, scsi_defs::command_mapping.size()); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x00)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x01)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x03)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x04)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x07)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x08)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x09)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x0a)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x0b)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x0c)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x0d)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x0e)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x10)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x12)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x15)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x16)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x17)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x1a)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x1b)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x1d)); - EXPECT_EQ(6, BUS::GetCommandByteCount(0x1e)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x25)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x28)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x2a)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x2b)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x2f)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x35)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x37)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x3e)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x3f)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x43)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x4a)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x55)); - EXPECT_EQ(10, BUS::GetCommandByteCount(0x5a)); - EXPECT_EQ(12, BUS::GetCommandByteCount(0xa0)); - EXPECT_EQ(16, BUS::GetCommandByteCount(0x88)); - EXPECT_EQ(16, BUS::GetCommandByteCount(0x8a)); - EXPECT_EQ(16, BUS::GetCommandByteCount(0x8f)); - EXPECT_EQ(16, BUS::GetCommandByteCount(0x91)); - EXPECT_EQ(16, BUS::GetCommandByteCount(0x9e)); - EXPECT_EQ(16, BUS::GetCommandByteCount(0x9f)); - EXPECT_EQ(0, BUS::GetCommandByteCount(0x1f)); + EXPECT_EQ(41, scsi_defs::command_mapping.size()); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x00)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x01)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x03)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x04)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x07)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x08)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x09)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x0a)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x0b)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x0c)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x0d)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x0e)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x10)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x12)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x15)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x16)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x17)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x1a)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x1b)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x1d)); + EXPECT_EQ(6, BUS::GetCommandByteCount(0x1e)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x25)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x28)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x2a)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x2b)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x2f)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x35)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x37)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x3e)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x3f)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x43)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x4a)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x55)); + EXPECT_EQ(10, BUS::GetCommandByteCount(0x5a)); + EXPECT_EQ(12, BUS::GetCommandByteCount(0xa0)); + EXPECT_EQ(16, BUS::GetCommandByteCount(0x88)); + EXPECT_EQ(16, BUS::GetCommandByteCount(0x8a)); + EXPECT_EQ(16, BUS::GetCommandByteCount(0x8f)); + EXPECT_EQ(16, BUS::GetCommandByteCount(0x91)); + EXPECT_EQ(16, BUS::GetCommandByteCount(0x9e)); + EXPECT_EQ(16, BUS::GetCommandByteCount(0x9f)); + EXPECT_EQ(0, BUS::GetCommandByteCount(0x1f)); } TEST(BusTest, GetPhase) { - EXPECT_EQ(BUS::phase_t::dataout, BUS::GetPhase(0b000)); - EXPECT_EQ(BUS::phase_t::datain, BUS::GetPhase(0b001)); - EXPECT_EQ(BUS::phase_t::command, BUS::GetPhase(0b010)); - EXPECT_EQ(BUS::phase_t::status, BUS::GetPhase(0b011)); - EXPECT_EQ(BUS::phase_t::reserved, BUS::GetPhase(0b100)); - EXPECT_EQ(BUS::phase_t::reserved, BUS::GetPhase(0b101)); - EXPECT_EQ(BUS::phase_t::msgout, BUS::GetPhase(0b110)); - EXPECT_EQ(BUS::phase_t::msgin, BUS::GetPhase(0b111)); + EXPECT_EQ(phase_t::dataout, BUS::GetPhase(0b000)); + EXPECT_EQ(phase_t::datain, BUS::GetPhase(0b001)); + EXPECT_EQ(phase_t::command, BUS::GetPhase(0b010)); + EXPECT_EQ(phase_t::status, BUS::GetPhase(0b011)); + EXPECT_EQ(phase_t::reserved, BUS::GetPhase(0b100)); + EXPECT_EQ(phase_t::reserved, BUS::GetPhase(0b101)); + EXPECT_EQ(phase_t::msgout, BUS::GetPhase(0b110)); + EXPECT_EQ(phase_t::msgin, BUS::GetPhase(0b111)); - NiceMock bus; + NiceMock bus; - EXPECT_EQ(BUS::phase_t::busfree, bus.GetPhase()); + EXPECT_EQ(phase_t::busfree, bus.GetPhase()); - ON_CALL(bus, GetSEL()).WillByDefault(Return(true)); - EXPECT_EQ(BUS::phase_t::selection, bus.GetPhase()); + ON_CALL(bus, GetSEL()).WillByDefault(Return(true)); + EXPECT_EQ(phase_t::selection, bus.GetPhase()); - ON_CALL(bus, GetSEL()).WillByDefault(Return(false)); - ON_CALL(bus, GetBSY()).WillByDefault(Return(true)); + ON_CALL(bus, GetSEL()).WillByDefault(Return(false)); + ON_CALL(bus, GetBSY()).WillByDefault(Return(true)); - ON_CALL(bus, GetMSG()).WillByDefault(Return(false)); - EXPECT_EQ(BUS::phase_t::dataout, bus.GetPhase()); - ON_CALL(bus, GetMSG()).WillByDefault(Return(true)); - EXPECT_EQ(BUS::phase_t::reserved, bus.GetPhase()); + ON_CALL(bus, GetMSG()).WillByDefault(Return(false)); + EXPECT_EQ(phase_t::dataout, bus.GetPhase()); + ON_CALL(bus, GetMSG()).WillByDefault(Return(true)); + EXPECT_EQ(phase_t::reserved, bus.GetPhase()); - ON_CALL(bus, GetMSG()).WillByDefault(Return(false)); - ON_CALL(bus, GetCD()).WillByDefault(Return(true)); - EXPECT_EQ(BUS::phase_t::command, bus.GetPhase()); + ON_CALL(bus, GetMSG()).WillByDefault(Return(false)); + ON_CALL(bus, GetCD()).WillByDefault(Return(true)); + EXPECT_EQ(phase_t::command, bus.GetPhase()); - ON_CALL(bus, GetMSG()).WillByDefault(Return(true)); - ON_CALL(bus, GetCD()).WillByDefault(Return(true)); - EXPECT_EQ(BUS::phase_t::msgout, bus.GetPhase()); + ON_CALL(bus, GetMSG()).WillByDefault(Return(true)); + ON_CALL(bus, GetCD()).WillByDefault(Return(true)); + EXPECT_EQ(phase_t::msgout, bus.GetPhase()); - ON_CALL(bus, GetMSG()).WillByDefault(Return(false)); - ON_CALL(bus, GetCD()).WillByDefault(Return(false)); - ON_CALL(bus, GetIO()).WillByDefault(Return(true)); - EXPECT_EQ(BUS::phase_t::datain, bus.GetPhase()); + ON_CALL(bus, GetMSG()).WillByDefault(Return(false)); + ON_CALL(bus, GetCD()).WillByDefault(Return(false)); + ON_CALL(bus, GetIO()).WillByDefault(Return(true)); + EXPECT_EQ(phase_t::datain, bus.GetPhase()); - ON_CALL(bus, GetMSG()).WillByDefault(Return(true)); - ON_CALL(bus, GetCD()).WillByDefault(Return(false)); - ON_CALL(bus, GetIO()).WillByDefault(Return(true)); - EXPECT_EQ(BUS::phase_t::reserved, bus.GetPhase()); + ON_CALL(bus, GetMSG()).WillByDefault(Return(true)); + ON_CALL(bus, GetCD()).WillByDefault(Return(false)); + ON_CALL(bus, GetIO()).WillByDefault(Return(true)); + EXPECT_EQ(phase_t::reserved, bus.GetPhase()); - ON_CALL(bus, GetMSG()).WillByDefault(Return(true)); - ON_CALL(bus, GetCD()).WillByDefault(Return(true)); - ON_CALL(bus, GetIO()).WillByDefault(Return(true)); - EXPECT_EQ(BUS::phase_t::msgin, bus.GetPhase()); + ON_CALL(bus, GetMSG()).WillByDefault(Return(true)); + ON_CALL(bus, GetCD()).WillByDefault(Return(true)); + ON_CALL(bus, GetIO()).WillByDefault(Return(true)); + EXPECT_EQ(phase_t::msgin, bus.GetPhase()); - ON_CALL(bus, GetMSG()).WillByDefault(Return(false)); - ON_CALL(bus, GetCD()).WillByDefault(Return(true)); - ON_CALL(bus, GetIO()).WillByDefault(Return(true)); - EXPECT_EQ(BUS::phase_t::status, bus.GetPhase()); + ON_CALL(bus, GetMSG()).WillByDefault(Return(false)); + ON_CALL(bus, GetCD()).WillByDefault(Return(true)); + ON_CALL(bus, GetIO()).WillByDefault(Return(true)); + EXPECT_EQ(phase_t::status, bus.GetPhase()); } TEST(BusTest, GetPhaseStrRaw) { - EXPECT_STREQ("busfree", BUS::GetPhaseStrRaw(BUS::phase_t::busfree)); - EXPECT_STREQ("arbitration", BUS::GetPhaseStrRaw(BUS::phase_t::arbitration)); - EXPECT_STREQ("selection", BUS::GetPhaseStrRaw(BUS::phase_t::selection)); - EXPECT_STREQ("reselection", BUS::GetPhaseStrRaw(BUS::phase_t::reselection)); - EXPECT_STREQ("command", BUS::GetPhaseStrRaw(BUS::phase_t::command)); - EXPECT_STREQ("datain", BUS::GetPhaseStrRaw(BUS::phase_t::datain)); - EXPECT_STREQ("dataout", BUS::GetPhaseStrRaw(BUS::phase_t::dataout)); - EXPECT_STREQ("status", BUS::GetPhaseStrRaw(BUS::phase_t::status)); - EXPECT_STREQ("msgin", BUS::GetPhaseStrRaw(BUS::phase_t::msgin)); - EXPECT_STREQ("msgout", BUS::GetPhaseStrRaw(BUS::phase_t::msgout)); - EXPECT_STREQ("reserved", BUS::GetPhaseStrRaw(BUS::phase_t::reserved)); -} - -TEST(BusTest, GetPinRaw) -{ - EXPECT_EQ(0, BUS::GetPinRaw(0, 0)); - EXPECT_EQ(0, BUS::GetPinRaw(0, 7)); - EXPECT_EQ(1, BUS::GetPinRaw(-1, 0)); - EXPECT_EQ(1, BUS::GetPinRaw(-1, 7)); + EXPECT_STREQ("busfree", BUS::GetPhaseStrRaw(phase_t::busfree)); + EXPECT_STREQ("arbitration", BUS::GetPhaseStrRaw(phase_t::arbitration)); + EXPECT_STREQ("selection", BUS::GetPhaseStrRaw(phase_t::selection)); + EXPECT_STREQ("reselection", BUS::GetPhaseStrRaw(phase_t::reselection)); + EXPECT_STREQ("command", BUS::GetPhaseStrRaw(phase_t::command)); + EXPECT_STREQ("datain", BUS::GetPhaseStrRaw(phase_t::datain)); + EXPECT_STREQ("dataout", BUS::GetPhaseStrRaw(phase_t::dataout)); + EXPECT_STREQ("status", BUS::GetPhaseStrRaw(phase_t::status)); + EXPECT_STREQ("msgin", BUS::GetPhaseStrRaw(phase_t::msgin)); + EXPECT_STREQ("msgout", BUS::GetPhaseStrRaw(phase_t::msgout)); + EXPECT_STREQ("reserved", BUS::GetPhaseStrRaw(phase_t::reserved)); } diff --git a/cpp/test/gpiobus_raspberry_test.cpp b/cpp/test/gpiobus_raspberry_test.cpp new file mode 100644 index 00000000..63c3d407 --- /dev/null +++ b/cpp/test/gpiobus_raspberry_test.cpp @@ -0,0 +1,234 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 akuker +// +//--------------------------------------------------------------------------- + +#include "hal/gpiobus_raspberry.h" +#include "mocks.h" +#include "stdlib.h" +#include "test/test_shared.h" + +class SetableGpiobusRaspberry : public GPIOBUS_Raspberry +{ + public: + void TestSetGpios(uint32_t value) + { + *level = ~value; + } + void TestSetGpioPin(int pin, bool value) + { + // Level is inverted logic + if (!value) { + *level |= (1 << pin); + } else { + *level &= ~(1 << pin); + } + } + SetableGpiobusRaspberry() + { + level = new uint32_t(); // NOSONAR: This is a pointer to a register on the real hardware + } +}; + +extern "C" { +uint32_t get_dt_ranges(const char *filename, uint32_t offset); +uint32_t bcm_host_get_peripheral_address(); +} + +TEST(GpiobusRaspberry, GetDtRanges) +{ + string soc_ranges_file = "/proc/device-tree/soc/ranges"; + + vector data; + // If bytes 4-7 are non-zero, get_peripheral address should return those bytes + data = vector( + {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}); + CreateTempFileWithData(soc_ranges_file, data); + EXPECT_EQ(0x44556677, GPIOBUS_Raspberry::bcm_host_get_peripheral_address()); + DeleteTempFile("/proc/device-tree/soc/ranges"); + + // If bytes 4-7 are zero, get_peripheral address should return bytes 8-11 + data = vector( + {0x00, 0x11, 0x22, 0x33, 0x00, 0x00, 0x00, 0x00, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}); + CreateTempFileWithData(soc_ranges_file, data); + EXPECT_EQ(0x8899AABB, GPIOBUS_Raspberry::bcm_host_get_peripheral_address()); + DeleteTempFile("/proc/device-tree/soc/ranges"); + + // If bytes 4-7 are zero, and 8-11 are 0xFF, get_peripheral address should return a default address of 0x20000000 + data = vector( + {0x00, 0x11, 0x22, 0x33, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xCC, 0xDD, 0xEE, 0xFF}); + CreateTempFileWithData(soc_ranges_file, data); + EXPECT_EQ(0x20000000, GPIOBUS_Raspberry::bcm_host_get_peripheral_address()); + DeleteTempFile("/proc/device-tree/soc/ranges"); + + CleanupAllTempFiles(); +} + +TEST(GpiobusRaspberry, GetDat) +{ + SetableGpiobusRaspberry bus; + + bus.TestSetGpios(0x00); + EXPECT_EQ(0, bus.GetDAT()); + + bus.TestSetGpioPin(PIN_DT0, true); + EXPECT_EQ(0x01, bus.GetDAT()); + + bus.TestSetGpioPin(PIN_DT1, true); + EXPECT_EQ(0x03, bus.GetDAT()); + + bus.TestSetGpioPin(PIN_DT2, true); + EXPECT_EQ(0x07, bus.GetDAT()); + + bus.TestSetGpioPin(PIN_DT3, true); + EXPECT_EQ(0x0F, bus.GetDAT()); + + bus.TestSetGpioPin(PIN_DT4, true); + EXPECT_EQ(0x1F, bus.GetDAT()); + + bus.TestSetGpioPin(PIN_DT5, true); + EXPECT_EQ(0x3F, bus.GetDAT()); + + bus.TestSetGpioPin(PIN_DT6, true); + EXPECT_EQ(0x7F, bus.GetDAT()); + + bus.TestSetGpioPin(PIN_DT7, true); + EXPECT_EQ(0xFF, bus.GetDAT()); + + bus.TestSetGpios(0xFFFFFFFF); + EXPECT_EQ(0xFF, bus.GetDAT()); +} + +TEST(GpiobusRaspberry, GetBSY) +{ + SetableGpiobusRaspberry bus; + + bus.TestSetGpios(0x00); + bus.TestSetGpioPin(PIN_BSY, true); + bus.Acquire(); + EXPECT_EQ(true, bus.GetBSY()); + bus.TestSetGpioPin(PIN_BSY, false); + bus.Acquire(); + EXPECT_EQ(false, bus.GetBSY()); +} + +TEST(GpiobusRaspberry, GetSEL) +{ + SetableGpiobusRaspberry bus; + + bus.TestSetGpios(0x00); + bus.TestSetGpioPin(PIN_SEL, true); + bus.Acquire(); + EXPECT_EQ(true, bus.GetSEL()); + bus.TestSetGpioPin(PIN_SEL, false); + bus.Acquire(); + EXPECT_EQ(false, bus.GetSEL()); +} + +TEST(GpiobusRaspberry, GetATN) +{ + SetableGpiobusRaspberry bus; + + bus.TestSetGpios(0x00); + bus.TestSetGpioPin(PIN_ATN, true); + bus.Acquire(); + EXPECT_EQ(true, bus.GetATN()); + bus.TestSetGpioPin(PIN_ATN, false); + bus.Acquire(); + EXPECT_EQ(false, bus.GetATN()); +} + +TEST(GpiobusRaspberry, GetACK) +{ + SetableGpiobusRaspberry bus; + + bus.TestSetGpios(0x00); + bus.TestSetGpioPin(PIN_ACK, true); + bus.Acquire(); + EXPECT_EQ(true, bus.GetACK()); + bus.TestSetGpioPin(PIN_ACK, false); + bus.Acquire(); + EXPECT_EQ(false, bus.GetACK()); +} + +TEST(GpiobusRaspberry, GetRST) +{ + SetableGpiobusRaspberry bus; + + bus.TestSetGpios(0x00); + bus.TestSetGpioPin(PIN_RST, true); + bus.Acquire(); + EXPECT_EQ(true, bus.GetRST()); + bus.TestSetGpioPin(PIN_RST, false); + bus.Acquire(); + EXPECT_EQ(false, bus.GetRST()); +} + +TEST(GpiobusRaspberry, GetMSG) +{ + SetableGpiobusRaspberry bus; + + bus.TestSetGpios(0x00); + bus.TestSetGpioPin(PIN_MSG, true); + bus.Acquire(); + EXPECT_EQ(true, bus.GetMSG()); + bus.TestSetGpioPin(PIN_MSG, false); + bus.Acquire(); + EXPECT_EQ(false, bus.GetMSG()); +} + +TEST(GpiobusRaspberry, GetCD) +{ + SetableGpiobusRaspberry bus; + + bus.TestSetGpios(0x00); + bus.TestSetGpioPin(PIN_CD, true); + bus.Acquire(); + EXPECT_EQ(true, bus.GetCD()); + bus.TestSetGpioPin(PIN_CD, false); + bus.Acquire(); + EXPECT_EQ(false, bus.GetCD()); +} + +TEST(GpiobusRaspberry, GetIO) +{ + SetableGpiobusRaspberry bus; + + bus.TestSetGpios(0x00); + bus.TestSetGpioPin(PIN_IO, true); + bus.Acquire(); + EXPECT_EQ(true, bus.GetIO()); + bus.TestSetGpioPin(PIN_IO, false); + bus.Acquire(); + EXPECT_EQ(false, bus.GetIO()); +} + +TEST(GpiobusRaspberry, GetREQ) +{ + SetableGpiobusRaspberry bus; + + bus.TestSetGpios(0x00); + bus.TestSetGpioPin(PIN_REQ, true); + bus.Acquire(); + EXPECT_EQ(true, bus.GetREQ()); + bus.TestSetGpioPin(PIN_REQ, false); + bus.Acquire(); + EXPECT_EQ(false, bus.GetREQ()); +} + +TEST(GpiobusRaspberry, GetDP) +{ + SetableGpiobusRaspberry bus; + + bus.TestSetGpios(0x00); + bus.TestSetGpioPin(PIN_DP, true); + bus.Acquire(); + EXPECT_EQ(true, bus.GetDP()); + bus.TestSetGpioPin(PIN_DP, false); + bus.Acquire(); + EXPECT_EQ(false, bus.GetDP()); +} \ No newline at end of file diff --git a/cpp/test/linux_os_stubs.cpp b/cpp/test/linux_os_stubs.cpp new file mode 100644 index 00000000..841569cc --- /dev/null +++ b/cpp/test/linux_os_stubs.cpp @@ -0,0 +1,59 @@ + +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 akuker +// +//--------------------------------------------------------------------------- + +#include "test/linux_os_stubs.h" +#include "test/test_shared.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace filesystem; + +extern "C" { + +#ifdef __USE_LARGEFILE64 +FILE *__wrap_fopen64(const char *__restrict __filename, const char *__restrict __modes) +#else +FILE *__wrap_fopen(const char *__restrict __filename, const char *__restrict __modes) +#endif +{ + path new_filename; + bool create_directory = false; + + // If we're trying to open up the device tree soc ranges, + // re-direct it to a temporary local file. + if (string(__filename) == "/proc/device-tree/soc/ranges") { + create_directory = true; + new_filename = test_data_temp_path; + new_filename += path(__filename); + } else { + new_filename = path(__filename); + } + + if (create_directory) { + create_directories(new_filename.parent_path()); + } +#ifdef __USE_LARGEFILE64 + return __real_fopen64(new_filename.c_str(), __modes); +#else + return __real_fopen(new_filename.c_str(), __modes); +#endif +} + +} // end extern "C" \ No newline at end of file diff --git a/cpp/test/linux_os_stubs.h b/cpp/test/linux_os_stubs.h new file mode 100644 index 00000000..952ce8b7 --- /dev/null +++ b/cpp/test/linux_os_stubs.h @@ -0,0 +1,21 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 akuker +// +//--------------------------------------------------------------------------- + +// This header file should ONLY be used in test procedures. It bypasses the +// standard c library functionality. DO NOT USE THIS IN PRODUCTION CODE. +#pragma once +#include + +extern "C" { +#ifdef __USE_LARGEFILE64 +FILE *__real_fopen64(const char *__restrict __filename, const char *__restrict __modes); +#else +FILE *__real_fopen(const char *__restrict __filename, const char *__restrict __modes); +#endif +} diff --git a/cpp/test/mocks.h b/cpp/test/mocks.h index 2722a64b..b3645f78 100644 --- a/cpp/test/mocks.h +++ b/cpp/test/mocks.h @@ -53,6 +53,9 @@ public: MOCK_METHOD(void, SetIO, (bool), (override)); MOCK_METHOD(bool, GetREQ, (), (const override)); MOCK_METHOD(void, SetREQ, (bool), (override)); + MOCK_METHOD(bool, GetACT, (), (const override)); + MOCK_METHOD(void, SetACT, (bool), (override)); + MOCK_METHOD(void, SetENB, (bool), (override)); MOCK_METHOD(uint8_t, GetDAT, (), (override)); MOCK_METHOD(void, SetDAT, (uint8_t), (override)); MOCK_METHOD(bool, GetDP, (), (const override)); @@ -62,6 +65,14 @@ public: MOCK_METHOD(int, SendHandShake, (uint8_t *, int, int), (override)); MOCK_METHOD(bool, GetSignal, (int), (const override)); MOCK_METHOD(void, SetSignal, (int, bool), (override)); + MOCK_METHOD(bool, PollSelectEvent, (), (override)); + MOCK_METHOD(void, ClearSelectEvent, (), (override)); + MOCK_METHOD(unique_ptr, GetSample, (uint64_t), (override)); + MOCK_METHOD(void, PinConfig, (int, int), (override)); + MOCK_METHOD(void, PullConfig, (int , int ), (override)); + MOCK_METHOD(void, SetControl, (int , bool ), (override)); + MOCK_METHOD(void, SetMode, (int , int ), (override)); + MOCK_METHOD(int, GetMode, (int ), (override)); MockBus() = default; ~MockBus() override = default; @@ -73,7 +84,7 @@ class MockPhaseHandler : public PhaseHandler public: - MOCK_METHOD(BUS::phase_t, Process, (int), (override)); + MOCK_METHOD(phase_t, Process, (int), (override)); MOCK_METHOD(void, Status, (), ()); MOCK_METHOD(void, DataIn, (), ()); MOCK_METHOD(void, DataOut, (), ()); @@ -140,7 +151,7 @@ class MockAbstractController : public AbstractController //NOSONAR Having many f public: - MOCK_METHOD(BUS::phase_t, Process, (int), (override)); + MOCK_METHOD(phase_t, Process, (int), (override)); MOCK_METHOD(int, GetEffectiveLun, (), (const override)); MOCK_METHOD(void, Error, (scsi_defs::sense_key, scsi_defs::asc, scsi_defs::status), (override)); MOCK_METHOD(int, GetInitiatorId, (), (const override)); diff --git a/cpp/test/phase_handler_test.cpp b/cpp/test/phase_handler_test.cpp index 699b83cb..22a5c733 100644 --- a/cpp/test/phase_handler_test.cpp +++ b/cpp/test/phase_handler_test.cpp @@ -14,7 +14,7 @@ TEST(PhaseHandlerTest, Phases) { MockPhaseHandler handler; - handler.SetPhase(BUS::phase_t::selection); + handler.SetPhase(phase_t::selection); EXPECT_TRUE(handler.IsSelection()); EXPECT_FALSE(handler.IsBusFree()); EXPECT_FALSE(handler.IsCommand()); @@ -24,7 +24,7 @@ TEST(PhaseHandlerTest, Phases) EXPECT_FALSE(handler.IsMsgIn()); EXPECT_FALSE(handler.IsMsgOut()); - handler.SetPhase(BUS::phase_t::busfree); + handler.SetPhase(phase_t::busfree); EXPECT_TRUE(handler.IsBusFree()); EXPECT_FALSE(handler.IsSelection()); EXPECT_FALSE(handler.IsCommand()); @@ -34,7 +34,7 @@ TEST(PhaseHandlerTest, Phases) EXPECT_FALSE(handler.IsMsgIn()); EXPECT_FALSE(handler.IsMsgOut()); - handler.SetPhase(BUS::phase_t::command); + handler.SetPhase(phase_t::command); EXPECT_TRUE(handler.IsCommand()); EXPECT_FALSE(handler.IsBusFree()); EXPECT_FALSE(handler.IsSelection()); @@ -44,7 +44,7 @@ TEST(PhaseHandlerTest, Phases) EXPECT_FALSE(handler.IsMsgIn()); EXPECT_FALSE(handler.IsMsgOut()); - handler.SetPhase(BUS::phase_t::status); + handler.SetPhase(phase_t::status); EXPECT_TRUE(handler.IsStatus()); EXPECT_FALSE(handler.IsBusFree()); EXPECT_FALSE(handler.IsSelection()); @@ -54,7 +54,7 @@ TEST(PhaseHandlerTest, Phases) EXPECT_FALSE(handler.IsMsgIn()); EXPECT_FALSE(handler.IsMsgOut()); - handler.SetPhase(BUS::phase_t::datain); + handler.SetPhase(phase_t::datain); EXPECT_TRUE(handler.IsDataIn()); EXPECT_FALSE(handler.IsBusFree()); EXPECT_FALSE(handler.IsSelection()); @@ -64,7 +64,7 @@ TEST(PhaseHandlerTest, Phases) EXPECT_FALSE(handler.IsMsgIn()); EXPECT_FALSE(handler.IsMsgOut()); - handler.SetPhase(BUS::phase_t::dataout); + handler.SetPhase(phase_t::dataout); EXPECT_TRUE(handler.IsDataOut()); EXPECT_FALSE(handler.IsBusFree()); EXPECT_FALSE(handler.IsSelection()); @@ -74,7 +74,7 @@ TEST(PhaseHandlerTest, Phases) EXPECT_FALSE(handler.IsMsgIn()); EXPECT_FALSE(handler.IsMsgOut()); - handler.SetPhase(BUS::phase_t::msgin); + handler.SetPhase(phase_t::msgin); EXPECT_TRUE(handler.IsMsgIn()); EXPECT_FALSE(handler.IsBusFree()); EXPECT_FALSE(handler.IsSelection()); @@ -84,7 +84,7 @@ TEST(PhaseHandlerTest, Phases) EXPECT_FALSE(handler.IsDataOut()); EXPECT_FALSE(handler.IsMsgOut()); - handler.SetPhase(BUS::phase_t::msgout); + handler.SetPhase(phase_t::msgout); EXPECT_TRUE(handler.IsMsgOut()); EXPECT_FALSE(handler.IsBusFree()); EXPECT_FALSE(handler.IsSelection()); diff --git a/cpp/test/scsi_controller_test.cpp b/cpp/test/scsi_controller_test.cpp index 13807165..e14934ab 100644 --- a/cpp/test/scsi_controller_test.cpp +++ b/cpp/test/scsi_controller_test.cpp @@ -34,26 +34,26 @@ TEST(ScsiControllerTest, Process) auto controller_manager = make_shared(*bus); MockScsiController controller(controller_manager, 0); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); ON_CALL(*bus, GetRST).WillByDefault(Return(true)); EXPECT_CALL(*bus, Acquire); EXPECT_CALL(*bus, GetRST); EXPECT_CALL(*bus, Reset); EXPECT_CALL(controller, Reset); - EXPECT_EQ(BUS::phase_t::reserved, controller.Process(0)); + EXPECT_EQ(phase_t::reserved, controller.Process(0)); - controller.SetPhase(BUS::phase_t::busfree); + controller.SetPhase(phase_t::busfree); ON_CALL(*bus, GetRST).WillByDefault(Return(false)); EXPECT_CALL(*bus, Acquire); EXPECT_CALL(*bus, GetRST); - EXPECT_EQ(BUS::phase_t::busfree, controller.Process(0)); + EXPECT_EQ(phase_t::busfree, controller.Process(0)); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); EXPECT_CALL(*bus, Acquire); EXPECT_CALL(*bus, GetRST); EXPECT_CALL(*bus, Reset); EXPECT_CALL(controller, Reset); - EXPECT_EQ(BUS::phase_t::busfree, controller.Process(0)); + EXPECT_EQ(phase_t::busfree, controller.Process(0)); } TEST(ScsiControllerTest, BusFree) @@ -62,30 +62,30 @@ TEST(ScsiControllerTest, BusFree) auto controller_manager = make_shared(*bus); MockScsiController controller(controller_manager, 0); - controller.SetPhase(BUS::phase_t::busfree); + controller.SetPhase(phase_t::busfree); controller.BusFree(); - EXPECT_EQ(BUS::phase_t::busfree, controller.GetPhase()); + EXPECT_EQ(phase_t::busfree, controller.GetPhase()); controller.SetStatus(status::CHECK_CONDITION); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); controller.BusFree(); - EXPECT_EQ(BUS::phase_t::busfree, controller.GetPhase()); + EXPECT_EQ(phase_t::busfree, controller.GetPhase()); EXPECT_EQ(status::GOOD, controller.GetStatus()); controller.ScheduleShutdown(AbstractController::rascsi_shutdown_mode::NONE); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); controller.BusFree(); controller.ScheduleShutdown(AbstractController::rascsi_shutdown_mode::STOP_PI); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); controller.BusFree(); controller.ScheduleShutdown(AbstractController::rascsi_shutdown_mode::RESTART_PI); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); controller.BusFree(); controller.ScheduleShutdown(AbstractController::rascsi_shutdown_mode::STOP_RASCSI); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); EXPECT_EXIT(controller.BusFree(), ExitedWithCode(EXIT_SUCCESS), ""); } @@ -95,55 +95,55 @@ TEST(ScsiControllerTest, Selection) auto controller_manager = make_shared(*bus); auto controller = make_shared(controller_manager, 0); - controller->SetPhase(BUS::phase_t::selection); + controller->SetPhase(phase_t::selection); ON_CALL(*bus, GetSEL).WillByDefault(Return(true)); ON_CALL(*bus, GetBSY).WillByDefault(Return(true)); EXPECT_CALL(*bus, GetATN).Times(0); controller->Selection(); - EXPECT_EQ(BUS::phase_t::selection, controller->GetPhase()); + EXPECT_EQ(phase_t::selection, controller->GetPhase()); ON_CALL(*bus, GetSEL).WillByDefault(Return(true)); ON_CALL(*bus, GetBSY).WillByDefault(Return(false)); EXPECT_CALL(*bus, GetATN).Times(0); EXPECT_CALL(*controller, Status); controller->Selection(); - EXPECT_EQ(BUS::phase_t::selection, controller->GetPhase()); + EXPECT_EQ(phase_t::selection, controller->GetPhase()); ON_CALL(*bus, GetSEL).WillByDefault(Return(false)); ON_CALL(*bus, GetBSY).WillByDefault(Return(false)); EXPECT_CALL(*bus, GetATN).Times(0); controller->Selection(); - EXPECT_EQ(BUS::phase_t::selection, controller->GetPhase()); + EXPECT_EQ(phase_t::selection, controller->GetPhase()); ON_CALL(*bus, GetSEL).WillByDefault(Return(false)); ON_CALL(*bus, GetBSY).WillByDefault(Return(true)); ON_CALL(*bus, GetATN).WillByDefault(Return(false)); EXPECT_CALL(*bus, GetATN); controller->Selection(); - EXPECT_EQ(BUS::phase_t::command, controller->GetPhase()); + EXPECT_EQ(phase_t::command, controller->GetPhase()); - controller->SetPhase(BUS::phase_t::selection); + controller->SetPhase(phase_t::selection); ON_CALL(*bus, GetSEL).WillByDefault(Return(false)); ON_CALL(*bus, GetBSY).WillByDefault(Return(true)); ON_CALL(*bus, GetATN).WillByDefault(Return(true)); EXPECT_CALL(*bus, GetATN); controller->Selection(); - EXPECT_EQ(BUS::phase_t::msgout, controller->GetPhase()); + EXPECT_EQ(phase_t::msgout, controller->GetPhase()); - controller->SetPhase(BUS::phase_t::reserved); + controller->SetPhase(phase_t::reserved); ON_CALL(*bus, GetDAT).WillByDefault(Return(0)); controller->Selection(); - EXPECT_EQ(BUS::phase_t::reserved, controller->GetPhase()); + EXPECT_EQ(phase_t::reserved, controller->GetPhase()); ON_CALL(*bus, GetDAT).WillByDefault(Return(1)); controller->Selection(); - EXPECT_EQ(BUS::phase_t::reserved, controller->GetPhase()) << "There is no device that can be selected"; + EXPECT_EQ(phase_t::reserved, controller->GetPhase()) << "There is no device that can be selected"; auto device = make_shared(0); controller->AddDevice(device); EXPECT_CALL(*bus, SetBSY(true)); controller->Selection(); - EXPECT_EQ(BUS::phase_t::selection, controller->GetPhase()); + EXPECT_EQ(phase_t::selection, controller->GetPhase()); } TEST(ScsiControllerTest, Command) @@ -152,26 +152,26 @@ TEST(ScsiControllerTest, Command) auto controller_manager = make_shared(*bus); MockScsiController controller(controller_manager, 0); - controller.SetPhase(BUS::phase_t::command); + controller.SetPhase(phase_t::command); EXPECT_CALL(controller, Status); controller.Command(); - EXPECT_EQ(BUS::phase_t::command, controller.GetPhase()); + EXPECT_EQ(phase_t::command, controller.GetPhase()); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); EXPECT_CALL(*bus, SetMSG(false)); EXPECT_CALL(*bus, SetCD(true)); EXPECT_CALL(*bus, SetIO(false)); controller.Command(); - EXPECT_EQ(BUS::phase_t::command, controller.GetPhase()); + EXPECT_EQ(phase_t::command, controller.GetPhase()); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); ON_CALL(*bus, CommandHandShake).WillByDefault(Return(6)); EXPECT_CALL(*bus, SetMSG(false)); EXPECT_CALL(*bus, SetCD(true)); EXPECT_CALL(*bus, SetIO(false)); EXPECT_CALL(controller, Execute); controller.Command(); - EXPECT_EQ(BUS::phase_t::command, controller.GetPhase()); + EXPECT_EQ(phase_t::command, controller.GetPhase()); } TEST(ScsiControllerTest, MsgIn) @@ -180,12 +180,12 @@ TEST(ScsiControllerTest, MsgIn) auto controller_manager = make_shared(*bus); MockScsiController controller(controller_manager, 0); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); EXPECT_CALL(*bus, SetMSG(true)); EXPECT_CALL(*bus, SetCD(true)); EXPECT_CALL(*bus, SetIO(true)); controller.MsgIn(); - EXPECT_EQ(BUS::phase_t::msgin, controller.GetPhase()); + EXPECT_EQ(phase_t::msgin, controller.GetPhase()); EXPECT_FALSE(controller.HasValidLength()); EXPECT_EQ(0, controller.GetOffset()); } @@ -196,12 +196,12 @@ TEST(ScsiControllerTest, MsgOut) auto controller_manager = make_shared(*bus); MockScsiController controller(controller_manager, 0); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); EXPECT_CALL(*bus, SetMSG(true)); EXPECT_CALL(*bus, SetCD(true)); EXPECT_CALL(*bus, SetIO(false)); controller.MsgOut(); - EXPECT_EQ(BUS::phase_t::msgout, controller.GetPhase()); + EXPECT_EQ(phase_t::msgout, controller.GetPhase()); EXPECT_EQ(1, controller.GetLength()); EXPECT_EQ(0, controller.GetOffset()); } @@ -212,18 +212,18 @@ TEST(ScsiControllerTest, DataIn) auto controller_manager = make_shared(*bus); MockScsiController controller(controller_manager, 0); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); controller.SetLength(0); EXPECT_CALL(controller, Status); controller.DataIn(); - EXPECT_EQ(BUS::phase_t::reserved, controller.GetPhase()); + EXPECT_EQ(phase_t::reserved, controller.GetPhase()); controller.SetLength(1); EXPECT_CALL(*bus, SetMSG(false)); EXPECT_CALL(*bus, SetCD(false)); EXPECT_CALL(*bus, SetIO(true)); controller.DataIn(); - EXPECT_EQ(BUS::phase_t::datain, controller.GetPhase()); + EXPECT_EQ(phase_t::datain, controller.GetPhase()); EXPECT_EQ(0, controller.GetOffset()); } @@ -233,18 +233,18 @@ TEST(ScsiControllerTest, DataOut) auto controller_manager = make_shared(*bus); MockScsiController controller(controller_manager, 0); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); controller.SetLength(0); EXPECT_CALL(controller, Status); controller.DataOut(); - EXPECT_EQ(BUS::phase_t::reserved, controller.GetPhase()); + EXPECT_EQ(phase_t::reserved, controller.GetPhase()); controller.SetLength(1); EXPECT_CALL(*bus, SetMSG(false)); EXPECT_CALL(*bus, SetCD(false)); EXPECT_CALL(*bus, SetIO(false)); controller.DataOut(); - EXPECT_EQ(BUS::phase_t::dataout, controller.GetPhase()); + EXPECT_EQ(phase_t::dataout, controller.GetPhase()); EXPECT_EQ(0, controller.GetOffset()); } @@ -255,33 +255,33 @@ TEST(ScsiControllerTest, Error) MockScsiController controller(controller_manager, 0); ON_CALL(*bus, GetRST).WillByDefault(Return(true)); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); EXPECT_CALL(*bus, Acquire); EXPECT_CALL(*bus, GetRST()); EXPECT_CALL(*bus, Reset); EXPECT_CALL(controller, Reset); controller.Error(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION, status::RESERVATION_CONFLICT); EXPECT_EQ(status::GOOD, controller.GetStatus()); - EXPECT_EQ(BUS::phase_t::reserved, controller.GetPhase()); + EXPECT_EQ(phase_t::reserved, controller.GetPhase()); ON_CALL(*bus, GetRST).WillByDefault(Return(false)); - controller.SetPhase(BUS::phase_t::status); + controller.SetPhase(phase_t::status); EXPECT_CALL(*bus, Acquire); EXPECT_CALL(*bus, GetRST()); EXPECT_CALL(*bus, Reset).Times(0); EXPECT_CALL(controller, Reset).Times(0); controller.Error(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION, status::RESERVATION_CONFLICT); - EXPECT_EQ(BUS::phase_t::busfree, controller.GetPhase()); + EXPECT_EQ(phase_t::busfree, controller.GetPhase()); - controller.SetPhase(BUS::phase_t::msgin); + controller.SetPhase(phase_t::msgin); EXPECT_CALL(*bus, Acquire); EXPECT_CALL(*bus, GetRST()); EXPECT_CALL(*bus, Reset).Times(0); EXPECT_CALL(controller, Reset).Times(0); controller.Error(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION, status::RESERVATION_CONFLICT); - EXPECT_EQ(BUS::phase_t::busfree, controller.GetPhase()); + EXPECT_EQ(phase_t::busfree, controller.GetPhase()); - controller.SetPhase(BUS::phase_t::reserved); + controller.SetPhase(phase_t::reserved); EXPECT_CALL(*bus, Acquire); EXPECT_CALL(*bus, GetRST()); EXPECT_CALL(*bus, Reset).Times(0); @@ -289,7 +289,7 @@ TEST(ScsiControllerTest, Error) EXPECT_CALL(controller, Status); controller.Error(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION, status::RESERVATION_CONFLICT); EXPECT_EQ(status::RESERVATION_CONFLICT, controller.GetStatus()); - EXPECT_EQ(BUS::phase_t::reserved, controller.GetPhase()); + EXPECT_EQ(phase_t::reserved, controller.GetPhase()); } TEST(ScsiControllerTest, RequestSense) diff --git a/cpp/test/test_shared.cpp b/cpp/test/test_shared.cpp index 4b474cc4..b39ee46c 100644 --- a/cpp/test/test_shared.cpp +++ b/cpp/test/test_shared.cpp @@ -7,99 +7,141 @@ // //--------------------------------------------------------------------------- +#include "test_shared.h" +#include "controllers/controller_manager.h" #include "mocks.h" #include "shared/rascsi_exceptions.h" #include "shared/rascsi_version.h" -#include "controllers/controller_manager.h" -#include "test_shared.h" +#include +#include #include #include -#include -#include using namespace std; using namespace filesystem; -shared_ptr CreateDevice(PbDeviceType type, MockAbstractController& controller, const string& extension) +// Inlude the process id in the temp file path so that multiple instances of the test procedures +// could run on the same host. +const path test_data_temp_path(temp_directory_path() / + path(fmt::format("rascsi-test-{}", + getpid()))); // NOSONAR Publicly writable directory is fine here + +shared_ptr CreateDevice(PbDeviceType type, MockAbstractController &controller, const string &extension) { - DeviceFactory device_factory; + DeviceFactory device_factory; - auto device = device_factory.CreateDevice(type, 0, extension); - unordered_map params; - device->Init(params); + auto device = device_factory.CreateDevice(type, 0, extension); + unordered_map params; + device->Init(params); - controller.AddDevice(device); + controller.AddDevice(device); - return device; + return device; } -void TestInquiry(PbDeviceType type, device_type t, scsi_level l, const string& ident, int additional_length, - bool removable, const string& extension) +void TestInquiry(PbDeviceType type, device_type t, scsi_level l, const string &ident, int additional_length, + bool removable, const string &extension) { - auto bus = make_shared(); - auto controller_manager = make_shared(*bus); - auto controller = make_shared>(controller_manager, 0); - auto device = CreateDevice(type, *controller, extension); + auto bus = make_shared(); + auto controller_manager = make_shared(*bus); + auto controller = make_shared>(controller_manager, 0); + auto device = CreateDevice(type, *controller, extension); - auto& cmd = controller->GetCmd(); + auto &cmd = controller->GetCmd(); // ALLOCATION LENGTH cmd[4] = 255; EXPECT_CALL(*controller, DataIn()); device->Dispatch(scsi_command::eCmdInquiry); - const vector& buffer = controller->GetBuffer(); - EXPECT_EQ(t, static_cast(buffer[0])); - EXPECT_EQ(removable ? 0x80: 0x00, buffer[1]); - EXPECT_EQ(l, static_cast(buffer[2])); - EXPECT_EQ(l > scsi_level::SCSI_2 ? scsi_level::SCSI_2 : l, static_cast(buffer[3])); - EXPECT_EQ(additional_length, buffer[4]); - string product_data; - if (ident.size() == 24) { - ostringstream s; - s << ident << setw(2) << setfill('0') << rascsi_major_version << rascsi_minor_version; - product_data = s.str(); - } - else { - product_data = ident; - } - EXPECT_TRUE(!memcmp(product_data.c_str(), &buffer[8], 28)); + const vector &buffer = controller->GetBuffer(); + EXPECT_EQ(t, static_cast(buffer[0])); + EXPECT_EQ(removable ? 0x80 : 0x00, buffer[1]); + EXPECT_EQ(l, static_cast(buffer[2])); + EXPECT_EQ(l > scsi_level::SCSI_2 ? scsi_level::SCSI_2 : l, static_cast(buffer[3])); + EXPECT_EQ(additional_length, buffer[4]); + string product_data; + if (ident.size() == 24) { + ostringstream s; + s << ident << setw(2) << setfill('0') << rascsi_major_version << rascsi_minor_version; + product_data = s.str(); + } else { + product_data = ident; + } + EXPECT_TRUE(!memcmp(product_data.c_str(), &buffer[8], 28)); } pair OpenTempFile() { - const string filename = string(temp_directory_path()) + "/rascsi_test-XXXXXX"; //NOSONAR Publicly writable directory is fine here - vector f(filename.begin(), filename.end()); - f.push_back(0); + const string filename = + string(test_data_temp_path) + "/rascsi_test-XXXXXX"; // NOSONAR Publicly writable directory is fine here + vector f(filename.begin(), filename.end()); + f.push_back(0); - const int fd = mkstemp(f.data()); - EXPECT_NE(-1, fd) << "Couldn't create temporary file '" << f.data() << "'"; + create_directories(path(filename).parent_path()); - return make_pair(fd, path(f.data())); + const int fd = mkstemp(f.data()); + EXPECT_NE(-1, fd) << "Couldn't create temporary file '" << f.data() << "'"; + + return make_pair(fd, path(f.data())); } path CreateTempFile(int size) { - const auto [fd, filename] = OpenTempFile(); + const auto [fd, filename] = OpenTempFile(); - vector data(size); - const size_t count = write(fd, data.data(), data.size()); - close(fd); - EXPECT_EQ(count, data.size()) << "Couldn't create temporary file '" << string(filename) << "'"; + vector data(size); + const size_t count = write(fd, data.data(), data.size()); + close(fd); + EXPECT_EQ(count, data.size()) << "Couldn't create temporary file '" << string(filename) << "'"; - return path(filename); + return path(filename); } -int GetInt16(const vector& buf, int offset) +void CreateTempFileWithData(string filename, vector &data) { - assert(buf.size() > static_cast(offset) + 1); + path new_filename = test_data_temp_path; + new_filename += path(filename); - return (to_integer(buf[offset]) << 8) | to_integer(buf[offset + 1]); + create_directories(new_filename.parent_path()); + + FILE *fp = fopen(new_filename.c_str(), "wb"); + if (fp == nullptr) { + printf("ERROR: Unable to open file %s\n", new_filename.c_str()); + return; + } + + size_t size_written = fwrite(&data[0], sizeof(uint8_t), data.size(), fp); + + if (size_written != sizeof(vector::value_type) * data.size()) { + printf("Expected to write %zu bytes, but only wrote %zu to %s", size_written, + sizeof(vector::value_type) * data.size(), filename.c_str()); + } + fclose(fp); } -uint32_t GetInt32(const vector& buf, int offset) +void DeleteTempFile(string filename) { - assert(buf.size() > static_cast(offset) + 3); - - return (to_integer(buf[offset]) << 24) | (to_integer(buf[offset + 1]) << 16) | - (to_integer(buf[offset + 2]) << 8) | to_integer(buf[offset + 3]); + path temp_file = test_data_temp_path; + temp_file += path(filename); + remove(temp_file); +} + +void CleanupAllTempFiles() +{ + remove_all(test_data_temp_path); +} + +int GetInt16(const vector &buf, int offset) +{ + assert(buf.size() > static_cast(offset) + 1); + + return (to_integer(buf[offset]) << 8) | to_integer(buf[offset + 1]); +} + +uint32_t GetInt32(const vector &buf, int offset) +{ + assert(buf.size() > static_cast(offset) + 3); + + return (to_integer(buf[offset]) << 24) | (to_integer(buf[offset + 1]) << 16) | + (to_integer(buf[offset + 2]) << 8) | to_integer(buf[offset + 3]); } diff --git a/cpp/test/test_shared.h b/cpp/test/test_shared.h index 365ec760..c376ab62 100644 --- a/cpp/test/test_shared.h +++ b/cpp/test/test_shared.h @@ -22,6 +22,8 @@ using namespace rascsi_interface; class PrimaryDevice; class MockAbstractController; +extern const path test_data_temp_path; + shared_ptr CreateDevice(PbDeviceType, MockAbstractController&, const string& = ""); void TestInquiry(PbDeviceType, scsi_defs::device_type, scsi_defs::scsi_level, const string&, @@ -30,5 +32,13 @@ void TestInquiry(PbDeviceType, scsi_defs::device_type, scsi_defs::scsi_level, co pair OpenTempFile(); path CreateTempFile(int); + +// create a file with the specified data +void CreateTempFileWithData(string filename, vector &data); + +void DeleteTempFile(string filename); +// Call this at the end of every test case to make sure things are cleaned up +void CleanupAllTempFiles(); + int GetInt16(const vector&, int); uint32_t GetInt32(const vector&, int); diff --git a/doc/scsiloop.1 b/doc/scsiloop.1 new file mode 100644 index 00000000..0ef620d0 --- /dev/null +++ b/doc/scsiloop.1 @@ -0,0 +1,35 @@ +.TH scsiloop 1 +.SH NAME +scsiloop \- Tool for testing the RaSCSI board with a loopback adapter installed +.SH SYNOPSIS +.B scsiloop +[\fB\-L\fR \fILOG_LEVEL\fR] +.SH DESCRIPTION +.B scsiloop +Performs a self-test of the RaSCSI hardware to ensure that the board is functioning properly. In order for this tool to work, a special loopback cable MUST be attached to the RaSCSI SCSI connector. + +In addition to testing the GPIO signals, scsiloop will perform a self-test of the hardware timers that are built into the system on a chip (SoC). + +The loopback connections for the DB25 connector are shown here: + + |Pin | Name | Pin | Name | + +----+------+-----+------+ + | 1 | REQ | 13 | DB7 | + | 2 | MSG | 12 | DB6 | + | 3 | I/O | 11 | DB5 | + | 4 | RST | 10 | DB3 | + | 5 | ACK | 8 | DB0 | + | 6 | BSY | 20 | DBP | + | 15 | C/D | 23 | DB4 | + | 17 | ATN | 22 | DB2 | + | 19 | SEL | 21 | DB1 | + +.SH OPTIONS +.TP +.BR \-L\fI " " \fILOG_LEVEL +The rascsi log level (trace, debug, info, warn, err, off). The default log level is 'info'. + +.SH SEE ALSO +rasctl(1), rascsi(1), scsimon(1) + +Full documentation is available at: diff --git a/doc/scsiloop_man_page.txt b/doc/scsiloop_man_page.txt new file mode 100644 index 00000000..be3049fd --- /dev/null +++ b/doc/scsiloop_man_page.txt @@ -0,0 +1,41 @@ +!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!! +!! ------ The native file is scsiloop.1. Re-run 'make docs' after updating + + +scsiloop(1) General Commands Manual scsiloop(1) + +NAME + scsiloop - Tool for testing the RaSCSI board with a loopback adapter installed + +SYNOPSIS + scsiloop [-L LOG_LEVEL] + +DESCRIPTION + scsiloop Performs a self-test of the RaSCSI hardware to ensure that the board is functioning properly. In order for this tool to work, a special loopback cable MUST be attached to the RaSCSI SCSI connector. + + In addition to testing the GPIO signals, scsiloop will perform a self-test of the hardware timers that are built into the system on a chip (SoC). + + The loopback connections for the DB25 connector are shown here: + + |Pin | Name | Pin | Name | + +----+------+-----+------+ + | 1 | REQ | 13 | DB7 | + | 2 | MSG | 12 | DB6 | + | 3 | I/O | 11 | DB5 | + | 4 | RST | 10 | DB3 | + | 5 | ACK | 8 | DB0 | + | 6 | BSY | 20 | DBP | + | 15 | C/D | 23 | DB4 | + | 17 | ATN | 22 | DB2 | + | 19 | SEL | 21 | DB1 | + +OPTIONS + -L LOG_LEVEL + The rascsi log level (trace, debug, info, warn, err, off). The default log level is 'info'. + +SEE ALSO + rasctl(1), rascsi(1), scsimon(1) + + Full documentation is available at: + + scsiloop(1)