Issue #7 - implement a scsi monitor function for RaSCSI to log SCSI traffic (#46)

* #7 Re-merge scsimon functionality with latest master. The old scsimon branch was waaaaay too out of date

* #7 Re-merge scsimon functionality with latest master. The old scsimon branch was waaaaay too out of date

* Added libspdlog-dev as a required package

* Cleanup from master re-base

* Updated to use GCC version 8, to match the raspberry pi

Co-authored-by: akuker <akuker@gmail.com>
This commit is contained in:
akuker 2020-10-19 07:31:06 -05:00 committed by GitHub
parent 3ddeac6180
commit 1118c344cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 739 additions and 88 deletions

View File

@ -9,9 +9,18 @@ jobs:
steps:
- name: Install cross compile toolchain
run: sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf binutils-arm-linux-gnueabihf
run: sudo apt-get install gcc-8-arm-linux-gnueabihf g++-8-arm-linux-gnueabihf binutils-arm-linux-gnueabihf libspdlog-dev
- uses: actions/checkout@v2
- name: dump arm gcc version
run: arm-linux-gnueabihf-gcc -v
working-directory: ./src/raspberrypi
- name: dump native gcc version
run: gcc -v
working-directory: ./src/raspberrypi
- name: make standard
run: make all DEBUG=1 CONNECT_TYPE=STANDARD
working-directory: ./src/raspberrypi

View File

@ -57,6 +57,6 @@ To create an empty, 100MB HD image, use the following command:
dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800
.SH SEE ALSO
rasctl(1), scsidump(1)
rasctl(1), scsimon(1)
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>

View File

@ -2,7 +2,7 @@
!! ------ The native file is rascsi.1. Re-run 'make docs' after updating
rascsi(1) General Commands Manual rascsi(1)
rascsi(1) General Commands Manual rascsi(1)
NAME
rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins
@ -13,10 +13,9 @@ SYNOPSIS
DESCRIPTION
rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins.
In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) devices can be specified. The number (n)
after the ID or HD idnetifier specifies the ID number for that device. For SCSI: The ID is limited from 0-7.
However, typically SCSI ID 7 is reserved for the "initiator" (the host computer).Note that SASI is considered
rare and only used on very early Sharp X68000 computers.
In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) devices can be specified. The number (n) after the ID or HD idnetifier specifies the ID number for that device. For SCSI:
The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer).Note that SASI is considered rare and only used on very early Sharp X68000 com
puters.
RaSCSI will determin the type of device based upon the file extension of the FILE argument.
hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used with X68000)
@ -31,9 +30,8 @@ DESCRIPTION
For example, if you want to specify an Apple-compatible HD image on ID 0, you can use the following command:
sudo rascsi -ID0 /path/to/drive/hdimage.hda
Once RaSCSI starts, it will open a socket (port 6868) to allow external management commands. If another
process is using port 6868, RaSCSI will terminate, since it is likely another instance of RaSCSI. Once
RaSCSI has initialized, the rasctl utility can be used to send commands.
Once RaSCSI starts, it will open a socket (port 6868) to allow external management commands. If another process is using port 6868, RaSCSI will terminate, since it is likely another in
stance of RaSCSI. Once RaSCSI has initialized, the rasctl utility can be used to send commands.
To quit RaSCSI, press Control + C. If it is running in the background, you can kill it using an INT signal.
@ -61,8 +59,8 @@ EXAMPLES
dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800
SEE ALSO
rasctl(1), scsidump(1)
rasctl(1), scsimon(1)
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
rascsi(1)
rascsi(1)

View File

@ -66,6 +66,6 @@ Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the cont
rasctl -i 0 -f HDIIMAGE0.HDS
.SH SEE ALSO
rascsi(1)
rascsi(1) scsimon(1)
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>

View File

