Initial merge of (incomplete) Banana Pi updates (#993)

This commit is contained in:
akuker 2022-12-02 22:20:27 -06:00 committed by GitHub
parent a403ec3ded
commit eb71c31cf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 8367 additions and 2881 deletions

View File

@ -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) \

View File

@ -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<PrimaryDevice> 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;

View File

@ -14,10 +14,10 @@
#include <unordered_map>
#include <unordered_set>
#include <memory>
#include "hal/bus.h"
using namespace std;
class BUS;
class AbstractController;
class PrimaryDevice;

View File

@ -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; }
};

View File

@ -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<int>(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;
}
}

View File

@ -60,7 +60,7 @@ public:
void Reset() override;
BUS::phase_t Process(int) override;
phase_t Process(int) override;
int GetEffectiveLun() const override;

View File

@ -5,6 +5,7 @@
// Copyright (C) 2001-2006 (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_t, 8> BUS::phase_table = {
const array<phase_t, 8> 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_t, 8> BUS::phase_table = {
// Phase string to phase mapping
//
//---------------------------------------------------------------------------
const unordered_map<BUS::phase_t, const char*> BUS::phase_str_mapping = {
const unordered_map<phase_t, const char*> BUS::phase_str_mapping = {
{ phase_t::busfree, "busfree" },
{ phase_t::arbitration, "arbitration" },
{ phase_t::selection, "selection" },

View File

@ -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 <cstdint>
#include <array>
#include <vector>
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <vector>
#include <stdexcept>
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<DataSample> GetSample(uint64_t timestamp = 0) = 0;
virtual int CommandHandShake(vector<uint8_t> &) = 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_t, 8> 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<uint8_t>&) = 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_t, 8> phase_table;
static const unordered_map<phase_t, const char *> phase_str_mapping;
static const unordered_map<phase_t, const char *> phase_str_mapping;
};

View File

@ -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 <string>
//
// 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

View File

@ -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 <string>
//
// 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

View File

@ -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 <string>
//
// 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

View File

@ -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 <string>
//
// 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

40
cpp/hal/data_sample.cpp Normal file
View File

@ -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 <string>
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);
}

54
cpp/hal/data_sample.h Normal file
View File

@ -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 <string>
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;
};

View File

@ -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 <cstdint>
uint8_t DataSample_BananaM2p::GetDAT() const
{
uint8_t ret_val = 0;
ret_val |= GetSignal(BPI_PIN_DT0) ? 0x01 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
ret_val |= GetSignal(BPI_PIN_DT1) ? 0x02 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
ret_val |= GetSignal(BPI_PIN_DT2) ? 0x04 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
ret_val |= GetSignal(BPI_PIN_DT3) ? 0x08 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
ret_val |= GetSignal(BPI_PIN_DT4) ? 0x10 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
ret_val |= GetSignal(BPI_PIN_DT5) ? 0x20 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
ret_val |= GetSignal(BPI_PIN_DT6) ? 0x40 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
ret_val |= GetSignal(BPI_PIN_DT7) ? 0x80 : 0x00; // NOSONAR: GCC 10 doesn't fully support std::byte
return ret_val;
}
bool DataSample_BananaM2p::GetSignal(int pin) const
{
int bank = SunXI::GPIO_BANK(pin);
int num = SunXI::GPIO_NUM(pin);
return (bool)((data[bank] >> num) & 0x1);
}
// This will return the Banana Pi data in the "Raspberry Pi" data format.
uint32_t DataSample_BananaM2p::GetRawCapture() const
{
uint32_t rpi_data = 0;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_BSY)) << PIN_BSY;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_SEL)) << PIN_SEL;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_ATN)) << PIN_ATN;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_ACK)) << PIN_ACK;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_RST)) << PIN_RST;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_MSG)) << PIN_MSG;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_CD)) << PIN_CD;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_IO)) << PIN_IO;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_REQ)) << PIN_REQ;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_ACT)) << PIN_ACT;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DP)) << PIN_DP;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT0)) << PIN_DT0;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT1)) << PIN_DT1;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT2)) << PIN_DT2;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT3)) << PIN_DT3;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT4)) << PIN_DT4;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT5)) << PIN_DT5;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT6)) << PIN_DT6;
rpi_data |= ((uint32_t)GetSignal(BPI_PIN_DT7)) << PIN_DT7;
return rpi_data;
}

View File

@ -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 <array>
#if defined CONNECT_TYPE_STANDARD
#include "hal/connection_type/connection_standard.h"
#elif defined CONNECT_TYPE_FULLSPEC
#include "hal/connection_type/connection_fullspec.h"
#elif defined CONNECT_TYPE_AIBOM
#include "hal/connection_type/connection_aibom.h"
#elif defined CONNECT_TYPE_GAMERNIUM
#include "hal/connection_type/connection_gamernium.h"
#else
#error Invalid connection type or none specified
#endif
class DataSample_BananaM2p final : public DataSample
{
public:
bool GetSignal(int pin) const override;
bool GetBSY() const override
{
return GetSignal(BPI_PIN_BSY);
}
bool GetSEL() const override
{
return GetSignal(BPI_PIN_SEL);
}
bool GetATN() const override
{
return GetSignal(BPI_PIN_ATN);
}
bool GetACK() const override
{
return GetSignal(BPI_PIN_ACK);
}
bool GetRST() const override
{
return GetSignal(BPI_PIN_RST);
}
bool GetMSG() const override
{
return GetSignal(BPI_PIN_MSG);
}
bool GetCD() const override
{
return GetSignal(BPI_PIN_CD);
}
bool GetIO() const override
{
return GetSignal(BPI_PIN_IO);
}
bool GetREQ() const override
{
return GetSignal(BPI_PIN_REQ);
}
bool GetACT() const override
{
return GetSignal(BPI_PIN_ACT);
}
bool GetDP() const override
{
return GetSignal(BPI_PIN_DP);
}
uint8_t GetDAT() const override;
uint32_t GetRawCapture() const override;
DataSample_BananaM2p(const array<uint32_t, 12> &in_data, uint64_t in_timestamp)
: DataSample{in_timestamp}, data{in_data}
{
}
DataSample_BananaM2p() = default;
~DataSample_BananaM2p() override = default;
private:
array<uint32_t, 12> data = {0};
};

View File

@ -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"

View File

@ -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;
};

File diff suppressed because it is too large Load Diff

View File

