From ddeede2bebe3c56f038302aadbd36669627749dc Mon Sep 17 00:00:00 2001
From: Uwe Seimet <48174652+uweseimet@users.noreply.github.com>
Date: Sat, 3 Sep 2022 16:53:53 +0200
Subject: [PATCH] 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
---
doc/rascsi.1 | 13 +-
doc/rascsi_man_page.txt | 40 +-
doc/rasctl.1 | 2 +-
doc/rasctl_man_page.txt | 19 +-
doc/sasidump.1 | 41 -
python/common/src/rascsi/ractl_cmds.py | 5 +-
python/web/src/templates/index.html | 12 +-
python/web/src/web_utils.py | 6 +-
src/raspberrypi/.gitignore | 8 +-
src/raspberrypi/Makefile | 75 +-
src/raspberrypi/command_context.h | 4 +-
src/raspberrypi/config.h | 3 +-
.../controllers/abstract_controller.cpp | 59 +
.../controllers/abstract_controller.h | 97 ++
.../controllers/controller_manager.cpp | 87 ++
.../controllers/controller_manager.h | 39 +
src/raspberrypi/controllers/phase_handler.h | 33 +
src/raspberrypi/controllers/sasidev_ctrl.cpp | 1093 ---------------
src/raspberrypi/controllers/sasidev_ctrl.h | 171 ---
.../controllers/scsi_controller.cpp | 1231 +++++++++++++++++
src/raspberrypi/controllers/scsi_controller.h | 135 ++
src/raspberrypi/controllers/scsidev_ctrl.cpp | 918 ------------
src/raspberrypi/controllers/scsidev_ctrl.h | 102 --
src/raspberrypi/devices/cfilesystem.cpp | 9 +-
src/raspberrypi/devices/cfilesystem.h | 2 +-
src/raspberrypi/devices/ctapdriver.cpp | 30 +-
src/raspberrypi/devices/ctapdriver.h | 14 +-
src/raspberrypi/devices/device.cpp | 35 +-
src/raspberrypi/devices/device.h | 112 +-
src/raspberrypi/devices/device_factory.cpp | 258 ++--
src/raspberrypi/devices/device_factory.h | 19 +-
src/raspberrypi/devices/disk.cpp | 513 +++----
src/raspberrypi/devices/disk.h | 153 +-
src/raspberrypi/devices/disk_track_cache.cpp | 26 +-
src/raspberrypi/devices/dispatcher.h | 24 +-
src/raspberrypi/devices/file_support.cpp | 10 +-
src/raspberrypi/devices/file_support.h | 11 +-
src/raspberrypi/devices/host_services.cpp | 62 +-
src/raspberrypi/devices/host_services.h | 17 +-
.../devices/interfaces/scsi_block_commands.h | 18 +-
.../devices/interfaces/scsi_mmc_commands.h | 8 +-
.../interfaces/scsi_primary_commands.h | 12 +-
.../interfaces/scsi_printer_commands.h | 12 +-
src/raspberrypi/devices/mode_page_device.cpp | 68 +-
src/raspberrypi/devices/mode_page_device.h | 21 +-
src/raspberrypi/devices/primary_device.cpp | 108 +-
src/raspberrypi/devices/primary_device.h | 36 +-
src/raspberrypi/devices/sasihd.cpp | 130 --
src/raspberrypi/devices/sasihd.h | 39 -
src/raspberrypi/devices/scsi_command_util.cpp | 89 ++
src/raspberrypi/devices/scsi_command_util.h | 24 +
src/raspberrypi/devices/scsi_daynaport.cpp | 103 +-
src/raspberrypi/devices/scsi_daynaport.h | 30 +-
src/raspberrypi/devices/scsi_host_bridge.cpp | 57 +-
src/raspberrypi/devices/scsi_host_bridge.h | 40 +-
src/raspberrypi/devices/scsi_printer.cpp | 101 +-
src/raspberrypi/devices/scsi_printer.h | 35 +-
src/raspberrypi/devices/scsicd.cpp | 109 +-
src/raspberrypi/devices/scsicd.h | 24 +-
src/raspberrypi/devices/scsihd.cpp | 92 +-
src/raspberrypi/devices/scsihd.h | 5 +-
src/raspberrypi/devices/scsihd_nec.cpp | 10 +-
src/raspberrypi/devices/scsihd_nec.h | 8 +-
src/raspberrypi/devices/scsimo.cpp | 90 +-
src/raspberrypi/devices/scsimo.h | 15 +-
src/raspberrypi/exceptions.h | 49 -
src/raspberrypi/fileio.cpp | 2 -
src/raspberrypi/fileio.h | 2 +-
src/raspberrypi/filepath.cpp | 8 -
src/raspberrypi/gpiobus.cpp | 275 +---
src/raspberrypi/gpiobus.h | 95 +-
src/raspberrypi/localizer.cpp | 8 -
src/raspberrypi/localizer.h | 4 +-
src/raspberrypi/os.h | 8 -
src/raspberrypi/protobuf_util.cpp | 21 +-
src/raspberrypi/protobuf_util.h | 4 +-
src/raspberrypi/rascsi.cpp | 509 ++-----
src/raspberrypi/rascsi_exceptions.h | 62 +
src/raspberrypi/rascsi_image.cpp | 2 -
src/raspberrypi/rascsi_image.h | 2 +-
src/raspberrypi/rascsi_interface.proto | 8 +-
src/raspberrypi/rascsi_response.cpp | 53 +-
src/raspberrypi/rascsi_response.h | 20 +-
src/raspberrypi/rasctl.cpp | 4 +-
src/raspberrypi/rasctl_commands.cpp | 12 +-
src/raspberrypi/rasctl_display.cpp | 32 +-
src/raspberrypi/rasdump.cpp | 15 +-
src/raspberrypi/sasidump.cpp | 745 ----------
src/raspberrypi/scsi.cpp | 1 -
src/raspberrypi/scsi.h | 61 +-
src/raspberrypi/scsimon.cpp | 10 +-
.../test/abstract_controller_test.cpp | 33 +
.../test/controller_manager_test.cpp | 33 +
src/raspberrypi/test/device_factory_test.cpp | 95 +-
src/raspberrypi/test/device_test.cpp | 80 +-
src/raspberrypi/test/file_support_test.cpp | 43 +
src/raspberrypi/test/mode_pages_test.cpp | 126 +-
src/raspberrypi/test/primary_device_test.cpp | 191 +++
src/raspberrypi/test/scsi_controller_test.cpp | 22 +
src/raspberrypi/test/test_setup.cpp | 38 +
src/raspberrypi/test/testing.h | 229 +++
101 files changed, 3966 insertions(+), 5708 deletions(-)
delete mode 100644 doc/sasidump.1
create mode 100644 src/raspberrypi/controllers/abstract_controller.cpp
create mode 100644 src/raspberrypi/controllers/abstract_controller.h
create mode 100644 src/raspberrypi/controllers/controller_manager.cpp
create mode 100644 src/raspberrypi/controllers/controller_manager.h
create mode 100644 src/raspberrypi/controllers/phase_handler.h
delete mode 100644 src/raspberrypi/controllers/sasidev_ctrl.cpp
delete mode 100644 src/raspberrypi/controllers/sasidev_ctrl.h
create mode 100644 src/raspberrypi/controllers/scsi_controller.cpp
create mode 100644 src/raspberrypi/controllers/scsi_controller.h
delete mode 100644 src/raspberrypi/controllers/scsidev_ctrl.cpp
delete mode 100644 src/raspberrypi/controllers/scsidev_ctrl.h
delete mode 100644 src/raspberrypi/devices/sasihd.cpp
delete mode 100644 src/raspberrypi/devices/sasihd.h
create mode 100644 src/raspberrypi/devices/scsi_command_util.cpp
create mode 100644 src/raspberrypi/devices/scsi_command_util.h
delete mode 100644 src/raspberrypi/exceptions.h
create mode 100644 src/raspberrypi/rascsi_exceptions.h
delete mode 100644 src/raspberrypi/sasidump.cpp
create mode 100644 src/raspberrypi/test/abstract_controller_test.cpp
create mode 100644 src/raspberrypi/test/controller_manager_test.cpp
create mode 100644 src/raspberrypi/test/file_support_test.cpp
create mode 100644 src/raspberrypi/test/primary_device_test.cpp
create mode 100644 src/raspberrypi/test/scsi_controller_test.cpp
create mode 100644 src/raspberrypi/test/test_setup.cpp
create mode 100644 src/raspberrypi/test/testing.h
diff --git a/doc/rascsi.1 b/doc/rascsi.1
index 8c63bc13..5b7c0877 100644
--- a/doc/rascsi.1
+++ b/doc/rascsi.1
@@ -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:
diff --git a/doc/rascsi_man_page.txt b/doc/rascsi_man_page.txt
index 1f6d8518..f2187222 100644
--- a/doc/rascsi_man_page.txt
+++ b/doc/rascsi_man_page.txt
@@ -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:
diff --git a/doc/rasctl.1 b/doc/rasctl.1
index 61b7a153..ca327c0c 100644
--- a/doc/rasctl.1
+++ b/doc/rasctl.1
@@ -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.
diff --git a/doc/rasctl_man_page.txt b/doc/rasctl_man_page.txt
index ad1f4da7..ff3b59ca 100644
--- a/doc/rasctl_man_page.txt
+++ b/doc/rasctl_man_page.txt
@@ -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
diff --git a/doc/sasidump.1 b/doc/sasidump.1
deleted file mode 100644
index ce6df31c..00000000
--- a/doc/sasidump.1
+++ /dev/null
@@ -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:
diff --git a/python/common/src/rascsi/ractl_cmds.py b/python/common/src/rascsi/ractl_cmds.py
index cd0dc2bb..d505901e 100644
--- a/python/common/src/rascsi/ractl_cmds.py
+++ b/python/common/src/rascsi/ractl_cmds.py
@@ -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,
diff --git a/python/web/src/templates/index.html b/python/web/src/templates/index.html
index 5a8cc522..493063fd 100644
--- a/python/web/src/templates/index.html
+++ b/python/web/src/templates/index.html
@@ -543,9 +543,10 @@
{{ _("Create Empty Disk Image File") }}
-
{{ _("The Generic image type is recommended for most computer platforms.") }}
-
{{ _("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.") }}
-
{{ _("SASI images should only be used on the original Sharp X68000, or other legacy systems that utilize this pre-SCSI standard.") }}
+
{{ _("The Generic Hard Disk image type is recommended for most computer platforms.") }}
+
{{ _("APPLE GENUINE is appropriate for Apple Macintosh computers.") }}
+
{{ _("NEC GENUINE is appropriate for NEC PC-98 computers.") }}
+
{{ _("The Generic Removable Disk image type can be used with SCSI floppy drives, SyQuest drives, Zip drives etc.") }}
@@ -566,10 +567,7 @@
{{ _("SCSI Hard Disk image (NEC GENUINE) [.hdn]") }}
-
diff --git a/python/web/src/web_utils.py b/python/web/src/web_utils.py
index 3f87df12..98dcbc31 100644
--- a/python/web/src/web_utils.py
+++ b/python/web/src/web_utils.py
@@ -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":
diff --git a/src/raspberrypi/.gitignore b/src/raspberrypi/.gitignore
index 321cbbb5..e5c2d4e4 100644
--- a/src/raspberrypi/.gitignore
+++ b/src/raspberrypi/.gitignore
@@ -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
diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile
index 99473a8e..64d48cfb 100644
--- a/src/raspberrypi/Makefile
+++ b/src/raspberrypi/Makefile
@@ -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)
diff --git a/src/raspberrypi/command_context.h b/src/raspberrypi/command_context.h
index 7bbe6bc3..b9f16490 100644
--- a/src/raspberrypi/command_context.h
+++ b/src/raspberrypi/command_context.h
@@ -12,6 +12,6 @@
#include
struct CommandContext {
- int fd;
- std::string locale;
+ int fd = -1;
+ std::string locale = "";
};
diff --git a/src/raspberrypi/config.h b/src/raspberrypi/config.h
index abb4d8c5..467a9c6b 100644
--- a/src/raspberrypi/config.h
+++ b/src/raspberrypi/config.h
@@ -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
diff --git a/src/raspberrypi/controllers/abstract_controller.cpp b/src/raspberrypi/controllers/abstract_controller.cpp
new file mode 100644
index 00000000..b6f6207f
--- /dev/null
+++ b/src/raspberrypi/controllers/abstract_controller.cpp
@@ -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;
+}
diff --git a/src/raspberrypi/controllers/abstract_controller.h b/src/raspberrypi/controllers/abstract_controller.h
new file mode 100644
index 00000000..ef63a58e
--- /dev/null
+++ b/src/raspberrypi/controllers/abstract_controller.h
@@ -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
+
+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 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 = {};
+};
diff --git a/src/raspberrypi/controllers/controller_manager.cpp b/src/raspberrypi/controllers/controller_manager.cpp
new file mode 100644
index 00000000..98961141
--- /dev/null
+++ b/src/raspberrypi/controllers/controller_manager.cpp
@@ -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 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;
+}
diff --git a/src/raspberrypi/controllers/controller_manager.h b/src/raspberrypi/controllers/controller_manager.h
new file mode 100644
index 00000000..15ee9571
--- /dev/null
+++ b/src/raspberrypi/controllers/controller_manager.h
@@ -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
+
+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 controllers;
+};
diff --git a/src/raspberrypi/controllers/phase_handler.h b/src/raspberrypi/controllers/phase_handler.h
new file mode 100644
index 00000000..55b7a0f7
--- /dev/null
+++ b/src/raspberrypi/controllers/phase_handler.h
@@ -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;
+};
+
diff --git a/src/raspberrypi/controllers/sasidev_ctrl.cpp b/src/raspberrypi/controllers/sasidev_ctrl.cpp
deleted file mode 100644
index 2067846f..00000000
--- a/src/raspberrypi/controllers/sasidev_ctrl.cpp
+++ /dev/null
@@ -1,1093 +0,0 @@
-//---------------------------------------------------------------------------
-//
-// SCSI Target Emulator RaSCSI Reloaded
-// for Raspberry Pi
-//
-// Copyright (C) 2001-2006 PI.(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 ]
-//
-//---------------------------------------------------------------------------
-#include "controllers/sasidev_ctrl.h"
-#include "filepath.h"
-#include "gpiobus.h"
-#include "devices/scsi_host_bridge.h"
-#include "devices/scsi_daynaport.h"
-#include
-
-//===========================================================================
-//
-// SASI Device
-//
-//===========================================================================
-
-//---------------------------------------------------------------------------
-//
-// Constructor
-//
-//---------------------------------------------------------------------------
-SASIDEV::SASIDEV()
-{
- // Work initialization
- ctrl.phase = BUS::busfree;
- ctrl.m_scsi_id = UNKNOWN_SCSI_ID;
- ctrl.bus = NULL;
- memset(ctrl.cmd, 0x00, sizeof(ctrl.cmd));
- ctrl.status = 0x00;
- ctrl.message = 0x00;
- ctrl.execstart = 0;
- // The initial buffer size will default to either the default buffer size OR
- // the size of an Ethernet message, whichever is larger.
- ctrl.bufsize = std::max(DEFAULT_BUFFER_SIZE, ETH_FRAME_LEN + 16 + ETH_FCS_LEN);
- ctrl.buffer = (BYTE *)malloc(ctrl.bufsize);
- memset(ctrl.buffer, 0x00, ctrl.bufsize);
- ctrl.blocks = 0;
- ctrl.next = 0;
- ctrl.offset = 0;
- ctrl.length = 0;
- ctrl.lun = -1;
-
- // Logical unit initialization
- for (int i = 0; i < UnitMax; i++) {
- ctrl.unit[i] = NULL;
- }
-}
-
-//---------------------------------------------------------------------------
-//
-// Destructor
-//
-//---------------------------------------------------------------------------
-SASIDEV::~SASIDEV()
-{
- // Free the buffer
- if (ctrl.buffer) {
- free(ctrl.buffer);
- ctrl.buffer = NULL;
- }
-}
-
-//---------------------------------------------------------------------------
-//
-// Device reset
-//
-//---------------------------------------------------------------------------
-void SASIDEV::Reset()
-{
- // Work initialization
- memset(ctrl.cmd, 0x00, sizeof(ctrl.cmd));
- ctrl.phase = BUS::busfree;
- ctrl.status = 0x00;
- ctrl.message = 0x00;
- ctrl.execstart = 0;
- memset(ctrl.buffer, 0x00, ctrl.bufsize);
- ctrl.blocks = 0;
- ctrl.next = 0;
- ctrl.offset = 0;
- ctrl.length = 0;
-
- // Unit initialization
- for (int i = 0; i < UnitMax; i++) {
- if (ctrl.unit[i]) {
- ctrl.unit[i]->Reset();
- }
- }
-}
-
-//---------------------------------------------------------------------------
-//
-// Connect the controller
-//
-//---------------------------------------------------------------------------
-void SASIDEV::Connect(int id, BUS *bus)
-{
- ctrl.m_scsi_id = id;
- ctrl.bus = bus;
-}
-
-//---------------------------------------------------------------------------
-//
-// Set the logical unit
-//
-//---------------------------------------------------------------------------
-void SASIDEV::SetUnit(int no, PrimaryDevice *dev)
-{
- ASSERT(no < UnitMax);
-
- ctrl.unit[no] = dev;
-}
-
-//---------------------------------------------------------------------------
-//
-// Check to see if this has a valid LUN
-//
-//---------------------------------------------------------------------------
-bool SASIDEV::HasUnit()
-{
- for (int i = 0; i < UnitMax; i++) {
- if (ctrl.unit[i]) {
- return true;
- }
- }
-
- return false;
-}
-
-//---------------------------------------------------------------------------
-//
-// Run
-//
-//---------------------------------------------------------------------------
-BUS::phase_t SASIDEV::Process(int)
-{
- // Do nothing if not connected
- if (ctrl.m_scsi_id < 0 || ctrl.bus == NULL) {
- return ctrl.phase;
- }
-
- // Get bus information
- ((GPIOBUS*)ctrl.bus)->Aquire();
-
- // For the monitor tool, we shouldn't need to reset. We're just logging information
- // Reset
- if (ctrl.bus->GetRST()) {
- LOGINFO("RESET signal received");
-
- // Reset the controller
- Reset();
-
- // Reset the bus
- ctrl.bus->Reset();
- return ctrl.phase;
- }
-
- // Phase processing
- switch (ctrl.phase) {
- // Bus free
- 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;
-
- // Msg in (MCI=111)
- case BUS::msgin:
- MsgIn();
- break;
-
- // Other
- default:
- ASSERT(FALSE);
- break;
- }
-
- return ctrl.phase;
-}
-
-//---------------------------------------------------------------------------
-//
-// Bus free phase
-//
-//---------------------------------------------------------------------------
-void SASIDEV::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;
-
- ctrl.lun = -1;
-
- return;
- }
-
- // Move to selection phase
- if (ctrl.bus->GetSEL() && !ctrl.bus->GetBSY()) {
- Selection();
- }
-}
-
-//---------------------------------------------------------------------------
-//
-// Selection phase
-//
-//---------------------------------------------------------------------------
-void SASIDEV::Selection()
-{
- // Phase change
- if (ctrl.phase != BUS::selection) {
- // Invalid if IDs do not match
- DWORD 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);
-
- // Phase change
- ctrl.phase = BUS::selection;
-
- // Raiase BSY and respond
- ctrl.bus->SetBSY(true);
- return;
- }
-
- // Command phase shifts when selection is completed
- if (!ctrl.bus->GetSEL() && ctrl.bus->GetBSY()) {
- Command();
- }
-}
-
-//---------------------------------------------------------------------------
-//
-// Command phase (used by SASI and SCSI)
-//
-//---------------------------------------------------------------------------
-void SASIDEV::Command()
-{
- // Phase change
- if (ctrl.phase != BUS::command) {
- LOGTRACE("%s Command Phase", __PRETTY_FUNCTION__);
-
- // Phase Setting
- ctrl.phase = BUS::command;
-
- // Signal line operated by the target
- ctrl.bus->SetMSG(FALSE);
- ctrl.bus->SetCD(TRUE);
- ctrl.bus->SetIO(FALSE);
-
- // Data transfer is 6 bytes x 1 block
- ctrl.offset = 0;
- ctrl.length = 6;
- ctrl.blocks = 1;
-
- // If no byte can be received move to the status phase
- int count = ctrl.bus->CommandHandShake(ctrl.buffer, IsSASI());
- if (!count) {
- Error();
- return;
- }
-
- ctrl.length = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]);
-
- // If not able to receive all, move to the status phase
- if (count != (int)ctrl.length) {
- Error();
- return;
- }
-
- // Command data transfer
- for (int i = 0; i < (int)ctrl.length; i++) {
- ctrl.cmd[i] = (DWORD)ctrl.buffer[i];
- LOGTRACE("%s CDB[%d]=$%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i]);
- }
-
- // Clear length and block
- ctrl.length = 0;
- ctrl.blocks = 0;
-
- // Execution Phase
- Execute();
- }
-}
-
-//---------------------------------------------------------------------------
-//
-// Execution Phase
-//
-//---------------------------------------------------------------------------
-void SASIDEV::Execute()
-{
- LOGTRACE("%s Execution Phase Command %02X", __PRETTY_FUNCTION__, (WORD)ctrl.cmd[0]);
-
- // Phase Setting
- ctrl.phase = BUS::execute;
-
- // Initialization for data transfer
- ctrl.offset = 0;
- ctrl.blocks = 1;
- ctrl.execstart = SysTimer::GetTimerLow();
-
- int lun = GetEffectiveLun();
- if (!ctrl.unit[lun]) {
- ctrl.device->SetStatusCode(STATUS_INVALIDLUN);
- Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN);
- return;
- }
-
- ctrl.device = ctrl.unit[lun];
- ctrl.device->SetCtrl(&ctrl);
-
- // Discard pending sense data from the previous command if the current command is not REQUEST SENSE
- if ((SASIDEV::sasi_command)ctrl.cmd[0] != SASIDEV::eCmdRequestSense) {
- ctrl.status = 0;
- }
-
- // Process by command
- switch ((SASIDEV::sasi_command)ctrl.cmd[0]) {
- case SASIDEV::eCmdTestUnitReady:
- LOGTRACE( "%s TEST UNIT READY Command", __PRETTY_FUNCTION__);
- ctrl.device->TestUnitReady(this);
- return;
-
- case SASIDEV::eCmdRezero:
- LOGTRACE( "%s REZERO Command", __PRETTY_FUNCTION__);
- ((Disk *)ctrl.device)->Rezero(this);
- return;
-
- case SASIDEV::eCmdRequestSense:
- LOGTRACE( "%s REQUEST SENSE Command", __PRETTY_FUNCTION__);
- ctrl.device->RequestSense(this);
- return;
-
- // FORMAT (the old RaSCSI code used 0x06 as opcode, which is not compliant with the SASI specification)
- // The FORMAT command of RaSCSI does not do anything but just returns a GOOD status
- case SASIDEV::eCmdFormat:
- case SASIDEV::eCmdFormatLegacy:
- LOGTRACE( "%s FORMAT UNIT Command", __PRETTY_FUNCTION__);
- ((Disk *)ctrl.device)->FormatUnit(this);
- return;
-
- case SASIDEV::eCmdReadCapacity:
- LOGTRACE( "%s READ CAPACITY Command", __PRETTY_FUNCTION__);
- ((Disk *)ctrl.device)->ReadCapacity10(this);
- return;
-
- case SASIDEV::eCmdReassign:
- LOGTRACE( "%s REASSIGN BLOCKS Command", __PRETTY_FUNCTION__);
- ((Disk *)ctrl.device)->ReassignBlocks(this);
- return;
-
- case SASIDEV::eCmdRead6:
- LOGTRACE( "%s READ Command", __PRETTY_FUNCTION__);
- ((Disk *)ctrl.device)->Read6(this);
- return;
-
- case SASIDEV::eCmdWrite6:
- LOGTRACE( "%s WRITE Command", __PRETTY_FUNCTION__);
- ((Disk *)ctrl.device)->Write6(this);
- return;
-
- case SASIDEV::eCmdSeek6:
- LOGTRACE( "%s SEEK Command", __PRETTY_FUNCTION__);
- ((Disk *)ctrl.device)->Seek(this);
- return;
-
- case SASIDEV::eCmdInquiry:
- LOGTRACE( "%s INQUIRY Command", __PRETTY_FUNCTION__);
- ((Disk *)ctrl.device)->Inquiry(this);
- return;
-
- // ASSIGN (SASI only)
- // This doesn't exist in the SASI Spec, but was in the original RaSCSI code.
- // leaving it here for now....
- case SASIDEV::eCmdSasiCmdAssign:
- CmdAssign();
- return;
-
- case SASIDEV::eCmdReserve6:
- LOGTRACE( "%s RESERVE Command", __PRETTY_FUNCTION__);
- ((Disk *)ctrl.device)->Reserve(this);
- return;
-
- case eCmdRelease6:
- LOGTRACE( "%s RELEASE Command", __PRETTY_FUNCTION__);
- ((Disk *)ctrl.device)->Release(this);
- return;
-
- // SPECIFY (SASI only)
- // This doesn't exist in the SASI Spec, but was in the original RaSCSI code.
- // leaving it here for now....
- case SASIDEV::eCmdInvalid:
- CmdSpecify();
- return;
-
- default:
- break;
- }
-
- LOGTRACE("%s ID %d received unsupported command: $%02X", __PRETTY_FUNCTION__, GetSCSIID(), (BYTE)ctrl.cmd[0]);
-
- ctrl.device->SetStatusCode(STATUS_INVALIDCMD);
-
- Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
-}
-
-//---------------------------------------------------------------------------
-//
-// Status phase
-//
-//---------------------------------------------------------------------------
-void SASIDEV::Status()
-{
- // Phase change
- if (ctrl.phase != BUS::status) {
- // Minimum execution time
- if (ctrl.execstart > 0) {
- DWORD min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
- DWORD time = SysTimer::GetTimerLow() - ctrl.execstart;
- if (time < min_exec_time) {
- SysTimer::SleepUsec(min_exec_time - time);
- }
- ctrl.execstart = 0;
- } else {
- SysTimer::SleepUsec(5);
- }
-
- LOGTRACE("%s Status phase", __PRETTY_FUNCTION__);
-
- // Phase Setting
- ctrl.phase = BUS::status;
-
- // Signal line operated by the target
- ctrl.bus->SetMSG(FALSE);
- ctrl.bus->SetCD(TRUE);
- ctrl.bus->SetIO(TRUE);
-
- // Data transfer is 1 byte x 1 block
- ctrl.offset = 0;
- ctrl.length = 1;
- ctrl.blocks = 1;
- ctrl.buffer[0] = (BYTE)ctrl.status;
-
- LOGTRACE( "%s Status Phase $%02X",__PRETTY_FUNCTION__, (unsigned int)ctrl.status);
-
- return;
- }
-
- // Send
- Send();
-}
-
-//---------------------------------------------------------------------------
-//
-// Message in phase (used by SASI and SCSI)
-//
-//---------------------------------------------------------------------------
-void SASIDEV::MsgIn()
-{
- // Phase change
- if (ctrl.phase != BUS::msgin) {
- LOGTRACE("%s Starting Message in phase", __PRETTY_FUNCTION__);
-
- // Phase Setting
- ctrl.phase = BUS::msgin;
-
- // Signal line operated by the target
- ctrl.bus->SetMSG(TRUE);
- ctrl.bus->SetCD(TRUE);
- ctrl.bus->SetIO(TRUE);
-
- // length, blocks are already set
- ASSERT(ctrl.length > 0);
- ASSERT(ctrl.blocks > 0);
- ctrl.offset = 0;
- return;
- }
-
- //Send
- LOGTRACE("%s Transitioning to Send()", __PRETTY_FUNCTION__);
- Send();
-}
-
-//---------------------------------------------------------------------------
-//
-// Data-in Phase (used by SASI and SCSI)
-//
-//---------------------------------------------------------------------------
-void SASIDEV::DataIn()
-{
- ASSERT(ctrl.length >= 0);
-
- // Phase change
- if (ctrl.phase != BUS::datain) {
- // Minimum execution time
- if (ctrl.execstart > 0) {
- DWORD min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
- DWORD time = SysTimer::GetTimerLow() - ctrl.execstart;
- if (time < min_exec_time) {
- SysTimer::SleepUsec(min_exec_time - time);
- }
- ctrl.execstart = 0;
- }
-
- // If the length is 0, go to the status phase
- if (ctrl.length == 0) {
- Status();
- return;
- }
-
- LOGTRACE("%s Going into Data-in Phase", __PRETTY_FUNCTION__);
- // Phase Setting
- ctrl.phase = BUS::datain;
-
- // Signal line operated by the target
- ctrl.bus->SetMSG(FALSE);
- ctrl.bus->SetCD(FALSE);
- ctrl.bus->SetIO(TRUE);
-
- // length, blocks are already set
- ASSERT(ctrl.length > 0);
- ASSERT(ctrl.blocks > 0);
- ctrl.offset = 0;
-
- return;
- }
-
- // Send
- Send();
-}
-
-//---------------------------------------------------------------------------
-//
-// Data out phase (used by SASI and SCSI)
-//
-//---------------------------------------------------------------------------
-void SASIDEV::DataOut()
-{
- ASSERT(ctrl.length >= 0);
-
- // Phase change
- if (ctrl.phase != BUS::dataout) {
- // Minimum execution time
- if (ctrl.execstart > 0) {
- DWORD min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
- DWORD time = SysTimer::GetTimerLow() - ctrl.execstart;
- if (time < min_exec_time) {
- SysTimer::SleepUsec(min_exec_time - time);
- }
- ctrl.execstart = 0;
- }
-
- // If the length is 0, go to the status phase
- if (ctrl.length == 0) {
- Status();
- return;
- }
-
- LOGTRACE("%s Data out phase", __PRETTY_FUNCTION__);
-
- // Phase Setting
- ctrl.phase = BUS::dataout;
-
- // Signal line operated by the target
- ctrl.bus->SetMSG(FALSE);
- ctrl.bus->SetCD(FALSE);
- ctrl.bus->SetIO(FALSE);
-
- // Length has already been calculated
- ASSERT(ctrl.length > 0);
- ctrl.offset = 0;
- return;
- }
-
- // Receive
- Receive();
-}
-
-void SASIDEV::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;
- }
-
- // Set status and message
- ctrl.status = (GetEffectiveLun() << 5) | status;
-
- // status phase
- Status();
-}
-
-void SASIDEV::CmdAssign()
-{
- LOGTRACE("%s ASSIGN Command ", __PRETTY_FUNCTION__);
-
- // Command processing on device
- bool status = ctrl.device->CheckReady();
- if (!status) {
- // Failure (Error)
- Error();
- return;
- }
-
- // Request 4 bytes of data
- ctrl.length = 4;
-
- // Write phase
- DataOut();
-}
-
-void SASIDEV::CmdSpecify()
-{
- LOGTRACE("%s SPECIFY Command ", __PRETTY_FUNCTION__);
-
- // Command processing on device
- bool status =ctrl.device->CheckReady();
- if (!status) {
- // Failure (Error)
- Error();
- return;
- }
-
- // Request 10 bytes of data
- ctrl.length = 10;
-
- // Write phase
- DataOut();
-}
-
-//===========================================================================
-//
-// Data transfer
-//
-//===========================================================================
-
-//---------------------------------------------------------------------------
-//
-// Data transmission
-//
-//---------------------------------------------------------------------------
-void SASIDEV::Send()
-{
- ASSERT(!ctrl.bus->GetREQ());
- ASSERT(ctrl.bus->GetIO());
-
- // Check that the length isn't 0
- if (ctrl.length != 0) {
- int len = ctrl.bus->SendHandShake(
- &ctrl.buffer[ctrl.offset], ctrl.length, BUS::SEND_NO_DELAY);
-
- // If you can not send it all, move on to the status phase
- if (len != (int)ctrl.length) {
- LOGERROR("%s ctrl.length (%d) did not match the amount of data sent (%d)",__PRETTY_FUNCTION__, (int)ctrl.length, len);
- Error();
- return;
- }
-
- // Offset and Length
- ctrl.offset += ctrl.length;
- ctrl.length = 0;
- return;
- }
- else{
- LOGINFO("%s ctrl.length was 0", __PRETTY_FUNCTION__);
- }
-
- // Remove block and initialize the result
- ctrl.blocks--;
- bool result = true;
-
- // Process 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 xfer in: %d",__PRETTY_FUNCTION__, result);
- LOGTRACE("%s processing after data collection", __PRETTY_FUNCTION__);
- }
- }
-
- // If result FALSE, move to the status phase
- if (!result) {
- LOGERROR("%s Send result was false", __PRETTY_FUNCTION__);
- Error();
- return;
- }
-
- // Continue sending if block != 0
- if (ctrl.blocks != 0){
- ASSERT(ctrl.length > 0);
- ASSERT(ctrl.offset == 0);
- return;
- }
-
- // Move to the next phase
- switch (ctrl.phase) {
- // Message in phase
- case BUS::msgin:
- // 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;
-
- // Other (impossible)
- default:
- ASSERT(FALSE);
- break;
- }
-}
-
-//---------------------------------------------------------------------------
-//
-// Receive data
-//
-//---------------------------------------------------------------------------
-void SASIDEV::Receive()
-{
- // REQ is low
- ASSERT(!ctrl.bus->GetREQ());
- ASSERT(!ctrl.bus->GetIO());
-
- // Length != 0 if received
- if (ctrl.length != 0) {
- // Receive
- int len = ctrl.bus->ReceiveHandShake(
- &ctrl.buffer[ctrl.offset], ctrl.length);
- LOGDEBUG("%s Received %d bytes", __PRETTY_FUNCTION__, len);
-
- // If not able to receive all, move to status phase
- if (len != (int)ctrl.length) {
- Error();
- return;
- }
-
- // Offset and Length
- ctrl.offset += ctrl.length;
- ctrl.length = 0;
- return;
- }
- else
- {
- LOGDEBUG("%s ctrl.length was 0", __PRETTY_FUNCTION__);
- }
-
- // Remove the control block and initialize the result
- ctrl.blocks--;
- bool result = true;
-
- // Process the data out phase
- if (ctrl.phase == BUS::dataout) {
- if (ctrl.blocks == 0) {
- // End with this buffer
- result = XferOut(false);
- } else {
- // Continue to next buffer (set offset, length)
- result = XferOut(true);
- }
- }
-
- // If result is false, move to the status phase
- if (!result) {
- Error();
- return;
- }
-
- // Continue to receive is block != 0
- if (ctrl.blocks != 0){
- ASSERT(ctrl.length > 0);
- ASSERT(ctrl.offset == 0);
- return;
- }
-
- // Move to the next phase
- switch (ctrl.phase) {
- // Data out phase
- case BUS::dataout:
- LOGTRACE("%s transitioning to FlushUnit()",__PRETTY_FUNCTION__);
- // Flush
- FlushUnit();
-
- // status phase
- Status();
- break;
-
- // Other (impossible)
- default:
- ASSERT(FALSE);
- break;
- }
-}
-
-//---------------------------------------------------------------------------
-//
-// Data transfer IN
-// *Reset offset and length
-// TODO XferIn probably needs a dispatcher, in order to avoid subclassing Disk, i.e.
-// just like the actual SCSI commands XferIn should be executed by the respective device
-//
-//---------------------------------------------------------------------------
-bool SASIDEV::XferIn(BYTE *buf)
-{
- ASSERT(ctrl.phase == BUS::datain);
- LOGTRACE("%s ctrl.cmd[0]=%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]);
-
- // Logical Unit
- DWORD lun = GetEffectiveLun();
- if (!ctrl.unit[lun]) {
- return false;
- }
-
- // Limited to read commands
- switch (ctrl.cmd[0]) {
- case eCmdRead6:
- case eCmdRead10:
- case eCmdRead16:
- // Read from disk
- ctrl.length = ((Disk *)ctrl.unit[lun])->Read(ctrl.cmd, buf, ctrl.next);
- ctrl.next++;
-
- // If there is an error, go to the status phase
- if (ctrl.length <= 0) {
- // Cancel data-in
- return false;
- }
-
- // If things are normal, work setting
- ctrl.offset = 0;
- break;
-
- // Other (impossible)
- default:
- ASSERT(FALSE);
- return false;
- }
-
- // Succeeded in setting the buffer
- return true;
-}
-
-//---------------------------------------------------------------------------
-//
-// Data transfer OUT
-// *If cont=true, reset the offset and length
-// TODO XferOut probably needs a dispatcher, in order to avoid subclassing Disk, i.e.
-// just like the actual SCSI commands XferOut should be executed by the respective device
-//
-//---------------------------------------------------------------------------
-bool SASIDEV::XferOut(bool cont)
-{
- ASSERT(ctrl.phase == BUS::dataout);
-
- // Logical Unit
- DWORD lun = GetEffectiveLun();
- if (!ctrl.unit[lun]) {
- return false;
- }
- Disk *device = (Disk *)ctrl.unit[lun];
-
- switch (ctrl.cmd[0]) {
- case SASIDEV::eCmdModeSelect6:
- case SASIDEV::eCmdModeSelect10:
- if (!device->ModeSelect(ctrl.cmd, ctrl.buffer, ctrl.offset)) {
- // MODE SELECT failed
- return false;
- }
- break;
-
- case SASIDEV::eCmdWrite6:
- case SASIDEV::eCmdWrite10:
- case SASIDEV::eCmdWrite16:
- case SASIDEV::eCmdVerify10:
- case SASIDEV::eCmdVerify16:
- {
- // If we're a host bridge, use the host bridge's SendMessage10 function
- // TODO This class must not know about SCSIBR
- SCSIBR *bridge = dynamic_cast(device);
- if (bridge) {
- if (!bridge->SendMessage10(ctrl.cmd, ctrl.buffer)) {
- // write failed
- return false;
- }
-
- // If normal, work setting
- ctrl.offset = 0;
- break;
- }
-
- // Special case Write function for DaynaPort
- // TODO This class must not know about DaynaPort
- SCSIDaynaPort *daynaport = dynamic_cast(device);
- if (daynaport) {
- if (!daynaport->Write(ctrl.cmd, ctrl.buffer, ctrl.length)) {
- // write failed
- return false;
- }
-
- // If normal, work setting
- ctrl.offset = 0;
- ctrl.blocks = 0;
- break;
- }
-
- if (!device->Write(ctrl.cmd, ctrl.buffer, ctrl.next - 1)) {
- // Write failed
- return false;
- }
-
- // If you do not need the next block, end here
- ctrl.next++;
- if (!cont) {
- break;
- }
-
- // Check the next block
- ctrl.length = device->WriteCheck(ctrl.next - 1);
- if (ctrl.length <= 0) {
- // Cannot write
- return false;
- }
-
- // If normal, work setting
- ctrl.offset = 0;
- break;
- }
-
- // SPECIFY(SASI only)
- case SASIDEV::eCmdInvalid:
- break;
-
- case SASIDEV::eCmdSetMcastAddr:
- LOGTRACE("%s Done with DaynaPort Set Multicast Address", __PRETTY_FUNCTION__);
- break;
-
- default:
- LOGWARN("Received an unexpected command ($%02X) in %s", (WORD)ctrl.cmd[0] , __PRETTY_FUNCTION__)
- break;
- }
-
- // Buffer saved successfully
- return true;
-}
-
-//---------------------------------------------------------------------------
-//
-// Logical unit flush
-//
-//---------------------------------------------------------------------------
-void SASIDEV::FlushUnit()
-{
- ASSERT(ctrl.phase == BUS::dataout);
-
- // Logical Unit
- DWORD lun = GetEffectiveLun();
- if (!ctrl.unit[lun]) {
- return;
- }
-
- Disk *disk = (Disk *)ctrl.unit[lun];
-
- // WRITE system only
- switch ((SASIDEV::sasi_command)ctrl.cmd[0]) {
- case SASIDEV::eCmdWrite6:
- case SASIDEV::eCmdWrite10:
- case SASIDEV::eCmdWrite16:
- case SASIDEV::eCmdWriteLong16:
- case SASIDEV::eCmdVerify10:
- case SASIDEV::eCmdVerify16:
- break;
-
- case SASIDEV::eCmdModeSelect6:
- case SASIDEV::eCmdModeSelect10:
- // Debug code related to Issue #2 on github, where we get an unhandled Mode Select when
- // the mac is rebooted
- // https://github.com/akuker/RASCSI/issues/2
- LOGWARN("Received \'Mode Select\'\n");
- LOGWARN(" Operation Code: [%02X]\n", (WORD)ctrl.cmd[0]);
- LOGWARN(" Logical Unit %01X, PF %01X, SP %01X [%02X]\n",\
- (WORD)ctrl.cmd[1] >> 5, 1 & ((WORD)ctrl.cmd[1] >> 4), \
- (WORD)ctrl.cmd[1] & 1, (WORD)ctrl.cmd[1]);
- LOGWARN(" Reserved: %02X\n", (WORD)ctrl.cmd[2]);
- LOGWARN(" Reserved: %02X\n", (WORD)ctrl.cmd[3]);
- LOGWARN(" Parameter List Len %02X\n", (WORD)ctrl.cmd[4]);
- LOGWARN(" Reserved: %02X\n",(WORD)ctrl.cmd[5]);
- LOGWARN(" Ctrl Len: %08X\n",(WORD)ctrl.length);
-
- if (!disk->ModeSelect(
- ctrl.cmd, ctrl.buffer, ctrl.offset)) {
- // MODE SELECT failed
- LOGWARN("Error occured while processing Mode Select command %02X\n", (unsigned char)ctrl.cmd[0]);
- return;
- }
- break;
-
- case SASIDEV::eCmdSetMcastAddr:
- // TODO: Eventually, we should store off the multicast address configuration data here...
- break;
-
- default:
- LOGWARN("Received an unexpected flush command $%02X\n",(WORD)ctrl.cmd[0]);
- break;
- }
-}
-
-int SASIDEV::GetEffectiveLun() const
-{
- return (ctrl.cmd[1] >> 5) & 0x07;
-}
-
diff --git a/src/raspberrypi/controllers/sasidev_ctrl.h b/src/raspberrypi/controllers/sasidev_ctrl.h
deleted file mode 100644
index 0f78c011..00000000
--- a/src/raspberrypi/controllers/sasidev_ctrl.h
+++ /dev/null
@@ -1,171 +0,0 @@
-//---------------------------------------------------------------------------
-//
-// SCSI Target Emulator RaSCSI Reloaded
-// for Raspberry Pi
-//
-// Copyright (C) 2001-2006 PI.(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
-};
diff --git a/src/raspberrypi/controllers/scsi_controller.cpp b/src/raspberrypi/controllers/scsi_controller.cpp
new file mode 100644
index 00000000..9a290194
--- /dev/null
+++ b/src/raspberrypi/controllers/scsi_controller.cpp
@@ -0,0 +1,1231 @@
+//---------------------------------------------------------------------------
+//
+// SCSI Target Emulator RaSCSI Reloaded
+// for Raspberry Pi
+//
+// Copyright (C) 2001-2006 PI.(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 "gpiobus.h"
+#include "rascsi_exceptions.h"
+#include "devices/scsi_host_bridge.h"
+#include "devices/scsi_daynaport.h"
+#include "scsi_controller.h"
+
+using namespace scsi_defs;
+
+ScsiController::ScsiController(BUS *bus, int target_id) : AbstractController(bus, target_id)
+{
+ // The initial buffer size will default to either the default buffer size OR
+ // the size of an Ethernet message, whichever is larger.
+ ctrl.bufsize = std::max(DEFAULT_BUFFER_SIZE, ETH_FRAME_LEN + 16 + ETH_FCS_LEN);
+ ctrl.buffer = new BYTE[ctrl.bufsize];
+}
+
+ScsiController::~ScsiController()
+{
+ delete[] ctrl.buffer;
+}
+
+void ScsiController::Reset()
+{
+ SetPhase(BUS::busfree);
+ ctrl.status = 0x00;
+ ctrl.message = 0x00;
+ execstart = 0;
+ ctrl.blocks = 0;
+ ctrl.next = 0;
+ ctrl.offset = 0;
+ ctrl.length = 0;
+ identified_lun = -1;
+
+ scsi.atnmsg = false;
+ scsi.msc = 0;
+ memset(scsi.msb, 0x00, sizeof(scsi.msb));
+
+ is_byte_transfer = false;
+ bytes_to_transfer = 0;
+
+ // Reset all LUNs
+ for (auto& lun : ctrl.luns) {
+ lun.second->Reset();
+ }
+}
+
+BUS::phase_t ScsiController::Process(int initiator_id)
+{
+ // Get bus information
+ bus->Acquire();
+
+ // Check to see if the reset signal was asserted
+ if (bus->GetRST()) {
+ LOGWARN("RESET signal received!");
+
+ // Reset the controller
+ Reset();
+
+ // Reset the bus
+ bus->Reset();
+
+ return ctrl.phase;
+ }
+
+ if (initiator_id != UNKNOWN_INITIATOR_ID) {
+ LOGTRACE("%s Initiator ID is %d", __PRETTY_FUNCTION__, initiator_id);
+ }
+ else {
+ LOGTRACE("%s Initiator ID is unknown", __PRETTY_FUNCTION__);
+ }
+
+ this->initiator_id = initiator_id;
+
+ try {
+ // Phase processing
+ switch (ctrl.phase) {
+ case BUS::busfree:
+ BusFree();
+ break;
+
+ case BUS::selection:
+ Selection();
+ break;
+
+ case BUS::dataout:
+ DataOut();
+ break;
+
+ case BUS::datain:
+ DataIn();
+ break;
+
+ case BUS::command:
+ Command();
+ break;
+
+ case BUS::status:
+ Status();
+ break;
+
+ case BUS::msgout:
+ MsgOut();
+ break;
+
+ case BUS::msgin:
+ MsgIn();
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+ }
+ catch(const scsi_error_exception& e) {
+ // Any exception should have been handled during the phase processing
+ assert(false);
+
+ LOGERROR("%s Unhandled SCSI error, resetting controller and bus and entering bus free phase", __PRETTY_FUNCTION__);
+
+ Reset();
+ bus->Reset();
+
+ BusFree();
+ }
+
+ return ctrl.phase;
+}
+
+void ScsiController::BusFree()
+{
+ if (ctrl.phase != BUS::busfree) {
+ LOGTRACE("%s Bus free phase", __PRETTY_FUNCTION__);
+
+ SetPhase(BUS::busfree);
+
+ bus->SetREQ(false);
+ bus->SetMSG(false);
+ bus->SetCD(false);
+ bus->SetIO(false);
+ bus->SetBSY(false);
+
+ // Initialize status and message
+ ctrl.status = 0x00;
+ ctrl.message = 0x00;
+
+ // Initialize ATN message reception status
+ scsi.atnmsg = false;
+
+ identified_lun = -1;
+
+ is_byte_transfer = false;
+ bytes_to_transfer = 0;
+
+ // When the bus is free RaSCSI or the Pi may be shut down.
+ // TODO Try to find a better place for this code without breaking encapsulation
+ 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 (bus->GetSEL() && !bus->GetBSY()) {
+ Selection();
+ }
+}
+
+void ScsiController::Selection()
+{
+ if (ctrl.phase != BUS::selection) {
+ // A different device controller was selected
+ int id = 1 << GetTargetId();
+ if ((bus->GetDAT() & id) == 0) {
+ return;
+ }
+
+ // Abort if there is no LUN for this controller
+ if (ctrl.luns.empty()) {
+ return;
+ }
+
+ LOGTRACE("%s Selection Phase Target ID=%d", __PRETTY_FUNCTION__, GetTargetId());
+
+ SetPhase(BUS::selection);
+
+ // Raise BSY and respond
+ bus->SetBSY(true);
+ return;
+ }
+
+ // Selection completed
+ if (!bus->GetSEL() && bus->GetBSY()) {
+ // Message out phase if ATN=1, otherwise command phase
+ if (bus->GetATN()) {
+ MsgOut();
+ } else {
+ Command();
+ }
+ }
+}
+
+void ScsiController::Command()
+{
+ if (ctrl.phase != BUS::command) {
+ LOGTRACE("%s Command Phase", __PRETTY_FUNCTION__);
+
+ SetPhase(BUS::command);
+
+ bus->SetMSG(false);
+ bus->SetCD(true);
+ bus->SetIO(false);
+
+ // Data transfer is 6 bytes x 1 block
+ ctrl.offset = 0;
+ ctrl.length = 6;
+ ctrl.blocks = 1;
+
+ // If no byte can be received move to the status phase
+ int command_byte_count = bus->CommandHandShake(ctrl.buffer);
+ if (!command_byte_count) {
+ LOGERROR("%s No command bytes received", __PRETTY_FUNCTION__);
+ Error(sense_key::ABORTED_COMMAND);
+ return;
+ }
+
+ ctrl.length = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]);
+
+ // If not able to receive all, move to the status phase
+ if (command_byte_count != (int)ctrl.length) {
+ Error(sense_key::ABORTED_COMMAND);
+ return;
+ }
+
+ // Command data transfer
+ for (int i = 0; i < (int)ctrl.length; i++) {
+ ctrl.cmd[i] = ctrl.buffer[i];
+ LOGTRACE("%s CDB[%d]=$%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i]);
+ }
+
+ // Clear length and block
+ ctrl.length = 0;
+ ctrl.blocks = 0;
+
+ Execute();
+ }
+}
+
+void ScsiController::Execute()
+{
+ LOGTRACE("%s Execution phase command $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]);
+
+ SetPhase(BUS::execute);
+
+ // Initialization for data transfer
+ ctrl.offset = 0;
+ ctrl.blocks = 1;
+ 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 (!HasDeviceForLun(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, GetTargetId());
+
+ 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(HasDeviceForLun(0));
+
+ lun = 0;
+ }
+ }
+
+ PrimaryDevice *device = GetDeviceForLun(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) {
+ device->SetStatusCode(0);
+ }
+
+ try {
+ if (!device->Dispatch()) {
+ LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetTargetId(), lun, (BYTE)ctrl.cmd[0]);
+
+ throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
+ }
+ }
+ catch(const scsi_error_exception& e) {
+ Error(e.get_sense_key(), e.get_asc(), e.get_status());
+
+ // Fall through
+ }
+
+ // SCSI-2 p.104 4.4.3 Incorrect logical unit handling
+ if ((scsi_command)ctrl.cmd[0] == scsi_command::eCmdInquiry && !HasDeviceForLun(lun)) {
+ lun = GetEffectiveLun();
+
+ LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, device->GetId());
+
+ ctrl.buffer[0] = 0x7f;
+ }
+}
+
+void ScsiController::Status()
+{
+ if (ctrl.phase != BUS::status) {
+ // Minimum execution time
+ if (execstart > 0) {
+ Sleep();
+ } else {
+ SysTimer::SleepUsec(5);
+ }
+
+ LOGTRACE("%s Status Phase $%02X",__PRETTY_FUNCTION__, (unsigned int)ctrl.status);
+
+ SetPhase(BUS::status);
+
+ // Signal line operated by the target
+ bus->SetMSG(false);
+ bus->SetCD(true);
+ bus->SetIO(true);
+
+ // Data transfer is 1 byte x 1 block
+ ctrl.offset = 0;
+ ctrl.length = 1;
+ ctrl.blocks = 1;
+ ctrl.buffer[0] = (BYTE)ctrl.status;
+
+ return;
+ }
+
+ Send();
+}
+
+void ScsiController::MsgIn()
+{
+ if (ctrl.phase != BUS::msgin) {
+ LOGTRACE("%s Message In phase", __PRETTY_FUNCTION__);
+
+ SetPhase(BUS::msgin);
+
+ bus->SetMSG(true);
+ bus->SetCD(true);
+ bus->SetIO(true);
+
+ // length, blocks are already set
+ assert(ctrl.length > 0);
+ assert(ctrl.blocks > 0);
+ ctrl.offset = 0;
+ return;
+ }
+
+ LOGTRACE("%s Transitioning to Send()", __PRETTY_FUNCTION__);
+ Send();
+}
+
+void ScsiController::MsgOut()
+{
+ LOGTRACE("%s ID %d",__PRETTY_FUNCTION__, GetTargetId());
+
+ 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));
+ }
+
+ SetPhase(BUS::msgout);
+
+ bus->SetMSG(true);
+ bus->SetCD(true);
+ bus->SetIO(false);
+
+ // Data transfer is 1 byte x 1 block
+ ctrl.offset = 0;
+ ctrl.length = 1;
+ ctrl.blocks = 1;
+
+ return;
+ }
+
+ Receive();
+}
+
+void ScsiController::DataIn()
+{
+ if (ctrl.phase != BUS::datain) {
+ // Minimum execution time
+ if (execstart > 0) {
+ Sleep();
+ }
+
+ // If the length is 0, go to the status phase
+ if (ctrl.length == 0) {
+ Status();
+ return;
+ }
+
+ LOGTRACE("%s Going into Data-in Phase", __PRETTY_FUNCTION__);
+
+ SetPhase(BUS::datain);
+
+ bus->SetMSG(false);
+ bus->SetCD(false);
+ bus->SetIO(true);
+
+ // length, blocks are already set
+ assert(ctrl.blocks > 0);
+ ctrl.offset = 0;
+
+ return;
+ }
+
+ Send();
+}
+
+void ScsiController::DataOut()
+{
+ if (ctrl.phase != BUS::dataout) {
+ // Minimum execution time
+ if (execstart > 0) {
+ Sleep();
+ }
+
+ // If the length is 0, go to the status phase
+ if (ctrl.length == 0) {
+ Status();
+ return;
+ }
+
+ LOGTRACE("%s Data out phase", __PRETTY_FUNCTION__);
+
+ SetPhase(BUS::dataout);
+
+ // Signal line operated by the target
+ bus->SetMSG(false);
+ bus->SetCD(false);
+ bus->SetIO(false);
+
+ ctrl.offset = 0;
+ return;
+ }
+
+ Receive();
+}
+
+void ScsiController::Error(sense_key sense_key, asc asc, status status)
+{
+ // Get bus information
+ bus->Acquire();
+
+ // Reset check
+ if (bus->GetRST()) {
+ Reset();
+ 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 (!HasDeviceForLun(lun) || asc == INVALID_LUN) {
+ assert(HasDeviceForLun(0));
+
+ lun = 0;
+ }
+
+ if (sense_key || asc) {
+ // Set Sense Key and ASC for a subsequent REQUEST SENSE
+ GetDeviceForLun(lun)->SetStatusCode((sense_key << 16) | (asc << 8));
+ }
+
+ ctrl.status = status;
+ ctrl.message = 0x00;
+
+ LOGTRACE("%s Error (to status phase)", __PRETTY_FUNCTION__);
+
+ Status();
+}
+
+void ScsiController::Send()
+{
+ assert(!bus->GetREQ());
+ assert(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 currently no Daynaport drivers for
+ // LUNs other than 0 this work-around works.
+ int len = bus->SendHandShake(&ctrl.buffer[ctrl.offset], ctrl.length,
+ HasDeviceForLun(0) ? GetDeviceForLun(0)->GetSendDelay() : 0);
+
+ // If you cannot send all, move to status phase
+ if (len != (int)ctrl.length) {
+ Error(sense_key::ABORTED_COMMAND);
+ 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(sense_key::ABORTED_COMMAND);
+ 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;
+ }
+}
+
+void ScsiController::Receive()
+{
+ if (is_byte_transfer) {
+ ReceiveBytes();
+ return;
+ }
+
+ int len;
+ BYTE data;
+
+ LOGTRACE("%s",__PRETTY_FUNCTION__);
+
+ // REQ is low
+ assert(!bus->GetREQ());
+ assert(!bus->GetIO());
+
+ // Length != 0 if received
+ if (ctrl.length != 0) {
+ LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, (int)ctrl.length);
+ // Receive
+ len = 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",__PRETTY_FUNCTION__, (int)ctrl.length, len);
+ Error(sense_key::ABORTED_COMMAND);
+ 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) {
+ 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;
+
+ 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(sense_key::ABORTED_COMMAND);
+ 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) {
+ 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]);
+ }
+
+ Execute();
+ break;
+
+ case BUS::msgout:
+ // Continue message out phase as long as ATN keeps asserting
+ if (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) {
+ identified_lun = data & 0x1F;
+ LOGTRACE("Message code IDENTIFY $%02X, LUN %d selected", data, identified_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;
+ }
+
+ scsi.syncperiod = scsi.msb[i + 3];
+ if (scsi.syncperiod > MAX_SYNC_PERIOD) {
+ scsi.syncperiod = MAX_SYNC_PERIOD;
+ }
+
+ scsi.syncoffset = scsi.msb[i + 4];
+ if (scsi.syncoffset > MAX_SYNC_OFFSET) {
+ scsi.syncoffset = MAX_SYNC_OFFSET;
+ }
+
+ // 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] = scsi.syncperiod;
+ ctrl.buffer[4] = scsi.syncoffset;
+ MsgIn();
+ return;
+ }
+
+ // next
+ i++;
+ }
+ }
+
+ // Initialize ATN message reception status
+ scsi.atnmsg = false;
+
+ Command();
+ break;
+
+ case BUS::dataout:
+ FlushUnit();
+
+ Status();
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+}
+
+bool ScsiController::XferMsg(int msg)
+{
+ assert(ctrl.phase == BUS::msgout);
+
+ // Save message out data
+ if (scsi.atnmsg) {
+ scsi.msb[scsi.msc] = msg;
+ scsi.msc++;
+ scsi.msc %= 256;
+ }
+
+ return true;
+}
+
+void ScsiController::ReceiveBytes()
+{
+ uint32_t len;
+ BYTE data;
+
+ LOGTRACE("%s",__PRETTY_FUNCTION__);
+
+ // REQ is low
+ assert(!bus->GetREQ());
+ assert(!bus->GetIO());
+
+ if (ctrl.length) {
+ LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length);
+
+ len = 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",
+ __PRETTY_FUNCTION__, ctrl.length, len);
+ Error(sense_key::ABORTED_COMMAND);
+ return;
+ }
+
+ bytes_to_transfer = ctrl.length;
+
+ ctrl.offset += 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(sense_key::ABORTED_COMMAND);
+ 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 (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) {
+ identified_lun = data & 0x1F;
+ LOGTRACE("Message code IDENTIFY $%02X, LUN %d selected", data, identified_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;
+ }
+
+ scsi.syncperiod = scsi.msb[i + 3];
+ if (scsi.syncperiod > MAX_SYNC_PERIOD) {
+ scsi.syncperiod = MAX_SYNC_PERIOD;
+ }
+
+ scsi.syncoffset = scsi.msb[i + 4];
+ if (scsi.syncoffset > MAX_SYNC_OFFSET) {
+ scsi.syncoffset = MAX_SYNC_OFFSET;
+ }
+
+ // 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] = scsi.syncperiod;
+ ctrl.buffer[4] = 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 ScsiController::XferOut(bool cont)
+{
+ assert(ctrl.phase == BUS::dataout);
+
+ if (!is_byte_transfer) {
+ return XferOutBlockOriented(cont);
+ }
+
+ is_byte_transfer = false;
+
+ PrimaryDevice *device = GetDeviceForLun(GetEffectiveLun());
+ if (device != nullptr && ctrl.cmd[0] == scsi_command::eCmdWrite6) {
+ return device->WriteByteSequence(ctrl.buffer, bytes_to_transfer);
+ }
+
+ LOGWARN("Received an unexpected command ($%02X) in %s", (WORD)ctrl.cmd[0] , __PRETTY_FUNCTION__)
+
+ return false;
+}
+
+void ScsiController::FlushUnit()
+{
+ assert(ctrl.phase == BUS::dataout);
+
+ Disk *disk = dynamic_cast(GetDeviceForLun(GetEffectiveLun()));
+ if (disk == nullptr) {
+ return;
+ }
+
+ // WRITE system only
+ switch ((ScsiController::rw_command)ctrl.cmd[0]) {
+ case ScsiController::eCmdWrite6:
+ case ScsiController::eCmdWrite10:
+ case ScsiController::eCmdWrite16:
+ case ScsiController::eCmdWriteLong10:
+ case ScsiController::eCmdWriteLong16:
+ case ScsiController::eCmdVerify10:
+ case ScsiController::eCmdVerify16:
+ break;
+
+ case ScsiController::eCmdModeSelect6:
+ case ScsiController::eCmdModeSelect10:
+ // TODO What is this special handling of ModeSelect good for?
+ // Without it we would not need this method at all.
+ // ModeSelect is already handled in XferOutBlockOriented(). Why would it have to be handled once more?
+ try {
+ disk->ModeSelect(ctrl.cmd, ctrl.buffer, ctrl.offset);
+ }
+ catch(const scsi_error_exception& e) {
+ LOGWARN("Error occured while processing Mode Select command %02X\n", (unsigned char)ctrl.cmd[0]);
+ Error(e.get_sense_key(), e.get_asc(), e.get_status());
+ return;
+ }
+ break;
+
+ case ScsiController::eCmdSetMcastAddr:
+ // TODO: Eventually, we should store off the multicast address configuration data here...
+ break;
+
+ default:
+ LOGWARN("Received an unexpected flush command $%02X\n",(WORD)ctrl.cmd[0]);
+ break;
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// Data transfer IN
+// *Reset offset and length
+// TODO XferIn probably needs a dispatcher, in order to avoid subclassing Disk, i.e.
+// just like the actual SCSI commands XferIn should be executed by the respective device
+//
+//---------------------------------------------------------------------------
+bool ScsiController::XferIn(BYTE *buf)
+{
+ assert(ctrl.phase == BUS::datain);
+
+ LOGTRACE("%s ctrl.cmd[0]=%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]);
+
+ int lun = GetEffectiveLun();
+ if (!HasDeviceForLun(lun)) {
+ return false;
+ }
+
+ // Limited to read commands
+ switch (ctrl.cmd[0]) {
+ case eCmdRead6:
+ case eCmdRead10:
+ case eCmdRead16:
+ // Read from disk
+ try {
+ ctrl.length = ((Disk *)GetDeviceForLun(lun))->Read(ctrl.cmd, buf, ctrl.next);
+ }
+ catch(const scsi_error_exception& e) {
+ // If there is an error, go to the status phase
+ return false;
+ }
+
+ ctrl.next++;
+
+ // If things are normal, work setting
+ ctrl.offset = 0;
+ break;
+
+ // Other (impossible)
+ default:
+ assert(false);
+ return false;
+ }
+
+ // Succeeded in setting the buffer
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// Data transfer OUT
+// *If cont=true, reset the offset and length
+// TODO XferOut probably needs a dispatcher, in order to avoid subclassing Disk, i.e.
+// just like the actual SCSI commands XferOut should be executed by the respective device
+//
+//---------------------------------------------------------------------------
+bool ScsiController::XferOutBlockOriented(bool cont)
+{
+ Disk *disk = dynamic_cast(GetDeviceForLun(GetEffectiveLun()));
+ if (disk == nullptr) {
+ return false;
+ }
+
+ // Limited to write commands
+ switch (ctrl.cmd[0]) {
+ case eCmdModeSelect6:
+ case eCmdModeSelect10:
+ try {
+ disk->ModeSelect(ctrl.cmd, ctrl.buffer, ctrl.offset);
+ }
+ catch(const scsi_error_exception& e) {
+ Error(e.get_sense_key(), e.get_asc(), e.get_status());
+ return false;
+ }
+ break;
+
+ case eCmdWrite6:
+ case eCmdWrite10:
+ case eCmdWrite16:
+ // TODO Verify has to verify, not to write
+ case eCmdVerify10:
+ case eCmdVerify16:
+ {
+ // Special case Write function for brige
+ // TODO This class must not know about SCSIBR
+ SCSIBR *bridge = dynamic_cast(disk);
+ if (bridge) {
+ if (!bridge->WriteBytes(ctrl.cmd, ctrl.buffer, ctrl.length)) {
+ // Write failed
+ return false;
+ }
+
+ ctrl.offset = 0;
+ break;
+ }
+
+ // Special case Write function for DaynaPort
+ // TODO This class must not know about DaynaPort
+ SCSIDaynaPort *daynaport = dynamic_cast(disk);
+ if (daynaport) {
+ daynaport->WriteBytes(ctrl.cmd, ctrl.buffer, 0);
+
+ ctrl.offset = 0;
+ ctrl.blocks = 0;
+ break;
+ }
+
+ try {
+ disk->Write(ctrl.cmd, ctrl.buffer, ctrl.next - 1);
+ }
+ catch(const scsi_error_exception& e) {
+ Error(e.get_sense_key(), e.get_asc(), e.get_status());
+
+ // Write failed
+ return false;
+ }
+
+ // If you do not need the next block, end here
+ ctrl.next++;
+ if (!cont) {
+ break;
+ }
+
+ // Check the next block
+ try {
+ ctrl.length = disk->WriteCheck(ctrl.next - 1);
+ }
+ catch(const scsi_error_exception& e) {
+ // Cannot write
+ return false;
+ }
+
+ ctrl.offset = 0;
+ break;
+ }
+
+ case eCmdSetMcastAddr:
+ LOGTRACE("%s Done with DaynaPort Set Multicast Address", __PRETTY_FUNCTION__);
+ break;
+
+ default:
+ LOGWARN("Received an unexpected command ($%02X) in %s", (WORD)ctrl.cmd[0] , __PRETTY_FUNCTION__)
+ break;
+ }
+
+ return true;
+}
+
+int ScsiController::GetEffectiveLun() const
+{
+ return identified_lun != -1 ? identified_lun : (ctrl.cmd[1] >> 5) & 0x07;
+}
+
+void ScsiController::Sleep()
+{
+ uint32_t time = SysTimer::GetTimerLow() - execstart;
+ if (time < MIN_EXEC_TIME) {
+ SysTimer::SleepUsec(MIN_EXEC_TIME - time);
+ }
+ execstart = 0;
+}
diff --git a/src/raspberrypi/controllers/scsi_controller.h b/src/raspberrypi/controllers/scsi_controller.h
new file mode 100644
index 00000000..1ea7baa9
--- /dev/null
+++ b/src/raspberrypi/controllers/scsi_controller.h
@@ -0,0 +1,135 @@
+//---------------------------------------------------------------------------
+//
+// SCSI Target Emulator RaSCSI Reloaded
+// for Raspberry Pi
+//
+// Copyright (C) 2001-2006 PI.(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;
+};
+
diff --git a/src/raspberrypi/controllers/scsidev_ctrl.cpp b/src/raspberrypi/controllers/scsidev_ctrl.cpp
deleted file mode 100644
index 92d2b180..00000000
--- a/src/raspberrypi/controllers/scsidev_ctrl.cpp
+++ /dev/null
@@ -1,918 +0,0 @@
-//---------------------------------------------------------------------------
-//
-// SCSI Target Emulator RaSCSI Reloaded
-// for Raspberry Pi
-//
-// Copyright (C) 2001-2006 PI.(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(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;
-}
-
diff --git a/src/raspberrypi/controllers/scsidev_ctrl.h b/src/raspberrypi/controllers/scsidev_ctrl.h
deleted file mode 100644
index 8ac4fa49..00000000
--- a/src/raspberrypi/controllers/scsidev_ctrl.h
+++ /dev/null
@@ -1,102 +0,0 @@
-//---------------------------------------------------------------------------
-//
-// SCSI Target Emulator RaSCSI Reloaded
-// for Raspberry Pi
-//
-// Copyright (C) 2001-2006 PI.(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;
-};
-
diff --git a/src/raspberrypi/devices/cfilesystem.cpp b/src/raspberrypi/devices/cfilesystem.cpp
index a5070d1a..8c7ab952 100644
--- a/src/raspberrypi/devices/cfilesystem.cpp
+++ b/src/raspberrypi/devices/cfilesystem.cpp
@@ -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;
}
diff --git a/src/raspberrypi/devices/cfilesystem.h b/src/raspberrypi/devices/cfilesystem.h
index d1989ea7..2a729f41 100644
--- a/src/raspberrypi/devices/cfilesystem.h
+++ b/src/raspberrypi/devices/cfilesystem.h
@@ -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
diff --git a/src/raspberrypi/devices/ctapdriver.cpp b/src/raspberrypi/devices/ctapdriver.cpp
index e7710dcc..66ea59b7 100644
--- a/src/raspberrypi/devices/ctapdriver.cpp
+++ b/src/raspberrypi/devices/ctapdriver.cpp
@@ -22,7 +22,7 @@
#include "ctapdriver.h"
#include "log.h"
#include "rasutil.h"
-#include "exceptions.h"
+#include "rascsi_exceptions.h"
#include
#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& 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,
};
diff --git a/src/raspberrypi/devices/ctapdriver.h b/src/raspberrypi/devices/ctapdriver.h
index 60ebd5af..4ae9949c 100644
--- a/src/raspberrypi/devices/ctapdriver.h
+++ b/src/raspberrypi/devices/ctapdriver.h
@@ -16,7 +16,7 @@
#include
#include "filepath.h"
#include
-#include
+#include
#include
#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&);
@@ -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 interfaces;
+ list interfaces;
string inet;
};
diff --git a/src/raspberrypi/devices/device.cpp b/src/raspberrypi/devices/device.cpp
index 5e7dee2f..0c689f8c 100644
--- a/src/raspberrypi/devices/device.cpp
+++ b/src/raspberrypi/devices/device.cpp
@@ -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::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& params)
+void Device::SetParams(const unordered_map& 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());
diff --git a/src/raspberrypi/devices/device.h b/src/raspberrypi/devices/device.h
index b2f069f2..c4a7ee89 100644
--- a/src/raspberrypi/devices/device.h
+++ b/src/raspberrypi/devices/device.h
@@ -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
#include
#include
@@ -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 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 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&);
+
+ static unordered_set 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&) { 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 GetParams() const { return params; }
- const string GetParam(const string&);
- void SetParams(const unordered_map&);
- const unordered_map GetDefaultParams() const { return default_params; }
void SetDefaultParams(const unordered_map& 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() { }
};
diff --git a/src/raspberrypi/devices/device_factory.cpp b/src/raspberrypi/devices/device_factory.cpp
index 6cc94228..e4be7f56 100644
--- a/src/raspberrypi/devices/device_factory.cpp
+++ b/src/raspberrypi/devices/device_factory.cpp
@@ -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
#include "host_services.h"
@@ -23,9 +22,10 @@
using namespace std;
using namespace rascsi_interface;
-DeviceFactory::DeviceFactory()
+multimap 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 DeviceFactory::GetAllDevices() const
+{
+ list 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& DeviceFactory::GetSectorSizes(const string& type)
+const unordered_set& DeviceFactory::GetSectorSizes(const string& type) const
{
PbDeviceType t = UNDEFINED;
PbDeviceType_Parse(type, &t);
- return sector_sizes[t];
-}
-const unordered_set DeviceFactory::GetCapacities(PbDeviceType type) const
-{
- unordered_set 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 DeviceFactory::GetNetworkInterfaces() const
@@ -251,16 +288,13 @@ const list 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;
diff --git a/src/raspberrypi/devices/device_factory.h b/src/raspberrypi/devices/device_factory.h
index 46715119..ae3bd4cc 100644
--- a/src/raspberrypi/devices/device_factory.h
+++ b/src/raspberrypi/devices/device_factory.h
@@ -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
+#include
#include
#include
#include
@@ -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 GetAllDevices() const;
PbDeviceType GetTypeForFile(const string&) const;
const unordered_set& GetSectorSizes(PbDeviceType type) { return sector_sizes[type]; }
- const unordered_set& GetSectorSizes(const string&);
- const unordered_set GetCapacities(PbDeviceType) const;
+ const unordered_set& GetSectorSizes(const string&) const;
const unordered_map& GetDefaultParams(PbDeviceType type) { return default_params[type]; }
const list GetNetworkInterfaces() const;
const unordered_map GetExtensionMapping() const { return extension_mapping; }
private:
+ void DeleteAllDevices();
+
unordered_map> sector_sizes;
// Optional mapping of drive capacities to drive geometries
@@ -54,4 +61,6 @@ private:
unordered_map extension_mapping;
string GetExtension(const string&) const;
+
+ static std::multimap devices;
};
diff --git a/src/raspberrypi/devices/disk.cpp b/src/raspberrypi/devices/disk.cpp
index d3383c1c..ff388020 100644
--- a/src/raspberrypi/devices/disk.cpp
+++ b/src/raspberrypi/devices/disk.cpp
@@ -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(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>& 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>& 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, 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 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& 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;
}
diff --git a/src/raspberrypi/devices/disk.h b/src/raspberrypi/devices/disk.h
index 691ed5df..2e40a34b 100644
--- a/src/raspberrypi/devices/disk.h
+++ b/src/raspberrypi/devices/disk.h
@@ -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 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 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 dispatcher;
+ Dispatcher 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 GetSectorSizes() const;
- void SetSectorSizes(const unordered_set&);
- uint32_t GetConfiguredSectorSize() const;
- bool SetConfiguredSectorSize(uint32_t);
- void SetGeometries(const unordered_map&);
- 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, bool) const override;
virtual void AddErrorPage(map>&, bool) const;
@@ -143,20 +130,14 @@ protected:
virtual void AddDrivePage(map>&, bool) const;
void AddCachePage(map>&, bool) const;
virtual void AddVendorPage(map>&, int, bool) const;
+ unordered_set GetSectorSizes() const;
+ void SetSectorSizes(const unordered_set&);
+ 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 = {};
};
diff --git a/src/raspberrypi/devices/disk_track_cache.cpp b/src/raspberrypi/devices/disk_track_cache.cpp
index 6a310943..4bf900b2 100644
--- a/src/raspberrypi/devices/disk_track_cache.cpp
+++ b/src/raspberrypi/devices/disk_track_cache.cpp
@@ -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();
}
diff --git a/src/raspberrypi/devices/dispatcher.h b/src/raspberrypi/devices/dispatcher.h
index 690da75d..3186cd19 100644
--- a/src/raspberrypi/devices/dispatcher.h
+++ b/src/raspberrypi/devices/dispatcher.h
@@ -15,18 +15,15 @@
#include "scsi.h"
#include
-class SASIDEV;
-class SCSIDEV;
-
using namespace std;
using namespace scsi_defs;
-template
+template
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 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(ctrl->cmd[0]));
+ const auto& it = commands.find(static_cast(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;
}
diff --git a/src/raspberrypi/devices/file_support.cpp b/src/raspberrypi/devices/file_support.cpp
index 6b7cb1ef..f9a68b11 100644
--- a/src/raspberrypi/devices/file_support.cpp
+++ b/src/raspberrypi/devices/file_support.cpp
@@ -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;
}
diff --git a/src/raspberrypi/devices/file_support.h b/src/raspberrypi/devices/file_support.h
index 803dbfa4..3168ac0a 100644
--- a/src/raspberrypi/devices/file_support.h
+++ b/src/raspberrypi/devices/file_support.h
@@ -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 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 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& 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;
};
diff --git a/src/raspberrypi/devices/host_services.cpp b/src/raspberrypi/devices/host_services.cpp
index 10bca4e3..ae6609cb 100644
--- a/src/raspberrypi/devices/host_services.cpp
+++ b/src/raspberrypi/devices/host_services.cpp
@@ -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 HostServices::Inquiry() const
+vector 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(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) {
diff --git a/src/raspberrypi/devices/host_services.h b/src/raspberrypi/devices/host_services.h
index 826b4b0e..88d93608 100644
--- a/src/raspberrypi/devices/host_services.h
+++ b/src/raspberrypi/devices/host_services.h
@@ -12,6 +12,7 @@
#include "mode_page_device.h"
#include
+#include