@ -2,7 +2,7 @@
!! ------ The native file is rasctl.1. Re-run 'make docs' after updating
rascsi(1) General Commands Manual rascsi(1)
rascsi(1) General Commands Manual rascsi(1)
NAME
rasctl - Sends management commands to the rascsi process
@ -11,15 +11,13 @@ SYNOPSIS
rasctl -l | -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE]
DESCRIPTION
rasctl Sends commands to the rascsi process to make configuration adjustments at runtime or to check the sta
tus of the devices.
rasctl Sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the devices.
Either the -i or -l option should be specified at one time. Not both.
You do NOT need root privileges to use rasctl.
Note: The command and type arguments are case insensitive. Only the first letter of the command/type are
evaluated by the tool.
Note: The command and type arguments are case insensitive. Only the first letter of the command/type are evaluated by the tool.
OPTIONS
-l List all of the devices that are currently being emulated by RaSCSI, as well as their current status.
@ -27,8 +25,7 @@ OPTIONS
-i ID ID is the SCSI ID that you want to control. (0-7)
-u UNIT
Unit number (0 or 1). This will default to 0. This option is only used when there are multiple SCSI
devices on a shared SCSI controller. (This is not common)
Unit number (0 or 1). This will default to 0. This option is only used when there are multiple SCSI devices on a shared SCSI controller. (This is not common)
-c CMD Command is the operation being requested. options are:
attach: attach disk
@ -40,8 +37,7 @@ OPTIONS
When the command is omited, rasctl will default to the 'attach' command
-t TYPE
Specifies the type of disk. If this disagrees with the file extension of the specified image, the TYPE
argument is ignored. Available drive types are:
Specifies the type of disk. If this disagrees with the file extension of the specified image, the TYPE argument is ignored. Available drive types are:
hd: Hard disk (SCSI or SASI)
mo: Magneto-Optical disk)
cd: CD-ROM
@ -61,13 +57,12 @@ EXAMPLES
| 0 | 1 | SCHD | /home/pi/harddisk.hda
+----+----+------+-------------------------------------
Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the contents of the file system image
"HDIIMAGE0.HDS".
Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the contents of the file system image "HDIIMAGE0.HDS".
rasctl -i 0 -f HDIIMAGE0.HDS
SEE ALSO
rascsi(1)
rascsi(1) scsimon(1)
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
rascsi(1)
rascsi(1)

27
doc/scsimon.1 Normal file
View File

@ -0,0 +1,27 @@
.TH scsimon 1
.SH NAME
scsimon \- Acts as a data capture tool for all traffic on the SCSI bus. Data is stored in a Value Change Dump (VCD) file.
.SH SYNOPSIS
.B scsimon
.SH DESCRIPTION
.B scsimon
Monitors all of the traffic on the SCSI bus, using a RaSCSI device. The data is cached in memory while the tool is running. A circular buffer is used so that only the most recent 1,000,000 transactions are stored. The tool will continue to run until the user presses CTRL-C, or the process receives a SIGINT signal.
.PP
The logged data is stored in a file called "log.vcd" in the current working directory from where scsimon was launched.
Currently, scsimon doesn't accept any agruments.
To quit scsimon, press Control + C.
.SH OPTIONS
.TP
None
.SH EXAMPLES
Launch scsimon to capture all SCSI traffic available to the RaSCSI hardware:
scsimon
.SH SEE ALSO
rasctl(1), rascsi(1)
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>

35
doc/scsimon_man_page.txt Normal file
View File

@ -0,0 +1,35 @@
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
!! ------ The native file is scsimon.1. Re-run 'make docs' after updating
scsimon(1) General Commands Manual scsimon(1)
NAME
scsimon - Acts as a data capture tool for all traffic on the SCSI bus. Data is stored in a Value Change Dump (VCD) file.
SYNOPSIS
scsimon
DESCRIPTION
scsimon Monitors all of the traffic on the SCSI bus, using a RaSCSI device. The data is cached in memory while the tool is running. A circular buffer is used so that only the most recent 1,000,000
transactions are stored. The tool will continue to run until the user presses CTRL-C, or the process receives a SIGINT signal.
The logged data is stored in a file called "log.vcd" in the current working directory from where scsimon was launched.
Currently, scsimon doesn't accept any agruments.
To quit scsimon, press Control + C.
OPTIONS
None
EXAMPLES
Launch scsimon to capture all SCSI traffic available to the RaSCSI hardware:
scsimon
SEE ALSO
rasctl(1), rascsi(1)
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
scsimon(1)

View File

@ -4,6 +4,7 @@
*.cbp
*.layout
*.log
*.vcd
rascsi
scsimon
rasctl

View File