@ -11,10 +11,11 @@
#pragma once
#include "shared/config.h"
#include "hal/bus.h"
#include "shared/scsi.h"
#include "bus.h"
#include <array>
#include <memory>
#include <vector>
#ifdef __linux__
#include <linux/gpio.h>
@ -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<<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))
//---------------------------------------------------------------------------
//
// Constant declarations (GPIO)
//
//---------------------------------------------------------------------------
const static uint32_t SYST_OFFSET = 0x00003000;
const static uint32_t IRPT_OFFSET = 0x0000B200;
const static uint32_t ARMT_OFFSET = 0x0000B400;
const static uint32_t PADS_OFFSET = 0x00100000;
const static uint32_t GPIO_OFFSET = 0x00200000;
const static uint32_t QA7_OFFSET = 0x01000000;
const static int GPIO_INPUT = 0;
const static int GPIO_OUTPUT = 1;
const static int GPIO_PULLNONE = 0;
const static int GPIO_PULLDOWN = 1;
const static int GPIO_PULLUP = 2;
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 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 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 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
#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
const static int GPIO_INPUT = 0;
const static int GPIO_OUTPUT = 1;
const static int GPIO_IRQ_IN = 3;
const static int GPIO_PULLNONE = 0;
const static int GPIO_PULLDOWN = 1;
const static int GPIO_PULLUP = 2;
//---------------------------------------------------------------------------
//
// Constant declarations (Control signals)
//
//---------------------------------------------------------------------------
#define ACT_OFF !ACT_ON
#define ENB_OFF !ENB_ON
#define TAD_OUT !TAD_IN
#define IND_OUT !IND_IN
#define DTD_OUT !DTD_IN
#define ACT_OFF !ACT_ON
#define ENB_OFF !ENB_ON
#define TAD_OUT !TAD_IN
#define IND_OUT !IND_IN
#define DTD_OUT !DTD_IN
//---------------------------------------------------------------------------
//
// Constant declarations (SCSI)
//
//---------------------------------------------------------------------------
#define IN GPIO_INPUT
#define OUT GPIO_OUTPUT
const static int ON = 1;
const static int OFF = 0;
//---------------------------------------------------------------------------
//
// 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;
#define IN GPIO_INPUT
#define OUT GPIO_OUTPUT
const static int ON = 1;
const static int OFF = 0;
//---------------------------------------------------------------------------
//
// Class definition
//
//---------------------------------------------------------------------------
class GPIOBUS final : public BUS
class GPIOBUS : public BUS
{
public:
// Basic Functions
GPIOBUS()= default;
~GPIOBUS() override = default;
// Destructor
bool Init(mode_e mode = mode_e::TARGET) override;
// Initialization
void Reset() override;
// Reset
void Cleanup() override;
// Cleanup
public:
// Basic Functions
GPIOBUS() = default;
// Destructor
~GPIOBUS() override = default;
// Initialization
bool Init(mode_e mode = mode_e::TARGET) override;
//---------------------------------------------------------------------------
//
// Bus signal acquisition
//
//---------------------------------------------------------------------------
inline uint32_t Acquire() override
{
#if defined(__x86_64__) || defined(__X86__)
// Only used for development/debugging purposes. Isn't really applicable
// to any real-world RaSCSI application
return 0;
#else
signals = *level;
// Command receive handshake
int CommandHandShake(vector<uint8_t> &) 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<uint8_t>&) 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<uint32_t, 4> 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<array<uint32_t, 256>, 3> tblDatMsk; // Data mask table
array<array<uint32_t, 256>, 3> tblDatSet; // Data setting table
#else
array<uint32_t, 256> tblDatMsk = {}; // Data mask table
array<uint32_t, 256> tblDatSet = {}; // Table setting table
#endif
static const array<int, 19> SignalTable; // signal table
};

View File

@ -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 <string>
//
// 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

File diff suppressed because it is too large Load Diff

221
cpp/hal/gpiobus_bananam2p.h Normal file
View File

@ -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 <map>
//---------------------------------------------------------------------------
//
// Class definition
//
//---------------------------------------------------------------------------
class GPIOBUS_BananaM2p : public GPIOBUS
{
public:
// Basic Functions
GPIOBUS_BananaM2p() = default;
~GPIOBUS_BananaM2p() override = default;
// Destructor
bool Init(mode_e mode = mode_e::TARGET) override;
void Cleanup() override;
void Reset() override;
//---------------------------------------------------------------------------
//
// Bus signal acquisition
//
//---------------------------------------------------------------------------
uint32_t Acquire() override;
void SetENB(bool ast) override;
// Set ENB signal
bool GetBSY() const override;
// Get BSY signal
void SetBSY(bool ast) override;
// Set BSY signal
bool GetSEL() const override;
// Get SEL signal
void SetSEL(bool ast) override;
// Set SEL signal
bool GetATN() const override;
// Get ATN signal
void SetATN(bool ast) override;
// Set ATN signal
bool GetACK() const override;
// Get ACK signal
void SetACK(bool ast) override;
// Set ACK signal
bool GetACT() const override;
// Get ACT signal
void SetACT(bool ast) override;
// Set ACT signal
bool GetRST() const override;
// Get RST signal
void SetRST(bool ast) override;
// Set RST signal
bool GetMSG() const override;
// Get MSG signal
void SetMSG(bool ast) override;
// Set MSG signal
bool GetCD() const override;
// Get CD signal
void SetCD(bool ast) override;
// Set CD signal
bool GetIO() override;
// Get IO signal
void SetIO(bool ast) override;
// Set IO signal
bool GetREQ() const override;
// Get REQ signal
void SetREQ(bool ast) override;
// Set REQ signal
bool GetDP() const override;
uint8_t GetDAT() override;
// Get DAT signal
void SetDAT(uint8_t dat) override;
// Set DAT signal
bool WaitREQ(bool ast) override
{
return WaitSignal(BPI_PIN_REQ, ast);
}
bool WaitACK(bool ast) override
{
return WaitSignal(BPI_PIN_ACK, ast);
}
// TODO: Restore these back to protected
// protected:
// SCSI I/O signal control
void MakeTable() override;
// Create work data
void SetControl(int pin, bool ast) override;
// Set Control Signal
void SetMode(int pin, int mode) override;
// Set SCSI I/O mode
int GetMode(int pin) override;
bool GetSignal(int pin) const override;
// Set SCSI output signal value
void SetSignal(int pin, bool ast) override;
// Interrupt control
// IRQ Disabled
void DisableIRQ() override;
// IRQ Enabled
void EnableIRQ() override;
// GPIO pin functionality settings
void PinConfig(int pin, int mode) override;
// GPIO pin direction setting
void PullConfig(int pin, int mode) override;
// GPIO pin pull up/down resistor setting
void PinSetSignal(int pin, bool ast) override;
// Set GPIO output signal
void DrvConfig(uint32_t drive) override;
// Set GPIO drive strength
unique_ptr<DataSample> GetSample(uint64_t timestamp) override
{
Acquire();
return make_unique<DataSample_BananaM2p>(signals, timestamp);
}
bool SetupSelEvent();
#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<uint32_t, 4> gpfsel; // GPFSEL0-4 backup values
array<uint32_t, 12> signals = {0}; // All bus signals
#if SIGNAL_CONTROL_MODE == 0
array<array<uint32_t, 256>, 3> tblDatMsk; // Data mask table
array<array<uint32_t, 256>, 3> tblDatSet; // Data setting table
#else
array<uint32_t, 256> tblDatMsk = {}; // Data mask table
array<uint32_t, 256> 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<int, 19> SignalTable;
void InitializeGpio();
std::vector<int> 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
};

