SASI code removal, error handling update, bug fixes, code cleanup (#806)

Summary ov most important changes triggered by the SASI code removal:

- Removed the SASI controller code
- New controller management. There is a new controller base class AbstractController and a class ControllerManager managing the controller lifecycle. The lifecycle management was removed from rasci.cpp and is covered by unit tests.
- New device management. The DeviceFactory manages the device lifecycle instead of rascsi.cpp. The new code is covered by unit tests.
- The lifecycle managment uses C++ collections with variable size instead of arrays with hard-coded sizes.
- The ScsiController method contains most of what was previously contained in scsidev_ctrl.cpp plus the code from sasidev_ctrl.cpp that was relevant for SCSI.
- scsi_command_util contains helper methods used for identical SCSI command implementations of more than one device
- Devices know their controllers, so that the controller instance does not need to be passed to each SCSI command. This change helps to decouple the devices from the controller. The phase_handler interface is also part of this decoupling.
- Use scsi_command_exception for propagating SCSI command execution errors, This resolves issues with the previous error handling, which was based on return values and often on magic numbers.
- Removed legacy SCSI error codes, all errors are now encoded by sense_key::, asc:: and status::.
- Fixed various warnings reported with -Wextra, -Weffc++ and -Wpedantic.
- Use constructor member initialization lists (recommended for ISO C++)
- Consistently use new/delete instead of malloc/free (recommended for ISO C++), resulting in better type safety and error handling
- Replaced variable sized arrays on the stack (violates ISO C++ and can cause a stack overflow)
- Replaced NULL by nullptr (recommended for C++), resulting in better type safety
- Use more const member functions in order to avoid side effects
- The format device page can now also be changed for hard disk drives (Fujitsu M2624S supports this, for instance), not just for MOs.
- Better encapsulation, updated access specifiers in many places
- Removed unused methods and method arguments
- Fixed a number of TODOs
- Added/updated unit tests for a lot of non-legacy classes
- Makefile support for creating HTML coverage reports with lcov/genhtml
This commit is contained in:
Uwe Seimet 2022-09-03 16:53:53 +02:00 committed by GitHub
parent 2411afb2c4
commit ddeede2beb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
101 changed files with 3966 additions and 5708 deletions

View File

@ -20,12 +20,11 @@ rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins
.B rascsi
Emulates SCSI devices using the Raspberry Pi GPIO pins.
.PP
In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) or SASI (-HDn[:u]) devices can be specified.
In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) devices can be specified.
The number (n) after the ID or HD identifier specifies the ID number for that device. The optional number (u) specifies the LUN (logical unit) for that device. The default LUN is 0.
For SCSI: The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer). The LUN is limited from 0-31. Note that SASI is considered rare and only used on very early Sharp X68000 computers.
For SCSI: The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer). The LUN is limited from 0-31.
.PP
RaSCSI will determine 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)
hds: SCSI Hard Disk image (generic, non-removable)
hdr: SCSI Hard Disk image (generic, removable)
hdn: SCSI Hard Disk image (NEC GENUINE)
@ -47,7 +46,7 @@ To quit RaSCSI, press Control + C. If it is running in the background, you can k
.SH OPTIONS
.TP
.BR \-b\fI " " \fIBLOCK_SIZE
The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
The optional block size, either 512, 1024, 2048 or 4096 bytes. Default size is 512 bytes.
.TP
.BR \-F\fI " " \fIFOLDER
The default folder for image files. For files in this folder no absolute path needs to be specified. The initial default folder is '~/images'.
@ -85,13 +84,9 @@ Overrides the default locale for client-faces error messages. The client can ove
n is the SCSI ID number (0-7). u (0-31) is the optional LUN (logical unit). The default LUN is 0.
.IP
FILE is the name of the image file to use for the SCSI device. For devices that do not support an image file (SCBR, SCDP, SCLP, SCHS) the filename may have a special meaning or a dummy name can be provided. For SCBR and SCDP it is an optioinal prioritized list of network interfaces, an optional IP address and netmask, e.g. "interfaces=eth0,eth1,wlan0:inet=10.10.20.1/24". For SCLP it is the print command to be used and a reservation timeout in seconds, e.g. "cmd=lp -oraw %f:timeout=60".
.TP
.BR \-HD\fIn[:u] " " \fIFILE
n is the SASI ID number (0-15). The effective SASI ID is calculated as n/2, the effective SASI LUN is calculated is the remainder of n/2. Alternatively the n:u syntax can be used, where ns is the SASI ID (0-7) and u the LUN (0-1).
.IP
FILE is the name of the image file to use for the SASI device.
FILE is the name of the image file to use for the SCSI device.
.IP
Note: SASI usage is rare, and is typically limited to early Unix workstations and Sharp X68000 systems.
.SH EXAMPLES
Launch RaSCSI with no emulated drives attached:

View File

@ -14,48 +14,43 @@ SYNOPSIS
DESCRIPTION
rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins.
In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) or SASI
(-HDn[:u]) devices can be specified. The number (n) after the ID or HD
identifier specifies the ID number for that device. The optional number
(u) specifies the LUN (logical unit) for that device. The default LUN
is 0. For SCSI: The ID is limited from 0-7. However, typically SCSI ID
7 is reserved for the "initiator" (the host computer). The LUN is lim
ited from 0-31. 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[:u]) devices can be
specified. The number (n) after the ID or HD identifier specifies the
ID number for that device. The optional number (u) specifies the LUN
(logical unit) for that device. The default LUN is 0. For SCSI: The ID
is limited from 0-7. However, typically SCSI ID 7 is reserved for the
"initiator" (the host computer). The LUN is limited from 0-31.
RaSCSI will determine 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)
hds: SCSI Hard Disk image (generic, non-removable)
hdr: SCSI Hard Disk image (generic, removable)
hdn: SCSI Hard Disk image (NEC GENUINE)
hdi: SCSI Hard Disk image (Anex86 HD image)
nhd: SCSI Hard Disk image (T98Next HD image)
hda: SCSI Hard Disk image (APPLE GENUINE - typically used with Mac
hda: SCSI Hard Disk image (APPLE GENUINE - typically used with Mac
SCSI emulation)
mos: SCSI Magneto-optical image (XM6 SCSI MO image - typically only
used with X68000)
iso: SCSI CD-ROM image (ISO 9660 image)
For example, if you want to specify an Apple-compatible HD image on ID
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 (default port is 6868) to al
low external management commands. If another process is using the
low external management commands. If another process is using the
rascsi port, RaSCSI will terminate, since it is likely another instance
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,
To quit RaSCSI, press Control + C. If it is running in the background,
you can kill it using an INT signal.
OPTIONS
-b BLOCK_SIZE
The optional block size. For SCSI drives 512, 1024, 2048 or 4096
bytes, default size is 512 bytes. For SASI drives 256 or 1024
bytes, default is 256 bytes.
The optional block size, either 512, 1024, 2048 or 4096 bytes.
Default size is 512 bytes.
-F FOLDER
The default folder for image files. For files in this folder no
@ -117,16 +112,7 @@ OPTIONS
For SCLP it is the print command to be used and a reservation
timeout in seconds, e.g. "cmd=lp -oraw %f:timeout=60".
-HDn[:u] FILE
n is the SASI ID number (0-15). The effective SASI ID is calcu
lated as n/2, the effective SASI LUN is calculated is the re
mainder of n/2. Alternatively the n:u syntax can be used, where
ns is the SASI ID (0-7) and u the LUN (0-1).
FILE is the name of the image file to use for the SASI device.
Note: SASI usage is rare, and is typically limited to early Unix
workstations and Sharp X68000 systems.
FILE is the name of the image file to use for the SCSI device.
EXAMPLES
Launch RaSCSI with no emulated drives attached:

View File

@ -136,7 +136,7 @@ Command is the operation being requested. Options are:
eject, protect and unprotect are idempotent.
.TP
.BR \-b\fI " " \fIBLOCK_SIZE
The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
The optional block size, either 512, 1024, 2048 or 4096 bytes. The default size is 512 bytes.
.TP
.BR \-f\fI " " \fIFILE|PARAM
Device-specific: Either a path to a disk image file, or a parameter for a non-disk device. See the rascsi(1) man page for permitted file types.

View File

@ -110,19 +110,18 @@ OPTIONS
eject, protect and unprotect are idempotent.
-b BLOCK_SIZE
The optional block size. For SCSI drives 512, 1024, 2048 or 4096
bytes, default size is 512 bytes. For SASI drives 256 or 1024
bytes, default is 256 bytes.
The optional block size, either 512, 1024, 2048 or 4096 bytes.
The default size is 512 bytes.
-f FILE|PARAM
Device-specific: Either a path to a disk image file, or a param
eter for a non-disk device. See the rascsi(1) man page for per
eter for a non-disk device. See the rascsi(1) man page for per
mitted file types.
-t TYPE
Specifies the device type. This type overrides the type derived
Specifies the device type. This type overrides the type derived
from the file extension of the specified image. See the
rascsi(1) man page for the available device types. For some
rascsi(1) man page for the available device types. For some
types there are shortcuts (only the first letter is required):
hd: SCSI hard disk drive
rm: SCSI removable media drive
@ -134,16 +133,16 @@ OPTIONS
services: Host services device
-n VENDOR:PRODUCT:REVISION
The vendor, product and revision for the device, to be returned
The vendor, product and revision for the device, to be returned
with the INQUIRY data. A complete set of name components must be
provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up
to 4 characters. Padding with blanks to the maxium length is au
tomatically applied. Once set the name of a device cannot be
tomatically applied. Once set the name of a device cannot be
changed.
-u UNIT
Unit number (0-31). This will default to 0. This option is only
used when there are multiple SCSI devices on a shared SCSI con
Unit number (0-31). This will default to 0. This option is only
used when there are multiple SCSI devices on a shared SCSI con
troller. (This is not common)
EXAMPLES

View File

@ -1,41 +0,0 @@
.TH sasidump 1
.SH NAME
sasidump \- SASI disk dumping tool for RaSCSI
.SH SYNOPSIS
.B sasidump
\fB\-i\fR \fIID\fR
[\fB\-u\fR \fIUT\fR]
[\fB\-b\fR \fIBSIZE\fR]
\fB\-c\fR \fICOUNT\fR
\fB\-f\fR \fIFILE\fR
[\fB\-r\fR]
.SH DESCRIPTION
.B sasidump
Samples the data on physical SASI storage media, and stores it to an image file. It can also restore from a dumped file onto physical SASI storage media.
.SH OPTIONS
.TP
.BR \-i\fI " "\fIID
SASI ID of the target device
.TP
.BR \-u\fI " "\fIUD
Unit ID of the target device
.TP
.BR \-b\fI " "\fIBSIZE
Block size (default is 512)
.TP
.BR \-c\fI " "\fICOUNT
Block count
.TP
.BR \-f\fI " "\fIFILE
Path to the dump file
.TP
.BR \-r\fI
Restoration mode
.SH EXAMPLES
.SH SEE ALSO
rasctl(1), rascsi(1), scsimon(1), rasdump(1)
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>

View File

@ -54,9 +54,7 @@ class RaCtlCmds:
scmo = []
sccd = []
for dtype in mappings:
if mappings[dtype] == proto.PbDeviceType.SAHD:
sahd.append(dtype)
elif mappings[dtype] == proto.PbDeviceType.SCHD:
if mappings[dtype] == proto.PbDeviceType.SCHD:
schd.append(dtype)
elif mappings[dtype] == proto.PbDeviceType.SCRM:
scrm.append(dtype)
@ -73,7 +71,6 @@ class RaCtlCmds:
"reserved_ids": reserved_ids,
"image_dir": image_dir,
"scan_depth": scan_depth,
"sahd": sahd,
"schd": schd,
"scrm": scrm,
"scmo": scmo,

View File

@ -543,9 +543,10 @@
{{ _("Create Empty Disk Image File") }}
</summary>
<ul>
<li>{{ _("The Generic image type is recommended for most computer platforms.") }}</li>
<li>{{ _("APPLE GENUINE (.hda) and NEC GENUINE (.hdn) image types will make RaSCSI behave as a particular drive type that are recognized by Mac and PC98 systems, respectively.") }}</li>
<li>{{ _("SASI images should only be used on the original Sharp X68000, or other legacy systems that utilize this pre-SCSI standard.") }}</li>
<li>{{ _("The Generic Hard Disk image type is recommended for most computer platforms.") }}</li>
<li>{{ _("APPLE GENUINE is appropriate for Apple Macintosh computers.") }}</li>
<li>{{ _("NEC GENUINE is appropriate for NEC PC-98 computers.") }}</li>
<li>{{ _("The Generic Removable Disk image type can be used with SCSI floppy drives, SyQuest drives, Zip drives etc.") }}</li>
</ul>
</details>
<table style="border: none">
@ -566,10 +567,7 @@
{{ _("SCSI Hard Disk image (NEC GENUINE) [.hdn]") }}
</option>
<option value="hdr">
{{ _("SCSI Removable Media Disk image (Generic) [.hdr]") }}
</option>
<option value="hdf">
{{ _("SASI Hard Disk image (Legacy) [.hdf]") }}
{{ _("SCSI Removable Disk image (Generic) [.hdr]") }}
</option>
</select>
<label for="size">{{ _("Size:") }}</label>

View File

@ -80,14 +80,12 @@ def get_device_name(device_type):
Takes a four letter device acronym (str) device_type.
Returns the human-readable name for the device type.
"""
if device_type == "SAHD":
return _("SASI Hard Disk")
if device_type == "SCHD":
return _("SCSI Hard Disk")
return _("Hard Disk")
if device_type == "SCRM":
return _("Removable Disk")
if device_type == "SCMO":
return _("Magneto-Optical")
return _("Magneto-Optical Disk")
if device_type == "SCCD":
return _("CD / DVD")
if device_type == "SCBR":

View File

@ -7,14 +7,10 @@
*.vcd
*.json
*.html
rascsi
scsimon
rasctl
sasidump
rasdump
scisparse
rascsi.dat
obj
bin
coverage
/rascsi_interface.pb.cpp
/rascsi_interface.pb.h
.project

View File

@ -1,5 +1,8 @@
.DEFAULT_GOAL: all
# Depending on the GCC version the compilation flags differ
GCCVERSION10 := $(shell expr `gcc -dumpversion` \>= 10)
## Optional build flags:
## CROSS_COMPILE : Specify which compiler toolchain to use.
## To cross compile set this accordingly, e.g. to:
@ -14,14 +17,12 @@ CXX = $(CROSS_COMPILE)g++
## this is only used by developers.
DEBUG ?= 0
ifeq ($(DEBUG), 1)
# Debug CFLAGS
CFLAGS += -O0 -g -Wall -DDEBUG
CXXFLAGS += -O0 -g -Wall -DDEBUG
# Debug compiler flags
CXXFLAGS += -O0 -g -Wall -Wextra -DDEBUG
BUILD_TYPE = Debug
else
# Release CFLAGS
CFLAGS += -O3 -Wall -Werror -DNDEBUG
CXXFLAGS += -O3 -Wall -Werror -DNDEBUG
# Release compiler flags, ignore unused parameters because they are reported for some assertions
CXXFLAGS += -O3 -Wall -Werror -Wextra -Wno-unused-parameter -DNDEBUG
BUILD_TYPE = Release
endif
ifeq ("$(shell uname -s)","Linux")
@ -29,23 +30,17 @@ ifeq ("$(shell uname -s)","Linux")
CXXFLAGS += -Wno-psabi
endif
CFLAGS += -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP
CXXFLAGS += -std=c++17 -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP
## EXTRA_FLAGS : Can be used to pass special purpose flags
CFLAGS += $(EXTRA_FLAGS)
CXXFLAGS += $(EXTRA_FLAGS)
# If we're using GCC version 10 or later, we need to add the FMT_HEADER_ONLY definition
GCCVERSION10 := $(shell expr `gcc -dumpversion` \>= 10)
ifeq "$(GCCVERSION10)" "1"
CFLAGS += -DFMT_HEADER_ONLY
CXXFLAGS += -DFMT_HEADER_ONLY
endif
## CONNECT_TYPE=FULLSPEC : Specify the type of RaSCSI board type
## that you are using. The typical options are
## STANDARD or FULLSPEC. The default is FULLSPEC
@ -55,14 +50,12 @@ endif
CONNECT_TYPE ?= FULLSPEC
ifdef CONNECT_TYPE
CFLAGS += -DCONNECT_TYPE_$(CONNECT_TYPE)
CXXFLAGS += -DCONNECT_TYPE_$(CONNECT_TYPE)
endif
RASCSI = rascsi
RASCTL = rasctl
RASDUMP = rasdump
SASIDUMP = sasidump
SCSIMON = scsimon
RASCSI_TEST = rascsi_test
@ -73,6 +66,8 @@ RSYSLOG_LOG = /var/log/rascsi.log
USR_LOCAL_BIN = /usr/local/bin
MAN_PAGE_DIR = /usr/local/man/man1
DOC_DIR = ../../doc
COVERAGE_DIR = ./coverage
COVERAGE_FILE = rascsi.dat
OS_FILES = ./os_integration
OBJDIR := ./obj/$(shell echo $(CONNECT_TYPE) | tr '[:upper:]' '[:lower:]')
@ -82,8 +77,7 @@ BIN_ALL = \
$(BINDIR)/$(RASCSI) \
$(BINDIR)/$(RASCTL) \
$(BINDIR)/$(SCSIMON) \
$(BINDIR)/$(RASDUMP) \
$(BINDIR)/$(SASIDUMP)
$(BINDIR)/$(RASDUMP)
SRC_PROTOC = \
rascsi_interface.proto
@ -91,7 +85,8 @@ SRC_PROTOC = \
SRC_PROTOBUF = \
rascsi_interface.pb.cpp
SRC_RASCSI_CORE = scsi.cpp \
SRC_RASCSI_CORE = \
scsi.cpp \
gpiobus.cpp \
filepath.cpp \
fileio.cpp \
@ -106,7 +101,6 @@ SRC_RASCSI_CORE += $(shell find ./devices -name '*.cpp')
SRC_RASCSI_CORE += $(SRC_PROTOBUF)
SRC_RASCSI = rascsi.cpp
SRC_RASCSI += $(SRC_RASCSI_CORE)
SRC_SCSIMON = \
scsimon.cpp \
@ -133,17 +127,8 @@ SRC_RASDUMP = \
fileio.cpp \
rascsi_version.cpp
SRC_SASIDUMP = \
sasidump.cpp \
scsi.cpp \
gpiobus.cpp \
filepath.cpp \
fileio.cpp \
rascsi_version.cpp
SRC_RASCSI_TEST = $(shell find ./test -name '*.cpp')
SRC_RASCSI_TEST += $(SRC_RASCSI_CORE)
vpath %.h ./ ./controllers ./devices ./monitor
@ -152,10 +137,10 @@ vpath %.o ./$(OBJDIR)
vpath ./$(BINDIR)
OBJ_RASCSI_CORE := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI_CORE:%.cpp=%.o)))
OBJ_RASCSI := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI:%.cpp=%.o)))
OBJ_RASCTL := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCTL:%.cpp=%.o)))
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_RASCSI_TEST := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI_TEST:%.cpp=%.o)))
@ -164,7 +149,7 @@ GEN_PROTOBUF := $(SRC_PROTOBUF) rascsi_interface.pb.h
# 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) $(OBJ_SCSIMON) $(OBJ_RASCSI_TEST))
ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_SCSIMON) $(OBJ_RASCSI_TEST))
-include $(ALL_DEPS)
$(OBJDIR) $(BINDIR):
@ -183,18 +168,26 @@ $(SRC_PROTOBUF): $(SRC_PROTOC)
## all : Rebuild all of the executable files and re-generate
## the text versions of the manpages
## docs : Re-generate the text versions of the man pages
## test : Build and run unit tests
## coverage : Build and run unit tests and create coverage HTML files.
## Note that you have to run 'make clean' before switching
## between coverage and no-coverage builds.
.DEFAULT_GOAL := all
.PHONY: all ALL docs
.PHONY: all ALL docs test coverage
all: $(BIN_ALL) docs
ALL: all
test: $(BINDIR)/$(RASCSI_TEST)
$(BINDIR)/$(RASCSI_TEST)
coverage: CXXFLAGS += --coverage
coverage: test
lcov -q -c -d . --include '*/raspberrypi/*' -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
$(BINDIR)/$(RASCSI): $(SRC_PROTOBUF) $(OBJ_RASCSI) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI) -lpthread -lpcap -lprotobuf -lstdc++fs
$(BINDIR)/$(RASCSI): $(SRC_PROTOBUF) $(OBJ_RASCSI_CORE) $(OBJ_RASCSI) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCSI) -lpthread -lpcap -lprotobuf -lstdc++fs
$(BINDIR)/$(RASCTL): $(SRC_PROTOBUF) $(OBJ_RASCTL) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) -lpthread -lprotobuf -lstdc++fs
@ -202,22 +195,18 @@ $(BINDIR)/$(RASCTL): $(SRC_PROTOBUF) $(OBJ_RASCTL) | $(BINDIR)
$(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP)
$(BINDIR)/$(SASIDUMP): $(OBJ_SASIDUMP) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_SASIDUMP)
$(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) -lpthread
$(BINDIR)/$(RASCSI_TEST): $(SRC_PROTOBUF) $(OBJ_RASCSI_TEST) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_TEST) -lpcap -lprotobuf -lgmock -lgtest -lgtest_main
$(BINDIR)/$(RASCSI_TEST): $(SRC_PROTOBUF) $(OBJ_RASCSI_CORE) $(OBJ_RASCSI_TEST) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCSI_TEST) -lpthread -lpcap -lprotobuf -lstdc++fs -lgmock -lgtest
# Phony rules for building individual utilities
.PHONY: $(RASCSI) $(RASCTL) $(RASDUMP) $(SASIDUMP) $(SCSIMON)
.PHONY: $(RASCSI) $(RASCTL) $(RASDUMP) $(SCSIMON)
$(RASCSI) : $(BINDIR)/$(RASCSI)
$(RASCTL) : $(BINDIR)/$(RASCTL)
$(RASDUMP) : $(BINDIR)/$(RASDUMP)
$(SASIDUMP): $(BINDIR)/$(SASIDUMP)
$(SCSIMON) : $(BINDIR)/$(SCSIMON)
@ -225,7 +214,7 @@ $(SCSIMON) : $(BINDIR)/$(SCSIMON)
## compiler files and executable files
.PHONY: clean
clean:
rm -rf $(OBJDIR) $(BINDIR) $(GEN_PROTOBUF)
rm -rf $(OBJDIR) $(BINDIR) $(GEN_PROTOBUF) $(COVERAGE_DIR) $(COVERAGE_FILE)
## install : Copies all of the man pages to the correct location
## Copies the binaries to a global install location
@ -245,12 +234,10 @@ install: \
$(MAN_PAGE_DIR)/rasctl.1 \
$(MAN_PAGE_DIR)/scsimon.1 \
$(MAN_PAGE_DIR)/rasdump.1 \
$(MAN_PAGE_DIR)/sasidump.1 \
$(USR_LOCAL_BIN)/$(RASCTL) \
$(USR_LOCAL_BIN)/$(RASCSI) \
$(USR_LOCAL_BIN)/$(SCSIMON) \
$(USR_LOCAL_BIN)/$(RASDUMP) \
$(USR_LOCAL_BIN)/$(SASIDUMP) \
$(SYSTEMD_CONF) \
$(RSYSLOG_CONF) \
$(RSYSLOG_LOG)

View File

@ -12,6 +12,6 @@
#include <string>
struct CommandContext {
int fd;
std::string locale;
int fd = -1;
std::string locale = "";
};

View File

@ -18,9 +18,8 @@
//
//---------------------------------------------------------------------------
#define USE_SEL_EVENT_ENABLE // Check SEL signal by event
#define REMOVE_FIXED_SASIHD_SIZE // remove the size limitation of SASIHD
// This avoids an indefinite loop with warnings if there is no RaSCSI hardware
// and thus helps with running certain tests on X86 hardware.
#if defined(__x86_64__) || defined(__X86__)
#if defined(__x86_64) || defined(__X86)
#undef USE_SEL_EVENT_ENABLE
#endif

View File

@ -0,0 +1,59 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "abstract_controller.h"
#include "devices/primary_device.h"
PrimaryDevice *AbstractController::GetDeviceForLun(int lun) const {
const auto& it = ctrl.luns.find(lun);
return it == ctrl.luns.end() ? nullptr : it->second;
}
bool AbstractController::AddDevice(PrimaryDevice *device)
{
if (HasDeviceForLun(device->GetLun())) {
return false;
}
ctrl.luns[device->GetLun()] = device;
device->SetController(this);
return true;
}
bool AbstractController::DeleteDevice(const PrimaryDevice *device)
{
return ctrl.luns.erase(device->GetLun()) == 1;
}
bool AbstractController::HasDeviceForLun(int lun) const
{
return ctrl.luns.find(lun) != ctrl.luns.end();
}
int AbstractController::ExtractInitiatorId(int id_data)
{
int initiator_id = -1;
int tmp = id_data - (1 << target_id);
if (tmp) {
initiator_id = 0;
for (int j = 0; j < 8; j++) {
tmp >>= 1;
if (tmp) {
initiator_id++;
}
else {
break;
}
}
}
return initiator_id;
}

View File

@ -0,0 +1,97 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
// Base class for device controllers
//
//---------------------------------------------------------------------------
#pragma once
#include "phase_handler.h"
#include <unordered_map>
using namespace std;
class PrimaryDevice;
class AbstractController : virtual public PhaseHandler
{
public:
// Maximum number of logical units
static const int LUN_MAX = 32;
enum rascsi_shutdown_mode {
NONE,
STOP_RASCSI,
STOP_PI,
RESTART_PI
};
// Internal data definition
// TODO Some of these data are probably device specific, and in this case they should be moved.
// These data are not internal, otherwise they could all be private
typedef struct _ctrl_t {
// General
BUS::phase_t phase = BUS::busfree; // Transition phase
// commands
DWORD cmd[16]; // Command data
DWORD status; // Status data
int message; // Message data
// Transfer
// TODO Try to get rid of the static buffer
BYTE *buffer; // Transfer data buffer
int bufsize; // Transfer data buffer size
uint32_t blocks; // Number of transfer blocks
uint64_t next; // Next record
uint32_t offset; // Transfer offset
uint32_t length; // Transfer remaining length
// Logical units of this device controller mapped to their LUN numbers
unordered_map<int, PrimaryDevice *> luns;
} ctrl_t;
AbstractController(BUS *bus, int target_id) : bus(bus), target_id(target_id) {}
virtual ~AbstractController() {}
virtual BUS::phase_t Process(int) = 0;
virtual void Error(scsi_defs::sense_key, scsi_defs::asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
scsi_defs::status = scsi_defs::status::CHECK_CONDITION) = 0;
virtual void Reset() = 0;
virtual int GetInitiatorId() const = 0;
virtual void SetByteTransfer(bool) = 0;
// Get requested LUN based on IDENTIFY message, with LUN from the CDB as fallback
virtual int GetEffectiveLun() const = 0;
virtual int GetMaxLuns() const = 0;
virtual void ScheduleShutdown(rascsi_shutdown_mode) = 0;
int GetTargetId() const { return target_id; }
PrimaryDevice *GetDeviceForLun(int) const;
bool AddDevice(PrimaryDevice *);
bool DeleteDevice(const PrimaryDevice *);
bool HasDeviceForLun(int) const;
int ExtractInitiatorId(int id_data);
// TODO Do not expose internal data
ctrl_t* GetCtrl() { return &ctrl; }
protected:
BUS *bus;
int target_id;
ctrl_t ctrl = {};
};

View File

@ -0,0 +1,87 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "devices/device_factory.h"
#include "devices/primary_device.h"
#include "devices/file_support.h"
#include "scsi_controller.h"
#include "controller_manager.h"
using namespace std;
unordered_map<int, AbstractController *> ControllerManager::controllers;
ControllerManager::~ControllerManager()
{
DeleteAllControllersAndDevices();
}
ControllerManager& ControllerManager::instance()
{
static ControllerManager instance;
return instance;
}
bool ControllerManager::CreateScsiController(BUS *bus, PrimaryDevice *device)
{
AbstractController *controller = FindController(device->GetId());
if (controller == nullptr) {
controller = new ScsiController(bus, device->GetId());
controllers[device->GetId()] = controller;
}
return controller->AddDevice(device);
}
AbstractController *ControllerManager::IdentifyController(int data) const
{
for (const auto& controller : controllers) {
if (data & (1 << controller.second->GetTargetId())) {
return controller.second;
}
}
return nullptr;
}
AbstractController *ControllerManager::FindController(int target_id) const
{
const auto& it = controllers.find(target_id);
return it == controllers.end() ? nullptr : it->second;
}
void ControllerManager::DeleteAllControllersAndDevices()
{
for (const auto& controller : controllers) {
delete controller.second;
}
controllers.clear();
DeviceFactory::instance().DeleteAllDevices();
FileSupport::UnreserveAll();
}
void ControllerManager::ResetAllControllers()
{
for (const auto& controller : controllers) {
controller.second->Reset();
}
}
PrimaryDevice *ControllerManager::GetDeviceByIdAndLun(int id, int lun) const
{
const AbstractController *controller = FindController(id);
if (controller != nullptr) {
return controller->GetDeviceForLun(lun);
}
return nullptr;
}

View File

@ -0,0 +1,39 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
// Keeps track of and manages the controllers
//
//---------------------------------------------------------------------------
#pragma once
#include <unordered_map>
class BUS;
class AbstractController;
class PrimaryDevice;
class ControllerManager
{
ControllerManager() {}
~ControllerManager();
public:
// Maximum number of controller devices
static const int DEVICE_MAX = 8;
static ControllerManager& instance();
bool CreateScsiController(BUS *, PrimaryDevice *);
AbstractController *IdentifyController(int) const;
AbstractController *FindController(int) const;
void DeleteAllControllersAndDevices();
void ResetAllControllers();
PrimaryDevice *GetDeviceByIdAndLun(int, int) const;
static std::unordered_map<int, AbstractController *> controllers;
};

View File

@ -0,0 +1,33 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
// An interface with methods for switching bus phases
//
//---------------------------------------------------------------------------
#pragma once
#include "scsi.h"
class PhaseHandler
{
public:
PhaseHandler() {}
virtual ~PhaseHandler() {}
virtual void SetPhase(BUS::phase_t) = 0;
virtual void BusFree() = 0;
virtual void Selection() = 0;
virtual void Command() = 0;
virtual void Status() = 0;
virtual void DataIn() = 0;
virtual void DataOut() = 0;
virtual void MsgIn() = 0;
virtual void MsgOut() = 0;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,171 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ SASI device controller ]
//
//---------------------------------------------------------------------------
#pragma once
#include "../config.h"
#include "os.h"
#include "scsi.h"
#include "fileio.h"
class PrimaryDevice;
//===========================================================================
//
// SASI Controller
//
//===========================================================================
class SASIDEV
{
protected:
private:
enum sasi_command : int {
eCmdTestUnitReady = 0x00,
eCmdRezero = 0x01,
eCmdRequestSense = 0x03,
eCmdFormat = 0x04,
eCmdReadCapacity = 0x05,
eCmdFormatLegacy = 0x06,
eCmdReassign = 0x07,
eCmdRead6 = 0x08,
eCmdWrite6 = 0x0A,
eCmdSeek6 = 0x0B,
eCmdSetMcastAddr = 0x0D, // DaynaPort specific command
eCmdInquiry = 0x12,
eCmdModeSelect6 = 0x15,
eCmdReserve6 = 0x16,
eCmdRelease6 = 0x17,
eCmdRead10 = 0x28,
eCmdWrite10 = 0x2A,
eCmdVerify10 = 0x2E,
eCmdVerify = 0x2F,
eCmdModeSelect10 = 0x55,
eCmdRead16 = 0x88,
eCmdWrite16 = 0x8A,
eCmdVerify16 = 0x8F,
eCmdWriteLong10 = 0x3F,
eCmdWriteLong16 = 0x9F,
eCmdInvalid = 0xC2,
eCmdSasiCmdAssign = 0x0E
};
public:
enum {
UnitMax = 32 // Maximum number of logical units
};
const int UNKNOWN_SCSI_ID = -1;
const int DEFAULT_BUFFER_SIZE = 0x1000;
// TODO Remove this duplicate
const int DAYNAPORT_BUFFER_SIZE = 0x1000000;
// For timing adjustments
enum {
min_exec_time_sasi = 100, // SASI BOOT/FORMAT 30:NG 35:OK
min_exec_time_scsi = 50
};
// Internal data definition
typedef struct {
// General
BUS::phase_t phase; // Transition phase
int m_scsi_id; // Controller ID (0-7)
BUS *bus; // Bus
// commands
DWORD cmd[16]; // Command data
DWORD status; // Status data
DWORD message; // Message data
// Run
DWORD execstart; // Execution start time
// Transfer
BYTE *buffer; // Transfer data buffer
int bufsize; // Transfer data buffer size
uint32_t blocks; // Number of transfer block
DWORD next; // Next record
DWORD offset; // Transfer offset
DWORD length; // Transfer remaining length
// Logical units
PrimaryDevice *unit[UnitMax];
// The current device
PrimaryDevice *device;
// The LUN from the IDENTIFY message
int lun;
} ctrl_t;
public:
// Basic Functions
SASIDEV();
virtual ~SASIDEV(); // Destructor
virtual void Reset(); // Device Reset
// External API
virtual BUS::phase_t Process(int); // Run
// Connect
void Connect(int id, BUS *sbus); // Controller connection
PrimaryDevice* GetUnit(int no); // Get logical unit
void SetUnit(int no, PrimaryDevice *dev); // Logical unit setting
bool HasUnit(); // Has a valid logical unit
// Other
BUS::phase_t GetPhase() {return ctrl.phase;} // Get the phase
int GetSCSIID() {return ctrl.m_scsi_id;} // Get the ID
ctrl_t* GetCtrl() { return &ctrl; } // Get the internal information address
virtual bool IsSASI() const { return true; } // SASI Check
virtual bool IsSCSI() const { return false; } // SCSI check
public:
void DataIn(); // Data in phase
void Status(); // Status phase
void MsgIn(); // Message in phase
void DataOut(); // Data out phase
virtual int GetEffectiveLun() const;
virtual void Error(scsi_defs::sense_key sense_key = scsi_defs::sense_key::NO_SENSE,
scsi_defs::asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
scsi_defs::status = scsi_defs::status::CHECK_CONDITION); // Common error handling
protected:
// Phase processing
virtual void BusFree(); // Bus free phase
virtual void Selection(); // Selection phase
virtual void Command(); // Command phase
virtual void Execute(); // Execution phase
// Commands
void CmdAssign(); // ASSIGN command
void CmdSpecify(); // SPECIFY command
// Data transfer
virtual void Send(); // Send data
virtual void Receive(); // Receive data
bool XferIn(BYTE* buf); // Data transfer IN
virtual bool XferOut(bool cont); // Data transfer OUT
// Special operations
void FlushUnit(); // Flush the logical unit
ctrl_t ctrl; // Internal data
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,135 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ SCSI device controller ]
//
//---------------------------------------------------------------------------
#pragma once
#include "abstract_controller.h"
#include "os.h"
#include "scsi.h"
class PrimaryDevice;
class ScsiController : public AbstractController
{
// For timing adjustments
static const unsigned int MIN_EXEC_TIME = 50;
// Transfer period factor (limited to 50 x 4 = 200ns)
static const int MAX_SYNC_PERIOD = 50;
// REQ/ACK offset(limited to 16)
static const int MAX_SYNC_OFFSET = 16;
static const int UNKNOWN_INITIATOR_ID = -1;
const int DEFAULT_BUFFER_SIZE = 0x1000;
enum rw_command : int {
eCmdRead6 = 0x08,
eCmdWrite6 = 0x0A,
eCmdSetMcastAddr = 0x0D, // DaynaPort specific command
eCmdModeSelect6 = 0x15,
eCmdRead10 = 0x28,
eCmdWrite10 = 0x2A,
eCmdVerify10 = 0x2E,
eCmdVerify = 0x2F,
eCmdModeSelect10 = 0x55,
eCmdRead16 = 0x88,
eCmdWrite16 = 0x8A,
eCmdVerify16 = 0x8F,
eCmdWriteLong10 = 0x3F,
eCmdWriteLong16 = 0x9F
};
typedef struct _scsi_t {
// Synchronous transfer
bool syncenable; // Synchronous transfer possible
int syncperiod = MAX_SYNC_PERIOD; // Synchronous transfer period
int syncoffset; // Synchronous transfer offset
int syncack; // Number of synchronous transfer ACKs
// ATN message
bool atnmsg;
int msc;
BYTE msb[256];
} scsi_t;
public:
ScsiController(BUS *, int);
~ScsiController();
void Reset() override;
BUS::phase_t Process(int) override;
int GetEffectiveLun() const override;
int GetMaxLuns() const override { return LUN_MAX; };
void Error(scsi_defs::sense_key sense_key, scsi_defs::asc asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
scsi_defs::status status = scsi_defs::status::CHECK_CONDITION) override;
int GetInitiatorId() const override { return initiator_id; }
void SetByteTransfer(bool is_byte_transfer) override { this->is_byte_transfer = is_byte_transfer; }
void Status() override;
void DataIn() override;
void DataOut() override;
private:
// Execution start time
DWORD execstart = 0;
// The initiator ID may be unavailable, e.g. with Atari ACSI and old host adapters
int initiator_id = UNKNOWN_INITIATOR_ID;
// The LUN from the IDENTIFY message
int identified_lun = -1;
bool is_byte_transfer = false;
uint32_t bytes_to_transfer = 0;
// Phases
void SetPhase(BUS::phase_t phase) override { ctrl.phase = phase; }
void BusFree() override;
void Selection() override;
void Command() override;
void MsgIn() override;
void MsgOut() override;
// Data transfer
void Send();
bool XferMsg(int);
bool XferIn(BYTE* buf);
bool XferOut(bool);
bool XferOutBlockOriented(bool);
void ReceiveBytes();
void Execute();
void FlushUnit();
void Receive();
void ScheduleShutdown(rascsi_shutdown_mode shutdown_mode) override { this->shutdown_mode = shutdown_mode; }
void Sleep();
scsi_t scsi = {};
rascsi_shutdown_mode shutdown_mode = NONE;
};

View File

@ -1,918 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ SCSI device controller ]
//
//---------------------------------------------------------------------------
#include "log.h"
#include "controllers/scsidev_ctrl.h"
#include "gpiobus.h"
#include "devices/scsi_daynaport.h"
#include "devices/scsi_printer.h"
using namespace scsi_defs;
//===========================================================================
//
// SCSI Device
//
//===========================================================================
SCSIDEV::SCSIDEV() : SASIDEV()
{
scsi.is_byte_transfer = false;
scsi.bytes_to_transfer = 0;
shutdown_mode = NONE;
// Synchronous transfer work initialization
scsi.syncenable = FALSE;
scsi.syncperiod = 50;
scsi.syncoffset = 0;
scsi.atnmsg = false;
scsi.msc = 0;
memset(scsi.msb, 0x00, sizeof(scsi.msb));
}
SCSIDEV::~SCSIDEV()
{
}
void SCSIDEV::Reset()
{
scsi.is_byte_transfer = false;
scsi.bytes_to_transfer = 0;
// Work initialization
scsi.atnmsg = false;
scsi.msc = 0;
memset(scsi.msb, 0x00, sizeof(scsi.msb));
super::Reset();
}
BUS::phase_t SCSIDEV::Process(int initiator_id)
{
// Do nothing if not connected
if (ctrl.m_scsi_id < 0 || ctrl.bus == NULL) {
return ctrl.phase;
}
// Get bus information
ctrl.bus->Aquire();
// Check to see if the reset signal was asserted
if (ctrl.bus->GetRST()) {
LOGWARN("RESET signal received!");
// Reset the controller
Reset();
// Reset the bus
ctrl.bus->Reset();
return ctrl.phase;
}
scsi.initiator_id = initiator_id;
// Phase processing
switch (ctrl.phase) {
// Bus free phase
case BUS::busfree:
BusFree();
break;
// Selection
case BUS::selection:
Selection();
break;
// Data out (MCI=000)
case BUS::dataout:
DataOut();
break;
// Data in (MCI=001)
case BUS::datain:
DataIn();
break;
// Command (MCI=010)
case BUS::command:
Command();
break;
// Status (MCI=011)
case BUS::status:
Status();
break;
// Message out (MCI=110)
case BUS::msgout:
MsgOut();
break;
// Message in (MCI=111)
case BUS::msgin:
MsgIn();
break;
default:
assert(false);
break;
}
return ctrl.phase;
}
//---------------------------------------------------------------------------
//
// Bus free phase
//
//---------------------------------------------------------------------------
void SCSIDEV::BusFree()
{
// Phase change
if (ctrl.phase != BUS::busfree) {
LOGTRACE("%s Bus free phase", __PRETTY_FUNCTION__);
// Phase setting
ctrl.phase = BUS::busfree;
// Set Signal lines
ctrl.bus->SetREQ(FALSE);
ctrl.bus->SetMSG(FALSE);
ctrl.bus->SetCD(FALSE);
ctrl.bus->SetIO(FALSE);
ctrl.bus->SetBSY(false);
// Initialize status and message
ctrl.status = 0x00;
ctrl.message = 0x00;
// Initialize ATN message reception status
scsi.atnmsg = false;
ctrl.lun = -1;
scsi.is_byte_transfer = false;
scsi.bytes_to_transfer = 0;
// When the bus is free RaSCSI or the Pi may be shut down
switch(shutdown_mode) {
case STOP_RASCSI:
LOGINFO("RaSCSI shutdown requested");
exit(0);
break;
case STOP_PI:
LOGINFO("Raspberry Pi shutdown requested");
if (system("init 0") == -1) {
LOGERROR("Raspberry Pi shutdown failed: %s", strerror(errno));
}
break;
case RESTART_PI:
LOGINFO("Raspberry Pi restart requested");
if (system("init 6") == -1) {
LOGERROR("Raspberry Pi restart failed: %s", strerror(errno));
}
break;
default:
break;
}
return;
}
// Move to selection phase
if (ctrl.bus->GetSEL() && !ctrl.bus->GetBSY()) {
Selection();
}
}
//---------------------------------------------------------------------------
//
// Selection Phase
//
//---------------------------------------------------------------------------
void SCSIDEV::Selection()
{
// Phase change
if (ctrl.phase != BUS::selection) {
// invalid if IDs do not match
int id = 1 << ctrl.m_scsi_id;
if ((ctrl.bus->GetDAT() & id) == 0) {
return;
}
// Return if there is no valid LUN
if (!HasUnit()) {
return;
}
LOGTRACE("%s Selection Phase ID=%d (with device)", __PRETTY_FUNCTION__, (int)ctrl.m_scsi_id);
if (scsi.initiator_id != UNKNOWN_SCSI_ID) {
LOGTRACE("%s Initiator ID is %d", __PRETTY_FUNCTION__, scsi.initiator_id);
}
else {
LOGTRACE("%s Initiator ID is unknown", __PRETTY_FUNCTION__);
}
// Phase setting
ctrl.phase = BUS::selection;
// Raise BSY and respond
ctrl.bus->SetBSY(true);
return;
}
// Selection completed
if (!ctrl.bus->GetSEL() && ctrl.bus->GetBSY()) {
// Message out phase if ATN=1, otherwise command phase
if (ctrl.bus->GetATN()) {
MsgOut();
} else {
Command();
}
}
}
//---------------------------------------------------------------------------
//
// Execution Phase
//
//---------------------------------------------------------------------------
void SCSIDEV::Execute()
{
LOGTRACE("%s Execution phase command $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]);
// Phase Setting
ctrl.phase = BUS::execute;
// Initialization for data transfer
ctrl.offset = 0;
ctrl.blocks = 1;
ctrl.execstart = SysTimer::GetTimerLow();
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE
if ((scsi_command)ctrl.cmd[0] != scsi_command::eCmdRequestSense) {
ctrl.status = 0;
}
LOGDEBUG("++++ CMD ++++ %s Executing command $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]);
int lun = GetEffectiveLun();
if (!ctrl.unit[lun]) {
if ((scsi_command)ctrl.cmd[0] != scsi_command::eCmdInquiry &&
(scsi_command)ctrl.cmd[0] != scsi_command::eCmdRequestSense) {
LOGDEBUG("Invalid LUN %d for ID %d", lun, GetSCSIID());
Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN);
return;
}
// Use LUN 0 for INQUIRY and REQUEST SENSE because LUN0 is assumed to be always available.
// INQUIRY and REQUEST SENSE have a special LUN handling of their own, required by the SCSI standard.
else {
assert(ctrl.unit[0]);
lun = 0;
}
}
ctrl.device = ctrl.unit[lun];
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE
if ((scsi_command)ctrl.cmd[0] != scsi_command::eCmdRequestSense) {
ctrl.device->SetStatusCode(0);
}
if (!ctrl.device->Dispatch(this)) {
LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetSCSIID(), lun, (BYTE)ctrl.cmd[0]);
Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
}
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
if ((scsi_command)ctrl.cmd[0] == scsi_command::eCmdInquiry && !ctrl.unit[lun]) {
lun = GetEffectiveLun();
LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, ctrl.device->GetId());
ctrl.buffer[0] = 0x7f;
}
}
//---------------------------------------------------------------------------
//
// Message out phase
//
//---------------------------------------------------------------------------
void SCSIDEV::MsgOut()
{
LOGTRACE("%s ID %d",__PRETTY_FUNCTION__, GetSCSIID());
// Phase change
if (ctrl.phase != BUS::msgout) {
LOGTRACE("Message Out Phase");
// process the IDENTIFY message
if (ctrl.phase == BUS::selection) {
scsi.atnmsg = true;
scsi.msc = 0;
memset(scsi.msb, 0x00, sizeof(scsi.msb));
}
// Phase Setting
ctrl.phase = BUS::msgout;
// Signal line operated by the target
ctrl.bus->SetMSG(TRUE);
ctrl.bus->SetCD(TRUE);
ctrl.bus->SetIO(FALSE);
// Data transfer is 1 byte x 1 block
ctrl.offset = 0;
ctrl.length = 1;
ctrl.blocks = 1;
return;
}
Receive();
}
//---------------------------------------------------------------------------
//
// Common Error Handling
//
//---------------------------------------------------------------------------
void SCSIDEV::Error(sense_key sense_key, asc asc, status status)
{
// Get bus information
ctrl.bus->Aquire();
// Reset check
if (ctrl.bus->GetRST()) {
// Reset the controller
Reset();
// Reset the bus
ctrl.bus->Reset();
return;
}
// Bus free for status phase and message in phase
if (ctrl.phase == BUS::status || ctrl.phase == BUS::msgin) {
BusFree();
return;
}
int lun = GetEffectiveLun();
if (!ctrl.unit[lun] || asc == INVALID_LUN) {
lun = 0;
assert(ctrl.unit[lun]);
}
if (sense_key || asc) {
// Set Sense Key and ASC for a subsequent REQUEST SENSE
ctrl.unit[lun]->SetStatusCode((sense_key << 16) | (asc << 8));
}
ctrl.status = status;
ctrl.message = 0x00;
LOGTRACE("%s Error (to status phase)", __PRETTY_FUNCTION__);
Status();
}
//---------------------------------------------------------------------------
//
// Send data
//
//---------------------------------------------------------------------------
void SCSIDEV::Send()
{
ASSERT(!ctrl.bus->GetREQ());
ASSERT(ctrl.bus->GetIO());
if (ctrl.length != 0) {
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Sending handhake with offset " + to_string(ctrl.offset) + ", length "
+ to_string(ctrl.length)).c_str());
// TODO The delay has to be taken from ctrl.unit[lun], but as there are no Daynaport drivers for
// LUNs other than 0 this work-around works.
int len = ctrl.bus->SendHandShake(&ctrl.buffer[ctrl.offset], ctrl.length, ctrl.unit[0] ? ctrl.unit[0]->GetSendDelay() : 0);
// If you cannot send all, move to status phase
if (len != (int)ctrl.length) {
Error();
return;
}
// offset and length
ctrl.offset += ctrl.length;
ctrl.length = 0;
return;
}
// Block subtraction, result initialization
ctrl.blocks--;
bool result = true;
// Processing after data collection (read/data-in only)
if (ctrl.phase == BUS::datain) {
if (ctrl.blocks != 0) {
// set next buffer (set offset, length)
result = XferIn(ctrl.buffer);
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Processing after data collection. Blocks: " + to_string(ctrl.blocks)).c_str());
}
}
// If result FALSE, move to status phase
if (!result) {
Error();
return;
}
// Continue sending if block !=0
if (ctrl.blocks != 0){
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Continuing to send. Blocks: " + to_string(ctrl.blocks)).c_str());
ASSERT(ctrl.length > 0);
ASSERT(ctrl.offset == 0);
return;
}
// Move to next phase
LOGTRACE("%s Move to next phase %s (%d)", __PRETTY_FUNCTION__, BUS::GetPhaseStrRaw(ctrl.phase), ctrl.phase);
switch (ctrl.phase) {
// Message in phase
case BUS::msgin:
// Completed sending response to extended message of IDENTIFY message
if (scsi.atnmsg) {
// flag off
scsi.atnmsg = false;
// command phase
Command();
} else {
// Bus free phase
BusFree();
}
break;
// Data-in Phase
case BUS::datain:
// status phase
Status();
break;
// status phase
case BUS::status:
// Message in phase
ctrl.length = 1;
ctrl.blocks = 1;
ctrl.buffer[0] = (BYTE)ctrl.message;
MsgIn();
break;
default:
assert(false);
break;
}
}
//---------------------------------------------------------------------------
//
// Receive Data
//
//---------------------------------------------------------------------------
void SCSIDEV::Receive()
{
if (scsi.is_byte_transfer) {
ReceiveBytes();
return;
}
int len;
BYTE data;
LOGTRACE("%s",__PRETTY_FUNCTION__);
// REQ is low
ASSERT(!ctrl.bus->GetREQ());
ASSERT(!ctrl.bus->GetIO());
// Length != 0 if received
if (ctrl.length != 0) {
LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, (int)ctrl.length);
// Receive
len = ctrl.bus->ReceiveHandShake(&ctrl.buffer[ctrl.offset], ctrl.length);
// If not able to receive all, move to status phase
if (len != (int)ctrl.length) {
LOGERROR("%s Not able to receive %d bytes of data, only received %d. Going to error",__PRETTY_FUNCTION__, (int)ctrl.length, len);
Error();
return;
}
// Offset and Length
ctrl.offset += ctrl.length;
ctrl.length = 0;
return;
}
// Block subtraction, result initialization
ctrl.blocks--;
bool result = true;
// Processing after receiving data (by phase)
LOGTRACE("%s ctrl.phase: %d (%s)",__PRETTY_FUNCTION__, (int)ctrl.phase, BUS::GetPhaseStrRaw(ctrl.phase));
switch (ctrl.phase) {
// Data out phase
case BUS::dataout:
if (ctrl.blocks == 0) {
// End with this buffer
result = XferOut(false);
} else {
// Continue to next buffer (set offset, length)
result = XferOut(true);
}
break;
// Message out phase
case BUS::msgout:
ctrl.message = ctrl.buffer[0];
if (!XferMsg(ctrl.message)) {
// Immediately free the bus if message output fails
BusFree();
return;
}
// Clear message data in preparation for message-in
ctrl.message = 0x00;
break;
default:
break;
}
// If result FALSE, move to status phase
if (!result) {
Error();
return;
}
// Continue to receive if block !=0
if (ctrl.blocks != 0){
ASSERT(ctrl.length > 0);
ASSERT(ctrl.offset == 0);
return;
}
// Move to next phase
switch (ctrl.phase) {
// Command phase
case BUS::command:
len = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]);
for (int i = 0; i < len; i++) {
ctrl.cmd[i] = ctrl.buffer[i];
LOGTRACE("%s Command Byte %d: $%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i]);
}
// Execution Phase
Execute();
break;
// Message out phase
case BUS::msgout:
// Continue message out phase as long as ATN keeps asserting
if (ctrl.bus->GetATN()) {
// Data transfer is 1 byte x 1 block
ctrl.offset = 0;
ctrl.length = 1;
ctrl.blocks = 1;
return;
}
// Parsing messages sent by ATN
if (scsi.atnmsg) {
int i = 0;
while (i < scsi.msc) {
// Message type
data = scsi.msb[i];
// ABORT
if (data == 0x06) {
LOGTRACE("Message code ABORT $%02X", data);
BusFree();
return;
}
// BUS DEVICE RESET
if (data == 0x0C) {
LOGTRACE("Message code BUS DEVICE RESET $%02X", data);
scsi.syncoffset = 0;
BusFree();
return;
}
// IDENTIFY
if (data >= 0x80) {
ctrl.lun = data & 0x1F;
LOGTRACE("Message code IDENTIFY $%02X, LUN %d selected", data, ctrl.lun);
}
// Extended Message
if (data == 0x01) {
LOGTRACE("Message code EXTENDED MESSAGE $%02X", data);
// Check only when synchronous transfer is possible
if (!scsi.syncenable || scsi.msb[i + 2] != 0x01) {
ctrl.length = 1;
ctrl.blocks = 1;
ctrl.buffer[0] = 0x07;
MsgIn();
return;
}
// Transfer period factor (limited to 50 x 4 = 200ns)
scsi.syncperiod = scsi.msb[i + 3];
if (scsi.syncperiod > 50) {
scsi.syncperiod = 50;
}
// REQ/ACK offset(limited to 16)
scsi.syncoffset = scsi.msb[i + 4];
if (scsi.syncoffset > 16) {
scsi.syncoffset = 16;
}
// STDR response message generation
ctrl.length = 5;
ctrl.blocks = 1;
ctrl.buffer[0] = 0x01;
ctrl.buffer[1] = 0x03;
ctrl.buffer[2] = 0x01;
ctrl.buffer[3] = (BYTE)scsi.syncperiod;
ctrl.buffer[4] = (BYTE)scsi.syncoffset;
MsgIn();
return;
}
// next
i++;
}
}
// Initialize ATN message reception status
scsi.atnmsg = false;
// Command phase
Command();
break;
// Data out phase
case BUS::dataout:
FlushUnit();
// status phase
Status();
break;
default:
assert(false);
break;
}
}
//---------------------------------------------------------------------------
//
// Transfer MSG
//
//---------------------------------------------------------------------------
bool SCSIDEV::XferMsg(int msg)
{
ASSERT(ctrl.phase == BUS::msgout);
// Save message out data
if (scsi.atnmsg) {
scsi.msb[scsi.msc] = (BYTE)msg;
scsi.msc++;
scsi.msc %= 256;
}
return true;
}
void SCSIDEV::ReceiveBytes()
{
uint32_t len;
BYTE data;
LOGTRACE("%s",__PRETTY_FUNCTION__);
// REQ is low
ASSERT(!ctrl.bus->GetREQ());
ASSERT(!ctrl.bus->GetIO());
if (ctrl.length) {
LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length);
len = ctrl.bus->ReceiveHandShake(&ctrl.buffer[ctrl.offset], ctrl.length);
// If not able to receive all, move to status phase
if (len != ctrl.length) {
LOGERROR("%s Not able to receive %d bytes of data, only received %d. Going to error",
__PRETTY_FUNCTION__, ctrl.length, len);
Error();
return;
}
ctrl.offset += ctrl.length;
scsi.bytes_to_transfer = ctrl.length;
ctrl.length = 0;
return;
}
// Result initialization
bool result = true;
// Processing after receiving data (by phase)
LOGTRACE("%s ctrl.phase: %d (%s)",__PRETTY_FUNCTION__, (int)ctrl.phase, BUS::GetPhaseStrRaw(ctrl.phase));
switch (ctrl.phase) {
case BUS::dataout:
result = XferOut(false);
break;
case BUS::msgout:
ctrl.message = ctrl.buffer[0];
if (!XferMsg(ctrl.message)) {
// Immediately free the bus if message output fails
BusFree();
return;
}
// Clear message data in preparation for message-in
ctrl.message = 0x00;
break;
default:
break;
}
// If result FALSE, move to status phase
if (!result) {
Error();
return;
}
// Move to next phase
switch (ctrl.phase) {
case BUS::command:
len = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]);
for (uint32_t i = 0; i < len; i++) {
ctrl.cmd[i] = ctrl.buffer[i];
LOGTRACE("%s Command Byte %d: $%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i]);
}
Execute();
break;
case BUS::msgout:
// Continue message out phase as long as ATN keeps asserting
if (ctrl.bus->GetATN()) {
// Data transfer is 1 byte x 1 block
ctrl.offset = 0;
ctrl.length = 1;
ctrl.blocks = 1;
return;
}
// Parsing messages sent by ATN
if (scsi.atnmsg) {
int i = 0;
while (i < scsi.msc) {
// Message type
data = scsi.msb[i];
// ABORT
if (data == 0x06) {
LOGTRACE("Message code ABORT $%02X", data);
BusFree();
return;
}
// BUS DEVICE RESET
if (data == 0x0C) {
LOGTRACE("Message code BUS DEVICE RESET $%02X", data);
scsi.syncoffset = 0;
BusFree();
return;
}
// IDENTIFY
if (data >= 0x80) {
ctrl.lun = data & 0x1F;
LOGTRACE("Message code IDENTIFY $%02X, LUN %d selected", data, ctrl.lun);
}
// Extended Message
if (data == 0x01) {
LOGTRACE("Message code EXTENDED MESSAGE $%02X", data);
// Check only when synchronous transfer is possible
if (!scsi.syncenable || scsi.msb[i + 2] != 0x01) {
ctrl.length = 1;
ctrl.blocks = 1;
ctrl.buffer[0] = 0x07;
MsgIn();
return;
}
// Transfer period factor (limited to 50 x 4 = 200ns)
scsi.syncperiod = scsi.msb[i + 3];
if (scsi.syncperiod > 50) {
scsi.syncoffset = 50;
}
// REQ/ACK offset(limited to 16)
scsi.syncoffset = scsi.msb[i + 4];
if (scsi.syncoffset > 16) {
scsi.syncoffset = 16;
}
// STDR response message generation
ctrl.length = 5;
ctrl.blocks = 1;
ctrl.buffer[0] = 0x01;
ctrl.buffer[1] = 0x03;
ctrl.buffer[2] = 0x01;
ctrl.buffer[3] = (BYTE)scsi.syncperiod;
ctrl.buffer[4] = (BYTE)scsi.syncoffset;
MsgIn();
return;
}
// next
i++;
}
}
// Initialize ATN message reception status
scsi.atnmsg = false;
Command();
break;
case BUS::dataout:
Status();
break;
default:
assert(false);
break;
}
}
bool SCSIDEV::XferOut(bool cont)
{
if (!scsi.is_byte_transfer) {
return super::XferOut(cont);
}
ASSERT(ctrl.phase == BUS::dataout);
scsi.is_byte_transfer = false;
PrimaryDevice *device = dynamic_cast<PrimaryDevice *>(ctrl.unit[GetEffectiveLun()]);
if (device && ctrl.cmd[0] == scsi_command::eCmdWrite6) {
return device->WriteBytes(ctrl.buffer, scsi.bytes_to_transfer);
}
LOGWARN("Received an unexpected command ($%02X) in %s", (WORD)ctrl.cmd[0] , __PRETTY_FUNCTION__)
return false;
}
int SCSIDEV::GetEffectiveLun() const
{
return ctrl.lun != -1 ? ctrl.lun : (ctrl.cmd[1] >> 5) & 0x07;
}

View File

@ -1,102 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ SCSI device controller ]
//
//---------------------------------------------------------------------------
#pragma once
#include "controllers/sasidev_ctrl.h"
//===========================================================================
//
// SCSI Device (Interits SASI device)
//
//===========================================================================
class SCSIDEV : public SASIDEV
{
public:
enum rascsi_shutdown_mode {
NONE,
STOP_RASCSI,
STOP_PI,
RESTART_PI
};
// Internal data definition
typedef struct {
// Synchronous transfer
BOOL syncenable; // Synchronous transfer possible
int syncperiod; // Synchronous transfer period
int syncoffset; // Synchronous transfer offset
int syncack; // Number of synchronous transfer ACKs
// ATN message
bool atnmsg;
int msc;
BYTE msb[256];
// -1 means that the initiator ID is unknown, e.g. with Atari ACSI and old host adapters
int initiator_id;
bool is_byte_transfer;
uint32_t bytes_to_transfer;
} scsi_t;
SCSIDEV();
~SCSIDEV();
void Reset() override;
BUS::phase_t Process(int) override;
void Receive() override;
// Get LUN based on IDENTIFY message, with LUN from the CDB as fallback
int GetEffectiveLun() const;
bool IsSASI() const override { return false; }
bool IsSCSI() const override { return true; }
// Common error handling
void Error(scsi_defs::sense_key sense_key = scsi_defs::sense_key::NO_SENSE,
scsi_defs::asc asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
scsi_defs::status status = scsi_defs::status::CHECK_CONDITION) override;
void ScheduleShutDown(rascsi_shutdown_mode shutdown_mode) { this->shutdown_mode = shutdown_mode; }
int GetInitiatorId() const { return scsi.initiator_id; }
bool IsByteTransfer() const { return scsi.is_byte_transfer; }
void SetByteTransfer(bool is_byte_transfer) { scsi.is_byte_transfer = is_byte_transfer; }
private:
typedef SASIDEV super;
// Phases
void BusFree() override;
void Selection() override;
void Execute() override;
void MsgOut();
// Data transfer
void Send() override;
bool XferMsg(int);
bool XferOut(bool);
void ReceiveBytes();
// Internal data
scsi_t scsi;
rascsi_shutdown_mode shutdown_mode;
};

View File

@ -267,7 +267,7 @@ void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag)
} else {
pClear = NULL;
}
if (((TCHAR)0x80 <= c && c <= (TCHAR)0x9F) || (TCHAR)0xE0 <= c) { // To be precise: 0x81~0x9F 0xE0~0xEF
if ((c <= (TCHAR)0x9F) || (TCHAR)0xE0 <= c) { // To be precise: 0x81~0x9F 0xE0~0xEF
p++;
if (*p == _T('\0'))
break;
@ -1550,7 +1550,6 @@ void CHostPath::Refresh()
m_cRing.InsertRing(&cRingBackup);
// Register file name
/// TODO: Process file duplication by ourselves rather than using the host API.
BOOL bUpdate = FALSE;
struct dirent **pd = NULL;
int nument = 0;
@ -2374,7 +2373,7 @@ void CHostFcb::SetHumanPath(const BYTE* szHumanPath)
/// Return FALSE if error is thrown.
//
//---------------------------------------------------------------------------
BOOL CHostFcb::Create(Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce)
BOOL CHostFcb::Create(DWORD nHumanAttribute, BOOL bForce)
{
ASSERT((nHumanAttribute & (Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) == 0);
ASSERT(strlen(m_szFilename) > 0);
@ -2534,7 +2533,7 @@ BOOL CHostFcb::TimeStamp(DWORD nHumanTime)
{
ASSERT(m_pFile || m_bFlag);
struct tm t = { 0 };
struct tm t = { };
t.tm_year = (nHumanTime >> 25) + 80;
t.tm_mon = ((nHumanTime >> 21) - 1) & 15;
t.tm_mday = (nHumanTime >> 16) & 31;
@ -3281,7 +3280,7 @@ int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamest
}
// Create file
if (pHostFcb->Create(pFcb, nHumanAttribute, bForce) == FALSE) {
if (pHostFcb->Create(nHumanAttribute, bForce) == FALSE) {
m_cFcb.Free(pHostFcb);
return FS_FILEEXIST;
}

View File

@ -674,7 +674,7 @@ public:
void SetHumanPath(const BYTE* szHumanPath); ///< Set Human68k path name
const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Get Human68k path name
BOOL Create(Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce); ///< Create file
BOOL Create(DWORD nHumanAttribute, BOOL bForce); ///< Create file
BOOL Open(); ///< Open file
BOOL Rewind(DWORD nOffset); ///< Seek file
DWORD Read(BYTE* pBuffer, DWORD nSize); ///< Read file

View File

@ -22,7 +22,7 @@
#include "ctapdriver.h"
#include "log.h"
#include "rasutil.h"
#include "exceptions.h"
#include "rascsi_exceptions.h"
#include <sstream>
#define BRIDGE_NAME "rascsi_bridge"
@ -30,14 +30,6 @@
using namespace std;
using namespace ras_util;
CTapDriver::CTapDriver()
{
m_hTAP = -1;
memset(&m_MacAddr, 0, sizeof(m_MacAddr));
m_pcap = NULL;
m_pcap_dumper = NULL;
}
//---------------------------------------------------------------------------
//
// Initialization
@ -345,10 +337,10 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
}
void CTapDriver::OpenDump(const Filepath& path) {
if (m_pcap == NULL) {
if (m_pcap == nullptr) {
m_pcap = pcap_open_dead(DLT_EN10MB, 65535);
}
if (m_pcap_dumper != NULL) {
if (m_pcap_dumper != nullptr) {
pcap_dump_close(m_pcap_dumper);
}
m_pcap_dumper = pcap_dump_open(m_pcap, path.GetPath());
@ -380,14 +372,14 @@ void CTapDriver::Cleanup()
m_hTAP = -1;
}
if (m_pcap_dumper != NULL) {
if (m_pcap_dumper != nullptr) {
pcap_dump_close(m_pcap_dumper);
m_pcap_dumper = NULL;
m_pcap_dumper = nullptr;
}
if (m_pcap != NULL) {
if (m_pcap != nullptr) {
pcap_close(m_pcap);
m_pcap = NULL;
m_pcap = nullptr;
}
}
@ -497,10 +489,11 @@ int CTapDriver::Rx(BYTE *buf)
dwReceived += 4;
}
if (m_pcap_dumper != NULL) {
if (m_pcap_dumper != nullptr) {
struct pcap_pkthdr h = {
.ts = {},
.caplen = dwReceived,
.len = dwReceived,
.len = dwReceived
};
gettimeofday(&h.ts, NULL);
pcap_dump((u_char*)m_pcap_dumper, &h, buf);
@ -520,8 +513,9 @@ int CTapDriver::Tx(const BYTE *buf, int len)
{
ASSERT(m_hTAP != -1);
if (m_pcap_dumper != NULL) {
if (m_pcap_dumper != nullptr) {
struct pcap_pkthdr h = {
.ts = {},
.caplen = (bpf_u_int32)len,
.len = (bpf_u_int32)len,
};

View File

@ -16,7 +16,7 @@
#include <pcap/pcap.h>
#include "filepath.h"
#include <unordered_map>
#include <vector>
#include <list>
#include <string>
#ifndef ETH_FRAME_LEN
@ -37,7 +37,7 @@ private:
friend class SCSIDaynaPort;
friend class SCSIBR;
CTapDriver();
CTapDriver() : interfaces({}), inet({}) {}
~CTapDriver() {}
bool Init(const unordered_map<string, string>&);
@ -56,16 +56,16 @@ public:
void Flush(); // Purge all of the packets that are waiting to be processed
private:
BYTE m_MacAddr[6]; // MAC Address
int m_hTAP; // File handle
BYTE m_MacAddr[6] = {}; // MAC Address
int m_hTAP = -1; // File handle
BYTE m_garbage_buffer[ETH_FRAME_LEN];
pcap_t *m_pcap;
pcap_dumper_t *m_pcap_dumper;
pcap_t *m_pcap = nullptr;
pcap_dumper_t *m_pcap_dumper = nullptr;
// Prioritized comma-separated list of interfaces to create the bridge for
vector<string> interfaces;
list<string> interfaces;
string inet;
};

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
@ -11,7 +11,7 @@
#include "rascsi_version.h"
#include "os.h"
#include "log.h"
#include "exceptions.h"
#include "rascsi_exceptions.h"
#include "device.h"
unordered_set<Device *> Device::devices;
@ -28,27 +28,6 @@ Device::Device(const string& type)
char rev[5];
sprintf(rev, "%02d%02d", rascsi_major_version, rascsi_minor_version);
revision = rev;
ready = false;
reset = false;
attn = false;
supported_luns = 32;
protectable = false;
write_protected = false;
read_only = false;
stoppable = false;
stopped = false;
removable = false;
removed = false;
lockable = false;
locked = false;
block_size_configurable = false;
supports_params = false;
id = 0;
lun = 0;
status_code = STATUS_NOERROR;
}
Device::~Device()
@ -121,14 +100,14 @@ const string Device::GetParam(const string& key)
return params.find(key) != params.end() ? params[key] : "";
}
void Device::SetParams(const unordered_map<string, string>& params)
void Device::SetParams(const unordered_map<string, string>& set_params)
{
this->params = GetDefaultParams();
params = default_params;
for (const auto& param : params) {
for (const auto& param : set_params) {
// It is assumed that there are default parameters for all supported parameters
if (this->params.find(param.first) != this->params.end()) {
this->params[param.first] = param.second;
if (params.find(param.first) != params.end()) {
params[param.first] = param.second;
}
else {
LOGWARN("%s", string("Ignored unknown parameter '" + param.first + "'").c_str());

View File

@ -3,12 +3,13 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include "scsi.h"
#include <unordered_set>
#include <unordered_map>
#include <string>
@ -17,79 +18,43 @@ using namespace std;
#define DEFAULT_VENDOR "RaSCSI"
//---------------------------------------------------------------------------
//
// Error definition (sense code returned by REQUEST SENSE)
//
// MSB Reserved (0x00)
// Sense Key
// Additional Sense Code (ASC)
// LSB Additional Sense Code Qualifier(ASCQ)
//
//---------------------------------------------------------------------------
#define STATUS_NOERROR 0x00000000 // NO ADDITIONAL SENSE INFO.
#define STATUS_DEVRESET 0x00062900 // POWER ON OR RESET OCCURED
#define STATUS_NOTREADY 0x00023a00 // MEDIUM NOT PRESENT
#define STATUS_ATTENTION 0x00062800 // MEDIUM MAY HAVE CHANGED
#define STATUS_PREVENT 0x00045302 // MEDIUM REMOVAL PREVENTED
#define STATUS_READFAULT 0x00031100 // UNRECOVERED READ ERROR
#define STATUS_WRITEFAULT 0x00030300 // PERIPHERAL DEVICE WRITE FAULT
#define STATUS_WRITEPROTECT 0x00042700 // WRITE PROTECTED
#define STATUS_MISCOMPARE 0x000e1d00 // MISCOMPARE DURING VERIFY
#define STATUS_INVALIDCMD 0x00052000 // INVALID COMMAND OPERATION CODE
#define STATUS_INVALIDLBA 0x00052100 // LOGICAL BLOCK ADDR. OUT OF RANGE
#define STATUS_INVALIDCDB 0x00052400 // INVALID FIELD IN CDB
#define STATUS_INVALIDLUN 0x00052500 // LOGICAL UNIT NOT SUPPORTED
#define STATUS_INVALIDPRM 0x00052600 // INVALID FIELD IN PARAMETER LIST
#define STATUS_INVALIDMSG 0x00054900 // INVALID MESSAGE ERROR
#define STATUS_PARAMLEN 0x00051a00 // PARAMETERS LIST LENGTH ERROR
#define STATUS_PARAMNOT 0x00052601 // PARAMETERS NOT SUPPORTED
#define STATUS_PARAMVALUE 0x00052602 // PARAMETERS VALUE INVALID
#define STATUS_PARAMSAVE 0x00053900 // SAVING PARAMETERS NOT SUPPORTED
#define STATUS_NODEFECT 0x00010000 // DEFECT LIST NOT FOUND
class SCSIDEV;
class Device
{
private:
friend class DeviceFactory;
string type;
bool ready;
bool reset;
bool attn;
// Number of supported luns
int supported_luns;
bool ready = false;
bool reset = false;
bool attn = false;
// Device is protectable/write-protected
bool protectable;
bool write_protected;
bool protectable = false;
bool write_protected = false;
// Device is permanently read-only
bool read_only;
bool read_only = false;
// Device can be stopped (parked)/is stopped (parked)
bool stoppable;
bool stopped;
bool stoppable = false;
bool stopped = false;
// Device is removable/removed
bool removable;
bool removed;
bool removable = false;
bool removed = false;
// Device is lockable/locked
bool lockable;
bool locked;
bool lockable = false;
bool locked = false;
// The block size is configurable
bool block_size_configurable;
bool block_size_configurable = false;
// Device can be created with parameters
bool supports_params;
bool supports_params = false;
// Device ID and LUN
int32_t id;
int32_t lun;
int32_t id = 0;
int32_t lun = 0;
// Device identifier (for INQUIRY)
string vendor;
@ -103,34 +68,41 @@ private:
unordered_map<string, string> default_params;
// Sense Key, ASC and ASCQ
int status_code;
// MSB Reserved (0x00)
// Sense Key
// Additional Sense Code (ASC)
// LSB Additional Sense Code Qualifier(ASCQ)
int status_code = scsi_defs::status::GOOD;
protected:
static unordered_set<Device *> devices;
void SetReady(bool ready) { this->ready = ready; }
bool IsReset() const { return reset; }
void SetReset(bool reset) { this->reset = reset; }
bool IsAttn() const { return attn; }
void SetAttn(bool attn) { this->attn = attn; }
public:
int GetStatusCode() const { return status_code; }
const string GetParam(const string&);
void SetParams(const unordered_map<string, string>&);
static unordered_set<Device *> devices;
Device(const string&);
virtual ~Device();
public:
// Override for device specific initializations, to be called after all device properties have been set
virtual bool Init(const unordered_map<string, string>&) { return true; };
virtual bool Dispatch(SCSIDEV *) = 0;
virtual bool Dispatch() = 0;
const string& GetType() const { return type; }
bool IsReady() const { return ready; }
void SetReady(bool ready) { this->ready = ready; }
bool IsReset() const { return reset; }
void SetReset(bool reset) { this->reset = reset; }
void Reset();
bool IsAttn() const { return attn; }
void SetAttn(bool attn) { this->attn = attn; }
int GetSupportedLuns() const { return supported_luns; }
void SetSupportedLuns(int supported_luns) { this->supported_luns = supported_luns; }
bool IsProtectable() const { return protectable; }
void SetProtectable(bool protectable) { this->protectable = protectable; }
@ -170,18 +142,12 @@ public:
virtual bool SupportsFile() const { return !supports_params; }
void SupportsParams(bool supports_paams) { this->supports_params = supports_paams; }
const unordered_map<string, string> GetParams() const { return params; }
const string GetParam(const string&);
void SetParams(const unordered_map<string, string>&);
const unordered_map<string, string> GetDefaultParams() const { return default_params; }
void SetDefaultParams(const unordered_map<string, string>& default_params) { this->default_params = default_params; }
int GetStatusCode() const { return status_code; }
void SetStatusCode(int);
bool Start();
void Stop();
virtual bool Eject(bool);
bool IsSASIHD() const { return type == "SAHD"; }
bool IsSCSIHD() const { return type == "SCHD" || type == "SCRM"; }
virtual void FlushCache() { }
};

View File

@ -3,11 +3,10 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "sasihd.h"
#include "scsihd.h"
#include "scsihd_nec.h"
#include "scsimo.h"
@ -15,7 +14,7 @@
#include "scsi_printer.h"
#include "scsi_host_bridge.h"
#include "scsi_daynaport.h"
#include "exceptions.h"
#include "rascsi_exceptions.h"
#include "device_factory.h"
#include <ifaddrs.h>
#include "host_services.h"
@ -23,9 +22,10 @@
using namespace std;
using namespace rascsi_interface;
DeviceFactory::DeviceFactory()
multimap<int, Device *> DeviceFactory::devices;
DeviceFactory::DeviceFactory() : sector_sizes({}), geometries({}), default_params({}), extension_mapping({})
{
sector_sizes[SAHD] = { 256, 512, 1024 };
sector_sizes[SCHD] = { 512, 1024, 2048, 4096 };
sector_sizes[SCRM] = { 512, 1024, 2048, 4096 };
sector_sizes[SCMO] = { 512, 1024, 2048, 4096 };
@ -55,7 +55,6 @@ DeviceFactory::DeviceFactory()
default_params[SCLP]["cmd"] = "lp -oraw %f";
default_params[SCLP]["timeout"] = "30";
extension_mapping["hdf"] = SAHD;
extension_mapping["hds"] = SCHD;
extension_mapping["hda"] = SCHD;
extension_mapping["hdn"] = SCHD;
@ -66,12 +65,62 @@ DeviceFactory::DeviceFactory()
extension_mapping["iso"] = SCCD;
}
DeviceFactory::~DeviceFactory()
{
DeleteAllDevices();
}
DeviceFactory& DeviceFactory::instance()
{
static DeviceFactory instance;
return instance;
}
void DeviceFactory::DeleteDevice(Device *device)
{
auto iterpair = devices.equal_range(device->GetId());
for (auto it = iterpair.first; it != iterpair.second; ++it) {
if (it->second->GetLun() == device->GetLun()) {
devices.erase(it);
delete device;
break;
}
}
}
void DeviceFactory::DeleteAllDevices()
{
for (const auto& device : devices) {
delete device.second;
}
devices.clear();
}
const Device * DeviceFactory::GetDeviceByIdAndLun(int id, int lun) const
{
for (const auto& device : devices) {
if (device.second->GetId() == id && device.second->GetLun() == lun) {
return device.second;
}
}
return nullptr;
}
const list<Device *> DeviceFactory::GetAllDevices() const
{
list<Device *> result;
for (const auto& device : devices) {
result.push_back(device.second);
}
return result;
}
string DeviceFactory::GetExtension(const string& filename) const
{
string ext;
@ -108,129 +157,117 @@ PbDeviceType DeviceFactory::GetTypeForFile(const string& filename) const
return UNDEFINED;
}
Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename)
// ID -1 is used by rascsi to create a temporary device
Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename, int id)
{
// If no type was specified try to derive the device type from the filename
if (type == UNDEFINED) {
type = GetTypeForFile(filename);
if (type == UNDEFINED) {
return NULL;
return nullptr;
}
}
Device *device = NULL;
try {
switch (type) {
case SAHD:
device = new SASIHD(sector_sizes[SAHD]);
device->SetSupportedLuns(2);
device->SetProduct("SASI HD");
break;
Device *device = nullptr;
switch (type) {
case SCHD: {
string ext = GetExtension(filename);
if (ext == "hdn" || ext == "hdi" || ext == "nhd") {
device = new SCSIHD_NEC({ 512 });
} else {
device = new SCSIHD(sector_sizes[SCHD], false);
case SCHD: {
string ext = GetExtension(filename);
if (ext == "hdn" || ext == "hdi" || ext == "nhd") {
device = new SCSIHD_NEC({ 512 });
} else {
device = new SCSIHD(sector_sizes[SCHD], false);
// Some Apple tools require a particular drive identification
if (ext == "hda") {
device->SetVendor("QUANTUM");
device->SetProduct("FIREBALL");
}
}
device->SetProtectable(true);
device->SetStoppable(true);
break;
// Some Apple tools require a particular drive identification
if (ext == "hda") {
device->SetVendor("QUANTUM");
device->SetProduct("FIREBALL");
}
case SCRM:
device = new SCSIHD(sector_sizes[SCRM], true);
device->SetProtectable(true);
device->SetStoppable(true);
device->SetRemovable(true);
device->SetLockable(true);
device->SetProduct("SCSI HD (REM.)");
break;
case SCMO:
device = new SCSIMO(sector_sizes[SCMO], geometries[SCMO]);
device->SetProtectable(true);
device->SetStoppable(true);
device->SetRemovable(true);
device->SetLockable(true);
device->SetProduct("SCSI MO");
break;
case SCCD:
device = new SCSICD(sector_sizes[SCCD]);
device->SetReadOnly(true);
device->SetStoppable(true);
device->SetRemovable(true);
device->SetLockable(true);
device->SetProduct("SCSI CD-ROM");
break;
case SCBR:
device = new SCSIBR();
device->SetProduct("SCSI HOST BRIDGE");
device->SupportsParams(true);
device->SetDefaultParams(default_params[SCBR]);
break;
case SCDP:
device = new SCSIDaynaPort();
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
device->SetVendor("Dayna");
device->SetProduct("SCSI/Link");
device->SetRevision("1.4a");
device->SupportsParams(true);
device->SetDefaultParams(default_params[SCDP]);
break;
case SCHS:
device = new HostServices();
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
device->SetVendor("RaSCSI");
device->SetProduct("Host Services");
break;
case SCLP:
device = new SCSIPrinter();
device->SetProduct("SCSI PRINTER");
device->SupportsParams(true);
device->SetDefaultParams(default_params[SCLP]);
break;
default:
break;
}
device->SetProtectable(true);
device->SetStoppable(true);
break;
}
catch(const illegal_argument_exception& e) {
// There was an internal problem with setting up the device data for INQUIRY
return NULL;
case SCRM:
device = new SCSIHD(sector_sizes[SCRM], true);
device->SetProtectable(true);
device->SetStoppable(true);
device->SetRemovable(true);
device->SetLockable(true);
device->SetProduct("SCSI HD (REM.)");
break;
case SCMO:
device = new SCSIMO(sector_sizes[SCMO], geometries[SCMO]);
device->SetProtectable(true);
device->SetStoppable(true);
device->SetRemovable(true);
device->SetLockable(true);
device->SetProduct("SCSI MO");
break;
case SCCD:
device = new SCSICD(sector_sizes[SCCD]);
device->SetReadOnly(true);
device->SetStoppable(true);
device->SetRemovable(true);
device->SetLockable(true);
device->SetProduct("SCSI CD-ROM");
break;
case SCBR:
device = new SCSIBR();
device->SetProduct("SCSI HOST BRIDGE");
device->SupportsParams(true);
device->SetDefaultParams(default_params[SCBR]);
break;
case SCDP:
device = new SCSIDaynaPort();
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
device->SetVendor("Dayna");
device->SetProduct("SCSI/Link");
device->SetRevision("1.4a");
device->SupportsParams(true);
device->SetDefaultParams(default_params[SCDP]);
break;
case SCHS:
device = new HostServices();
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
device->SetVendor("RaSCSI");
device->SetProduct("Host Services");
break;
case SCLP:
device = new SCSIPrinter();
device->SetProduct("SCSI PRINTER");
device->SupportsParams(true);
device->SetDefaultParams(default_params[SCLP]);
break;
default:
break;
}
assert(device != nullptr);
device->SetId(id);
devices.emplace(id, device);
return device;
}
const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(const string& type)
const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(const string& type) const
{
PbDeviceType t = UNDEFINED;
PbDeviceType_Parse(type, &t);
return sector_sizes[t];
}
const unordered_set<uint64_t> DeviceFactory::GetCapacities(PbDeviceType type) const
{
unordered_set<uint64_t> keys;
const auto it = sector_sizes.find(t);
assert (it != sector_sizes.end());
for (auto it = geometries.begin(); it != geometries.end(); ++it) {
keys.insert(it->first);
}
return keys;
return it->second;
}
const list<string> DeviceFactory::GetNetworkInterfaces() const
@ -251,16 +288,13 @@ const list<string> DeviceFactory::GetNetworkInterfaces() const
strcpy(ifr.ifr_name, tmp->ifa_name);
if (!ioctl(fd, SIOCGIFFLAGS, &ifr)) {
close(fd);
// Only list interfaces that are up
if (ifr.ifr_flags & IFF_UP) {
network_interfaces.push_back(tmp->ifa_name);
}
}
else {
close(fd);
}
close(fd);
}
tmp = tmp->ifa_next;

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
// The DeviceFactory singleton creates devices based on their type and the image file extension
//
@ -12,6 +12,7 @@
#pragma once
#include <list>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <string>
@ -26,24 +27,30 @@ class Device;
class DeviceFactory
{
friend class ControllerManager;
DeviceFactory();
~DeviceFactory() {}
~DeviceFactory();
public:
static DeviceFactory& instance();
Device *CreateDevice(PbDeviceType, const string&);
Device *CreateDevice(PbDeviceType, const string&, int);
void DeleteDevice(Device *);
const Device *GetDeviceByIdAndLun(int, int) const;
const list<Device *> GetAllDevices() const;
PbDeviceType GetTypeForFile(const string&) const;
const unordered_set<uint32_t>& GetSectorSizes(PbDeviceType type) { return sector_sizes[type]; }
const unordered_set<uint32_t>& GetSectorSizes(const string&);
const unordered_set<uint64_t> GetCapacities(PbDeviceType) const;
const unordered_set<uint32_t>& GetSectorSizes(const string&) const;
const unordered_map<string, string>& GetDefaultParams(PbDeviceType type) { return default_params[type]; }
const list<string> GetNetworkInterfaces() const;
const unordered_map<string, PbDeviceType> GetExtensionMapping() const { return extension_mapping; }
private:
void DeleteAllDevices();
unordered_map<PbDeviceType, unordered_set<uint32_t>> sector_sizes;
// Optional mapping of drive capacities to drive geometries
@ -54,4 +61,6 @@ private:
unordered_map<string, PbDeviceType> extension_mapping;
string GetExtension(const string&) const;
static std::multimap<int, Device *> devices;
};

View File

@ -15,23 +15,16 @@
//---------------------------------------------------------------------------
#include "os.h"
#include "fileio.h"
#include "device_factory.h"
#include "exceptions.h"
#include "rascsi_exceptions.h"
#include "disk.h"
#include "mode_page_device.h"
using namespace scsi_defs;
Disk::Disk(const string& id) : ModePageDevice(id), ScsiBlockCommands()
Disk::Disk(const string& id) : ModePageDevice(id), ScsiBlockCommands(), dispatcher({})
{
// Work initialization
configured_sector_size = 0;
disk.size = 0;
disk.blocks = 0;
disk.dcache = NULL;
disk.image_offset = 0;
disk.is_medium_changed = false;
dispatcher.AddCommand(eCmdRezero, "Rezero", &Disk::Rezero);
dispatcher.AddCommand(eCmdFormat, "FormatUnit", &Disk::FormatUnit);
dispatcher.AddCommand(eCmdReassign, "ReassignBlocks", &Disk::ReassignBlocks);
@ -46,9 +39,9 @@ Disk::Disk(const string& id) : ModePageDevice(id), ScsiBlockCommands()
dispatcher.AddCommand(eCmdReadCapacity10, "ReadCapacity10", &Disk::ReadCapacity10);
dispatcher.AddCommand(eCmdRead10, "Read10", &Disk::Read10);
dispatcher.AddCommand(eCmdWrite10, "Write10", &Disk::Write10);
dispatcher.AddCommand(eCmdReadLong10, "ReadLong10", &Disk::ReadLong10);
dispatcher.AddCommand(eCmdWriteLong10, "WriteLong10", &Disk::WriteLong10);
dispatcher.AddCommand(eCmdWriteLong16, "WriteLong16", &Disk::WriteLong16);
dispatcher.AddCommand(eCmdReadLong10, "ReadLong10", &Disk::ReadWriteLong10);
dispatcher.AddCommand(eCmdWriteLong10, "WriteLong10", &Disk::ReadWriteLong10);
dispatcher.AddCommand(eCmdWriteLong16, "WriteLong16", &Disk::ReadWriteLong16);
dispatcher.AddCommand(eCmdSeek10, "Seek10", &Disk::Seek10);
dispatcher.AddCommand(eCmdVerify10, "Verify10", &Disk::Verify10);
dispatcher.AddCommand(eCmdSynchronizeCache10, "SynchronizeCache10", &Disk::SynchronizeCache10);
@ -70,14 +63,10 @@ Disk::~Disk()
FlushCache();
}
// Clear disk cache
if (disk.dcache) {
delete disk.dcache;
disk.dcache = NULL;
}
delete disk.dcache;
}
bool Disk::Dispatch(SCSIDEV *controller)
bool Disk::Dispatch()
{
// Media changes must be reported on the next access, i.e. not only for TEST UNIT READY
if (disk.is_medium_changed) {
@ -85,12 +74,11 @@ bool Disk::Dispatch(SCSIDEV *controller)
disk.is_medium_changed = false;
controller->Error(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
return true;
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
}
// The superclass handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
return dispatcher.Dispatch(this, ctrl->cmd[0]) ? true : super::Dispatch();
}
//---------------------------------------------------------------------------
@ -133,229 +121,159 @@ void Disk::FlushCache()
}
}
void Disk::Rezero(SASIDEV *controller)
void Disk::Rezero()
{
if (!CheckReady()) {
controller->Error();
return;
}
CheckReady();
controller->Status();
EnterStatusPhase();
}
void Disk::FormatUnit(SASIDEV *controller)
void Disk::FormatUnit()
{
if (!Format(ctrl->cmd)) {
controller->Error();
return;
}
Format(ctrl->cmd);
controller->Status();
EnterStatusPhase();
}
void Disk::ReassignBlocks(SASIDEV *controller)
void Disk::ReassignBlocks()
{
if (!CheckReady()) {
controller->Error();
return;
}
CheckReady();
controller->Status();
EnterStatusPhase();
}
void Disk::Read(SASIDEV *controller, uint64_t record)
{
ctrl->length = Read(ctrl->cmd, ctrl->buffer, record);
LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, (int)ctrl->length);
if (ctrl->length <= 0) {
controller->Error();
return;
}
// Set next block
ctrl->next = record + 1;
controller->DataIn();
}
void Disk::Read6(SASIDEV *controller)
void Disk::Read(access_mode mode)
{
uint64_t start;
if (GetStartAndCount(controller, start, ctrl->blocks, RW6)) {
Read(controller, start);
if (CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
ctrl->length = Read(ctrl->cmd, ctrl->buffer, start);
LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, (int)ctrl->length);
// Set next block
ctrl->next = start + 1;
EnterDataInPhase();
}
}
void Disk::Read10(SASIDEV *controller)
void Disk::Read6()
{
uint64_t start;
if (GetStartAndCount(controller, start, ctrl->blocks, RW10)) {
Read(controller, start);
}
Read(RW6);
}
void Disk::Read16(SASIDEV *controller)
void Disk::Read10()
{
uint64_t start;
if (GetStartAndCount(controller, start, ctrl->blocks, RW16)) {
Read(controller, start);
}
Read(RW10);
}
void Disk::ReadWriteLong10(SASIDEV *controller)
void Disk::Read16()
{
Read(RW16);
}
void Disk::ReadWriteLong10()
{
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
if (ctrl->cmd[7] || ctrl->cmd[8]) {
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
return;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
if (ValidateBlockAddress(controller, RW10)) {
controller->Status();
}
ValidateBlockAddress(RW10);
EnterStatusPhase();
}
void Disk::ReadLong10(SASIDEV *controller)
{
ReadWriteLong10(controller);
}
void Disk::ReadWriteLong16(SASIDEV *controller)
void Disk::ReadWriteLong16()
{
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
if (ctrl->cmd[12] || ctrl->cmd[13]) {
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
return;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
if (ValidateBlockAddress(controller, RW16)) {
controller->Status();
}
ValidateBlockAddress(RW16);
EnterStatusPhase();
}
void Disk::ReadLong16(SASIDEV *controller)
{
ReadWriteLong16(controller);
}
void Disk::Write(SASIDEV *controller, uint64_t record)
{
ctrl->length = WriteCheck(record);
if (ctrl->length == 0) {
controller->Error(sense_key::NOT_READY, asc::NO_ADDITIONAL_SENSE_INFORMATION);
return;
}
else if (ctrl->length < 0) {
controller->Error(sense_key::ILLEGAL_REQUEST, asc::WRITE_PROTECTED);
return;
}
// Set next block
ctrl->next = record + 1;
controller->DataOut();
}
void Disk::Write6(SASIDEV *controller)
void Disk::Write(access_mode mode)
{
uint64_t start;
if (GetStartAndCount(controller, start, ctrl->blocks, RW6)) {
Write(controller, start);
if (CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
ctrl->length = WriteCheck(start);
// Set next block
ctrl->next = start + 1;
EnterDataOutPhase();
}
}
void Disk::Write10(SASIDEV *controller)
void Disk::Write6()
{
Write(RW6);
}
void Disk::Write10()
{
Write(RW10);
}
void Disk::Write16()
{
Write(RW16);
}
void Disk::Verify(access_mode mode)
{
uint64_t start;
if (GetStartAndCount(controller, start, ctrl->blocks, RW10)) {
Write(controller, start);
if (CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
// if BytChk=0
if ((ctrl->cmd[1] & 0x02) == 0) {
Seek();
return;
}
// Test reading
ctrl->length = Read(ctrl->cmd, ctrl->buffer, start);
// Set next block
ctrl->next = start + 1;
EnterDataOutPhase();
}
}
void Disk::Write16(SASIDEV *controller)
void Disk::Verify10()
{
uint64_t start;
if (GetStartAndCount(controller, start, ctrl->blocks, RW16)) {
Write(controller, start);
}
Verify(RW10);
}
void Disk::WriteLong10(SASIDEV *controller)
void Disk::Verify16()
{
ReadWriteLong10(controller);
Verify(RW16);
}
void Disk::WriteLong16(SASIDEV *controller)
{
ReadWriteLong16(controller);
}
void Disk::Verify(SASIDEV *controller, uint64_t record)
{
// if BytChk=0
if ((ctrl->cmd[1] & 0x02) == 0) {
Seek(controller);
return;
}
// Test loading
ctrl->length = Read(ctrl->cmd, ctrl->buffer, record);
if (ctrl->length <= 0) {
controller->Error();
return;
}
// Set next block
ctrl->next = record + 1;
controller->DataOut();
}
void Disk::Verify10(SASIDEV *controller)
{
// Get record number and block number
uint64_t record;
if (GetStartAndCount(controller, record, ctrl->blocks, RW10)) {
Verify(controller, record);
}
}
void Disk::Verify16(SASIDEV *controller)
{
// Get record number and block number
uint64_t record;
if (GetStartAndCount(controller, record, ctrl->blocks, RW16)) {
Verify(controller, record);
}
}
void Disk::StartStopUnit(SASIDEV *controller)
void Disk::StartStopUnit()
{
if (!StartStop(ctrl->cmd)) {
controller->Error();
return;
throw scsi_error_exception();
}
controller->Status();
EnterStatusPhase();
}
void Disk::SendDiagnostic(SASIDEV *controller)
void Disk::SendDiagnostic()
{
if (!SendDiag(ctrl->cmd)) {
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
return;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
controller->Status();
EnterStatusPhase();
}
void Disk::PreventAllowMediumRemoval(SASIDEV *controller)
void Disk::PreventAllowMediumRemoval()
{
if (!CheckReady()) {
controller->Error();
return;
}
CheckReady();
bool lock = ctrl->cmd[4] & 0x01;
@ -363,22 +281,22 @@ void Disk::PreventAllowMediumRemoval(SASIDEV *controller)
SetLocked(lock);
controller->Status();
EnterStatusPhase();
}
void Disk::SynchronizeCache10(SASIDEV *controller)
void Disk::SynchronizeCache10()
{
FlushCache();
controller->Status();
EnterStatusPhase();
}
void Disk::SynchronizeCache16(SASIDEV *controller)
void Disk::SynchronizeCache16()
{
return SynchronizeCache10(controller);
return SynchronizeCache10();
}
void Disk::ReadDefectData10(SASIDEV *controller)
void Disk::ReadDefectData10()
{
int allocation_length = (ctrl->cmd[7] << 8) | ctrl->cmd[8];
if (allocation_length > 4) {
@ -389,7 +307,7 @@ void Disk::ReadDefectData10(SASIDEV *controller)
memset(ctrl->buffer, 0, allocation_length);
ctrl->length = allocation_length;
controller->DataIn();
EnterDataInPhase();
}
void Disk::MediumChanged()
@ -407,9 +325,10 @@ bool Disk::Eject(bool force)
if (status) {
FlushCache();
delete disk.dcache;
disk.dcache = NULL;
disk.dcache = nullptr;
// The image file for this drive is not in use anymore
// TODO This cast and the FileSupport class can be removed as soon as only disk-like devices inherit from Disk
FileSupport *file_support = dynamic_cast<FileSupport *>(this);
if (file_support) {
file_support->UnreserveFile();
@ -419,10 +338,13 @@ bool Disk::Eject(bool force)
return status;
}
int Disk::ModeSense6(const DWORD *cdb, BYTE *buf)
int Disk::ModeSense6(const DWORD *cdb, BYTE *buf, int max_length)
{
// Get length, clear buffer
int length = (int)cdb[4];
if (length > max_length) {
length = max_length;
}
memset(buf, 0, length);
// Basic information
@ -453,15 +375,9 @@ int Disk::ModeSense6(const DWORD *cdb, BYTE *buf)
size = 12;
}
int pages_size = super::AddModePages(cdb, &buf[size], length - size);
if (!pages_size) {
return 0;
}
size += pages_size;
size += super::AddModePages(cdb, &buf[size], length - size);
if (size > 255) {
SetStatusCode(STATUS_INVALIDPRM);
return 0;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// Do not return more than ALLOCATION LENGTH bytes
@ -540,15 +456,9 @@ int Disk::ModeSense10(const DWORD *cdb, BYTE *buf, int max_length)
}
}
int pages_size = super::AddModePages(cdb, &buf[size], length - size);
if (!pages_size) {
return 0;
}
size += pages_size;
size += super::AddModePages(cdb, &buf[size], length - size);
if (size > 65535) {
SetStatusCode(STATUS_INVALIDPRM);
return 0;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// Do not return more than ALLOCATION LENGTH bytes
@ -618,10 +528,10 @@ void Disk::AddFormatPage(map<int, vector<BYTE>>& pages, bool changeable) const
}
if (IsReady()) {
// Set the number of tracks in one zone to 8 (TODO)
// Set the number of tracks in one zone to 8
buf[0x03] = 0x08;
// Set sector/track to 25 (TODO)
// Set sector/track to 25
buf[0x0a] = 0x00;
buf[0x0b] = 0x19;
@ -665,12 +575,12 @@ void Disk::AddDrivePage(map<int, vector<BYTE>>& pages, bool changeable) const
if (IsReady()) {
// Set the number of cylinders (total number of blocks
// divided by 25 sectors/track and 8 heads)
uint32_t cylinder = disk.blocks;
cylinder >>= 3;
cylinder /= 25;
buf[0x02] = (BYTE)(cylinder >> 16);
buf[0x03] = (BYTE)(cylinder >> 8);
buf[0x04] = (BYTE)cylinder;
uint64_t cylinders = disk.blocks;
cylinders >>= 3;
cylinders /= 25;
buf[0x02] = (BYTE)(cylinders >> 16);
buf[0x03] = (BYTE)(cylinders >> 8);
buf[0x04] = (BYTE)cylinders;
// Fix the head at 8
buf[0x05] = 0x8;
@ -716,135 +626,101 @@ void Disk::AddVendorPage(map<int, vector<BYTE>>&, int, bool) const
// Nothing to add by default
}
//---------------------------------------------------------------------------
//
// FORMAT UNIT
// *Opcode $06 for SASI, Opcode $04 for SCSI
//
//---------------------------------------------------------------------------
bool Disk::Format(const DWORD *cdb)
void Disk::Format(const DWORD *cdb)
{
if (!CheckReady()) {
return false;
}
CheckReady();
// FMTDATA=1 is not supported (but OK if there is no DEFECT LIST)
if ((cdb[1] & 0x10) != 0 && cdb[4] != 0) {
SetStatusCode(STATUS_INVALIDCDB);
return false;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// FORMAT Success
return true;
}
// TODO Read more than one block in a single call. Currently blocked by the SASI code (missing early range check)
// and the track-oriented cache.
int Disk::Read(const DWORD *cdb, BYTE *buf, uint64_t block)
// TODO Read more than one block in a single call. Currently blocked by the the track-oriented cache
int Disk::Read(const DWORD *, BYTE *buf, uint64_t block)
{
LOGTRACE("%s", __PRETTY_FUNCTION__);
if (!CheckReady()) {
return 0;
}
CheckReady();
// Error if the total number of blocks is exceeded
if (block >= disk.blocks) {
SetStatusCode(STATUS_INVALIDLBA);
return 0;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// leave it to the cache
if (!disk.dcache->ReadSector(buf, block)) {
SetStatusCode(STATUS_READFAULT);
return 0;
throw scsi_error_exception(sense_key::MEDIUM_ERROR, asc::READ_FAULT);
}
// Success
return 1 << disk.size;
}
int Disk::WriteCheck(DWORD block)
int Disk::WriteCheck(uint64_t block)
{
// Status check
if (!CheckReady()) {
LOGDEBUG("WriteCheck failed (not ready)");
return 0;
}
CheckReady();
// Error if the total number of blocks is exceeded
if (block >= disk.blocks) {
LOGDEBUG("WriteCheck failed (capacity exceeded)");
return 0;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// Error if write protected
if (IsProtected()) {
LOGDEBUG("WriteCheck failed (protected)");
return -1;
throw scsi_error_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
}
// Success
return 1 << disk.size;
}
// TODO Write more than one block in a single call. Currently blocked by the SASI code (missing early range check)
// and the track-oriented cache.
bool Disk::Write(const DWORD *cdb, const BYTE *buf, DWORD block)
// TODO Write more than one block in a single call. Currently blocked by the track-oriented cache
void Disk::Write(const DWORD *, BYTE *buf, uint64_t block)
{
LOGTRACE("%s", __PRETTY_FUNCTION__);
// Error if not ready
if (!IsReady()) {
SetStatusCode(STATUS_NOTREADY);
return false;
throw scsi_error_exception(sense_key::NOT_READY);
}
// Error if the total number of blocks is exceeded
if (block >= disk.blocks) {
SetStatusCode(STATUS_INVALIDLBA);
return false;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
// Error if write protected
if (IsProtected()) {
SetStatusCode(STATUS_WRITEPROTECT);
return false;
throw scsi_error_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
}
// Leave it to the cache
if (!disk.dcache->WriteSector(buf, block)) {
SetStatusCode(STATUS_WRITEFAULT);
return false;
throw scsi_error_exception(sense_key::MEDIUM_ERROR, asc::WRITE_FAULT);
}
return true;
}
void Disk::Seek(SASIDEV *controller)
void Disk::Seek()
{
if (!CheckReady()) {
controller->Error();
return;
}
CheckReady();
controller->Status();
EnterStatusPhase();
}
void Disk::Seek6(SASIDEV *controller)
{
// For SASI do not check LBA (SASI IOCS)
uint64_t start;
if (IsSASIHD() || GetStartAndCount(controller, start, ctrl->blocks, SEEK6)) {
Seek(controller);
}
}
void Disk::Seek10(SASIDEV *controller)
void Disk::Seek6()
{
uint64_t start;
if (GetStartAndCount(controller, start, ctrl->blocks, SEEK10)) {
Seek(controller);
if (CheckAndGetStartAndCount(start, ctrl->blocks, SEEK6)) {
Seek();
}
}
void Disk::Seek10()
{
uint64_t start;
if (CheckAndGetStartAndCount(start, ctrl->blocks, SEEK10)) {
Seek();
}
}
@ -869,8 +745,7 @@ bool Disk::StartStop(const DWORD *cdb)
if (load) {
if (IsLocked()) {
// Cannot be ejected because it is locked
SetStatusCode(STATUS_PREVENT);
return false;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
}
// Eject
@ -896,11 +771,12 @@ bool Disk::SendDiag(const DWORD *cdb) const
return true;
}
void Disk::ReadCapacity10(SASIDEV *controller)
void Disk::ReadCapacity10()
{
if (!CheckReady() || disk.blocks <= 0) {
controller->Error(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
return;
CheckReady();
if (disk.blocks <= 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
}
BYTE *buf = ctrl->buffer;
@ -922,14 +798,15 @@ void Disk::ReadCapacity10(SASIDEV *controller)
// the size
ctrl->length = 8;
controller->DataIn();
EnterDataInPhase();
}
void Disk::ReadCapacity16(SASIDEV *controller)
void Disk::ReadCapacity16()
{
if (!CheckReady() || disk.blocks <= 0) {
controller->Error(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
return;
CheckReady();
if (disk.blocks <= 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
}
BYTE *buf = ctrl->buffer;
@ -960,23 +837,23 @@ void Disk::ReadCapacity16(SASIDEV *controller)
// the size
ctrl->length = 14;
controller->DataIn();
EnterDataInPhase();
}
void Disk::ReadCapacity16_ReadLong16(SASIDEV *controller)
void Disk::ReadCapacity16_ReadLong16()
{
// The service action determines the actual command
switch (ctrl->cmd[1] & 0x1f) {
case 0x10:
ReadCapacity16(controller);
ReadCapacity16();
break;
case 0x11:
ReadLong16(controller);
ReadWriteLong16();
break;
default:
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
break;
}
}
@ -991,14 +868,14 @@ void Disk::ReadCapacity16_ReadLong16(SASIDEV *controller)
// just respond with an OK status.
//
//---------------------------------------------------------------------------
void Disk::Reserve(SASIDEV *controller)
void Disk::Reserve()
{
controller->Status();
EnterStatusPhase();
}
void Disk::Release(SASIDEV *controller)
void Disk::Release()
{
controller->Status();
EnterStatusPhase();
}
//---------------------------------------------------------------------------
@ -1007,7 +884,7 @@ void Disk::Release(SASIDEV *controller)
//
//---------------------------------------------------------------------------
bool Disk::ValidateBlockAddress(SASIDEV *controller, access_mode mode)
void Disk::ValidateBlockAddress(access_mode mode) const
{
uint64_t block = ctrl->cmd[2];
block <<= 8;
@ -1032,14 +909,11 @@ bool Disk::ValidateBlockAddress(SASIDEV *controller, access_mode mode)
if (block > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " blocks exceeded: Trying to access block "
+ to_string(block)).c_str());
controller->Error(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
return false;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
return true;
}
bool Disk::GetStartAndCount(SASIDEV *controller, uint64_t& start, uint32_t& count, access_mode mode)
bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mode mode)
{
if (mode == RW6 || mode == SEEK6) {
start = ctrl->cmd[1] & 0x1f;
@ -1099,14 +973,12 @@ bool Disk::GetStartAndCount(SASIDEV *controller, uint64_t& start, uint32_t& coun
if (start > capacity || start + count > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " blocks exceeded: Trying to access block "
+ to_string(start) + ", block count " + to_string(count)).c_str());
controller->Error(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
return false;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
// Do not process 0 blocks
if (!count && (mode != SEEK6 && mode != SEEK10)) {
LOGTRACE("NOT processing 0 blocks");
controller->Status();
EnterStatusPhase();
return false;
}
@ -1118,7 +990,7 @@ uint32_t Disk::GetSectorSizeInBytes() const
return disk.size ? 1 << disk.size : 0;
}
void Disk::SetSectorSizeInBytes(uint32_t size, bool sasi)
void Disk::SetSectorSizeInBytes(uint32_t size)
{
unordered_set<uint32_t> sector_sizes = DeviceFactory::instance().GetSectorSizes(GetType());
if (!sector_sizes.empty() && sector_sizes.find(size) == sector_sizes.end()) {
@ -1126,10 +998,6 @@ void Disk::SetSectorSizeInBytes(uint32_t size, bool sasi)
}
switch (size) {
case 256:
disk.size = 8;
break;
case 512:
disk.size = 9;
break;
@ -1191,29 +1059,12 @@ bool Disk::SetConfiguredSectorSize(uint32_t configured_sector_size)
return true;
}
void Disk::SetGeometries(const unordered_map<uint64_t, Geometry>& geometries)
{
this->geometries = geometries;
}
bool Disk::SetGeometryForCapacity(uint64_t capacity) {
const auto& geometry = geometries.find(capacity);
if (geometry != geometries.end()) {
SetSectorSizeInBytes(geometry->second.first, false);
SetBlockCount(geometry->second.second);
return true;
}
return false;
}
uint64_t Disk::GetBlockCount() const
{
return disk.blocks;
}
void Disk::SetBlockCount(uint32_t blocks)
void Disk::SetBlockCount(uint64_t blocks)
{
disk.blocks = blocks;
}

View File

@ -19,7 +19,6 @@
#include "log.h"
#include "scsi.h"
#include "controllers/scsidev_ctrl.h"
#include "device.h"
#include "device_factory.h"
#include "disk_track_cache.h"
@ -36,106 +35,94 @@ using namespace std;
class Disk : public ModePageDevice, ScsiBlockCommands
{
private:
enum access_mode { RW6, RW10, RW16, SEEK6, SEEK10 };
// The supported configurable block sizes, empty if not configurable
unordered_set<uint32_t> sector_sizes;
uint32_t configured_sector_size;
// The mapping of supported capacities to block sizes and block counts, empty if there is no capacity restriction
unordered_map<uint64_t, Geometry> geometries;
uint32_t configured_sector_size = 0;
typedef struct {
uint32_t size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096)
// TODO blocks should be a 64 bit value in order to support higher capacities
uint32_t blocks; // Total number of sectors
uint32_t size; // Sector Size (9=512, 10=1024, 11=2048, 12=4096)
uint64_t blocks; // Total number of sectors
DiskCache *dcache; // Disk cache
off_t image_offset; // Offset to actual data
bool is_medium_changed;
} disk_t;
Dispatcher<Disk, SASIDEV> dispatcher;
Dispatcher<Disk> dispatcher;
public:
Disk(const string&);
virtual ~Disk();
virtual bool Dispatch(SCSIDEV *) override;
virtual bool Dispatch() override;
void MediumChanged();
void ReserveFile(const string&);
// Media Operations
virtual void Open(const Filepath& path);
void GetPath(Filepath& path) const;
bool Eject(bool) override;
// Command helpers
virtual int WriteCheck(uint64_t);
virtual void Write(const DWORD *, BYTE *, uint64_t);
virtual int Read(const DWORD *, BYTE *, uint64_t);
uint32_t GetSectorSizeInBytes() const;
bool IsSectorSizeConfigurable() const;
bool SetConfiguredSectorSize(uint32_t);
uint64_t GetBlockCount() const;
void FlushCache() override;
private:
friend class SASIDEV;
typedef ModePageDevice super;
// Commands covered by the SCSI specification (see https://www.t10.org/drafts.htm)
void StartStopUnit(SASIDEV *);
void SendDiagnostic(SASIDEV *);
void PreventAllowMediumRemoval(SASIDEV *);
void SynchronizeCache10(SASIDEV *);
void SynchronizeCache16(SASIDEV *);
void ReadDefectData10(SASIDEV *);
virtual void Read6(SASIDEV *);
void Read10(SASIDEV *) override;
void Read16(SASIDEV *) override;
virtual void Write6(SASIDEV *);
void Write10(SASIDEV *) override;
void Write16(SASIDEV *) override;
void ReadLong10(SASIDEV *);
void ReadLong16(SASIDEV *);
void WriteLong10(SASIDEV *);
void WriteLong16(SASIDEV *);
void Verify10(SASIDEV *);
void Verify16(SASIDEV *);
void Seek(SASIDEV *);
void Seek10(SASIDEV *);
virtual void ReadCapacity10(SASIDEV *) override;
void ReadCapacity16(SASIDEV *) override;
void Reserve(SASIDEV *);
void Release(SASIDEV *);
// Commands covered by the SCSI specifications (see https://www.t10.org/drafts.htm)
void StartStopUnit();
void SendDiagnostic();
void PreventAllowMediumRemoval();
void SynchronizeCache10();
void SynchronizeCache16();
void ReadDefectData10();
virtual void Read6();
void Read10() override;
void Read16() override;
virtual void Write6();
void Write10() override;
void Write16() override;
void Verify10();
void Verify16();
void Seek();
void Seek10();
virtual void ReadCapacity10() override;
void ReadCapacity16() override;
void Reserve();
void Release();
void Rezero();
void FormatUnit() override;
void ReassignBlocks();
void Seek6();
void Read(access_mode);
void Write(access_mode);
void Verify(access_mode);
void ReadWriteLong10();
void ReadWriteLong16();
void ReadCapacity16_ReadLong16();
void Format(const DWORD *);
bool SendDiag(const DWORD *) const;
bool StartStop(const DWORD *);
public:
void ValidateBlockAddress(access_mode) const;
bool CheckAndGetStartAndCount(uint64_t&, uint32_t&, access_mode);
// Commands covered by the SCSI specification (see https://www.t10.org/drafts.htm)
void Rezero(SASIDEV *);
void FormatUnit(SASIDEV *) override;
void ReassignBlocks(SASIDEV *);
void Seek6(SASIDEV *);
// Command helpers
virtual int WriteCheck(DWORD block);
virtual bool Write(const DWORD *cdb, const BYTE *buf, DWORD block);
bool StartStop(const DWORD *cdb);
bool SendDiag(const DWORD *cdb) const;
virtual int Read(const DWORD *cdb, BYTE *buf, uint64_t block);
uint32_t GetSectorSizeInBytes() const;
void SetSectorSizeInBytes(uint32_t, bool);
uint32_t GetSectorSizeShiftCount() const;
void SetSectorSizeShiftCount(uint32_t);
bool IsSectorSizeConfigurable() const;
unordered_set<uint32_t> GetSectorSizes() const;
void SetSectorSizes(const unordered_set<uint32_t>&);
uint32_t GetConfiguredSectorSize() const;
bool SetConfiguredSectorSize(uint32_t);
void SetGeometries(const unordered_map<uint64_t, Geometry>&);
bool SetGeometryForCapacity(uint64_t);
uint64_t GetBlockCount() const;
void SetBlockCount(uint32_t);
void FlushCache();
int ModeSense6(const DWORD *, BYTE *, int) override;
int ModeSense10(const DWORD *, BYTE *, int) override;
protected:
int ModeSense6(const DWORD *cdb, BYTE *buf);
int ModeSense10(const DWORD *cdb, BYTE *buf, int);
virtual void Open(const Filepath&);
virtual void SetDeviceParameters(BYTE *);
void AddModePages(map<int, vector<BYTE>>&, int, bool) const override;
virtual void AddErrorPage(map<int, vector<BYTE>>&, bool) const;
@ -143,20 +130,14 @@ protected:
virtual void AddDrivePage(map<int, vector<BYTE>>&, bool) const;
void AddCachePage(map<int, vector<BYTE>>&, bool) const;
virtual void AddVendorPage(map<int, vector<BYTE>>&, int, bool) const;
unordered_set<uint32_t> GetSectorSizes() const;
void SetSectorSizes(const unordered_set<uint32_t>&);
void SetSectorSizeInBytes(uint32_t);
uint32_t GetSectorSizeShiftCount() const;
void SetSectorSizeShiftCount(uint32_t);
uint32_t GetConfiguredSectorSize() const;
void SetBlockCount(uint64_t);
// Internal disk data
disk_t disk;
private:
void Read(SASIDEV *, uint64_t);
void Write(SASIDEV *, uint64_t);
void Verify(SASIDEV *, uint64_t);
void ReadWriteLong10(SASIDEV *);
void ReadWriteLong16(SASIDEV *);
void ReadCapacity16_ReadLong16(SASIDEV *);
bool Format(const DWORD *cdb);
bool ValidateBlockAddress(SASIDEV *, access_mode);
bool GetStartAndCount(SASIDEV *, uint64_t&, uint32_t&, access_mode);
disk_t disk = {};
};

View File

@ -37,9 +37,9 @@ DiskTrack::DiskTrack()
dt.init = FALSE;
dt.changed = FALSE;
dt.length = 0;
dt.buffer = NULL;
dt.buffer = nullptr;
dt.maplen = 0;
dt.changemap = NULL;
dt.changemap = nullptr;
dt.imgoffset = 0;
}
@ -48,11 +48,11 @@ DiskTrack::~DiskTrack()
// Release memory, but do not save automatically
if (dt.buffer) {
free(dt.buffer);
dt.buffer = NULL;
dt.buffer = nullptr;
}
if (dt.changemap) {
free(dt.changemap);
dt.changemap = NULL;
dt.changemap = nullptr;
}
}
@ -106,7 +106,7 @@ bool DiskTrack::Load(const Filepath& path)
// Allocate buffer memory
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
if (dt.buffer == NULL) {
if (dt.buffer == nullptr) {
if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) {
LOGWARN("%s posix_memalign failed", __PRETTY_FUNCTION__);
}
@ -127,7 +127,7 @@ bool DiskTrack::Load(const Filepath& path)
}
// Reserve change map memory
if (dt.changemap == NULL) {
if (dt.changemap == nullptr) {
dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL));
dt.maplen = dt.sectors;
}
@ -348,7 +348,7 @@ DiskCache::DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgo
// Cache work
for (int i = 0; i < CacheMax; i++) {
cache[i].disktrk = NULL;
cache[i].disktrk = nullptr;
cache[i].serial = 0;
}
@ -416,7 +416,7 @@ void DiskCache::Clear()
for (int i = 0; i < CacheMax; i++) {
if (cache[i].disktrk) {
delete cache[i].disktrk;
cache[i].disktrk = NULL;
cache[i].disktrk = nullptr;
}
}
}
@ -493,7 +493,7 @@ DiskTrack* DiskCache::Assign(int track)
}
// Load failed
return NULL;
return nullptr;
}
}
@ -516,12 +516,12 @@ DiskTrack* DiskCache::Assign(int track)
// Save this track
if (!cache[c].disktrk->Save(sec_path)) {
return NULL;
return nullptr;
}
// Delete this track
DiskTrack *disktrk = cache[c].disktrk;
cache[c].disktrk = NULL;
cache[c].disktrk = nullptr;
if (Load(c, track, disktrk)) {
// Successful loading
@ -530,7 +530,7 @@ DiskTrack* DiskCache::Assign(int track)
}
// Load failed
return NULL;
return nullptr;
}
//---------------------------------------------------------------------------
@ -552,7 +552,7 @@ bool DiskCache::Load(int index, int track, DiskTrack *disktrk)
}
// Create a disk track
if (disktrk == NULL) {
if (disktrk == nullptr) {
disktrk = new DiskTrack();
}

View File

@ -15,18 +15,15 @@
#include "scsi.h"
#include <unordered_map>
class SASIDEV;
class SCSIDEV;
using namespace std;
using namespace scsi_defs;
template<class T, class U>
template<class T>
class Dispatcher
{
public:
Dispatcher() {}
Dispatcher() : commands({}) {}
~Dispatcher()
{
for (auto const& command : commands) {
@ -36,27 +33,24 @@ public:
typedef struct _command_t {
const char* name;
void (T::*execute)(U *);
void (T::*execute)();
_command_t(const char* _name, void (T::*_execute)(U *)) : name(_name), execute(_execute) { };
_command_t(const char* _name, void (T::*_execute)()) : name(_name), execute(_execute) { };
} command_t;
unordered_map<scsi_command, command_t*> commands;
void AddCommand(scsi_command opcode, const char* name, void (T::*execute)(U *))
void AddCommand(scsi_command opcode, const char* name, void (T::*execute)())
{
commands[opcode] = new command_t(name, execute);
}
bool Dispatch(T *instance, U *controller)
bool Dispatch(T *instance, DWORD cmd)
{
SASIDEV::ctrl_t *ctrl = controller->GetCtrl();
instance->SetCtrl(ctrl);
const auto& it = commands.find(static_cast<scsi_command>(ctrl->cmd[0]));
const auto& it = commands.find(static_cast<scsi_command>(cmd));
if (it != commands.end()) {
LOGDEBUG("%s Executing %s ($%02X)", __PRETTY_FUNCTION__, it->second->name, (unsigned int)ctrl->cmd[0]);
LOGDEBUG("%s Executing %s ($%02X)", __PRETTY_FUNCTION__, it->second->name, (uint32_t)cmd);
(instance->*it->second->execute)(controller);
(instance->*it->second->execute)();
return true;
}

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
@ -23,13 +23,13 @@ void FileSupport::UnreserveFile()
reserved_files.erase(diskpath.GetPath());
}
bool FileSupport::GetIdsForReservedFile(const Filepath& path, int& id, int& unit)
bool FileSupport::GetIdsForReservedFile(const Filepath& path, int& id, int& lun)
{
const auto& it = reserved_files.find(path.GetPath());
if (it != reserved_files.end()) {
const id_set ids = it->second;
id = ids.first;
unit = ids.second;
id = it->second.first;
lun = it->second.second;
return true;
}

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
// Devices inheriting from FileSupport support image files
//
@ -21,19 +21,23 @@ typedef pair<int, int> id_set;
class FileSupport
{
private:
friend class ControllerManager;
Filepath diskpath;
// The list of image files in use and the IDs and LUNs using these files
static unordered_map<string, id_set> reserved_files;
static void UnreserveAll();
public:
FileSupport() {}
FileSupport() : diskpath({}) {}
virtual ~FileSupport() {}
void GetPath(Filepath& path) const { path = diskpath; }
void SetPath(const Filepath& path) { diskpath = path; }
void ReserveFile(const Filepath&, int, int);
void UnreserveFile();
@ -41,7 +45,6 @@ public:
static void SetReservedFiles(const unordered_map<string, id_set>& files_in_use)
{ FileSupport::reserved_files = files_in_use; }
static bool GetIdsForReservedFile(const Filepath&, int&, int&);
static void UnreserveAll();
virtual void Open(const Filepath&) = 0;
};

View File

@ -33,8 +33,8 @@
// c) start && load (LOAD): Reboot the Raspberry Pi
//
#include "controllers/scsidev_ctrl.h"
#include "disk.h"
#include "rascsi_exceptions.h"
#include "device.h"
#include "host_services.h"
using namespace scsi_defs;
@ -45,24 +45,24 @@ HostServices::HostServices() : ModePageDevice("SCHS")
dispatcher.AddCommand(eCmdStartStop, "StartStopUnit", &HostServices::StartStopUnit);
}
bool HostServices::Dispatch(SCSIDEV *controller)
bool HostServices::Dispatch()
{
// The superclass class handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
return dispatcher.Dispatch(this, ctrl->cmd[0]) ? true : super::Dispatch();
}
void HostServices::TestUnitReady(SCSIDEV *controller)
void HostServices::TestUnitReady()
{
// Always successful
controller->Status();
EnterStatusPhase();
}
vector<BYTE> HostServices::Inquiry() const
vector<BYTE> HostServices::InquiryInternal() const
{
return PrimaryDevice::Inquiry(device_type::PROCESSOR, scsi_level::SPC_3, false);
return HandleInquiry(device_type::PROCESSOR, scsi_level::SPC_3, false);
}
void HostServices::StartStopUnit(SCSIDEV *controller)
void HostServices::StartStopUnit()
{
bool start = ctrl->cmd[4] & 0x01;
bool load = ctrl->cmd[4] & 0x02;
@ -70,52 +70,51 @@ void HostServices::StartStopUnit(SCSIDEV *controller)
if (!start) {
// Flush any caches
for (Device *device : devices) {
Disk *disk = dynamic_cast<Disk *>(device);
if (disk) {
disk->FlushCache();
}
device->FlushCache();
}
if (load) {
controller->ScheduleShutDown(SCSIDEV::rascsi_shutdown_mode::STOP_PI);
controller->ScheduleShutdown(ScsiController::rascsi_shutdown_mode::STOP_PI);
}
else {
controller->ScheduleShutDown(SCSIDEV::rascsi_shutdown_mode::STOP_RASCSI);
controller->ScheduleShutdown(ScsiController::rascsi_shutdown_mode::STOP_RASCSI);
}
controller->Status();
EnterStatusPhase();
return;
}
else {
if (load) {
controller->ScheduleShutDown(SCSIDEV::rascsi_shutdown_mode::RESTART_PI);
controller->ScheduleShutdown(ScsiController::rascsi_shutdown_mode::RESTART_PI);
controller->Status();
EnterStatusPhase();
return;
}
}
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
int HostServices::ModeSense6(const DWORD *cdb, BYTE *buf)
int HostServices::ModeSense6(const DWORD *cdb, BYTE *buf, int max_length)
{
// Block descriptors cannot be returned
if (!(cdb[1] & 0x08)) {
return 0;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
int length = (int)cdb[4];
if (length > max_length) {
length = max_length;
}
memset(buf, 0, length);
// Basic information
// Basic Information
int size = 4;
int pages_size = super::AddModePages(cdb, &buf[size], length - size);
if (!pages_size) {
return 0;
size += super::AddModePages(cdb, &buf[size], length - size);
if (size > 255) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
size += pages_size;
// Do not return more than ALLOCATION LENGTH bytes
if (size > length) {
@ -131,7 +130,7 @@ int HostServices::ModeSense10(const DWORD *cdb, BYTE *buf, int max_length)
{
// Block descriptors cannot be returned
if (!(cdb[1] & 0x08)) {
return 0;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
int length = (cdb[7] << 8) | cdb[8];
@ -140,14 +139,13 @@ int HostServices::ModeSense10(const DWORD *cdb, BYTE *buf, int max_length)
}
memset(buf, 0, length);
// Basic information
// Basic Information
int size = 8;
int pages_size = super::AddModePages(cdb, &buf[size], length - size);
if (!pages_size) {
return 0;
size += super::AddModePages(cdb, &buf[size], length - size);
if (size > 65535) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
size += pages_size;
// Do not return more than ALLOCATION LENGTH bytes
if (size > length) {

View File

@ -12,6 +12,7 @@
#include "mode_page_device.h"
#include <vector>
#include <map>
using namespace std;
@ -23,14 +24,11 @@ public:
HostServices();
~HostServices() {}
virtual bool Dispatch(SCSIDEV *) override;
virtual bool Dispatch() override;
vector<BYTE> Inquiry() const override;
void TestUnitReady(SCSIDEV *);
void StartStopUnit(SCSIDEV *);
int ModeSense6(const DWORD *, BYTE *);
int ModeSense10(const DWORD *, BYTE *, int);
vector<BYTE> InquiryInternal() const override;
void TestUnitReady() override;
void StartStopUnit();
bool SupportsFile() const override { return false; }
@ -42,7 +40,10 @@ private:
typedef ModePageDevice super;
Dispatcher<HostServices, SCSIDEV> dispatcher;
Dispatcher<HostServices> dispatcher;
int ModeSense6(const DWORD *, BYTE *, int) override;
int ModeSense10(const DWORD *, BYTE *, int) override;
void AddRealtimeClockPage(map<int, vector<BYTE>>&, bool) const;
};

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
// Interface for SCSI block commands (see https://www.t10.org/drafts.htm, SBC-5)
//
@ -13,8 +13,6 @@
#include "scsi_primary_commands.h"
class SASIDEV;
class ScsiBlockCommands : virtual public ScsiPrimaryCommands
{
public:
@ -23,11 +21,11 @@ public:
virtual ~ScsiBlockCommands() {}
// Mandatory commands
virtual void FormatUnit(SASIDEV *) = 0;
virtual void ReadCapacity10(SASIDEV *) = 0;
virtual void ReadCapacity16(SASIDEV *) = 0;
virtual void Read10(SASIDEV *) = 0;
virtual void Read16(SASIDEV *) = 0;
virtual void Write10(SASIDEV *) = 0;
virtual void Write16(SASIDEV *) = 0;
virtual void FormatUnit() = 0;
virtual void ReadCapacity10() = 0;
virtual void ReadCapacity16() = 0;
virtual void Read10() = 0;
virtual void Read16() = 0;
virtual void Write10() = 0;
virtual void Write16() = 0;
};

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
// Interface for SCSI Multi-Media commands (see https://www.t10.org/drafts.htm, MMC-6)
//
@ -13,8 +13,6 @@
#include "scsi_primary_commands.h"
class SASIDEV;
class ScsiMmcCommands : virtual public ScsiPrimaryCommands
{
public:
@ -22,6 +20,6 @@ public:
ScsiMmcCommands() {}
virtual ~ScsiMmcCommands() {}
virtual void ReadToc(SASIDEV *) = 0;
virtual void GetEventStatusNotification(SASIDEV *) = 0;
virtual void ReadToc() = 0;
virtual void GetEventStatusNotification() = 0;
};

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
// Interface for SCSI primary commands (see https://www.t10.org/drafts.htm, SPC-6)
//
@ -11,8 +11,6 @@
#pragma once
class SASIDEV;
class ScsiPrimaryCommands
{
public:
@ -21,10 +19,10 @@ public:
virtual ~ScsiPrimaryCommands() {}
// Mandatory commands
virtual void TestUnitReady(SASIDEV *) = 0;
virtual void Inquiry(SASIDEV *) = 0;
virtual void ReportLuns(SASIDEV *) = 0;
virtual void TestUnitReady() = 0;
virtual void Inquiry() = 0;
virtual void ReportLuns() = 0;
// Implemented for all RaSCSI device types
virtual void RequestSense(SASIDEV *) = 0;
virtual void RequestSense() = 0;
};

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
// Interface for SCSI printer commands (see SCSI-2 specification)
//
@ -13,8 +13,6 @@
#include "scsi_primary_commands.h"
class SCSIDEV;
class ScsiPrinterCommands : virtual public ScsiPrimaryCommands
{
public:
@ -23,8 +21,8 @@ public:
virtual ~ScsiPrinterCommands() {}
// Mandatory commands
virtual void Print(SCSIDEV *) = 0;
virtual void ReleaseUnit(SCSIDEV *) = 0;
virtual void ReserveUnit(SCSIDEV *) = 0;
virtual void SendDiagnostic(SCSIDEV *) = 0;
virtual void Print() = 0;
virtual void ReleaseUnit() = 0;
virtual void ReserveUnit() = 0;
virtual void SendDiagnostic() = 0;
};

View File

@ -10,13 +10,13 @@
//---------------------------------------------------------------------------
#include "log.h"
#include "controllers/scsidev_ctrl.h"
#include "rascsi_exceptions.h"
#include "mode_page_device.h"
using namespace std;
using namespace scsi_defs;
ModePageDevice::ModePageDevice(const string& id) : PrimaryDevice(id)
ModePageDevice::ModePageDevice(const string& id) : PrimaryDevice(id), dispatcher({})
{
dispatcher.AddCommand(eCmdModeSense6, "ModeSense6", &ModePageDevice::ModeSense6);
dispatcher.AddCommand(eCmdModeSense10, "ModeSense10", &ModePageDevice::ModeSense10);
@ -24,14 +24,18 @@ ModePageDevice::ModePageDevice(const string& id) : PrimaryDevice(id)
dispatcher.AddCommand(eCmdModeSelect10, "ModeSelect10", &ModePageDevice::ModeSelect10);
}
bool ModePageDevice::Dispatch(SCSIDEV *controller)
bool ModePageDevice::Dispatch()
{
// The superclass class handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
return dispatcher.Dispatch(this, ctrl->cmd[0]) ? true : super::Dispatch();
}
int ModePageDevice::AddModePages(const DWORD *cdb, BYTE *buf, int max_length)
{
if (max_length <= 0) {
return 0;
}
bool changeable = (cdb[2] & 0xc0) == 0x40;
// Get page code (0x3f means all pages)
@ -43,11 +47,9 @@ int ModePageDevice::AddModePages(const DWORD *cdb, BYTE *buf, int max_length)
map<int, vector<BYTE>> pages;
AddModePages(pages, page, changeable);
// If no mode data were added at all something must be wrong
if (pages.empty()) {
LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, page);
SetStatusCode(STATUS_INVALIDCDB);
return 0;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
// Holds all mode page data
@ -88,69 +90,45 @@ int ModePageDevice::AddModePages(const DWORD *cdb, BYTE *buf, int max_length)
return size;
}
void ModePageDevice::ModeSense6(SASIDEV *controller)
void ModePageDevice::ModeSense6()
{
ctrl->length = ModeSense6(ctrl->cmd, ctrl->buffer);
if (ctrl->length <= 0) {
controller->Error();
return;
}
ctrl->length = ModeSense6(ctrl->cmd, ctrl->buffer, ctrl->bufsize);
controller->DataIn();
EnterDataInPhase();
}
void ModePageDevice::ModeSense10(SASIDEV *controller)
void ModePageDevice::ModeSense10()
{
ctrl->length = ModeSense10(ctrl->cmd, ctrl->buffer, ctrl->bufsize);
if (ctrl->length <= 0) {
controller->Error();
return;
}
controller->DataIn();
EnterDataInPhase();
}
bool ModePageDevice::ModeSelect(const DWORD*, const BYTE *, int)
void ModePageDevice::ModeSelect(const DWORD*, const BYTE *, int)
{
// Cannot be set
SetStatusCode(STATUS_INVALIDPRM);
return false;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
}
void ModePageDevice::ModeSelect6(SASIDEV *controller)
void ModePageDevice::ModeSelect6()
{
LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, ctrl->buffer[0]);
ctrl->length = ModeSelectCheck6();
if (ctrl->length <= 0) {
controller->Error();
return;
}
controller->DataOut();
EnterDataOutPhase();
}
void ModePageDevice::ModeSelect10(SASIDEV *controller)
void ModePageDevice::ModeSelect10()
{
LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, ctrl->buffer[0]);
ctrl->length = ModeSelectCheck10();
if (ctrl->length <= 0) {
controller->Error();
return;
}
controller->DataOut();
EnterDataOutPhase();
}
int ModePageDevice::ModeSelectCheck(int length)
{
// Error if save parameters are set for other types than of SCHD or SCRM
// Error if save parameters are set for other types than of SCHD, SCRM or SCMO
// TODO The assumption above is not correct, and this code should be located elsewhere
if (!IsSCSIHD() && (ctrl->cmd[1] & 0x01)) {
SetStatusCode(STATUS_INVALIDCDB);
return 0;
if (GetType() != "SCHD" && GetType() != "SCRM" && GetType() != "SCMO" && (ctrl->cmd[1] & 0x01)) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
return length;

View File

@ -23,13 +23,9 @@ public:
ModePageDevice(const string&);
virtual ~ModePageDevice() {}
virtual bool Dispatch(SCSIDEV *) override;
virtual bool Dispatch() override;
virtual int ModeSense6(const DWORD *, BYTE *) = 0;
virtual int ModeSense10(const DWORD *, BYTE *, int) = 0;
// TODO This method should not be called by SASIDEV
virtual bool ModeSelect(const DWORD *, const BYTE *, int);
virtual void ModeSelect(const DWORD *, const BYTE *, int);
protected:
@ -40,12 +36,15 @@ private:
typedef PrimaryDevice super;
Dispatcher<ModePageDevice, SASIDEV> dispatcher;
Dispatcher<ModePageDevice> dispatcher;
void ModeSense6(SASIDEV *);
void ModeSense10(SASIDEV *);
void ModeSelect6(SASIDEV *);
void ModeSelect10(SASIDEV *);
virtual int ModeSense6(const DWORD *, BYTE *, int) = 0;
virtual int ModeSense10(const DWORD *, BYTE *, int) = 0;
void ModeSense6();
void ModeSense10();
void ModeSelect6();
void ModeSelect10();
int ModeSelectCheck(int);
int ModeSelectCheck6();

View File

@ -8,17 +8,15 @@
//---------------------------------------------------------------------------
#include "log.h"
#include "controllers/scsidev_ctrl.h"
#include "rascsi_exceptions.h"
#include "dispatcher.h"
#include "primary_device.h"
using namespace std;
using namespace scsi_defs;
PrimaryDevice::PrimaryDevice(const string& id) : ScsiPrimaryCommands(), Device(id)
PrimaryDevice::PrimaryDevice(const string& id) : ScsiPrimaryCommands(), Device(id), dispatcher({})
{
ctrl = NULL;
// Mandatory SCSI primary commands
dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady);
dispatcher.AddCommand(eCmdInquiry, "Inquiry", &PrimaryDevice::Inquiry);
@ -28,30 +26,32 @@ PrimaryDevice::PrimaryDevice(const string& id) : ScsiPrimaryCommands(), Device(i
dispatcher.AddCommand(eCmdRequestSense, "RequestSense", &PrimaryDevice::RequestSense);
}
bool PrimaryDevice::Dispatch(SCSIDEV *controller)
bool PrimaryDevice::Dispatch()
{
return dispatcher.Dispatch(this, controller);
return dispatcher.Dispatch(this, ctrl->cmd[0]);
}
void PrimaryDevice::TestUnitReady(SASIDEV *controller)
void PrimaryDevice::SetController(AbstractController *controller)
{
if (!CheckReady()) {
controller->Error();
return;
}
controller->Status();
this->controller = controller;
ctrl = controller->GetCtrl();
}
void PrimaryDevice::Inquiry(SASIDEV *controller)
void PrimaryDevice::TestUnitReady()
{
CheckReady();
EnterStatusPhase();
}
void PrimaryDevice::Inquiry()
{
// EVPD and page code check
if ((ctrl->cmd[1] & 0x01) || ctrl->cmd[2]) {
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
return;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
vector<BYTE> buf = Inquiry();
vector<BYTE> buf = InquiryInternal();
size_t allocation_length = ctrl->cmd[4] + (ctrl->cmd[3] << 8);
if (allocation_length > buf.size()) {
@ -64,63 +64,68 @@ void PrimaryDevice::Inquiry(SASIDEV *controller)
int lun = controller->GetEffectiveLun();
// Report if the device does not support the requested LUN
if (!ctrl->unit[lun]) {
LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, ctrl->device->GetId());
if (!controller->HasDeviceForLun(lun)) {
LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, GetId());
// Signal that the requested LUN does not exist
ctrl->buffer[0] |= 0x7f;
}
controller->DataIn();
EnterDataInPhase();
}
void PrimaryDevice::ReportLuns(SASIDEV *controller)
void PrimaryDevice::ReportLuns()
{
int allocation_length = (ctrl->cmd[6] << 24) + (ctrl->cmd[7] << 16) + (ctrl->cmd[8] << 8) + ctrl->cmd[9];
BYTE *buf = ctrl->buffer;
memset(buf, 0, allocation_length);
memset(buf, 0, allocation_length < ctrl->bufsize ? allocation_length : ctrl->bufsize);
int size = 0;
// Only SELECT REPORT mode 0 is supported
if (!ctrl->cmd[2]) {
for (int lun = 0; lun < controller->GetCtrl()->device->GetSupportedLuns(); lun++) {
if (controller->GetCtrl()->unit[lun]) {
size += 8;
buf[size + 7] = lun;
}
}
buf[2] = size >> 8;
buf[3] = size;
if (ctrl->cmd[2]) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
for (int lun = 0; lun < controller->GetMaxLuns(); lun++) {
if (controller->HasDeviceForLun(lun)) {
size += 8;
buf[size + 7] = lun;
}
}
buf[2] = size >> 8;
buf[3] = size;
size += 8;
ctrl->length = allocation_length < size ? allocation_length : size;
controller->DataIn();
EnterDataInPhase();
}
void PrimaryDevice::RequestSense(SASIDEV *controller)
void PrimaryDevice::RequestSense()
{
int lun = controller->GetEffectiveLun();
// Note: According to the SCSI specs the LUN handling for REQUEST SENSE non-existing LUNs do *not* result
// in CHECK CONDITION. Only the Sense Key and ASC are set in order to signal the non-existing LUN.
if (!ctrl->unit[lun]) {
if (!controller->HasDeviceForLun(lun)) {
// LUN 0 can be assumed to be present (required to call RequestSense() below)
assert(controller->HasDeviceForLun(0));
lun = 0;
// Do not raise an exception here because the rest of the code must be executed
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN);
ctrl->status = 0x00;
}
vector<BYTE> buf = controller->GetDeviceForLun(lun)->HandleRequestSense();
size_t allocation_length = ctrl->cmd[4];
vector<BYTE> buf = ((PrimaryDevice *)ctrl->unit[lun])->RequestSense(allocation_length);
if (allocation_length > buf.size()) {
allocation_length = buf.size();
}
@ -128,43 +133,38 @@ void PrimaryDevice::RequestSense(SASIDEV *controller)
memcpy(ctrl->buffer, buf.data(), allocation_length);
ctrl->length = allocation_length;
controller->DataIn();
EnterDataInPhase();
}
bool PrimaryDevice::CheckReady()
void PrimaryDevice::CheckReady()
{
// Not ready if reset
if (IsReset()) {
SetStatusCode(STATUS_DEVRESET);
SetReset(false);
LOGTRACE("%s Device in reset", __PRETTY_FUNCTION__);
return false;
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::POWER_ON_OR_RESET);
}
// Not ready if it needs attention
if (IsAttn()) {
SetStatusCode(STATUS_ATTENTION);
SetAttn(false);
LOGTRACE("%s Device in needs attention", __PRETTY_FUNCTION__);
return false;
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
}
// Return status if not ready
if (!IsReady()) {
SetStatusCode(STATUS_NOTREADY);
LOGTRACE("%s Device not ready", __PRETTY_FUNCTION__);
return false;
throw scsi_error_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT);
}
// Initialization with no error
LOGTRACE("%s Device is ready", __PRETTY_FUNCTION__);
return true;
}
vector<BYTE> PrimaryDevice::Inquiry(device_type type, scsi_level level, bool is_removable) const
vector<BYTE> PrimaryDevice::HandleInquiry(device_type type, scsi_level level, bool is_removable) const
{
vector<BYTE> buf = vector<BYTE>(0x1F + 5);
vector<BYTE> buf(0x1F + 5);
// Basic data
// buf[0] ... SCSI device type
@ -184,11 +184,11 @@ vector<BYTE> PrimaryDevice::Inquiry(device_type type, scsi_level level, bool is_
return buf;
}
vector<BYTE> PrimaryDevice::RequestSense(int)
vector<BYTE> PrimaryDevice::HandleRequestSense()
{
// Return not ready only if there are no errors
if (GetStatusCode() == STATUS_NOERROR && !IsReady()) {
SetStatusCode(STATUS_NOTREADY);
if (!GetStatusCode() && !IsReady()) {
throw scsi_error_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT);
}
// Set 18 bytes including extended sense data
@ -208,7 +208,7 @@ vector<BYTE> PrimaryDevice::RequestSense(int)
return buf;
}
bool PrimaryDevice::WriteBytes(BYTE *buf, uint32_t length)
bool PrimaryDevice::WriteByteSequence(BYTE *, uint32_t)
{
LOGERROR("%s Writing bytes is not supported by this device", __PRETTY_FUNCTION__);

View File

@ -11,8 +11,8 @@
#pragma once
#include "controllers/scsidev_ctrl.h"
#include "interfaces/scsi_primary_commands.h"
#include "controllers/scsi_controller.h"
#include "device.h"
#include "dispatcher.h"
#include <string>
@ -26,29 +26,33 @@ public:
PrimaryDevice(const string&);
virtual ~PrimaryDevice() {}
virtual bool Dispatch(SCSIDEV *);
virtual bool Dispatch();
void TestUnitReady(SASIDEV *);
void RequestSense(SASIDEV *);
virtual void Inquiry(SASIDEV *);
void SetCtrl(SASIDEV::ctrl_t *ctrl) { this->ctrl = ctrl; }
bool CheckReady();
virtual vector<BYTE> Inquiry() const = 0;
virtual vector<BYTE> RequestSense(int);
virtual bool WriteBytes(BYTE *, uint32_t);
void SetController(AbstractController *);
virtual bool WriteByteSequence(BYTE *, uint32_t);
virtual int GetSendDelay() const { return BUS::SEND_NO_DELAY; }
protected:
vector<BYTE> Inquiry(scsi_defs::device_type, scsi_level, bool) const;
vector<BYTE> HandleInquiry(scsi_defs::device_type, scsi_level, bool) const;
virtual vector<BYTE> InquiryInternal() const = 0;
void CheckReady();
SASIDEV::ctrl_t *ctrl;
void EnterStatusPhase() { controller->Status(); }
void EnterDataInPhase() { controller->DataIn(); }
void EnterDataOutPhase() { controller->DataOut(); }
AbstractController *controller = nullptr;
AbstractController::ctrl_t *ctrl = nullptr;
private:
Dispatcher<PrimaryDevice, SASIDEV> dispatcher;
void TestUnitReady();
void RequestSense();
void ReportLuns();
void Inquiry();
void ReportLuns(SASIDEV *);
vector<BYTE> HandleRequestSense();
Dispatcher<PrimaryDevice> dispatcher;
};

View File

@ -1,130 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ SASI hard disk ]
//
//---------------------------------------------------------------------------
#include "sasihd.h"
#include "fileio.h"
#include "exceptions.h"
#include "../config.h"
SASIHD::SASIHD(const unordered_set<uint32_t>& sector_sizes) : Disk("SAHD")
{
SetSectorSizes(sector_sizes);
}
void SASIHD::Reset()
{
// Unlock, clear attention
SetLocked(false);
SetAttn(false);
// Reset, clear the code
SetReset(false);
SetStatusCode(STATUS_NOERROR);
}
void SASIHD::Open(const Filepath& path)
{
assert(!IsReady());
// Open as read-only
Fileio fio;
if (!fio.Open(path, Fileio::ReadOnly)) {
throw file_not_found_exception("Can't open SASI hard disk file");
}
// Get file size
off_t size = fio.GetFileSize();
fio.Close();
// Sector size (default 256 bytes) and number of blocks
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 256, true);
SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount()));
// SASI only supports READ/WRITE(6), limiting the block count to 2^21
if (GetBlockCount() > 2097152) {
throw io_exception("SASI drives are limited to 2097152 blocks");
}
#if defined(REMOVE_FIXED_SASIHD_SIZE)
// Effective size must be a multiple of the sector size
size = (size / GetSectorSizeInBytes()) * GetSectorSizeInBytes();
#else
// 10MB, 20MB, 40MBのみ
switch (size) {
// 10MB (10441728 BS=256 C=40788)
case 0x9f5400:
break;
// 20MB (20748288 BS=256 C=81048)
case 0x13c9800:
break;
// 40MB (41496576 BS=256 C=162096)
case 0x2793000:
break;
// Other (Not supported )
default:
throw io_exception("Unsupported file size");
}
#endif // REMOVE_FIXED_SASIHD_SIZE
Disk::Open(path);
FileSupport::SetPath(path);
}
vector<BYTE> SASIHD::Inquiry() const
{
// Byte 0 = 0: Direct access device
return vector<BYTE>(2);
}
vector<BYTE> SASIHD::RequestSense(int allocation_length)
{
// Transfer 4 bytes when size is 0 (Shugart Associates System Interface specification)
vector<BYTE> buf(allocation_length ? allocation_length : 4);
// SASI fixed to non-extended format
buf[0] = (BYTE)(GetStatusCode() >> 16);
buf[1] = (BYTE)(GetLun() << 5);
LOGTRACE("%s Status $%02X",__PRETTY_FUNCTION__, buf[0]);
return buf;
}
void SASIHD::ReadCapacity10(SASIDEV *controller)
{
BYTE *buf = ctrl->buffer;
// Create end of logical block address (disk.blocks-1)
uint32_t blocks = disk.blocks - 1;
buf[0] = (BYTE)(blocks >> 24);
buf[1] = (BYTE)(blocks >> 16);
buf[2] = (BYTE)(blocks >> 8);
buf[3] = (BYTE)blocks;
// Create block length (1 << disk.size)
uint32_t length = 1 << disk.size;
buf[4] = (BYTE)(length >> 8);
buf[5] = (BYTE)length;
// the size
ctrl->length = 6;
controller->DataIn();
}

View File

@ -1,39 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ SASI hard disk ]
//
//---------------------------------------------------------------------------
#pragma once
#include "os.h"
#include "disk.h"
#include "filepath.h"
//===========================================================================
//
// SASI Hard Disk
//
//===========================================================================
class SASIHD : public Disk, public FileSupport
{
public:
SASIHD(const unordered_set<uint32_t>&);
~SASIHD() {}
void Reset();
void Open(const Filepath&) override;
vector<BYTE> RequestSense(int) override;
vector<BYTE> Inquiry() const override;
virtual void ReadCapacity10(SASIDEV *) override;
};

View File

@ -0,0 +1,89 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "log.h"
#include "rascsi_exceptions.h"
#include "scsi_command_util.h"
using namespace scsi_defs;
void scsi_command_util::ModeSelect(const DWORD *cdb, const BYTE *buf, int length, int sector_size)
{
assert(length >= 0);
// PF
if (cdb[1] & 0x10) {
bool has_valid_page_code = false;
// Mode Parameter header
if (length >= 12) {
// Check the block length
if (buf[9] != (BYTE)(sector_size >> 16) || buf[10] != (BYTE)(sector_size >> 8) ||
buf[11] != (BYTE)sector_size) {
// See below for details
LOGWARN("In order to change the sector size use the -b option when launching rascsi");
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
}
buf += 12;
length -= 12;
}
// Parsing the page
// TODO The length handling is wrong in case of length < size
while (length > 0) {
int page = buf[0];
switch (page) {
// Format device page
case 0x03: {
// With this page the sector size for a subsequent FORMAT can be selected, but only very few
// drives support this, e.g FUJITSU M2624S
// We are fine as long as the current sector size remains unchanged
if (buf[0xc] != (BYTE)(sector_size >> 8) || buf[0xd] != (BYTE)sector_size) {
// With rascsi it is not possible to permanently (by formatting) change the sector size,
// because the size is an externally configurable setting only
LOGWARN("In order to change the sector size use the -b option when launching rascsi");
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
}
has_valid_page_code = true;
}
break;
default:
LOGWARN("Unknown MODE SELECT page code: $%02X", page);
break;
}
// Advance to the next page
int size = buf[1] + 2;
length -= size;
buf += size;
}
if (!has_valid_page_code) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
}
}
else {
// Vendor-specific parameters (SCSI-1) are not supported
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
}
}
void scsi_command_util::EnrichFormatPage(map<int, vector<BYTE>>& pages, bool changeable, int sector_size)
{
if (changeable) {
// The sector size is simulated to be changeable, see the MODE SELECT implementation for details
vector<BYTE>& format_page = pages[3];
format_page[12] = sector_size >> 8;
format_page[13] = sector_size;
}
}

View File

@ -0,0 +1,24 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
// Shared code for SCSI command implementations
//
//---------------------------------------------------------------------------
#pragma once
#include "os.h"
#include <vector>
#include <map>
using namespace std;
namespace scsi_command_util
{
void ModeSelect(const DWORD *, const BYTE *, int, int);
void EnrichFormatPage(map<int, vector<BYTE>>&, bool, int);
}

View File

@ -27,6 +27,7 @@
// Note: This requires a DaynaPort SCSI Link driver.
//---------------------------------------------------------------------------
#include "rascsi_exceptions.h"
#include "scsi_daynaport.h"
using namespace scsi_defs;
@ -35,11 +36,8 @@ const BYTE SCSIDaynaPort::m_bcast_addr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
const BYTE SCSIDaynaPort::m_apple_talk_addr[6] = { 0x09, 0x00, 0x07, 0xff, 0xff, 0xff };
// TODO Disk should not be the superclass
SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP")
SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP"), dispatcher({})
{
m_tap = NULL;
m_bTapEnable = false;
dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &SCSIDaynaPort::TestUnitReady);
dispatcher.AddCommand(eCmdRead6, "Read6", &SCSIDaynaPort::Read6);
dispatcher.AddCommand(eCmdWrite6, "Write6", &SCSIDaynaPort::Write6);
@ -58,17 +56,16 @@ SCSIDaynaPort::~SCSIDaynaPort()
}
}
bool SCSIDaynaPort::Dispatch(SCSIDEV *controller)
bool SCSIDaynaPort::Dispatch()
{
// TODO As long as DaynaPort suffers from being a subclass of Disk at least reject MODE SENSE and MODE SELECT
SASIDEV::ctrl_t *ctrl = controller->GetCtrl();
if (ctrl->cmd[0] == eCmdModeSense6 || ctrl->cmd[0] == eCmdModeSelect6 ||
ctrl->cmd[0] == eCmdModeSense10 || ctrl->cmd[0] == eCmdModeSelect10) {
return false;
}
// The superclass class handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
return dispatcher.Dispatch(this, ctrl->cmd[0]) ? true : super::Dispatch();
}
bool SCSIDaynaPort::Init(const unordered_map<string, string>& params)
@ -118,9 +115,9 @@ void SCSIDaynaPort::Open(const Filepath& path)
m_tap->OpenDump(path);
}
vector<BYTE> SCSIDaynaPort::Inquiry() const
vector<BYTE> SCSIDaynaPort::InquiryInternal() const
{
vector<BYTE> buf = PrimaryDevice::Inquiry(device_type::PROCESSOR, scsi_level::SCSI_2, false);
vector<BYTE> buf = HandleInquiry(device_type::PROCESSOR, scsi_level::SCSI_2, false);
// The Daynaport driver for the Mac expects 37 bytes: Increase additional length and
// add a vendor-specific byte in order to satisfy this driver.
@ -161,7 +158,7 @@ vector<BYTE> SCSIDaynaPort::Inquiry() const
// - The SCSI/Link apparently has about 6KB buffer space for packets.
//
//---------------------------------------------------------------------------
int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, uint64_t block)
int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, uint64_t)
{
int rx_packet_size = 0;
scsi_resp_read_t *response = (scsi_resp_read_t*)buf;
@ -287,27 +284,17 @@ int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, uint64_t block)
return DAYNAPORT_READ_HEADER_SZ;
}
//---------------------------------------------------------------------------
//
// WRITE check
//
//---------------------------------------------------------------------------
int SCSIDaynaPort::WriteCheck(DWORD block)
int SCSIDaynaPort::WriteCheck(uint64_t)
{
// Status check
if (!CheckReady()) {
return 0;
}
CheckReady();
if (!m_bTapEnable){
SetStatusCode(STATUS_NOTREADY);
return 0;
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::MEDIUM_NOT_PRESENT);
}
// Success
return 1;
}
//---------------------------------------------------------------------------
//
// Write
@ -326,7 +313,7 @@ int SCSIDaynaPort::WriteCheck(DWORD block)
// XX XX ... is the actual packet
//
//---------------------------------------------------------------------------
bool SCSIDaynaPort::Write(const DWORD *cdb, const BYTE *buf, DWORD block)
bool SCSIDaynaPort::WriteBytes(const DWORD *cdb, BYTE *buf, uint64_t)
{
BYTE data_format = cdb[5];
WORD data_length = (WORD)cdb[4] + ((WORD)cdb[3] << 8);
@ -334,21 +321,20 @@ bool SCSIDaynaPort::Write(const DWORD *cdb, const BYTE *buf, DWORD block)
if (data_format == 0x00){
m_tap->Tx(buf, data_length);
LOGTRACE("%s Transmitted %u bytes (00 format)", __PRETTY_FUNCTION__, data_length);
return true;
}
else if (data_format == 0x80){
// The data length is specified in the first 2 bytes of the payload
data_length=(WORD)buf[1] + ((WORD)buf[0] << 8);
m_tap->Tx(&buf[4], data_length);
LOGTRACE("%s Transmitted %u bytes (80 format)", __PRETTY_FUNCTION__, data_length);
return true;
}
else
{
// LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)command->format);
LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)data_format);
return true;
}
return true;
}
//---------------------------------------------------------------------------
@ -393,7 +379,6 @@ int SCSIDaynaPort::RetrieveStats(const DWORD *cdb, BYTE *buffer)
response_size = allocation_length;
}
// Success
return response_size;
}
@ -435,14 +420,13 @@ bool SCSIDaynaPort::EnableInterface(const DWORD *cdb)
return result;
}
void SCSIDaynaPort::TestUnitReady(SASIDEV *controller)
void SCSIDaynaPort::TestUnitReady()
{
// TEST UNIT READY Success
controller->Status();
// Always successful
EnterStatusPhase();
}
void SCSIDaynaPort::Read6(SASIDEV *controller)
void SCSIDaynaPort::Read6()
{
// Get record number and block number
uint32_t record = ctrl->cmd[1] & 0x1f;
@ -456,8 +440,7 @@ void SCSIDaynaPort::Read6(SASIDEV *controller)
// generated by the DaynaPort driver so ignore them
if (ctrl->cmd[5] != 0xc0 && ctrl->cmd[5] != 0x80) {
LOGTRACE("%s Control value %d, (%04X), returning invalid CDB", __PRETTY_FUNCTION__, ctrl->cmd[5], ctrl->cmd[5]);
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
return;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
LOGTRACE("%s READ(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl->blocks);
@ -468,16 +451,16 @@ void SCSIDaynaPort::Read6(SASIDEV *controller)
// Set next block
ctrl->next = record + 1;
controller->DataIn();
EnterDataInPhase();
}
void SCSIDaynaPort::Write6(SASIDEV *controller)
void SCSIDaynaPort::Write6()
{
// Reallocate buffer (because it is not transfer for each block)
if (ctrl->bufsize < DAYNAPORT_BUFFER_SIZE) {
free(ctrl->buffer);
delete[] ctrl->buffer;
ctrl->buffer = new BYTE[ctrl->bufsize];
ctrl->bufsize = DAYNAPORT_BUFFER_SIZE;
ctrl->buffer = (BYTE *)malloc(ctrl->bufsize);
}
DWORD data_format = ctrl->cmd[5];
@ -494,32 +477,25 @@ void SCSIDaynaPort::Write6(SASIDEV *controller)
LOGTRACE("%s length: %04X (%d) format: %02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->length, (int)ctrl->length, (unsigned int)data_format);
if (ctrl->length <= 0) {
// Failure (Error)
controller->Error();
return;
throw scsi_error_exception();
}
// Set next block
ctrl->blocks = 1;
ctrl->next = 1;
controller->DataOut();
EnterDataOutPhase();
}
void SCSIDaynaPort::RetrieveStatistics(SASIDEV *controller)
void SCSIDaynaPort::RetrieveStatistics()
{
ctrl->length = RetrieveStats(ctrl->cmd, ctrl->buffer);
if (ctrl->length <= 0) {
// Failure (Error)
controller->Error();
return;
}
// Set next block
ctrl->blocks = 1;
ctrl->next = 1;
controller->DataIn();
EnterDataInPhase();
}
//---------------------------------------------------------------------------
@ -548,7 +524,7 @@ void SCSIDaynaPort::RetrieveStatistics(SASIDEV *controller)
// value.
//
//---------------------------------------------------------------------------
void SCSIDaynaPort::SetInterfaceMode(SASIDEV *controller)
void SCSIDaynaPort::SetInterfaceMode()
{
// Check whether this command is telling us to "Set Interface Mode" or "Set MAC Address"
@ -556,12 +532,12 @@ void SCSIDaynaPort::SetInterfaceMode(SASIDEV *controller)
switch(ctrl->cmd[5]){
case SCSIDaynaPort::CMD_SCSILINK_SETMODE:
// TODO Not implemented, do nothing
controller->Status();
EnterStatusPhase();
break;
case SCSIDaynaPort::CMD_SCSILINK_SETMAC:
ctrl->length = 6;
controller->DataOut();
EnterDataOutPhase();
break;
default:
@ -570,30 +546,25 @@ void SCSIDaynaPort::SetInterfaceMode(SASIDEV *controller)
}
}
void SCSIDaynaPort::SetMcastAddr(SASIDEV *controller)
void SCSIDaynaPort::SetMcastAddr()
{
ctrl->length = (DWORD)ctrl->cmd[4];
if (ctrl->length == 0) {
LOGWARN("%s Not supported SetMcastAddr Command %02X", __PRETTY_FUNCTION__, (WORD)ctrl->cmd[2]);
// Failure (Error)
controller->Error();
return;
throw scsi_error_exception();
}
controller->DataOut();
EnterDataOutPhase();
}
void SCSIDaynaPort::EnableInterface(SASIDEV *controller)
void SCSIDaynaPort::EnableInterface()
{
bool status = EnableInterface(ctrl->cmd);
if (!status) {
// Failure (Error)
controller->Error();
return;
if (!EnableInterface(ctrl->cmd)) {
throw scsi_error_exception();
}
controller->Status();
EnterStatusPhase();
}
int SCSIDaynaPort::GetSendDelay() const

View File

@ -49,26 +49,26 @@ public:
void Open(const Filepath& path) override;
// Commands
vector<BYTE> Inquiry() const override;
int Read(const DWORD *cdb, BYTE *buf, uint64_t block) override;
bool Write(const DWORD *cdb, const BYTE *buf, DWORD block) override;
int WriteCheck(DWORD block) override; // WRITE check
vector<BYTE> InquiryInternal() const override;
int Read(const DWORD *cdb, BYTE *, uint64_t) override;
bool WriteBytes(const DWORD *, BYTE *, uint64_t);
int WriteCheck(uint64_t block) override;
int RetrieveStats(const DWORD *cdb, BYTE *buffer);
bool EnableInterface(const DWORD *cdb);
void SetMacAddr(const DWORD *cdb, BYTE *buffer); // Set MAC address
void TestUnitReady(SASIDEV *) override;
void Read6(SASIDEV *) override;
void Write6(SASIDEV *) override;
void RetrieveStatistics(SASIDEV *);
void SetInterfaceMode(SASIDEV *);
void SetMcastAddr(SASIDEV *);
void EnableInterface(SASIDEV *);
void TestUnitReady() override;
void Read6() override;
void Write6() override;
void RetrieveStatistics();
void SetInterfaceMode();
void SetMcastAddr();
void EnableInterface();
int GetSendDelay() const override;
bool Dispatch(SCSIDEV *) override;
bool Dispatch() override;
const int DAYNAPORT_BUFFER_SIZE = 0x1000000;
@ -92,7 +92,7 @@ public:
private:
typedef Disk super;
Dispatcher<SCSIDaynaPort, SASIDEV> dispatcher;
Dispatcher<SCSIDaynaPort> dispatcher;
typedef struct __attribute__((packed)) {
BYTE operation_code;
@ -131,9 +131,9 @@ private:
const BYTE m_daynacom_mac_prefix[3] = { 0x00, 0x80, 0x19 };
CTapDriver *m_tap;
CTapDriver *m_tap = nullptr;
// TAP driver
bool m_bTapEnable;
bool m_bTapEnable = false;
// TAP valid flag
BYTE m_mac_addr[6];
// MAC Address

View File

@ -16,7 +16,7 @@
// work with the Sharp X68000 operating system.
//---------------------------------------------------------------------------
#include "controllers/scsidev_ctrl.h"
#include "rascsi_exceptions.h"
#include "scsi_host_bridge.h"
#include "ctapdriver.h"
#include "cfilesystem.h"
@ -24,19 +24,9 @@
using namespace std;
using namespace scsi_defs;
SCSIBR::SCSIBR() : Disk("SCBR")
SCSIBR::SCSIBR() : Disk("SCBR"), dispatcher({}), fs(new CFileSys())
{
tap = NULL;
m_bTapEnable = false;
packet_enable = false;
fsoptlen = 0;
fsoutlen = 0;
fsresult = 0;
packet_len = 0;
// Create host file system
fs = new CFileSys();
fs->Reset();
dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &SCSIBR::TestUnitReady);
@ -93,15 +83,15 @@ bool SCSIBR::Init(const unordered_map<string, string>& params)
#endif
}
bool SCSIBR::Dispatch(SCSIDEV *controller)
bool SCSIBR::Dispatch()
{
// The superclass class handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
return dispatcher.Dispatch(this, ctrl->cmd[0]) ? true : super::Dispatch();
}
vector<BYTE> SCSIBR::Inquiry() const
vector<BYTE> SCSIBR::InquiryInternal() const
{
vector<BYTE> b = PrimaryDevice::Inquiry(device_type::COMMUNICATIONS, scsi_level::SCSI_2, false);
vector<BYTE> b = HandleInquiry(device_type::COMMUNICATIONS, scsi_level::SCSI_2, false);
// The bridge returns 6 more additional bytes than the other devices
vector<BYTE> buf = vector<BYTE>(0x1F + 8 + 5);
@ -123,10 +113,10 @@ vector<BYTE> SCSIBR::Inquiry() const
return buf;
}
void SCSIBR::TestUnitReady(SASIDEV *controller)
void SCSIBR::TestUnitReady()
{
// Always successful
controller->Status();
EnterStatusPhase();
}
int SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf)
@ -209,7 +199,7 @@ int SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf)
return 0;
}
bool SCSIBR::SendMessage10(const DWORD *cdb, BYTE *buf)
bool SCSIBR::WriteBytes(const DWORD *cdb, BYTE *buf, uint64_t)
{
// Type
int type = cdb[2];
@ -258,32 +248,29 @@ bool SCSIBR::SendMessage10(const DWORD *cdb, BYTE *buf)
break;
}
// Error
ASSERT(false);
assert(false);
return false;
}
void SCSIBR::GetMessage10(SASIDEV *controller)
void SCSIBR::GetMessage10()
{
// Reallocate buffer (because it is not transfer for each block)
if (ctrl->bufsize < 0x1000000) {
free(ctrl->buffer);
delete[] ctrl->buffer;
ctrl->bufsize = 0x1000000;
ctrl->buffer = (BYTE *)malloc(ctrl->bufsize);
ctrl->buffer = new BYTE[ctrl->bufsize];
}
ctrl->length = GetMessage10(ctrl->cmd, ctrl->buffer);
if (ctrl->length <= 0) {
// Failure (Error)
controller->Error();
return;
throw scsi_error_exception();
}
// Set next block
ctrl->blocks = 1;
ctrl->next = 1;
controller->DataIn();
EnterDataInPhase();
}
//---------------------------------------------------------------------------
@ -293,13 +280,13 @@ void SCSIBR::GetMessage10(SASIDEV *controller)
// This Send Message command is used by the X68000 host driver
//
//---------------------------------------------------------------------------
void SCSIBR::SendMessage10(SASIDEV *controller)
void SCSIBR::SendMessage10()
{
// Reallocate buffer (because it is not transfer for each block)
if (ctrl->bufsize < 0x1000000) {
free(ctrl->buffer);
delete[] ctrl->buffer;
ctrl->buffer = new BYTE[ctrl->bufsize];
ctrl->bufsize = 0x1000000;
ctrl->buffer = (BYTE *)malloc(ctrl->bufsize);
}
// Set transfer amount
@ -310,16 +297,14 @@ void SCSIBR::SendMessage10(SASIDEV *controller)
ctrl->length |= ctrl->cmd[8];
if (ctrl->length <= 0) {
// Failure (Error)
controller->Error();
return;
throw scsi_error_exception();
}
// Set next block
ctrl->blocks = 1;
ctrl->next = 1;
controller->DataOut();
EnterDataOutPhase();
}
int SCSIBR::GetMacAddr(BYTE *mac)
@ -328,7 +313,7 @@ int SCSIBR::GetMacAddr(BYTE *mac)
return 6;
}
void SCSIBR::SetMacAddr(BYTE *mac)
void SCSIBR::SetMacAddr(const BYTE *mac)
{
memcpy(mac_addr, mac, 6);
}

View File

@ -37,33 +37,33 @@ public:
~SCSIBR();
bool Init(const unordered_map<string, string>&) override;
bool Dispatch(SCSIDEV *) override;
bool Dispatch() override;
// Commands
vector<BYTE> Inquiry() const override;
int GetMessage10(const DWORD *cdb, BYTE *buf);
bool SendMessage10(const DWORD *cdb, BYTE *buf);
void TestUnitReady(SASIDEV *) override;
void GetMessage10(SASIDEV *);
void SendMessage10(SASIDEV *);
vector<BYTE> InquiryInternal() const override;
int GetMessage10(const DWORD *, BYTE *);
bool WriteBytes(const DWORD *, BYTE *, uint64_t);
void TestUnitReady() override;
void GetMessage10();
void SendMessage10();
private:
typedef Disk super;
Dispatcher<SCSIBR, SASIDEV> dispatcher;
Dispatcher<SCSIBR> dispatcher;
int GetMacAddr(BYTE *buf); // Get MAC address
void SetMacAddr(BYTE *buf); // Set MAC address
void SetMacAddr(const BYTE *buf); // Set MAC address
void ReceivePacket(); // Receive a packet
void GetPacketBuf(BYTE *buf); // Get a packet
void SendPacket(BYTE *buf, int len); // Send a packet
void GetPacketBuf(BYTE *buf); // Get a packet
void SendPacket(BYTE *buf, int len); // Send a packet
CTapDriver *tap; // TAP driver
bool m_bTapEnable; // TAP valid flag
CTapDriver *tap = nullptr; // TAP driver
bool m_bTapEnable = false; // TAP valid flag
BYTE mac_addr[6]; // MAC Addres
int packet_len; // Receive packet size
BYTE packet_buf[0x1000]; // Receive packet buffer
bool packet_enable; // Received packet valid
int packet_len = 0; // Receive packet size
BYTE packet_buf[0x1000]; // Receive packet buffer
bool packet_enable = false; // Received packet valid
int ReadFsResult(BYTE *buf); // Read filesystem (result code)
int ReadFsOut(BYTE *buf); // Read filesystem (return data)
@ -99,9 +99,9 @@ private:
void FS_Lock(BYTE *buf); // $58 - get exclusive control
CFileSys *fs; // File system accessor
DWORD fsresult; // File system access result code
DWORD fsresult = 0; // File system access result code
BYTE fsout[0x800]; // File system access result buffer
DWORD fsoutlen; // File system access result buffer size
BYTE fsopt[0x1000000]; // File system access buffer
DWORD fsoptlen; // File system access buffer size
DWORD fsoutlen = 0; // File system access result buffer size
BYTE fsopt[0x1000000]; // File system access buffer
DWORD fsoptlen = 0; // File system access buffer size
};

View File

@ -40,23 +40,16 @@
//
#include <sys/stat.h>
#include "controllers/scsidev_ctrl.h"
#include "rascsi_exceptions.h"
#include "../rasutil.h"
#include "scsi_printer.h"
#define NOT_RESERVED -2
using namespace std;
using namespace scsi_defs;
using namespace ras_util;
SCSIPrinter::SCSIPrinter() : PrimaryDevice("SCLP"), ScsiPrinterCommands()
SCSIPrinter::SCSIPrinter() : PrimaryDevice("SCLP"), ScsiPrinterCommands(), dispatcher({})
{
fd = -1;
reserving_initiator = NOT_RESERVED;
reservation_time = 0;
timeout = 0;
dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &SCSIPrinter::TestUnitReady);
dispatcher.AddCommand(eCmdReserve6, "ReserveUnit", &SCSIPrinter::ReserveUnit);
dispatcher.AddCommand(eCmdRelease6, "ReleaseUnit", &SCSIPrinter::ReleaseUnit);
@ -88,36 +81,32 @@ bool SCSIPrinter::Init(const unordered_map<string, string>& params)
return true;
}
bool SCSIPrinter::Dispatch(SCSIDEV *controller)
bool SCSIPrinter::Dispatch()
{
// The superclass class handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
return dispatcher.Dispatch(this, ctrl->cmd[0]) ? true : super::Dispatch();
}
void SCSIPrinter::TestUnitReady(SCSIDEV *controller)
void SCSIPrinter::TestUnitReady()
{
if (!CheckReservation(controller)) {
return;
}
CheckReservation();
controller->Status();
EnterStatusPhase();
}
vector<BYTE> SCSIPrinter::Inquiry() const
vector<BYTE> SCSIPrinter::InquiryInternal() const
{
return PrimaryDevice::Inquiry(device_type::PRINTER, scsi_level::SCSI_2, false);
return HandleInquiry(device_type::PRINTER, scsi_level::SCSI_2, false);
}
void SCSIPrinter::ReserveUnit(SCSIDEV *controller)
void SCSIPrinter::ReserveUnit()
{
// The printer is released after a configurable time in order to prevent deadlocks caused by broken clients
if (reservation_time + timeout < time(0)) {
DiscardReservation();
}
if (!CheckReservation(controller)) {
return;
}
CheckReservation();
reserving_initiator = controller->GetInitiatorId();
@ -130,14 +119,12 @@ void SCSIPrinter::ReserveUnit(SCSIDEV *controller)
Cleanup();
controller->Status();
EnterStatusPhase();
}
void SCSIPrinter::ReleaseUnit(SCSIDEV *controller)
void SCSIPrinter::ReleaseUnit()
{
if (!CheckReservation(controller)) {
return;
}
CheckReservation();
if (reserving_initiator != -1) {
LOGTRACE("Released device ID %d, LUN %d reserved by initiator ID %d", GetId(), GetLun(), reserving_initiator);
@ -148,14 +135,12 @@ void SCSIPrinter::ReleaseUnit(SCSIDEV *controller)
DiscardReservation();
controller->Status();
EnterStatusPhase();
}
void SCSIPrinter::Print(SCSIDEV *controller)
void SCSIPrinter::Print()
{
if (!CheckReservation(controller)) {
return;
}
CheckReservation();
uint32_t length = ctrl->cmd[2];
length <<= 8;
@ -170,25 +155,21 @@ void SCSIPrinter::Print(SCSIDEV *controller)
if (length > (uint32_t)ctrl->bufsize) {
LOGERROR("Transfer buffer overflow");
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
return;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
ctrl->length = length;
controller->SetByteTransfer(true);
controller->DataOut();
EnterDataOutPhase();
}
void SCSIPrinter::SynchronizeBuffer(SCSIDEV *controller)
void SCSIPrinter::SynchronizeBuffer()
{
if (!CheckReservation(controller)) {
return;
}
CheckReservation();
if (fd == -1) {
controller->Error();
return;
throw scsi_error_exception();
}
// Make the file readable for the lp user
@ -213,36 +194,33 @@ void SCSIPrinter::SynchronizeBuffer(SCSIDEV *controller)
if (system(cmd.c_str())) {
LOGERROR("Printing failed, the printing system might not be configured");
controller->Error();
}
else {
controller->Status();
unlink(filename);
throw scsi_error_exception();
}
unlink(filename);
EnterStatusPhase();
}
void SCSIPrinter::SendDiagnostic(SCSIDEV *controller)
void SCSIPrinter::SendDiagnostic()
{
if (!CheckReservation(controller)) {
return;
}
CheckReservation();
controller->Status();
EnterStatusPhase();
}
void SCSIPrinter::StopPrint(SCSIDEV *controller)
void SCSIPrinter::StopPrint()
{
if (!CheckReservation(controller)) {
return;
}
CheckReservation();
// Nothing to do, printing has not yet been started
controller->Status();
EnterStatusPhase();
}
bool SCSIPrinter::WriteBytes(BYTE *buf, uint32_t length)
bool SCSIPrinter::WriteByteSequence(BYTE *buf, uint32_t length)
{
if (fd == -1) {
strcpy(filename, TMP_FILE_PATTERN);
@ -259,15 +237,14 @@ bool SCSIPrinter::WriteBytes(BYTE *buf, uint32_t length)
uint32_t num_written = write(fd, buf, length);
return (num_written == length);
return num_written == length;
}
bool SCSIPrinter::CheckReservation(SCSIDEV *controller)
void SCSIPrinter::CheckReservation()
{
if (reserving_initiator == NOT_RESERVED || reserving_initiator == controller->GetInitiatorId()) {
reservation_time = time(0);
return true;
return;
}
if (controller->GetInitiatorId() != -1) {
@ -277,10 +254,8 @@ bool SCSIPrinter::CheckReservation(SCSIDEV *controller)
LOGTRACE("Unknown initiator tries to access reserved device ID %d, LUN %d", GetId(), GetLun());
}
controller->Error(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION,
throw scsi_error_exception(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION,
status::RESERVATION_CONFLICT);
return false;
}
void SCSIPrinter::DiscardReservation()

View File

@ -21,27 +21,28 @@ using namespace std;
class SCSIPrinter: public PrimaryDevice, ScsiPrinterCommands
{
static const int NOT_RESERVED = -2;
public:
SCSIPrinter();
~SCSIPrinter();
virtual bool Dispatch(SCSIDEV *) override;
virtual bool Dispatch() override;
bool Init(const unordered_map<string, string>&);
bool Init(const unordered_map<string, string>&) override;
vector<BYTE> Inquiry() const override;
void TestUnitReady(SCSIDEV *);
void ReserveUnit(SCSIDEV *);
void ReleaseUnit(SCSIDEV *);
void Print(SCSIDEV *);
void SynchronizeBuffer(SCSIDEV *);
void SendDiagnostic(SCSIDEV *);
void StopPrint(SCSIDEV *);
vector<BYTE> InquiryInternal() const override;
void TestUnitReady() override;
void ReserveUnit() override;
void ReleaseUnit() override;
void Print() override;
void SynchronizeBuffer();
void SendDiagnostic() override;
void StopPrint();
bool WriteBytes(BYTE *, uint32_t) override;
bool CheckReservation(SCSIDEV *);
bool WriteByteSequence(BYTE *, uint32_t) override;
void CheckReservation();
void DiscardReservation();
void Cleanup();
@ -49,13 +50,13 @@ private:
typedef PrimaryDevice super;
Dispatcher<SCSIPrinter, SCSIDEV> dispatcher;
Dispatcher<SCSIPrinter> dispatcher;
char filename[sizeof(TMP_FILE_PATTERN) + 1];
int fd;
int fd = -1;
int reserving_initiator;
int reserving_initiator = NOT_RESERVED;
time_t reservation_time;
int timeout;
time_t reservation_time = 0;
int timeout = 0;
};

View File

@ -16,7 +16,7 @@
#include "scsicd.h"
#include "fileio.h"
#include "exceptions.h"
#include "rascsi_exceptions.h"
using namespace scsi_defs;
@ -165,7 +165,7 @@ bool CDTrack::IsValid(DWORD lba) const
//---------------------------------------------------------------------------
bool CDTrack::IsAudio() const
{
ASSERT(valid);
assert(valid);
return audio;
}
@ -180,41 +180,24 @@ SCSICD::SCSICD(const unordered_set<uint32_t>& sector_sizes) : Disk("SCCD"), Scsi
{
SetSectorSizes(sector_sizes);
// NOT in raw format
rawfile = false;
// Frame initialization
frame = 0;
// Track initialization
for (int i = 0; i < TrackMax; i++) {
track[i] = NULL;
}
tracks = 0;
dataindex = -1;
audioindex = -1;
dispatcher.AddCommand(eCmdReadToc, "ReadToc", &SCSICD::ReadToc);
dispatcher.AddCommand(eCmdGetEventStatusNotification, "GetEventStatusNotification", &SCSICD::GetEventStatusNotification);
}
SCSICD::~SCSICD()
{
// Clear track
ClearTrack();
}
bool SCSICD::Dispatch(SCSIDEV *controller)
bool SCSICD::Dispatch()
{
// The superclass class handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
return dispatcher.Dispatch(this, ctrl->cmd[0]) ? true : super::Dispatch();
}
void SCSICD::Open(const Filepath& path)
{
off_t size;
ASSERT(!IsReady());
assert(!IsReady());
// Initialization, track clear
SetBlockCount(0);
@ -228,7 +211,7 @@ void SCSICD::Open(const Filepath& path)
}
// Default sector size is 2048 bytes
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 2048, false);
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 2048);
// Close and transfer for physical CD access
if (path.GetPath()[0] == _T('\\')) {
@ -239,7 +222,7 @@ void SCSICD::Open(const Filepath& path)
OpenPhysical(path);
} else {
// Get file size
size = fio.GetFileSize();
off_t size = fio.GetFileSize();
if (size <= 4) {
fio.Close();
throw io_exception("CD-ROM file size must be at least 4 bytes");
@ -268,7 +251,7 @@ void SCSICD::Open(const Filepath& path)
FileSupport::SetPath(path);
// Set RAW flag
ASSERT(disk.dcache);
assert(disk.dcache);
disk.dcache->SetRawMode(rawfile);
// Attention if ready
@ -344,7 +327,7 @@ void SCSICD::OpenIso(const Filepath& path)
}
// Create only one data track
ASSERT(!track[0]);
assert(!track[0]);
track[0] = new CDTrack(this);
track[0]->Init(1, 0, GetBlockCount() - 1);
track[0]->SetPath(false, path);
@ -385,21 +368,16 @@ void SCSICD::OpenPhysical(const Filepath& path)
dataindex = 0;
}
void SCSICD::ReadToc(SASIDEV *controller)
void SCSICD::ReadToc()
{
ctrl->length = ReadToc(ctrl->cmd, ctrl->buffer);
if (ctrl->length <= 0) {
// Failure (Error)
controller->Error();
return;
}
controller->DataIn();
EnterDataInPhase();
}
vector<BYTE> SCSICD::Inquiry() const
vector<BYTE> SCSICD::InquiryInternal() const
{
return PrimaryDevice::Inquiry(device_type::CD_ROM, scsi_level::SCSI_2, true);
return HandleInquiry(device_type::CD_ROM, scsi_level::SCSI_2, true);
//
// The following code worked with the modified Apple CD-ROM drivers. Need to
@ -469,28 +447,25 @@ void SCSICD::AddCDDAPage(map<int, vector<BYTE>>& pages, bool) const
int SCSICD::Read(const DWORD *cdb, BYTE *buf, uint64_t block)
{
ASSERT(buf);
assert(buf);
// Status check
if (!CheckReady()) {
return 0;
}
CheckReady();
// Search for the track
int index = SearchTrack(block);
// if invalid, out of range
// If invalid, out of range
if (index < 0) {
SetStatusCode(STATUS_INVALIDLBA);
return 0;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
ASSERT(track[index]);
assert(track[index]);
// If different from the current data track
if (dataindex != index) {
// Delete current disk cache (no need to save)
delete disk.dcache;
disk.dcache = NULL;
disk.dcache = nullptr;
// Reset the number of blocks
SetBlockCount(track[index]->GetBlocks());
@ -507,23 +482,20 @@ int SCSICD::Read(const DWORD *cdb, BYTE *buf, uint64_t block)
}
// Base class
ASSERT(dataindex >= 0);
assert(dataindex >= 0);
return super::Read(cdb, buf, block);
}
int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf)
{
ASSERT(cdb);
ASSERT(buf);
assert(cdb);
assert(buf);
// Check if ready
if (!CheckReady()) {
return 0;
}
CheckReady();
// If ready, there is at least one track
ASSERT(tracks > 0);
ASSERT(track[0]);
assert(tracks > 0);
assert(track[0]);
// Get allocation length, clear buffer
int length = cdb[7] << 8;
@ -538,8 +510,7 @@ int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf)
if ((int)cdb[6] > last) {
// Except for AA
if (cdb[6] != 0xaa) {
SetStatusCode(STATUS_INVALIDCDB);
return 0;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
}
@ -574,14 +545,13 @@ int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf)
}
// Otherwise, error
SetStatusCode(STATUS_INVALIDCDB);
return 0;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
}
// Number of track descriptors returned this time (number of loops)
int loop = last - track[index]->GetTrackNo() + 1;
ASSERT(loop >= 1);
assert(loop >= 1);
// Create header
buf[0] = (BYTE)(((loop << 3) + 2) >> 8);
@ -621,16 +591,15 @@ int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf)
return length;
}
void SCSICD::GetEventStatusNotification(SASIDEV *controller)
void SCSICD::GetEventStatusNotification()
{
if (!(ctrl->cmd[1] & 0x01)) {
// Asynchronous notification is optional and not supported by rascsi
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
return;
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
LOGTRACE("Received request for event polling, which is currently not supported");
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
//---------------------------------------------------------------------------
@ -654,9 +623,9 @@ void SCSICD::LBAtoMSF(DWORD lba, BYTE *msf) const
}
// Store
ASSERT(m < 0x100);
ASSERT(s < 60);
ASSERT(f < 75);
assert(m < 0x100);
assert(s < 60);
assert(f < 75);
msf[0] = 0x00;
msf[1] = (BYTE)m;
msf[2] = (BYTE)s;
@ -667,10 +636,8 @@ void SCSICD::ClearTrack()
{
// delete the track object
for (int i = 0; i < TrackMax; i++) {
if (track[i]) {
delete track[i];
track[i] = NULL;
}
delete track[i];
track[i] = nullptr;
}
// Number of tracks is 0
@ -692,7 +659,7 @@ int SCSICD::SearchTrack(DWORD lba) const
// Track loop
for (int i = 0; i < tracks; i++) {
// Listen to the track
ASSERT(track[i]);
assert(track[i]);
if (track[i]->IsValid(lba)) {
return i;
}

View File

@ -79,12 +79,12 @@ public:
SCSICD(const unordered_set<uint32_t>&);
~SCSICD();
bool Dispatch(SCSIDEV *) override;
bool Dispatch() override;
void Open(const Filepath& path) override;
// Commands
vector<BYTE> Inquiry() const override;
vector<BYTE> InquiryInternal() const override;
int Read(const DWORD *cdb, BYTE *buf, uint64_t block) override;
int ReadToc(const DWORD *cdb, BYTE *buf);
@ -95,7 +95,7 @@ protected:
private:
typedef Disk super;
Dispatcher<SCSICD, SASIDEV> dispatcher;
Dispatcher<SCSICD> dispatcher;
void AddCDROMPage(map<int, vector<BYTE>>&, bool) const;
void AddCDDAPage(map<int, vector<BYTE>>&, bool) const;
@ -105,20 +105,18 @@ private:
void OpenIso(const Filepath& path); // Open(ISO)
void OpenPhysical(const Filepath& path); // Open(Physical)
void ReadToc(SASIDEV *) override;
void GetEventStatusNotification(SASIDEV *) override;
void ReadToc() override;
void GetEventStatusNotification() override;
void LBAtoMSF(DWORD lba, BYTE *msf) const; // LBA→MSF conversion
bool rawfile; // RAW flag
bool rawfile = false; // RAW flag
// Track management
void ClearTrack(); // Clear the track
int SearchTrack(DWORD lba) const; // Track search
CDTrack* track[TrackMax]; // Track opbject references
int tracks; // Effective number of track objects
int dataindex; // Current data track
int audioindex; // Current audio track
int frame; // Frame number
int SearchTrack(DWORD lba) const; // Track search
CDTrack* track[TrackMax] = {}; // Track opbject references
int tracks = 0; // Effective number of track objects
int dataindex = -1; // Current data track
int audioindex = -1; // Current audio track
};

View File

@ -15,17 +15,12 @@
//---------------------------------------------------------------------------
#include "scsihd.h"
#include "fileio.h"
#include "exceptions.h"
#include "rascsi_exceptions.h"
#include "scsi_command_util.h"
#include <sstream>
#define DEFAULT_PRODUCT "SCSI HD"
//===========================================================================
//
// SCSI Hard Disk
//
//===========================================================================
SCSIHD::SCSIHD(const unordered_set<uint32_t>& sector_sizes, bool removable) : Disk(removable ? "SCRM" : "SCHD")
{
SetSectorSizes(sector_sizes);
@ -71,7 +66,7 @@ void SCSIHD::Reset()
// No reset, clear code
SetReset(false);
SetStatusCode(STATUS_NOERROR);
SetStatusCode(0);
}
void SCSIHD::Open(const Filepath& path)
@ -89,7 +84,7 @@ void SCSIHD::Open(const Filepath& path)
fio.Close();
// Sector size (default 512 bytes) and number of blocks
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512, false);
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512);
SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount()));
// Effective size must be a multiple of the sector size
@ -98,82 +93,21 @@ void SCSIHD::Open(const Filepath& path)
FinalizeSetup(path, size);
}
vector<BYTE> SCSIHD::Inquiry() const
vector<BYTE> SCSIHD::InquiryInternal() const
{
return PrimaryDevice::Inquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_2, IsRemovable());
return HandleInquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_2, IsRemovable());
}
bool SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
void SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
{
assert(length >= 0);
scsi_command_util::ModeSelect(cdb, buf, length, 1 << GetSectorSizeShiftCount());
}
int size;
void SCSIHD::AddFormatPage(map<int, vector<BYTE>>& pages, bool changeable) const
{
Disk::AddFormatPage(pages, changeable);
// PF
if (cdb[1] & 0x10) {
// Mode Parameter header
if (length >= 12) {
// Check the block length bytes
size = 1 << GetSectorSizeShiftCount();
if (buf[9] != (BYTE)(size >> 16) ||
buf[10] != (BYTE)(size >> 8) ||
buf[11] != (BYTE)size) {
// currently does not allow changing sector length
SetStatusCode(STATUS_INVALIDPRM);
return false;
}
buf += 12;
length -= 12;
}
// Parsing the page
while (length > 0) {
// Get page
BYTE page = buf[0];
switch (page) {
// format device
case 0x03:
// check the number of bytes in the physical sector
size = 1 << GetSectorSizeShiftCount();
if (buf[0xc] != (BYTE)(size >> 8) ||
buf[0xd] != (BYTE)size) {
// currently does not allow changing sector length
SetStatusCode(STATUS_INVALIDPRM);
return false;
}
break;
// CD-ROM Parameters
// TODO Move to scsicd.cpp
// According to the SONY CDU-541 manual, Page code 8 is supposed
// to set the Logical Block Adress Format, as well as the
// inactivity timer multiplier
case 0x08:
// Debug code for Issue #2:
// https://github.com/akuker/RASCSI/issues/2
LOGWARN("[Unhandled page code] Received mode page code 8 with total length %d\n ", length);
for (int i = 0; i<length; i++)
{
printf("%02X ", buf[i]);
}
printf("\n");
break;
// Other page
default:
printf("Unknown Mode Select page code received: %02X\n",page);
break;
}
// Advance to the next page
size = buf[1] + 2;
length -= size;
buf += size;
}
}
// Do not generate an error for the time being (MINIX)
return true;
scsi_command_util::EnrichFormatPage(pages, changeable, 1 << GetSectorSizeShiftCount());
}
//---------------------------------------------------------------------------

View File

@ -31,8 +31,9 @@ public:
virtual void Open(const Filepath&) override;
// Commands
virtual vector<BYTE> Inquiry() const override;
bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override;
virtual vector<BYTE> InquiryInternal() const override;
void ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override;
void AddFormatPage(map<int, vector<BYTE>>&, bool) const override;
void AddVendorPage(map<int, vector<BYTE>>&, int, bool) const override;
};

View File

@ -16,14 +16,10 @@
#include "scsihd_nec.h"
#include "fileio.h"
#include "exceptions.h"
#include "rascsi_exceptions.h"
SCSIHD_NEC::SCSIHD_NEC(const unordered_set<uint32_t>& sector_sizes) : SCSIHD(sector_sizes, false)
{
// Work initialization
cylinders = 0;
heads = 0;
sectors = 0;
}
//---------------------------------------------------------------------------
@ -135,9 +131,9 @@ void SCSIHD_NEC::Open(const Filepath& path)
FinalizeSetup(path, size);
}
vector<BYTE> SCSIHD_NEC::Inquiry() const
vector<BYTE> SCSIHD_NEC::InquiryInternal() const
{
return PrimaryDevice::Inquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_1_CCS, false);
return HandleInquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_1_CCS, false);
}
void SCSIHD_NEC::AddErrorPage(map<int, vector<BYTE>>& pages, bool) const

View File

@ -31,7 +31,7 @@ public:
void Open(const Filepath& path) override;
// Commands
vector<BYTE> Inquiry() const override;
vector<BYTE> InquiryInternal() const override;
void AddErrorPage(map<int, vector<BYTE>>&, bool) const override;
void AddFormatPage(map<int, vector<BYTE>>&, bool) const override;
@ -39,7 +39,7 @@ public:
private:
// Geometry data
int cylinders;
int heads;
int sectors;
int cylinders = 0;
int heads = 0;
int sectors = 0;
};

View File

@ -15,10 +15,12 @@
//---------------------------------------------------------------------------
#include "fileio.h"
#include "exceptions.h"
#include "rascsi_exceptions.h"
#include "scsi_command_util.h"
#include "scsimo.h"
SCSIMO::SCSIMO(const unordered_set<uint32_t>& sector_sizes, const unordered_map<uint64_t, Geometry>& geometries) : Disk("SCMO")
SCSIMO::SCSIMO(const unordered_set<uint32_t>& sector_sizes, const unordered_map<uint64_t, Geometry>& geometries)
: Disk("SCMO"), geometries({})
{
SetSectorSizes(sector_sizes);
SetGeometries(geometries);
@ -43,7 +45,7 @@ void SCSIMO::Open(const Filepath& path)
// TODO Find a more flexible solution
if (!SetGeometryForCapacity(size)) {
// Sector size (default 512 bytes) and number of blocks
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512, true);
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512);
SetBlockCount(size >> GetSectorSizeShiftCount());
}
@ -63,9 +65,9 @@ void SCSIMO::Open(const Filepath& path)
}
}
vector<BYTE> SCSIMO::Inquiry() const
vector<BYTE> SCSIMO::InquiryInternal() const
{
return PrimaryDevice::Inquiry(device_type::OPTICAL_MEMORY, scsi_level::SCSI_2, true);
return HandleInquiry(device_type::OPTICAL_MEMORY, scsi_level::SCSI_2, true);
}
void SCSIMO::SetDeviceParameters(BYTE *buf)
@ -86,6 +88,13 @@ void SCSIMO::AddModePages(map<int, vector<BYTE>>& pages, int page, bool changeab
}
}
void SCSIMO::AddFormatPage(map<int, vector<BYTE>>& pages, bool changeable) const
{
Disk::AddFormatPage(pages, changeable);
scsi_command_util::EnrichFormatPage(pages, changeable, 1 << GetSectorSizeShiftCount());
}
void SCSIMO::AddOptionPage(map<int, vector<BYTE>>& pages, bool) const
{
vector<BYTE> buf(4);
@ -94,64 +103,9 @@ void SCSIMO::AddOptionPage(map<int, vector<BYTE>>& pages, bool) const
// Do not report update blocks
}
bool SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
void SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
{
int size;
ASSERT(length >= 0);
// PF
if (cdb[1] & 0x10) {
// Mode Parameter header
if (length >= 12) {
// Check the block length (in bytes)
size = 1 << GetSectorSizeShiftCount();
if (buf[9] != (BYTE)(size >> 16) ||
buf[10] != (BYTE)(size >> 8) || buf[11] != (BYTE)size) {
// Currently does not allow changing sector length
SetStatusCode(STATUS_INVALIDPRM);
return false;
}
buf += 12;
length -= 12;
}
// Parsing the page
while (length > 0) {
// Get the page
int page = buf[0];
switch (page) {
// format device
case 0x03:
// Check the number of bytes in the physical sector
size = 1 << GetSectorSizeShiftCount();
if (buf[0xc] != (BYTE)(size >> 8) ||
buf[0xd] != (BYTE)size) {
// Currently does not allow changing sector length
SetStatusCode(STATUS_INVALIDPRM);
return false;
}
break;
// vendor unique format
case 0x20:
// just ignore, for now
break;
// Other page
default:
break;
}
// Advance to the next page
size = buf[1] + 2;
length -= size;
buf += size;
}
}
// Do not generate an error for the time being (MINIX)
return true;
scsi_command_util::ModeSelect(cdb, buf, length, 1 << GetSectorSizeShiftCount());
}
//---------------------------------------------------------------------------
@ -257,3 +211,15 @@ void SCSIMO::AddVendorPage(map<int, vector<BYTE>>& pages, int page, bool changea
return;
}
bool SCSIMO::SetGeometryForCapacity(uint64_t capacity) {
const auto& geometry = geometries.find(capacity);
if (geometry != geometries.end()) {
SetSectorSizeInBytes(geometry->second.first);
SetBlockCount(geometry->second.second);
return true;
}
return false;
}

View File

@ -13,6 +13,7 @@
// [ SCSI Magneto-Optical Disk]
//
//---------------------------------------------------------------------------
#pragma once
#include "os.h"
@ -22,23 +23,29 @@
class SCSIMO : public Disk, public FileSupport
{
public:
SCSIMO(const unordered_set<uint32_t>&, const unordered_map<uint64_t, Geometry>&);
~SCSIMO() {}
void Open(const Filepath& path) override;
// Commands
vector<BYTE> Inquiry() const override;
bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override;
vector<BYTE> InquiryInternal() const override;
void ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override;
protected:
// Internal processing
void SetDeviceParameters(BYTE *) override;
void AddModePages(map<int, vector<BYTE>>&, int, bool) const override;
void AddFormatPage(map<int, vector<BYTE>>&, bool) const override;
void AddVendorPage(map<int, vector<BYTE>>&, int, bool) const override;
private:
void AddOptionPage(map<int, vector<BYTE>>&, bool) const;
void SetGeometries(const unordered_map<uint64_t, Geometry>& geometries) { this->geometries = geometries; }
bool SetGeometryForCapacity(uint64_t);
// The mapping of supported capacities to block sizes and block counts, empty if there is no capacity restriction
unordered_map<uint64_t, Geometry> geometries;
};

View File

@ -1,49 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
//
// Various exceptions
//
//---------------------------------------------------------------------------
#pragma once
#include <exception>
#include <string>
using namespace std;
class illegal_argument_exception final : public exception {
private:
string msg;
public:
illegal_argument_exception(const string& _msg) : msg(_msg) {}
illegal_argument_exception() {};
const string& getmsg() const {
return msg;
}
};
class io_exception : public exception {
private:
string msg;
public:
io_exception(const string& _msg) : msg(_msg) {}
virtual ~io_exception() {}
const string& getmsg() const {
return msg;
}
};
class file_not_found_exception : public io_exception {
public:
file_not_found_exception(const string& msg) : io_exception(msg) {}
~file_not_found_exception() {}
};

View File

@ -22,8 +22,6 @@
Fileio::Fileio()
{
// Initialize work
handle = -1;
}
Fileio::~Fileio()

View File

@ -63,7 +63,7 @@ public:
private:
BOOL Open(const char *fname, OpenMode mode, BOOL directIO);
int handle; // File handle
int handle = -1; // File handle
};
#endif // fileio_h

View File

@ -13,15 +13,8 @@
#include "config.h"
#include "fileio.h"
//===========================================================================
//
// File path
//
//===========================================================================
Filepath::Filepath()
{
// Clear
Clear();
}
@ -39,7 +32,6 @@ Filepath& Filepath::operator=(const Filepath& path)
void Filepath::Clear()
{
// Clear the path and each part
m_szPath[0] = _T('\0');
m_szDir[0] = _T('\0');

View File

@ -78,26 +78,13 @@ DWORD bcm_host_get_peripheral_address(void)
GPIOBUS::GPIOBUS()
{
actmode = TARGET;
baseaddr = 0;
gicc = 0;
gicd = 0;
gpio = 0;
level = 0;
pads = 0;
irpctl = 0;
qa7regs = 0;
signals = 0;
rpitype = 0;
}
GPIOBUS::~GPIOBUS()
{
}
BOOL GPIOBUS::Init(mode_e mode)
bool GPIOBUS::Init(mode_e mode)
{
#if defined(__x86_64__) || defined(__X86__)
actmode = mode;
// When we're running on x86, there is no hardware to talk to, so just return.
return true;
#else
@ -120,7 +107,7 @@ BOOL GPIOBUS::Init(mode_e mode)
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;
return false;
}
// Map peripheral region memory
@ -128,7 +115,7 @@ BOOL GPIOBUS::Init(mode_e mode)
if (map == MAP_FAILED) {
LOGERROR("Error: Unable to map memory");
close(fd);
return FALSE;
return false;
}
// Determine the type of raspberry pi from the base address
@ -168,7 +155,7 @@ BOOL GPIOBUS::Init(mode_e mode)
PROT_READ | PROT_WRITE, MAP_SHARED, fd, ARM_GICD_BASE);
if (map == MAP_FAILED) {
close(fd);
return FALSE;
return false;
}
gicd = (DWORD *)map;
gicc = (DWORD *)map;
@ -226,7 +213,7 @@ BOOL GPIOBUS::Init(mode_e mode)
fd = open("/dev/gpiochip0", 0);
if (fd == -1) {
LOGERROR("Unable to open /dev/gpiochip0. Is RaSCSI already running?")
return FALSE;
return false;
}
// Event request setting
@ -243,7 +230,7 @@ BOOL GPIOBUS::Init(mode_e mode)
if (ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &selevreq) == -1) {
LOGERROR("Unable to register event request. Is RaSCSI already running?")
close(fd);
return FALSE;
return false;
}
// Close GPIO chip file handle
@ -313,7 +300,7 @@ BOOL GPIOBUS::Init(mode_e mode)
// Show the user that this app is running
SetControl(PIN_ENB, ENB_ON);
return TRUE;
return true;
#endif // ifdef __x86_64__ || __X86__
}
@ -441,31 +428,16 @@ void GPIOBUS::Reset()
#endif // ifdef __x86_64__ || __X86__
}
//---------------------------------------------------------------------------
//
// ENB signal setting
//
//---------------------------------------------------------------------------
void GPIOBUS::SetENB(BOOL ast)
void GPIOBUS::SetENB(bool ast)
{
PinSetSignal(PIN_ENB, ast ? ENB_ON : ENB_OFF);
}
//---------------------------------------------------------------------------
//
// Get BSY signal
//
//---------------------------------------------------------------------------
bool GPIOBUS::GetBSY()
bool GPIOBUS::GetBSY() const
{
return GetSignal(PIN_BSY);
}
//---------------------------------------------------------------------------
//
// Set BSY signal
//
//---------------------------------------------------------------------------
void GPIOBUS::SetBSY(bool ast)
{
// Set BSY signal
@ -500,22 +472,12 @@ void GPIOBUS::SetBSY(bool ast)
}
}
//---------------------------------------------------------------------------
//
// Get SEL signal
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::GetSEL()
bool GPIOBUS::GetSEL() const
{
return GetSignal(PIN_SEL);
}
//---------------------------------------------------------------------------
//
// Set SEL signal
//
//---------------------------------------------------------------------------
void GPIOBUS::SetSEL(BOOL ast)
void GPIOBUS::SetSEL(bool ast)
{
if (actmode == INITIATOR && ast) {
// Turn on ACTIVE signal
@ -526,134 +488,69 @@ void GPIOBUS::SetSEL(BOOL ast)
SetSignal(PIN_SEL, ast);
}
//---------------------------------------------------------------------------
//
// Get ATN signal
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::GetATN()
bool GPIOBUS::GetATN() const
{
return GetSignal(PIN_ATN);
}
//---------------------------------------------------------------------------
//
// Get ATN signal
//
//---------------------------------------------------------------------------
void GPIOBUS::SetATN(BOOL ast)
void GPIOBUS::SetATN(bool ast)
{
SetSignal(PIN_ATN, ast);
}
//---------------------------------------------------------------------------
//
// Get ACK signal
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::GetACK()
bool GPIOBUS::GetACK() const
{
return GetSignal(PIN_ACK);
}
//---------------------------------------------------------------------------
//
// Set ACK signal
//
//---------------------------------------------------------------------------
void GPIOBUS::SetACK(BOOL ast)
void GPIOBUS::SetACK(bool ast)
{
SetSignal(PIN_ACK, ast);
}
//---------------------------------------------------------------------------
//
// Get ACK signal
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::GetACT()
bool GPIOBUS::GetACT() const
{
return GetSignal(PIN_ACT);
}
//---------------------------------------------------------------------------
//
// Set ACK signal
//
//---------------------------------------------------------------------------
void GPIOBUS::SetACT(BOOL ast)
void GPIOBUS::SetACT(bool ast)
{
SetSignal(PIN_ACT, ast);
}
//---------------------------------------------------------------------------
//
// Get RST signal
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::GetRST()
bool GPIOBUS::GetRST() const
{
return GetSignal(PIN_RST);
}
//---------------------------------------------------------------------------
//
// Set RST signal
//
//---------------------------------------------------------------------------
void GPIOBUS::SetRST(BOOL ast)
void GPIOBUS::SetRST(bool ast)
{
SetSignal(PIN_RST, ast);
}
//---------------------------------------------------------------------------
//
// Get MSG signal
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::GetMSG()
bool GPIOBUS::GetMSG() const
{
return GetSignal(PIN_MSG);
}
//---------------------------------------------------------------------------
//
// Set MSG signal
//
//---------------------------------------------------------------------------
void GPIOBUS::SetMSG(BOOL ast)
void GPIOBUS::SetMSG(bool ast)
{
SetSignal(PIN_MSG, ast);
}
//---------------------------------------------------------------------------
//
// Get CD signal
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::GetCD()
bool GPIOBUS::GetCD() const
{
return GetSignal(PIN_CD);
}
//---------------------------------------------------------------------------
//
// Set CD Signal
//
//---------------------------------------------------------------------------
void GPIOBUS::SetCD(BOOL ast)
void GPIOBUS::SetCD(bool ast)
{
SetSignal(PIN_CD, ast);
}
//---------------------------------------------------------------------------
//
// Get IO Signal
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::GetIO()
bool GPIOBUS::GetIO()
{
BOOL ast = GetSignal(PIN_IO);
bool ast = GetSignal(PIN_IO);
if (actmode == INITIATOR) {
// Change the data input/output direction by IO signal
@ -685,12 +582,7 @@ BOOL GPIOBUS::GetIO()
return ast;
}
//---------------------------------------------------------------------------
//
// Set IO signal
//
//---------------------------------------------------------------------------
void GPIOBUS::SetIO(BOOL ast)
void GPIOBUS::SetIO(bool ast)
{
SetSignal(PIN_IO, ast);
@ -723,22 +615,12 @@ void GPIOBUS::SetIO(BOOL ast)
}
}
//---------------------------------------------------------------------------
//
// Get REQ signal
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::GetREQ()
bool GPIOBUS::GetREQ() const
{
return GetSignal(PIN_REQ);
}
//---------------------------------------------------------------------------
//
// Set REQ signal
//
//---------------------------------------------------------------------------
void GPIOBUS::SetREQ(BOOL ast)
void GPIOBUS::SetREQ(bool ast)
{
SetSignal(PIN_REQ, ast);
}
@ -750,7 +632,7 @@ void GPIOBUS::SetREQ(BOOL ast)
//---------------------------------------------------------------------------
BYTE GPIOBUS::GetDAT()
{
DWORD data = Aquire();
DWORD data = Acquire();
data =
((data >> (PIN_DT0 - 0)) & (1 << 0)) |
((data >> (PIN_DT1 - 1)) & (1 << 1)) |
@ -802,12 +684,7 @@ void GPIOBUS::SetDAT(BYTE dat)
#endif // SIGNAL_CONTROL_MODE
}
//---------------------------------------------------------------------------
//
// Get data parity signal
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::GetDP()
bool GPIOBUS::GetDP() const
{
return GetSignal(PIN_DP);
}
@ -817,26 +694,20 @@ BOOL GPIOBUS::GetDP()
// Receive command handshake
//
//---------------------------------------------------------------------------
int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi)
int GPIOBUS::CommandHandShake(BYTE *buf)
{
int count;
// Only works in TARGET mode
if (actmode != TARGET) {
return 0;
}
// IRQs disabled
DisableIRQ();
// Get the first command byte
int i = 0;
// Assert REQ signal
SetSignal(PIN_REQ, ON);
// Wait for ACK signal
BOOL ret = WaitSignal(PIN_ACK, TRUE);
bool ret = WaitSignal(PIN_ACK, TRUE);
// Wait until the signal line stabilizes
SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS);
@ -849,7 +720,8 @@ int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi)
// Timeout waiting for ACK assertion
if (!ret) {
goto irq_enable_exit;
EnableIRQ();
return 0;
}
// Wait for ACK to clear
@ -857,7 +729,8 @@ int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi)
// Timeout waiting for ACK to clear
if (!ret) {
goto irq_enable_exit;
EnableIRQ();
return 0;
}
// The ICD AdSCSI ST, AdSCSI Plus ST and AdSCSI Micro ST host adapters allow SCSI devices to be connected
@ -869,7 +742,7 @@ int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi)
// semantics. I fact, these semantics have become a standard in the Atari world.
// RaSCSI becomes ICD compatible by ignoring the prepended $1F byte before processing the CDB.
if (!is_sasi && *buf == 0x1F) {
if (*buf == 0x1F) {
SetSignal(PIN_REQ, ON);
ret = WaitSignal(PIN_ACK, TRUE);
@ -882,22 +755,25 @@ int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi)
SetSignal(PIN_REQ, OFF);
if (!ret) {
goto irq_enable_exit;
EnableIRQ();
return 0;
}
WaitSignal(PIN_ACK, FALSE);
if (!ret) {
goto irq_enable_exit;
EnableIRQ();
return 0;
}
}
count = GetCommandByteCount(*buf);
int command_byte_count = GetCommandByteCount(*buf);
// Increment buffer pointer
buf++;
for (i = 1; i < count; i++) {
int bytes_received;
for (bytes_received = 1; bytes_received < command_byte_count; bytes_received++) {
// Assert REQ signal
SetSignal(PIN_REQ, ON);
@ -930,12 +806,9 @@ int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi)
buf++;
}
irq_enable_exit:
// IRQs enabled
EnableIRQ();
// returned the number of bytes received
return i;
return bytes_received;
}
//---------------------------------------------------------------------------
@ -946,8 +819,6 @@ irq_enable_exit:
int GPIOBUS::ReceiveHandShake(BYTE *buf, int count)
{
int i;
BOOL ret;
DWORD phase;
// Disable IRQs
DisableIRQ();
@ -958,7 +829,7 @@ int GPIOBUS::ReceiveHandShake(BYTE *buf, int count)
SetSignal(PIN_REQ, ON);
// Wait for ACK
ret = WaitSignal(PIN_ACK, TRUE);
bool ret = WaitSignal(PIN_ACK, TRUE);
// Wait until the signal line stabilizes
SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS);
@ -987,11 +858,11 @@ int GPIOBUS::ReceiveHandShake(BYTE *buf, int count)
}
} else {
// Get phase
phase = Aquire() & GPIO_MCI;
DWORD phase = Acquire() & GPIO_MCI;
for (i = 0; i < count; i++) {
// Wait for the REQ signal to be asserted
ret = WaitSignal(PIN_REQ, TRUE);
bool ret = WaitSignal(PIN_REQ, TRUE);
// Check for timeout waiting for REQ signal
if (!ret) {
@ -1063,7 +934,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes)
SetDAT(*buf);
// Wait for ACK to clear
BOOL ret = WaitSignal(PIN_ACK, FALSE);
bool ret = WaitSignal(PIN_ACK, FALSE);
// Check for timeout waiting for ACK to clear
if (!ret) {
@ -1094,7 +965,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes)
WaitSignal(PIN_ACK, FALSE);
} else {
// Get Phase
DWORD phase = Aquire() & GPIO_MCI;
DWORD phase = Acquire() & GPIO_MCI;
for (i = 0; i < count; i++) {
if(i==delay_after_bytes){
@ -1106,7 +977,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes)
SetDAT(*buf);
// Wait for REQ to be asserted
BOOL ret = WaitSignal(PIN_REQ, TRUE);
bool ret = WaitSignal(PIN_REQ, TRUE);
// Check for timeout waiting for REQ to be asserted
if (!ret) {
@ -1157,7 +1028,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes)
// SEL signal event polling
//
//---------------------------------------------------------------------------
int GPIOBUS::PollSelectEvent()
bool GPIOBUS::PollSelectEvent()
{
// clear errno
errno = 0;
@ -1166,15 +1037,15 @@ int GPIOBUS::PollSelectEvent()
if (epoll_wait(epfd, &epev, 1, -1) <= 0) {
LOGWARN("%s epoll_wait failed", __PRETTY_FUNCTION__);
return -1;
return false;
}
if (read(selevreq.fd, &gpev, sizeof(gpev)) < 0) {
LOGWARN("%s read failed", __PRETTY_FUNCTION__);
return -1;
return false;
}
return 0;
return true;
}
//---------------------------------------------------------------------------
@ -1306,7 +1177,7 @@ void GPIOBUS::MakeTable(void)
// Control signal setting
//
//---------------------------------------------------------------------------
void GPIOBUS::SetControl(int pin, BOOL ast)
void GPIOBUS::SetControl(int pin, bool ast)
{
PinSetSignal(pin, ast);
}
@ -1340,7 +1211,7 @@ void GPIOBUS::SetMode(int pin, int mode)
// Get input signal value
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::GetSignal(int pin)
bool GPIOBUS::GetSignal(int pin) const
{
return (signals >> pin) & 1;
}
@ -1350,7 +1221,7 @@ BOOL GPIOBUS::GetSignal(int pin)
// Set output signal value
//
//---------------------------------------------------------------------------
void GPIOBUS::SetSignal(int pin, BOOL ast)
void GPIOBUS::SetSignal(int pin, bool ast)
{
#if SIGNAL_CONTROL_MODE == 0
int index = pin / 10;
@ -1383,7 +1254,7 @@ void GPIOBUS::SetSignal(int pin, BOOL ast)
// Wait for signal change
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::WaitSignal(int pin, BOOL ast)
bool GPIOBUS::WaitSignal(int pin, BOOL ast)
{
// Get current time
DWORD now = SysTimer::GetTimerLow();
@ -1394,26 +1265,21 @@ BOOL GPIOBUS::WaitSignal(int pin, BOOL ast)
// end immediately if the signal has changed
do {
// Immediately upon receiving a reset
Aquire();
Acquire();
if (GetRST()) {
return FALSE;
return false;
}
// Check for the signal edge
if (((signals >> pin) ^ ~ast) & 1) {
return TRUE;
return true;
}
} while ((SysTimer::GetTimerLow() - now) < timeout);
// We timed out waiting for the signal
return FALSE;
return false;
}
//---------------------------------------------------------------------------
//
// Disable IRQ
//
//---------------------------------------------------------------------------
void GPIOBUS::DisableIRQ()
{
#ifdef __linux__
@ -1436,11 +1302,6 @@ void GPIOBUS::DisableIRQ()
#endif
}
//---------------------------------------------------------------------------
//
// Enable IRQ
//
//---------------------------------------------------------------------------
void GPIOBUS::EnableIRQ()
{
if (rpitype == 4) {
@ -1523,7 +1384,7 @@ void GPIOBUS::PullConfig(int pin, int mode)
// Set output pin
//
//---------------------------------------------------------------------------
void GPIOBUS::PinSetSignal(int pin, BOOL ast)
void GPIOBUS::PinSetSignal(int pin, bool ast)
{
// Check for invalid pin
if (pin < 0) {
@ -1580,9 +1441,7 @@ BUS::phase_t GPIOBUS::GetPhaseRaw(DWORD raw_data)
//---------------------------------------------------------------------------
//
// Get the number of bytes for a command
//
// TODO The command length should be determined based on the bytes transferred in the COMMAND phase
// Get the number of bytes for a command
//
//---------------------------------------------------------------------------
int GPIOBUS::GetCommandByteCount(BYTE opcode) {

View File

@ -473,14 +473,13 @@ class GPIOBUS : public BUS
public:
// Basic Functions
GPIOBUS();
// Constructor
virtual ~GPIOBUS();
virtual ~GPIOBUS() {}
// Destructor
BOOL Init(mode_e mode = TARGET);
bool Init(mode_e mode = TARGET) override;
// Initialization
void Reset();
void Reset() override;
// Reset
void Cleanup();
void Cleanup() override;
// Cleanup
//---------------------------------------------------------------------------
@ -488,7 +487,7 @@ public:
// Bus signal acquisition
//
//---------------------------------------------------------------------------
inline DWORD Aquire() override
inline DWORD Acquire() override
{
#if defined(__x86_64__) || defined(__X86__)
// Only used for development/debugging purposes. Isn't really applicable
@ -506,66 +505,66 @@ public:
#endif // ifdef __x86_64__ || __X86__
}
void SetENB(BOOL ast);
void SetENB(bool ast);
// Set ENB signal
bool GetBSY() override;
bool GetBSY() const override;
// Get BSY signal
void SetBSY(bool ast) override;
// Set BSY signal
BOOL GetSEL() override;
bool GetSEL() const override;
// Get SEL signal
void SetSEL(BOOL ast) override;
void SetSEL(bool ast) override;
// Set SEL signal
BOOL GetATN() override;
bool GetATN() const override;
// Get ATN signal
void SetATN(BOOL ast) override;
void SetATN(bool ast) override;
// Set ATN signal
BOOL GetACK() override;
bool GetACK() const override;
// Get ACK signal
void SetACK(BOOL ast) override;
void SetACK(bool ast) override;
// Set ACK signal
BOOL GetACT();
bool GetACT() const;
// Get ACT signal
void SetACT(BOOL ast);
void SetACT(bool ast);
// Set ACT signal
BOOL GetRST() override;
bool GetRST() const override;
// Get RST signal
void SetRST(BOOL ast) override;
void SetRST(bool ast) override;
// Set RST signal
BOOL GetMSG() override;
bool GetMSG() const override;
// Get MSG signal
void SetMSG(BOOL ast) override;
void SetMSG(bool ast) override;
// Set MSG signal
BOOL GetCD() override;
bool GetCD() const override;
// Get CD signal
void SetCD(BOOL ast) override;
void SetCD(bool ast) override;
// Set CD signal
BOOL GetIO() override;
bool GetIO() override;
// Get IO signal
void SetIO(BOOL ast) override;
void SetIO(bool ast) override;
// Set IO signal
BOOL GetREQ() override;
bool GetREQ() const override;
// Get REQ signal
void SetREQ(BOOL ast) override;
void SetREQ(bool ast) override;
// Set REQ signal
BYTE GetDAT() override;
// Get DAT signal
void SetDAT(BYTE dat) override;
// Set DAT signal
BOOL GetDP() override;
bool GetDP() const override;
// Get Data parity signal
int CommandHandShake(BYTE *buf, bool) override;
int CommandHandShake(BYTE *buf) override;
// Command receive handshake
int ReceiveHandShake(BYTE *buf, int count) override;
// Data receive handshake
@ -579,7 +578,7 @@ public:
#ifdef USE_SEL_EVENT_ENABLE
// SEL signal interrupt
int PollSelectEvent();
bool PollSelectEvent();
// SEL signal event polling
void ClearSelectEvent();
// Clear SEL signal event
@ -589,15 +588,15 @@ private:
// SCSI I/O signal control
void MakeTable();
// Create work data
void SetControl(int pin, BOOL ast);
void SetControl(int pin, bool ast);
// Set Control Signal
void SetMode(int pin, int mode);
// Set SCSI I/O mode
BOOL GetSignal(int pin);
bool GetSignal(int pin) const override;
// Get SCSI input signal value
void SetSignal(int pin, BOOL ast);
void SetSignal(int pin, bool ast) override;
// Set SCSI output signal value
BOOL WaitSignal(int pin, BOOL ast);
bool WaitSignal(int pin, BOOL ast);
// Wait for a signal to change
// Interrupt control
void DisableIRQ();
@ -610,43 +609,43 @@ private:
// GPIO pin direction setting
void PullConfig(int pin, int mode);
// GPIO pin pull up/down resistor setting
void PinSetSignal(int pin, BOOL ast);
void PinSetSignal(int pin, bool ast);
// Set GPIO output signal
void DrvConfig(DWORD drive);
// Set GPIO drive strength
mode_e actmode; // Operation mode
mode_e actmode = TARGET; // Operation mode
DWORD baseaddr; // Base address
uint32_t baseaddr = 0; // Base address
int rpitype; // Type of Raspberry Pi
int rpitype = 0; // Type of Raspberry Pi
volatile DWORD *gpio; // GPIO register
volatile uint32_t *gpio = 0; // GPIO register
volatile DWORD *pads; // PADS register
volatile uint32_t *pads = 0; // PADS register
volatile DWORD *level; // GPIO input level
volatile uint32_t *level = 0; // GPIO input level
volatile DWORD *irpctl; // Interrupt control register
volatile uint32_t *irpctl = 0; // Interrupt control register
volatile DWORD irptenb; // Interrupt enabled state
volatile uint32_t irptenb; // Interrupt enabled state
volatile DWORD *qa7regs; // QA7 register
volatile uint32_t *qa7regs = 0; // QA7 register
volatile int tintcore; // Interupt control target CPU.
volatile DWORD tintctl; // Interupt control
volatile uint32_t tintctl; // Interupt control
volatile DWORD giccpmr; // GICC priority setting
volatile uint32_t giccpmr; // GICC priority setting
volatile DWORD *gicd; // GIC Interrupt distributor register
volatile uint32_t *gicd = 0; // GIC Interrupt distributor register
volatile DWORD *gicc; // GIC CPU interface register
volatile uint32_t *gicc = 0; // GIC CPU interface register
DWORD gpfsel[4]; // GPFSEL0-4 backup values
DWORD signals; // All bus signals
uint32_t signals = 0; // All bus signals
#ifdef USE_SEL_EVENT_ENABLE
struct gpioevent_request selevreq = {}; // SEL signal event request

View File

@ -17,9 +17,6 @@ using namespace std;
Localizer::Localizer()
{
// Supported locales, always lower case
supported_languages = { "en", "de", "sv", "fr", "es" };
// Positional string arguments are %1, %2, %3
Add(ERROR_AUTHENTICATION, "en", "Authentication failed");
Add(ERROR_AUTHENTICATION, "de", "Authentifizierung fehlgeschlagen");
@ -81,11 +78,6 @@ Localizer::Localizer()
Add(ERROR_DUPLICATE_ID, "sv", "Duplikat ID %1, LUN %2");
Add(ERROR_DUPLICATE_ID, "fr", "ID %1, unité %2 dupliquée");
Add(ERROR_DUPLICATE_ID, "es", "ID duplicado %1, unidad %2");
Add(ERROR_SASI_SCSI, "en", "SASI and SCSI can't be used at the same time");
Add(ERROR_SASI_SCSI, "de", "SASI und SCSI können nicht gleichzeitig verwendet werden");
Add(ERROR_SASI_SCSI, "sv", "SASI och SCSI kan ej användas samtidigt");
Add(ERROR_SASI_SCSI, "fr", "SASI et SCSI ne peuvent être utilisés en même temps");
Add(ERROR_SASI_SCSI, "es", "SASI y SCSI no pueden utilizarse al mismo tiempo");
Add(ERROR_EJECT_REQUIRED, "en", "Existing medium must first be ejected");
Add(ERROR_EJECT_REQUIRED, "de", "Das vorhandene Medium muss erst ausgeworfen werden");
Add(ERROR_EJECT_REQUIRED, "sv", "Nuvarande skiva måste utmatas först");

View File

@ -30,7 +30,6 @@ enum LocalizationKey {
ERROR_UNKNOWN_DEVICE_TYPE,
ERROR_MISSING_DEVICE_TYPE,
ERROR_DUPLICATE_ID,
ERROR_SASI_SCSI,
ERROR_EJECT_REQUIRED,
ERROR_DEVICE_NAME_UPDATE,
ERROR_SHUTDOWN_MODE_MISSING,
@ -55,5 +54,6 @@ private:
void Add(LocalizationKey, const string&, const string&);
unordered_map<string, unordered_map<LocalizationKey, string>> localized_messages;
unordered_set<string> supported_languages;
// Supported locales, always lower case
unordered_set<string> supported_languages = { "en", "de", "sv", "fr", "es" };
};

View File

@ -75,14 +75,6 @@
#endif // NDEBUG
#endif // ASSERT
#if !defined(ASSERT_DIAG)
#if !defined(NDEBUG)
#define ASSERT_DIAG() AssertDiag()
#else
#define ASSERT_DIAG() ((void)0)
#endif // NDEBUG
#endif // ASSERT_DIAG
#define ARRAY_SIZE(x) (sizeof(x)/(sizeof(x[0])))
//---------------------------------------------------------------------------

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
@ -12,7 +12,7 @@
#include "log.h"
#include "rascsi_interface.pb.h"
#include "localizer.h"
#include "exceptions.h"
#include "rascsi_exceptions.h"
#include "protobuf_util.h"
using namespace std;
@ -115,17 +115,17 @@ void protobuf_util::DeserializeMessage(int fd, google::protobuf::Message& messag
{
// Read the header with the size of the protobuf data
uint8_t header_buf[4];
int bytes_read = ReadNBytes(fd, header_buf, sizeof(header_buf));
if (bytes_read < (int)sizeof(header_buf)) {
size_t bytes_read = ReadNBytes(fd, header_buf, sizeof(header_buf));
if (bytes_read < sizeof(header_buf)) {
return;
}
int32_t size = (header_buf[3] << 24) + (header_buf[2] << 16) + (header_buf[1] << 8) + header_buf[0];
size_t size = (header_buf[3] << 24) + (header_buf[2] << 16) + (header_buf[1] << 8) + header_buf[0];
if (size <= 0) {
throw io_exception("Broken protobuf message header");
}
// Read the binary protobuf data
uint8_t data_buf[size];
uint8_t *data_buf = new uint8_t[size];
bytes_read = ReadNBytes(fd, data_buf, size);
if (bytes_read < size) {
throw io_exception("Missing protobuf message data");
@ -133,16 +133,17 @@ void protobuf_util::DeserializeMessage(int fd, google::protobuf::Message& messag
// Create protobuf message
string data((const char *)data_buf, size);
delete[] data_buf;
message.ParseFromString(data);
}
int protobuf_util::ReadNBytes(int fd, uint8_t *buf, int n)
size_t protobuf_util::ReadNBytes(int fd, uint8_t *buf, size_t n)
{
int offset = 0;
size_t offset = 0;
while (offset < n) {
ssize_t len = read(fd, buf + offset, n - offset);
if (!len) {
break;
if (len <= 0) {
return len;
}
offset += len;

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
// Helper methods for serializing/deserializing protobuf messages
//
@ -31,7 +31,7 @@ namespace protobuf_util
void AddParam(PbDeviceDefinition&, const string&, const string&);
void SerializeMessage(int, const google::protobuf::Message&);
void DeserializeMessage(int, google::protobuf::Message&);
int ReadNBytes(int, uint8_t *, int);
size_t ReadNBytes(int, uint8_t *, size_t);
bool ReturnLocalizedError(const CommandContext&, const LocalizationKey, const string& = "", const string& = "",
const string& = "");
bool ReturnLocalizedError(const CommandContext&, const LocalizationKey, const PbErrorCode, const string& = "",

View File

@ -5,19 +5,20 @@
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2020-2021 Contributors to the RaSCSI project
// Copyright (C) 2020-2022 Contributors to the RaSCSI project
// [ RaSCSI main ]
//
//---------------------------------------------------------------------------
#include "config.h"
#include "os.h"
#include "controllers/sasidev_ctrl.h"
#include "controllers/controller_manager.h"
#include "devices/device_factory.h"
#include "devices/device.h"
#include "devices/disk.h"
#include "devices/file_support.h"
#include "gpiobus.h"
#include "exceptions.h"
#include "rascsi_exceptions.h"
#include "protobuf_util.h"
#include "rascsi_version.h"
#include "rascsi_response.h"
@ -31,9 +32,7 @@
#include <iostream>
#include <fstream>
#include <list>
#include <vector>
#include <map>
#include "config.h"
using namespace std;
using namespace spdlog;
@ -46,10 +45,8 @@ using namespace protobuf_util;
// Constant declarations
//
//---------------------------------------------------------------------------
#define CtrlMax 8 // Maximum number of SCSI controllers
#define UnitNum SASIDEV::UnitMax // Number of units around controller
#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
#define DEFAULT_PORT 6868
#define COMPONENT_SEPARATOR ':'
//---------------------------------------------------------------------------
@ -59,8 +56,6 @@ using namespace protobuf_util;
//---------------------------------------------------------------------------
static volatile bool running; // Running flag
static volatile bool active; // Processing flag
vector<SASIDEV *> controllers(CtrlMax); // Controllers
vector<Device *> devices(CtrlMax * UnitNum); // Disks
GPIOBUS *bus; // GPIO Bus
int monsocket; // Monitor Socket
pthread_t monthread; // Monitor Thread
@ -70,15 +65,17 @@ string current_log_level; // Some versions of spdlog do not support get_log_le
string access_token;
unordered_set<int> reserved_ids;
DeviceFactory& device_factory = DeviceFactory::instance();
ControllerManager&controller_manager = ControllerManager::instance();
RascsiImage rascsi_image;
RascsiResponse rascsi_response(&device_factory, &rascsi_image);
void DetachAll();
//---------------------------------------------------------------------------
//
// Signal Processing
//
//---------------------------------------------------------------------------
void KillHandler(int sig)
void KillHandler(int)
{
// Stop instruction
running = false;
@ -99,21 +96,18 @@ void Banner(int argc, char* argv[])
FPRT(stdout,"Powered by XM6 TypeG Technology / ");
FPRT(stdout,"Copyright (C) 2016-2020 GIMONS\n");
FPRT(stdout,"Copyright (C) 2020-2022 Contributors to the RaSCSI Reloaded project\n");
FPRT(stdout,"Connect type : %s\n", CONNECT_DESC);
FPRT(stdout,"Connect type: %s\n", CONNECT_DESC);
if ((argc > 1 && strcmp(argv[1], "-h") == 0) ||
(argc > 1 && strcmp(argv[1], "--help") == 0)){
FPRT(stdout,"\n");
FPRT(stdout,"Usage: %s [-IDn FILE] ...\n\n", argv[0]);
FPRT(stdout," n is SCSI identification number(0-7).\n");
FPRT(stdout," FILE is disk image file.\n\n");
FPRT(stdout,"Usage: %s [-HDn FILE] ...\n\n", argv[0]);
FPRT(stdout," n is X68000 SASI HD number(0-15).\n");
FPRT(stdout," FILE is disk image file, \"daynaport\", \"bridge\", \"printer\" or \"services\".\n\n");
FPRT(stdout," Image type is detected based on file extension.\n");
FPRT(stdout," hdf : SASI HD image (XM6 SASI HD image)\n");
FPRT(stdout," hds : SCSI HD image (Non-removable generic SCSI HD image)\n");
FPRT(stdout," hdr : SCSI HD image (Removable generic SCSI HD image)\n");
FPRT(stdout,"Usage: %s [-idn[:m] FILE] ...\n\n", argv[0]);
FPRT(stdout," n is SCSI device ID (0-7).\n");
FPRT(stdout," m is the optional logical unit (LUN) (0-31).\n");
FPRT(stdout," FILE is a disk image file, \"daynaport\", \"bridge\", \"printer\" or \"services\".\n\n");
FPRT(stdout," Image type is detected based on file extension if no explicit type is specified.\n");
FPRT(stdout," hds : SCSI HD image (Non-removable generic HD image)\n");
FPRT(stdout," hdr : SCSI HD image (Removable generic HD image)\n");
FPRT(stdout," hdn : SCSI HD image (NEC GENUINE)\n");
FPRT(stdout," hdi : SCSI HD image (Anex86 HD image)\n");
FPRT(stdout," nhd : SCSI HD image (T98Next HD image)\n");
@ -132,7 +126,7 @@ void Banner(int argc, char* argv[])
bool InitService(int port)
{
int result = pthread_mutex_init(&ctrl_mutex,NULL);
int result = pthread_mutex_init(&ctrl_mutex, NULL);
if (result != EXIT_SUCCESS){
LOGERROR("Unable to create a mutex. Error code: %d", result);
return false;
@ -143,7 +137,7 @@ bool InitService(int port)
monsocket = socket(PF_INET, SOCK_STREAM, 0);
memset(&server, 0, sizeof(server));
server.sin_family = PF_INET;
server.sin_port = htons(port);
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
// allow address reuse
@ -157,7 +151,7 @@ bool InitService(int port)
// Bind
if (bind(monsocket, (struct sockaddr *)&server,
sizeof(struct sockaddr_in)) < 0) {
FPRT(stderr, "Error: Already running?\n");
FPRT(stderr, "Error: Port %d is in use, is rascsi already running?\n", port);
return false;
}
@ -188,6 +182,7 @@ bool InitBus()
// GPIO Initialization
if (!bus->Init()) {
delete bus;
return false;
}
@ -197,34 +192,13 @@ bool InitBus()
return true;
}
//---------------------------------------------------------------------------
//
// Cleanup
//
//---------------------------------------------------------------------------
void Cleanup()
{
// Delete the disks
for (auto it = devices.begin(); it != devices.end(); ++it) {
if (*it) {
delete *it;
*it = NULL;
}
}
DetachAll();
// Delete the Controllers
for (auto it = controllers.begin(); it != controllers.end(); ++it) {
if (*it) {
delete *it;
*it = NULL;
}
}
// Cleanup the Bus
// Clean up and discard the bus
if (bus) {
bus->Cleanup();
// Discard the GPIOBUS object
delete bus;
}
@ -236,144 +210,13 @@ void Cleanup()
pthread_mutex_destroy(&ctrl_mutex);
}
//---------------------------------------------------------------------------
//
// Reset
//
//---------------------------------------------------------------------------
void Reset()
{
// Reset all of the controllers
for (const auto& controller : controllers) {
if (controller) {
controller->Reset();
}
}
controller_manager.ResetAllControllers();
// Reset the bus
bus->Reset();
}
//---------------------------------------------------------------------------
//
// Controller Mapping
//
//---------------------------------------------------------------------------
bool MapController(Device **map)
{
assert(bus);
bool status = true;
// Take ownership of the ctrl data structure
pthread_mutex_lock(&ctrl_mutex);
// Replace the changed unit
for (size_t i = 0; i < controllers.size(); i++) {
for (int j = 0; j < UnitNum; j++) {
int unitno = i * UnitNum + j;
if (devices[unitno] != map[unitno]) {
// Check if the original unit exists
if (devices[unitno]) {
// Disconnect it from the controller
if (controllers[i]) {
controllers[i]->SetUnit(j, NULL);
}
// Free the Unit
delete devices[unitno];
}
// Setup a new unit
devices[unitno] = map[unitno];
}
}
}
// Reconfigure all of the controllers
int i = 0;
for (auto it = controllers.begin(); it != controllers.end(); ++i, ++it) {
// Examine the unit configuration
int sasi_num = 0;
int scsi_num = 0;
for (int j = 0; j < UnitNum; j++) {
int unitno = i * UnitNum + j;
// branch by unit type
if (devices[unitno]) {
if (devices[unitno]->IsSASIHD()) {
// Drive is SASI, so increment SASI count
sasi_num++;
} else {
// Drive is SCSI, so increment SCSI count
scsi_num++;
}
}
// Remove the unit
if (*it) {
(*it)->SetUnit(j, NULL);
}
}
// If there are no units connected
if (!sasi_num && !scsi_num) {
if (*it) {
delete *it;
*it = NULL;
continue;
}
}
// Mixture of SCSI and SASI
if (sasi_num > 0 && scsi_num > 0) {
status = false;
continue;
}
if (sasi_num > 0) {
// Only SASI Unit(s)
// Release the controller if it is not SASI
if (*it && !(*it)->IsSASI()) {
delete *it;
*it = NULL;
}
// Create a new SASI controller
if (!*it) {
*it = new SASIDEV();
(*it)->Connect(i, bus);
}
} else {
// Only SCSI Unit(s)
// Release the controller if it is not SCSI
if (*it && !(*it)->IsSCSI()) {
delete *it;
*it = NULL;
}
// Create a new SCSI controller
if (!*it) {
*it = new SCSIDEV();
(*it)->Connect(i, bus);
}
}
// connect all units
for (int j = 0; j < UnitNum; j++) {
int unitno = i * UnitNum + j;
if (devices[unitno]) {
// Add the unit connection
(*it)->SetUnit(j, (static_cast<Disk *>(devices[unitno])));
}
}
}
pthread_mutex_unlock(&ctrl_mutex);
return status;
}
bool ReadAccessToken(const char *filename)
{
struct stat st;
@ -411,21 +254,19 @@ bool ReadAccessToken(const char *filename)
return true;
}
string ValidateLunSetup(const PbCommand& command, const vector<Device *>& existing_devices)
string ValidateLunSetup(const PbCommand& command)
{
// Mapping of available LUNs (bit vector) to devices
map<uint32_t, uint32_t> luns;
// Collect LUN vectors of new devices
// Collect LUN bit vectors of new devices
for (const auto& device : command.devices()) {
luns[device.id()] |= 1 << device.unit();
}
// Collect LUN vectors of existing devices
for (auto const& device : existing_devices) {
if (device) {
luns[device->GetId()] |= 1 << device->GetLun();
}
// Collect LUN bit vectors of existing devices
for (const Device *device : device_factory.GetAllDevices()) {
luns[device->GetId()] |= 1 << device->GetLun();
}
// LUN 0 must exist for all devices
@ -500,7 +341,7 @@ string SetReservedIds(const string& ids)
return "Invalid ID " + id_to_reserve;
}
if (devices[id * UnitNum]) {
if (controller_manager.FindController(id) != nullptr) {
return "ID " + id_to_reserve + " is currently in use";
}
@ -523,7 +364,7 @@ string SetReservedIds(const string& ids)
LOGINFO("Reserved ID(s) set to %s", s.c_str());
}
else {
LOGINFO("Cleared reserved IDs");
LOGINFO("Cleared reserved ID(s)");
}
return "";
@ -531,33 +372,30 @@ string SetReservedIds(const string& ids)
void DetachAll()
{
Device *map[devices.size()];
for (size_t i = 0; i < devices.size(); i++) {
map[i] = NULL;
}
controller_manager.DeleteAllControllersAndDevices();
if (MapController(map)) {
LOGINFO("Detached all devices");
}
FileSupport::UnreserveAll();
LOGINFO("Detached all devices");
}
bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device, Device *map[], bool dryRun)
bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device, bool dryRun)
{
const int id = pb_device.id();
const int unit = pb_device.unit();
const PbDeviceType type = pb_device.type();
if (map[id * UnitNum + unit]) {
if (controller_manager.GetDeviceByIdAndLun(id, unit) != nullptr) {
return ReturnLocalizedError(context, ERROR_DUPLICATE_ID, to_string(id), to_string(unit));
}
if (unit >= AbstractController::LUN_MAX) {
return ReturnStatus(context, false, "Invalid unit " + to_string(unit) + " (0-" + to_string(AbstractController::LUN_MAX)
+ ")");
}
string filename = GetParam(pb_device, "file");
// Create a new device, based on the provided type or filename
Device *device = device_factory.CreateDevice(type, filename);
if (!device) {
Device *device = device_factory.CreateDevice(type, filename, id);
if (device == nullptr) {
if (type == UNDEFINED) {
return ReturnLocalizedError(context, ERROR_MISSING_DEVICE_TYPE, filename);
}
@ -566,30 +404,15 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
}
}
int supported_luns = device->GetSupportedLuns();
if (unit >= supported_luns) {
delete device;
string error = "Invalid unit " + to_string(unit) + " for device type " + PbDeviceType_Name(type);
if (supported_luns == 1) {
error += " (0)";
}
else {
error += " (0-" + to_string(supported_luns -1) + ")";
}
return ReturnStatus(context, false, error);
}
// If no filename was provided the medium is considered removed
FileSupport *file_support = dynamic_cast<FileSupport *>(device);
if (file_support) {
if (file_support != nullptr) {
device->SetRemoved(filename.empty());
}
else {
device->SetRemoved(false);
}
device->SetId(id);
device->SetLun(unit);
try {
@ -604,28 +427,28 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
}
}
catch(const illegal_argument_exception& e) {
return ReturnStatus(context, false, e.getmsg());
return ReturnStatus(context, false, e.get_msg());
}
if (pb_device.block_size()) {
Disk *disk = dynamic_cast<Disk *>(device);
if (disk && disk->IsSectorSizeConfigurable()) {
if (disk != nullptr && disk->IsSectorSizeConfigurable()) {
if (!disk->SetConfiguredSectorSize(pb_device.block_size())) {
delete device;
device_factory.DeleteDevice(device);
return ReturnLocalizedError(context, ERROR_BLOCK_SIZE, to_string(pb_device.block_size()));
}
}
else {
delete device;
device_factory.DeleteDevice(device);
return ReturnLocalizedError(context, ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, PbDeviceType_Name(type));
}
}
// File check (type is HD, for removable media drives, CD and MO the medium (=file) may be inserted later
if (file_support && !device->IsRemovable() && filename.empty()) {
delete device;
if (file_support != nullptr && !device->IsRemovable() && filename.empty()) {
device_factory.DeleteDevice(device);
return ReturnStatus(context, false, "Device type " + PbDeviceType_Name(type) + " requires a filename");
}
@ -638,7 +461,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
int id;
int unit;
if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) {
delete device;
device_factory.DeleteDevice(device);
return ReturnLocalizedError(context, ERROR_IMAGE_IN_USE, filename, to_string(id), to_string(unit));
}
@ -652,7 +475,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
filepath.SetPath(string(rascsi_image.GetDefaultImageFolder() + "/" + filename).c_str());
if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) {
delete device;
device_factory.DeleteDevice(device);
return ReturnLocalizedError(context, ERROR_IMAGE_IN_USE, filename, to_string(id), to_string(unit));
}
@ -661,9 +484,9 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
}
}
catch(const io_exception& e) {
delete device;
device_factory.DeleteDevice(device);
return ReturnLocalizedError(context, ERROR_FILE_OPEN, initial_filename, e.getmsg());
return ReturnLocalizedError(context, ERROR_FILE_OPEN, initial_filename, e.get_msg());
}
file_support->ReserveFile(filepath, device->GetId(), device->GetLun());
@ -677,7 +500,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
// Stop the dry run here, before permanently modifying something
if (dryRun) {
delete device;
device_factory.DeleteDevice(device);
return true;
}
@ -687,56 +510,68 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
params.erase("file");
}
if (!device->Init(params)) {
delete device;
device_factory.DeleteDevice(device);
return ReturnStatus(context, false, "Initialization of " + PbDeviceType_Name(type) + " device, ID " +to_string(id) +
", unit " +to_string(unit) + " failed");
}
// Replace with the newly created unit
map[id * UnitNum + unit] = device;
pthread_mutex_lock(&ctrl_mutex);
PrimaryDevice *primary_device = static_cast<PrimaryDevice *>(device);
if (!controller_manager.CreateScsiController(bus, primary_device)) {
pthread_mutex_unlock(&ctrl_mutex);
// Re-map the controller
if (MapController(map)) {
string msg = "Attached ";
if (device->IsReadOnly()) {
msg += "read-only ";
}
else if (device->IsProtectable() && device->IsProtected()) {
msg += "protected ";
}
msg += device->GetType() + " device, ID " + to_string(id) + ", unit " + to_string(unit);
LOGINFO("%s", msg.c_str());
return true;
return ReturnStatus(context, false, "Couldn't create SCSI controller instance");
}
pthread_mutex_unlock(&ctrl_mutex);
return ReturnLocalizedError(context, ERROR_SASI_SCSI);
string msg = "Attached ";
if (device->IsReadOnly()) {
msg += "read-only ";
}
else if (device->IsProtectable() && device->IsProtected()) {
msg += "protected ";
}
msg += device->GetType() + " device, ID " + to_string(id) + ", unit " + to_string(unit);
LOGINFO("%s", msg.c_str());
return true;
}
bool Detach(const CommandContext& context, Device *device, Device *map[], bool dryRun)
bool Detach(const CommandContext& context, PrimaryDevice *device, bool dryRun)
{
if (!device->GetLun()) {
for (auto const& d : devices) {
// LUN 0 can only be detached if there is no other lUN anymore
if (d && d->GetId() == device->GetId() && d->GetLun()) {
for (const Device *d : device_factory.GetAllDevices()) {
// LUN 0 can only be detached if there is no other LUN anymore
if (d->GetId() == device->GetId() && d->GetLun()) {
return ReturnStatus(context, false, "LUN 0 cannot be detached as long as there is still another LUN");
}
}
}
if (!dryRun) {
map[device->GetId() * UnitNum + device->GetLun()] = NULL;
// Remember data that going to be deleted but are used for logging
int id = device->GetId();
int lun = device->GetLun();
string type = device->GetType();
FileSupport *file_support = dynamic_cast<FileSupport *>(device);
if (file_support) {
if (file_support != nullptr) {
file_support->UnreserveFile();
}
LOGINFO("Detached %s device with ID %d, unit %d", device->GetType().c_str(), device->GetId(), device->GetLun());
// Delete the existing unit
pthread_mutex_lock(&ctrl_mutex);
if (!controller_manager.FindController(id)->DeleteDevice(device)) {
pthread_mutex_unlock(&ctrl_mutex);
// Re-map the controller
MapController(map);
return ReturnStatus(context, false, "Couldn't detach device");
}
device_factory.DeleteDevice(device);
pthread_mutex_unlock(&ctrl_mutex);
LOGINFO("Detached %s device with ID %d, unit %d", type.c_str(), id, lun);
}
return true;
@ -767,7 +602,7 @@ bool Insert(const CommandContext& context, const PbDeviceDefinition& pb_device,
Disk *disk = dynamic_cast<Disk *>(device);
if (pb_device.block_size()) {
if (disk && disk->IsSectorSizeConfigurable()) {
if (disk != nullptr&& disk->IsSectorSizeConfigurable()) {
if (!disk->SetConfiguredSectorSize(pb_device.block_size())) {
return ReturnLocalizedError(context, ERROR_BLOCK_SIZE, to_string(pb_device.block_size()));
}
@ -804,7 +639,7 @@ bool Insert(const CommandContext& context, const PbDeviceDefinition& pb_device,
}
}
catch(const io_exception& e) {
return ReturnLocalizedError(context, ERROR_FILE_OPEN, initial_filename, e.getmsg());
return ReturnLocalizedError(context, ERROR_FILE_OPEN, initial_filename, e.get_msg());
}
file_support->ReserveFile(filepath, device->GetId(), device->GetLun());
@ -815,7 +650,7 @@ bool Insert(const CommandContext& context, const PbDeviceDefinition& pb_device,
device->SetProtected(pb_device.protected_());
}
if (disk) {
if (disk != nullptr) {
disk->MediumChanged();
}
@ -824,8 +659,6 @@ bool Insert(const CommandContext& context, const PbDeviceDefinition& pb_device,
void TerminationHandler(int signum)
{
DetachAll();
Cleanup();
exit(signum);
@ -885,8 +718,9 @@ bool ProcessCmd(const CommandContext& context, const PbDeviceDefinition& pb_devi
if (id < 0) {
return ReturnLocalizedError(context, ERROR_MISSING_DEVICE_ID);
}
if (id >= CtrlMax) {
return ReturnStatus(context, false, "Invalid device ID " + to_string(id) + " (0-" + to_string(CtrlMax - 1) + ")");
if (id >= ControllerManager::DEVICE_MAX) {
return ReturnStatus(context, false, "Invalid device ID " + to_string(id) + " (0-"
+ to_string(ControllerManager::DEVICE_MAX - 1) + ")");
}
if (operation == ATTACH && reserved_ids.find(id) != reserved_ids.end()) {
@ -894,33 +728,27 @@ bool ProcessCmd(const CommandContext& context, const PbDeviceDefinition& pb_devi
}
// Check the Unit Number
if (unit < 0 || unit >= UnitNum) {
return ReturnStatus(context, false, "Invalid unit " + to_string(unit) + " (0-" + to_string(UnitNum - 1) + ")");
}
// Copy the devices
Device *map[devices.size()];
for (size_t i = 0; i < devices.size(); i++) {
map[i] = devices[i];
if (unit < 0 || unit >= AbstractController::LUN_MAX) {
return ReturnStatus(context, false, "Invalid unit " + to_string(unit) + " (0-" + to_string(AbstractController::LUN_MAX - 1) + ")");
}
if (operation == ATTACH) {
return Attach(context, pb_device, map, dryRun);
return Attach(context, pb_device, dryRun);
}
// Does the controller exist?
if (!dryRun && !controllers[id]) {
if (!dryRun && controller_manager.FindController(id) == nullptr) {
return ReturnLocalizedError(context, ERROR_NON_EXISTING_DEVICE, to_string(id));
}
// Does the unit exist?
Device *device = devices[id * UnitNum + unit];
if (!device) {
PrimaryDevice *device = controller_manager.GetDeviceByIdAndLun(id, unit);
if (device == nullptr) {
return ReturnLocalizedError(context, ERROR_NON_EXISTING_UNIT, to_string(id), to_string(unit));
}
if (operation == DETACH) {
return Detach(context, device, map, dryRun);
return Detach(context, device, dryRun);
}
if ((operation == START || operation == STOP) && !device->IsStoppable()) {
@ -1059,7 +887,7 @@ bool ProcessCmd(const CommandContext& context, const PbCommand& command)
// Restore the list of reserved files before proceeding
FileSupport::SetReservedFiles(reserved_files);
string result = ValidateLunSetup(command, devices);
string result = ValidateLunSetup(command);
if (!result.empty()) {
return ReturnStatus(context, false, result);
}
@ -1075,7 +903,7 @@ bool ProcessCmd(const CommandContext& context, const PbCommand& command)
// A new command with an empty device list is required here in order to return data for all devices
PbCommand command;
PbResult result;
rascsi_response.GetDevicesInfo(result, command, devices, UnitNum);
rascsi_response.GetDevicesInfo(result, command);
SerializeMessage(context.fd, result);
return true;
}
@ -1083,32 +911,21 @@ bool ProcessCmd(const CommandContext& context, const PbCommand& command)
return ReturnStatus(context);
}
bool ProcessId(const string id_spec, PbDeviceType type, int& id, int& unit)
bool ProcessId(const string id_spec, int& id, int& unit)
{
size_t separator_pos = id_spec.find(COMPONENT_SEPARATOR);
if (separator_pos == string::npos) {
int max_id = type == SAHD ? 16 : 8;
if (!GetAsInt(id_spec, id) || id < 0 || id >= max_id) {
cerr << optarg << ": Invalid device ID (0-" << (max_id - 1) << ")" << endl;
if (!GetAsInt(id_spec, id) || id < 0 || id >= 8) {
cerr << optarg << ": Invalid device ID (0-7)" << endl;
return false;
}
// Required for SASI ID/LUN handling backwards compatibility
unit = 0;
if (type == SAHD) {
unit = id % 2;
id /= 2;
}
}
else {
int max_unit = type == SAHD ? 2 : UnitNum;
if (!GetAsInt(id_spec.substr(0, separator_pos), id) || id < 0 || id > 7 ||
!GetAsInt(id_spec.substr(separator_pos + 1), unit) || unit < 0 || unit >= max_unit) {
cerr << optarg << ": Invalid unit (0-" << (max_unit - 1) << ")" << endl;
return false;
}
else if (!GetAsInt(id_spec.substr(0, separator_pos), id) || id < 0 || id > 7 ||
!GetAsInt(id_spec.substr(separator_pos + 1), unit) || unit < 0 || unit >= AbstractController::LUN_MAX) {
cerr << optarg << ": Invalid unit (0-" << (AbstractController::LUN_MAX - 1) << ")" << endl;
return false;
}
return true;
@ -1186,25 +1003,18 @@ bool ParseArgument(int argc, char* argv[], int& port)
opterr = 1;
int opt;
while ((opt = getopt(argc, argv, "-IiHhb:d:n:p:r:t:z:D:F:L:P:R:")) != -1) {
while ((opt = getopt(argc, argv, "-Iib:d:n:p:r:t:z:D:F:L:P:R:")) != -1) {
switch (opt) {
// The three options below are kind of a compound option with two letters
// The two options below are kind of a compound option with two letters
case 'i':
case 'I':
id = -1;
unit = -1;
continue;
case 'h':
case 'H':
id = -1;
unit = -1;
type = SAHD;
continue;
case 'd':
case 'D': {
if (!ProcessId(optarg, type, id, unit)) {
if (!ProcessId(optarg, id, unit)) {
return false;
}
continue;
@ -1340,7 +1150,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
// Display and log the device list
PbServerInfo server_info;
rascsi_response.GetDevices(server_info, devices);
rascsi_response.GetDevices(server_info);
const list<PbDevice>& devices = { server_info.devices_info().devices().begin(), server_info.devices_info().devices().end() };
const string device_list = ListDevices(devices);
LogDevices(device_list);
@ -1375,7 +1185,7 @@ void FixCpu(int cpu)
// Monitor Thread
//
//---------------------------------------------------------------------------
static void *MonThread(void *param)
static void *MonThread(void *)
{
// Scheduler Settings
struct sched_param schedparam;
@ -1467,20 +1277,20 @@ static void *MonThread(void *param)
}
case DEVICES_INFO: {
rascsi_response.GetDevicesInfo(result, command, devices, UnitNum);
rascsi_response.GetDevicesInfo(result, command);
SerializeMessage(context.fd, result);
break;
}
case DEVICE_TYPES_INFO: {
result.set_allocated_device_types_info(rascsi_response.GetDeviceTypesInfo(result, command));
result.set_allocated_device_types_info(rascsi_response.GetDeviceTypesInfo(result));
SerializeMessage(context.fd, result);
break;
}
case SERVER_INFO: {
result.set_allocated_server_info(rascsi_response.GetServerInfo(
result, devices, reserved_ids, current_log_level, GetParam(command, "folder_pattern"),
result, reserved_ids, current_log_level, GetParam(command, "folder_pattern"),
GetParam(command, "file_pattern"), rascsi_image.GetDepth()));
SerializeMessage(context.fd, result);
break;
@ -1568,7 +1378,7 @@ static void *MonThread(void *param)
}
}
catch(const io_exception& e) {
LOGWARN("%s", e.getmsg().c_str());
LOGWARN("%s", e.get_msg().c_str());
// Fall through
}
@ -1578,7 +1388,7 @@ static void *MonThread(void *param)
}
}
return NULL;
return nullptr;
}
//---------------------------------------------------------------------------
@ -1592,17 +1402,17 @@ int main(int argc, char* argv[])
#ifndef NDEBUG
// Get temporary operation info, in order to trigger an assertion on startup if the operation list is incomplete
// TODO Move to unit test?
PbResult pb_operation_info_result;
const PbOperationInfo *operation_info = rascsi_response.GetOperationInfo(pb_operation_info_result, 0);
assert(operation_info->operations_size() == PbOperation_ARRAYSIZE - 1);
delete operation_info;
#endif
int actid;
BUS::phase_t phase;
// added setvbuf to override stdout buffering, so logs are written immediately and not when the process exits.
setvbuf(stdout, NULL, _IONBF, 0);
struct sched_param schparam;
// Output the Banner
Banner(argc, argv);
@ -1621,21 +1431,20 @@ int main(int argc, char* argv[])
// Create a thread-safe stdout logger to process the log messages
auto logger = stdout_color_mt("rascsi stdout logger");
int port = 6868;
if (!InitBus()) {
return EPERM;
}
int port = DEFAULT_PORT;
if (!InitService(port)) {
return EPERM;
}
if (!ParseArgument(argc, argv, port)) {
Cleanup();
return -1;
}
if (!InitService(port)) {
return EPERM;
}
// Signal handler to detach all devices on a KILL or TERM signal
struct sigaction termination_handler;
termination_handler.sa_handler = TerminationHandler;
@ -1650,11 +1459,14 @@ int main(int argc, char* argv[])
// Set the affinity to a specific processor core
FixCpu(3);
struct sched_param schparam;
#ifdef USE_SEL_EVENT_ENABLE
// Scheduling policy setting (highest priority)
schparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &schparam);
#endif // USE_SEL_EVENT_ENABLE
#else
cout << "Note: No RaSCSI hardware support, only client interface calls are supported" << endl;
#endif
// Start execution
running = true;
@ -1662,12 +1474,11 @@ int main(int argc, char* argv[])
// Main Loop
while (running) {
// Work initialization
actid = -1;
phase = BUS::busfree;
#ifdef USE_SEL_EVENT_ENABLE
// SEL signal polling
if (bus->PollSelectEvent() < 0) {
if (!bus->PollSelectEvent()) {
// Stop on interrupt
if (errno == EINTR) {
break;
@ -1676,21 +1487,21 @@ int main(int argc, char* argv[])
}
// Get the bus
bus->Aquire();
bus->Acquire();
#else
bus->Aquire();
bus->Acquire();
if (!bus->GetSEL()) {
usleep(0);
continue;
}
#endif // USE_SEL_EVENT_ENABLE
#endif
// Wait until BSY is released as there is a possibility for the
// initiator to assert it while setting the ID (for up to 3 seconds)
if (bus->GetBSY()) {
int now = SysTimer::GetTimerLow();
while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) {
bus->Aquire();
bus->Acquire();
if (!bus->GetBSY()) {
break;
}
@ -1702,42 +1513,20 @@ int main(int argc, char* argv[])
continue;
}
pthread_mutex_lock(&ctrl_mutex);
BYTE data = bus->GetDAT();
int initiator_id = -1;
// Notify all controllers
int i = 0;
for (auto it = controllers.begin(); it != controllers.end(); ++i, ++it) {
if (!*it || (data & (1 << i)) == 0) {
continue;
}
// The initiator and target ID
BYTE id_data = bus->GetDAT();
// Extract the SCSI initiator ID
int tmp = data - (1 << i);
if (tmp) {
initiator_id = 0;
for (int j = 0; j < 8; j++) {
tmp >>= 1;
if (tmp) {
initiator_id++;
}
else {
break;
}
}
}
pthread_mutex_lock(&ctrl_mutex);
// Find the target that has moved to the selection phase
if ((*it)->Process(initiator_id) == BUS::selection) {
// Get the target ID
actid = i;
// Identify the responsible controller
AbstractController *controller = controller_manager.IdentifyController(id_data);
if (controller != nullptr) {
initiator_id = controller->ExtractInitiatorId(id_data);
// Bus Selection phase
if (controller->Process(initiator_id) == BUS::selection) {
phase = BUS::selection;
break;
}
}
@ -1754,12 +1543,12 @@ int main(int argc, char* argv[])
// Scheduling policy setting (highest priority)
schparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &schparam);
#endif // USE_SEL_EVENT_ENABLE
#endif
// Loop until the bus is free
while (running) {
// Target drive
phase = controllers[actid]->Process(initiator_id);
phase = controller->Process(initiator_id);
// End when the bus is free
if (phase == BUS::busfree) {
@ -1773,7 +1562,7 @@ int main(int argc, char* argv[])
// Set the scheduling priority back to normal
schparam.sched_priority = 0;
sched_setscheduler(0, SCHED_OTHER, &schparam);
#endif // USE_SEL_EVENT_ENABLE
#endif
// End the target travel
active = false;

View File

@ -0,0 +1,62 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include "scsi.h"
#include <exception>
#include <string>
using namespace std;
class illegal_argument_exception final : public exception {
private:
string msg;
public:
illegal_argument_exception(const string& msg) : msg(msg) {}
~illegal_argument_exception() {}
const string& get_msg() const { return msg; }
};
class io_exception : public exception {
private:
string msg;
public:
io_exception(const string& msg) : msg(msg) {}
virtual ~io_exception() {}
const string& get_msg() const { return msg; }
};
class file_not_found_exception : public io_exception {
public:
file_not_found_exception(const string& msg) : io_exception(msg) {}
~file_not_found_exception() {}
};
class scsi_error_exception : public exception {
private:
scsi_defs::sense_key sense_key;
scsi_defs::asc asc;
scsi_defs::status status;
public:
scsi_error_exception(scsi_defs::sense_key sense_key = scsi_defs::sense_key::ABORTED_COMMAND,
scsi_defs::asc asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
scsi_defs::status status = scsi_defs::status::CHECK_CONDITION)
: sense_key(sense_key), asc(asc), status(status) {}
~scsi_error_exception() {};
scsi_defs::sense_key get_sense_key() const { return sense_key; }
scsi_defs::asc get_asc() const { return asc; }
scsi_defs::status get_status() const { return status; }
};

View File

@ -43,8 +43,6 @@ RascsiImage::RascsiImage()
else {
default_image_folder = "/home/pi/images";
}
depth = 1;
}
bool RascsiImage::CheckDepth(const string& filename)

View File

@ -40,5 +40,5 @@ public:
private:
string default_image_folder;
int depth;
int depth = -1;
};

View File

@ -14,8 +14,8 @@ package rascsi_interface;
// The available device types
enum PbDeviceType {
UNDEFINED = 0;
// Non-removable SASI drive
SAHD = 1;
// Non-removable SASI drive, not supported anymore
SAHD = 1 [deprecated = true];
// Non-removable SCSI drive
SCHD = 2;
// Removable SCSI drive
@ -241,7 +241,7 @@ message PbDeviceProperties {
bool supports_params = 7;
// List of default parameters, if any (requires supports_params to be true)
map<string, string> default_params = 8;
// LUN numbers this device can represent. At least 1 (for LUN 0 only). Maxium is 32, for LUNs 0-31.
// LUN numbers this device can have, usually 32
int32 luns = 9;
// Unordered list of permitted block sizes in bytes, empty if the block size is not configurable
repeated uint32 block_sizes = 10;
@ -306,7 +306,7 @@ message PbDeviceDefinition {
PbDeviceType type = 3;
// Device specific named parameters, e.g. the name of an image file
map<string, string> params = 4;
// The optional block size in bytes per sector, must be one of the supported block sizes for SASI/SCSI
// The optional block size in bytes per sector, must be one of the supported block sizes
int32 block_size = 5;
// The device name components
string vendor = 6;

View File

@ -3,14 +3,13 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "devices/file_support.h"
#include "devices/disk.h"
#include "devices/device_factory.h"
#include "devices/device.h"
#include "protobuf_util.h"
#include "rascsi_version.h"
#include "rascsi_interface.pb.h"
@ -20,17 +19,14 @@
using namespace rascsi_interface;
using namespace protobuf_util;
RascsiResponse::RascsiResponse(DeviceFactory *device_factory, const RascsiImage *rascsi_image)
{
this->device_factory = device_factory;
this->rascsi_image = rascsi_image;
}
list<string> RascsiResponse::log_levels = { "trace", "debug", "info", "warn", "err", "critical", "off" };
PbDeviceProperties *RascsiResponse::GetDeviceProperties(const Device *device)
{
PbDeviceProperties *properties = new PbDeviceProperties();
properties->set_luns(device->GetSupportedLuns());
properties->set_luns(AbstractController::LUN_MAX);
properties->set_read_only(device->IsReadOnly());
properties->set_protectable(device->IsProtectable());
properties->set_stoppable(device->IsStoppable());
@ -60,14 +56,15 @@ void RascsiResponse::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_inf
{
PbDeviceTypeProperties *type_properties = device_types_info.add_properties();
type_properties->set_type(type);
Device *device = device_factory->CreateDevice(type, "");
Device *device = device_factory->CreateDevice(type, "", -1);
type_properties->set_allocated_properties(GetDeviceProperties(device));
delete device;
device_factory->DeleteDevice(device);
}
void RascsiResponse::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info)
{
int ordinal = 1;
// Start with 2 instead of 1. 1 was the removed SASI drive type.
int ordinal = 2;
while (PbDeviceType_IsValid(ordinal)) {
PbDeviceType type = UNDEFINED;
PbDeviceType_Parse(PbDeviceType_Name((PbDeviceType)ordinal), &type);
@ -232,31 +229,25 @@ PbReservedIdsInfo *RascsiResponse::GetReservedIds(PbResult& result, const unorde
return reserved_ids_info;
}
void RascsiResponse::GetDevices(PbServerInfo& server_info, const vector<Device *>& devices)
void RascsiResponse::GetDevices(PbServerInfo& server_info)
{
for (const Device *device : devices) {
// Skip if unit does not exist or is not assigned
if (device) {
PbDevice *pb_device = server_info.mutable_devices_info()->add_devices();
GetDevice(device, pb_device);
}
for (const Device *device : device_factory->GetAllDevices()) {
PbDevice *pb_device = server_info.mutable_devices_info()->add_devices();
GetDevice(device, pb_device);
}
}
void RascsiResponse::GetDevicesInfo(PbResult& result, const PbCommand& command, const vector<Device *>& devices,
int unit_count)
void RascsiResponse::GetDevicesInfo(PbResult& result, const PbCommand& command)
{
set<id_set> id_sets;
if (!command.devices_size()) {
for (const Device *device : devices) {
if (device) {
id_sets.insert(make_pair(device->GetId(), device->GetLun()));
}
for (const Device *device : device_factory->GetAllDevices()) {
id_sets.insert(make_pair(device->GetId(), device->GetLun()));
}
}
else {
for (const auto& device : command.devices()) {
if (devices[device.id() * unit_count + device.unit()]) {
if (device_factory->GetDeviceByIdAndLun(device.id(), device.unit())) {
id_sets.insert(make_pair(device.id(), device.unit()));
}
else {
@ -271,14 +262,13 @@ void RascsiResponse::GetDevicesInfo(PbResult& result, const PbCommand& command,
result.set_allocated_devices_info(devices_info);
for (const auto& id_set : id_sets) {
const Device *device = devices[id_set.first * unit_count + id_set.second];
GetDevice(device, devices_info->add_devices());
GetDevice(device_factory->GetDeviceByIdAndLun(id_set.first, id_set.second), devices_info->add_devices());
}
result.set_status(true);
}
PbDeviceTypesInfo *RascsiResponse::GetDeviceTypesInfo(PbResult& result, const PbCommand& command)
PbDeviceTypesInfo *RascsiResponse::GetDeviceTypesInfo(PbResult& result)
{
PbDeviceTypesInfo *device_types_info = new PbDeviceTypesInfo();
@ -289,9 +279,8 @@ PbDeviceTypesInfo *RascsiResponse::GetDeviceTypesInfo(PbResult& result, const Pb
return device_types_info;
}
PbServerInfo *RascsiResponse::GetServerInfo(PbResult& result, const vector<Device *>& devices,
const unordered_set<int>& reserved_ids, const string& current_log_level, const string& folder_pattern,
const string& file_pattern, int scan_depth)
PbServerInfo *RascsiResponse::GetServerInfo(PbResult& result, const unordered_set<int>& reserved_ids,
const string& current_log_level, const string& folder_pattern, const string& file_pattern, int scan_depth)
{
PbServerInfo *server_info = new PbServerInfo();
@ -301,7 +290,7 @@ PbServerInfo *RascsiResponse::GetServerInfo(PbResult& result, const vector<Devic
GetAvailableImages(result, *server_info, folder_pattern, file_pattern, scan_depth);
server_info->set_allocated_network_interfaces_info(GetNetworkInterfacesInfo(result));
server_info->set_allocated_mapping_info(GetMappingInfo(result));
GetDevices(*server_info, devices);
GetDevices(*server_info);
server_info->set_allocated_reserved_ids_info(GetReservedIds(result, reserved_ids));
server_info->set_allocated_operation_info(GetOperationInfo(result, scan_depth));

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
@ -11,7 +11,7 @@
#include "devices/device_factory.h"
#include "rascsi_interface.pb.h"
#include <vector>
#include <list>
#include <string>
using namespace std;
@ -25,18 +25,18 @@ class RascsiResponse
{
public:
RascsiResponse(DeviceFactory *, const RascsiImage *);
~RascsiResponse() {};
RascsiResponse(DeviceFactory *device_factory, const RascsiImage *rascsi_image)
: device_factory(device_factory), rascsi_image(rascsi_image) {}
~RascsiResponse() {}
bool GetImageFile(PbImageFile *, const string&);
PbImageFilesInfo *GetAvailableImages(PbResult&, const string&, const string&, int);
PbReservedIdsInfo *GetReservedIds(PbResult&, const unordered_set<int>&);
void GetDevices(PbServerInfo&, const vector<Device *>&);
void GetDevicesInfo(PbResult&, const PbCommand&, const vector<Device *>&, int);
PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&, const PbCommand&);
void GetDevices(PbServerInfo&);
void GetDevicesInfo(PbResult&, const PbCommand&);
PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&);
PbVersionInfo *GetVersionInfo(PbResult&);
PbServerInfo *GetServerInfo(PbResult&, const vector<Device *>&, const unordered_set<int>&, const string&,
const string&, const string&, int);
PbServerInfo *GetServerInfo(PbResult&, const unordered_set<int>&, const string&, const string&, const string&, int);
PbNetworkInterfacesInfo *GetNetworkInterfacesInfo(PbResult&);
PbMappingInfo *GetMappingInfo(PbResult&);
PbLogLevelInfo *GetLogLevelInfo(PbResult&, const string&);
@ -47,7 +47,7 @@ private:
DeviceFactory *device_factory;
const RascsiImage *rascsi_image;
vector<string> log_levels { "trace", "debug", "info", "warn", "err", "critical", "off" };
static list<string> log_levels;
PbDeviceProperties *GetDeviceProperties(const Device *);
void GetDevice(const Device *, PbDevice *);

View File

@ -131,8 +131,8 @@ int main(int argc, char* argv[])
cerr << " where ID := {0-7}" << endl;
cerr << " UNIT := {0-31}, default is 0" << endl;
cerr << " CMD := {attach|detach|insert|eject|protect|unprotect|show}" << endl;
cerr << " TYPE := {sahd|schd|scrm|sccd|scmo|scbr|scdp} or convenience type {hd|rm|mo|cd|bridge|daynaport}" << endl;
cerr << " BLOCK_SIZE := {256|512|1024|2048|4096) bytes per hard disk drive block" << endl;
cerr << " TYPE := {schd|scrm|sccd|scmo|scbr|scdp} or convenience type {hd|rm|mo|cd|bridge|daynaport}" << endl;
cerr << " BLOCK_SIZE := {512|1024|2048|4096) bytes per hard disk drive block" << endl;
cerr << " NAME := name of device to attach (VENDOR:PRODUCT:REVISION)" << endl;
cerr << " FILE|PARAM := image file path or device-specific parameter" << endl;
cerr << " IMAGE_FOLDER := default location for image files, default is '~/images'" << endl;

View File

@ -9,7 +9,7 @@
#include <netdb.h>
#include "os.h"
#include "exceptions.h"
#include "rascsi_exceptions.h"
#include "protobuf_util.h"
#include "rasutil.h"
#include "rasctl_commands.h"
@ -26,12 +26,8 @@ using namespace protobuf_util;
RasctlCommands::RasctlCommands(PbCommand& command, const string& hostname, int port, const string& token,
const string& locale)
: command(command), hostname(hostname), port(port), token(token), locale(locale)
{
this->command = command;
this->hostname = hostname;
this->port = port;
this->token = token;
this->locale = locale;
}
void RasctlCommands::SendCommand()
@ -76,7 +72,7 @@ void RasctlCommands::SendCommand()
SerializeMessage(fd, command);
}
catch(const io_exception& e) {
cerr << "Error: " << e.getmsg() << endl;
cerr << "Error: " << e.get_msg() << endl;
if (fd >= 0) {
close(fd);
@ -96,7 +92,7 @@ void RasctlCommands::SendCommand()
catch(const io_exception& e) {
close(fd);
cerr << "Error: " << e.getmsg() << endl;
cerr << "Error: " << e.get_msg() << endl;
exit(EXIT_FAILURE);
}

View File

@ -119,21 +119,15 @@ void RasctlDisplay::DisplayLogLevelInfo(const PbLogLevelInfo& log_level_info)
void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types_info)
{
cout << "Supported device types and their properties:" << endl;
cout << "Supported device types and their properties:";
for (const auto& device_type_info : device_types_info.properties()) {
cout << " " << PbDeviceType_Name(device_type_info.type());
cout << endl << " " << PbDeviceType_Name(device_type_info.type()) << " ";
const PbDeviceProperties& properties = device_type_info.properties();
cout << " Supported LUN numbers: 0";
if (properties.luns() > 1) {
cout << "-" << (properties.luns() - 1);
}
cout << endl;
if (properties.read_only() || properties.protectable() || properties.stoppable() || properties.read_only()
|| properties.lockable()) {
cout << " Properties: ";
cout << "Properties: ";
bool has_property = false;
if (properties.read_only()) {
cout << "read-only";
@ -154,28 +148,28 @@ void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types
if (properties.lockable()) {
cout << (has_property ? ", " : "") << "lockable";
}
cout << endl;
cout << endl << " ";
}
if (properties.supports_file()) {
cout << " Image file support" << endl;
cout << "Image file support" << endl << " ";
}
else if (properties.supports_params()) {
cout << " Parameter support" << endl;
if (properties.supports_params()) {
cout << "Parameter support" << endl << " ";
}
if (properties.supports_params() && properties.default_params_size()) {
// Creates a sorted map
map<string, string> params = { properties.default_params().begin(), properties.default_params().end() };
cout << " Default parameters: ";
cout << "Default parameters: ";
bool isFirst = true;
for (const auto& param : params) {
if (!isFirst) {
cout << " ";
cout << endl << " ";
}
cout << param.first << "=" << param.second << endl;
cout << param.first << "=" << param.second;
isFirst = false;
}
@ -185,7 +179,7 @@ void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types
// Creates a sorted set
set<uint32_t> block_sizes = { properties.block_sizes().begin(), properties.block_sizes().end() };
cout << " Configurable block sizes in bytes: ";
cout << "Configurable block sizes in bytes: ";
bool isFirst = true;
for (const auto& block_size : block_sizes) {
@ -196,7 +190,6 @@ void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types
isFirst = false;
}
cout << endl;
}
}
}
@ -259,6 +252,9 @@ void RasctlDisplay::DisplayNetworkInterfaces(const PbNetworkInterfacesInfo& netw
if (!isFirst) {
cout << ", ";
}
else {
cout << " ";
}
isFirst = false;
cout << interface;
}

View File

@ -48,7 +48,7 @@ void Cleanup();
// Signal processing
//
//---------------------------------------------------------------------------
void KillHandler(int sig)
void KillHandler(int)
{
// Stop running
Cleanup();
@ -141,10 +141,7 @@ void Reset()
bool ParseArgument(int argc, char* argv[])
{
int opt;
char *file;
// Initialization
file = NULL;
char *file = nullptr;
// Argument Parsing
opterr = 0;
@ -213,7 +210,7 @@ bool WaitPhase(BUS::phase_t phase)
// Timeout (3000ms)
now = SysTimer::GetTimerLow();
while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) {
bus.Aquire();
bus.Acquire();
if (bus.GetREQ() && bus.GetPhase() == phase) {
return true;
}
@ -248,20 +245,20 @@ bool Selection(int id)
data |= (1 << boardid);
data |= (1 << id);
bus.SetDAT(data);
bus.SetSEL(TRUE);
bus.SetSEL(true);
// wait for busy
count = 10000;
do {
usleep(20);
bus.Aquire();
bus.Acquire();
if (bus.GetBSY()) {
break;
}
} while (count--);
// SEL negate
bus.SetSEL(FALSE);
bus.SetSEL(false);
// Success if the target is busy
return bus.GetBSY();

View File

@ -1,745 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ HDD dump utility (Initiator mode/SASI Version) ]
//
// SASI IMAGE EXAMPLE
// X68000
// 10MB(10441728 BS=256 C=40788)
// 20MB(20748288 BS=256 C=81048)
// 40MB(41496576 BS=256 C=162096)
//
// MZ-2500/MZ-2800 MZ-1F23
// 20MB(22437888 BS=1024 C=21912)
//
//---------------------------------------------------------------------------
#include <cerrno>
#include "os.h"
#include "fileio.h"
#include "filepath.h"
#include "gpiobus.h"
#include "rascsi_version.h"
#define BUFSIZE 1024 * 64 // Maybe around 64KB?
GPIOBUS bus;
int targetid;
int unitid;
int bsiz; // Block size
int bnum; // Number of blocks
Filepath hdffile; // HDF file
bool restore; // Restore flag
BYTE buffer[BUFSIZE]; // Work buffer
int result; // Result code
void Cleanup();
//---------------------------------------------------------------------------
//
// Signal processing
//
//---------------------------------------------------------------------------
void KillHandler(int sig)
{
// Stop instruction
Cleanup();
exit(0);
}
//---------------------------------------------------------------------------
//
// Banner output
//
//---------------------------------------------------------------------------
bool Banner(int argc, char* argv[])
{
printf("RaSCSI hard disk dump utility (SASI HDD) ");
printf("version %s (%s, %s)\n",
rascsi_get_version_string(),
__DATE__,
__TIME__);
if (argc < 2 || strcmp(argv[1], "-h") == 0) {
printf("Usage: %s -i ID [-u UT] [-b BSIZE] -c COUNT -f FILE [-r]\n", argv[0]);
printf(" ID is target device SASI ID {0|1|2|3|4|5|6|7}.\n");
printf(" UT is target unit ID {0|1}. Default is 0.\n");
printf(" BSIZE is block size {256|512|1024}. Default is 256.\n");
printf(" COUNT is block count.\n");
printf(" FILE is HDF file path.\n");
printf(" -r is restore operation.\n");
return false;
}
return true;
}
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;
}
// GPIO initialization
if (!bus.Init(BUS::INITIATOR)) {
return false;
}
// Work initialization
targetid = -1;
unitid = 0;
bsiz = 256;
bnum = -1;
restore = false;
return true;
}
void Cleanup()
{
bus.Cleanup();
}
void Reset()
{
// Reset bus signal line
bus.Reset();
}
bool ParseArgument(int argc, char* argv[])
{
int opt;
char *file;
// Initialization
file = NULL;
// Argument parsing
opterr = 0;
while ((opt = getopt(argc, argv, "i:u:b:c:f:r")) != -1) {
switch (opt) {
case 'i':
targetid = optarg[0] - '0';
break;
case 'u':
unitid = optarg[0] - '0';
break;
case 'b':
bsiz = atoi(optarg);
break;
case 'c':
bnum = atoi(optarg);
break;
case 'f':
file = optarg;
break;
case 'r':
restore = true;
break;
}
}
// TARGET ID check
if (targetid < 0 || targetid > 7) {
fprintf(stderr,
"Error : Invalid target id range\n");
return false;
}
// UNIT ID check
if (unitid < 0 || unitid > 1) {
fprintf(stderr,
"Error : Invalid unit id range\n");
return false;
}
// BSIZ check
if (bsiz != 256 && bsiz != 512 && bsiz != 1024) {
fprintf(stderr,
"Error : Invalid block size\n");
return false;
}
// BNUM check
if (bnum < 0) {
fprintf(stderr,
"Error : Invalid block count\n");
return false;
}
// File check
if (!file) {
fprintf(stderr,
"Error : Invalid file path\n");
return false;
}
hdffile.SetPath(file);
return true;
}
bool WaitPhase(BUS::phase_t phase)
{
DWORD now;
// Timeout (3000ms)
now = SysTimer::GetTimerLow();
while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) {
bus.Aquire();
if (bus.GetREQ() && bus.GetPhase() == phase) {
return true;
}
}
return false;
}
//---------------------------------------------------------------------------
//
// Bus free phase execution
//
//---------------------------------------------------------------------------
void BusFree()
{
bus.Reset();
}
//---------------------------------------------------------------------------
//
// Selection phase execution
//
//---------------------------------------------------------------------------
bool Selection(int id)
{
BYTE data;
int count;
// ID setting and SEL assertion
data = 0;
data |= (1 << id);
bus.SetDAT(data);
bus.SetSEL(TRUE);
// Wait for BSY
count = 10000;
do {
usleep(20);
bus.Aquire();
if (bus.GetBSY()) {
break;
}
} while (count--);
// SEL negate
bus.SetSEL(FALSE);
// Return true if target is busy
return bus.GetBSY();
}
//---------------------------------------------------------------------------
//
// Command phase execution
//
//---------------------------------------------------------------------------
bool Command(BYTE *buf, int length)
{
int count;
// Wait for phase
if (!WaitPhase(BUS::command)) {
return false;
}
// Send command
count = bus.SendHandShake(buf, length, BUS::SEND_NO_DELAY);
// Return true is send results match number of requests
if (count == length) {
return true;
}
// Send error
return false;
}
//---------------------------------------------------------------------------
//
// Data in phase execution
//
//---------------------------------------------------------------------------
int DataIn(BYTE *buf, int length)
{
// Wait for phase
if (!WaitPhase(BUS::datain)) {
return -1;
}
// Receive data
return bus.ReceiveHandShake(buf, length);
}
//---------------------------------------------------------------------------
//
// Data out phase execution
//
//---------------------------------------------------------------------------
int DataOut(BYTE *buf, int length)
{
// Wait for phase
if (!WaitPhase(BUS::dataout)) {
return -1;
}
// Receive data
return bus.SendHandShake(buf, length, BUS::SEND_NO_DELAY);
}
//---------------------------------------------------------------------------
//
// Status phase execution
//
//---------------------------------------------------------------------------
int Status()
{
BYTE buf[256];
// Wait for phase
if (!WaitPhase(BUS::status)) {
return -2;
}
// Receive data
if (bus.ReceiveHandShake(buf, 1) == 1) {
return (int)buf[0];
}
// Receive error
return -1;
}
//---------------------------------------------------------------------------
//
// Message in phase execution
//
//---------------------------------------------------------------------------
int MessageIn()
{
BYTE buf[256];
// Wait for phase
if (!WaitPhase(BUS::msgin)) {
return -2;
}
// Receive data
if (bus.ReceiveHandShake(buf, 1) == 1) {
return (int)buf[0];
}
// Receive error
return -1;
}
//---------------------------------------------------------------------------
//
// TEST UNIT READY execution
//
//---------------------------------------------------------------------------
int TestUnitReady(int id)
{
BYTE cmd[256];
// Initialize result code
result = 0;
if (!Selection(id)) {
result = -1;
goto exit;
}
memset(cmd, 0x00, 6);
cmd[0] = 0x00;
cmd[1] = unitid << 5;
if (!Command(cmd, 6)) {
result = -2;
goto exit;
}
if (Status() < 0) {
result = -4;
goto exit;
}
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
BusFree();
return result;
}
//---------------------------------------------------------------------------
//
// REQUEST SENSE execution
//
//---------------------------------------------------------------------------
int RequestSense(int id, BYTE *buf)
{
BYTE cmd[256];
int count;
// Initialize result codes
result = 0;
count = 0;
if (!Selection(id)) {
result = -1;
goto exit;
}
memset(cmd, 0x00, 6);
cmd[0] = 0x03;
cmd[1] = unitid << 5;
cmd[4] = 4;
if (!Command(cmd, 6)) {
result = -2;
goto exit;
}
memset(buf, 0x00, 256);
count = DataIn(buf, 256);
if (count <= 0) {
result = -3;
goto exit;
}
if (Status() < 0) {
result = -4;
goto exit;
}
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
BusFree();
// If successful, return number of transfers
if (result == 0) {
return count;
}
return result;
}
//---------------------------------------------------------------------------
//
// READ6 execution
//
//---------------------------------------------------------------------------
int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
{
BYTE cmd[256];
int count;
// Initialize result codes
result = 0;
count = 0;
if (!Selection(id)) {
result = -1;
goto exit;
}
memset(cmd, 0x00, 10);
cmd[0] = 0x8;
cmd[1] = (BYTE)(bstart >> 16);
cmd[1] &= 0x1f;
cmd[1] = unitid << 5;
cmd[2] = (BYTE)(bstart >> 8);
cmd[3] = (BYTE)bstart;
cmd[4] = (BYTE)blength;
if (!Command(cmd, 6)) {
result = -2;
goto exit;
}
count = DataIn(buf, length);
if (count <= 0) {
result = -3;
goto exit;
}
if (Status() < 0) {
result = -4;
goto exit;
}
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
BusFree();
// If successful, return number of transfers
if (result == 0) {
return count;
}
return result;
}
//---------------------------------------------------------------------------
//
// WRITE6 execution
//
//---------------------------------------------------------------------------
int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
{
BYTE cmd[256];
int count;
// Initialize result codes
result = 0;
count = 0;
if (!Selection(id)) {
result = -1;
goto exit;
}
memset(cmd, 0x00, 10);
cmd[0] = 0xa;
cmd[1] = (BYTE)(bstart >> 16);
cmd[1] &= 0x1f;
cmd[1] = unitid << 5;
cmd[2] = (BYTE)(bstart >> 8);
cmd[3] = (BYTE)bstart;
cmd[4] = (BYTE)blength;
if (!Command(cmd, 6)) {
result = -2;
goto exit;
}
count = DataOut(buf, length);
if (count <= 0) {
result = -3;
goto exit;
}
if (Status() < 0) {
result = -4;
goto exit;
}
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
BusFree();
// If successful, return number of transfers
if (result == 0) {
return count;
}
return result;
}
//---------------------------------------------------------------------------
//
// Main processing
//
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
int i;
int count;
DWORD duni;
DWORD dsiz;
DWORD dnum;
Fileio fio;
Fileio::OpenMode omode;
off_t size;
// Output banner
if (!Banner(argc, argv)) {
exit(0);
}
if (!Init()) {
fprintf(stderr, "Error : Initializing\n");
// Probably not executing as root?
exit(EPERM);
}
if (!ParseArgument(argc, argv)) {
Cleanup();
// Exit with argument error
exit(EINVAL);
}
Reset();
// Open file
if (restore) {
omode = Fileio::ReadOnly;
} else {
omode = Fileio::WriteOnly;
}
if (!fio.Open(hdffile.GetPath(), omode)) {
fprintf(stderr, "Error : Can't open hdf file\n");
Cleanup();
exit(EPERM);
}
BusFree();
// Execute RESET signal
bus.SetRST(TRUE);
usleep(1000);
bus.SetRST(FALSE);
// Start dump
printf("TARGET ID : %d\n", targetid);
printf("UNIT ID : %d\n", unitid);
// TEST UNIT READY
count = TestUnitReady(targetid);
if (count < 0) {
fprintf(stderr, "TEST UNIT READY ERROR %d\n", count);
goto cleanup_exit;
}
// REQUEST SENSE (for CHECK CONDITION)
count = RequestSense(targetid, buffer);
if (count < 0) {
fprintf(stderr, "REQUEST SENSE ERROR %d\n", count);
goto cleanup_exit;
}
printf("Number of blocks : %d Blocks\n", bnum);
printf("Block length : %d Bytes\n", bsiz);
// Display data size
printf("Total length : %d MBytes %d Bytes\n",
(bsiz * bnum / 1024 / 1024),
(bsiz * bnum));
// Get restore file size
if (restore) {
size = fio.GetFileSize();
printf("Restore file size : %d bytes", (int)size);
if (size > (off_t)(bsiz * bnum)) {
printf("(WARNING : File size is larger than disk size)");
} else if (size < (off_t)(bsiz * bnum)) {
printf("(ERROR : File size is smaller than disk size)\n");
goto cleanup_exit;
}
printf("\n");
}
// Dump by buffer size
duni = BUFSIZE;
duni /= bsiz;
dsiz = BUFSIZE;
dnum = bnum * bsiz;
dnum /= BUFSIZE;
if (restore) {
printf("Restore progress : ");
} else {
printf("Dump progress : ");
}
for (i = 0; i < (int)dnum; i++) {
if (i > 0) {
printf("\033[21D");
printf("\033[0K");
}
printf("%3d%%(%7d/%7d)",
(int)((i + 1) * 100 / dnum),
(int)(i * duni),
bnum);
fflush(stdout);
if (restore) {
if (fio.Read(buffer, dsiz)) {
if (Write6(targetid, i * duni, duni, dsiz, buffer) >= 0) {
continue;
}
}
} else {
if (Read6(targetid, i * duni, duni, dsiz, buffer) >= 0) {
if (fio.Write(buffer, dsiz)) {
continue;
}
}
}
printf("\n");
printf("Error occured and aborted... %d\n", result);
goto cleanup_exit;
}
if (dnum > 0) {
printf("\033[21D");
printf("\033[0K");
}
// Rounding of capacity
dnum = bnum % duni;
dsiz = dnum * bsiz;
if (dnum > 0) {
if (restore) {
if (fio.Read(buffer, dsiz)) {
Write6(targetid, i * duni, dnum, dsiz, buffer);
}
} else {
if (Read6(targetid, i * duni, dnum, dsiz, buffer) >= 0) {
fio.Write(buffer, dsiz);
}
}
}
// Completion message
printf("%3d%%(%7d/%7d)\n", 100, bnum, bnum);
cleanup_exit:
fio.Close();
Cleanup();
exit(0);
}

View File

@ -9,7 +9,6 @@
//
//---------------------------------------------------------------------------
#include "os.h"
#include "scsi.h"
//---------------------------------------------------------------------------

View File

@ -14,7 +14,7 @@
//===========================================================================
//
// SASI/SCSI Bus
// SCSI Bus
//
//===========================================================================
class BUS
@ -47,7 +47,7 @@ public:
virtual ~BUS() {}
// Basic Functions
virtual BOOL Init(mode_e mode) = 0;
virtual bool Init(mode_e mode) = 0;
virtual void Reset() = 0;
virtual void Cleanup() = 0;
phase_t GetPhase();
@ -66,45 +66,45 @@ public:
return ((raw_data >> pin_num) & 1);
}
virtual bool GetBSY() = 0;
virtual bool GetBSY() const = 0;
virtual void SetBSY(bool ast) = 0;
virtual BOOL GetSEL() = 0;
virtual void SetSEL(BOOL ast) = 0;
virtual bool GetSEL() const = 0;
virtual void SetSEL(bool ast) = 0;
virtual BOOL GetATN() = 0;
virtual void SetATN(BOOL ast) = 0;
virtual bool GetATN() const = 0;
virtual void SetATN(bool ast) = 0;
virtual BOOL GetACK() = 0;
virtual void SetACK(BOOL ast) = 0;
virtual bool GetACK() const = 0;
virtual void SetACK(bool ast) = 0;
virtual BOOL GetRST() = 0;
virtual void SetRST(BOOL ast) = 0;
virtual bool GetRST() const = 0;
virtual void SetRST(bool ast) = 0;
virtual BOOL GetMSG() = 0;
virtual void SetMSG(BOOL ast) = 0;
virtual bool GetMSG() const = 0;
virtual void SetMSG(bool ast) = 0;
virtual BOOL GetCD() = 0;
virtual void SetCD(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 GetIO() = 0;
virtual void SetIO(bool ast) = 0;
virtual BOOL GetREQ() = 0;
virtual void SetREQ(BOOL ast) = 0;
virtual bool GetREQ() const = 0;
virtual void SetREQ(bool ast) = 0;
virtual BYTE GetDAT() = 0;
virtual void SetDAT(BYTE dat) = 0;
virtual BOOL GetDP() = 0; // Get parity signal
virtual bool GetDP() const = 0; // Get parity signal
virtual DWORD Aquire() = 0;
virtual int CommandHandShake(BYTE *buf, bool) = 0;
virtual DWORD Acquire() = 0;
virtual int CommandHandShake(BYTE *buf) = 0;
virtual int ReceiveHandShake(BYTE *buf, int count) = 0;
virtual int SendHandShake(BYTE *buf, int count, int delay_after_bytes) = 0;
virtual BOOL GetSignal(int pin) = 0;
virtual bool GetSignal(int pin) const = 0;
// Get SCSI input signal value
virtual void SetSignal(int pin, BOOL ast) = 0;
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
@ -117,12 +117,6 @@ private:
static const char* phase_str_table[];
};
//===========================================================================
//
// For Status byte codes, Sense Keys and Additional Sense Codes
// See https://www.t10.org/lists/1spc-lst.htm
//
//===========================================================================
namespace scsi_defs {
enum scsi_level : int {
SCSI_1_CCS = 1,
@ -228,12 +222,17 @@ namespace scsi_defs {
enum 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,
MEDIUM_NOT_PRESENT = 0x3a
POWER_ON_OR_RESET = 0x29,
MEDIUM_NOT_PRESENT = 0x3a,
LOAD_OR_EJECT_FAILED = 0x53
};
};

View File

@ -76,7 +76,7 @@ char input_file_name[_MAX_FNAME];
// Signal Processing
//
//---------------------------------------------------------------------------
void KillHandler(int sig)
void KillHandler(int)
{
// Stop instruction
running = false;
@ -130,7 +130,7 @@ void parse_arguments(int argc, char *argv[])
// Copyright text
//
//---------------------------------------------------------------------------
void print_copyright_text(int argc, char *argv[])
void print_copyright_text(int, char *[])
{
LOGINFO("SCSI Monitor Capture Tool - part of RaSCSI(*^..^*) ");
LOGINFO("version %s (%s, %s)",
@ -148,7 +148,7 @@ void print_copyright_text(int argc, char *argv[])
// Help text
//
//---------------------------------------------------------------------------
void print_help_text(int argc, char *argv[])
void print_help_text(int, char *argv[])
{
LOGINFO("%s -i [input file json] -b [buffer size] [output file]", argv[0]);
LOGINFO(" -i [input file json] - scsimon will parse the json file instead of capturing new data");
@ -163,7 +163,7 @@ void print_help_text(int argc, char *argv[])
// Banner Output
//
//---------------------------------------------------------------------------
void Banner(int argc, char *argv[])
void Banner(int, char *[])
{
if (import_data)
{
@ -368,7 +368,7 @@ int main(int argc, char *argv[])
while (running)
{
// Work initialization
this_sample = (bus->Aquire() & ALL_SCSI_PINS);
this_sample = (bus->Acquire() & ALL_SCSI_PINS);
loop_count++;
if (loop_count > LLONG_MAX - 1)
{

View File

@ -0,0 +1,33 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "testing.h"
#include "controllers/abstract_controller.h"
TEST(AbstractControllerTest, AbstractController)
{
const int ID = 1;
const int LUN = 4;
const int INITIATOR_ID = 7;
const int UNKNOWN_INITIATOR_ID = -1;
MockAbstractController controller(ID);
MockPrimaryDevice device;
device.SetLun(LUN);
EXPECT_TRUE(controller.AddDevice(&device));
EXPECT_EQ(ID, controller.GetTargetId());
EXPECT_TRUE(controller.HasDeviceForLun(LUN));
EXPECT_FALSE(controller.HasDeviceForLun(0));
EXPECT_TRUE(controller.DeleteDevice(&device));
EXPECT_EQ(INITIATOR_ID, controller.ExtractInitiatorId((1 << INITIATOR_ID) | ( 1 << ID)));
EXPECT_EQ(UNKNOWN_INITIATOR_ID, controller.ExtractInitiatorId(1 << ID));
}

View File

@ -0,0 +1,33 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "testing.h"
#include "controllers/controller_manager.h"
TEST(ControllerManagerTest, ControllerManager)
{
const int ID = 4;
const int LUN = 6;
PrimaryDevice *device = static_cast<PrimaryDevice *>(device_factory.CreateDevice(UNDEFINED, "services", ID));
device->SetId(ID);
device->SetLun(LUN);
controller_manager.CreateScsiController(nullptr, device);
EXPECT_NE(nullptr, controller_manager.IdentifyController(1 << ID));
EXPECT_EQ(nullptr, controller_manager.IdentifyController(0));
EXPECT_NE(nullptr, controller_manager.FindController(ID));
EXPECT_EQ(nullptr, controller_manager.FindController(0));
EXPECT_EQ(device, controller_manager.GetDeviceByIdAndLun(ID, LUN));
EXPECT_EQ(nullptr, controller_manager.GetDeviceByIdAndLun(0, 0));
controller_manager.DeleteAllControllersAndDevices();
EXPECT_EQ(nullptr, controller_manager.FindController(ID));
EXPECT_EQ(nullptr, controller_manager.GetDeviceByIdAndLun(ID, LUN));
}

View File

@ -5,26 +5,16 @@
//
// Copyright (C) 2022 Uwe Seimet
//
// Unit tests based on GoogleTest and GoogleMock
//
//---------------------------------------------------------------------------
#include <gtest/gtest.h>
#include "testing.h"
#include "rascsi_exceptions.h"
#include "rascsi_version.h"
#include "../devices/device.h"
#include "../devices/device_factory.h"
using namespace rascsi_interface;
namespace DeviceFactoryTest
{
DeviceFactory& device_factory = DeviceFactory::instance();
#include "devices/device.h"
#include "devices/device_factory.h"
TEST(DeviceFactoryTest, GetTypeForFile)
{
EXPECT_EQ(device_factory.GetTypeForFile("test.hdf"), SAHD);
EXPECT_EQ(device_factory.GetTypeForFile("test.hds"), SCHD);
EXPECT_EQ(device_factory.GetTypeForFile("test.HDS"), SCHD);
EXPECT_EQ(device_factory.GetTypeForFile("test.hda"), SCHD);
@ -43,6 +33,24 @@ TEST(DeviceFactoryTest, GetTypeForFile)
EXPECT_EQ(device_factory.GetTypeForFile("test.iso.suffix"), UNDEFINED);
}
TEST(DeviceFactoryTest, LifeCycle)
{
Device *device = device_factory.CreateDevice(UNDEFINED, "services", -1);
EXPECT_NE(nullptr, device);
list<Device *> devices = device_factory.GetAllDevices();
EXPECT_EQ(1, devices.size());
EXPECT_EQ(device, devices.front());
EXPECT_EQ(device, device_factory.GetDeviceByIdAndLun(-1, 0));
EXPECT_EQ(nullptr, device_factory.GetDeviceByIdAndLun(-1, 1));
device_factory.DeleteDevice(device);
devices = device_factory.GetAllDevices();
EXPECT_EQ(0, devices.size());
EXPECT_EQ(nullptr, device_factory.GetDeviceByIdAndLun(-1, 0));
}
TEST(DeviceFactoryTest, GetSectorSizes)
{
unordered_set<uint32_t> sector_sizes;
@ -88,14 +96,15 @@ TEST(DeviceFactoryTest, GetSectorSizes)
TEST(DeviceFactoryTest, UnknownDeviceType)
{
Device *device = device_factory.CreateDevice(UNDEFINED, "test");
Device *device = device_factory.CreateDevice(UNDEFINED, "test", -1);
EXPECT_EQ(nullptr, device);
}
TEST(DeviceFactoryTest, SCHD_Device_Defaults)
{
Device *device = device_factory.CreateDevice(UNDEFINED, "test.hda");
Device *device = device_factory.CreateDevice(UNDEFINED, "test.hda", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHD", device->GetType());
EXPECT_TRUE(device->SupportsFile());
EXPECT_FALSE(device->SupportsParams());
EXPECT_TRUE(device->IsProtectable());
@ -113,13 +122,29 @@ TEST(DeviceFactoryTest, SCHD_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
EXPECT_EQ(32, device->GetSupportedLuns());
device_factory.DeleteDevice(device);
device = device_factory.CreateDevice(UNDEFINED, "test.hds", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHD", device->GetType());
device_factory.DeleteDevice(device);
device = device_factory.CreateDevice(UNDEFINED, "test.hdi", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHD", device->GetType());
device_factory.DeleteDevice(device);
device = device_factory.CreateDevice(UNDEFINED, "test.nhd", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHD", device->GetType());
device_factory.DeleteDevice(device);
}
TEST(DeviceFactoryTest, SCRM_Device_Defaults)
{
Device *device = device_factory.CreateDevice(UNDEFINED, "test.hdr");
Device *device = device_factory.CreateDevice(UNDEFINED, "test.hdr", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCRM", device->GetType());
EXPECT_TRUE(device->SupportsFile());
EXPECT_FALSE(device->SupportsParams());
EXPECT_TRUE(device->IsProtectable());
@ -137,13 +162,14 @@ TEST(DeviceFactoryTest, SCRM_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
EXPECT_EQ(32, device->GetSupportedLuns());
device_factory.DeleteDevice(device);
}
TEST(DeviceFactoryTest, SCMO_Device_Defaults)
{
Device *device = device_factory.CreateDevice(UNDEFINED, "test.mos");
Device *device = device_factory.CreateDevice(UNDEFINED, "test.mos", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCMO", device->GetType());
EXPECT_TRUE(device->SupportsFile());
EXPECT_FALSE(device->SupportsParams());
EXPECT_TRUE(device->IsProtectable());
@ -161,13 +187,14 @@ TEST(DeviceFactoryTest, SCMO_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
EXPECT_EQ(32, device->GetSupportedLuns());
device_factory.DeleteDevice(device);
}
TEST(DeviceFactoryTest, SCCD_Device_Defaults)
{
Device *device = device_factory.CreateDevice(UNDEFINED, "test.iso");
Device *device = device_factory.CreateDevice(UNDEFINED, "test.iso", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCCD", device->GetType());
EXPECT_TRUE(device->SupportsFile());
EXPECT_FALSE(device->SupportsParams());
EXPECT_FALSE(device->IsProtectable());
@ -185,13 +212,14 @@ TEST(DeviceFactoryTest, SCCD_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
EXPECT_EQ(32, device->GetSupportedLuns());
device_factory.DeleteDevice(device);
}
TEST(DeviceFactoryTest, SCBR_Device_Defaults)
{
Device *device = device_factory.CreateDevice(UNDEFINED, "bridge");
Device *device = device_factory.CreateDevice(UNDEFINED, "bridge", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCBR", device->GetType());
EXPECT_FALSE(device->SupportsFile());
EXPECT_TRUE(device->SupportsParams());
EXPECT_FALSE(device->IsProtectable());
@ -209,13 +237,14 @@ TEST(DeviceFactoryTest, SCBR_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
EXPECT_EQ(32, device->GetSupportedLuns());
device_factory.DeleteDevice(device);
}
TEST(DeviceFactoryTest, SCDP_Device_Defaults)
{
Device *device = device_factory.CreateDevice(UNDEFINED, "daynaport");
Device *device = device_factory.CreateDevice(UNDEFINED, "daynaport", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCDP", device->GetType());
EXPECT_FALSE(device->SupportsFile());
EXPECT_TRUE(device->SupportsParams());
EXPECT_FALSE(device->IsProtectable());
@ -232,13 +261,14 @@ TEST(DeviceFactoryTest, SCDP_Device_Defaults)
EXPECT_EQ("SCSI/Link", device->GetProduct());
EXPECT_EQ("1.4a", device->GetRevision());
EXPECT_EQ(32, device->GetSupportedLuns());
device_factory.DeleteDevice(device);
}
TEST(DeviceFactoryTest, SCHS_Device_Defaults)
{
Device *device = device_factory.CreateDevice(UNDEFINED, "services");
Device *device = device_factory.CreateDevice(UNDEFINED, "services", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHS", device->GetType());
EXPECT_FALSE(device->SupportsFile());
EXPECT_FALSE(device->SupportsParams());
EXPECT_FALSE(device->IsProtectable());
@ -256,13 +286,14 @@ TEST(DeviceFactoryTest, SCHS_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
EXPECT_EQ(32, device->GetSupportedLuns());
device_factory.DeleteDevice(device);
}
TEST(DeviceFactoryTest, SCLP_Device_Defaults)
{
Device *device = device_factory.CreateDevice(UNDEFINED, "printer");
Device *device = device_factory.CreateDevice(UNDEFINED, "printer", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCLP", device->GetType());
EXPECT_FALSE(device->SupportsFile());
EXPECT_TRUE(device->SupportsParams());
EXPECT_FALSE(device->IsProtectable());
@ -280,7 +311,5 @@ TEST(DeviceFactoryTest, SCLP_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
EXPECT_EQ(32, device->GetSupportedLuns());
}
device_factory.DeleteDevice(device);
}

View File

@ -5,68 +5,44 @@
//
// Copyright (C) 2022 Uwe Seimet
//
// Unit tests based on GoogleTest and GoogleMock
//
//---------------------------------------------------------------------------
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "testing.h"
#include "rascsi_exceptions.h"
#include "devices/device.h"
#include "gpiobus.h"
#include "../devices/scsihd.h"
#include "../devices/device_factory.h"
using namespace rascsi_interface;
namespace DeviceTest
class TestDevice : public Device
{
public:
class MockController : public SCSIDEV
{
MOCK_METHOD(BUS::phase_t, Process, (int), (override));
MOCK_METHOD(int, GetEffectiveLun, (), ());
MOCK_METHOD(void, Error, (scsi_defs::sense_key, scsi_defs::asc, scsi_defs::status), (override));
MOCK_METHOD(int, GetInitiatorId, (), ());
MOCK_METHOD(void, SetUnit, (int), ());
MOCK_METHOD(void, Connect, (int, BUS *), ());
MOCK_METHOD(void, Status, (), ());
MOCK_METHOD(void, DataIn, (), ());
MOCK_METHOD(void, DataOut, (), ());
MOCK_METHOD(void, BusFree, (), ());
MOCK_METHOD(void, Selection, (), ());
MOCK_METHOD(void, Command, (), ());
MOCK_METHOD(void, MsgIn, (), ());
MOCK_METHOD(void, MsgOut, (), ());
MOCK_METHOD(void, Send, (), (override));
MOCK_METHOD(bool, XferMsg, (int), ());
MOCK_METHOD(bool, XferIn, (BYTE *), ());
MOCK_METHOD(bool, XferOut, (bool), (override));
MOCK_METHOD(void, ReceiveBytes, (), ());
MOCK_METHOD(void, Execute, (), (override));
MOCK_METHOD(void, FlushUnit, (), ());
MOCK_METHOD(void, Receive, (), (override));
MOCK_METHOD(bool, HasUnit, (), (const override));
TestDevice() : Device("test") {}
~TestDevice() {}
FRIEND_TEST(DeviceTest, TestUnitReady);
MockController() { }
~MockController() { }
bool Dispatch() { return false; }
};
DeviceFactory& device_factory = DeviceFactory::instance();
TEST(DeviceTest, TestUnitReady)
TEST(DeviceTest, ProductData)
{
MockController controller;
Device *device = device_factory.CreateDevice(SCHD, "test");
TestDevice device;
controller.ctrl.cmd[0] = eCmdTestUnitReady;
EXPECT_THROW(device.SetVendor(""), illegal_argument_exception);
EXPECT_THROW(device.SetVendor("123456789"), illegal_argument_exception);
device.SetVendor("12345678");
EXPECT_EQ("12345678", device.GetVendor());
device->SetReady(false);
EXPECT_CALL(controller, Error);
EXPECT_TRUE(device->Dispatch(&controller));
// TODO Add tests for a device that is ready after the SASI code removal
}
EXPECT_THROW(device.SetProduct(""), illegal_argument_exception);
EXPECT_THROW(device.SetProduct("12345678901234567"), illegal_argument_exception);
device.SetProduct("1234567890123456");
EXPECT_EQ("1234567890123456", device.GetProduct());
EXPECT_THROW(device.SetRevision(""), illegal_argument_exception);
EXPECT_THROW(device.SetRevision("12345"), illegal_argument_exception);
device.SetRevision("1234");
EXPECT_EQ("1234", device.GetRevision());
device.SetVendor("V");
device.SetProduct("P");
device.SetRevision("R");
EXPECT_EQ("V P R ", device.GetPaddedName());
}

View File

@ -0,0 +1,43 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "testing.h"
#include "devices/file_support.h"
class TestFileSupport : public FileSupport
{
void Open(const Filepath&) {}
};
TEST(FileSupportTest, FileSupport)
{
const int ID = 1;
const int LUN = 2;
Filepath path;
path.SetPath("path");
TestFileSupport file_support;
file_support.SetPath(path);
Filepath result;
file_support.GetPath(result);
EXPECT_STREQ("path", result.GetPath());
int id;
int lun;
EXPECT_FALSE(FileSupport::GetIdsForReservedFile(path, id, lun));
file_support.ReserveFile(path, ID, LUN);
EXPECT_TRUE(FileSupport::GetIdsForReservedFile(path, id, lun));
EXPECT_EQ(ID, id);
EXPECT_EQ(LUN, lun);
file_support.UnreserveFile();
EXPECT_FALSE(FileSupport::GetIdsForReservedFile(path, id, lun));
}

View File

@ -5,94 +5,22 @@
//
// Copyright (C) 2022 Uwe Seimet
//
// Unit tests based on GoogleTest and GoogleMock
//
//---------------------------------------------------------------------------
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "../devices/scsihd.h"
#include "../devices/scsihd_nec.h"
#include "../devices/scsicd.h"
#include "../devices/scsimo.h"
#include "../devices/host_services.h"
#include "testing.h"
#include "spdlog/spdlog.h"
#include "rascsi_exceptions.h"
#include "devices/scsi_command_util.h"
#include "devices/scsihd.h"
#include "devices/scsihd_nec.h"
#include "devices/scsicd.h"
#include "devices/scsimo.h"
#include "devices/host_services.h"
using namespace std;
namespace ModePagesTest
{
unordered_set<uint32_t> sector_sizes;
class MockModePageDevice : public ModePageDevice
{
public:
MockModePageDevice() : ModePageDevice("test") { }
~MockModePageDevice() { }
MOCK_METHOD(vector<BYTE>, Inquiry, (), (const, override));
MOCK_METHOD(int, ModeSense6, (const DWORD *, BYTE *), (override));
MOCK_METHOD(int, ModeSense10, (const DWORD *, BYTE *, int), (override));
void AddModePages(map<int, vector<BYTE>>& pages, int page, bool) const override {
// Return dummy data for other pages than page 0
if (page) {
vector<BYTE> buf(255);
pages[page] = buf;
}
}
// Make protected methods visible for testing
// TODO Why does FRIEND_TEST not work for this method?
int AddModePages(const DWORD *cdb, BYTE *buf, int max_length) {
return ModePageDevice::AddModePages(cdb, buf, max_length);
}
};
class MockSCSIHD : public SCSIHD
{
FRIEND_TEST(ModePagesTest, SCSIHD_AddModePages);
MockSCSIHD(const unordered_set<uint32_t>& sector_sizes) : SCSIHD(sector_sizes, false) { };
~MockSCSIHD() { };
};
class MockSCSIHD_NEC : public SCSIHD_NEC
{
FRIEND_TEST(ModePagesTest, SCSIHD_NEC_AddModePages);
MockSCSIHD_NEC(const unordered_set<uint32_t>& sector_sizes) : SCSIHD_NEC(sector_sizes) { };
~MockSCSIHD_NEC() { };
};
class MockSCSICD : public SCSICD
{
FRIEND_TEST(ModePagesTest, SCSICD_AddModePages);
MockSCSICD(const unordered_set<uint32_t>& sector_sizes) : SCSICD(sector_sizes) { };
~MockSCSICD() { };
};
class MockSCSIMO : public SCSIMO
{
FRIEND_TEST(ModePagesTest, SCSIMO_AddModePages);
MockSCSIMO(const unordered_set<uint32_t>& sector_sizes, const unordered_map<uint64_t, Geometry>& geometries)
: SCSIMO(sector_sizes, geometries) { };
~MockSCSIMO() { };
};
class MockHostServices : public HostServices
{
FRIEND_TEST(ModePagesTest, HostServices_AddModePages);
MockHostServices() { };
~MockHostServices() { };
};
TEST(ModePagesTest, ModePageDevice_AddModePages)
{
DWORD cdb[6];
@ -104,7 +32,7 @@ TEST(ModePagesTest, ModePageDevice_AddModePages)
EXPECT_EQ(1, device.AddModePages(cdb, buf, 1)) << "Allocation length was not limited";
cdb[2] = 0x00;
EXPECT_EQ(0, device.AddModePages(cdb, buf, 12)) << "Data for non-existing code page 0 were returned";
EXPECT_THROW(device.AddModePages(cdb, buf, 12), scsi_error_exception) << "Data for non-existing code page 0 were returned";
}
TEST(ModePagesTest, SCSIHD_AddModePages)
@ -181,4 +109,38 @@ TEST(ModePagesTest, HostServices_AddModePages)
EXPECT_EQ(10, mode_pages[32].size());
}
TEST(ModePagesTest, ModeSelect)
{
const int LENGTH = 12;
DWORD cdb[16] = {};
BYTE buf[255] = {};
// PF (vendor-specific parameter format)
cdb[1] = 0x00;
EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf, LENGTH, 0), scsi_error_exception)
<< "Vendor-specific parameters are not supported";
// PF (standard parameter format)
cdb[1] = 0x10;
// Request 512 bytes per sector
buf[9] = 0x00;
buf[10] = 0x02;
buf[11] = 0x00;
EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf, LENGTH, 256), scsi_error_exception)
<< "Requested sector size does not match current sector size";
// Page 0
buf[LENGTH] = 0x00;
EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf, LENGTH + 2, 512), scsi_error_exception)
<< "Unsupported page 0 was not rejected";
// Page 3 (Format Device Page)
buf[LENGTH] = 0x03;
EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf, LENGTH + 2, 512), scsi_error_exception)
<< "Requested sector size does not match current sector size";
// Match the requested to the current sector size
buf[LENGTH + 12] = 0x02;
scsi_command_util::ModeSelect(cdb, buf, LENGTH + 2, 512);
}

View File

@ -0,0 +1,191 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "testing.h"
#include "rascsi_exceptions.h"
#include "devices/primary_device.h"
#include "devices/device_factory.h"
TEST(PrimaryDeviceTest, UnitReady)
{
MockScsiController controller(nullptr, 0);
MockPrimaryDevice device;
controller.AddDevice(&device);
controller.ctrl.cmd[0] = eCmdTestUnitReady;
device.SetReset(true);
device.SetAttn(true);
device.SetReady(false);
EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(), scsi_error_exception);
device.SetReset(false);
EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(), scsi_error_exception);
device.SetReset(true);
device.SetAttn(false);
EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(), scsi_error_exception);
device.SetReset(false);
device.SetAttn(true);
EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(), scsi_error_exception);
device.SetAttn(false);
EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(), scsi_error_exception);
device.SetReady(true);
EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(device.Dispatch());
EXPECT_TRUE(controller.ctrl.status == scsi_defs::status::GOOD);
}
TEST(PrimaryDeviceTest, Inquiry)
{
MockScsiController controller(nullptr, 0);
MockPrimaryDevice device;
device.SetController(&controller);
controller.ctrl.cmd[0] = eCmdInquiry;
// ALLOCATION LENGTH
controller.ctrl.cmd[4] = 255;
ON_CALL(device, InquiryInternal()).WillByDefault([&device]() {
return device.HandleInquiry(device_type::PROCESSOR, scsi_level::SPC_3, false);
});
EXPECT_CALL(device, InquiryInternal()).Times(1);
EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device.Dispatch());
EXPECT_EQ(0x7F, controller.ctrl.buffer[0]) << "Invalid LUN was not reported";
EXPECT_TRUE(controller.AddDevice(&device));
EXPECT_FALSE(controller.AddDevice(&device)) << "Duplicate LUN was not rejected";
EXPECT_CALL(device, InquiryInternal()).Times(1);
EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device.Dispatch());
EXPECT_EQ(device_type::PROCESSOR, controller.ctrl.buffer[0]);
EXPECT_EQ(0x00, controller.ctrl.buffer[1]) << "Device was not reported as non-removable";
EXPECT_EQ(scsi_level::SPC_3, controller.ctrl.buffer[2]) << "Wrong SCSI level";
EXPECT_EQ(scsi_level::SCSI_2, controller.ctrl.buffer[3]) << "Wrong response level";
EXPECT_EQ(0x1F, controller.ctrl.buffer[4]) << "Wrong additional data size";
ON_CALL(device, InquiryInternal()).WillByDefault([&device]() {
return device.HandleInquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_1_CCS, true);
});
EXPECT_CALL(device, InquiryInternal()).Times(1);
EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device.Dispatch());
EXPECT_EQ(device_type::DIRECT_ACCESS, controller.ctrl.buffer[0]);
EXPECT_EQ(0x80, controller.ctrl.buffer[1]) << "Device was not reported as removable";
EXPECT_EQ(scsi_level::SCSI_1_CCS, controller.ctrl.buffer[2]) << "Wrong SCSI level";
EXPECT_EQ(scsi_level::SCSI_1_CCS, controller.ctrl.buffer[3]) << "Wrong response level";
EXPECT_EQ(0x1F, controller.ctrl.buffer[4]) << "Wrong additional data size";
controller.ctrl.cmd[1] = 0x01;
EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(), scsi_error_exception) << "EVPD bit is not supported";
controller.ctrl.cmd[2] = 0x01;
EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(), scsi_error_exception) << "PAGE CODE field is not supported";
controller.ctrl.cmd[1] = 0x00;
controller.ctrl.cmd[2] = 0x00;
// ALLOCATION LENGTH
controller.ctrl.cmd[4] = 1;
EXPECT_CALL(device, InquiryInternal()).Times(1);
EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device.Dispatch());
EXPECT_EQ(0x1F, controller.ctrl.buffer[4]) << "Wrong additional data size";
EXPECT_EQ(1, controller.ctrl.length) << "Wrong ALLOCATION LENGTH handling";
}
TEST(PrimaryDeviceTest, RequestSense)
{
MockScsiController controller(nullptr, 0);
MockPrimaryDevice device;
controller.AddDevice(&device);
controller.ctrl.cmd[0] = eCmdRequestSense;
// ALLOCATION LENGTH
controller.ctrl.cmd[4] = 255;
device.SetReady(false);
EXPECT_THROW(device.Dispatch(), scsi_error_exception);
device.SetReady(true);
EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device.Dispatch());
EXPECT_TRUE(controller.ctrl.status == scsi_defs::status::GOOD);
}
TEST(PrimaryDeviceTest, ReportLuns)
{
const int LUN1 = 1;
const int LUN2 = 4;
MockScsiController controller(nullptr, 0);
MockPrimaryDevice device1;
MockPrimaryDevice device2;
device1.SetLun(LUN1);
controller.AddDevice(&device1);
ASSERT_TRUE(controller.HasDeviceForLun(LUN1));
device2.SetLun(LUN2);
controller.AddDevice(&device2);
ASSERT_TRUE(controller.HasDeviceForLun(LUN2));
controller.ctrl.cmd[0] = eCmdReportLuns;
// ALLOCATION LENGTH
controller.ctrl.cmd[9] = 255;
EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device1.Dispatch());
EXPECT_EQ(0x00, controller.ctrl.buffer[0]) << "Wrong data length";
EXPECT_EQ(0x00, controller.ctrl.buffer[1]) << "Wrong data length";
EXPECT_EQ(0x00, controller.ctrl.buffer[2]) << "Wrong data length";
EXPECT_EQ(0x10, controller.ctrl.buffer[3]) << "Wrong data length";
EXPECT_EQ(0x00, controller.ctrl.buffer[8]) << "Wrong LUN1 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[9]) << "Wrong LUN1 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[10]) << "Wrong LUN1 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[11]) << "Wrong LUN1 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[12]) << "Wrong LUN1 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[13]) << "Wrong LUN1 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[14]) << "Wrong LUN1 number";
EXPECT_EQ(LUN1, controller.ctrl.buffer[15]) << "Wrong LUN1 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[16]) << "Wrong LUN2 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[17]) << "Wrong LUN2 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[18]) << "Wrong LUN2 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[19]) << "Wrong LUN2 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[20]) << "Wrong LUN2 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[21]) << "Wrong LUN2 number";
EXPECT_EQ(0x00, controller.ctrl.buffer[22]) << "Wrong LUN2 number";
EXPECT_EQ(LUN2, controller.ctrl.buffer[23]) << "Wrong LUN2 number";
controller.ctrl.cmd[2] = 0x01;
EXPECT_THROW(device1.Dispatch(), scsi_error_exception) << "Only SELECT REPORT mode 0 is supported";
}
TEST(PrimaryDeviceTest, UnknownCommand)
{
MockScsiController controller(nullptr, 0);
MockPrimaryDevice device;
controller.AddDevice(&device);
controller.ctrl.cmd[0] = 0xFF;
EXPECT_FALSE(device.Dispatch());
}

View File

@ -0,0 +1,22 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "testing.h"
#include "controllers/scsi_controller.h"
TEST(ScsiControllerTest, ScsiController)
{
MockBus bus;
MockScsiController controller(&bus, 0);
EXPECT_EQ(32, controller.GetMaxLuns());
EXPECT_CALL(controller, SetPhase(BUS::phase_t::busfree)).Times(1);
controller.Reset();
}

View File

@ -0,0 +1,38 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include <gtest/gtest.h>
#include "spdlog/spdlog.h"
#include "controllers/controller_manager.h"
#include "devices/device_factory.h"
class Environment : public ::testing::Environment
{
public:
Environment() {}
~Environment() {}
// Turn off logging
void SetUp() override { spdlog::set_level(spdlog::level::off); }
};
DeviceFactory& device_factory = DeviceFactory::instance();
ControllerManager& controller_manager = ControllerManager::instance();
int main(int, char*[])
{
testing::AddGlobalTestEnvironment(new Environment());
testing::InitGoogleTest();
return RUN_ALL_TESTS();
}

Some files were not shown because too many files have changed in this diff Show More