@ -38,8 +38,8 @@ CXXFLAGS += -std=c++14 -iquote . -MD -MP
## STANDARD or FULLSPEC. The default is STANDARD
## * THIS IS TYPICALLY THE ONLY COMPILE OPTION YOU
## * NEED TO SPECIFY
# If its not specified, build for STANDARD configuration
CONNECT_TYPE ?= STANDARD
# If its not specified, build for FULLSPEC configuration
CONNECT_TYPE ?= FULLSPEC
ifdef CONNECT_TYPE
CFLAGS += -DCONNECT_TYPE_$(CONNECT_TYPE)
@ -67,8 +67,7 @@ BINDIR := ./bin/$(shell echo $(CONNECT_TYPE) | tr '[:upper:]' '[:lower:]')
#BIN_ALL = $(RASCSI) $(RASCTL) $(RASDUMP) $(SASIDUMP) $(SCSIMON)
# Temporarily remove the RASDUMP and RASDUMP tools, since they're not needed
# for my specific use case. If you need them - add them back in!
BIN_ALL = $(BINDIR)/$(RASCSI) $(BINDIR)/$(RASCTL)
BIN_ALL = $(BINDIR)/$(RASCSI) $(BINDIR)/$(RASCTL) $(BINDIR)/$(SCSIMON)
SRC_RASCSI = \
rascsi.cpp \
@ -83,6 +82,16 @@ SRC_RASCSI = \
SRC_RASCSI += $(shell find ./controllers -name '*.cpp')
SRC_RASCSI += $(shell find ./devices -name '*.cpp')
SRC_SCSIMON = \
scsimon.cpp \
scsi.cpp \
gpiobus.cpp \
filepath.cpp \
fileio.cpp
SRC_SCSIMON += $(shell find ./controllers -name '*.cpp')
SRC_SCSIMON += $(shell find ./devices -name '*.cpp')
SRC_RASCTL = \
rasctl.cpp
# rasctl_command.cpp
@ -113,12 +122,12 @@ OBJ_RASDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASDUMP:%.cpp=%.o)))
OBJ_SASIDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SASIDUMP:%.cpp=%.o)))
OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o)))
#OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) $(OBJ_SCSIMON)
OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP)
OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) $(OBJ_SCSIMON)
# 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) $(OBJ_RASCTL))
ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_SCSIMON))
-include $(ALL_DEPS)
$(OBJDIR) $(BINDIR):
@ -137,21 +146,21 @@ $(OBJDIR)/%.o: %.cpp | $(OBJDIR)
all: $(BIN_ALL) docs
ALL: all
docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt
docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt
$(BINDIR)/$(RASCSI): $(OBJ_RASCSI) | $(BINDIR)
$(CXX) -o $@ $(OBJ_RASCSI) -lpthread
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI) -lpthread
$(BINDIR)/$(RASCTL): $(OBJ_RASCTL) $(BINDIR)
$(CXX) -o $@ $(OBJ_RASCTL)
$(BINDIR)/$(RASCTL): $(OBJ_RASCTL) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL)
$(RASDUMP): $(OBJ_RASDUMP) $(BINDIR)
$(CXX) -o $@ $(OBJ_RASDUMP)
$(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP)
$(SASIDUMP): $(OBJ_SASIDUMP) $(BINDIR)
$(CXX) -o $@ $(OBJ_SASIDUMP)
$(BINDIR)/$(SASIDUMP): $(OBJ_SASIDUMP) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_SASIDUMP)
$(SCSIMON): $(OBJ_SCSIMON) $(BINDIR)
$(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) -lpthread
## clean : Remove all of the object files, intermediate
@ -180,7 +189,7 @@ run:
## * sudo systemctl enable rascsi
## * sudo systemctl start rascsi
.PHONY: install
install: $(MAN_PAGE_DIR)/rascsi.1 $(MAN_PAGE_DIR)/rasctl.1 $(USR_LOCAL_BIN)/$(RASCTL) $(USR_LOCAL_BIN)/$(RASCSI) $(SYSTEMD_CONF) $(RSYSLOG_CONF) $(RSYSLOG_LOG)
install: $(MAN_PAGE_DIR)/rascsi.1 $(MAN_PAGE_DIR)/rasctl.1 $(MAN_PAGE_DIR)/scsimon.1 $(USR_LOCAL_BIN)/$(RASCTL) $(USR_LOCAL_BIN)/$(RASCSI) $(USR_LOCAL_BIN)/$(SCSIMON) $(SYSTEMD_CONF) $(RSYSLOG_CONF) $(RSYSLOG_LOG)
@echo "-- Done installing!"
$(USR_LOCAL_BIN)% : $(BINDIR)/%

View File