View File

@ -4,24 +4,41 @@
// for Raspberry Pi
//
// Copyright (C) 2022 akuker
// [ GPIO bus factory ]
//
//---------------------------------------------------------------------------
#include <memory>
#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<BUS> 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<GPIOBUS>(); 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<BUS> return_ptr;
SBC_Version::Init();
if (SBC_Version::IsBananaPi()) {
LOGTRACE("Creating GPIOBUS_BananaM2p")
return_ptr = make_unique<GPIOBUS_BananaM2p>();
} else if (SBC_Version::IsRaspberryPi()) {
LOGTRACE("Creating GPIOBUS_Raspberry")
return_ptr = make_unique<GPIOBUS_Raspberry>();
} else {
LOGINFO("Creating Virtual GPIOBUS")
return_ptr = make_unique<GPIOBUS_Virtual>();
}
if (!return_ptr->Init(mode)) {
return nullptr;
}
return_ptr->Reset();
return return_ptr;
}

View File

@ -9,14 +9,12 @@
#pragma once
#include "bus.h"
#include "hal/bus.h"
#include <memory>
using namespace std;
class GPIOBUS_Factory
{
public:
static unique_ptr<BUS> Create(BUS::mode_e);
static unique_ptr<BUS> Create(BUS::mode_e mode);
};

View File

@ -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 <string>
//
// 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

View File

@ -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 <string>
//
// 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

View File

@ -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 <map>
#include <string.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/time.h>
//---------------------------------------------------------------------------
//
// 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<uint8_t, 4> buf; fread(buf.data(), 1, buf.size(), fp) == buf.size()) {
address = (int)buf[0] << 24 | (int)buf[1] << 16 | (int)buf[2] << 8 | (int)buf[3] << 0;
}
fclose(fp);
}
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<int, 19> 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<int, 9> pintbl = {PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP};
array<bool, 256> 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;
}

295
cpp/hal/gpiobus_raspberry.h Normal file
View File

@ -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 <map>
//---------------------------------------------------------------------------
//
// SCSI signal pin assignment setting
// GPIO pin mapping table for SCSI signals.
// PIN_DT0PIN_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<DataSample> GetSample(uint64_t timestamp) override
{
Acquire();
return make_unique<DataSample_Raspberry>(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<uint32_t, 4> gpfsel;
#if SIGNAL_CONTROL_MODE == 0
// Data mask table
array<array<uint32_t, 256>, 3> tblDatMsk;
// Data setting table
array<array<uint32_t, 256>, 3> tblDatSet;
#else
// Data mask table
array<uint32_t, 256> tblDatMsk = {};
// Table setting table
array<uint32_t, 256> tblDatSet = {};
#endif
static const array<int, 19> 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;
};

View File

@ -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 <string>
//
// 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

564
cpp/hal/gpiobus_virtual.cpp Normal file
View File

@ -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 <cstddef>
#include <map>
#include <memory>
#include <string.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/time.h>
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<uint32_t *>(mmap(NULL, sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd_shm, 0));
if (static_cast<void *>(signals) == MAP_FAILED) {
LOGERROR("Unabled to map shared memory");
sem_close(mutex_sem);
shm_unlink(SHARED_MEM_NAME.c_str());
}
#else
signals = make_shared<uint32_t>(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<void *>(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<byte>(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;
}

174
cpp/hal/gpiobus_virtual.h Normal file
View File

@ -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 <map>
#include <semaphore.h>
//---------------------------------------------------------------------------
//
// 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<int, 19> SignalTable;
shared_ptr<uint32_t> signals; // All bus signals
unique_ptr<DataSample> GetSample(uint64_t timestamp) override
{
return make_unique<DataSample_Raspberry>(*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
};

356
cpp/hal/pi_defs/bpi-gpio.h Normal file
View File

@ -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;

71
cpp/hal/pi_defs/bpi-m2p.h Executable file
View File

@ -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;

68
cpp/hal/pin_control.h Normal file
View File

@ -0,0 +1,68 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 akuker
//
//---------------------------------------------------------------------------
#include <cstdint>
#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;
};

226
cpp/hal/sbc_version.cpp Normal file
View File

@ -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 <fstream>
#include <iostream>
#include <sstream>
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<std::string, SBC_Version::sbc_version_type, std::less<>> 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<int>(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<uint8_t, 4> buf; fread(buf.data(), 1, buf.size(), fp) == buf.size()) {
address = (int)buf[0] << 24 | (int)buf[1] << 16 | (int)buf[2] << 8 | (int)buf[3] << 0;
}
fclose(fp);
}
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

78
cpp/hal/sbc_version.h Normal file
View File

@ -0,0 +1,78 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 akuker
//
// [ Hardware version detection routines ]
//
//---------------------------------------------------------------------------
#pragma once
#include <map>
#include <string>
//===========================================================================
//
// 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<std::string, sbc_version_type, std::less<>> m_proc_device_tree_mapping;
static const std::string m_device_tree_model_path;
static uint32_t GetDeviceTreeRanges(const char *filename, uint32_t offset);
};

65
cpp/hal/sunxi_utils.cpp Normal file
View File

@ -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 <stdio.h>
#include <string>
using namespace std;
static const string BLACK = "\033[30m"; /* Black */
static const string RED = "\033[31m"; /* Red */
static const string GREEN = "\033[32m"; /* Green */
static const string YELLOW = "\033[33m"; /* Yellow */
static const string BLUE = "\033[34m"; /* Blue */
static const string MAGENTA = "\033[35m"; /* Magenta */
static const string CYAN = "\033[36m"; /* Cyan */
static const string WHITE = "\033[37m"; /* White */
// TODO: this is only a debug function that will be removed at a later date.....
void dump_gpio_registers(const SunXI::sunxi_gpio_reg_t *regs)
{
printf("%s--- GPIO BANK 0 CFG: %08X %08X %08X %08X\n", CYAN.c_str(), regs->gpio_bank[0].CFG[0],
regs->gpio_bank[0].CFG[1], regs->gpio_bank[0].CFG[2], regs->gpio_bank[0].CFG[3]);
printf("--- Dat: (%08X) DRV: %08X %08X\n", regs->gpio_bank[0].DAT, regs->gpio_bank[0].DRV[0],
regs->gpio_bank[0].DRV[1]);
printf("--- Pull: %08X %08x\n", regs->gpio_bank[0].PULL[0], regs->gpio_bank[0].PULL[1]);
printf("--- GPIO INT CFG: %08X %08X %08X\n", regs->gpio_int.CFG[0], regs->gpio_int.CFG[1], regs->gpio_int.CFG[2]);
printf("--- CTL: (%08X) STA: %08X DEB: %08X\n %s", regs->gpio_int.CTL, regs->gpio_int.STA, regs->gpio_int.DEB,
WHITE.c_str());
}

189
cpp/hal/sunxi_utils.h Normal file
View File

@ -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 <cstdint>
#ifndef __arm__
#include <time.h>
#endif
class SunXI
{
public:
static inline int GPIO_BANK(int pin)
{
return (pin >> 5);
}
static inline int GPIO_NUM(int pin)
{
return (pin & 0x1F);
}
static inline int GPIO_CFG_INDEX(int pin)
{
return ((pin & 0x1F) >> 3);
}
static inline int GPIO_CFG_OFFSET(int pin)
{
return (((pin & 0x1F) & 0x7) << 2);
}
static inline int GPIO_PUL_INDEX(int pin)
{
return ((pin & 0x1F) >> 4);
}
static inline int GPIO_PUL_OFFSET(int pin)
{
return ((pin & 0x0F) << 1);
}
static inline int GPIO_DRV_INDEX(int pin)
{
return ((pin & 0x1F) >> 4);
}
static inline int GPIO_DRV_OFFSET(int pin)
{
return ((pin & 0x0F) << 1);
}
static inline void short_wait(void)
{
for (int i = 0; i < 150; i++) {
#ifndef __arm__
// Timing doesn't really matter if we're not on ARM.
// The SunXI SoCs are all ARM-based.
const timespec ts = {.tv_sec = 0, .tv_nsec = 1};
nanosleep(&ts, nullptr);
#else
// wait 150 cycles
asm volatile("nop");
#endif
}
}
enum class gpio_configure_values_e : uint8_t {
gpio_input = 0b000,
gpio_output = 0b001,
gpio_alt_func_1 = 0b010,
gpio_alt_func_2 = 0b011,
gpio_reserved_1 = 0b100,
gpio_reserved_2 = 0b101,
gpio_interupt = 0b110,
gpio_disable = 0b111
};
struct sunxi_gpio {
unsigned int CFG[4]; // NOSONAR: Intentionally using C style arrays for low level register access
unsigned int DAT;
unsigned int DRV[2]; // NOSONAR: Intentionally using C style arrays for low level register access
unsigned int PULL[2]; // NOSONAR: Intentionally using C style arrays for low level register access
};
using sunxi_gpio_t = struct sunxi_gpio;
/* gpio interrupt control */
struct sunxi_gpio_int {
unsigned int CFG[3]; // NOSONAR: Intentionally using C style arrays for low level register access
unsigned int CTL;
unsigned int STA;
unsigned int DEB;
};
using sunxi_gpio_int_t = struct sunxi_gpio_int;
struct sunxi_gpio_reg {
struct sunxi_gpio gpio_bank[9]; // NOSONAR: Intentionally using C style arrays for low level register access
unsigned char res[0xbc]; // NOSONAR: Intentionally using C style arrays for low level register access
struct sunxi_gpio_int gpio_int;
};
using sunxi_gpio_reg_t = struct sunxi_gpio_reg;
static const uint32_t PAGE_SIZE = (4 * 1024);
static const uint32_t BLOCK_SIZE = (4 * 1024);
static const int SETUP_DEVMEM_FAIL = 1;
static const int SETUP_MALLOC_FAIL = 2;
static const int SETUP_MMAP_FAIL = 3;
static const int SETUP_CPUINFO_FAIL = 4;
static const int SETUP_NOT_RPI_FAIL = 5;
static const int INPUT = 1;
static const int OUTPUT = 0;
static const int ALT0 = 4;
static const int HIGH = 1;
static const int LOW = 0;
static const int PUD_OFF = 0;
static const int PUD_DOWN = 1;
static const int PUD_UP = 2;
static const uint32_t SUNXI_R_GPIO_BASE = 0x01F02000;
static const uint32_t SUNXI_R_GPIO_REG_OFFSET = 0xC00;
static const uint32_t SUNXI_GPIO_BASE = 0x01C20000;
static const uint32_t SUNXI_GPIO_REG_OFFSET = 0x800;
static const uint32_t SUNXI_CFG_OFFSET = 0x00;
static const uint32_t SUNXI_DATA_OFFSET = 0x10;
static const uint32_t SUNXI_PUD_OFFSET = 0x1C;
static const uint32_t SUNXI_BANK_SIZE = 0x24;
static const uint32_t MAP_SIZE = (4096 * 2);
static const uint32_t MAP_MASK = (MAP_SIZE - 1);
static const int FSEL_OFFSET = 0; // 0x0000
static const int SET_OFFSET = 7; // 0x001c / 4
static const int CLR_OFFSET = 10; // 0x0028 / 4
static const int PINLEVEL_OFFSET = 13; // 0x0034 / 4
static const int EVENT_DETECT_OFFSET = 16; // 0x0040 / 4
static const int RISING_ED_OFFSET = 19; // 0x004c / 4
static const int FALLING_ED_OFFSET = 22; // 0x0058 / 4
static const int HIGH_DETECT_OFFSET = 25; // 0x0064 / 4
static const int LOW_DETECT_OFFSET = 28; // 0x0070 / 4
static const int PULLUPDN_OFFSET = 37; // 0x0094 / 4
static const int PULLUPDNCLK_OFFSET = 38; // 0x0098 / 4
static const uint32_t TMR_REGISTER_BASE = 0x01C20C00;
static const uint32_t TMR_IRQ_EN_REG = 0x0; // T imer IRQ Enable Register
static const uint32_t TMR_IRQ_STA_REG = 0x4; // Timer Status Register
static const uint32_t TMR0_CTRL_REG = 0x10; // Timer 0 Control Register
static const uint32_t TMR0_INTV_VALUE_REG = 0x14; // Timer 0 Interval Value Register
static const uint32_t TMR0_CUR_VALUE_REG = 0x18; // Timer 0 Current Value Register
static const uint32_t TMR1_CTRL_REG = 0x20; // Timer 1 Control Register
static const uint32_t TMR1_INTV_VALUE_REG = 0x24; // Timer 1 Interval Value Register
static const uint32_t TMR1_CUR_VALUE_REG = 0x28; // Timer 1 Current Value Register
static const uint32_t AVS_CNT_CTL_REG = 0x80; // AVS Control Register
static const uint32_t AVS_CNT0_REG = 0x84; // AVS Counter 0 Register
static const uint32_t AVS_CNT1_REG = 0x88; // AVS Counter 1 Register
static const uint32_t AVS_CNT_DIV_REG = 0x8C; // AVS Divisor Register
static const uint32_t WDOG0_IRQ_EN_REG = 0xA0; // Watchdog 0 IRQ Enable Register
static const uint32_t WDOG0_IRQ_STA_REG = 0xA4; // Watchdog 0 Status Register
static const uint32_t WDOG0_CTRL_REG = 0xB0; // Watchdog 0 Control Register
static const uint32_t WDOG0_CFG_REG = 0xB4; // Watchdog 0 Configuration Register
static const uint32_t WDOG0_MODE_REG = 0xB8; // Watchdog 0 Mode Register
};

View File

@ -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 <sys/mman.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <array>
//---------------------------------------------------------------------------
//
// 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<PlatformSpecificTimer> 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<uint32_t, 32> 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<SysTimer_Raspberry>();
is_raspberry = true;
} else if (SBC_Version::IsBananaPi()) {
systimer_ptr = make_unique<SysTimer_AllWinner>();
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);
}