@ -296,7 +296,7 @@ BUS::phase_t FASTCALL SASIDEV::Process()
}
// Get bus information
ctrl.bus->Aquire();
((GPIOBUS*)ctrl.bus)->Aquire();
// For the monitor tool, we shouldn't need to reset. We're just logging information
// Reset
@ -927,7 +927,7 @@ void FASTCALL SASIDEV::Error()
ASSERT(this);
// Get bus information
ctrl.bus->Aquire();
((GPIOBUS*)ctrl.bus)->Aquire();
// Reset check
if (ctrl.bus->GetRST()) {

View File

@ -76,7 +76,7 @@ BUS::phase_t FASTCALL SCSIDEV::Process()
}
// Get bus information
ctrl.bus->Aquire();
((GPIOBUS*)ctrl.bus)->Aquire();
// Reset
if (ctrl.bus->GetRST()) {
@ -488,7 +488,7 @@ void FASTCALL SCSIDEV::Error()
ASSERT(this);
// Get bus information
ctrl.bus->Aquire();
((GPIOBUS*)ctrl.bus)->Aquire();
// Reset check
if (ctrl.bus->GetRST()) {

View File

@ -15,6 +15,7 @@
#include "os.h"
#include "xm6.h"
#include "gpiobus.h"
#include "log.h"
#ifndef BAREMETAL
#ifdef __linux__
@ -65,7 +66,7 @@ DWORD bcm_host_get_peripheral_address(void)
char buf[1024];
size_t len = sizeof(buf);
DWORD address;
if (sysctlbyname("hw.model", buf, &len, NULL, 0) ||
strstr(buf, "ARM1176JZ-S") != buf) {
// Failed to get CPU model || Not BCM2835
@ -88,7 +89,7 @@ extern uint32_t RPi_IO_Base_Addr;
// Core frequency
extern uint32_t RPi_Core_Freq;
#ifdef USE_SEL_EVENT_ENABLE
#ifdef USE_SEL_EVENT_ENABLE
//---------------------------------------------------------------------------
//
// Interrupt control function
@ -173,7 +174,7 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode)
// Open /dev/mem
fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd == -1) {
printf("Error: Unable to open /dev/mem. Are you running as root?\n");
LOGERROR("Error: Unable to open /dev/mem. Are you running as root?");
return FALSE;
}
@ -295,6 +296,7 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode)
// GPIO chip open
fd = open("/dev/gpiochip0", 0);
if (fd == -1) {
LOGERROR("Unable to open /dev/gpiochip0. Is RaSCSI already running?")
return FALSE;
}
@ -310,6 +312,7 @@ BOOL FASTCALL GPIOBUS::Init(mode_e 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;
}
@ -522,27 +525,6 @@ void FASTCALL GPIOBUS::Reset()
#endif // ifdef __x86_64__ || __X86__
}
//---------------------------------------------------------------------------
//
// Bus signal acquisition
//
//---------------------------------------------------------------------------
DWORD FASTCALL GPIOBUS::Aquire()
{
#if defined(__x86_64__) || defined(__X86__)
return 0;
#else
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;
#endif // ifdef __x86_64__ || __X86__
}
//---------------------------------------------------------------------------
//
// ENB signal setting
@ -668,6 +650,26 @@ void FASTCALL GPIOBUS::SetACK(BOOL ast)
SetSignal(PIN_ACK, ast);
}
//---------------------------------------------------------------------------
//
// Get ACK signal
//
//---------------------------------------------------------------------------
BOOL FASTCALL GPIOBUS::GetACT()
{
return GetSignal(PIN_ACT);
}
//---------------------------------------------------------------------------
//
// Set ACK signal
//
//---------------------------------------------------------------------------
void FASTCALL GPIOBUS::SetACT(BOOL ast)
{
SetSignal(PIN_ACT, ast);
}
//---------------------------------------------------------------------------
//
// Get RST signal
@ -1173,7 +1175,7 @@ int FASTCALL GPIOBUS::SendHandShake(BYTE *buf, int count)
}
// Already waiting for REQ assertion
// Assert the ACK signal
SetSignal(PIN_ACK, ON);
@ -1233,7 +1235,7 @@ int FASTCALL GPIOBUS::PollSelectEvent()
return -1;
}
read(selevreq.fd, &gpev, sizeof(gpev));
(void)read(selevreq.fd, &gpev, sizeof(gpev));
#endif // BAREMETAL
return 0;
@ -1417,7 +1419,7 @@ void FASTCALL GPIOBUS::SetMode(int pin, int mode)
gpio[index] = data;
gpfsel[index] = data;
}
//---------------------------------------------------------------------------
//
// Get input signal value
@ -1427,7 +1429,7 @@ BOOL FASTCALL GPIOBUS::GetSignal(int pin)
{
return (signals >> pin) & 1;
}
//---------------------------------------------------------------------------
//
// Set output signal value
@ -1645,6 +1647,39 @@ void FASTCALL GPIOBUS::DrvConfig(DWORD drive)
pads[PAD_0_27] = (0xFFFFFFF8 & data) | drive | 0x5a000000;
}
//---------------------------------------------------------------------------
//
// Generic Phase Acquisition (Doesn't read GPIO)
//
//---------------------------------------------------------------------------
BUS::phase_t FASTCALL GPIOBUS::GetPhaseRaw(DWORD raw_data)
{
DWORD mci;
// Selection Phase
if (GetPinRaw(raw_data, PIN_SEL))
{
if(GetPinRaw(raw_data, PIN_IO)){
return BUS::reselection;
}else{
return BUS::selection;
}
}
// Bus busy phase
if (!GetPinRaw(raw_data, PIN_BSY)) {
return BUS::busfree;
}
// Get target phase from bus signal line
mci = GetPinRaw(raw_data, PIN_MSG) ? 0x04 : 0x00;
mci |= GetPinRaw(raw_data, PIN_CD) ? 0x02 : 0x00;
mci |= GetPinRaw(raw_data, PIN_IO) ? 0x01 : 0x00;
return GetPhase(mci);
}
//---------------------------------------------------------------------------
//
// System timer address