View File

@ -13,11 +13,30 @@
#pragma once
#include <memory>
#include <stdint.h>
#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<PlatformSpecificTimer> systimer_ptr;
};

View File

@ -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 <sys/mman.h>
#include "hal/gpiobus.h"
#include "shared/log.h"
const std::string SysTimer_AllWinner::dev_mem_filename = "/dev/mem";
//---------------------------------------------------------------------------
//
// Initialize the system timer
//
//---------------------------------------------------------------------------
void SysTimer_AllWinner::Init()
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
int fd;
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
LOGERROR("I can't open /dev/mem. Are you running as root?")
exit(-1);
}
hsitimer_regs =
(sun8i_hsitimer_registers *)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, hs_timer_base_address);
if (hsitimer_regs == MAP_FAILED) {
LOGERROR("Unable to map high speed timer registers. Are you running as root?")
}
sysbus_regs = (struct sun8i_sysbus_registers *)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
system_bus_base_address);
if (sysbus_regs == MAP_FAILED) {
LOGERROR("Unable to map system bus registers. Are you running as root?")
}
enable_hs_timer();
}
void SysTimer_AllWinner::enable_hs_timer()
{
// By default, the HSTimer clock gating is masked. When it is necessary to use
// the HSTimer, its clock gating should be opened in BUS Clock Gating Register 0
// and then de-assert the software reset in BUS Software Reset Register 0 on the
// CCU module. If it is not needed to use the HSTimer, both the gating bit and
// the software reset bit should be set 0.
LOGTRACE("%s [Before Enable] CLK GATE: %08X SOFT RST: %08X", __PRETTY_FUNCTION__, sysbus_regs->bus_clk_gating_reg0,
sysbus_regs->bus_soft_rst_reg0)
sysbus_regs->bus_clk_gating_reg0 = sysbus_regs->bus_clk_gating_reg0 | (1 << BUS_CLK_GATING_REG0_HSTMR);
sysbus_regs->bus_soft_rst_reg0 = sysbus_regs->bus_soft_rst_reg0 | (1 << BUS_SOFT_RST_REG0_HSTMR);
LOGTRACE("%s [After Enable] CLK GATE: %08X SOFT RST: %08X", __PRETTY_FUNCTION__, sysbus_regs->bus_clk_gating_reg0,
sysbus_regs->bus_soft_rst_reg0)
// Set interval value to the maximum value. (its a 52 bit register)
hsitimer_regs->hs_tmr_intv_hi_reg = (1 << 20) - 1; //(0xFFFFF)
hsitimer_regs->hs_tmr_intv_lo_reg = UINT32_MAX;
// Select prescale value of 1, continuouse mode
hsitimer_regs->hs_tmr_ctrl_reg = HS_TMR_CLK_PRE_SCALE_1;
// Set reload bit
hsitimer_regs->hs_tmr_ctrl_reg |= HS_TMR_RELOAD;
// Enable HSTimer
hsitimer_regs->hs_tmr_ctrl_reg |= HS_TMR_EN;
}
// TODO: According to the data sheet, we should turn off the HS timer when we're done with it. But, its just going to
// eat up a little extra power if we leave it running.
void SysTimer_AllWinner::disable_hs_timer()
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
LOGINFO("[Before Disable] CLK GATE: %08X SOFT RST: %08X", sysbus_regs->bus_clk_gating_reg0,
sysbus_regs->bus_soft_rst_reg0)
sysbus_regs->bus_clk_gating_reg0 = sysbus_regs->bus_clk_gating_reg0 & ~(1 << BUS_CLK_GATING_REG0_HSTMR);
sysbus_regs->bus_soft_rst_reg0 = sysbus_regs->bus_soft_rst_reg0 & ~(1 << BUS_SOFT_RST_REG0_HSTMR);
LOGINFO("[After Disable] CLK GATE: %08X SOFT RST: %08X", sysbus_regs->bus_clk_gating_reg0,
sysbus_regs->bus_soft_rst_reg0)
}
uint32_t SysTimer_AllWinner::GetTimerLow()
{
// 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)
;
}