View File

@ -276,6 +276,26 @@
#define PIN_SEL 23 // SEL
#endif
#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)
@ -434,8 +454,28 @@ public:
void FASTCALL Cleanup();
// Cleanup
DWORD FASTCALL Aquire();
// Signal acquisition
//---------------------------------------------------------------------------
//
// Bus signal acquisition
//
//---------------------------------------------------------------------------
inline DWORD Aquire()
{
#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;
#if SIGNAL_CONTROL_MODE < 2
// Invert if negative logic (internal processing is unified to positive logic)
signals = ~signals;
#endif // SIGNAL_CONTROL_MODE
return signals;
#endif // ifdef __x86_64__ || __X86__
}
void FASTCALL SetENB(BOOL ast);
// Set ENB signal
@ -460,6 +500,11 @@ public:
void FASTCALL SetACK(BOOL ast);
// Set ACK signal
BOOL FASTCALL GetACT();
// Get ACT signal
void FASTCALL SetACT(BOOL ast);
// Set ACT signal
BOOL FASTCALL GetRST();
// Get RST signal
void FASTCALL SetRST(BOOL ast);
@ -498,6 +543,9 @@ public:
int FASTCALL SendHandShake(BYTE *buf, int count);
// Data transmission handshake
static BUS::phase_t FASTCALL GetPhaseRaw(DWORD raw_data);
// Get the phase based on raw data
#ifdef USE_SEL_EVENT_ENABLE
// SEL signal interrupt
int FASTCALL PollSelectEvent();

View File

@ -5,13 +5,37 @@
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ ログ ]
// Copyright (C) 2020 akuker
// [ Logging utilities ]
//
//---------------------------------------------------------------------------
#if !defined(log_h)
#define log_h
#include "spdlog/spdlog.h"
#include "spdlog/sinks/sink.h"
#define SPDLOGWRAPPER(loglevel, ...)\
do{ char buf[256]; \
snprintf(buf, sizeof(buf),__VA_ARGS__); \
spdlog::log(loglevel,buf);}while(0);
#ifndef DEBUG
// If we're doing a non-debug build, we want to skip the overhead of
// formatting the string, then calling the logger
#define LOGTRACE(...) ((void)0)
#define LOGDEBUG(...) ((void)0)
#else
#define LOGTRACE(...) SPDLOGWRAPPER(spdlog::level::trace, __VA_ARGS__)
#define LOGDEBUG(...) SPDLOGWRAPPER(spdlog::level::debug, __VA_ARGS__)
#endif
#define LOGINFO(...) SPDLOGWRAPPER(spdlog::level::info, __VA_ARGS__)
#define LOGWARN(...) SPDLOGWRAPPER(spdlog::level::warn, __VA_ARGS__)
#define LOGERROR(...) SPDLOGWRAPPER(spdlog::level::err, __VA_ARGS__)
#define LOGCRITICAL(...) SPDLOGWRAPPER(spdlog::level::critical, __VA_ARGS__)
//===========================================================================
//
// ログ

View File

@ -24,7 +24,7 @@
#include "controllers/scsidev_ctrl.h"
#include "controllers/sasidev_ctrl.h"
#include "gpiobus.h"
#include "spdlog/spdlog.h"
//---------------------------------------------------------------------------
//
@ -1064,6 +1064,8 @@ int main(int argc, char* argv[])
struct sched_param schparam;
#endif // BAREMETAL
spdlog::set_level(spdlog::level::trace);
LOGTRACE("Entering the function %s with %d arguments", __PRETTY_FUNCTION__, argc);
// Output the Banner
Banner(argc, argv);

View File

@ -41,18 +41,58 @@ BUS::phase_t FASTCALL BUS::GetPhase()
return GetPhase(mci);
}
//---------------------------------------------------------------------------
//
// Determine Phase String phase enum
//
//---------------------------------------------------------------------------
const char* FASTCALL BUS::GetPhaseStrRaw(phase_t current_phase){
if(current_phase <= phase_t::reserved){
return phase_str_table[current_phase];
}
else
{
return "INVALID";
}
}
//---------------------------------------------------------------------------
//
// Phase Table
// Reference Table 8: https://www.staff.uni-mainz.de/tacke/scsi/SCSI2-06.html
// This determines the phase based upon the Msg, C/D and I/O signals.
//
//---------------------------------------------------------------------------
const BUS::phase_t BUS::phase_table[8] = {
dataout,
datain,
command,
status,
reserved,
reserved,
msgout,
msgin
// | MSG|C/D|I/O |
dataout, // | 0 | 0 | 0 |
datain, // | 0 | 0 | 1 |
command, // | 0 | 1 | 0 |
status, // | 0 | 1 | 1 |
reserved, // | 1 | 0 | 0 |
reserved, // | 1 | 0 | 1 |
msgout, // | 1 | 1 | 0 |
msgin // | 1 | 1 | 1 |
};
//---------------------------------------------------------------------------
//
// Phase Table
// This MUST be kept in sync with the phase_t enum type!
//
//---------------------------------------------------------------------------
const char* BUS::phase_str_table[] = {
"busfree",
"arbitration",
"selection",
"reselection",
"command",
"execute",
"datain",
"dataout",
"status",
"msgin",
"msgout",
"reserved"
};

View File

@ -60,8 +60,14 @@ public:
}
// フェーズ取得
virtual DWORD FASTCALL Aquire() = 0;
// 信号取り込み
static const char* FASTCALL GetPhaseStrRaw(phase_t current_phase);
// Get the string phase name, based upon the raw data
// Extract as specific pin field from a raw data capture
static inline DWORD GetPinRaw(DWORD raw_data, DWORD pin_num)
{
return ((raw_data >> pin_num) & 1);
}
virtual BOOL FASTCALL GetBSY() = 0;
// BSYシグナル取得
@ -125,6 +131,8 @@ public:
private:
static const phase_t phase_table[8];
// フェーズテーブル
static const char* phase_str_table[];
};
#endif // scsi_h

420
src/raspberrypi/scsimon.cpp Normal file
View File