View File

@ -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 <stdint.h>
#include <string>
//===========================================================================
//
// System timer
//
//===========================================================================
class SysTimer_AllWinner : public PlatformSpecificTimer
{
public:
// Default constructor
SysTimer_AllWinner() = default;
// Default destructor
~SysTimer_AllWinner() override = default;
// Initialization
void Init() override;
// Get system timer low byte
uint32_t GetTimerLow() override;
// Get system timer high byte
uint32_t GetTimerHigh() override;
// Sleep for N nanoseconds
void SleepNsec(uint32_t nsec) override;
// Sleep for N microseconds
void SleepUsec(uint32_t usec) override;
private:
void enable_hs_timer();
void disable_hs_timer();
static const std::string dev_mem_filename;
/* Reference: Allwinner H3 Datasheet, section 4.9.3 */
static const uint32_t hs_timer_base_address = 0x01C60000;
/* Note: Currently the high speed timer is NOT in the armbian
* device tree. If it is ever added, this should be pulled
* from there */
using sun8i_hsitimer_registers = struct {
/* 0x00 HS Timer IRQ Enabled Register */
uint32_t hs_tmr_irq_en_reg;
/* 0x04 HS Timer Status Register */
uint32_t hs_tmr_irq_stat_reg;
/* 0x08 Unused */
uint32_t unused_08;
/* 0x0C Unused */
uint32_t unused_0C;
/* 0x10 HS Timer Control Register */
uint32_t hs_tmr_ctrl_reg;
/* 0x14 HS Timer Interval Value Low Reg */
uint32_t hs_tmr_intv_lo_reg;
/* 0x18 HS Timer Interval Value High Register */
uint32_t hs_tmr_intv_hi_reg;
/* 0x1C HS Timer Current Value Low Register */
uint32_t hs_tmr_curnt_lo_reg;
/* 0x20 HS Timer Current Value High Register */
uint32_t hs_tmr_curnt_hi_reg;
};
/* Constants for the HS Timer IRQ enable Register (section 4.9.4.1) */
static const uint32_t HS_TMR_INTERUPT_ENB = (1 << 0);
/* Constants for the HS Timer Control Register (section 4.9.4.3) */
static const uint32_t HS_TMR_EN = (1 << 0);
static const uint32_t HS_TMR_RELOAD = (1 << 1);
static const uint32_t HS_TMR_CLK_PRE_SCALE_1 = (0 << 4);
static const uint32_t HS_TMR_CLK_PRE_SCALE_2 = (1 << 4);
static const uint32_t HS_TMR_CLK_PRE_SCALE_4 = (2 << 4);
static const uint32_t HS_TMR_CLK_PRE_SCALE_8 = (3 << 4);
static const uint32_t HS_TMR_CLK_PRE_SCALE_16 = (4 << 4); // NOSONAR This matches the datasheet
static const uint32_t HS_TMR_MODE_SINGLE = (1 << 7);
static const uint32_t HS_TMR_TEST_MODE = (1 << 31);
volatile sun8i_hsitimer_registers *hsitimer_regs;
/* Reference: Allwinner H3 Datasheet, section 4.3.4 */
static const uint32_t system_bus_base_address = 0x01C20000;
struct sun8i_sysbus_registers {
uint32_t pad_00_5C[(0x60 / sizeof(uint32_t))]; // NOSONAR c-style array used for padding
/* 0x0060 Bus Clock Gating Register 0 */
uint32_t bus_clk_gating_reg0;
uint32_t pad_64_2C0[((0x2C0 - 0x64) / sizeof(uint32_t))]; // NOSONAR c-style array used for padding
/* 0x2C0 Bus Software Reset Register 0 */
uint32_t bus_soft_rst_reg0;
};
/* Bit associated with the HS Timer */
static const uint32_t BUS_CLK_GATING_REG0_HSTMR = 19;
static const uint32_t BUS_SOFT_RST_REG0_HSTMR = 19;
struct sun8i_sysbus_registers *sysbus_regs;
};

View File

@ -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 <memory>
#include <sys/ioctl.h>
#include <sys/mman.h>
#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<uint32_t, 32> maxclock = {32, 0, 0x00030004, 8, 0, 4, 0, 0};
// Save the base address
systaddr = (uint32_t *)map + SYST_OFFSET / sizeof(uint32_t);
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)
;
}

View File

@ -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 <stdint.h>
//===========================================================================
//
// 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;
};

View File

@ -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);
}

View File

@ -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);

View File

@ -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 <sys/time.h>
#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 <climits>
#include <csignal>
#include <iostream>
#include <getopt.h>
#include <iostream>
#include <sched.h>
#include <sys/time.h>
using namespace std;
using namespace ras_util;
@ -31,7 +32,7 @@ void ScsiMon::KillHandler(int)
running = false;
}
void ScsiMon::ParseArguments(const vector<char *>& args)
void ScsiMon::ParseArguments(const vector<char *> &args)
{
int opt;
@ -46,11 +47,11 @@ void ScsiMon::ParseArguments(const vector<char *>& 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<char *>& args)
/* Process any remaining command line arguments (not options). */
if (optind < static_cast<int>(args.size())) {
while (optind < static_cast<int>(args.size()))
file_base_name = args[optind++];
while (optind < static_cast<int>(args.size())) {
file_base_name = args[optind];
optind++;
}
}
vcd_file_name = file_base_name;
@ -72,7 +75,7 @@ void ScsiMon::ParseArguments(const vector<char *>& args)
html_file_name += ".html";
}
void ScsiMon::PrintHelpText(const vector<char *>& args) const
void ScsiMon::PrintHelpText(const vector<char *> &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<char *>& args)
int ScsiMon::run(const vector<char *> &args)
{
#ifdef DEBUG
spdlog::set_level(spdlog::level::trace);
@ -162,12 +159,8 @@ int ScsiMon::run(const vector<char *>& 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<DataSample> prev_sample = nullptr;
shared_ptr<DataSample> this_sample = nullptr;
timeval start_time;
timeval stop_time;
uint64_t loop_count = 0;
@ -181,12 +174,11 @@ int ScsiMon::run(const vector<char *>& 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<char *>& 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<char *>& 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<char *>& 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<char *>& 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();

View File

@ -10,52 +10,50 @@
#pragma once
#include "hal/bus.h"
#include "monitor/data_sample.h"
#include <vector>
#include "hal/data_sample.h"
#include <memory>
#include <vector>
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<char *> &);
int run(const vector<char *>&);
inline static double ns_per_loop;
inline static double ns_per_loop;
private:
void ParseArguments(const vector<char *> &);
void PrintHelpText(const vector<char *> &) const;
void Banner() const;
bool Init();
void Cleanup() const;
void Reset() const;
private:
static void KillHandler(int);
void ParseArguments(const vector<char *>&);
void PrintHelpText(const vector<char *>&) const;
void Banner() const;
bool Init();
void Cleanup() const;
void Reset() const;
static inline volatile bool running;
static void KillHandler(int);
shared_ptr<BUS> bus;
static inline volatile bool running;
uint32_t buff_size = 1000000;
unique_ptr<BUS> bus;
vector<shared_ptr<DataSample>> 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;
};

View File

@ -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<shared_ptr<DataSample>> &data_capture_array)
{
const data_capture *data;
shared_ptr<DataSample> 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 << "<table>" << 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 << "<tr>";
close_row = true; // Close the row the next time around
html_fp << "<td>" << (double)data->timestamp / 100000 << "</td>";
html_fp << "<td>" << GetPhaseStr(data) << "</td>";
html_fp << "<td>" << (double)data->GetTimestamp() / 100000 << "</td>";
html_fp << "<td>" << data->GetPhaseStr() << "</td>";
html_fp << "<td>" << std::hex << selected_id << "</td>";
html_fp << "<td>";
}
@ -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<shared_ptr<DataSample>> &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;

View File

@ -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 <iostream>
#include <fstream>
#include <iostream>
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<shared_ptr<DataSample>> &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, &timestamp_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<uint32_t>(strtoul(data, &ptr, 16));
data = str_buf.substr(data_pos + data_label.length(), 8);
data_uint = static_cast<uint32_t>(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<DataSample_Raspberry>(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<shared_ptr<DataSample>> &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();
}

View File

@ -9,10 +9,11 @@
//
//---------------------------------------------------------------------------
#include "data_sample.h"
#include "hal/data_sample.h"
#include <memory>
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<shared_ptr<DataSample>> &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<shared_ptr<DataSample>> &data_capture_array);
void scsimon_generate_json(const string &filename, const vector<shared_ptr<DataSample>> &data_capture_array);
void scsimon_generate_value_change_dump(const string &filename, const vector<shared_ptr<DataSample>> &data_capture_array);

View File

@ -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 <sstream>
#include <iostream>
#include <fstream>
#include <iostream>
#include <sstream>
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<uint8_t,32> 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<uint8_t>(new_value)) {
prev_value[pin] = static_cast<uint8_t>(new_value);
fp << "s" << GPIOBUS::GetPhaseStrRaw(new_value) << " " << symbol << endl;
if (prev_value[pin] != static_cast<uint8_t>(data)) {
prev_value[pin] = static_cast<uint8_t>(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<shared_ptr<DataSample>> &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(&timestamp[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<DataSample> 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();
}

View File

@ -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<char *>& args) const
{
@ -584,7 +585,7 @@ int Rascsi::run(const vector<char *>& 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<char *>& 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<char *>& args)
phase = controller->Process(initiator_id);
// End when the bus is free
if (phase == BUS::phase_t::busfree) {
if (phase == phase_t::busfree) {
break;
}
}

View File

@ -139,7 +139,7 @@ void RasDump::ParseArguments(const vector<char *>& args)
buffer = vector<uint8_t>(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<uint8_t>& cdb) const
Selection();
WaitPhase(BUS::phase_t::command);
WaitPhase(phase_t::command);
cdb[0] = static_cast<uint8_t>(cmd);
cdb[1] = static_cast<uint8_t>(static_cast<byte>(cdb[1]) | static_cast<byte>(target_lun << 5));
@ -189,7 +189,7 @@ void RasDump::Command(scsi_command cmd, vector<uint8_t>& 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<uint8_t, 256> 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<uint8_t, 256> buf; bus->ReceiveHandShake(buf.data(), 1) != 1) {
throw parser_exception("MESSAGE IN failed");

View File

@ -36,7 +36,7 @@ private:
void ParseArguments(const vector<char *>&);
int DumpRestore();
pair<uint64_t, uint32_t> GetDeviceInfo();
void WaitPhase(BUS::phase_t) const;
void WaitPhase(phase_t) const;
void Selection() const;
void Command(scsi_defs::scsi_command, vector<uint8_t>&) const;
void DataIn(int);

20
cpp/scsiloop.cpp Normal file
View File

@ -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<char *> args(argv, argv + argc);
return ScsiLoop().run(args);
}

View File

@ -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 <iostream>
#include <signal.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
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<char *> &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<char *> &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<int>(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<char *> &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<string> 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;
}

View File

@ -0,0 +1,32 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
// Copyright (C) 2022 akuker
//
//---------------------------------------------------------------------------
#pragma once
#include <map>
#include <string>
#include <vector>
using namespace std;
class ScsiLoop
{
public:
ScsiLoop() = default;
~ScsiLoop() = default;
int run(const vector<char *> &args);
private:
void Banner(const vector<char *> &) const;
static void TerminationHandler(int signum);
bool ParseArgument(const vector<char *> &);
bool SetLogLevel(const string &);
};

View File

@ -0,0 +1,40 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded for Raspberry Pi
// Loopback tester utility
//
// Copyright (C) 2022 akuker
//
//---------------------------------------------------------------------------
#include "scsiloop_cout.h"
#include <iostream>
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<string> &test_errors)
{
if (test_errors.size() > 0) {
for (auto err_string : test_errors) {
cout << RED << err_string << endl;
}
}
}

View File

@ -0,0 +1,33 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded for Raspberry Pi
// Loopback tester utility
//
// Copyright (C) 2022 akuker
//
//---------------------------------------------------------------------------
#include <string>
#include <vector>
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<string> &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 */
};

View File

@ -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<string> &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<string> &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<string> &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<string> &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;
}

View File

@ -0,0 +1,81 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded for Raspberry Pi
// Loopback tester utility
//
// Copyright (C) 2022 akuker
//
//---------------------------------------------------------------------------
#include <map>
#include <string>
#include <vector>
#include "hal/gpiobus.h"
using namespace std;
class ScsiLoop_GPIO
{
public:
ScsiLoop_GPIO();
~ScsiLoop_GPIO() = default;
int RunLoopbackTest(vector<string> &error_list);
int RunDataInputTest(vector<string> &error_list);
int RunDataOutputTest(vector<string> &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<int, string> pin_name_lookup;
std::vector<loopback_connection> 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<string> &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> bus;
};

View File

@ -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<string> &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;
}

View File

@ -0,0 +1,19 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded for Raspberry Pi
// Loopback tester utility
//
// Copyright (C) 2022 akuker
//
//---------------------------------------------------------------------------
#include <string>
#include <vector>
using namespace std;
class ScsiLoop_Timer
{
public:
static int RunTimerTest(vector<string> &error_list);
};

View File

@ -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<scsi_command, pair<int, const char *>> 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<scsi_command, pair<int, const char *>> 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

View File

@ -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<ControllerManager>(*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<scsi_exception>(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<scsi_exception>(AllOf(
Property(&scsi_exception::get_sense_key, sense_key::ABORTED_COMMAND),
Property(&scsi_exception::get_asc, asc::NO_ADDITIONAL_SENSE_INFORMATION))));

View File

@ -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<MockBus> bus;
NiceMock<MockBus> 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));
}