@ -0,0 +1,420 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ RaSCSI main ]
//
//---------------------------------------------------------------------------
#include "os.h"
#include "xm6.h"
#include "filepath.h"
#include "fileio.h"
#include "devices/disk.h"
#include "log.h"
#include "gpiobus.h"
#include "spdlog/spdlog.h"
#include <sys/time.h>
//---------------------------------------------------------------------------
//
// Constant declarations
//
//---------------------------------------------------------------------------
#define MAX_BUFF_SIZE 1000000
// 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.
#define SYMBOL_PIN_DAT '#'
#define SYMBOL_PIN_ATN '+'
#define SYMBOL_PIN_RST '$'
#define SYMBOL_PIN_ACK '%'
#define SYMBOL_PIN_REQ '^'
#define SYMBOL_PIN_MSG '&'
#define SYMBOL_PIN_CD '*'
#define SYMBOL_PIN_IO '('
#define SYMBOL_PIN_BSY ')'
#define SYMBOL_PIN_SEL '-'
#define SYMBOL_PIN_PHASE '='
// We'll use position 0 in the prev_value array to store the previous phase
#define PIN_PHASE 0
//---------------------------------------------------------------------------
//
// Variable declarations
//
//---------------------------------------------------------------------------
static BYTE prev_value[32] = {0xFF};
static volatile BOOL running; // Running flag
static volatile BOOL active; // Processing flag
GPIOBUS *bus; // GPIO Bus
typedef struct data_capture{
DWORD data;
timeval timestamp;
} data_capture_t;
data_capture data_buffer[MAX_BUFF_SIZE];
int data_idx = 0;
//---------------------------------------------------------------------------
//
// Signal Processing
//
//---------------------------------------------------------------------------
void KillHandler(int sig)
{
// Stop instruction
running = FALSE;
}
//---------------------------------------------------------------------------
//
// Banner Output
//
//---------------------------------------------------------------------------
void Banner(int argc, char* argv[])
{
LOGINFO("SCSI Monitor Capture Tool - part of RaSCSI(*^..^*) ");
LOGINFO("version %01d.%01d%01d(%s, %s)",
(int)((VERSION >> 8) & 0xf),
(int)((VERSION >> 4) & 0xf),
(int)((VERSION ) & 0xf),
__DATE__,
__TIME__);
LOGINFO("Powered by XM6 TypeG Technology ");
LOGINFO("Copyright (C) 2016-2020 GIMONS");
LOGINFO("Copyright (C) 2020 akuker");
LOGINFO("Connect type : %s", CONNECT_DESC);
LOGINFO(" log.vcd - Value Change Dump file that can be opened with GTKWave");
if ((argc > 1 && strcmp(argv[1], "-h") == 0) ||
(argc > 1 && strcmp(argv[1], "--help") == 0)){
LOGINFO("Usage: %s ...", argv[0]);
exit(0);
}
else
{
LOGINFO(" ");
LOGINFO("Current running & collecting data. Press CTRL-C to stop.")
LOGINFO(" ");
}
}
//---------------------------------------------------------------------------
//
// Initialization
//
//---------------------------------------------------------------------------
BOOL Init()
{
// Interrupt handler settings
if (signal(SIGINT, KillHandler) == SIG_ERR) {
return FALSE;
}
if (signal(SIGHUP, KillHandler) == SIG_ERR) {
return FALSE;
}
if (signal(SIGTERM, KillHandler) == SIG_ERR) {
return FALSE;
}
// GPIOBUS creation
bus = new GPIOBUS();
// GPIO Initialization
if (!bus->Init()) {
LOGERROR("Unable to intiailize the GPIO bus. Exiting....");
return FALSE;
}
// Bus Reset
bus->Reset();
// Other
running = FALSE;
active = FALSE;
return TRUE;
}
BOOL get_pin_value(DWORD data, int pin)
{
return (data >> pin) & 1;
}
BYTE get_data_field(DWORD data)
{
DWORD data_out;
data_out =
((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 (BYTE)data_out;
}
void vcd_output_if_changed_phase(FILE *fp, DWORD data, int pin, char symbol)
{
BUS::phase_t new_value = GPIOBUS::GetPhaseRaw(data);
if(prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fprintf(fp, "s%s %c\n", GPIOBUS::GetPhaseStrRaw(new_value), symbol);
}
}
void vcd_output_if_changed_bool(FILE *fp, DWORD data, int pin, char symbol)
{
BOOL new_value = get_pin_value(data,pin);
if(prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fprintf(fp, "%d%c\n", new_value, symbol);
}
}
void vcd_output_if_changed_byte(FILE *fp, DWORD data, int pin, char symbol)
{
BYTE new_value = get_data_field(data);
if(prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fprintf(fp, "b%d%d%d%d%d%d%d%d %c\n",
get_pin_value(data,PIN_DT0),
get_pin_value(data,PIN_DT1),
get_pin_value(data,PIN_DT2),
get_pin_value(data,PIN_DT3),
get_pin_value(data,PIN_DT4),
get_pin_value(data,PIN_DT5),
get_pin_value(data,PIN_DT6),
get_pin_value(data,PIN_DT7), symbol);
}
}
void create_value_change_dump()
{
time_t rawtime;
struct tm * timeinfo;
int i = 0;
timeval time_diff;
char timestamp[256];
FILE *fp;
timeval start_time = data_buffer[0].timestamp;
LOGINFO("Creating Value Change Dump file (log.vcd)\n");
fp = fopen("log.vcd","w");
// Get the current time
time (&rawtime);
timeinfo = localtime(&rawtime);
strftime (timestamp,sizeof(timestamp),"%d-%m-%Y %H-%M-%S",timeinfo);
fprintf(fp, "$date\n");
fprintf(fp, "%s\n", timestamp);
fprintf(fp, "$end\n");
fprintf(fp, "$version\n");
fprintf(fp, " VCD generator tool version info text.\n");
fprintf(fp, "$end\n");
fprintf(fp, "$comment\n");
fprintf(fp, " Any comment text.\n");
fprintf(fp, "$end\n");
fprintf(fp, "$timescale 1 us $end\n");
fprintf(fp, "$scope module logic $end\n");
fprintf(fp, "$var wire 1 %c BSY $end\n", SYMBOL_PIN_BSY);
fprintf(fp, "$var wire 1 %c SEL $end\n", SYMBOL_PIN_SEL);
fprintf(fp, "$var wire 1 %c CD $end\n", SYMBOL_PIN_CD);
fprintf(fp, "$var wire 1 %c IO $end\n", SYMBOL_PIN_IO);
fprintf(fp, "$var wire 1 %c MSG $end\n", SYMBOL_PIN_MSG);
fprintf(fp, "$var wire 1 %c REQ $end\n", SYMBOL_PIN_REQ);
fprintf(fp, "$var wire 1 %c ACK $end\n", SYMBOL_PIN_ACK);
fprintf(fp, "$var wire 1 %c ATN $end\n", SYMBOL_PIN_ATN);
fprintf(fp, "$var wire 1 %c RST $end\n", SYMBOL_PIN_RST);
fprintf(fp, "$var wire 8 %c data $end\n", SYMBOL_PIN_DAT);
fprintf(fp, "$var string 1 %c phase $end\n", SYMBOL_PIN_PHASE);
fprintf(fp, "$upscope $end\n");
fprintf(fp, "$enddefinitions $end\n");
// Initial values - default to zeros
fprintf(fp, "$dumpvars\n");
fprintf(fp, "0%c\n", SYMBOL_PIN_BSY);
fprintf(fp, "0%c\n", SYMBOL_PIN_SEL);
fprintf(fp, "0%c\n", SYMBOL_PIN_CD);
fprintf(fp, "0%c\n", SYMBOL_PIN_IO);
fprintf(fp, "0%c\n", SYMBOL_PIN_MSG);
fprintf(fp, "0%c\n", SYMBOL_PIN_REQ);
fprintf(fp, "0%c\n", SYMBOL_PIN_ACK);
fprintf(fp, "0%c\n", SYMBOL_PIN_ATN);
fprintf(fp, "0%c\n", SYMBOL_PIN_RST);
fprintf(fp, "b00000000 %c\n", SYMBOL_PIN_DAT);
fprintf(fp, "$end\n");
while(i < data_idx)
{
timersub(&(data_buffer[i].timestamp), &start_time, &time_diff);
fprintf(fp, "#%ld\n",((time_diff.tv_sec*1000000) + time_diff.tv_usec));
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_BSY, SYMBOL_PIN_BSY);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_SEL, SYMBOL_PIN_SEL);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_CD, SYMBOL_PIN_CD);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_IO, SYMBOL_PIN_IO);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_MSG, SYMBOL_PIN_MSG);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_REQ, SYMBOL_PIN_REQ);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_ACK, SYMBOL_PIN_ACK);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_ATN, SYMBOL_PIN_ATN);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_RST, SYMBOL_PIN_RST);
vcd_output_if_changed_byte(fp, data_buffer[i].data, PIN_DT0, SYMBOL_PIN_DAT);
vcd_output_if_changed_phase(fp, data_buffer[i].data, PIN_PHASE, SYMBOL_PIN_PHASE);
i++;
}
fclose(fp);
}
//---------------------------------------------------------------------------
//
// Cleanup
//
//---------------------------------------------------------------------------
void Cleanup()
{
LOGINFO("Stoping data collection....\n");
create_value_change_dump();
// Cleanup the Bus
bus->Cleanup();
// Discard the GPIOBUS object
delete bus;
}
//---------------------------------------------------------------------------
//
// Reset
//
//---------------------------------------------------------------------------
void Reset()
{
// Reset the bus
bus->Reset();
}
//---------------------------------------------------------------------------
//
// Pin the thread to a specific CPU
//
//---------------------------------------------------------------------------
void FixCpu(int cpu)
{
cpu_set_t cpuset;
int cpus;
// Get the number of CPUs
CPU_ZERO(&cpuset);
sched_getaffinity(0, sizeof(cpu_set_t), &cpuset);
cpus = CPU_COUNT(&cpuset);
// Set the thread affinity
if (cpu < cpus) {
CPU_ZERO(&cpuset);
CPU_SET(cpu, &cpuset);
sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
}
}
#ifdef DEBUG
static DWORD high_bits = 0x0;
static DWORD low_bits = 0xFFFFFFFF;
#endif
//---------------------------------------------------------------------------
//
// Main processing
//
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
#ifdef DEBUG
DWORD prev_high = high_bits;
DWORD prev_low = low_bits;
#endif
DWORD prev_sample = 0xFFFFFFFF;
DWORD this_sample = 0;
int ret;
struct sched_param schparam;
spdlog::set_level(spdlog::level::trace);
spdlog::set_pattern("%^[%l]%$ %v");
// Output the Banner
Banner(argc, argv);
memset(data_buffer,0,sizeof(data_buffer));
// Initialize
ret = 0;
if (!Init()) {
ret = EPERM;
goto init_exit;
}
// Reset
Reset();
// Set the affinity to a specific processor core
FixCpu(3);
// Scheduling policy setting (highest priority)
schparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &schparam);
// Start execution
running = TRUE;
bus->SetACT(FALSE);
LOGDEBUG("ALL_SCSI_PINS %08X\n",ALL_SCSI_PINS);
// Main Loop
while (running) {
// Work initialization
this_sample = (bus->Aquire() & ALL_SCSI_PINS);
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(" %08lX %08lX\n",high_bits, low_bits);
}
prev_high = high_bits;
prev_low = low_bits;
#endif
data_buffer[data_idx].data = this_sample;
(void)gettimeofday(&(data_buffer[data_idx].timestamp), NULL);
data_idx = (data_idx + 1) % MAX_BUFF_SIZE;
prev_sample = this_sample;
}
continue;
}
// Cleanup
Cleanup();
init_exit:
exit(ret);
}