View File

@ -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<uint8_t> data;
// If bytes 4-7 are non-zero, get_peripheral address should return those bytes
data = vector<uint8_t>(
{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<uint8_t>(
{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<uint8_t>(
{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());
}

View File

@ -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 <filesystem>
#include <map>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/time.h>
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"

21
cpp/test/linux_os_stubs.h Normal file
View File

@ -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 <stdio.h>
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
}

View File

@ -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<DataSample>, 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));

View File

@ -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());

View File

@ -34,26 +34,26 @@ TEST(ScsiControllerTest, Process)
auto controller_manager = make_shared<ControllerManager>(*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<ControllerManager>(*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<ControllerManager>(*bus);
auto controller = make_shared<MockScsiController>(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<MockPrimaryDevice>(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<ControllerManager>(*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<ControllerManager>(*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<ControllerManager>(*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<ControllerManager>(*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<ControllerManager>(*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)

View File

@ -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 <filesystem>
#include <sstream>
#include <unistd.h>
#include <vector>
#include <sstream>
#include <filesystem>
using namespace std;
using namespace filesystem;
shared_ptr<PrimaryDevice> 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<PrimaryDevice> CreateDevice(PbDeviceType type, MockAbstractController &controller, const string &extension)
{
DeviceFactory device_factory;
DeviceFactory device_factory;
auto device = device_factory.CreateDevice(type, 0, extension);
unordered_map<string, string> params;
device->Init(params);
auto device = device_factory.CreateDevice(type, 0, extension);
unordered_map<string, string> 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<MockBus>();
auto controller_manager = make_shared<ControllerManager>(*bus);
auto controller = make_shared<NiceMock<MockAbstractController>>(controller_manager, 0);
auto device = CreateDevice(type, *controller, extension);
auto bus = make_shared<MockBus>();
auto controller_manager = make_shared<ControllerManager>(*bus);
auto controller = make_shared<NiceMock<MockAbstractController>>(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<uint8_t>& buffer = controller->GetBuffer();
EXPECT_EQ(t, static_cast<device_type>(buffer[0]));
EXPECT_EQ(removable ? 0x80: 0x00, buffer[1]);
EXPECT_EQ(l, static_cast<scsi_level>(buffer[2]));
EXPECT_EQ(l > scsi_level::SCSI_2 ? scsi_level::SCSI_2 : l, static_cast<scsi_level>(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<uint8_t> &buffer = controller->GetBuffer();
EXPECT_EQ(t, static_cast<device_type>(buffer[0]));
EXPECT_EQ(removable ? 0x80 : 0x00, buffer[1]);
EXPECT_EQ(l, static_cast<scsi_level>(buffer[2]));
EXPECT_EQ(l > scsi_level::SCSI_2 ? scsi_level::SCSI_2 : l, static_cast<scsi_level>(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<int, path> OpenTempFile()
{
const string filename = string(temp_directory_path()) + "/rascsi_test-XXXXXX"; //NOSONAR Publicly writable directory is fine here
vector<char> 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<char> 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<char> 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<char> 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<byte>& buf, int offset)
void CreateTempFileWithData(string filename, vector<uint8_t> &data)
{
assert(buf.size() > static_cast<size_t>(offset) + 1);
path new_filename = test_data_temp_path;
new_filename += path(filename);
return (to_integer<int>(buf[offset]) << 8) | to_integer<int>(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<uint8_t>::value_type) * data.size()) {
printf("Expected to write %zu bytes, but only wrote %zu to %s", size_written,
sizeof(vector<uint8_t>::value_type) * data.size(), filename.c_str());
}
fclose(fp);
}
uint32_t GetInt32(const vector<byte>& buf, int offset)
void DeleteTempFile(string filename)
{
assert(buf.size() > static_cast<size_t>(offset) + 3);
return (to_integer<uint32_t>(buf[offset]) << 24) | (to_integer<uint32_t>(buf[offset + 1]) << 16) |
(to_integer<uint32_t>(buf[offset + 2]) << 8) | to_integer<uint32_t>(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<byte> &buf, int offset)
{
assert(buf.size() > static_cast<size_t>(offset) + 1);
return (to_integer<int>(buf[offset]) << 8) | to_integer<int>(buf[offset + 1]);
}
uint32_t GetInt32(const vector<byte> &buf, int offset)
{
assert(buf.size() > static_cast<size_t>(offset) + 3);
return (to_integer<uint32_t>(buf[offset]) << 24) | (to_integer<uint32_t>(buf[offset + 1]) << 16) |
(to_integer<uint32_t>(buf[offset + 2]) << 8) | to_integer<uint32_t>(buf[offset + 3]);
}

View File

@ -22,6 +22,8 @@ using namespace rascsi_interface;
class PrimaryDevice;
class MockAbstractController;
extern const path test_data_temp_path;
shared_ptr<PrimaryDevice> 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<int, path> OpenTempFile();
path CreateTempFile(int);
// create a file with the specified data
void CreateTempFileWithData(string filename, vector<uint8_t> &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<byte>&, int);
uint32_t GetInt32(const vector<byte>&, int);

35
doc/scsiloop.1 Normal file
View File

@ -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: <https://www.github.com/akuker/RASCSI/wiki/>

41
doc/scsiloop_man_page.txt Normal file
View File

@ -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: <https://www.github.com/akuker/RASCSI/wiki/>
scsiloop(1)