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") }} @@ -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 using namespace std; @@ -23,14 +24,11 @@ public: HostServices(); ~HostServices() {} - virtual bool Dispatch(SCSIDEV *) override; + virtual bool Dispatch() override; - vector Inquiry() const override; - void TestUnitReady(SCSIDEV *); - void StartStopUnit(SCSIDEV *); - - int ModeSense6(const DWORD *, BYTE *); - int ModeSense10(const DWORD *, BYTE *, int); + vector InquiryInternal() const override; + void TestUnitReady() override; + void StartStopUnit(); bool SupportsFile() const override { return false; } @@ -42,7 +40,10 @@ private: typedef ModePageDevice super; - Dispatcher dispatcher; + Dispatcher dispatcher; + + int ModeSense6(const DWORD *, BYTE *, int) override; + int ModeSense10(const DWORD *, BYTE *, int) override; void AddRealtimeClockPage(map>&, bool) const; }; diff --git a/src/raspberrypi/devices/interfaces/scsi_block_commands.h b/src/raspberrypi/devices/interfaces/scsi_block_commands.h index 0733d017..d983b661 100644 --- a/src/raspberrypi/devices/interfaces/scsi_block_commands.h +++ b/src/raspberrypi/devices/interfaces/scsi_block_commands.h @@ -3,7 +3,7 @@ // SCSI Target Emulator RaSCSI Reloaded // for Raspberry Pi // -// Copyright (C) 2021 Uwe Seimet +// Copyright (C) 2021-2022 Uwe Seimet // // Interface for SCSI block commands (see https://www.t10.org/drafts.htm, SBC-5) // @@ -13,8 +13,6 @@ #include "scsi_primary_commands.h" -class SASIDEV; - class ScsiBlockCommands : virtual public ScsiPrimaryCommands { public: @@ -23,11 +21,11 @@ public: virtual ~ScsiBlockCommands() {} // Mandatory commands - virtual void FormatUnit(SASIDEV *) = 0; - virtual void ReadCapacity10(SASIDEV *) = 0; - virtual void ReadCapacity16(SASIDEV *) = 0; - virtual void Read10(SASIDEV *) = 0; - virtual void Read16(SASIDEV *) = 0; - virtual void Write10(SASIDEV *) = 0; - virtual void Write16(SASIDEV *) = 0; + virtual void FormatUnit() = 0; + virtual void ReadCapacity10() = 0; + virtual void ReadCapacity16() = 0; + virtual void Read10() = 0; + virtual void Read16() = 0; + virtual void Write10() = 0; + virtual void Write16() = 0; }; diff --git a/src/raspberrypi/devices/interfaces/scsi_mmc_commands.h b/src/raspberrypi/devices/interfaces/scsi_mmc_commands.h index d4666e14..c8680171 100644 --- a/src/raspberrypi/devices/interfaces/scsi_mmc_commands.h +++ b/src/raspberrypi/devices/interfaces/scsi_mmc_commands.h @@ -3,7 +3,7 @@ // SCSI Target Emulator RaSCSI Reloaded // for Raspberry Pi // -// Copyright (C) 2021 Uwe Seimet +// Copyright (C) 2021-2022 Uwe Seimet // // Interface for SCSI Multi-Media commands (see https://www.t10.org/drafts.htm, MMC-6) // @@ -13,8 +13,6 @@ #include "scsi_primary_commands.h" -class SASIDEV; - class ScsiMmcCommands : virtual public ScsiPrimaryCommands { public: @@ -22,6 +20,6 @@ public: ScsiMmcCommands() {} virtual ~ScsiMmcCommands() {} - virtual void ReadToc(SASIDEV *) = 0; - virtual void GetEventStatusNotification(SASIDEV *) = 0; + virtual void ReadToc() = 0; + virtual void GetEventStatusNotification() = 0; }; diff --git a/src/raspberrypi/devices/interfaces/scsi_primary_commands.h b/src/raspberrypi/devices/interfaces/scsi_primary_commands.h index 902239f5..420fb760 100644 --- a/src/raspberrypi/devices/interfaces/scsi_primary_commands.h +++ b/src/raspberrypi/devices/interfaces/scsi_primary_commands.h @@ -3,7 +3,7 @@ // SCSI Target Emulator RaSCSI Reloaded // for Raspberry Pi // -// Copyright (C) 2021 Uwe Seimet +// Copyright (C) 2021-2022 Uwe Seimet // // Interface for SCSI primary commands (see https://www.t10.org/drafts.htm, SPC-6) // @@ -11,8 +11,6 @@ #pragma once -class SASIDEV; - class ScsiPrimaryCommands { public: @@ -21,10 +19,10 @@ public: virtual ~ScsiPrimaryCommands() {} // Mandatory commands - virtual void TestUnitReady(SASIDEV *) = 0; - virtual void Inquiry(SASIDEV *) = 0; - virtual void ReportLuns(SASIDEV *) = 0; + virtual void TestUnitReady() = 0; + virtual void Inquiry() = 0; + virtual void ReportLuns() = 0; // Implemented for all RaSCSI device types - virtual void RequestSense(SASIDEV *) = 0; + virtual void RequestSense() = 0; }; diff --git a/src/raspberrypi/devices/interfaces/scsi_printer_commands.h b/src/raspberrypi/devices/interfaces/scsi_printer_commands.h index 45729472..b9e07aab 100644 --- a/src/raspberrypi/devices/interfaces/scsi_printer_commands.h +++ b/src/raspberrypi/devices/interfaces/scsi_printer_commands.h @@ -3,7 +3,7 @@ // SCSI Target Emulator RaSCSI Reloaded // for Raspberry Pi // -// Copyright (C) 2022 Uwe Seimet +// Copyright (C) 2021-2022 Uwe Seimet // // Interface for SCSI printer commands (see SCSI-2 specification) // @@ -13,8 +13,6 @@ #include "scsi_primary_commands.h" -class SCSIDEV; - class ScsiPrinterCommands : virtual public ScsiPrimaryCommands { public: @@ -23,8 +21,8 @@ public: virtual ~ScsiPrinterCommands() {} // Mandatory commands - virtual void Print(SCSIDEV *) = 0; - virtual void ReleaseUnit(SCSIDEV *) = 0; - virtual void ReserveUnit(SCSIDEV *) = 0; - virtual void SendDiagnostic(SCSIDEV *) = 0; + virtual void Print() = 0; + virtual void ReleaseUnit() = 0; + virtual void ReserveUnit() = 0; + virtual void SendDiagnostic() = 0; }; diff --git a/src/raspberrypi/devices/mode_page_device.cpp b/src/raspberrypi/devices/mode_page_device.cpp index b1e5d4ae..025db176 100644 --- a/src/raspberrypi/devices/mode_page_device.cpp +++ b/src/raspberrypi/devices/mode_page_device.cpp @@ -10,13 +10,13 @@ //--------------------------------------------------------------------------- #include "log.h" -#include "controllers/scsidev_ctrl.h" +#include "rascsi_exceptions.h" #include "mode_page_device.h" using namespace std; using namespace scsi_defs; -ModePageDevice::ModePageDevice(const string& id) : PrimaryDevice(id) +ModePageDevice::ModePageDevice(const string& id) : PrimaryDevice(id), dispatcher({}) { dispatcher.AddCommand(eCmdModeSense6, "ModeSense6", &ModePageDevice::ModeSense6); dispatcher.AddCommand(eCmdModeSense10, "ModeSense10", &ModePageDevice::ModeSense10); @@ -24,14 +24,18 @@ ModePageDevice::ModePageDevice(const string& id) : PrimaryDevice(id) dispatcher.AddCommand(eCmdModeSelect10, "ModeSelect10", &ModePageDevice::ModeSelect10); } -bool ModePageDevice::Dispatch(SCSIDEV *controller) +bool ModePageDevice::Dispatch() { // The superclass class handles the less specific commands - return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller); + return dispatcher.Dispatch(this, ctrl->cmd[0]) ? true : super::Dispatch(); } int ModePageDevice::AddModePages(const DWORD *cdb, BYTE *buf, int max_length) { + if (max_length <= 0) { + return 0; + } + bool changeable = (cdb[2] & 0xc0) == 0x40; // Get page code (0x3f means all pages) @@ -43,11 +47,9 @@ int ModePageDevice::AddModePages(const DWORD *cdb, BYTE *buf, int max_length) map> pages; AddModePages(pages, page, changeable); - // If no mode data were added at all something must be wrong if (pages.empty()) { LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, page); - SetStatusCode(STATUS_INVALIDCDB); - return 0; + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } // Holds all mode page data @@ -88,69 +90,45 @@ int ModePageDevice::AddModePages(const DWORD *cdb, BYTE *buf, int max_length) return size; } -void ModePageDevice::ModeSense6(SASIDEV *controller) +void ModePageDevice::ModeSense6() { - ctrl->length = ModeSense6(ctrl->cmd, ctrl->buffer); - if (ctrl->length <= 0) { - controller->Error(); - return; - } + ctrl->length = ModeSense6(ctrl->cmd, ctrl->buffer, ctrl->bufsize); - controller->DataIn(); + EnterDataInPhase(); } -void ModePageDevice::ModeSense10(SASIDEV *controller) +void ModePageDevice::ModeSense10() { ctrl->length = ModeSense10(ctrl->cmd, ctrl->buffer, ctrl->bufsize); - if (ctrl->length <= 0) { - controller->Error(); - return; - } - controller->DataIn(); + EnterDataInPhase(); } -bool ModePageDevice::ModeSelect(const DWORD*, const BYTE *, int) +void ModePageDevice::ModeSelect(const DWORD*, const BYTE *, int) { - // Cannot be set - SetStatusCode(STATUS_INVALIDPRM); - - return false; + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE); } -void ModePageDevice::ModeSelect6(SASIDEV *controller) +void ModePageDevice::ModeSelect6() { - LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, ctrl->buffer[0]); - ctrl->length = ModeSelectCheck6(); - if (ctrl->length <= 0) { - controller->Error(); - return; - } - controller->DataOut(); + EnterDataOutPhase(); } -void ModePageDevice::ModeSelect10(SASIDEV *controller) +void ModePageDevice::ModeSelect10() { - LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, ctrl->buffer[0]); - ctrl->length = ModeSelectCheck10(); - if (ctrl->length <= 0) { - controller->Error(); - return; - } - controller->DataOut(); + EnterDataOutPhase(); } int ModePageDevice::ModeSelectCheck(int length) { - // Error if save parameters are set for other types than of SCHD or SCRM + // Error if save parameters are set for other types than of SCHD, SCRM or SCMO // TODO The assumption above is not correct, and this code should be located elsewhere - if (!IsSCSIHD() && (ctrl->cmd[1] & 0x01)) { - SetStatusCode(STATUS_INVALIDCDB); - return 0; + if (GetType() != "SCHD" && GetType() != "SCRM" && GetType() != "SCMO" && (ctrl->cmd[1] & 0x01)) { + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } return length; diff --git a/src/raspberrypi/devices/mode_page_device.h b/src/raspberrypi/devices/mode_page_device.h index 87358482..708ff116 100644 --- a/src/raspberrypi/devices/mode_page_device.h +++ b/src/raspberrypi/devices/mode_page_device.h @@ -23,13 +23,9 @@ public: ModePageDevice(const string&); virtual ~ModePageDevice() {} - virtual bool Dispatch(SCSIDEV *) override; + virtual bool Dispatch() override; - virtual int ModeSense6(const DWORD *, BYTE *) = 0; - virtual int ModeSense10(const DWORD *, BYTE *, int) = 0; - - // TODO This method should not be called by SASIDEV - virtual bool ModeSelect(const DWORD *, const BYTE *, int); + virtual void ModeSelect(const DWORD *, const BYTE *, int); protected: @@ -40,12 +36,15 @@ private: typedef PrimaryDevice super; - Dispatcher dispatcher; + Dispatcher dispatcher; - void ModeSense6(SASIDEV *); - void ModeSense10(SASIDEV *); - void ModeSelect6(SASIDEV *); - void ModeSelect10(SASIDEV *); + virtual int ModeSense6(const DWORD *, BYTE *, int) = 0; + virtual int ModeSense10(const DWORD *, BYTE *, int) = 0; + + void ModeSense6(); + void ModeSense10(); + void ModeSelect6(); + void ModeSelect10(); int ModeSelectCheck(int); int ModeSelectCheck6(); diff --git a/src/raspberrypi/devices/primary_device.cpp b/src/raspberrypi/devices/primary_device.cpp index 60e0d537..c347a8f8 100644 --- a/src/raspberrypi/devices/primary_device.cpp +++ b/src/raspberrypi/devices/primary_device.cpp @@ -8,17 +8,15 @@ //--------------------------------------------------------------------------- #include "log.h" -#include "controllers/scsidev_ctrl.h" +#include "rascsi_exceptions.h" #include "dispatcher.h" #include "primary_device.h" using namespace std; using namespace scsi_defs; -PrimaryDevice::PrimaryDevice(const string& id) : ScsiPrimaryCommands(), Device(id) +PrimaryDevice::PrimaryDevice(const string& id) : ScsiPrimaryCommands(), Device(id), dispatcher({}) { - ctrl = NULL; - // Mandatory SCSI primary commands dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady); dispatcher.AddCommand(eCmdInquiry, "Inquiry", &PrimaryDevice::Inquiry); @@ -28,30 +26,32 @@ PrimaryDevice::PrimaryDevice(const string& id) : ScsiPrimaryCommands(), Device(i dispatcher.AddCommand(eCmdRequestSense, "RequestSense", &PrimaryDevice::RequestSense); } -bool PrimaryDevice::Dispatch(SCSIDEV *controller) +bool PrimaryDevice::Dispatch() { - return dispatcher.Dispatch(this, controller); + return dispatcher.Dispatch(this, ctrl->cmd[0]); } -void PrimaryDevice::TestUnitReady(SASIDEV *controller) +void PrimaryDevice::SetController(AbstractController *controller) { - if (!CheckReady()) { - controller->Error(); - return; - } - - controller->Status(); + this->controller = controller; + ctrl = controller->GetCtrl(); } -void PrimaryDevice::Inquiry(SASIDEV *controller) +void PrimaryDevice::TestUnitReady() +{ + CheckReady(); + + EnterStatusPhase(); +} + +void PrimaryDevice::Inquiry() { // EVPD and page code check if ((ctrl->cmd[1] & 0x01) || ctrl->cmd[2]) { - controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); - return; + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } - vector buf = Inquiry(); + vector buf = InquiryInternal(); size_t allocation_length = ctrl->cmd[4] + (ctrl->cmd[3] << 8); if (allocation_length > buf.size()) { @@ -64,63 +64,68 @@ void PrimaryDevice::Inquiry(SASIDEV *controller) int lun = controller->GetEffectiveLun(); // Report if the device does not support the requested LUN - if (!ctrl->unit[lun]) { - LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, ctrl->device->GetId()); + if (!controller->HasDeviceForLun(lun)) { + LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, GetId()); // Signal that the requested LUN does not exist ctrl->buffer[0] |= 0x7f; } - controller->DataIn(); + EnterDataInPhase(); } -void PrimaryDevice::ReportLuns(SASIDEV *controller) +void PrimaryDevice::ReportLuns() { int allocation_length = (ctrl->cmd[6] << 24) + (ctrl->cmd[7] << 16) + (ctrl->cmd[8] << 8) + ctrl->cmd[9]; BYTE *buf = ctrl->buffer; - memset(buf, 0, allocation_length); + memset(buf, 0, allocation_length < ctrl->bufsize ? allocation_length : ctrl->bufsize); int size = 0; // Only SELECT REPORT mode 0 is supported - if (!ctrl->cmd[2]) { - for (int lun = 0; lun < controller->GetCtrl()->device->GetSupportedLuns(); lun++) { - if (controller->GetCtrl()->unit[lun]) { - size += 8; - buf[size + 7] = lun; - } - } - - buf[2] = size >> 8; - buf[3] = size; + if (ctrl->cmd[2]) { + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } + for (int lun = 0; lun < controller->GetMaxLuns(); lun++) { + if (controller->HasDeviceForLun(lun)) { + size += 8; + buf[size + 7] = lun; + } + } + + buf[2] = size >> 8; + buf[3] = size; + size += 8; ctrl->length = allocation_length < size ? allocation_length : size; - controller->DataIn(); + EnterDataInPhase(); } -void PrimaryDevice::RequestSense(SASIDEV *controller) +void PrimaryDevice::RequestSense() { int lun = controller->GetEffectiveLun(); // Note: According to the SCSI specs the LUN handling for REQUEST SENSE non-existing LUNs do *not* result // in CHECK CONDITION. Only the Sense Key and ASC are set in order to signal the non-existing LUN. - if (!ctrl->unit[lun]) { + if (!controller->HasDeviceForLun(lun)) { // LUN 0 can be assumed to be present (required to call RequestSense() below) + assert(controller->HasDeviceForLun(0)); + lun = 0; + // Do not raise an exception here because the rest of the code must be executed controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN); + ctrl->status = 0x00; } + vector buf = controller->GetDeviceForLun(lun)->HandleRequestSense(); + size_t allocation_length = ctrl->cmd[4]; - - vector buf = ((PrimaryDevice *)ctrl->unit[lun])->RequestSense(allocation_length); - if (allocation_length > buf.size()) { allocation_length = buf.size(); } @@ -128,43 +133,38 @@ void PrimaryDevice::RequestSense(SASIDEV *controller) memcpy(ctrl->buffer, buf.data(), allocation_length); ctrl->length = allocation_length; - controller->DataIn(); + EnterDataInPhase(); } -bool PrimaryDevice::CheckReady() +void PrimaryDevice::CheckReady() { // Not ready if reset if (IsReset()) { - SetStatusCode(STATUS_DEVRESET); SetReset(false); LOGTRACE("%s Device in reset", __PRETTY_FUNCTION__); - return false; + throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::POWER_ON_OR_RESET); } // Not ready if it needs attention if (IsAttn()) { - SetStatusCode(STATUS_ATTENTION); SetAttn(false); LOGTRACE("%s Device in needs attention", __PRETTY_FUNCTION__); - return false; + throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE); } // Return status if not ready if (!IsReady()) { - SetStatusCode(STATUS_NOTREADY); LOGTRACE("%s Device not ready", __PRETTY_FUNCTION__); - return false; + throw scsi_error_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT); } // Initialization with no error LOGTRACE("%s Device is ready", __PRETTY_FUNCTION__); - - return true; } -vector PrimaryDevice::Inquiry(device_type type, scsi_level level, bool is_removable) const +vector PrimaryDevice::HandleInquiry(device_type type, scsi_level level, bool is_removable) const { - vector buf = vector(0x1F + 5); + vector buf(0x1F + 5); // Basic data // buf[0] ... SCSI device type @@ -184,11 +184,11 @@ vector PrimaryDevice::Inquiry(device_type type, scsi_level level, bool is_ return buf; } -vector PrimaryDevice::RequestSense(int) +vector PrimaryDevice::HandleRequestSense() { // Return not ready only if there are no errors - if (GetStatusCode() == STATUS_NOERROR && !IsReady()) { - SetStatusCode(STATUS_NOTREADY); + if (!GetStatusCode() && !IsReady()) { + throw scsi_error_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT); } // Set 18 bytes including extended sense data @@ -208,7 +208,7 @@ vector PrimaryDevice::RequestSense(int) return buf; } -bool PrimaryDevice::WriteBytes(BYTE *buf, uint32_t length) +bool PrimaryDevice::WriteByteSequence(BYTE *, uint32_t) { LOGERROR("%s Writing bytes is not supported by this device", __PRETTY_FUNCTION__); diff --git a/src/raspberrypi/devices/primary_device.h b/src/raspberrypi/devices/primary_device.h index fd8e4a2a..41e33076 100644 --- a/src/raspberrypi/devices/primary_device.h +++ b/src/raspberrypi/devices/primary_device.h @@ -11,8 +11,8 @@ #pragma once -#include "controllers/scsidev_ctrl.h" #include "interfaces/scsi_primary_commands.h" +#include "controllers/scsi_controller.h" #include "device.h" #include "dispatcher.h" #include @@ -26,29 +26,33 @@ public: PrimaryDevice(const string&); virtual ~PrimaryDevice() {} - virtual bool Dispatch(SCSIDEV *); + virtual bool Dispatch(); - void TestUnitReady(SASIDEV *); - void RequestSense(SASIDEV *); - virtual void Inquiry(SASIDEV *); - - void SetCtrl(SASIDEV::ctrl_t *ctrl) { this->ctrl = ctrl; } - - bool CheckReady(); - virtual vector Inquiry() const = 0; - virtual vector RequestSense(int); - virtual bool WriteBytes(BYTE *, uint32_t); + void SetController(AbstractController *); + virtual bool WriteByteSequence(BYTE *, uint32_t); virtual int GetSendDelay() const { return BUS::SEND_NO_DELAY; } protected: - vector Inquiry(scsi_defs::device_type, scsi_level, bool) const; + vector HandleInquiry(scsi_defs::device_type, scsi_level, bool) const; + virtual vector InquiryInternal() const = 0; + void CheckReady(); - SASIDEV::ctrl_t *ctrl; + void EnterStatusPhase() { controller->Status(); } + void EnterDataInPhase() { controller->DataIn(); } + void EnterDataOutPhase() { controller->DataOut(); } + + AbstractController *controller = nullptr; + AbstractController::ctrl_t *ctrl = nullptr; private: - Dispatcher dispatcher; + void TestUnitReady(); + void RequestSense(); + void ReportLuns(); + void Inquiry(); - void ReportLuns(SASIDEV *); + vector HandleRequestSense(); + + Dispatcher dispatcher; }; diff --git a/src/raspberrypi/devices/sasihd.cpp b/src/raspberrypi/devices/sasihd.cpp deleted file mode 100644 index 36c91591..00000000 --- a/src/raspberrypi/devices/sasihd.cpp +++ /dev/null @@ -1,130 +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 hard disk ] -// -//--------------------------------------------------------------------------- - -#include "sasihd.h" -#include "fileio.h" -#include "exceptions.h" -#include "../config.h" - -SASIHD::SASIHD(const unordered_set& sector_sizes) : Disk("SAHD") -{ - SetSectorSizes(sector_sizes); -} - -void SASIHD::Reset() -{ - // Unlock, clear attention - SetLocked(false); - SetAttn(false); - - // Reset, clear the code - SetReset(false); - SetStatusCode(STATUS_NOERROR); -} - -void SASIHD::Open(const Filepath& path) -{ - assert(!IsReady()); - - // Open as read-only - Fileio fio; - if (!fio.Open(path, Fileio::ReadOnly)) { - throw file_not_found_exception("Can't open SASI hard disk file"); - } - - // Get file size - off_t size = fio.GetFileSize(); - fio.Close(); - - // Sector size (default 256 bytes) and number of blocks - SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 256, true); - SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount())); - - // SASI only supports READ/WRITE(6), limiting the block count to 2^21 - if (GetBlockCount() > 2097152) { - throw io_exception("SASI drives are limited to 2097152 blocks"); - } - - #if defined(REMOVE_FIXED_SASIHD_SIZE) - // Effective size must be a multiple of the sector size - size = (size / GetSectorSizeInBytes()) * GetSectorSizeInBytes(); - #else - // 10MB, 20MB, 40MBのみ - switch (size) { - // 10MB (10441728 BS=256 C=40788) - case 0x9f5400: - break; - - // 20MB (20748288 BS=256 C=81048) - case 0x13c9800: - break; - - // 40MB (41496576 BS=256 C=162096) - case 0x2793000: - break; - - // Other (Not supported ) - default: - throw io_exception("Unsupported file size"); - } - #endif // REMOVE_FIXED_SASIHD_SIZE - - Disk::Open(path); - FileSupport::SetPath(path); -} - -vector SASIHD::Inquiry() const -{ - // Byte 0 = 0: Direct access device - - return vector(2); -} - -vector SASIHD::RequestSense(int allocation_length) -{ - // Transfer 4 bytes when size is 0 (Shugart Associates System Interface specification) - vector buf(allocation_length ? allocation_length : 4); - - // SASI fixed to non-extended format - buf[0] = (BYTE)(GetStatusCode() >> 16); - buf[1] = (BYTE)(GetLun() << 5); - - LOGTRACE("%s Status $%02X",__PRETTY_FUNCTION__, buf[0]); - - return buf; -} - -void SASIHD::ReadCapacity10(SASIDEV *controller) -{ - BYTE *buf = ctrl->buffer; - - // Create end of logical block address (disk.blocks-1) - uint32_t blocks = disk.blocks - 1; - buf[0] = (BYTE)(blocks >> 24); - buf[1] = (BYTE)(blocks >> 16); - buf[2] = (BYTE)(blocks >> 8); - buf[3] = (BYTE)blocks; - - // Create block length (1 << disk.size) - uint32_t length = 1 << disk.size; - buf[4] = (BYTE)(length >> 8); - buf[5] = (BYTE)length; - - // the size - ctrl->length = 6; - - controller->DataIn(); -} diff --git a/src/raspberrypi/devices/sasihd.h b/src/raspberrypi/devices/sasihd.h deleted file mode 100644 index 003fa2d4..00000000 --- a/src/raspberrypi/devices/sasihd.h +++ /dev/null @@ -1,39 +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 hard disk ] -// -//--------------------------------------------------------------------------- -#pragma once - -#include "os.h" -#include "disk.h" -#include "filepath.h" - -//=========================================================================== -// -// SASI Hard Disk -// -//=========================================================================== -class SASIHD : public Disk, public FileSupport -{ -public: - SASIHD(const unordered_set&); - ~SASIHD() {} - - void Reset(); - void Open(const Filepath&) override; - - vector RequestSense(int) override; - vector Inquiry() const override; - virtual void ReadCapacity10(SASIDEV *) override; -}; diff --git a/src/raspberrypi/devices/scsi_command_util.cpp b/src/raspberrypi/devices/scsi_command_util.cpp new file mode 100644 index 00000000..38544c4b --- /dev/null +++ b/src/raspberrypi/devices/scsi_command_util.cpp @@ -0,0 +1,89 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "log.h" +#include "rascsi_exceptions.h" +#include "scsi_command_util.h" + +using namespace scsi_defs; + +void scsi_command_util::ModeSelect(const DWORD *cdb, const BYTE *buf, int length, int sector_size) +{ + assert(length >= 0); + + // PF + if (cdb[1] & 0x10) { + bool has_valid_page_code = false; + + // Mode Parameter header + if (length >= 12) { + // Check the block length + if (buf[9] != (BYTE)(sector_size >> 16) || buf[10] != (BYTE)(sector_size >> 8) || + buf[11] != (BYTE)sector_size) { + // See below for details + LOGWARN("In order to change the sector size use the -b option when launching rascsi"); + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST); + } + + buf += 12; + length -= 12; + } + + // Parsing the page + // TODO The length handling is wrong in case of length < size + while (length > 0) { + int page = buf[0]; + + switch (page) { + // Format device page + case 0x03: { + // With this page the sector size for a subsequent FORMAT can be selected, but only very few + // drives support this, e.g FUJITSU M2624S + // We are fine as long as the current sector size remains unchanged + if (buf[0xc] != (BYTE)(sector_size >> 8) || buf[0xd] != (BYTE)sector_size) { + // With rascsi it is not possible to permanently (by formatting) change the sector size, + // because the size is an externally configurable setting only + LOGWARN("In order to change the sector size use the -b option when launching rascsi"); + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST); + } + + has_valid_page_code = true; + } + break; + + default: + LOGWARN("Unknown MODE SELECT page code: $%02X", page); + break; + } + + // Advance to the next page + int size = buf[1] + 2; + length -= size; + buf += size; + } + + if (!has_valid_page_code) { + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST); + } + } + else { + // Vendor-specific parameters (SCSI-1) are not supported + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST); + } +} + +void scsi_command_util::EnrichFormatPage(map>& pages, bool changeable, int sector_size) +{ + if (changeable) { + // The sector size is simulated to be changeable, see the MODE SELECT implementation for details + vector& format_page = pages[3]; + format_page[12] = sector_size >> 8; + format_page[13] = sector_size; + } +} diff --git a/src/raspberrypi/devices/scsi_command_util.h b/src/raspberrypi/devices/scsi_command_util.h new file mode 100644 index 00000000..1530aa64 --- /dev/null +++ b/src/raspberrypi/devices/scsi_command_util.h @@ -0,0 +1,24 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// +// Shared code for SCSI command implementations +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "os.h" +#include +#include + +using namespace std; + +namespace scsi_command_util +{ + void ModeSelect(const DWORD *, const BYTE *, int, int); + void EnrichFormatPage(map>&, bool, int); +} diff --git a/src/raspberrypi/devices/scsi_daynaport.cpp b/src/raspberrypi/devices/scsi_daynaport.cpp index 1ab19fd5..3fe0ab3c 100644 --- a/src/raspberrypi/devices/scsi_daynaport.cpp +++ b/src/raspberrypi/devices/scsi_daynaport.cpp @@ -27,6 +27,7 @@ // Note: This requires a DaynaPort SCSI Link driver. //--------------------------------------------------------------------------- +#include "rascsi_exceptions.h" #include "scsi_daynaport.h" using namespace scsi_defs; @@ -35,11 +36,8 @@ const BYTE SCSIDaynaPort::m_bcast_addr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff const BYTE SCSIDaynaPort::m_apple_talk_addr[6] = { 0x09, 0x00, 0x07, 0xff, 0xff, 0xff }; // TODO Disk should not be the superclass -SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP") +SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP"), dispatcher({}) { - m_tap = NULL; - m_bTapEnable = false; - dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &SCSIDaynaPort::TestUnitReady); dispatcher.AddCommand(eCmdRead6, "Read6", &SCSIDaynaPort::Read6); dispatcher.AddCommand(eCmdWrite6, "Write6", &SCSIDaynaPort::Write6); @@ -58,17 +56,16 @@ SCSIDaynaPort::~SCSIDaynaPort() } } -bool SCSIDaynaPort::Dispatch(SCSIDEV *controller) +bool SCSIDaynaPort::Dispatch() { // TODO As long as DaynaPort suffers from being a subclass of Disk at least reject MODE SENSE and MODE SELECT - SASIDEV::ctrl_t *ctrl = controller->GetCtrl(); if (ctrl->cmd[0] == eCmdModeSense6 || ctrl->cmd[0] == eCmdModeSelect6 || ctrl->cmd[0] == eCmdModeSense10 || ctrl->cmd[0] == eCmdModeSelect10) { return false; } // The superclass class handles the less specific commands - return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller); + return dispatcher.Dispatch(this, ctrl->cmd[0]) ? true : super::Dispatch(); } bool SCSIDaynaPort::Init(const unordered_map& params) @@ -118,9 +115,9 @@ void SCSIDaynaPort::Open(const Filepath& path) m_tap->OpenDump(path); } -vector SCSIDaynaPort::Inquiry() const +vector SCSIDaynaPort::InquiryInternal() const { - vector buf = PrimaryDevice::Inquiry(device_type::PROCESSOR, scsi_level::SCSI_2, false); + vector buf = HandleInquiry(device_type::PROCESSOR, scsi_level::SCSI_2, false); // The Daynaport driver for the Mac expects 37 bytes: Increase additional length and // add a vendor-specific byte in order to satisfy this driver. @@ -161,7 +158,7 @@ vector SCSIDaynaPort::Inquiry() const // - The SCSI/Link apparently has about 6KB buffer space for packets. // //--------------------------------------------------------------------------- -int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, uint64_t block) +int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, uint64_t) { int rx_packet_size = 0; scsi_resp_read_t *response = (scsi_resp_read_t*)buf; @@ -287,27 +284,17 @@ int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, uint64_t block) return DAYNAPORT_READ_HEADER_SZ; } -//--------------------------------------------------------------------------- -// -// WRITE check -// -//--------------------------------------------------------------------------- -int SCSIDaynaPort::WriteCheck(DWORD block) +int SCSIDaynaPort::WriteCheck(uint64_t) { - // Status check - if (!CheckReady()) { - return 0; - } + CheckReady(); if (!m_bTapEnable){ - SetStatusCode(STATUS_NOTREADY); - return 0; + throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::MEDIUM_NOT_PRESENT); } - // Success return 1; } - + //--------------------------------------------------------------------------- // // Write @@ -326,7 +313,7 @@ int SCSIDaynaPort::WriteCheck(DWORD block) // XX XX ... is the actual packet // //--------------------------------------------------------------------------- -bool SCSIDaynaPort::Write(const DWORD *cdb, const BYTE *buf, DWORD block) +bool SCSIDaynaPort::WriteBytes(const DWORD *cdb, BYTE *buf, uint64_t) { BYTE data_format = cdb[5]; WORD data_length = (WORD)cdb[4] + ((WORD)cdb[3] << 8); @@ -334,21 +321,20 @@ bool SCSIDaynaPort::Write(const DWORD *cdb, const BYTE *buf, DWORD block) if (data_format == 0x00){ m_tap->Tx(buf, data_length); LOGTRACE("%s Transmitted %u bytes (00 format)", __PRETTY_FUNCTION__, data_length); - return true; } else if (data_format == 0x80){ // The data length is specified in the first 2 bytes of the payload data_length=(WORD)buf[1] + ((WORD)buf[0] << 8); m_tap->Tx(&buf[4], data_length); LOGTRACE("%s Transmitted %u bytes (80 format)", __PRETTY_FUNCTION__, data_length); - return true; } else { // LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)command->format); LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)data_format); - return true; } + + return true; } //--------------------------------------------------------------------------- @@ -393,7 +379,6 @@ int SCSIDaynaPort::RetrieveStats(const DWORD *cdb, BYTE *buffer) response_size = allocation_length; } - // Success return response_size; } @@ -435,14 +420,13 @@ bool SCSIDaynaPort::EnableInterface(const DWORD *cdb) return result; } -void SCSIDaynaPort::TestUnitReady(SASIDEV *controller) +void SCSIDaynaPort::TestUnitReady() { - // TEST UNIT READY Success - - controller->Status(); + // Always successful + EnterStatusPhase(); } -void SCSIDaynaPort::Read6(SASIDEV *controller) +void SCSIDaynaPort::Read6() { // Get record number and block number uint32_t record = ctrl->cmd[1] & 0x1f; @@ -456,8 +440,7 @@ void SCSIDaynaPort::Read6(SASIDEV *controller) // generated by the DaynaPort driver so ignore them if (ctrl->cmd[5] != 0xc0 && ctrl->cmd[5] != 0x80) { LOGTRACE("%s Control value %d, (%04X), returning invalid CDB", __PRETTY_FUNCTION__, ctrl->cmd[5], ctrl->cmd[5]); - controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); - return; + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } LOGTRACE("%s READ(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl->blocks); @@ -468,16 +451,16 @@ void SCSIDaynaPort::Read6(SASIDEV *controller) // Set next block ctrl->next = record + 1; - controller->DataIn(); + EnterDataInPhase(); } -void SCSIDaynaPort::Write6(SASIDEV *controller) +void SCSIDaynaPort::Write6() { // Reallocate buffer (because it is not transfer for each block) if (ctrl->bufsize < DAYNAPORT_BUFFER_SIZE) { - free(ctrl->buffer); + delete[] ctrl->buffer; + ctrl->buffer = new BYTE[ctrl->bufsize]; ctrl->bufsize = DAYNAPORT_BUFFER_SIZE; - ctrl->buffer = (BYTE *)malloc(ctrl->bufsize); } DWORD data_format = ctrl->cmd[5]; @@ -494,32 +477,25 @@ void SCSIDaynaPort::Write6(SASIDEV *controller) LOGTRACE("%s length: %04X (%d) format: %02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->length, (int)ctrl->length, (unsigned int)data_format); if (ctrl->length <= 0) { - // Failure (Error) - controller->Error(); - return; + throw scsi_error_exception(); } // Set next block ctrl->blocks = 1; ctrl->next = 1; - controller->DataOut(); + EnterDataOutPhase(); } -void SCSIDaynaPort::RetrieveStatistics(SASIDEV *controller) +void SCSIDaynaPort::RetrieveStatistics() { ctrl->length = RetrieveStats(ctrl->cmd, ctrl->buffer); - if (ctrl->length <= 0) { - // Failure (Error) - controller->Error(); - return; - } // Set next block ctrl->blocks = 1; ctrl->next = 1; - controller->DataIn(); + EnterDataInPhase(); } //--------------------------------------------------------------------------- @@ -548,7 +524,7 @@ void SCSIDaynaPort::RetrieveStatistics(SASIDEV *controller) // value. // //--------------------------------------------------------------------------- -void SCSIDaynaPort::SetInterfaceMode(SASIDEV *controller) +void SCSIDaynaPort::SetInterfaceMode() { // Check whether this command is telling us to "Set Interface Mode" or "Set MAC Address" @@ -556,12 +532,12 @@ void SCSIDaynaPort::SetInterfaceMode(SASIDEV *controller) switch(ctrl->cmd[5]){ case SCSIDaynaPort::CMD_SCSILINK_SETMODE: // TODO Not implemented, do nothing - controller->Status(); + EnterStatusPhase(); break; case SCSIDaynaPort::CMD_SCSILINK_SETMAC: ctrl->length = 6; - controller->DataOut(); + EnterDataOutPhase(); break; default: @@ -570,30 +546,25 @@ void SCSIDaynaPort::SetInterfaceMode(SASIDEV *controller) } } -void SCSIDaynaPort::SetMcastAddr(SASIDEV *controller) +void SCSIDaynaPort::SetMcastAddr() { ctrl->length = (DWORD)ctrl->cmd[4]; if (ctrl->length == 0) { LOGWARN("%s Not supported SetMcastAddr Command %02X", __PRETTY_FUNCTION__, (WORD)ctrl->cmd[2]); - // Failure (Error) - controller->Error(); - return; + throw scsi_error_exception(); } - controller->DataOut(); + EnterDataOutPhase(); } -void SCSIDaynaPort::EnableInterface(SASIDEV *controller) +void SCSIDaynaPort::EnableInterface() { - bool status = EnableInterface(ctrl->cmd); - if (!status) { - // Failure (Error) - controller->Error(); - return; + if (!EnableInterface(ctrl->cmd)) { + throw scsi_error_exception(); } - controller->Status(); + EnterStatusPhase(); } int SCSIDaynaPort::GetSendDelay() const diff --git a/src/raspberrypi/devices/scsi_daynaport.h b/src/raspberrypi/devices/scsi_daynaport.h index 6a2d409a..2f592eb6 100644 --- a/src/raspberrypi/devices/scsi_daynaport.h +++ b/src/raspberrypi/devices/scsi_daynaport.h @@ -49,26 +49,26 @@ public: void Open(const Filepath& path) override; // Commands - vector Inquiry() const override; - int Read(const DWORD *cdb, BYTE *buf, uint64_t block) override; - bool Write(const DWORD *cdb, const BYTE *buf, DWORD block) override; - int WriteCheck(DWORD block) override; // WRITE check + vector InquiryInternal() const override; + int Read(const DWORD *cdb, BYTE *, uint64_t) override; + bool WriteBytes(const DWORD *, BYTE *, uint64_t); + int WriteCheck(uint64_t block) override; int RetrieveStats(const DWORD *cdb, BYTE *buffer); bool EnableInterface(const DWORD *cdb); void SetMacAddr(const DWORD *cdb, BYTE *buffer); // Set MAC address - void TestUnitReady(SASIDEV *) override; - void Read6(SASIDEV *) override; - void Write6(SASIDEV *) override; - void RetrieveStatistics(SASIDEV *); - void SetInterfaceMode(SASIDEV *); - void SetMcastAddr(SASIDEV *); - void EnableInterface(SASIDEV *); + void TestUnitReady() override; + void Read6() override; + void Write6() override; + void RetrieveStatistics(); + void SetInterfaceMode(); + void SetMcastAddr(); + void EnableInterface(); int GetSendDelay() const override; - bool Dispatch(SCSIDEV *) override; + bool Dispatch() override; const int DAYNAPORT_BUFFER_SIZE = 0x1000000; @@ -92,7 +92,7 @@ public: private: typedef Disk super; - Dispatcher dispatcher; + Dispatcher dispatcher; typedef struct __attribute__((packed)) { BYTE operation_code; @@ -131,9 +131,9 @@ private: const BYTE m_daynacom_mac_prefix[3] = { 0x00, 0x80, 0x19 }; - CTapDriver *m_tap; + CTapDriver *m_tap = nullptr; // TAP driver - bool m_bTapEnable; + bool m_bTapEnable = false; // TAP valid flag BYTE m_mac_addr[6]; // MAC Address diff --git a/src/raspberrypi/devices/scsi_host_bridge.cpp b/src/raspberrypi/devices/scsi_host_bridge.cpp index 4a4a6f10..caa882ae 100644 --- a/src/raspberrypi/devices/scsi_host_bridge.cpp +++ b/src/raspberrypi/devices/scsi_host_bridge.cpp @@ -16,7 +16,7 @@ // work with the Sharp X68000 operating system. //--------------------------------------------------------------------------- -#include "controllers/scsidev_ctrl.h" +#include "rascsi_exceptions.h" #include "scsi_host_bridge.h" #include "ctapdriver.h" #include "cfilesystem.h" @@ -24,19 +24,9 @@ using namespace std; using namespace scsi_defs; -SCSIBR::SCSIBR() : Disk("SCBR") +SCSIBR::SCSIBR() : Disk("SCBR"), dispatcher({}), fs(new CFileSys()) { - tap = NULL; - m_bTapEnable = false; - packet_enable = false; - - fsoptlen = 0; - fsoutlen = 0; - fsresult = 0; - packet_len = 0; - // Create host file system - fs = new CFileSys(); fs->Reset(); dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &SCSIBR::TestUnitReady); @@ -93,15 +83,15 @@ bool SCSIBR::Init(const unordered_map& params) #endif } -bool SCSIBR::Dispatch(SCSIDEV *controller) +bool SCSIBR::Dispatch() { // The superclass class handles the less specific commands - return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller); + return dispatcher.Dispatch(this, ctrl->cmd[0]) ? true : super::Dispatch(); } -vector SCSIBR::Inquiry() const +vector SCSIBR::InquiryInternal() const { - vector b = PrimaryDevice::Inquiry(device_type::COMMUNICATIONS, scsi_level::SCSI_2, false); + vector b = HandleInquiry(device_type::COMMUNICATIONS, scsi_level::SCSI_2, false); // The bridge returns 6 more additional bytes than the other devices vector buf = vector(0x1F + 8 + 5); @@ -123,10 +113,10 @@ vector SCSIBR::Inquiry() const return buf; } -void SCSIBR::TestUnitReady(SASIDEV *controller) +void SCSIBR::TestUnitReady() { // Always successful - controller->Status(); + EnterStatusPhase(); } int SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf) @@ -209,7 +199,7 @@ int SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf) return 0; } -bool SCSIBR::SendMessage10(const DWORD *cdb, BYTE *buf) +bool SCSIBR::WriteBytes(const DWORD *cdb, BYTE *buf, uint64_t) { // Type int type = cdb[2]; @@ -258,32 +248,29 @@ bool SCSIBR::SendMessage10(const DWORD *cdb, BYTE *buf) break; } - // Error - ASSERT(false); + assert(false); return false; } -void SCSIBR::GetMessage10(SASIDEV *controller) +void SCSIBR::GetMessage10() { // Reallocate buffer (because it is not transfer for each block) if (ctrl->bufsize < 0x1000000) { - free(ctrl->buffer); + delete[] ctrl->buffer; ctrl->bufsize = 0x1000000; - ctrl->buffer = (BYTE *)malloc(ctrl->bufsize); + ctrl->buffer = new BYTE[ctrl->bufsize]; } ctrl->length = GetMessage10(ctrl->cmd, ctrl->buffer); if (ctrl->length <= 0) { - // Failure (Error) - controller->Error(); - return; + throw scsi_error_exception(); } // Set next block ctrl->blocks = 1; ctrl->next = 1; - controller->DataIn(); + EnterDataInPhase(); } //--------------------------------------------------------------------------- @@ -293,13 +280,13 @@ void SCSIBR::GetMessage10(SASIDEV *controller) // This Send Message command is used by the X68000 host driver // //--------------------------------------------------------------------------- -void SCSIBR::SendMessage10(SASIDEV *controller) +void SCSIBR::SendMessage10() { // Reallocate buffer (because it is not transfer for each block) if (ctrl->bufsize < 0x1000000) { - free(ctrl->buffer); + delete[] ctrl->buffer; + ctrl->buffer = new BYTE[ctrl->bufsize]; ctrl->bufsize = 0x1000000; - ctrl->buffer = (BYTE *)malloc(ctrl->bufsize); } // Set transfer amount @@ -310,16 +297,14 @@ void SCSIBR::SendMessage10(SASIDEV *controller) ctrl->length |= ctrl->cmd[8]; if (ctrl->length <= 0) { - // Failure (Error) - controller->Error(); - return; + throw scsi_error_exception(); } // Set next block ctrl->blocks = 1; ctrl->next = 1; - controller->DataOut(); + EnterDataOutPhase(); } int SCSIBR::GetMacAddr(BYTE *mac) @@ -328,7 +313,7 @@ int SCSIBR::GetMacAddr(BYTE *mac) return 6; } -void SCSIBR::SetMacAddr(BYTE *mac) +void SCSIBR::SetMacAddr(const BYTE *mac) { memcpy(mac_addr, mac, 6); } diff --git a/src/raspberrypi/devices/scsi_host_bridge.h b/src/raspberrypi/devices/scsi_host_bridge.h index 0da3f5bf..c9424f54 100644 --- a/src/raspberrypi/devices/scsi_host_bridge.h +++ b/src/raspberrypi/devices/scsi_host_bridge.h @@ -37,33 +37,33 @@ public: ~SCSIBR(); bool Init(const unordered_map&) override; - bool Dispatch(SCSIDEV *) override; + bool Dispatch() override; // Commands - vector Inquiry() const override; - int GetMessage10(const DWORD *cdb, BYTE *buf); - bool SendMessage10(const DWORD *cdb, BYTE *buf); - void TestUnitReady(SASIDEV *) override; - void GetMessage10(SASIDEV *); - void SendMessage10(SASIDEV *); + vector InquiryInternal() const override; + int GetMessage10(const DWORD *, BYTE *); + bool WriteBytes(const DWORD *, BYTE *, uint64_t); + void TestUnitReady() override; + void GetMessage10(); + void SendMessage10(); private: typedef Disk super; - Dispatcher dispatcher; + Dispatcher dispatcher; int GetMacAddr(BYTE *buf); // Get MAC address - void SetMacAddr(BYTE *buf); // Set MAC address + void SetMacAddr(const BYTE *buf); // Set MAC address void ReceivePacket(); // Receive a packet - void GetPacketBuf(BYTE *buf); // Get a packet - void SendPacket(BYTE *buf, int len); // Send a packet + void GetPacketBuf(BYTE *buf); // Get a packet + void SendPacket(BYTE *buf, int len); // Send a packet - CTapDriver *tap; // TAP driver - bool m_bTapEnable; // TAP valid flag + CTapDriver *tap = nullptr; // TAP driver + bool m_bTapEnable = false; // TAP valid flag BYTE mac_addr[6]; // MAC Addres - int packet_len; // Receive packet size - BYTE packet_buf[0x1000]; // Receive packet buffer - bool packet_enable; // Received packet valid + int packet_len = 0; // Receive packet size + BYTE packet_buf[0x1000]; // Receive packet buffer + bool packet_enable = false; // Received packet valid int ReadFsResult(BYTE *buf); // Read filesystem (result code) int ReadFsOut(BYTE *buf); // Read filesystem (return data) @@ -99,9 +99,9 @@ private: void FS_Lock(BYTE *buf); // $58 - get exclusive control CFileSys *fs; // File system accessor - DWORD fsresult; // File system access result code + DWORD fsresult = 0; // File system access result code BYTE fsout[0x800]; // File system access result buffer - DWORD fsoutlen; // File system access result buffer size - BYTE fsopt[0x1000000]; // File system access buffer - DWORD fsoptlen; // File system access buffer size + DWORD fsoutlen = 0; // File system access result buffer size + BYTE fsopt[0x1000000]; // File system access buffer + DWORD fsoptlen = 0; // File system access buffer size }; diff --git a/src/raspberrypi/devices/scsi_printer.cpp b/src/raspberrypi/devices/scsi_printer.cpp index 2711596f..7a2eb126 100644 --- a/src/raspberrypi/devices/scsi_printer.cpp +++ b/src/raspberrypi/devices/scsi_printer.cpp @@ -40,23 +40,16 @@ // #include -#include "controllers/scsidev_ctrl.h" +#include "rascsi_exceptions.h" #include "../rasutil.h" #include "scsi_printer.h" -#define NOT_RESERVED -2 - using namespace std; using namespace scsi_defs; using namespace ras_util; -SCSIPrinter::SCSIPrinter() : PrimaryDevice("SCLP"), ScsiPrinterCommands() +SCSIPrinter::SCSIPrinter() : PrimaryDevice("SCLP"), ScsiPrinterCommands(), dispatcher({}) { - fd = -1; - reserving_initiator = NOT_RESERVED; - reservation_time = 0; - timeout = 0; - dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &SCSIPrinter::TestUnitReady); dispatcher.AddCommand(eCmdReserve6, "ReserveUnit", &SCSIPrinter::ReserveUnit); dispatcher.AddCommand(eCmdRelease6, "ReleaseUnit", &SCSIPrinter::ReleaseUnit); @@ -88,36 +81,32 @@ bool SCSIPrinter::Init(const unordered_map& params) return true; } -bool SCSIPrinter::Dispatch(SCSIDEV *controller) +bool SCSIPrinter::Dispatch() { // The superclass class handles the less specific commands - return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller); + return dispatcher.Dispatch(this, ctrl->cmd[0]) ? true : super::Dispatch(); } -void SCSIPrinter::TestUnitReady(SCSIDEV *controller) +void SCSIPrinter::TestUnitReady() { - if (!CheckReservation(controller)) { - return; - } + CheckReservation(); - controller->Status(); + EnterStatusPhase(); } -vector SCSIPrinter::Inquiry() const +vector SCSIPrinter::InquiryInternal() const { - return PrimaryDevice::Inquiry(device_type::PRINTER, scsi_level::SCSI_2, false); + return HandleInquiry(device_type::PRINTER, scsi_level::SCSI_2, false); } -void SCSIPrinter::ReserveUnit(SCSIDEV *controller) +void SCSIPrinter::ReserveUnit() { // The printer is released after a configurable time in order to prevent deadlocks caused by broken clients if (reservation_time + timeout < time(0)) { DiscardReservation(); } - if (!CheckReservation(controller)) { - return; - } + CheckReservation(); reserving_initiator = controller->GetInitiatorId(); @@ -130,14 +119,12 @@ void SCSIPrinter::ReserveUnit(SCSIDEV *controller) Cleanup(); - controller->Status(); + EnterStatusPhase(); } -void SCSIPrinter::ReleaseUnit(SCSIDEV *controller) +void SCSIPrinter::ReleaseUnit() { - if (!CheckReservation(controller)) { - return; - } + CheckReservation(); if (reserving_initiator != -1) { LOGTRACE("Released device ID %d, LUN %d reserved by initiator ID %d", GetId(), GetLun(), reserving_initiator); @@ -148,14 +135,12 @@ void SCSIPrinter::ReleaseUnit(SCSIDEV *controller) DiscardReservation(); - controller->Status(); + EnterStatusPhase(); } -void SCSIPrinter::Print(SCSIDEV *controller) +void SCSIPrinter::Print() { - if (!CheckReservation(controller)) { - return; - } + CheckReservation(); uint32_t length = ctrl->cmd[2]; length <<= 8; @@ -170,25 +155,21 @@ void SCSIPrinter::Print(SCSIDEV *controller) if (length > (uint32_t)ctrl->bufsize) { LOGERROR("Transfer buffer overflow"); - controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); - return; + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } ctrl->length = length; controller->SetByteTransfer(true); - controller->DataOut(); + EnterDataOutPhase(); } -void SCSIPrinter::SynchronizeBuffer(SCSIDEV *controller) +void SCSIPrinter::SynchronizeBuffer() { - if (!CheckReservation(controller)) { - return; - } + CheckReservation(); if (fd == -1) { - controller->Error(); - return; + throw scsi_error_exception(); } // Make the file readable for the lp user @@ -213,36 +194,33 @@ void SCSIPrinter::SynchronizeBuffer(SCSIDEV *controller) if (system(cmd.c_str())) { LOGERROR("Printing failed, the printing system might not be configured"); - controller->Error(); - } - else { - controller->Status(); + unlink(filename); + + throw scsi_error_exception(); } unlink(filename); + + EnterStatusPhase(); } -void SCSIPrinter::SendDiagnostic(SCSIDEV *controller) +void SCSIPrinter::SendDiagnostic() { - if (!CheckReservation(controller)) { - return; - } + CheckReservation(); - controller->Status(); + EnterStatusPhase(); } -void SCSIPrinter::StopPrint(SCSIDEV *controller) +void SCSIPrinter::StopPrint() { - if (!CheckReservation(controller)) { - return; - } + CheckReservation(); // Nothing to do, printing has not yet been started - controller->Status(); + EnterStatusPhase(); } -bool SCSIPrinter::WriteBytes(BYTE *buf, uint32_t length) +bool SCSIPrinter::WriteByteSequence(BYTE *buf, uint32_t length) { if (fd == -1) { strcpy(filename, TMP_FILE_PATTERN); @@ -259,15 +237,14 @@ bool SCSIPrinter::WriteBytes(BYTE *buf, uint32_t length) uint32_t num_written = write(fd, buf, length); - return (num_written == length); + return num_written == length; } -bool SCSIPrinter::CheckReservation(SCSIDEV *controller) +void SCSIPrinter::CheckReservation() { if (reserving_initiator == NOT_RESERVED || reserving_initiator == controller->GetInitiatorId()) { reservation_time = time(0); - - return true; + return; } if (controller->GetInitiatorId() != -1) { @@ -277,10 +254,8 @@ bool SCSIPrinter::CheckReservation(SCSIDEV *controller) LOGTRACE("Unknown initiator tries to access reserved device ID %d, LUN %d", GetId(), GetLun()); } - controller->Error(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION, + throw scsi_error_exception(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION, status::RESERVATION_CONFLICT); - - return false; } void SCSIPrinter::DiscardReservation() diff --git a/src/raspberrypi/devices/scsi_printer.h b/src/raspberrypi/devices/scsi_printer.h index 41f685db..7a113037 100644 --- a/src/raspberrypi/devices/scsi_printer.h +++ b/src/raspberrypi/devices/scsi_printer.h @@ -21,27 +21,28 @@ using namespace std; class SCSIPrinter: public PrimaryDevice, ScsiPrinterCommands { + static const int NOT_RESERVED = -2; public: SCSIPrinter(); ~SCSIPrinter(); - virtual bool Dispatch(SCSIDEV *) override; + virtual bool Dispatch() override; - bool Init(const unordered_map&); + bool Init(const unordered_map&) override; - vector Inquiry() const override; - void TestUnitReady(SCSIDEV *); - void ReserveUnit(SCSIDEV *); - void ReleaseUnit(SCSIDEV *); - void Print(SCSIDEV *); - void SynchronizeBuffer(SCSIDEV *); - void SendDiagnostic(SCSIDEV *); - void StopPrint(SCSIDEV *); + vector InquiryInternal() const override; + void TestUnitReady() override; + void ReserveUnit() override; + void ReleaseUnit() override; + void Print() override; + void SynchronizeBuffer(); + void SendDiagnostic() override; + void StopPrint(); - bool WriteBytes(BYTE *, uint32_t) override; - bool CheckReservation(SCSIDEV *); + bool WriteByteSequence(BYTE *, uint32_t) override; + void CheckReservation(); void DiscardReservation(); void Cleanup(); @@ -49,13 +50,13 @@ private: typedef PrimaryDevice super; - Dispatcher dispatcher; + Dispatcher dispatcher; char filename[sizeof(TMP_FILE_PATTERN) + 1]; - int fd; + int fd = -1; - int reserving_initiator; + int reserving_initiator = NOT_RESERVED; - time_t reservation_time; - int timeout; + time_t reservation_time = 0; + int timeout = 0; }; diff --git a/src/raspberrypi/devices/scsicd.cpp b/src/raspberrypi/devices/scsicd.cpp index 83ab1cdb..262483db 100644 --- a/src/raspberrypi/devices/scsicd.cpp +++ b/src/raspberrypi/devices/scsicd.cpp @@ -16,7 +16,7 @@ #include "scsicd.h" #include "fileio.h" -#include "exceptions.h" +#include "rascsi_exceptions.h" using namespace scsi_defs; @@ -165,7 +165,7 @@ bool CDTrack::IsValid(DWORD lba) const //--------------------------------------------------------------------------- bool CDTrack::IsAudio() const { - ASSERT(valid); + assert(valid); return audio; } @@ -180,41 +180,24 @@ SCSICD::SCSICD(const unordered_set& sector_sizes) : Disk("SCCD"), Scsi { SetSectorSizes(sector_sizes); - // NOT in raw format - rawfile = false; - - // Frame initialization - frame = 0; - - // Track initialization - for (int i = 0; i < TrackMax; i++) { - track[i] = NULL; - } - tracks = 0; - dataindex = -1; - audioindex = -1; - dispatcher.AddCommand(eCmdReadToc, "ReadToc", &SCSICD::ReadToc); dispatcher.AddCommand(eCmdGetEventStatusNotification, "GetEventStatusNotification", &SCSICD::GetEventStatusNotification); } SCSICD::~SCSICD() { - // Clear track ClearTrack(); } -bool SCSICD::Dispatch(SCSIDEV *controller) +bool SCSICD::Dispatch() { // The superclass class handles the less specific commands - return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller); + return dispatcher.Dispatch(this, ctrl->cmd[0]) ? true : super::Dispatch(); } void SCSICD::Open(const Filepath& path) { - off_t size; - - ASSERT(!IsReady()); + assert(!IsReady()); // Initialization, track clear SetBlockCount(0); @@ -228,7 +211,7 @@ void SCSICD::Open(const Filepath& path) } // Default sector size is 2048 bytes - SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 2048, false); + SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 2048); // Close and transfer for physical CD access if (path.GetPath()[0] == _T('\\')) { @@ -239,7 +222,7 @@ void SCSICD::Open(const Filepath& path) OpenPhysical(path); } else { // Get file size - size = fio.GetFileSize(); + off_t size = fio.GetFileSize(); if (size <= 4) { fio.Close(); throw io_exception("CD-ROM file size must be at least 4 bytes"); @@ -268,7 +251,7 @@ void SCSICD::Open(const Filepath& path) FileSupport::SetPath(path); // Set RAW flag - ASSERT(disk.dcache); + assert(disk.dcache); disk.dcache->SetRawMode(rawfile); // Attention if ready @@ -344,7 +327,7 @@ void SCSICD::OpenIso(const Filepath& path) } // Create only one data track - ASSERT(!track[0]); + assert(!track[0]); track[0] = new CDTrack(this); track[0]->Init(1, 0, GetBlockCount() - 1); track[0]->SetPath(false, path); @@ -385,21 +368,16 @@ void SCSICD::OpenPhysical(const Filepath& path) dataindex = 0; } -void SCSICD::ReadToc(SASIDEV *controller) +void SCSICD::ReadToc() { ctrl->length = ReadToc(ctrl->cmd, ctrl->buffer); - if (ctrl->length <= 0) { - // Failure (Error) - controller->Error(); - return; - } - controller->DataIn(); + EnterDataInPhase(); } -vector SCSICD::Inquiry() const +vector SCSICD::InquiryInternal() const { - return PrimaryDevice::Inquiry(device_type::CD_ROM, scsi_level::SCSI_2, true); + return HandleInquiry(device_type::CD_ROM, scsi_level::SCSI_2, true); // // The following code worked with the modified Apple CD-ROM drivers. Need to @@ -469,28 +447,25 @@ void SCSICD::AddCDDAPage(map>& pages, bool) const int SCSICD::Read(const DWORD *cdb, BYTE *buf, uint64_t block) { - ASSERT(buf); + assert(buf); - // Status check - if (!CheckReady()) { - return 0; - } + CheckReady(); // Search for the track int index = SearchTrack(block); - // if invalid, out of range + // If invalid, out of range if (index < 0) { - SetStatusCode(STATUS_INVALIDLBA); - return 0; + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE); } - ASSERT(track[index]); + + assert(track[index]); // If different from the current data track if (dataindex != index) { // Delete current disk cache (no need to save) delete disk.dcache; - disk.dcache = NULL; + disk.dcache = nullptr; // Reset the number of blocks SetBlockCount(track[index]->GetBlocks()); @@ -507,23 +482,20 @@ int SCSICD::Read(const DWORD *cdb, BYTE *buf, uint64_t block) } // Base class - ASSERT(dataindex >= 0); + assert(dataindex >= 0); return super::Read(cdb, buf, block); } int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) { - ASSERT(cdb); - ASSERT(buf); + assert(cdb); + assert(buf); - // Check if ready - if (!CheckReady()) { - return 0; - } + CheckReady(); // If ready, there is at least one track - ASSERT(tracks > 0); - ASSERT(track[0]); + assert(tracks > 0); + assert(track[0]); // Get allocation length, clear buffer int length = cdb[7] << 8; @@ -538,8 +510,7 @@ int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) if ((int)cdb[6] > last) { // Except for AA if (cdb[6] != 0xaa) { - SetStatusCode(STATUS_INVALIDCDB); - return 0; + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } } @@ -574,14 +545,13 @@ int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) } // Otherwise, error - SetStatusCode(STATUS_INVALIDCDB); - return 0; + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } } // Number of track descriptors returned this time (number of loops) int loop = last - track[index]->GetTrackNo() + 1; - ASSERT(loop >= 1); + assert(loop >= 1); // Create header buf[0] = (BYTE)(((loop << 3) + 2) >> 8); @@ -621,16 +591,15 @@ int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) return length; } -void SCSICD::GetEventStatusNotification(SASIDEV *controller) +void SCSICD::GetEventStatusNotification() { if (!(ctrl->cmd[1] & 0x01)) { // Asynchronous notification is optional and not supported by rascsi - controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); - return; + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } LOGTRACE("Received request for event polling, which is currently not supported"); - controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); + throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); } //--------------------------------------------------------------------------- @@ -654,9 +623,9 @@ void SCSICD::LBAtoMSF(DWORD lba, BYTE *msf) const } // Store - ASSERT(m < 0x100); - ASSERT(s < 60); - ASSERT(f < 75); + assert(m < 0x100); + assert(s < 60); + assert(f < 75); msf[0] = 0x00; msf[1] = (BYTE)m; msf[2] = (BYTE)s; @@ -667,10 +636,8 @@ void SCSICD::ClearTrack() { // delete the track object for (int i = 0; i < TrackMax; i++) { - if (track[i]) { - delete track[i]; - track[i] = NULL; - } + delete track[i]; + track[i] = nullptr; } // Number of tracks is 0 @@ -692,7 +659,7 @@ int SCSICD::SearchTrack(DWORD lba) const // Track loop for (int i = 0; i < tracks; i++) { // Listen to the track - ASSERT(track[i]); + assert(track[i]); if (track[i]->IsValid(lba)) { return i; } diff --git a/src/raspberrypi/devices/scsicd.h b/src/raspberrypi/devices/scsicd.h index af213567..196ca37c 100644 --- a/src/raspberrypi/devices/scsicd.h +++ b/src/raspberrypi/devices/scsicd.h @@ -79,12 +79,12 @@ public: SCSICD(const unordered_set&); ~SCSICD(); - bool Dispatch(SCSIDEV *) override; + bool Dispatch() override; void Open(const Filepath& path) override; // Commands - vector Inquiry() const override; + vector InquiryInternal() const override; int Read(const DWORD *cdb, BYTE *buf, uint64_t block) override; int ReadToc(const DWORD *cdb, BYTE *buf); @@ -95,7 +95,7 @@ protected: private: typedef Disk super; - Dispatcher dispatcher; + Dispatcher dispatcher; void AddCDROMPage(map>&, bool) const; void AddCDDAPage(map>&, bool) const; @@ -105,20 +105,18 @@ private: void OpenIso(const Filepath& path); // Open(ISO) void OpenPhysical(const Filepath& path); // Open(Physical) - void ReadToc(SASIDEV *) override; - void GetEventStatusNotification(SASIDEV *) override; + void ReadToc() override; + void GetEventStatusNotification() override; void LBAtoMSF(DWORD lba, BYTE *msf) const; // LBA→MSF conversion - bool rawfile; // RAW flag + bool rawfile = false; // RAW flag // Track management void ClearTrack(); // Clear the track - int SearchTrack(DWORD lba) const; // Track search - CDTrack* track[TrackMax]; // Track opbject references - int tracks; // Effective number of track objects - int dataindex; // Current data track - int audioindex; // Current audio track - - int frame; // Frame number + int SearchTrack(DWORD lba) const; // Track search + CDTrack* track[TrackMax] = {}; // Track opbject references + int tracks = 0; // Effective number of track objects + int dataindex = -1; // Current data track + int audioindex = -1; // Current audio track }; diff --git a/src/raspberrypi/devices/scsihd.cpp b/src/raspberrypi/devices/scsihd.cpp index 60219f04..cbdeee2b 100644 --- a/src/raspberrypi/devices/scsihd.cpp +++ b/src/raspberrypi/devices/scsihd.cpp @@ -15,17 +15,12 @@ //--------------------------------------------------------------------------- #include "scsihd.h" #include "fileio.h" -#include "exceptions.h" +#include "rascsi_exceptions.h" +#include "scsi_command_util.h" #include #define DEFAULT_PRODUCT "SCSI HD" -//=========================================================================== -// -// SCSI Hard Disk -// -//=========================================================================== - SCSIHD::SCSIHD(const unordered_set& sector_sizes, bool removable) : Disk(removable ? "SCRM" : "SCHD") { SetSectorSizes(sector_sizes); @@ -71,7 +66,7 @@ void SCSIHD::Reset() // No reset, clear code SetReset(false); - SetStatusCode(STATUS_NOERROR); + SetStatusCode(0); } void SCSIHD::Open(const Filepath& path) @@ -89,7 +84,7 @@ void SCSIHD::Open(const Filepath& path) fio.Close(); // Sector size (default 512 bytes) and number of blocks - SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512, false); + SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512); SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount())); // Effective size must be a multiple of the sector size @@ -98,82 +93,21 @@ void SCSIHD::Open(const Filepath& path) FinalizeSetup(path, size); } -vector SCSIHD::Inquiry() const +vector SCSIHD::InquiryInternal() const { - return PrimaryDevice::Inquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_2, IsRemovable()); + return HandleInquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_2, IsRemovable()); } -bool SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) +void SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) { - assert(length >= 0); + scsi_command_util::ModeSelect(cdb, buf, length, 1 << GetSectorSizeShiftCount()); +} - int size; +void SCSIHD::AddFormatPage(map>& pages, bool changeable) const +{ + Disk::AddFormatPage(pages, changeable); - // PF - if (cdb[1] & 0x10) { - // Mode Parameter header - if (length >= 12) { - // Check the block length bytes - size = 1 << GetSectorSizeShiftCount(); - if (buf[9] != (BYTE)(size >> 16) || - buf[10] != (BYTE)(size >> 8) || - buf[11] != (BYTE)size) { - // currently does not allow changing sector length - SetStatusCode(STATUS_INVALIDPRM); - return false; - } - buf += 12; - length -= 12; - } - - // Parsing the page - while (length > 0) { - // Get page - BYTE page = buf[0]; - - switch (page) { - // format device - case 0x03: - // check the number of bytes in the physical sector - size = 1 << GetSectorSizeShiftCount(); - if (buf[0xc] != (BYTE)(size >> 8) || - buf[0xd] != (BYTE)size) { - // currently does not allow changing sector length - SetStatusCode(STATUS_INVALIDPRM); - return false; - } - break; - - // CD-ROM Parameters - // TODO Move to scsicd.cpp - // According to the SONY CDU-541 manual, Page code 8 is supposed - // to set the Logical Block Adress Format, as well as the - // inactivity timer multiplier - case 0x08: - // Debug code for Issue #2: - // https://github.com/akuker/RASCSI/issues/2 - LOGWARN("[Unhandled page code] Received mode page code 8 with total length %d\n ", length); - for (int i = 0; i Inquiry() const override; - bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override; + virtual vector InquiryInternal() const override; + void ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override; + void AddFormatPage(map>&, bool) const override; void AddVendorPage(map>&, int, bool) const override; }; diff --git a/src/raspberrypi/devices/scsihd_nec.cpp b/src/raspberrypi/devices/scsihd_nec.cpp index 2a02a75d..23a87092 100644 --- a/src/raspberrypi/devices/scsihd_nec.cpp +++ b/src/raspberrypi/devices/scsihd_nec.cpp @@ -16,14 +16,10 @@ #include "scsihd_nec.h" #include "fileio.h" -#include "exceptions.h" +#include "rascsi_exceptions.h" SCSIHD_NEC::SCSIHD_NEC(const unordered_set& sector_sizes) : SCSIHD(sector_sizes, false) { - // Work initialization - cylinders = 0; - heads = 0; - sectors = 0; } //--------------------------------------------------------------------------- @@ -135,9 +131,9 @@ void SCSIHD_NEC::Open(const Filepath& path) FinalizeSetup(path, size); } -vector SCSIHD_NEC::Inquiry() const +vector SCSIHD_NEC::InquiryInternal() const { - return PrimaryDevice::Inquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_1_CCS, false); + return HandleInquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_1_CCS, false); } void SCSIHD_NEC::AddErrorPage(map>& pages, bool) const diff --git a/src/raspberrypi/devices/scsihd_nec.h b/src/raspberrypi/devices/scsihd_nec.h index c3562e59..3903fa72 100644 --- a/src/raspberrypi/devices/scsihd_nec.h +++ b/src/raspberrypi/devices/scsihd_nec.h @@ -31,7 +31,7 @@ public: void Open(const Filepath& path) override; // Commands - vector Inquiry() const override; + vector InquiryInternal() const override; void AddErrorPage(map>&, bool) const override; void AddFormatPage(map>&, bool) const override; @@ -39,7 +39,7 @@ public: private: // Geometry data - int cylinders; - int heads; - int sectors; + int cylinders = 0; + int heads = 0; + int sectors = 0; }; diff --git a/src/raspberrypi/devices/scsimo.cpp b/src/raspberrypi/devices/scsimo.cpp index 30d58af5..c7fbd73f 100644 --- a/src/raspberrypi/devices/scsimo.cpp +++ b/src/raspberrypi/devices/scsimo.cpp @@ -15,10 +15,12 @@ //--------------------------------------------------------------------------- #include "fileio.h" -#include "exceptions.h" +#include "rascsi_exceptions.h" +#include "scsi_command_util.h" #include "scsimo.h" -SCSIMO::SCSIMO(const unordered_set& sector_sizes, const unordered_map& geometries) : Disk("SCMO") +SCSIMO::SCSIMO(const unordered_set& sector_sizes, const unordered_map& geometries) + : Disk("SCMO"), geometries({}) { SetSectorSizes(sector_sizes); SetGeometries(geometries); @@ -43,7 +45,7 @@ void SCSIMO::Open(const Filepath& path) // TODO Find a more flexible solution if (!SetGeometryForCapacity(size)) { // Sector size (default 512 bytes) and number of blocks - SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512, true); + SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512); SetBlockCount(size >> GetSectorSizeShiftCount()); } @@ -63,9 +65,9 @@ void SCSIMO::Open(const Filepath& path) } } -vector SCSIMO::Inquiry() const +vector SCSIMO::InquiryInternal() const { - return PrimaryDevice::Inquiry(device_type::OPTICAL_MEMORY, scsi_level::SCSI_2, true); + return HandleInquiry(device_type::OPTICAL_MEMORY, scsi_level::SCSI_2, true); } void SCSIMO::SetDeviceParameters(BYTE *buf) @@ -86,6 +88,13 @@ void SCSIMO::AddModePages(map>& pages, int page, bool changeab } } +void SCSIMO::AddFormatPage(map>& pages, bool changeable) const +{ + Disk::AddFormatPage(pages, changeable); + + scsi_command_util::EnrichFormatPage(pages, changeable, 1 << GetSectorSizeShiftCount()); +} + void SCSIMO::AddOptionPage(map>& pages, bool) const { vector buf(4); @@ -94,64 +103,9 @@ void SCSIMO::AddOptionPage(map>& pages, bool) const // Do not report update blocks } -bool SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) +void SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) { - int size; - - ASSERT(length >= 0); - - // PF - if (cdb[1] & 0x10) { - // Mode Parameter header - if (length >= 12) { - // Check the block length (in bytes) - size = 1 << GetSectorSizeShiftCount(); - if (buf[9] != (BYTE)(size >> 16) || - buf[10] != (BYTE)(size >> 8) || buf[11] != (BYTE)size) { - // Currently does not allow changing sector length - SetStatusCode(STATUS_INVALIDPRM); - return false; - } - buf += 12; - length -= 12; - } - - // Parsing the page - while (length > 0) { - // Get the page - int page = buf[0]; - - switch (page) { - // format device - case 0x03: - // Check the number of bytes in the physical sector - size = 1 << GetSectorSizeShiftCount(); - if (buf[0xc] != (BYTE)(size >> 8) || - buf[0xd] != (BYTE)size) { - // Currently does not allow changing sector length - SetStatusCode(STATUS_INVALIDPRM); - return false; - } - break; - // vendor unique format - case 0x20: - // just ignore, for now - break; - - // Other page - default: - break; - } - - // Advance to the next page - size = buf[1] + 2; - length -= size; - buf += size; - } - } - - // Do not generate an error for the time being (MINIX) - return true; + scsi_command_util::ModeSelect(cdb, buf, length, 1 << GetSectorSizeShiftCount()); } //--------------------------------------------------------------------------- @@ -257,3 +211,15 @@ void SCSIMO::AddVendorPage(map>& pages, int page, bool changea return; } + +bool SCSIMO::SetGeometryForCapacity(uint64_t capacity) { + const auto& geometry = geometries.find(capacity); + if (geometry != geometries.end()) { + SetSectorSizeInBytes(geometry->second.first); + SetBlockCount(geometry->second.second); + + return true; + } + + return false; +} diff --git a/src/raspberrypi/devices/scsimo.h b/src/raspberrypi/devices/scsimo.h index 77aae122..6d71fdad 100644 --- a/src/raspberrypi/devices/scsimo.h +++ b/src/raspberrypi/devices/scsimo.h @@ -13,6 +13,7 @@ // [ SCSI Magneto-Optical Disk] // //--------------------------------------------------------------------------- + #pragma once #include "os.h" @@ -22,23 +23,29 @@ class SCSIMO : public Disk, public FileSupport { public: + SCSIMO(const unordered_set&, const unordered_map&); ~SCSIMO() {} void Open(const Filepath& path) override; - // Commands - vector Inquiry() const override; - bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override; + vector InquiryInternal() const override; + void ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override; protected: - // Internal processing void SetDeviceParameters(BYTE *) override; void AddModePages(map>&, int, bool) const override; + void AddFormatPage(map>&, bool) const override; void AddVendorPage(map>&, int, bool) const override; private: void AddOptionPage(map>&, bool) const; + + void SetGeometries(const unordered_map& geometries) { this->geometries = geometries; } + bool SetGeometryForCapacity(uint64_t); + + // The mapping of supported capacities to block sizes and block counts, empty if there is no capacity restriction + unordered_map geometries; }; diff --git a/src/raspberrypi/exceptions.h b/src/raspberrypi/exceptions.h deleted file mode 100644 index 01227bf5..00000000 --- a/src/raspberrypi/exceptions.h +++ /dev/null @@ -1,49 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI Target Emulator RaSCSI Reloaded -// for Raspberry Pi -// -// Copyright (C) 2021 Uwe Seimet -// -// Various exceptions -// -//--------------------------------------------------------------------------- - -#pragma once - -#include -#include - -using namespace std; - -class illegal_argument_exception final : public exception { -private: - string msg; - -public: - illegal_argument_exception(const string& _msg) : msg(_msg) {} - illegal_argument_exception() {}; - - const string& getmsg() const { - return msg; - } -}; - -class io_exception : public exception { -private: - string msg; - -public: - io_exception(const string& _msg) : msg(_msg) {} - virtual ~io_exception() {} - - const string& getmsg() const { - return msg; - } -}; - -class file_not_found_exception : public io_exception { -public: - file_not_found_exception(const string& msg) : io_exception(msg) {} - ~file_not_found_exception() {} -}; diff --git a/src/raspberrypi/fileio.cpp b/src/raspberrypi/fileio.cpp index c6dc70b2..0151e5cf 100644 --- a/src/raspberrypi/fileio.cpp +++ b/src/raspberrypi/fileio.cpp @@ -22,8 +22,6 @@ Fileio::Fileio() { - // Initialize work - handle = -1; } Fileio::~Fileio() diff --git a/src/raspberrypi/fileio.h b/src/raspberrypi/fileio.h index f0815d08..282fcf65 100644 --- a/src/raspberrypi/fileio.h +++ b/src/raspberrypi/fileio.h @@ -63,7 +63,7 @@ public: private: BOOL Open(const char *fname, OpenMode mode, BOOL directIO); - int handle; // File handle + int handle = -1; // File handle }; #endif // fileio_h diff --git a/src/raspberrypi/filepath.cpp b/src/raspberrypi/filepath.cpp index 1088bcd7..605c9812 100644 --- a/src/raspberrypi/filepath.cpp +++ b/src/raspberrypi/filepath.cpp @@ -13,15 +13,8 @@ #include "config.h" #include "fileio.h" -//=========================================================================== -// -// File path -// -//=========================================================================== - Filepath::Filepath() { - // Clear Clear(); } @@ -39,7 +32,6 @@ Filepath& Filepath::operator=(const Filepath& path) void Filepath::Clear() { - // Clear the path and each part m_szPath[0] = _T('\0'); m_szDir[0] = _T('\0'); diff --git a/src/raspberrypi/gpiobus.cpp b/src/raspberrypi/gpiobus.cpp index 0987c3ca..6f57ad2d 100644 --- a/src/raspberrypi/gpiobus.cpp +++ b/src/raspberrypi/gpiobus.cpp @@ -78,26 +78,13 @@ DWORD bcm_host_get_peripheral_address(void) GPIOBUS::GPIOBUS() { - actmode = TARGET; - baseaddr = 0; - gicc = 0; - gicd = 0; - gpio = 0; - level = 0; - pads = 0; - irpctl = 0; - qa7regs = 0; - signals = 0; - rpitype = 0; } -GPIOBUS::~GPIOBUS() -{ -} - -BOOL GPIOBUS::Init(mode_e mode) +bool GPIOBUS::Init(mode_e mode) { #if defined(__x86_64__) || defined(__X86__) + actmode = mode; + // When we're running on x86, there is no hardware to talk to, so just return. return true; #else @@ -120,7 +107,7 @@ BOOL GPIOBUS::Init(mode_e mode) fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd == -1) { LOGERROR("Error: Unable to open /dev/mem. Are you running as root?"); - return FALSE; + return false; } // Map peripheral region memory @@ -128,7 +115,7 @@ BOOL GPIOBUS::Init(mode_e mode) if (map == MAP_FAILED) { LOGERROR("Error: Unable to map memory"); close(fd); - return FALSE; + return false; } // Determine the type of raspberry pi from the base address @@ -168,7 +155,7 @@ BOOL GPIOBUS::Init(mode_e mode) PROT_READ | PROT_WRITE, MAP_SHARED, fd, ARM_GICD_BASE); if (map == MAP_FAILED) { close(fd); - return FALSE; + return false; } gicd = (DWORD *)map; gicc = (DWORD *)map; @@ -226,7 +213,7 @@ BOOL GPIOBUS::Init(mode_e mode) fd = open("/dev/gpiochip0", 0); if (fd == -1) { LOGERROR("Unable to open /dev/gpiochip0. Is RaSCSI already running?") - return FALSE; + return false; } // Event request setting @@ -243,7 +230,7 @@ BOOL GPIOBUS::Init(mode_e mode) if (ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &selevreq) == -1) { LOGERROR("Unable to register event request. Is RaSCSI already running?") close(fd); - return FALSE; + return false; } // Close GPIO chip file handle @@ -313,7 +300,7 @@ BOOL GPIOBUS::Init(mode_e mode) // Show the user that this app is running SetControl(PIN_ENB, ENB_ON); - return TRUE; + return true; #endif // ifdef __x86_64__ || __X86__ } @@ -441,31 +428,16 @@ void GPIOBUS::Reset() #endif // ifdef __x86_64__ || __X86__ } -//--------------------------------------------------------------------------- -// -// ENB signal setting -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetENB(BOOL ast) +void GPIOBUS::SetENB(bool ast) { PinSetSignal(PIN_ENB, ast ? ENB_ON : ENB_OFF); } -//--------------------------------------------------------------------------- -// -// Get BSY signal -// -//--------------------------------------------------------------------------- -bool GPIOBUS::GetBSY() +bool GPIOBUS::GetBSY() const { return GetSignal(PIN_BSY); } -//--------------------------------------------------------------------------- -// -// Set BSY signal -// -//--------------------------------------------------------------------------- void GPIOBUS::SetBSY(bool ast) { // Set BSY signal @@ -500,22 +472,12 @@ void GPIOBUS::SetBSY(bool ast) } } -//--------------------------------------------------------------------------- -// -// Get SEL signal -// -//--------------------------------------------------------------------------- -BOOL GPIOBUS::GetSEL() +bool GPIOBUS::GetSEL() const { return GetSignal(PIN_SEL); } -//--------------------------------------------------------------------------- -// -// Set SEL signal -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetSEL(BOOL ast) +void GPIOBUS::SetSEL(bool ast) { if (actmode == INITIATOR && ast) { // Turn on ACTIVE signal @@ -526,134 +488,69 @@ void GPIOBUS::SetSEL(BOOL ast) SetSignal(PIN_SEL, ast); } -//--------------------------------------------------------------------------- -// -// Get ATN signal -// -//--------------------------------------------------------------------------- -BOOL GPIOBUS::GetATN() +bool GPIOBUS::GetATN() const { return GetSignal(PIN_ATN); } -//--------------------------------------------------------------------------- -// -// Get ATN signal -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetATN(BOOL ast) +void GPIOBUS::SetATN(bool ast) { SetSignal(PIN_ATN, ast); } -//--------------------------------------------------------------------------- -// -// Get ACK signal -// -//--------------------------------------------------------------------------- -BOOL GPIOBUS::GetACK() +bool GPIOBUS::GetACK() const { return GetSignal(PIN_ACK); } -//--------------------------------------------------------------------------- -// -// Set ACK signal -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetACK(BOOL ast) +void GPIOBUS::SetACK(bool ast) { SetSignal(PIN_ACK, ast); } -//--------------------------------------------------------------------------- -// -// Get ACK signal -// -//--------------------------------------------------------------------------- -BOOL GPIOBUS::GetACT() +bool GPIOBUS::GetACT() const { return GetSignal(PIN_ACT); } -//--------------------------------------------------------------------------- -// -// Set ACK signal -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetACT(BOOL ast) +void GPIOBUS::SetACT(bool ast) { SetSignal(PIN_ACT, ast); } -//--------------------------------------------------------------------------- -// -// Get RST signal -// -//--------------------------------------------------------------------------- -BOOL GPIOBUS::GetRST() +bool GPIOBUS::GetRST() const { return GetSignal(PIN_RST); } -//--------------------------------------------------------------------------- -// -// Set RST signal -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetRST(BOOL ast) +void GPIOBUS::SetRST(bool ast) { SetSignal(PIN_RST, ast); } -//--------------------------------------------------------------------------- -// -// Get MSG signal -// -//--------------------------------------------------------------------------- -BOOL GPIOBUS::GetMSG() +bool GPIOBUS::GetMSG() const { return GetSignal(PIN_MSG); } -//--------------------------------------------------------------------------- -// -// Set MSG signal -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetMSG(BOOL ast) +void GPIOBUS::SetMSG(bool ast) { SetSignal(PIN_MSG, ast); } -//--------------------------------------------------------------------------- -// -// Get CD signal -// -//--------------------------------------------------------------------------- -BOOL GPIOBUS::GetCD() +bool GPIOBUS::GetCD() const { return GetSignal(PIN_CD); } -//--------------------------------------------------------------------------- -// -// Set CD Signal -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetCD(BOOL ast) +void GPIOBUS::SetCD(bool ast) { SetSignal(PIN_CD, ast); } -//--------------------------------------------------------------------------- -// -// Get IO Signal -// -//--------------------------------------------------------------------------- -BOOL GPIOBUS::GetIO() +bool GPIOBUS::GetIO() { - BOOL ast = GetSignal(PIN_IO); + bool ast = GetSignal(PIN_IO); if (actmode == INITIATOR) { // Change the data input/output direction by IO signal @@ -685,12 +582,7 @@ BOOL GPIOBUS::GetIO() return ast; } -//--------------------------------------------------------------------------- -// -// Set IO signal -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetIO(BOOL ast) +void GPIOBUS::SetIO(bool ast) { SetSignal(PIN_IO, ast); @@ -723,22 +615,12 @@ void GPIOBUS::SetIO(BOOL ast) } } -//--------------------------------------------------------------------------- -// -// Get REQ signal -// -//--------------------------------------------------------------------------- -BOOL GPIOBUS::GetREQ() +bool GPIOBUS::GetREQ() const { return GetSignal(PIN_REQ); } -//--------------------------------------------------------------------------- -// -// Set REQ signal -// -//--------------------------------------------------------------------------- -void GPIOBUS::SetREQ(BOOL ast) +void GPIOBUS::SetREQ(bool ast) { SetSignal(PIN_REQ, ast); } @@ -750,7 +632,7 @@ void GPIOBUS::SetREQ(BOOL ast) //--------------------------------------------------------------------------- BYTE GPIOBUS::GetDAT() { - DWORD data = Aquire(); + DWORD data = Acquire(); data = ((data >> (PIN_DT0 - 0)) & (1 << 0)) | ((data >> (PIN_DT1 - 1)) & (1 << 1)) | @@ -802,12 +684,7 @@ void GPIOBUS::SetDAT(BYTE dat) #endif // SIGNAL_CONTROL_MODE } -//--------------------------------------------------------------------------- -// -// Get data parity signal -// -//--------------------------------------------------------------------------- -BOOL GPIOBUS::GetDP() +bool GPIOBUS::GetDP() const { return GetSignal(PIN_DP); } @@ -817,26 +694,20 @@ BOOL GPIOBUS::GetDP() // Receive command handshake // //--------------------------------------------------------------------------- -int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi) +int GPIOBUS::CommandHandShake(BYTE *buf) { - int count; - // Only works in TARGET mode if (actmode != TARGET) { return 0; } - // IRQs disabled DisableIRQ(); - // Get the first command byte - int i = 0; - // Assert REQ signal SetSignal(PIN_REQ, ON); // Wait for ACK signal - BOOL ret = WaitSignal(PIN_ACK, TRUE); + bool ret = WaitSignal(PIN_ACK, TRUE); // Wait until the signal line stabilizes SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); @@ -849,7 +720,8 @@ int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi) // Timeout waiting for ACK assertion if (!ret) { - goto irq_enable_exit; + EnableIRQ(); + return 0; } // Wait for ACK to clear @@ -857,7 +729,8 @@ int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi) // Timeout waiting for ACK to clear if (!ret) { - goto irq_enable_exit; + EnableIRQ(); + return 0; } // The ICD AdSCSI ST, AdSCSI Plus ST and AdSCSI Micro ST host adapters allow SCSI devices to be connected @@ -869,7 +742,7 @@ int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi) // semantics. I fact, these semantics have become a standard in the Atari world. // RaSCSI becomes ICD compatible by ignoring the prepended $1F byte before processing the CDB. - if (!is_sasi && *buf == 0x1F) { + if (*buf == 0x1F) { SetSignal(PIN_REQ, ON); ret = WaitSignal(PIN_ACK, TRUE); @@ -882,22 +755,25 @@ int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi) SetSignal(PIN_REQ, OFF); if (!ret) { - goto irq_enable_exit; + EnableIRQ(); + return 0; } WaitSignal(PIN_ACK, FALSE); if (!ret) { - goto irq_enable_exit; + EnableIRQ(); + return 0; } } - count = GetCommandByteCount(*buf); + int command_byte_count = GetCommandByteCount(*buf); // Increment buffer pointer buf++; - for (i = 1; i < count; i++) { + int bytes_received; + for (bytes_received = 1; bytes_received < command_byte_count; bytes_received++) { // Assert REQ signal SetSignal(PIN_REQ, ON); @@ -930,12 +806,9 @@ int GPIOBUS::CommandHandShake(BYTE *buf, bool is_sasi) buf++; } -irq_enable_exit: - // IRQs enabled EnableIRQ(); - // returned the number of bytes received - return i; + return bytes_received; } //--------------------------------------------------------------------------- @@ -946,8 +819,6 @@ irq_enable_exit: int GPIOBUS::ReceiveHandShake(BYTE *buf, int count) { int i; - BOOL ret; - DWORD phase; // Disable IRQs DisableIRQ(); @@ -958,7 +829,7 @@ int GPIOBUS::ReceiveHandShake(BYTE *buf, int count) SetSignal(PIN_REQ, ON); // Wait for ACK - ret = WaitSignal(PIN_ACK, TRUE); + bool ret = WaitSignal(PIN_ACK, TRUE); // Wait until the signal line stabilizes SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); @@ -987,11 +858,11 @@ int GPIOBUS::ReceiveHandShake(BYTE *buf, int count) } } else { // Get phase - phase = Aquire() & GPIO_MCI; + DWORD phase = Acquire() & GPIO_MCI; for (i = 0; i < count; i++) { // Wait for the REQ signal to be asserted - ret = WaitSignal(PIN_REQ, TRUE); + bool ret = WaitSignal(PIN_REQ, TRUE); // Check for timeout waiting for REQ signal if (!ret) { @@ -1063,7 +934,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes) SetDAT(*buf); // Wait for ACK to clear - BOOL ret = WaitSignal(PIN_ACK, FALSE); + bool ret = WaitSignal(PIN_ACK, FALSE); // Check for timeout waiting for ACK to clear if (!ret) { @@ -1094,7 +965,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes) WaitSignal(PIN_ACK, FALSE); } else { // Get Phase - DWORD phase = Aquire() & GPIO_MCI; + DWORD phase = Acquire() & GPIO_MCI; for (i = 0; i < count; i++) { if(i==delay_after_bytes){ @@ -1106,7 +977,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes) SetDAT(*buf); // Wait for REQ to be asserted - BOOL ret = WaitSignal(PIN_REQ, TRUE); + bool ret = WaitSignal(PIN_REQ, TRUE); // Check for timeout waiting for REQ to be asserted if (!ret) { @@ -1157,7 +1028,7 @@ int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes) // SEL signal event polling // //--------------------------------------------------------------------------- -int GPIOBUS::PollSelectEvent() +bool GPIOBUS::PollSelectEvent() { // clear errno errno = 0; @@ -1166,15 +1037,15 @@ int GPIOBUS::PollSelectEvent() if (epoll_wait(epfd, &epev, 1, -1) <= 0) { LOGWARN("%s epoll_wait failed", __PRETTY_FUNCTION__); - return -1; + return false; } if (read(selevreq.fd, &gpev, sizeof(gpev)) < 0) { LOGWARN("%s read failed", __PRETTY_FUNCTION__); - return -1; + return false; } - return 0; + return true; } //--------------------------------------------------------------------------- @@ -1306,7 +1177,7 @@ void GPIOBUS::MakeTable(void) // Control signal setting // //--------------------------------------------------------------------------- -void GPIOBUS::SetControl(int pin, BOOL ast) +void GPIOBUS::SetControl(int pin, bool ast) { PinSetSignal(pin, ast); } @@ -1340,7 +1211,7 @@ void GPIOBUS::SetMode(int pin, int mode) // Get input signal value // //--------------------------------------------------------------------------- -BOOL GPIOBUS::GetSignal(int pin) +bool GPIOBUS::GetSignal(int pin) const { return (signals >> pin) & 1; } @@ -1350,7 +1221,7 @@ BOOL GPIOBUS::GetSignal(int pin) // Set output signal value // //--------------------------------------------------------------------------- -void GPIOBUS::SetSignal(int pin, BOOL ast) +void GPIOBUS::SetSignal(int pin, bool ast) { #if SIGNAL_CONTROL_MODE == 0 int index = pin / 10; @@ -1383,7 +1254,7 @@ void GPIOBUS::SetSignal(int pin, BOOL ast) // Wait for signal change // //--------------------------------------------------------------------------- -BOOL GPIOBUS::WaitSignal(int pin, BOOL ast) +bool GPIOBUS::WaitSignal(int pin, BOOL ast) { // Get current time DWORD now = SysTimer::GetTimerLow(); @@ -1394,26 +1265,21 @@ BOOL GPIOBUS::WaitSignal(int pin, BOOL ast) // end immediately if the signal has changed do { // Immediately upon receiving a reset - Aquire(); + Acquire(); if (GetRST()) { - return FALSE; + return false; } // Check for the signal edge if (((signals >> pin) ^ ~ast) & 1) { - return TRUE; + return true; } } while ((SysTimer::GetTimerLow() - now) < timeout); // We timed out waiting for the signal - return FALSE; + return false; } -//--------------------------------------------------------------------------- -// -// Disable IRQ -// -//--------------------------------------------------------------------------- void GPIOBUS::DisableIRQ() { #ifdef __linux__ @@ -1436,11 +1302,6 @@ void GPIOBUS::DisableIRQ() #endif } -//--------------------------------------------------------------------------- -// -// Enable IRQ -// -//--------------------------------------------------------------------------- void GPIOBUS::EnableIRQ() { if (rpitype == 4) { @@ -1523,7 +1384,7 @@ void GPIOBUS::PullConfig(int pin, int mode) // Set output pin // //--------------------------------------------------------------------------- -void GPIOBUS::PinSetSignal(int pin, BOOL ast) +void GPIOBUS::PinSetSignal(int pin, bool ast) { // Check for invalid pin if (pin < 0) { @@ -1580,9 +1441,7 @@ BUS::phase_t GPIOBUS::GetPhaseRaw(DWORD raw_data) //--------------------------------------------------------------------------- // -// Get the number of bytes for a command -// -// TODO The command length should be determined based on the bytes transferred in the COMMAND phase +// Get the number of bytes for a command // //--------------------------------------------------------------------------- int GPIOBUS::GetCommandByteCount(BYTE opcode) { diff --git a/src/raspberrypi/gpiobus.h b/src/raspberrypi/gpiobus.h index 14b0cf11..9ddd1a17 100644 --- a/src/raspberrypi/gpiobus.h +++ b/src/raspberrypi/gpiobus.h @@ -473,14 +473,13 @@ class GPIOBUS : public BUS public: // Basic Functions GPIOBUS(); - // Constructor - virtual ~GPIOBUS(); + virtual ~GPIOBUS() {} // Destructor - BOOL Init(mode_e mode = TARGET); + bool Init(mode_e mode = TARGET) override; // Initialization - void Reset(); + void Reset() override; // Reset - void Cleanup(); + void Cleanup() override; // Cleanup //--------------------------------------------------------------------------- @@ -488,7 +487,7 @@ public: // Bus signal acquisition // //--------------------------------------------------------------------------- - inline DWORD Aquire() override + inline DWORD Acquire() override { #if defined(__x86_64__) || defined(__X86__) // Only used for development/debugging purposes. Isn't really applicable @@ -506,66 +505,66 @@ public: #endif // ifdef __x86_64__ || __X86__ } - void SetENB(BOOL ast); + void SetENB(bool ast); // Set ENB signal - bool GetBSY() override; + bool GetBSY() const override; // Get BSY signal void SetBSY(bool ast) override; // Set BSY signal - BOOL GetSEL() override; + bool GetSEL() const override; // Get SEL signal - void SetSEL(BOOL ast) override; + void SetSEL(bool ast) override; // Set SEL signal - BOOL GetATN() override; + bool GetATN() const override; // Get ATN signal - void SetATN(BOOL ast) override; + void SetATN(bool ast) override; // Set ATN signal - BOOL GetACK() override; + bool GetACK() const override; // Get ACK signal - void SetACK(BOOL ast) override; + void SetACK(bool ast) override; // Set ACK signal - BOOL GetACT(); + bool GetACT() const; // Get ACT signal - void SetACT(BOOL ast); + void SetACT(bool ast); // Set ACT signal - BOOL GetRST() override; + bool GetRST() const override; // Get RST signal - void SetRST(BOOL ast) override; + void SetRST(bool ast) override; // Set RST signal - BOOL GetMSG() override; + bool GetMSG() const override; // Get MSG signal - void SetMSG(BOOL ast) override; + void SetMSG(bool ast) override; // Set MSG signal - BOOL GetCD() override; + bool GetCD() const override; // Get CD signal - void SetCD(BOOL ast) override; + void SetCD(bool ast) override; // Set CD signal - BOOL GetIO() override; + bool GetIO() override; // Get IO signal - void SetIO(BOOL ast) override; + void SetIO(bool ast) override; // Set IO signal - BOOL GetREQ() override; + bool GetREQ() const override; // Get REQ signal - void SetREQ(BOOL ast) override; + void SetREQ(bool ast) override; // Set REQ signal BYTE GetDAT() override; // Get DAT signal void SetDAT(BYTE dat) override; // Set DAT signal - BOOL GetDP() override; + bool GetDP() const override; // Get Data parity signal - int CommandHandShake(BYTE *buf, bool) override; + int CommandHandShake(BYTE *buf) override; // Command receive handshake int ReceiveHandShake(BYTE *buf, int count) override; // Data receive handshake @@ -579,7 +578,7 @@ public: #ifdef USE_SEL_EVENT_ENABLE // SEL signal interrupt - int PollSelectEvent(); + bool PollSelectEvent(); // SEL signal event polling void ClearSelectEvent(); // Clear SEL signal event @@ -589,15 +588,15 @@ private: // SCSI I/O signal control void MakeTable(); // Create work data - void SetControl(int pin, BOOL ast); + void SetControl(int pin, bool ast); // Set Control Signal void SetMode(int pin, int mode); // Set SCSI I/O mode - BOOL GetSignal(int pin); + bool GetSignal(int pin) const override; // Get SCSI input signal value - void SetSignal(int pin, BOOL ast); + void SetSignal(int pin, bool ast) override; // Set SCSI output signal value - BOOL WaitSignal(int pin, BOOL ast); + bool WaitSignal(int pin, BOOL ast); // Wait for a signal to change // Interrupt control void DisableIRQ(); @@ -610,43 +609,43 @@ private: // GPIO pin direction setting void PullConfig(int pin, int mode); // GPIO pin pull up/down resistor setting - void PinSetSignal(int pin, BOOL ast); + void PinSetSignal(int pin, bool ast); // Set GPIO output signal void DrvConfig(DWORD drive); // Set GPIO drive strength - mode_e actmode; // Operation mode + mode_e actmode = TARGET; // Operation mode - DWORD baseaddr; // Base address + uint32_t baseaddr = 0; // Base address - int rpitype; // Type of Raspberry Pi + int rpitype = 0; // Type of Raspberry Pi - volatile DWORD *gpio; // GPIO register + volatile uint32_t *gpio = 0; // GPIO register - volatile DWORD *pads; // PADS register + volatile uint32_t *pads = 0; // PADS register - volatile DWORD *level; // GPIO input level + volatile uint32_t *level = 0; // GPIO input level - volatile DWORD *irpctl; // Interrupt control register + volatile uint32_t *irpctl = 0; // Interrupt control register - volatile DWORD irptenb; // Interrupt enabled state + volatile uint32_t irptenb; // Interrupt enabled state - volatile DWORD *qa7regs; // QA7 register + volatile uint32_t *qa7regs = 0; // QA7 register volatile int tintcore; // Interupt control target CPU. - volatile DWORD tintctl; // Interupt control + volatile uint32_t tintctl; // Interupt control - volatile DWORD giccpmr; // GICC priority setting + volatile uint32_t giccpmr; // GICC priority setting - volatile DWORD *gicd; // GIC Interrupt distributor register + volatile uint32_t *gicd = 0; // GIC Interrupt distributor register - volatile DWORD *gicc; // GIC CPU interface register + volatile uint32_t *gicc = 0; // GIC CPU interface register DWORD gpfsel[4]; // GPFSEL0-4 backup values - DWORD signals; // All bus signals + uint32_t signals = 0; // All bus signals #ifdef USE_SEL_EVENT_ENABLE struct gpioevent_request selevreq = {}; // SEL signal event request diff --git a/src/raspberrypi/localizer.cpp b/src/raspberrypi/localizer.cpp index ce37b615..94095792 100644 --- a/src/raspberrypi/localizer.cpp +++ b/src/raspberrypi/localizer.cpp @@ -17,9 +17,6 @@ using namespace std; Localizer::Localizer() { - // Supported locales, always lower case - supported_languages = { "en", "de", "sv", "fr", "es" }; - // Positional string arguments are %1, %2, %3 Add(ERROR_AUTHENTICATION, "en", "Authentication failed"); Add(ERROR_AUTHENTICATION, "de", "Authentifizierung fehlgeschlagen"); @@ -81,11 +78,6 @@ Localizer::Localizer() Add(ERROR_DUPLICATE_ID, "sv", "Duplikat ID %1, LUN %2"); Add(ERROR_DUPLICATE_ID, "fr", "ID %1, unité %2 dupliquée"); Add(ERROR_DUPLICATE_ID, "es", "ID duplicado %1, unidad %2"); - Add(ERROR_SASI_SCSI, "en", "SASI and SCSI can't be used at the same time"); - Add(ERROR_SASI_SCSI, "de", "SASI und SCSI können nicht gleichzeitig verwendet werden"); - Add(ERROR_SASI_SCSI, "sv", "SASI och SCSI kan ej användas samtidigt"); - Add(ERROR_SASI_SCSI, "fr", "SASI et SCSI ne peuvent être utilisés en même temps"); - Add(ERROR_SASI_SCSI, "es", "SASI y SCSI no pueden utilizarse al mismo tiempo"); Add(ERROR_EJECT_REQUIRED, "en", "Existing medium must first be ejected"); Add(ERROR_EJECT_REQUIRED, "de", "Das vorhandene Medium muss erst ausgeworfen werden"); Add(ERROR_EJECT_REQUIRED, "sv", "Nuvarande skiva måste utmatas först"); diff --git a/src/raspberrypi/localizer.h b/src/raspberrypi/localizer.h index 712ba5cf..31aed139 100644 --- a/src/raspberrypi/localizer.h +++ b/src/raspberrypi/localizer.h @@ -30,7 +30,6 @@ enum LocalizationKey { ERROR_UNKNOWN_DEVICE_TYPE, ERROR_MISSING_DEVICE_TYPE, ERROR_DUPLICATE_ID, - ERROR_SASI_SCSI, ERROR_EJECT_REQUIRED, ERROR_DEVICE_NAME_UPDATE, ERROR_SHUTDOWN_MODE_MISSING, @@ -55,5 +54,6 @@ private: void Add(LocalizationKey, const string&, const string&); unordered_map> localized_messages; - unordered_set supported_languages; + // Supported locales, always lower case + unordered_set supported_languages = { "en", "de", "sv", "fr", "es" }; }; diff --git a/src/raspberrypi/os.h b/src/raspberrypi/os.h index e741db95..daffeaa8 100644 --- a/src/raspberrypi/os.h +++ b/src/raspberrypi/os.h @@ -75,14 +75,6 @@ #endif // NDEBUG #endif // ASSERT -#if !defined(ASSERT_DIAG) -#if !defined(NDEBUG) -#define ASSERT_DIAG() AssertDiag() -#else -#define ASSERT_DIAG() ((void)0) -#endif // NDEBUG -#endif // ASSERT_DIAG - #define ARRAY_SIZE(x) (sizeof(x)/(sizeof(x[0]))) //--------------------------------------------------------------------------- diff --git a/src/raspberrypi/protobuf_util.cpp b/src/raspberrypi/protobuf_util.cpp index 675da508..3275b5f1 100644 --- a/src/raspberrypi/protobuf_util.cpp +++ b/src/raspberrypi/protobuf_util.cpp @@ -3,7 +3,7 @@ // SCSI Target Emulator RaSCSI Reloaded // for Raspberry Pi // -// Copyright (C) 2021 Uwe Seimet +// Copyright (C) 2021-2022 Uwe Seimet // //--------------------------------------------------------------------------- @@ -12,7 +12,7 @@ #include "log.h" #include "rascsi_interface.pb.h" #include "localizer.h" -#include "exceptions.h" +#include "rascsi_exceptions.h" #include "protobuf_util.h" using namespace std; @@ -115,17 +115,17 @@ void protobuf_util::DeserializeMessage(int fd, google::protobuf::Message& messag { // Read the header with the size of the protobuf data uint8_t header_buf[4]; - int bytes_read = ReadNBytes(fd, header_buf, sizeof(header_buf)); - if (bytes_read < (int)sizeof(header_buf)) { + size_t bytes_read = ReadNBytes(fd, header_buf, sizeof(header_buf)); + if (bytes_read < sizeof(header_buf)) { return; } - int32_t size = (header_buf[3] << 24) + (header_buf[2] << 16) + (header_buf[1] << 8) + header_buf[0]; + size_t size = (header_buf[3] << 24) + (header_buf[2] << 16) + (header_buf[1] << 8) + header_buf[0]; if (size <= 0) { throw io_exception("Broken protobuf message header"); } // Read the binary protobuf data - uint8_t data_buf[size]; + uint8_t *data_buf = new uint8_t[size]; bytes_read = ReadNBytes(fd, data_buf, size); if (bytes_read < size) { throw io_exception("Missing protobuf message data"); @@ -133,16 +133,17 @@ void protobuf_util::DeserializeMessage(int fd, google::protobuf::Message& messag // Create protobuf message string data((const char *)data_buf, size); + delete[] data_buf; message.ParseFromString(data); } -int protobuf_util::ReadNBytes(int fd, uint8_t *buf, int n) +size_t protobuf_util::ReadNBytes(int fd, uint8_t *buf, size_t n) { - int offset = 0; + size_t offset = 0; while (offset < n) { ssize_t len = read(fd, buf + offset, n - offset); - if (!len) { - break; + if (len <= 0) { + return len; } offset += len; diff --git a/src/raspberrypi/protobuf_util.h b/src/raspberrypi/protobuf_util.h index e4b615e6..70558c0e 100644 --- a/src/raspberrypi/protobuf_util.h +++ b/src/raspberrypi/protobuf_util.h @@ -3,7 +3,7 @@ // SCSI Target Emulator RaSCSI Reloaded // for Raspberry Pi // -// Copyright (C) 2021 Uwe Seimet +// Copyright (C) 2021-2022 Uwe Seimet // // Helper methods for serializing/deserializing protobuf messages // @@ -31,7 +31,7 @@ namespace protobuf_util void AddParam(PbDeviceDefinition&, const string&, const string&); void SerializeMessage(int, const google::protobuf::Message&); void DeserializeMessage(int, google::protobuf::Message&); - int ReadNBytes(int, uint8_t *, int); + size_t ReadNBytes(int, uint8_t *, size_t); bool ReturnLocalizedError(const CommandContext&, const LocalizationKey, const string& = "", const string& = "", const string& = ""); bool ReturnLocalizedError(const CommandContext&, const LocalizationKey, const PbErrorCode, const string& = "", diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 5de182ae..2607f1f7 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -5,19 +5,20 @@ // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS -// Copyright (C) 2020-2021 Contributors to the RaSCSI project +// Copyright (C) 2020-2022 Contributors to the RaSCSI project // [ RaSCSI main ] // //--------------------------------------------------------------------------- +#include "config.h" #include "os.h" -#include "controllers/sasidev_ctrl.h" +#include "controllers/controller_manager.h" #include "devices/device_factory.h" #include "devices/device.h" #include "devices/disk.h" #include "devices/file_support.h" #include "gpiobus.h" -#include "exceptions.h" +#include "rascsi_exceptions.h" #include "protobuf_util.h" #include "rascsi_version.h" #include "rascsi_response.h" @@ -31,9 +32,7 @@ #include #include #include -#include #include -#include "config.h" using namespace std; using namespace spdlog; @@ -46,10 +45,8 @@ using namespace protobuf_util; // Constant declarations // //--------------------------------------------------------------------------- -#define CtrlMax 8 // Maximum number of SCSI controllers -#define UnitNum SASIDEV::UnitMax // Number of units around controller #define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ ) - +#define DEFAULT_PORT 6868 #define COMPONENT_SEPARATOR ':' //--------------------------------------------------------------------------- @@ -59,8 +56,6 @@ using namespace protobuf_util; //--------------------------------------------------------------------------- static volatile bool running; // Running flag static volatile bool active; // Processing flag -vector controllers(CtrlMax); // Controllers -vector devices(CtrlMax * UnitNum); // Disks GPIOBUS *bus; // GPIO Bus int monsocket; // Monitor Socket pthread_t monthread; // Monitor Thread @@ -70,15 +65,17 @@ string current_log_level; // Some versions of spdlog do not support get_log_le string access_token; unordered_set reserved_ids; DeviceFactory& device_factory = DeviceFactory::instance(); +ControllerManager&controller_manager = ControllerManager::instance(); RascsiImage rascsi_image; RascsiResponse rascsi_response(&device_factory, &rascsi_image); +void DetachAll(); //--------------------------------------------------------------------------- // // Signal Processing // //--------------------------------------------------------------------------- -void KillHandler(int sig) +void KillHandler(int) { // Stop instruction running = false; @@ -99,21 +96,18 @@ void Banner(int argc, char* argv[]) FPRT(stdout,"Powered by XM6 TypeG Technology / "); FPRT(stdout,"Copyright (C) 2016-2020 GIMONS\n"); FPRT(stdout,"Copyright (C) 2020-2022 Contributors to the RaSCSI Reloaded project\n"); - FPRT(stdout,"Connect type : %s\n", CONNECT_DESC); + FPRT(stdout,"Connect type: %s\n", CONNECT_DESC); if ((argc > 1 && strcmp(argv[1], "-h") == 0) || (argc > 1 && strcmp(argv[1], "--help") == 0)){ FPRT(stdout,"\n"); - FPRT(stdout,"Usage: %s [-IDn FILE] ...\n\n", argv[0]); - FPRT(stdout," n is SCSI identification number(0-7).\n"); - FPRT(stdout," FILE is disk image file.\n\n"); - FPRT(stdout,"Usage: %s [-HDn FILE] ...\n\n", argv[0]); - FPRT(stdout," n is X68000 SASI HD number(0-15).\n"); - FPRT(stdout," FILE is disk image file, \"daynaport\", \"bridge\", \"printer\" or \"services\".\n\n"); - FPRT(stdout," Image type is detected based on file extension.\n"); - FPRT(stdout," hdf : SASI HD image (XM6 SASI HD image)\n"); - FPRT(stdout," hds : SCSI HD image (Non-removable generic SCSI HD image)\n"); - FPRT(stdout," hdr : SCSI HD image (Removable generic SCSI HD image)\n"); + FPRT(stdout,"Usage: %s [-idn[:m] FILE] ...\n\n", argv[0]); + FPRT(stdout," n is SCSI device ID (0-7).\n"); + FPRT(stdout," m is the optional logical unit (LUN) (0-31).\n"); + FPRT(stdout," FILE is a disk image file, \"daynaport\", \"bridge\", \"printer\" or \"services\".\n\n"); + FPRT(stdout," Image type is detected based on file extension if no explicit type is specified.\n"); + FPRT(stdout," hds : SCSI HD image (Non-removable generic HD image)\n"); + FPRT(stdout," hdr : SCSI HD image (Removable generic HD image)\n"); FPRT(stdout," hdn : SCSI HD image (NEC GENUINE)\n"); FPRT(stdout," hdi : SCSI HD image (Anex86 HD image)\n"); FPRT(stdout," nhd : SCSI HD image (T98Next HD image)\n"); @@ -132,7 +126,7 @@ void Banner(int argc, char* argv[]) bool InitService(int port) { - int result = pthread_mutex_init(&ctrl_mutex,NULL); + int result = pthread_mutex_init(&ctrl_mutex, NULL); if (result != EXIT_SUCCESS){ LOGERROR("Unable to create a mutex. Error code: %d", result); return false; @@ -143,7 +137,7 @@ bool InitService(int port) monsocket = socket(PF_INET, SOCK_STREAM, 0); memset(&server, 0, sizeof(server)); server.sin_family = PF_INET; - server.sin_port = htons(port); + server.sin_port = htons(port); server.sin_addr.s_addr = htonl(INADDR_ANY); // allow address reuse @@ -157,7 +151,7 @@ bool InitService(int port) // Bind if (bind(monsocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) { - FPRT(stderr, "Error: Already running?\n"); + FPRT(stderr, "Error: Port %d is in use, is rascsi already running?\n", port); return false; } @@ -188,6 +182,7 @@ bool InitBus() // GPIO Initialization if (!bus->Init()) { + delete bus; return false; } @@ -197,34 +192,13 @@ bool InitBus() return true; } -//--------------------------------------------------------------------------- -// -// Cleanup -// -//--------------------------------------------------------------------------- void Cleanup() { - // Delete the disks - for (auto it = devices.begin(); it != devices.end(); ++it) { - if (*it) { - delete *it; - *it = NULL; - } - } + DetachAll(); - // Delete the Controllers - for (auto it = controllers.begin(); it != controllers.end(); ++it) { - if (*it) { - delete *it; - *it = NULL; - } - } - - // Cleanup the Bus + // Clean up and discard the bus if (bus) { bus->Cleanup(); - - // Discard the GPIOBUS object delete bus; } @@ -236,144 +210,13 @@ void Cleanup() pthread_mutex_destroy(&ctrl_mutex); } -//--------------------------------------------------------------------------- -// -// Reset -// -//--------------------------------------------------------------------------- void Reset() { - // Reset all of the controllers - for (const auto& controller : controllers) { - if (controller) { - controller->Reset(); - } - } + controller_manager.ResetAllControllers(); - // Reset the bus bus->Reset(); } -//--------------------------------------------------------------------------- -// -// Controller Mapping -// -//--------------------------------------------------------------------------- -bool MapController(Device **map) -{ - assert(bus); - - bool status = true; - - // Take ownership of the ctrl data structure - pthread_mutex_lock(&ctrl_mutex); - - // Replace the changed unit - for (size_t i = 0; i < controllers.size(); i++) { - for (int j = 0; j < UnitNum; j++) { - int unitno = i * UnitNum + j; - if (devices[unitno] != map[unitno]) { - // Check if the original unit exists - if (devices[unitno]) { - // Disconnect it from the controller - if (controllers[i]) { - controllers[i]->SetUnit(j, NULL); - } - - // Free the Unit - delete devices[unitno]; - } - - // Setup a new unit - devices[unitno] = map[unitno]; - } - } - } - - // Reconfigure all of the controllers - int i = 0; - for (auto it = controllers.begin(); it != controllers.end(); ++i, ++it) { - // Examine the unit configuration - int sasi_num = 0; - int scsi_num = 0; - for (int j = 0; j < UnitNum; j++) { - int unitno = i * UnitNum + j; - // branch by unit type - if (devices[unitno]) { - if (devices[unitno]->IsSASIHD()) { - // Drive is SASI, so increment SASI count - sasi_num++; - } else { - // Drive is SCSI, so increment SCSI count - scsi_num++; - } - } - - // Remove the unit - if (*it) { - (*it)->SetUnit(j, NULL); - } - } - - // If there are no units connected - if (!sasi_num && !scsi_num) { - if (*it) { - delete *it; - *it = NULL; - continue; - } - } - - // Mixture of SCSI and SASI - if (sasi_num > 0 && scsi_num > 0) { - status = false; - continue; - } - - if (sasi_num > 0) { - // Only SASI Unit(s) - - // Release the controller if it is not SASI - if (*it && !(*it)->IsSASI()) { - delete *it; - *it = NULL; - } - - // Create a new SASI controller - if (!*it) { - *it = new SASIDEV(); - (*it)->Connect(i, bus); - } - } else { - // Only SCSI Unit(s) - - // Release the controller if it is not SCSI - if (*it && !(*it)->IsSCSI()) { - delete *it; - *it = NULL; - } - - // Create a new SCSI controller - if (!*it) { - *it = new SCSIDEV(); - (*it)->Connect(i, bus); - } - } - - // connect all units - for (int j = 0; j < UnitNum; j++) { - int unitno = i * UnitNum + j; - if (devices[unitno]) { - // Add the unit connection - (*it)->SetUnit(j, (static_cast(devices[unitno]))); - } - } - } - pthread_mutex_unlock(&ctrl_mutex); - - return status; -} - bool ReadAccessToken(const char *filename) { struct stat st; @@ -411,21 +254,19 @@ bool ReadAccessToken(const char *filename) return true; } -string ValidateLunSetup(const PbCommand& command, const vector& existing_devices) +string ValidateLunSetup(const PbCommand& command) { // Mapping of available LUNs (bit vector) to devices map luns; - // Collect LUN vectors of new devices + // Collect LUN bit vectors of new devices for (const auto& device : command.devices()) { luns[device.id()] |= 1 << device.unit(); } - // Collect LUN vectors of existing devices - for (auto const& device : existing_devices) { - if (device) { - luns[device->GetId()] |= 1 << device->GetLun(); - } + // Collect LUN bit vectors of existing devices + for (const Device *device : device_factory.GetAllDevices()) { + luns[device->GetId()] |= 1 << device->GetLun(); } // LUN 0 must exist for all devices @@ -500,7 +341,7 @@ string SetReservedIds(const string& ids) return "Invalid ID " + id_to_reserve; } - if (devices[id * UnitNum]) { + if (controller_manager.FindController(id) != nullptr) { return "ID " + id_to_reserve + " is currently in use"; } @@ -523,7 +364,7 @@ string SetReservedIds(const string& ids) LOGINFO("Reserved ID(s) set to %s", s.c_str()); } else { - LOGINFO("Cleared reserved IDs"); + LOGINFO("Cleared reserved ID(s)"); } return ""; @@ -531,33 +372,30 @@ string SetReservedIds(const string& ids) void DetachAll() { - Device *map[devices.size()]; - for (size_t i = 0; i < devices.size(); i++) { - map[i] = NULL; - } + controller_manager.DeleteAllControllersAndDevices(); - if (MapController(map)) { - LOGINFO("Detached all devices"); - } - - FileSupport::UnreserveAll(); + LOGINFO("Detached all devices"); } -bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device, Device *map[], bool dryRun) +bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device, bool dryRun) { const int id = pb_device.id(); const int unit = pb_device.unit(); const PbDeviceType type = pb_device.type(); - if (map[id * UnitNum + unit]) { + if (controller_manager.GetDeviceByIdAndLun(id, unit) != nullptr) { return ReturnLocalizedError(context, ERROR_DUPLICATE_ID, to_string(id), to_string(unit)); } + if (unit >= AbstractController::LUN_MAX) { + return ReturnStatus(context, false, "Invalid unit " + to_string(unit) + " (0-" + to_string(AbstractController::LUN_MAX) + + ")"); + } + string filename = GetParam(pb_device, "file"); - // Create a new device, based on the provided type or filename - Device *device = device_factory.CreateDevice(type, filename); - if (!device) { + Device *device = device_factory.CreateDevice(type, filename, id); + if (device == nullptr) { if (type == UNDEFINED) { return ReturnLocalizedError(context, ERROR_MISSING_DEVICE_TYPE, filename); } @@ -566,30 +404,15 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device, } } - int supported_luns = device->GetSupportedLuns(); - if (unit >= supported_luns) { - delete device; - - string error = "Invalid unit " + to_string(unit) + " for device type " + PbDeviceType_Name(type); - if (supported_luns == 1) { - error += " (0)"; - } - else { - error += " (0-" + to_string(supported_luns -1) + ")"; - } - return ReturnStatus(context, false, error); - } - // If no filename was provided the medium is considered removed FileSupport *file_support = dynamic_cast(device); - if (file_support) { + if (file_support != nullptr) { device->SetRemoved(filename.empty()); } else { device->SetRemoved(false); } - device->SetId(id); device->SetLun(unit); try { @@ -604,28 +427,28 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device, } } catch(const illegal_argument_exception& e) { - return ReturnStatus(context, false, e.getmsg()); + return ReturnStatus(context, false, e.get_msg()); } if (pb_device.block_size()) { Disk *disk = dynamic_cast(device); - if (disk && disk->IsSectorSizeConfigurable()) { + if (disk != nullptr && disk->IsSectorSizeConfigurable()) { if (!disk->SetConfiguredSectorSize(pb_device.block_size())) { - delete device; + device_factory.DeleteDevice(device); return ReturnLocalizedError(context, ERROR_BLOCK_SIZE, to_string(pb_device.block_size())); } } else { - delete device; + device_factory.DeleteDevice(device); return ReturnLocalizedError(context, ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, PbDeviceType_Name(type)); } } // File check (type is HD, for removable media drives, CD and MO the medium (=file) may be inserted later - if (file_support && !device->IsRemovable() && filename.empty()) { - delete device; + if (file_support != nullptr && !device->IsRemovable() && filename.empty()) { + device_factory.DeleteDevice(device); return ReturnStatus(context, false, "Device type " + PbDeviceType_Name(type) + " requires a filename"); } @@ -638,7 +461,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device, int id; int unit; if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) { - delete device; + device_factory.DeleteDevice(device); return ReturnLocalizedError(context, ERROR_IMAGE_IN_USE, filename, to_string(id), to_string(unit)); } @@ -652,7 +475,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device, filepath.SetPath(string(rascsi_image.GetDefaultImageFolder() + "/" + filename).c_str()); if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) { - delete device; + device_factory.DeleteDevice(device); return ReturnLocalizedError(context, ERROR_IMAGE_IN_USE, filename, to_string(id), to_string(unit)); } @@ -661,9 +484,9 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device, } } catch(const io_exception& e) { - delete device; + device_factory.DeleteDevice(device); - return ReturnLocalizedError(context, ERROR_FILE_OPEN, initial_filename, e.getmsg()); + return ReturnLocalizedError(context, ERROR_FILE_OPEN, initial_filename, e.get_msg()); } file_support->ReserveFile(filepath, device->GetId(), device->GetLun()); @@ -677,7 +500,7 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device, // Stop the dry run here, before permanently modifying something if (dryRun) { - delete device; + device_factory.DeleteDevice(device); return true; } @@ -687,56 +510,68 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device, params.erase("file"); } if (!device->Init(params)) { - delete device; + device_factory.DeleteDevice(device); return ReturnStatus(context, false, "Initialization of " + PbDeviceType_Name(type) + " device, ID " +to_string(id) + ", unit " +to_string(unit) + " failed"); } // Replace with the newly created unit - map[id * UnitNum + unit] = device; + pthread_mutex_lock(&ctrl_mutex); + PrimaryDevice *primary_device = static_cast(device); + if (!controller_manager.CreateScsiController(bus, primary_device)) { + pthread_mutex_unlock(&ctrl_mutex); - // Re-map the controller - if (MapController(map)) { - string msg = "Attached "; - if (device->IsReadOnly()) { - msg += "read-only "; - } - else if (device->IsProtectable() && device->IsProtected()) { - msg += "protected "; - } - msg += device->GetType() + " device, ID " + to_string(id) + ", unit " + to_string(unit); - LOGINFO("%s", msg.c_str()); - - return true; + return ReturnStatus(context, false, "Couldn't create SCSI controller instance"); } + pthread_mutex_unlock(&ctrl_mutex); - return ReturnLocalizedError(context, ERROR_SASI_SCSI); + string msg = "Attached "; + if (device->IsReadOnly()) { + msg += "read-only "; + } + else if (device->IsProtectable() && device->IsProtected()) { + msg += "protected "; + } + msg += device->GetType() + " device, ID " + to_string(id) + ", unit " + to_string(unit); + LOGINFO("%s", msg.c_str()); + + return true; } -bool Detach(const CommandContext& context, Device *device, Device *map[], bool dryRun) +bool Detach(const CommandContext& context, PrimaryDevice *device, bool dryRun) { if (!device->GetLun()) { - for (auto const& d : devices) { - // LUN 0 can only be detached if there is no other lUN anymore - if (d && d->GetId() == device->GetId() && d->GetLun()) { + for (const Device *d : device_factory.GetAllDevices()) { + // LUN 0 can only be detached if there is no other LUN anymore + if (d->GetId() == device->GetId() && d->GetLun()) { return ReturnStatus(context, false, "LUN 0 cannot be detached as long as there is still another LUN"); } } } if (!dryRun) { - map[device->GetId() * UnitNum + device->GetLun()] = NULL; + // Remember data that going to be deleted but are used for logging + int id = device->GetId(); + int lun = device->GetLun(); + string type = device->GetType(); FileSupport *file_support = dynamic_cast(device); - if (file_support) { + if (file_support != nullptr) { file_support->UnreserveFile(); } - LOGINFO("Detached %s device with ID %d, unit %d", device->GetType().c_str(), device->GetId(), device->GetLun()); + // Delete the existing unit + pthread_mutex_lock(&ctrl_mutex); + if (!controller_manager.FindController(id)->DeleteDevice(device)) { + pthread_mutex_unlock(&ctrl_mutex); - // Re-map the controller - MapController(map); + return ReturnStatus(context, false, "Couldn't detach device"); + } + device_factory.DeleteDevice(device); + pthread_mutex_unlock(&ctrl_mutex); + + LOGINFO("Detached %s device with ID %d, unit %d", type.c_str(), id, lun); } return true; @@ -767,7 +602,7 @@ bool Insert(const CommandContext& context, const PbDeviceDefinition& pb_device, Disk *disk = dynamic_cast(device); if (pb_device.block_size()) { - if (disk && disk->IsSectorSizeConfigurable()) { + if (disk != nullptr&& disk->IsSectorSizeConfigurable()) { if (!disk->SetConfiguredSectorSize(pb_device.block_size())) { return ReturnLocalizedError(context, ERROR_BLOCK_SIZE, to_string(pb_device.block_size())); } @@ -804,7 +639,7 @@ bool Insert(const CommandContext& context, const PbDeviceDefinition& pb_device, } } catch(const io_exception& e) { - return ReturnLocalizedError(context, ERROR_FILE_OPEN, initial_filename, e.getmsg()); + return ReturnLocalizedError(context, ERROR_FILE_OPEN, initial_filename, e.get_msg()); } file_support->ReserveFile(filepath, device->GetId(), device->GetLun()); @@ -815,7 +650,7 @@ bool Insert(const CommandContext& context, const PbDeviceDefinition& pb_device, device->SetProtected(pb_device.protected_()); } - if (disk) { + if (disk != nullptr) { disk->MediumChanged(); } @@ -824,8 +659,6 @@ bool Insert(const CommandContext& context, const PbDeviceDefinition& pb_device, void TerminationHandler(int signum) { - DetachAll(); - Cleanup(); exit(signum); @@ -885,8 +718,9 @@ bool ProcessCmd(const CommandContext& context, const PbDeviceDefinition& pb_devi if (id < 0) { return ReturnLocalizedError(context, ERROR_MISSING_DEVICE_ID); } - if (id >= CtrlMax) { - return ReturnStatus(context, false, "Invalid device ID " + to_string(id) + " (0-" + to_string(CtrlMax - 1) + ")"); + if (id >= ControllerManager::DEVICE_MAX) { + return ReturnStatus(context, false, "Invalid device ID " + to_string(id) + " (0-" + + to_string(ControllerManager::DEVICE_MAX - 1) + ")"); } if (operation == ATTACH && reserved_ids.find(id) != reserved_ids.end()) { @@ -894,33 +728,27 @@ bool ProcessCmd(const CommandContext& context, const PbDeviceDefinition& pb_devi } // Check the Unit Number - if (unit < 0 || unit >= UnitNum) { - return ReturnStatus(context, false, "Invalid unit " + to_string(unit) + " (0-" + to_string(UnitNum - 1) + ")"); - } - - // Copy the devices - Device *map[devices.size()]; - for (size_t i = 0; i < devices.size(); i++) { - map[i] = devices[i]; + if (unit < 0 || unit >= AbstractController::LUN_MAX) { + return ReturnStatus(context, false, "Invalid unit " + to_string(unit) + " (0-" + to_string(AbstractController::LUN_MAX - 1) + ")"); } if (operation == ATTACH) { - return Attach(context, pb_device, map, dryRun); + return Attach(context, pb_device, dryRun); } // Does the controller exist? - if (!dryRun && !controllers[id]) { + if (!dryRun && controller_manager.FindController(id) == nullptr) { return ReturnLocalizedError(context, ERROR_NON_EXISTING_DEVICE, to_string(id)); } // Does the unit exist? - Device *device = devices[id * UnitNum + unit]; - if (!device) { + PrimaryDevice *device = controller_manager.GetDeviceByIdAndLun(id, unit); + if (device == nullptr) { return ReturnLocalizedError(context, ERROR_NON_EXISTING_UNIT, to_string(id), to_string(unit)); } if (operation == DETACH) { - return Detach(context, device, map, dryRun); + return Detach(context, device, dryRun); } if ((operation == START || operation == STOP) && !device->IsStoppable()) { @@ -1059,7 +887,7 @@ bool ProcessCmd(const CommandContext& context, const PbCommand& command) // Restore the list of reserved files before proceeding FileSupport::SetReservedFiles(reserved_files); - string result = ValidateLunSetup(command, devices); + string result = ValidateLunSetup(command); if (!result.empty()) { return ReturnStatus(context, false, result); } @@ -1075,7 +903,7 @@ bool ProcessCmd(const CommandContext& context, const PbCommand& command) // A new command with an empty device list is required here in order to return data for all devices PbCommand command; PbResult result; - rascsi_response.GetDevicesInfo(result, command, devices, UnitNum); + rascsi_response.GetDevicesInfo(result, command); SerializeMessage(context.fd, result); return true; } @@ -1083,32 +911,21 @@ bool ProcessCmd(const CommandContext& context, const PbCommand& command) return ReturnStatus(context); } -bool ProcessId(const string id_spec, PbDeviceType type, int& id, int& unit) +bool ProcessId(const string id_spec, int& id, int& unit) { size_t separator_pos = id_spec.find(COMPONENT_SEPARATOR); if (separator_pos == string::npos) { - int max_id = type == SAHD ? 16 : 8; - - if (!GetAsInt(id_spec, id) || id < 0 || id >= max_id) { - cerr << optarg << ": Invalid device ID (0-" << (max_id - 1) << ")" << endl; + if (!GetAsInt(id_spec, id) || id < 0 || id >= 8) { + cerr << optarg << ": Invalid device ID (0-7)" << endl; return false; } - // Required for SASI ID/LUN handling backwards compatibility unit = 0; - if (type == SAHD) { - unit = id % 2; - id /= 2; - } } - else { - int max_unit = type == SAHD ? 2 : UnitNum; - - if (!GetAsInt(id_spec.substr(0, separator_pos), id) || id < 0 || id > 7 || - !GetAsInt(id_spec.substr(separator_pos + 1), unit) || unit < 0 || unit >= max_unit) { - cerr << optarg << ": Invalid unit (0-" << (max_unit - 1) << ")" << endl; - return false; - } + else if (!GetAsInt(id_spec.substr(0, separator_pos), id) || id < 0 || id > 7 || + !GetAsInt(id_spec.substr(separator_pos + 1), unit) || unit < 0 || unit >= AbstractController::LUN_MAX) { + cerr << optarg << ": Invalid unit (0-" << (AbstractController::LUN_MAX - 1) << ")" << endl; + return false; } return true; @@ -1186,25 +1003,18 @@ bool ParseArgument(int argc, char* argv[], int& port) opterr = 1; int opt; - while ((opt = getopt(argc, argv, "-IiHhb:d:n:p:r:t:z:D:F:L:P:R:")) != -1) { + while ((opt = getopt(argc, argv, "-Iib:d:n:p:r:t:z:D:F:L:P:R:")) != -1) { switch (opt) { - // The three options below are kind of a compound option with two letters + // The two options below are kind of a compound option with two letters case 'i': case 'I': id = -1; unit = -1; continue; - case 'h': - case 'H': - id = -1; - unit = -1; - type = SAHD; - continue; - case 'd': case 'D': { - if (!ProcessId(optarg, type, id, unit)) { + if (!ProcessId(optarg, id, unit)) { return false; } continue; @@ -1340,7 +1150,7 @@ bool ParseArgument(int argc, char* argv[], int& port) // Display and log the device list PbServerInfo server_info; - rascsi_response.GetDevices(server_info, devices); + rascsi_response.GetDevices(server_info); const list& devices = { server_info.devices_info().devices().begin(), server_info.devices_info().devices().end() }; const string device_list = ListDevices(devices); LogDevices(device_list); @@ -1375,7 +1185,7 @@ void FixCpu(int cpu) // Monitor Thread // //--------------------------------------------------------------------------- -static void *MonThread(void *param) +static void *MonThread(void *) { // Scheduler Settings struct sched_param schedparam; @@ -1467,20 +1277,20 @@ static void *MonThread(void *param) } case DEVICES_INFO: { - rascsi_response.GetDevicesInfo(result, command, devices, UnitNum); + rascsi_response.GetDevicesInfo(result, command); SerializeMessage(context.fd, result); break; } case DEVICE_TYPES_INFO: { - result.set_allocated_device_types_info(rascsi_response.GetDeviceTypesInfo(result, command)); + result.set_allocated_device_types_info(rascsi_response.GetDeviceTypesInfo(result)); SerializeMessage(context.fd, result); break; } case SERVER_INFO: { result.set_allocated_server_info(rascsi_response.GetServerInfo( - result, devices, reserved_ids, current_log_level, GetParam(command, "folder_pattern"), + result, reserved_ids, current_log_level, GetParam(command, "folder_pattern"), GetParam(command, "file_pattern"), rascsi_image.GetDepth())); SerializeMessage(context.fd, result); break; @@ -1568,7 +1378,7 @@ static void *MonThread(void *param) } } catch(const io_exception& e) { - LOGWARN("%s", e.getmsg().c_str()); + LOGWARN("%s", e.get_msg().c_str()); // Fall through } @@ -1578,7 +1388,7 @@ static void *MonThread(void *param) } } - return NULL; + return nullptr; } //--------------------------------------------------------------------------- @@ -1592,17 +1402,17 @@ int main(int argc, char* argv[]) #ifndef NDEBUG // Get temporary operation info, in order to trigger an assertion on startup if the operation list is incomplete + // TODO Move to unit test? PbResult pb_operation_info_result; const PbOperationInfo *operation_info = rascsi_response.GetOperationInfo(pb_operation_info_result, 0); assert(operation_info->operations_size() == PbOperation_ARRAYSIZE - 1); delete operation_info; #endif - int actid; BUS::phase_t phase; + // added setvbuf to override stdout buffering, so logs are written immediately and not when the process exits. setvbuf(stdout, NULL, _IONBF, 0); - struct sched_param schparam; // Output the Banner Banner(argc, argv); @@ -1621,21 +1431,20 @@ int main(int argc, char* argv[]) // Create a thread-safe stdout logger to process the log messages auto logger = stdout_color_mt("rascsi stdout logger"); - int port = 6868; - if (!InitBus()) { return EPERM; } + int port = DEFAULT_PORT; + if (!InitService(port)) { + return EPERM; + } + if (!ParseArgument(argc, argv, port)) { Cleanup(); return -1; } - if (!InitService(port)) { - return EPERM; - } - // Signal handler to detach all devices on a KILL or TERM signal struct sigaction termination_handler; termination_handler.sa_handler = TerminationHandler; @@ -1650,11 +1459,14 @@ int main(int argc, char* argv[]) // Set the affinity to a specific processor core FixCpu(3); + struct sched_param schparam; #ifdef USE_SEL_EVENT_ENABLE // Scheduling policy setting (highest priority) schparam.sched_priority = sched_get_priority_max(SCHED_FIFO); sched_setscheduler(0, SCHED_FIFO, &schparam); -#endif // USE_SEL_EVENT_ENABLE +#else + cout << "Note: No RaSCSI hardware support, only client interface calls are supported" << endl; +#endif // Start execution running = true; @@ -1662,12 +1474,11 @@ int main(int argc, char* argv[]) // Main Loop while (running) { // Work initialization - actid = -1; phase = BUS::busfree; #ifdef USE_SEL_EVENT_ENABLE // SEL signal polling - if (bus->PollSelectEvent() < 0) { + if (!bus->PollSelectEvent()) { // Stop on interrupt if (errno == EINTR) { break; @@ -1676,21 +1487,21 @@ int main(int argc, char* argv[]) } // Get the bus - bus->Aquire(); + bus->Acquire(); #else - bus->Aquire(); + bus->Acquire(); if (!bus->GetSEL()) { usleep(0); continue; } -#endif // USE_SEL_EVENT_ENABLE +#endif // Wait until BSY is released as there is a possibility for the // initiator to assert it while setting the ID (for up to 3 seconds) if (bus->GetBSY()) { int now = SysTimer::GetTimerLow(); while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) { - bus->Aquire(); + bus->Acquire(); if (!bus->GetBSY()) { break; } @@ -1702,42 +1513,20 @@ int main(int argc, char* argv[]) continue; } - pthread_mutex_lock(&ctrl_mutex); - - BYTE data = bus->GetDAT(); - int initiator_id = -1; - // Notify all controllers - int i = 0; - for (auto it = controllers.begin(); it != controllers.end(); ++i, ++it) { - if (!*it || (data & (1 << i)) == 0) { - continue; - } + // The initiator and target ID + BYTE id_data = bus->GetDAT(); - // Extract the SCSI initiator ID - int tmp = data - (1 << i); - if (tmp) { - initiator_id = 0; - for (int j = 0; j < 8; j++) { - tmp >>= 1; - if (tmp) { - initiator_id++; - } - else { - break; - } - } - } + pthread_mutex_lock(&ctrl_mutex); - // Find the target that has moved to the selection phase - if ((*it)->Process(initiator_id) == BUS::selection) { - // Get the target ID - actid = i; + // Identify the responsible controller + AbstractController *controller = controller_manager.IdentifyController(id_data); + if (controller != nullptr) { + initiator_id = controller->ExtractInitiatorId(id_data); - // Bus Selection phase + if (controller->Process(initiator_id) == BUS::selection) { phase = BUS::selection; - break; } } @@ -1754,12 +1543,12 @@ int main(int argc, char* argv[]) // Scheduling policy setting (highest priority) schparam.sched_priority = sched_get_priority_max(SCHED_FIFO); sched_setscheduler(0, SCHED_FIFO, &schparam); -#endif // USE_SEL_EVENT_ENABLE +#endif // Loop until the bus is free while (running) { // Target drive - phase = controllers[actid]->Process(initiator_id); + phase = controller->Process(initiator_id); // End when the bus is free if (phase == BUS::busfree) { @@ -1773,7 +1562,7 @@ int main(int argc, char* argv[]) // Set the scheduling priority back to normal schparam.sched_priority = 0; sched_setscheduler(0, SCHED_OTHER, &schparam); -#endif // USE_SEL_EVENT_ENABLE +#endif // End the target travel active = false; diff --git a/src/raspberrypi/rascsi_exceptions.h b/src/raspberrypi/rascsi_exceptions.h new file mode 100644 index 00000000..f3758c4c --- /dev/null +++ b/src/raspberrypi/rascsi_exceptions.h @@ -0,0 +1,62 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2021-2022 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "scsi.h" +#include +#include + +using namespace std; + +class illegal_argument_exception final : public exception { +private: + string msg; + +public: + illegal_argument_exception(const string& msg) : msg(msg) {} + ~illegal_argument_exception() {} + + const string& get_msg() const { return msg; } +}; + +class io_exception : public exception { +private: + string msg; + +public: + io_exception(const string& msg) : msg(msg) {} + virtual ~io_exception() {} + + const string& get_msg() const { return msg; } +}; + +class file_not_found_exception : public io_exception { +public: + file_not_found_exception(const string& msg) : io_exception(msg) {} + ~file_not_found_exception() {} +}; + +class scsi_error_exception : public exception { +private: + scsi_defs::sense_key sense_key; + scsi_defs::asc asc; + scsi_defs::status status; + +public: + scsi_error_exception(scsi_defs::sense_key sense_key = scsi_defs::sense_key::ABORTED_COMMAND, + scsi_defs::asc asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION, + scsi_defs::status status = scsi_defs::status::CHECK_CONDITION) + : sense_key(sense_key), asc(asc), status(status) {} + ~scsi_error_exception() {}; + + scsi_defs::sense_key get_sense_key() const { return sense_key; } + scsi_defs::asc get_asc() const { return asc; } + scsi_defs::status get_status() const { return status; } +}; diff --git a/src/raspberrypi/rascsi_image.cpp b/src/raspberrypi/rascsi_image.cpp index ead8efde..d433ff4e 100644 --- a/src/raspberrypi/rascsi_image.cpp +++ b/src/raspberrypi/rascsi_image.cpp @@ -43,8 +43,6 @@ RascsiImage::RascsiImage() else { default_image_folder = "/home/pi/images"; } - - depth = 1; } bool RascsiImage::CheckDepth(const string& filename) diff --git a/src/raspberrypi/rascsi_image.h b/src/raspberrypi/rascsi_image.h index 1fa5165c..81102675 100644 --- a/src/raspberrypi/rascsi_image.h +++ b/src/raspberrypi/rascsi_image.h @@ -40,5 +40,5 @@ public: private: string default_image_folder; - int depth; + int depth = -1; }; diff --git a/src/raspberrypi/rascsi_interface.proto b/src/raspberrypi/rascsi_interface.proto index 5207b7a2..e0c41a68 100644 --- a/src/raspberrypi/rascsi_interface.proto +++ b/src/raspberrypi/rascsi_interface.proto @@ -14,8 +14,8 @@ package rascsi_interface; // The available device types enum PbDeviceType { UNDEFINED = 0; - // Non-removable SASI drive - SAHD = 1; + // Non-removable SASI drive, not supported anymore + SAHD = 1 [deprecated = true]; // Non-removable SCSI drive SCHD = 2; // Removable SCSI drive @@ -241,7 +241,7 @@ message PbDeviceProperties { bool supports_params = 7; // List of default parameters, if any (requires supports_params to be true) map default_params = 8; - // LUN numbers this device can represent. At least 1 (for LUN 0 only). Maxium is 32, for LUNs 0-31. + // LUN numbers this device can have, usually 32 int32 luns = 9; // Unordered list of permitted block sizes in bytes, empty if the block size is not configurable repeated uint32 block_sizes = 10; @@ -306,7 +306,7 @@ message PbDeviceDefinition { PbDeviceType type = 3; // Device specific named parameters, e.g. the name of an image file map params = 4; - // The optional block size in bytes per sector, must be one of the supported block sizes for SASI/SCSI + // The optional block size in bytes per sector, must be one of the supported block sizes int32 block_size = 5; // The device name components string vendor = 6; diff --git a/src/raspberrypi/rascsi_response.cpp b/src/raspberrypi/rascsi_response.cpp index 4174e44f..1636eeae 100644 --- a/src/raspberrypi/rascsi_response.cpp +++ b/src/raspberrypi/rascsi_response.cpp @@ -3,14 +3,13 @@ // SCSI Target Emulator RaSCSI Reloaded // for Raspberry Pi // -// Copyright (C) 2021 Uwe Seimet +// Copyright (C) 2021-2022 Uwe Seimet // //--------------------------------------------------------------------------- #include "devices/file_support.h" #include "devices/disk.h" #include "devices/device_factory.h" -#include "devices/device.h" #include "protobuf_util.h" #include "rascsi_version.h" #include "rascsi_interface.pb.h" @@ -20,17 +19,14 @@ using namespace rascsi_interface; using namespace protobuf_util; -RascsiResponse::RascsiResponse(DeviceFactory *device_factory, const RascsiImage *rascsi_image) -{ - this->device_factory = device_factory; - this->rascsi_image = rascsi_image; -} +list RascsiResponse::log_levels = { "trace", "debug", "info", "warn", "err", "critical", "off" }; + PbDeviceProperties *RascsiResponse::GetDeviceProperties(const Device *device) { PbDeviceProperties *properties = new PbDeviceProperties(); - properties->set_luns(device->GetSupportedLuns()); + properties->set_luns(AbstractController::LUN_MAX); properties->set_read_only(device->IsReadOnly()); properties->set_protectable(device->IsProtectable()); properties->set_stoppable(device->IsStoppable()); @@ -60,14 +56,15 @@ void RascsiResponse::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_inf { PbDeviceTypeProperties *type_properties = device_types_info.add_properties(); type_properties->set_type(type); - Device *device = device_factory->CreateDevice(type, ""); + Device *device = device_factory->CreateDevice(type, "", -1); type_properties->set_allocated_properties(GetDeviceProperties(device)); - delete device; + device_factory->DeleteDevice(device); } void RascsiResponse::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info) { - int ordinal = 1; + // Start with 2 instead of 1. 1 was the removed SASI drive type. + int ordinal = 2; while (PbDeviceType_IsValid(ordinal)) { PbDeviceType type = UNDEFINED; PbDeviceType_Parse(PbDeviceType_Name((PbDeviceType)ordinal), &type); @@ -232,31 +229,25 @@ PbReservedIdsInfo *RascsiResponse::GetReservedIds(PbResult& result, const unorde return reserved_ids_info; } -void RascsiResponse::GetDevices(PbServerInfo& server_info, const vector& devices) +void RascsiResponse::GetDevices(PbServerInfo& server_info) { - for (const Device *device : devices) { - // Skip if unit does not exist or is not assigned - if (device) { - PbDevice *pb_device = server_info.mutable_devices_info()->add_devices(); - GetDevice(device, pb_device); - } + for (const Device *device : device_factory->GetAllDevices()) { + PbDevice *pb_device = server_info.mutable_devices_info()->add_devices(); + GetDevice(device, pb_device); } } -void RascsiResponse::GetDevicesInfo(PbResult& result, const PbCommand& command, const vector& devices, - int unit_count) +void RascsiResponse::GetDevicesInfo(PbResult& result, const PbCommand& command) { set id_sets; if (!command.devices_size()) { - for (const Device *device : devices) { - if (device) { - id_sets.insert(make_pair(device->GetId(), device->GetLun())); - } + for (const Device *device : device_factory->GetAllDevices()) { + id_sets.insert(make_pair(device->GetId(), device->GetLun())); } } else { for (const auto& device : command.devices()) { - if (devices[device.id() * unit_count + device.unit()]) { + if (device_factory->GetDeviceByIdAndLun(device.id(), device.unit())) { id_sets.insert(make_pair(device.id(), device.unit())); } else { @@ -271,14 +262,13 @@ void RascsiResponse::GetDevicesInfo(PbResult& result, const PbCommand& command, result.set_allocated_devices_info(devices_info); for (const auto& id_set : id_sets) { - const Device *device = devices[id_set.first * unit_count + id_set.second]; - GetDevice(device, devices_info->add_devices()); + GetDevice(device_factory->GetDeviceByIdAndLun(id_set.first, id_set.second), devices_info->add_devices()); } result.set_status(true); } -PbDeviceTypesInfo *RascsiResponse::GetDeviceTypesInfo(PbResult& result, const PbCommand& command) +PbDeviceTypesInfo *RascsiResponse::GetDeviceTypesInfo(PbResult& result) { PbDeviceTypesInfo *device_types_info = new PbDeviceTypesInfo(); @@ -289,9 +279,8 @@ PbDeviceTypesInfo *RascsiResponse::GetDeviceTypesInfo(PbResult& result, const Pb return device_types_info; } -PbServerInfo *RascsiResponse::GetServerInfo(PbResult& result, const vector& devices, - const unordered_set& reserved_ids, const string& current_log_level, const string& folder_pattern, - const string& file_pattern, int scan_depth) +PbServerInfo *RascsiResponse::GetServerInfo(PbResult& result, const unordered_set& reserved_ids, + const string& current_log_level, const string& folder_pattern, const string& file_pattern, int scan_depth) { PbServerInfo *server_info = new PbServerInfo(); @@ -301,7 +290,7 @@ PbServerInfo *RascsiResponse::GetServerInfo(PbResult& result, const vectorset_allocated_network_interfaces_info(GetNetworkInterfacesInfo(result)); server_info->set_allocated_mapping_info(GetMappingInfo(result)); - GetDevices(*server_info, devices); + GetDevices(*server_info); server_info->set_allocated_reserved_ids_info(GetReservedIds(result, reserved_ids)); server_info->set_allocated_operation_info(GetOperationInfo(result, scan_depth)); diff --git a/src/raspberrypi/rascsi_response.h b/src/raspberrypi/rascsi_response.h index eba4cef2..1e9220db 100644 --- a/src/raspberrypi/rascsi_response.h +++ b/src/raspberrypi/rascsi_response.h @@ -3,7 +3,7 @@ // SCSI Target Emulator RaSCSI Reloaded // for Raspberry Pi // -// Copyright (C) 2021 Uwe Seimet +// Copyright (C) 2021-2022 Uwe Seimet // //--------------------------------------------------------------------------- @@ -11,7 +11,7 @@ #include "devices/device_factory.h" #include "rascsi_interface.pb.h" -#include +#include #include using namespace std; @@ -25,18 +25,18 @@ class RascsiResponse { public: - RascsiResponse(DeviceFactory *, const RascsiImage *); - ~RascsiResponse() {}; + RascsiResponse(DeviceFactory *device_factory, const RascsiImage *rascsi_image) + : device_factory(device_factory), rascsi_image(rascsi_image) {} + ~RascsiResponse() {} bool GetImageFile(PbImageFile *, const string&); PbImageFilesInfo *GetAvailableImages(PbResult&, const string&, const string&, int); PbReservedIdsInfo *GetReservedIds(PbResult&, const unordered_set&); - void GetDevices(PbServerInfo&, const vector&); - void GetDevicesInfo(PbResult&, const PbCommand&, const vector&, int); - PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&, const PbCommand&); + void GetDevices(PbServerInfo&); + void GetDevicesInfo(PbResult&, const PbCommand&); + PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&); PbVersionInfo *GetVersionInfo(PbResult&); - PbServerInfo *GetServerInfo(PbResult&, const vector&, const unordered_set&, const string&, - const string&, const string&, int); + PbServerInfo *GetServerInfo(PbResult&, const unordered_set&, const string&, const string&, const string&, int); PbNetworkInterfacesInfo *GetNetworkInterfacesInfo(PbResult&); PbMappingInfo *GetMappingInfo(PbResult&); PbLogLevelInfo *GetLogLevelInfo(PbResult&, const string&); @@ -47,7 +47,7 @@ private: DeviceFactory *device_factory; const RascsiImage *rascsi_image; - vector log_levels { "trace", "debug", "info", "warn", "err", "critical", "off" }; + static list log_levels; PbDeviceProperties *GetDeviceProperties(const Device *); void GetDevice(const Device *, PbDevice *); diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp index 7c029277..0be9f2dc 100644 --- a/src/raspberrypi/rasctl.cpp +++ b/src/raspberrypi/rasctl.cpp @@ -131,8 +131,8 @@ int main(int argc, char* argv[]) cerr << " where ID := {0-7}" << endl; cerr << " UNIT := {0-31}, default is 0" << endl; cerr << " CMD := {attach|detach|insert|eject|protect|unprotect|show}" << endl; - cerr << " TYPE := {sahd|schd|scrm|sccd|scmo|scbr|scdp} or convenience type {hd|rm|mo|cd|bridge|daynaport}" << endl; - cerr << " BLOCK_SIZE := {256|512|1024|2048|4096) bytes per hard disk drive block" << endl; + cerr << " TYPE := {schd|scrm|sccd|scmo|scbr|scdp} or convenience type {hd|rm|mo|cd|bridge|daynaport}" << endl; + cerr << " BLOCK_SIZE := {512|1024|2048|4096) bytes per hard disk drive block" << endl; cerr << " NAME := name of device to attach (VENDOR:PRODUCT:REVISION)" << endl; cerr << " FILE|PARAM := image file path or device-specific parameter" << endl; cerr << " IMAGE_FOLDER := default location for image files, default is '~/images'" << endl; diff --git a/src/raspberrypi/rasctl_commands.cpp b/src/raspberrypi/rasctl_commands.cpp index 773f8294..67870de8 100644 --- a/src/raspberrypi/rasctl_commands.cpp +++ b/src/raspberrypi/rasctl_commands.cpp @@ -9,7 +9,7 @@ #include #include "os.h" -#include "exceptions.h" +#include "rascsi_exceptions.h" #include "protobuf_util.h" #include "rasutil.h" #include "rasctl_commands.h" @@ -26,12 +26,8 @@ using namespace protobuf_util; RasctlCommands::RasctlCommands(PbCommand& command, const string& hostname, int port, const string& token, const string& locale) + : command(command), hostname(hostname), port(port), token(token), locale(locale) { - this->command = command; - this->hostname = hostname; - this->port = port; - this->token = token; - this->locale = locale; } void RasctlCommands::SendCommand() @@ -76,7 +72,7 @@ void RasctlCommands::SendCommand() SerializeMessage(fd, command); } catch(const io_exception& e) { - cerr << "Error: " << e.getmsg() << endl; + cerr << "Error: " << e.get_msg() << endl; if (fd >= 0) { close(fd); @@ -96,7 +92,7 @@ void RasctlCommands::SendCommand() catch(const io_exception& e) { close(fd); - cerr << "Error: " << e.getmsg() << endl; + cerr << "Error: " << e.get_msg() << endl; exit(EXIT_FAILURE); } diff --git a/src/raspberrypi/rasctl_display.cpp b/src/raspberrypi/rasctl_display.cpp index 96e3f35d..03b734c8 100644 --- a/src/raspberrypi/rasctl_display.cpp +++ b/src/raspberrypi/rasctl_display.cpp @@ -119,21 +119,15 @@ void RasctlDisplay::DisplayLogLevelInfo(const PbLogLevelInfo& log_level_info) void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types_info) { - cout << "Supported device types and their properties:" << endl; + cout << "Supported device types and their properties:"; for (const auto& device_type_info : device_types_info.properties()) { - cout << " " << PbDeviceType_Name(device_type_info.type()); + cout << endl << " " << PbDeviceType_Name(device_type_info.type()) << " "; const PbDeviceProperties& properties = device_type_info.properties(); - cout << " Supported LUN numbers: 0"; - if (properties.luns() > 1) { - cout << "-" << (properties.luns() - 1); - } - cout << endl; - if (properties.read_only() || properties.protectable() || properties.stoppable() || properties.read_only() || properties.lockable()) { - cout << " Properties: "; + cout << "Properties: "; bool has_property = false; if (properties.read_only()) { cout << "read-only"; @@ -154,28 +148,28 @@ void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types if (properties.lockable()) { cout << (has_property ? ", " : "") << "lockable"; } - cout << endl; + cout << endl << " "; } if (properties.supports_file()) { - cout << " Image file support" << endl; + cout << "Image file support" << endl << " "; } - else if (properties.supports_params()) { - cout << " Parameter support" << endl; + if (properties.supports_params()) { + cout << "Parameter support" << endl << " "; } if (properties.supports_params() && properties.default_params_size()) { // Creates a sorted map map params = { properties.default_params().begin(), properties.default_params().end() }; - cout << " Default parameters: "; + cout << "Default parameters: "; bool isFirst = true; for (const auto& param : params) { if (!isFirst) { - cout << " "; + cout << endl << " "; } - cout << param.first << "=" << param.second << endl; + cout << param.first << "=" << param.second; isFirst = false; } @@ -185,7 +179,7 @@ void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types // Creates a sorted set set block_sizes = { properties.block_sizes().begin(), properties.block_sizes().end() }; - cout << " Configurable block sizes in bytes: "; + cout << "Configurable block sizes in bytes: "; bool isFirst = true; for (const auto& block_size : block_sizes) { @@ -196,7 +190,6 @@ void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types isFirst = false; } - cout << endl; } } } @@ -259,6 +252,9 @@ void RasctlDisplay::DisplayNetworkInterfaces(const PbNetworkInterfacesInfo& netw if (!isFirst) { cout << ", "; } + else { + cout << " "; + } isFirst = false; cout << interface; } diff --git a/src/raspberrypi/rasdump.cpp b/src/raspberrypi/rasdump.cpp index 3f3a5d43..a30159ba 100644 --- a/src/raspberrypi/rasdump.cpp +++ b/src/raspberrypi/rasdump.cpp @@ -48,7 +48,7 @@ void Cleanup(); // Signal processing // //--------------------------------------------------------------------------- -void KillHandler(int sig) +void KillHandler(int) { // Stop running Cleanup(); @@ -141,10 +141,7 @@ void Reset() bool ParseArgument(int argc, char* argv[]) { int opt; - char *file; - - // Initialization - file = NULL; + char *file = nullptr; // Argument Parsing opterr = 0; @@ -213,7 +210,7 @@ bool WaitPhase(BUS::phase_t phase) // Timeout (3000ms) now = SysTimer::GetTimerLow(); while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) { - bus.Aquire(); + bus.Acquire(); if (bus.GetREQ() && bus.GetPhase() == phase) { return true; } @@ -248,20 +245,20 @@ bool Selection(int id) data |= (1 << boardid); data |= (1 << id); bus.SetDAT(data); - bus.SetSEL(TRUE); + bus.SetSEL(true); // wait for busy count = 10000; do { usleep(20); - bus.Aquire(); + bus.Acquire(); if (bus.GetBSY()) { break; } } while (count--); // SEL negate - bus.SetSEL(FALSE); + bus.SetSEL(false); // Success if the target is busy return bus.GetBSY(); diff --git a/src/raspberrypi/sasidump.cpp b/src/raspberrypi/sasidump.cpp deleted file mode 100644 index 10bd5be1..00000000 --- a/src/raspberrypi/sasidump.cpp +++ /dev/null @@ -1,745 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI Target Emulator RaSCSI Reloaded -// for Raspberry Pi -// -// Powered by XM6 TypeG Technology. -// Copyright (C) 2016-2020 GIMONS -// [ HDD dump utility (Initiator mode/SASI Version) ] -// -// SASI IMAGE EXAMPLE -// X68000 -// 10MB(10441728 BS=256 C=40788) -// 20MB(20748288 BS=256 C=81048) -// 40MB(41496576 BS=256 C=162096) -// -// MZ-2500/MZ-2800 MZ-1F23 -// 20MB(22437888 BS=1024 C=21912) -// -//--------------------------------------------------------------------------- - -#include -#include "os.h" -#include "fileio.h" -#include "filepath.h" -#include "gpiobus.h" -#include "rascsi_version.h" - -#define BUFSIZE 1024 * 64 // Maybe around 64KB? - -GPIOBUS bus; -int targetid; -int unitid; -int bsiz; // Block size -int bnum; // Number of blocks -Filepath hdffile; // HDF file -bool restore; // Restore flag -BYTE buffer[BUFSIZE]; // Work buffer -int result; // Result code - -void Cleanup(); - -//--------------------------------------------------------------------------- -// -// Signal processing -// -//--------------------------------------------------------------------------- -void KillHandler(int sig) -{ - // Stop instruction - Cleanup(); - exit(0); -} - -//--------------------------------------------------------------------------- -// -// Banner output -// -//--------------------------------------------------------------------------- -bool Banner(int argc, char* argv[]) -{ - printf("RaSCSI hard disk dump utility (SASI HDD) "); - printf("version %s (%s, %s)\n", - rascsi_get_version_string(), - __DATE__, - __TIME__); - - if (argc < 2 || strcmp(argv[1], "-h") == 0) { - printf("Usage: %s -i ID [-u UT] [-b BSIZE] -c COUNT -f FILE [-r]\n", argv[0]); - printf(" ID is target device SASI ID {0|1|2|3|4|5|6|7}.\n"); - printf(" UT is target unit ID {0|1}. Default is 0.\n"); - printf(" BSIZE is block size {256|512|1024}. Default is 256.\n"); - printf(" COUNT is block count.\n"); - printf(" FILE is HDF file path.\n"); - printf(" -r is restore operation.\n"); - return false; - } - - return true; -} - -bool Init() -{ - // Interrupt handler settings - if (signal(SIGINT, KillHandler) == SIG_ERR) { - return false; - } - if (signal(SIGHUP, KillHandler) == SIG_ERR) { - return false; - } - if (signal(SIGTERM, KillHandler) == SIG_ERR) { - return false; - } - - // GPIO initialization - if (!bus.Init(BUS::INITIATOR)) { - return false; - } - - // Work initialization - targetid = -1; - unitid = 0; - bsiz = 256; - bnum = -1; - restore = false; - - return true; -} - -void Cleanup() -{ - bus.Cleanup(); -} - -void Reset() -{ - // Reset bus signal line - bus.Reset(); -} - -bool ParseArgument(int argc, char* argv[]) -{ - int opt; - char *file; - - // Initialization - file = NULL; - - // Argument parsing - opterr = 0; - while ((opt = getopt(argc, argv, "i:u:b:c:f:r")) != -1) { - switch (opt) { - case 'i': - targetid = optarg[0] - '0'; - break; - - case 'u': - unitid = optarg[0] - '0'; - break; - - case 'b': - bsiz = atoi(optarg); - break; - - case 'c': - bnum = atoi(optarg); - break; - - case 'f': - file = optarg; - break; - - case 'r': - restore = true; - break; - } - } - - // TARGET ID check - if (targetid < 0 || targetid > 7) { - fprintf(stderr, - "Error : Invalid target id range\n"); - return false; - } - - // UNIT ID check - if (unitid < 0 || unitid > 1) { - fprintf(stderr, - "Error : Invalid unit id range\n"); - return false; - } - - // BSIZ check - if (bsiz != 256 && bsiz != 512 && bsiz != 1024) { - fprintf(stderr, - "Error : Invalid block size\n"); - return false; - } - - // BNUM check - if (bnum < 0) { - fprintf(stderr, - "Error : Invalid block count\n"); - return false; - } - - // File check - if (!file) { - fprintf(stderr, - "Error : Invalid file path\n"); - return false; - } - - hdffile.SetPath(file); - - return true; -} - -bool WaitPhase(BUS::phase_t phase) -{ - DWORD now; - - // Timeout (3000ms) - now = SysTimer::GetTimerLow(); - while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) { - bus.Aquire(); - if (bus.GetREQ() && bus.GetPhase() == phase) { - return true; - } - } - - return false; -} - -//--------------------------------------------------------------------------- -// -// Bus free phase execution -// -//--------------------------------------------------------------------------- -void BusFree() -{ - bus.Reset(); -} - -//--------------------------------------------------------------------------- -// -// Selection phase execution -// -//--------------------------------------------------------------------------- -bool Selection(int id) -{ - BYTE data; - int count; - - // ID setting and SEL assertion - data = 0; - data |= (1 << id); - bus.SetDAT(data); - bus.SetSEL(TRUE); - - // Wait for BSY - count = 10000; - do { - usleep(20); - bus.Aquire(); - if (bus.GetBSY()) { - break; - } - } while (count--); - - // SEL negate - bus.SetSEL(FALSE); - - // Return true if target is busy - return bus.GetBSY(); -} - -//--------------------------------------------------------------------------- -// -// Command phase execution -// -//--------------------------------------------------------------------------- -bool Command(BYTE *buf, int length) -{ - int count; - - // Wait for phase - if (!WaitPhase(BUS::command)) { - return false; - } - - // Send command - count = bus.SendHandShake(buf, length, BUS::SEND_NO_DELAY); - - // Return true is send results match number of requests - if (count == length) { - return true; - } - - // Send error - return false; -} - -//--------------------------------------------------------------------------- -// -// Data in phase execution -// -//--------------------------------------------------------------------------- -int DataIn(BYTE *buf, int length) -{ - // Wait for phase - if (!WaitPhase(BUS::datain)) { - return -1; - } - - // Receive data - return bus.ReceiveHandShake(buf, length); -} - -//--------------------------------------------------------------------------- -// -// Data out phase execution -// -//--------------------------------------------------------------------------- -int DataOut(BYTE *buf, int length) -{ - // Wait for phase - if (!WaitPhase(BUS::dataout)) { - return -1; - } - - // Receive data - return bus.SendHandShake(buf, length, BUS::SEND_NO_DELAY); -} - -//--------------------------------------------------------------------------- -// -// Status phase execution -// -//--------------------------------------------------------------------------- -int Status() -{ - BYTE buf[256]; - - // Wait for phase - if (!WaitPhase(BUS::status)) { - return -2; - } - - // Receive data - if (bus.ReceiveHandShake(buf, 1) == 1) { - return (int)buf[0]; - } - - // Receive error - return -1; -} - -//--------------------------------------------------------------------------- -// -// Message in phase execution -// -//--------------------------------------------------------------------------- -int MessageIn() -{ - BYTE buf[256]; - - // Wait for phase - if (!WaitPhase(BUS::msgin)) { - return -2; - } - - // Receive data - if (bus.ReceiveHandShake(buf, 1) == 1) { - return (int)buf[0]; - } - - // Receive error - return -1; -} - -//--------------------------------------------------------------------------- -// -// TEST UNIT READY execution -// -//--------------------------------------------------------------------------- -int TestUnitReady(int id) -{ - BYTE cmd[256]; - - // Initialize result code - result = 0; - - if (!Selection(id)) { - result = -1; - goto exit; - } - - memset(cmd, 0x00, 6); - cmd[0] = 0x00; - cmd[1] = unitid << 5; - if (!Command(cmd, 6)) { - result = -2; - goto exit; - } - - if (Status() < 0) { - result = -4; - goto exit; - } - - if (MessageIn() < 0) { - result = -5; - goto exit; - } - -exit: - BusFree(); - - return result; -} - -//--------------------------------------------------------------------------- -// -// REQUEST SENSE execution -// -//--------------------------------------------------------------------------- -int RequestSense(int id, BYTE *buf) -{ - BYTE cmd[256]; - int count; - - // Initialize result codes - result = 0; - count = 0; - - if (!Selection(id)) { - result = -1; - goto exit; - } - - memset(cmd, 0x00, 6); - cmd[0] = 0x03; - cmd[1] = unitid << 5; - cmd[4] = 4; - if (!Command(cmd, 6)) { - result = -2; - goto exit; - } - - memset(buf, 0x00, 256); - count = DataIn(buf, 256); - if (count <= 0) { - result = -3; - goto exit; - } - - if (Status() < 0) { - result = -4; - goto exit; - } - - if (MessageIn() < 0) { - result = -5; - goto exit; - } - -exit: - BusFree(); - - // If successful, return number of transfers - if (result == 0) { - return count; - } - - return result; -} - -//--------------------------------------------------------------------------- -// -// READ6 execution -// -//--------------------------------------------------------------------------- -int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) -{ - BYTE cmd[256]; - int count; - - // Initialize result codes - result = 0; - count = 0; - - if (!Selection(id)) { - result = -1; - goto exit; - } - - memset(cmd, 0x00, 10); - cmd[0] = 0x8; - cmd[1] = (BYTE)(bstart >> 16); - cmd[1] &= 0x1f; - cmd[1] = unitid << 5; - cmd[2] = (BYTE)(bstart >> 8); - cmd[3] = (BYTE)bstart; - cmd[4] = (BYTE)blength; - if (!Command(cmd, 6)) { - result = -2; - goto exit; - } - - count = DataIn(buf, length); - if (count <= 0) { - result = -3; - goto exit; - } - - if (Status() < 0) { - result = -4; - goto exit; - } - - if (MessageIn() < 0) { - result = -5; - goto exit; - } - -exit: - BusFree(); - - // If successful, return number of transfers - if (result == 0) { - return count; - } - - return result; -} - -//--------------------------------------------------------------------------- -// -// WRITE6 execution -// -//--------------------------------------------------------------------------- -int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf) -{ - BYTE cmd[256]; - int count; - - // Initialize result codes - result = 0; - count = 0; - - if (!Selection(id)) { - result = -1; - goto exit; - } - - memset(cmd, 0x00, 10); - cmd[0] = 0xa; - cmd[1] = (BYTE)(bstart >> 16); - cmd[1] &= 0x1f; - cmd[1] = unitid << 5; - cmd[2] = (BYTE)(bstart >> 8); - cmd[3] = (BYTE)bstart; - cmd[4] = (BYTE)blength; - if (!Command(cmd, 6)) { - result = -2; - goto exit; - } - - count = DataOut(buf, length); - if (count <= 0) { - result = -3; - goto exit; - } - - if (Status() < 0) { - result = -4; - goto exit; - } - - if (MessageIn() < 0) { - result = -5; - goto exit; - } - -exit: - BusFree(); - - // If successful, return number of transfers - if (result == 0) { - return count; - } - - return result; -} - -//--------------------------------------------------------------------------- -// -// Main processing -// -//--------------------------------------------------------------------------- -int main(int argc, char* argv[]) -{ - int i; - int count; - DWORD duni; - DWORD dsiz; - DWORD dnum; - Fileio fio; - Fileio::OpenMode omode; - off_t size; - - // Output banner - if (!Banner(argc, argv)) { - exit(0); - } - - if (!Init()) { - fprintf(stderr, "Error : Initializing\n"); - - // Probably not executing as root? - exit(EPERM); - } - - if (!ParseArgument(argc, argv)) { - Cleanup(); - - // Exit with argument error - exit(EINVAL); - } - - Reset(); - - // Open file - if (restore) { - omode = Fileio::ReadOnly; - } else { - omode = Fileio::WriteOnly; - } - if (!fio.Open(hdffile.GetPath(), omode)) { - fprintf(stderr, "Error : Can't open hdf file\n"); - - Cleanup(); - exit(EPERM); - } - - BusFree(); - - // Execute RESET signal - bus.SetRST(TRUE); - usleep(1000); - bus.SetRST(FALSE); - - // Start dump - printf("TARGET ID : %d\n", targetid); - printf("UNIT ID : %d\n", unitid); - - // TEST UNIT READY - count = TestUnitReady(targetid); - if (count < 0) { - fprintf(stderr, "TEST UNIT READY ERROR %d\n", count); - goto cleanup_exit; - } - - // REQUEST SENSE (for CHECK CONDITION) - count = RequestSense(targetid, buffer); - if (count < 0) { - fprintf(stderr, "REQUEST SENSE ERROR %d\n", count); - goto cleanup_exit; - } - - printf("Number of blocks : %d Blocks\n", bnum); - printf("Block length : %d Bytes\n", bsiz); - - // Display data size - printf("Total length : %d MBytes %d Bytes\n", - (bsiz * bnum / 1024 / 1024), - (bsiz * bnum)); - - // Get restore file size - if (restore) { - size = fio.GetFileSize(); - printf("Restore file size : %d bytes", (int)size); - if (size > (off_t)(bsiz * bnum)) { - printf("(WARNING : File size is larger than disk size)"); - } else if (size < (off_t)(bsiz * bnum)) { - printf("(ERROR : File size is smaller than disk size)\n"); - goto cleanup_exit; - } - printf("\n"); - } - - // Dump by buffer size - duni = BUFSIZE; - duni /= bsiz; - dsiz = BUFSIZE; - dnum = bnum * bsiz; - dnum /= BUFSIZE; - - if (restore) { - printf("Restore progress : "); - } else { - printf("Dump progress : "); - } - - for (i = 0; i < (int)dnum; i++) { - if (i > 0) { - printf("\033[21D"); - printf("\033[0K"); - } - printf("%3d%%(%7d/%7d)", - (int)((i + 1) * 100 / dnum), - (int)(i * duni), - bnum); - fflush(stdout); - - if (restore) { - if (fio.Read(buffer, dsiz)) { - if (Write6(targetid, i * duni, duni, dsiz, buffer) >= 0) { - continue; - } - } - } else { - if (Read6(targetid, i * duni, duni, dsiz, buffer) >= 0) { - if (fio.Write(buffer, dsiz)) { - continue; - } - } - } - - printf("\n"); - printf("Error occured and aborted... %d\n", result); - goto cleanup_exit; - } - - if (dnum > 0) { - printf("\033[21D"); - printf("\033[0K"); - } - - // Rounding of capacity - dnum = bnum % duni; - dsiz = dnum * bsiz; - - if (dnum > 0) { - if (restore) { - if (fio.Read(buffer, dsiz)) { - Write6(targetid, i * duni, dnum, dsiz, buffer); - } - } else { - if (Read6(targetid, i * duni, dnum, dsiz, buffer) >= 0) { - fio.Write(buffer, dsiz); - } - } - } - - // Completion message - printf("%3d%%(%7d/%7d)\n", 100, bnum, bnum); - -cleanup_exit: - fio.Close(); - - Cleanup(); - - exit(0); -} diff --git a/src/raspberrypi/scsi.cpp b/src/raspberrypi/scsi.cpp index 5fbd3243..02b94ef6 100644 --- a/src/raspberrypi/scsi.cpp +++ b/src/raspberrypi/scsi.cpp @@ -9,7 +9,6 @@ // //--------------------------------------------------------------------------- -#include "os.h" #include "scsi.h" //--------------------------------------------------------------------------- diff --git a/src/raspberrypi/scsi.h b/src/raspberrypi/scsi.h index 2f746f7b..6e402a23 100644 --- a/src/raspberrypi/scsi.h +++ b/src/raspberrypi/scsi.h @@ -14,7 +14,7 @@ //=========================================================================== // -// SASI/SCSI Bus +// SCSI Bus // //=========================================================================== class BUS @@ -47,7 +47,7 @@ public: virtual ~BUS() {} // Basic Functions - virtual BOOL Init(mode_e mode) = 0; + virtual bool Init(mode_e mode) = 0; virtual void Reset() = 0; virtual void Cleanup() = 0; phase_t GetPhase(); @@ -66,45 +66,45 @@ public: return ((raw_data >> pin_num) & 1); } - virtual bool GetBSY() = 0; + virtual bool GetBSY() const = 0; virtual void SetBSY(bool ast) = 0; - virtual BOOL GetSEL() = 0; - virtual void SetSEL(BOOL ast) = 0; + virtual bool GetSEL() const = 0; + virtual void SetSEL(bool ast) = 0; - virtual BOOL GetATN() = 0; - virtual void SetATN(BOOL ast) = 0; + virtual bool GetATN() const = 0; + virtual void SetATN(bool ast) = 0; - virtual BOOL GetACK() = 0; - virtual void SetACK(BOOL ast) = 0; + virtual bool GetACK() const = 0; + virtual void SetACK(bool ast) = 0; - virtual BOOL GetRST() = 0; - virtual void SetRST(BOOL ast) = 0; + virtual bool GetRST() const = 0; + virtual void SetRST(bool ast) = 0; - virtual BOOL GetMSG() = 0; - virtual void SetMSG(BOOL ast) = 0; + virtual bool GetMSG() const = 0; + virtual void SetMSG(bool ast) = 0; - virtual BOOL GetCD() = 0; - virtual void SetCD(BOOL ast) = 0; + virtual bool GetCD() const = 0; + virtual void SetCD(bool ast) = 0; - virtual BOOL GetIO() = 0; - virtual void SetIO(BOOL ast) = 0; + virtual bool GetIO() = 0; + virtual void SetIO(bool ast) = 0; - virtual BOOL GetREQ() = 0; - virtual void SetREQ(BOOL ast) = 0; + virtual bool GetREQ() const = 0; + virtual void SetREQ(bool ast) = 0; virtual BYTE GetDAT() = 0; virtual void SetDAT(BYTE dat) = 0; - virtual BOOL GetDP() = 0; // Get parity signal + virtual bool GetDP() const = 0; // Get parity signal - virtual DWORD Aquire() = 0; - virtual int CommandHandShake(BYTE *buf, bool) = 0; + virtual DWORD Acquire() = 0; + virtual int CommandHandShake(BYTE *buf) = 0; virtual int ReceiveHandShake(BYTE *buf, int count) = 0; virtual int SendHandShake(BYTE *buf, int count, int delay_after_bytes) = 0; - virtual BOOL GetSignal(int pin) = 0; + virtual bool GetSignal(int pin) const = 0; // Get SCSI input signal value - virtual void SetSignal(int pin, BOOL ast) = 0; + virtual void SetSignal(int pin, bool ast) = 0; // Set SCSI output signal value static const int SEND_NO_DELAY = -1; // Passed into SendHandShake when we don't want to delay @@ -117,12 +117,6 @@ private: static const char* phase_str_table[]; }; -//=========================================================================== -// -// For Status byte codes, Sense Keys and Additional Sense Codes -// See https://www.t10.org/lists/1spc-lst.htm -// -//=========================================================================== namespace scsi_defs { enum scsi_level : int { SCSI_1_CCS = 1, @@ -228,12 +222,17 @@ namespace scsi_defs { enum asc : int { NO_ADDITIONAL_SENSE_INFORMATION = 0x00, + WRITE_FAULT = 0x03, + READ_FAULT = 0x11, INVALID_COMMAND_OPERATION_CODE = 0x20, LBA_OUT_OF_RANGE = 0x21, INVALID_FIELD_IN_CDB = 0x24, INVALID_LUN = 0x25, + INVALID_FIELD_IN_PARAMETER_LIST = 0x26, WRITE_PROTECTED = 0x27, NOT_READY_TO_READY_CHANGE = 0x28, - MEDIUM_NOT_PRESENT = 0x3a + POWER_ON_OR_RESET = 0x29, + MEDIUM_NOT_PRESENT = 0x3a, + LOAD_OR_EJECT_FAILED = 0x53 }; }; diff --git a/src/raspberrypi/scsimon.cpp b/src/raspberrypi/scsimon.cpp index 560613db..7b8a9a1a 100644 --- a/src/raspberrypi/scsimon.cpp +++ b/src/raspberrypi/scsimon.cpp @@ -76,7 +76,7 @@ char input_file_name[_MAX_FNAME]; // Signal Processing // //--------------------------------------------------------------------------- -void KillHandler(int sig) +void KillHandler(int) { // Stop instruction running = false; @@ -130,7 +130,7 @@ void parse_arguments(int argc, char *argv[]) // Copyright text // //--------------------------------------------------------------------------- -void print_copyright_text(int argc, char *argv[]) +void print_copyright_text(int, char *[]) { LOGINFO("SCSI Monitor Capture Tool - part of RaSCSI(*^..^*) "); LOGINFO("version %s (%s, %s)", @@ -148,7 +148,7 @@ void print_copyright_text(int argc, char *argv[]) // Help text // //--------------------------------------------------------------------------- -void print_help_text(int argc, char *argv[]) +void print_help_text(int, char *argv[]) { LOGINFO("%s -i [input file json] -b [buffer size] [output file]", argv[0]); LOGINFO(" -i [input file json] - scsimon will parse the json file instead of capturing new data"); @@ -163,7 +163,7 @@ void print_help_text(int argc, char *argv[]) // Banner Output // //--------------------------------------------------------------------------- -void Banner(int argc, char *argv[]) +void Banner(int, char *[]) { if (import_data) { @@ -368,7 +368,7 @@ int main(int argc, char *argv[]) while (running) { // Work initialization - this_sample = (bus->Aquire() & ALL_SCSI_PINS); + this_sample = (bus->Acquire() & ALL_SCSI_PINS); loop_count++; if (loop_count > LLONG_MAX - 1) { diff --git a/src/raspberrypi/test/abstract_controller_test.cpp b/src/raspberrypi/test/abstract_controller_test.cpp new file mode 100644 index 00000000..21e6ef65 --- /dev/null +++ b/src/raspberrypi/test/abstract_controller_test.cpp @@ -0,0 +1,33 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "testing.h" +#include "controllers/abstract_controller.h" + +TEST(AbstractControllerTest, AbstractController) +{ + const int ID = 1; + const int LUN = 4; + const int INITIATOR_ID = 7; + const int UNKNOWN_INITIATOR_ID = -1; + + MockAbstractController controller(ID); + MockPrimaryDevice device; + + device.SetLun(LUN); + + EXPECT_TRUE(controller.AddDevice(&device)); + EXPECT_EQ(ID, controller.GetTargetId()); + EXPECT_TRUE(controller.HasDeviceForLun(LUN)); + EXPECT_FALSE(controller.HasDeviceForLun(0)); + EXPECT_TRUE(controller.DeleteDevice(&device)); + + EXPECT_EQ(INITIATOR_ID, controller.ExtractInitiatorId((1 << INITIATOR_ID) | ( 1 << ID))); + EXPECT_EQ(UNKNOWN_INITIATOR_ID, controller.ExtractInitiatorId(1 << ID)); +} diff --git a/src/raspberrypi/test/controller_manager_test.cpp b/src/raspberrypi/test/controller_manager_test.cpp new file mode 100644 index 00000000..d2aa1014 --- /dev/null +++ b/src/raspberrypi/test/controller_manager_test.cpp @@ -0,0 +1,33 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "testing.h" +#include "controllers/controller_manager.h" + +TEST(ControllerManagerTest, ControllerManager) +{ + const int ID = 4; + const int LUN = 6; + + PrimaryDevice *device = static_cast(device_factory.CreateDevice(UNDEFINED, "services", ID)); + device->SetId(ID); + device->SetLun(LUN); + + controller_manager.CreateScsiController(nullptr, device); + EXPECT_NE(nullptr, controller_manager.IdentifyController(1 << ID)); + EXPECT_EQ(nullptr, controller_manager.IdentifyController(0)); + EXPECT_NE(nullptr, controller_manager.FindController(ID)); + EXPECT_EQ(nullptr, controller_manager.FindController(0)); + EXPECT_EQ(device, controller_manager.GetDeviceByIdAndLun(ID, LUN)); + EXPECT_EQ(nullptr, controller_manager.GetDeviceByIdAndLun(0, 0)); + + controller_manager.DeleteAllControllersAndDevices(); + EXPECT_EQ(nullptr, controller_manager.FindController(ID)); + EXPECT_EQ(nullptr, controller_manager.GetDeviceByIdAndLun(ID, LUN)); +} diff --git a/src/raspberrypi/test/device_factory_test.cpp b/src/raspberrypi/test/device_factory_test.cpp index 821b29d6..1a57ea42 100644 --- a/src/raspberrypi/test/device_factory_test.cpp +++ b/src/raspberrypi/test/device_factory_test.cpp @@ -5,26 +5,16 @@ // // Copyright (C) 2022 Uwe Seimet // -// Unit tests based on GoogleTest and GoogleMock -// //--------------------------------------------------------------------------- -#include - +#include "testing.h" +#include "rascsi_exceptions.h" #include "rascsi_version.h" -#include "../devices/device.h" -#include "../devices/device_factory.h" - -using namespace rascsi_interface; - -namespace DeviceFactoryTest -{ - -DeviceFactory& device_factory = DeviceFactory::instance(); +#include "devices/device.h" +#include "devices/device_factory.h" TEST(DeviceFactoryTest, GetTypeForFile) { - EXPECT_EQ(device_factory.GetTypeForFile("test.hdf"), SAHD); EXPECT_EQ(device_factory.GetTypeForFile("test.hds"), SCHD); EXPECT_EQ(device_factory.GetTypeForFile("test.HDS"), SCHD); EXPECT_EQ(device_factory.GetTypeForFile("test.hda"), SCHD); @@ -43,6 +33,24 @@ TEST(DeviceFactoryTest, GetTypeForFile) EXPECT_EQ(device_factory.GetTypeForFile("test.iso.suffix"), UNDEFINED); } +TEST(DeviceFactoryTest, LifeCycle) +{ + Device *device = device_factory.CreateDevice(UNDEFINED, "services", -1); + EXPECT_NE(nullptr, device); + + list devices = device_factory.GetAllDevices(); + EXPECT_EQ(1, devices.size()); + EXPECT_EQ(device, devices.front()); + + EXPECT_EQ(device, device_factory.GetDeviceByIdAndLun(-1, 0)); + EXPECT_EQ(nullptr, device_factory.GetDeviceByIdAndLun(-1, 1)); + + device_factory.DeleteDevice(device); + devices = device_factory.GetAllDevices(); + EXPECT_EQ(0, devices.size()); + EXPECT_EQ(nullptr, device_factory.GetDeviceByIdAndLun(-1, 0)); +} + TEST(DeviceFactoryTest, GetSectorSizes) { unordered_set sector_sizes; @@ -88,14 +96,15 @@ TEST(DeviceFactoryTest, GetSectorSizes) TEST(DeviceFactoryTest, UnknownDeviceType) { - Device *device = device_factory.CreateDevice(UNDEFINED, "test"); + Device *device = device_factory.CreateDevice(UNDEFINED, "test", -1); EXPECT_EQ(nullptr, device); } TEST(DeviceFactoryTest, SCHD_Device_Defaults) { - Device *device = device_factory.CreateDevice(UNDEFINED, "test.hda"); + Device *device = device_factory.CreateDevice(UNDEFINED, "test.hda", -1); EXPECT_NE(nullptr, device); + EXPECT_EQ("SCHD", device->GetType()); EXPECT_TRUE(device->SupportsFile()); EXPECT_FALSE(device->SupportsParams()); EXPECT_TRUE(device->IsProtectable()); @@ -113,13 +122,29 @@ TEST(DeviceFactoryTest, SCHD_Device_Defaults) EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), device->GetRevision()); - EXPECT_EQ(32, device->GetSupportedLuns()); + device_factory.DeleteDevice(device); + + device = device_factory.CreateDevice(UNDEFINED, "test.hds", -1); + EXPECT_NE(nullptr, device); + EXPECT_EQ("SCHD", device->GetType()); + device_factory.DeleteDevice(device); + + device = device_factory.CreateDevice(UNDEFINED, "test.hdi", -1); + EXPECT_NE(nullptr, device); + EXPECT_EQ("SCHD", device->GetType()); + device_factory.DeleteDevice(device); + + device = device_factory.CreateDevice(UNDEFINED, "test.nhd", -1); + EXPECT_NE(nullptr, device); + EXPECT_EQ("SCHD", device->GetType()); + device_factory.DeleteDevice(device); } TEST(DeviceFactoryTest, SCRM_Device_Defaults) { - Device *device = device_factory.CreateDevice(UNDEFINED, "test.hdr"); + Device *device = device_factory.CreateDevice(UNDEFINED, "test.hdr", -1); EXPECT_NE(nullptr, device); + EXPECT_EQ("SCRM", device->GetType()); EXPECT_TRUE(device->SupportsFile()); EXPECT_FALSE(device->SupportsParams()); EXPECT_TRUE(device->IsProtectable()); @@ -137,13 +162,14 @@ TEST(DeviceFactoryTest, SCRM_Device_Defaults) EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), device->GetRevision()); - EXPECT_EQ(32, device->GetSupportedLuns()); + device_factory.DeleteDevice(device); } TEST(DeviceFactoryTest, SCMO_Device_Defaults) { - Device *device = device_factory.CreateDevice(UNDEFINED, "test.mos"); + Device *device = device_factory.CreateDevice(UNDEFINED, "test.mos", -1); EXPECT_NE(nullptr, device); + EXPECT_EQ("SCMO", device->GetType()); EXPECT_TRUE(device->SupportsFile()); EXPECT_FALSE(device->SupportsParams()); EXPECT_TRUE(device->IsProtectable()); @@ -161,13 +187,14 @@ TEST(DeviceFactoryTest, SCMO_Device_Defaults) EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), device->GetRevision()); - EXPECT_EQ(32, device->GetSupportedLuns()); + device_factory.DeleteDevice(device); } TEST(DeviceFactoryTest, SCCD_Device_Defaults) { - Device *device = device_factory.CreateDevice(UNDEFINED, "test.iso"); + Device *device = device_factory.CreateDevice(UNDEFINED, "test.iso", -1); EXPECT_NE(nullptr, device); + EXPECT_EQ("SCCD", device->GetType()); EXPECT_TRUE(device->SupportsFile()); EXPECT_FALSE(device->SupportsParams()); EXPECT_FALSE(device->IsProtectable()); @@ -185,13 +212,14 @@ TEST(DeviceFactoryTest, SCCD_Device_Defaults) EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), device->GetRevision()); - EXPECT_EQ(32, device->GetSupportedLuns()); + device_factory.DeleteDevice(device); } TEST(DeviceFactoryTest, SCBR_Device_Defaults) { - Device *device = device_factory.CreateDevice(UNDEFINED, "bridge"); + Device *device = device_factory.CreateDevice(UNDEFINED, "bridge", -1); EXPECT_NE(nullptr, device); + EXPECT_EQ("SCBR", device->GetType()); EXPECT_FALSE(device->SupportsFile()); EXPECT_TRUE(device->SupportsParams()); EXPECT_FALSE(device->IsProtectable()); @@ -209,13 +237,14 @@ TEST(DeviceFactoryTest, SCBR_Device_Defaults) EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), device->GetRevision()); - EXPECT_EQ(32, device->GetSupportedLuns()); + device_factory.DeleteDevice(device); } TEST(DeviceFactoryTest, SCDP_Device_Defaults) { - Device *device = device_factory.CreateDevice(UNDEFINED, "daynaport"); + Device *device = device_factory.CreateDevice(UNDEFINED, "daynaport", -1); EXPECT_NE(nullptr, device); + EXPECT_EQ("SCDP", device->GetType()); EXPECT_FALSE(device->SupportsFile()); EXPECT_TRUE(device->SupportsParams()); EXPECT_FALSE(device->IsProtectable()); @@ -232,13 +261,14 @@ TEST(DeviceFactoryTest, SCDP_Device_Defaults) EXPECT_EQ("SCSI/Link", device->GetProduct()); EXPECT_EQ("1.4a", device->GetRevision()); - EXPECT_EQ(32, device->GetSupportedLuns()); + device_factory.DeleteDevice(device); } TEST(DeviceFactoryTest, SCHS_Device_Defaults) { - Device *device = device_factory.CreateDevice(UNDEFINED, "services"); + Device *device = device_factory.CreateDevice(UNDEFINED, "services", -1); EXPECT_NE(nullptr, device); + EXPECT_EQ("SCHS", device->GetType()); EXPECT_FALSE(device->SupportsFile()); EXPECT_FALSE(device->SupportsParams()); EXPECT_FALSE(device->IsProtectable()); @@ -256,13 +286,14 @@ TEST(DeviceFactoryTest, SCHS_Device_Defaults) EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), device->GetRevision()); - EXPECT_EQ(32, device->GetSupportedLuns()); + device_factory.DeleteDevice(device); } TEST(DeviceFactoryTest, SCLP_Device_Defaults) { - Device *device = device_factory.CreateDevice(UNDEFINED, "printer"); + Device *device = device_factory.CreateDevice(UNDEFINED, "printer", -1); EXPECT_NE(nullptr, device); + EXPECT_EQ("SCLP", device->GetType()); EXPECT_FALSE(device->SupportsFile()); EXPECT_TRUE(device->SupportsParams()); EXPECT_FALSE(device->IsProtectable()); @@ -280,7 +311,5 @@ TEST(DeviceFactoryTest, SCLP_Device_Defaults) EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), device->GetRevision()); - EXPECT_EQ(32, device->GetSupportedLuns()); -} - + device_factory.DeleteDevice(device); } diff --git a/src/raspberrypi/test/device_test.cpp b/src/raspberrypi/test/device_test.cpp index a40ecd70..e75fb44c 100644 --- a/src/raspberrypi/test/device_test.cpp +++ b/src/raspberrypi/test/device_test.cpp @@ -5,68 +5,44 @@ // // Copyright (C) 2022 Uwe Seimet // -// Unit tests based on GoogleTest and GoogleMock -// //--------------------------------------------------------------------------- -#include -#include +#include "testing.h" +#include "rascsi_exceptions.h" +#include "devices/device.h" -#include "gpiobus.h" -#include "../devices/scsihd.h" -#include "../devices/device_factory.h" - -using namespace rascsi_interface; - -namespace DeviceTest +class TestDevice : public Device { +public: -class MockController : public SCSIDEV -{ - MOCK_METHOD(BUS::phase_t, Process, (int), (override)); - MOCK_METHOD(int, GetEffectiveLun, (), ()); - MOCK_METHOD(void, Error, (scsi_defs::sense_key, scsi_defs::asc, scsi_defs::status), (override)); - MOCK_METHOD(int, GetInitiatorId, (), ()); - MOCK_METHOD(void, SetUnit, (int), ()); - MOCK_METHOD(void, Connect, (int, BUS *), ()); - MOCK_METHOD(void, Status, (), ()); - MOCK_METHOD(void, DataIn, (), ()); - MOCK_METHOD(void, DataOut, (), ()); - MOCK_METHOD(void, BusFree, (), ()); - MOCK_METHOD(void, Selection, (), ()); - MOCK_METHOD(void, Command, (), ()); - MOCK_METHOD(void, MsgIn, (), ()); - MOCK_METHOD(void, MsgOut, (), ()); - MOCK_METHOD(void, Send, (), (override)); - MOCK_METHOD(bool, XferMsg, (int), ()); - MOCK_METHOD(bool, XferIn, (BYTE *), ()); - MOCK_METHOD(bool, XferOut, (bool), (override)); - MOCK_METHOD(void, ReceiveBytes, (), ()); - MOCK_METHOD(void, Execute, (), (override)); - MOCK_METHOD(void, FlushUnit, (), ()); - MOCK_METHOD(void, Receive, (), (override)); - MOCK_METHOD(bool, HasUnit, (), (const override)); + TestDevice() : Device("test") {} + ~TestDevice() {} - FRIEND_TEST(DeviceTest, TestUnitReady); - - MockController() { } - ~MockController() { } + bool Dispatch() { return false; } }; -DeviceFactory& device_factory = DeviceFactory::instance(); - -TEST(DeviceTest, TestUnitReady) +TEST(DeviceTest, ProductData) { - MockController controller; - Device *device = device_factory.CreateDevice(SCHD, "test"); + TestDevice device; - controller.ctrl.cmd[0] = eCmdTestUnitReady; + EXPECT_THROW(device.SetVendor(""), illegal_argument_exception); + EXPECT_THROW(device.SetVendor("123456789"), illegal_argument_exception); + device.SetVendor("12345678"); + EXPECT_EQ("12345678", device.GetVendor()); - device->SetReady(false); - EXPECT_CALL(controller, Error); - EXPECT_TRUE(device->Dispatch(&controller)); - - // TODO Add tests for a device that is ready after the SASI code removal -} + EXPECT_THROW(device.SetProduct(""), illegal_argument_exception); + EXPECT_THROW(device.SetProduct("12345678901234567"), illegal_argument_exception); + device.SetProduct("1234567890123456"); + EXPECT_EQ("1234567890123456", device.GetProduct()); + EXPECT_THROW(device.SetRevision(""), illegal_argument_exception); + EXPECT_THROW(device.SetRevision("12345"), illegal_argument_exception); + device.SetRevision("1234"); + EXPECT_EQ("1234", device.GetRevision()); + + device.SetVendor("V"); + device.SetProduct("P"); + device.SetRevision("R"); + + EXPECT_EQ("V P R ", device.GetPaddedName()); } diff --git a/src/raspberrypi/test/file_support_test.cpp b/src/raspberrypi/test/file_support_test.cpp new file mode 100644 index 00000000..ceaea752 --- /dev/null +++ b/src/raspberrypi/test/file_support_test.cpp @@ -0,0 +1,43 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "testing.h" +#include "devices/file_support.h" + +class TestFileSupport : public FileSupport +{ + void Open(const Filepath&) {} +}; + +TEST(FileSupportTest, FileSupport) +{ + const int ID = 1; + const int LUN = 2; + + Filepath path; + path.SetPath("path"); + TestFileSupport file_support; + + file_support.SetPath(path); + Filepath result; + file_support.GetPath(result); + EXPECT_STREQ("path", result.GetPath()); + + int id; + int lun; + EXPECT_FALSE(FileSupport::GetIdsForReservedFile(path, id, lun)); + + file_support.ReserveFile(path, ID, LUN); + EXPECT_TRUE(FileSupport::GetIdsForReservedFile(path, id, lun)); + EXPECT_EQ(ID, id); + EXPECT_EQ(LUN, lun); + + file_support.UnreserveFile(); + EXPECT_FALSE(FileSupport::GetIdsForReservedFile(path, id, lun)); +} diff --git a/src/raspberrypi/test/mode_pages_test.cpp b/src/raspberrypi/test/mode_pages_test.cpp index c685c5e8..d2abe84b 100644 --- a/src/raspberrypi/test/mode_pages_test.cpp +++ b/src/raspberrypi/test/mode_pages_test.cpp @@ -5,94 +5,22 @@ // // Copyright (C) 2022 Uwe Seimet // -// Unit tests based on GoogleTest and GoogleMock -// //--------------------------------------------------------------------------- -#include -#include - -#include "../devices/scsihd.h" -#include "../devices/scsihd_nec.h" -#include "../devices/scsicd.h" -#include "../devices/scsimo.h" -#include "../devices/host_services.h" +#include "testing.h" +#include "spdlog/spdlog.h" +#include "rascsi_exceptions.h" +#include "devices/scsi_command_util.h" +#include "devices/scsihd.h" +#include "devices/scsihd_nec.h" +#include "devices/scsicd.h" +#include "devices/scsimo.h" +#include "devices/host_services.h" using namespace std; -namespace ModePagesTest -{ - unordered_set sector_sizes; -class MockModePageDevice : public ModePageDevice -{ -public: - - MockModePageDevice() : ModePageDevice("test") { } - ~MockModePageDevice() { } - - MOCK_METHOD(vector, Inquiry, (), (const, override)); - MOCK_METHOD(int, ModeSense6, (const DWORD *, BYTE *), (override)); - MOCK_METHOD(int, ModeSense10, (const DWORD *, BYTE *, int), (override)); - - void AddModePages(map>& pages, int page, bool) const override { - // Return dummy data for other pages than page 0 - if (page) { - vector buf(255); - pages[page] = buf; - } - } - - // Make protected methods visible for testing - // TODO Why does FRIEND_TEST not work for this method? - - int AddModePages(const DWORD *cdb, BYTE *buf, int max_length) { - return ModePageDevice::AddModePages(cdb, buf, max_length); - } -}; - -class MockSCSIHD : public SCSIHD -{ - FRIEND_TEST(ModePagesTest, SCSIHD_AddModePages); - - MockSCSIHD(const unordered_set& sector_sizes) : SCSIHD(sector_sizes, false) { }; - ~MockSCSIHD() { }; -}; - -class MockSCSIHD_NEC : public SCSIHD_NEC -{ - FRIEND_TEST(ModePagesTest, SCSIHD_NEC_AddModePages); - - MockSCSIHD_NEC(const unordered_set& sector_sizes) : SCSIHD_NEC(sector_sizes) { }; - ~MockSCSIHD_NEC() { }; -}; - -class MockSCSICD : public SCSICD -{ - FRIEND_TEST(ModePagesTest, SCSICD_AddModePages); - - MockSCSICD(const unordered_set& sector_sizes) : SCSICD(sector_sizes) { }; - ~MockSCSICD() { }; -}; - -class MockSCSIMO : public SCSIMO -{ - FRIEND_TEST(ModePagesTest, SCSIMO_AddModePages); - - MockSCSIMO(const unordered_set& sector_sizes, const unordered_map& geometries) - : SCSIMO(sector_sizes, geometries) { }; - ~MockSCSIMO() { }; -}; - -class MockHostServices : public HostServices -{ - FRIEND_TEST(ModePagesTest, HostServices_AddModePages); - - MockHostServices() { }; - ~MockHostServices() { }; -}; - TEST(ModePagesTest, ModePageDevice_AddModePages) { DWORD cdb[6]; @@ -104,7 +32,7 @@ TEST(ModePagesTest, ModePageDevice_AddModePages) EXPECT_EQ(1, device.AddModePages(cdb, buf, 1)) << "Allocation length was not limited"; cdb[2] = 0x00; - EXPECT_EQ(0, device.AddModePages(cdb, buf, 12)) << "Data for non-existing code page 0 were returned"; + EXPECT_THROW(device.AddModePages(cdb, buf, 12), scsi_error_exception) << "Data for non-existing code page 0 were returned"; } TEST(ModePagesTest, SCSIHD_AddModePages) @@ -181,4 +109,38 @@ TEST(ModePagesTest, HostServices_AddModePages) EXPECT_EQ(10, mode_pages[32].size()); } +TEST(ModePagesTest, ModeSelect) +{ + const int LENGTH = 12; + + DWORD cdb[16] = {}; + BYTE buf[255] = {}; + + // PF (vendor-specific parameter format) + cdb[1] = 0x00; + EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf, LENGTH, 0), scsi_error_exception) + << "Vendor-specific parameters are not supported"; + + // PF (standard parameter format) + cdb[1] = 0x10; + // Request 512 bytes per sector + buf[9] = 0x00; + buf[10] = 0x02; + buf[11] = 0x00; + EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf, LENGTH, 256), scsi_error_exception) + << "Requested sector size does not match current sector size"; + + // Page 0 + buf[LENGTH] = 0x00; + EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf, LENGTH + 2, 512), scsi_error_exception) + << "Unsupported page 0 was not rejected"; + + // Page 3 (Format Device Page) + buf[LENGTH] = 0x03; + EXPECT_THROW(scsi_command_util::ModeSelect(cdb, buf, LENGTH + 2, 512), scsi_error_exception) + << "Requested sector size does not match current sector size"; + + // Match the requested to the current sector size + buf[LENGTH + 12] = 0x02; + scsi_command_util::ModeSelect(cdb, buf, LENGTH + 2, 512); } diff --git a/src/raspberrypi/test/primary_device_test.cpp b/src/raspberrypi/test/primary_device_test.cpp new file mode 100644 index 00000000..a37d81fc --- /dev/null +++ b/src/raspberrypi/test/primary_device_test.cpp @@ -0,0 +1,191 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "testing.h" +#include "rascsi_exceptions.h" +#include "devices/primary_device.h" +#include "devices/device_factory.h" + +TEST(PrimaryDeviceTest, UnitReady) +{ + MockScsiController controller(nullptr, 0); + MockPrimaryDevice device; + + controller.AddDevice(&device); + + controller.ctrl.cmd[0] = eCmdTestUnitReady; + + device.SetReset(true); + device.SetAttn(true); + device.SetReady(false); + EXPECT_CALL(controller, DataIn()).Times(0); + EXPECT_THROW(device.Dispatch(), scsi_error_exception); + + device.SetReset(false); + EXPECT_CALL(controller, DataIn()).Times(0); + EXPECT_THROW(device.Dispatch(), scsi_error_exception); + + device.SetReset(true); + device.SetAttn(false); + EXPECT_CALL(controller, DataIn()).Times(0); + EXPECT_THROW(device.Dispatch(), scsi_error_exception); + + device.SetReset(false); + device.SetAttn(true); + EXPECT_CALL(controller, DataIn()).Times(0); + EXPECT_THROW(device.Dispatch(), scsi_error_exception); + + device.SetAttn(false); + EXPECT_CALL(controller, DataIn()).Times(0); + EXPECT_THROW(device.Dispatch(), scsi_error_exception); + + device.SetReady(true); + EXPECT_CALL(controller, Status()).Times(1); + EXPECT_TRUE(device.Dispatch()); + EXPECT_TRUE(controller.ctrl.status == scsi_defs::status::GOOD); +} + +TEST(PrimaryDeviceTest, Inquiry) +{ + MockScsiController controller(nullptr, 0); + MockPrimaryDevice device; + + device.SetController(&controller); + + controller.ctrl.cmd[0] = eCmdInquiry; + // ALLOCATION LENGTH + controller.ctrl.cmd[4] = 255; + + ON_CALL(device, InquiryInternal()).WillByDefault([&device]() { + return device.HandleInquiry(device_type::PROCESSOR, scsi_level::SPC_3, false); + }); + EXPECT_CALL(device, InquiryInternal()).Times(1); + EXPECT_CALL(controller, DataIn()).Times(1); + EXPECT_TRUE(device.Dispatch()); + EXPECT_EQ(0x7F, controller.ctrl.buffer[0]) << "Invalid LUN was not reported"; + + EXPECT_TRUE(controller.AddDevice(&device)); + EXPECT_FALSE(controller.AddDevice(&device)) << "Duplicate LUN was not rejected"; + EXPECT_CALL(device, InquiryInternal()).Times(1); + EXPECT_CALL(controller, DataIn()).Times(1); + EXPECT_TRUE(device.Dispatch()); + EXPECT_EQ(device_type::PROCESSOR, controller.ctrl.buffer[0]); + EXPECT_EQ(0x00, controller.ctrl.buffer[1]) << "Device was not reported as non-removable"; + EXPECT_EQ(scsi_level::SPC_3, controller.ctrl.buffer[2]) << "Wrong SCSI level"; + EXPECT_EQ(scsi_level::SCSI_2, controller.ctrl.buffer[3]) << "Wrong response level"; + EXPECT_EQ(0x1F, controller.ctrl.buffer[4]) << "Wrong additional data size"; + + ON_CALL(device, InquiryInternal()).WillByDefault([&device]() { + return device.HandleInquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_1_CCS, true); + }); + EXPECT_CALL(device, InquiryInternal()).Times(1); + EXPECT_CALL(controller, DataIn()).Times(1); + EXPECT_TRUE(device.Dispatch()); + EXPECT_EQ(device_type::DIRECT_ACCESS, controller.ctrl.buffer[0]); + EXPECT_EQ(0x80, controller.ctrl.buffer[1]) << "Device was not reported as removable"; + EXPECT_EQ(scsi_level::SCSI_1_CCS, controller.ctrl.buffer[2]) << "Wrong SCSI level"; + EXPECT_EQ(scsi_level::SCSI_1_CCS, controller.ctrl.buffer[3]) << "Wrong response level"; + EXPECT_EQ(0x1F, controller.ctrl.buffer[4]) << "Wrong additional data size"; + + controller.ctrl.cmd[1] = 0x01; + EXPECT_CALL(controller, DataIn()).Times(0); + EXPECT_THROW(device.Dispatch(), scsi_error_exception) << "EVPD bit is not supported"; + + controller.ctrl.cmd[2] = 0x01; + EXPECT_CALL(controller, DataIn()).Times(0); + EXPECT_THROW(device.Dispatch(), scsi_error_exception) << "PAGE CODE field is not supported"; + + controller.ctrl.cmd[1] = 0x00; + controller.ctrl.cmd[2] = 0x00; + // ALLOCATION LENGTH + controller.ctrl.cmd[4] = 1; + EXPECT_CALL(device, InquiryInternal()).Times(1); + EXPECT_CALL(controller, DataIn()).Times(1); + EXPECT_TRUE(device.Dispatch()); + EXPECT_EQ(0x1F, controller.ctrl.buffer[4]) << "Wrong additional data size"; + EXPECT_EQ(1, controller.ctrl.length) << "Wrong ALLOCATION LENGTH handling"; +} + +TEST(PrimaryDeviceTest, RequestSense) +{ + MockScsiController controller(nullptr, 0); + MockPrimaryDevice device; + + controller.AddDevice(&device); + + controller.ctrl.cmd[0] = eCmdRequestSense; + // ALLOCATION LENGTH + controller.ctrl.cmd[4] = 255; + + device.SetReady(false); + EXPECT_THROW(device.Dispatch(), scsi_error_exception); + + device.SetReady(true); + EXPECT_CALL(controller, DataIn()).Times(1); + EXPECT_TRUE(device.Dispatch()); + EXPECT_TRUE(controller.ctrl.status == scsi_defs::status::GOOD); +} + +TEST(PrimaryDeviceTest, ReportLuns) +{ + const int LUN1 = 1; + const int LUN2 = 4; + + MockScsiController controller(nullptr, 0); + MockPrimaryDevice device1; + MockPrimaryDevice device2; + + device1.SetLun(LUN1); + controller.AddDevice(&device1); + ASSERT_TRUE(controller.HasDeviceForLun(LUN1)); + device2.SetLun(LUN2); + controller.AddDevice(&device2); + ASSERT_TRUE(controller.HasDeviceForLun(LUN2)); + + controller.ctrl.cmd[0] = eCmdReportLuns; + // ALLOCATION LENGTH + controller.ctrl.cmd[9] = 255; + + EXPECT_CALL(controller, DataIn()).Times(1); + EXPECT_TRUE(device1.Dispatch()); + EXPECT_EQ(0x00, controller.ctrl.buffer[0]) << "Wrong data length"; + EXPECT_EQ(0x00, controller.ctrl.buffer[1]) << "Wrong data length"; + EXPECT_EQ(0x00, controller.ctrl.buffer[2]) << "Wrong data length"; + EXPECT_EQ(0x10, controller.ctrl.buffer[3]) << "Wrong data length"; + EXPECT_EQ(0x00, controller.ctrl.buffer[8]) << "Wrong LUN1 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[9]) << "Wrong LUN1 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[10]) << "Wrong LUN1 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[11]) << "Wrong LUN1 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[12]) << "Wrong LUN1 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[13]) << "Wrong LUN1 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[14]) << "Wrong LUN1 number"; + EXPECT_EQ(LUN1, controller.ctrl.buffer[15]) << "Wrong LUN1 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[16]) << "Wrong LUN2 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[17]) << "Wrong LUN2 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[18]) << "Wrong LUN2 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[19]) << "Wrong LUN2 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[20]) << "Wrong LUN2 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[21]) << "Wrong LUN2 number"; + EXPECT_EQ(0x00, controller.ctrl.buffer[22]) << "Wrong LUN2 number"; + EXPECT_EQ(LUN2, controller.ctrl.buffer[23]) << "Wrong LUN2 number"; + + controller.ctrl.cmd[2] = 0x01; + EXPECT_THROW(device1.Dispatch(), scsi_error_exception) << "Only SELECT REPORT mode 0 is supported"; +} + +TEST(PrimaryDeviceTest, UnknownCommand) +{ + MockScsiController controller(nullptr, 0); + MockPrimaryDevice device; + + controller.AddDevice(&device); + + controller.ctrl.cmd[0] = 0xFF; + EXPECT_FALSE(device.Dispatch()); +} diff --git a/src/raspberrypi/test/scsi_controller_test.cpp b/src/raspberrypi/test/scsi_controller_test.cpp new file mode 100644 index 00000000..e1d35cfd --- /dev/null +++ b/src/raspberrypi/test/scsi_controller_test.cpp @@ -0,0 +1,22 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "testing.h" +#include "controllers/scsi_controller.h" + +TEST(ScsiControllerTest, ScsiController) +{ + MockBus bus; + MockScsiController controller(&bus, 0); + + EXPECT_EQ(32, controller.GetMaxLuns()); + + EXPECT_CALL(controller, SetPhase(BUS::phase_t::busfree)).Times(1); + controller.Reset(); +} diff --git a/src/raspberrypi/test/test_setup.cpp b/src/raspberrypi/test/test_setup.cpp new file mode 100644 index 00000000..c2c26040 --- /dev/null +++ b/src/raspberrypi/test/test_setup.cpp @@ -0,0 +1,38 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include + +#include "spdlog/spdlog.h" +#include "controllers/controller_manager.h" +#include "devices/device_factory.h" + +class Environment : public ::testing::Environment +{ +public: + + Environment() {} + ~Environment() {} + + // Turn off logging + void SetUp() override { spdlog::set_level(spdlog::level::off); } +}; + +DeviceFactory& device_factory = DeviceFactory::instance(); + +ControllerManager& controller_manager = ControllerManager::instance(); + +int main(int, char*[]) +{ + testing::AddGlobalTestEnvironment(new Environment()); + + testing::InitGoogleTest(); + + return RUN_ALL_TESTS(); +} diff --git a/src/raspberrypi/test/testing.h b/src/raspberrypi/test/testing.h new file mode 100644 index 00000000..8960fd07 --- /dev/null +++ b/src/raspberrypi/test/testing.h @@ -0,0 +1,229 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#pragma once + +#include + +#include "controllers/controller_manager.h" +#include "devices/device_factory.h" +#include "devices/primary_device.h" +#include "devices/scsihd.h" +#include "devices/scsihd_nec.h" +#include "devices/scsicd.h" +#include "devices/scsimo.h" +#include "devices/host_services.h" + +// Note that these global variables are convenient, +// but might cause issues because they are reused by all tests +extern DeviceFactory& device_factory; +extern ControllerManager& controller_manager; + +class MockBus : public BUS +{ +public: + + MOCK_METHOD(bool, Init, (mode_e), (override)); + MOCK_METHOD(void, Reset, (), (override)); + MOCK_METHOD(void, Cleanup, (), (override)); + MOCK_METHOD(bool, GetBSY, (), (const override)); + MOCK_METHOD(void, SetBSY, (bool), (override)); + MOCK_METHOD(bool, GetSEL, (), (const override)); + MOCK_METHOD(void, SetSEL, (bool), (override)); + MOCK_METHOD(bool, GetATN, (), (const override)); + MOCK_METHOD(void, SetATN, (bool), (override)); + MOCK_METHOD(bool, GetACK, (), (const override)); + MOCK_METHOD(void, SetACK, (bool), (override)); + MOCK_METHOD(bool, GetRST, (), (const override)); + MOCK_METHOD(void, SetRST, (bool), (override)); + MOCK_METHOD(bool, GetMSG, (), (const override)); + MOCK_METHOD(void, SetMSG, (bool), (override)); + MOCK_METHOD(bool, GetCD, (), (const override)); + MOCK_METHOD(void, SetCD, (bool), (override)); + MOCK_METHOD(bool, GetIO, (), (override)); + MOCK_METHOD(void, SetIO, (bool), (override)); + MOCK_METHOD(bool, GetREQ, (), (const override)); + MOCK_METHOD(void, SetREQ, (bool), (override)); + MOCK_METHOD(BYTE, GetDAT, (), (override)); + MOCK_METHOD(void, SetDAT, (BYTE), (override)); + MOCK_METHOD(bool, GetDP, (), (const, override)); + MOCK_METHOD(DWORD, Acquire,(), (override)); + MOCK_METHOD(int, CommandHandShake, (BYTE *), (override)); + MOCK_METHOD(int, ReceiveHandShake, (BYTE *, int), (override)); + MOCK_METHOD(int, SendHandShake, (BYTE *, int, int), (override)); + MOCK_METHOD(bool, GetSignal, (int), (const, override)); + MOCK_METHOD(void, SetSignal, (int, bool), (override)); + + MockBus() {} + ~MockBus() {} +}; + +class MockAbstractController : public AbstractController +{ +public: + + MOCK_METHOD(BUS::phase_t, Process, (int), (override)); + MOCK_METHOD(int, GetEffectiveLun, (), (const override)); + MOCK_METHOD(void, Error, (scsi_defs::sense_key, scsi_defs::asc, scsi_defs::status), (override)); + MOCK_METHOD(int, GetInitiatorId, (), (const override)); + MOCK_METHOD(void, SetUnit, (int), ()); + MOCK_METHOD(void, Connect, (int, BUS *), ()); + MOCK_METHOD(void, Status, (), ()); + MOCK_METHOD(void, DataIn, (), ()); + MOCK_METHOD(void, DataOut, (), ()); + MOCK_METHOD(void, BusFree, (), ()); + MOCK_METHOD(void, Selection, (), ()); + MOCK_METHOD(void, Command, (), ()); + MOCK_METHOD(void, MsgIn, (), ()); + MOCK_METHOD(void, MsgOut, (), ()); + MOCK_METHOD(void, Send, (), ()); + MOCK_METHOD(bool, XferMsg, (int), ()); + MOCK_METHOD(bool, XferIn, (BYTE *), ()); + MOCK_METHOD(bool, XferOut, (bool), ()); + MOCK_METHOD(void, ReceiveBytes, (), ()); + MOCK_METHOD(void, Execute, (), ()); + MOCK_METHOD(void, FlushUnit, (), ()); + MOCK_METHOD(void, Receive, (), ()); + MOCK_METHOD(bool, HasUnit, (), (const override)); + MOCK_METHOD(int, GetMaxLuns, (), (const override)); + MOCK_METHOD(void, SetByteTransfer, (bool), (override)); + MOCK_METHOD(void, ScheduleShutdown, (rascsi_shutdown_mode), (override)); + MOCK_METHOD(void, SetPhase, (BUS::phase_t), (override)); + MOCK_METHOD(void, Reset, (), (override)); + + MockAbstractController(int target_id) : AbstractController(nullptr, target_id) {} + ~MockAbstractController() {} +}; + +class MockScsiController : public ScsiController +{ +public: + + MOCK_METHOD(BUS::phase_t, Process, (int), (override)); + MOCK_METHOD(int, GetEffectiveLun, (), (const override)); + MOCK_METHOD(void, Error, (scsi_defs::sense_key, scsi_defs::asc, scsi_defs::status), (override)); + MOCK_METHOD(int, GetInitiatorId, (), (const override)); + MOCK_METHOD(void, SetUnit, (int), ()); + MOCK_METHOD(void, Connect, (int, BUS *), ()); + MOCK_METHOD(void, Status, (), ()); + MOCK_METHOD(void, DataIn, (), ()); + MOCK_METHOD(void, DataOut, (), ()); + MOCK_METHOD(void, BusFree, (), ()); + MOCK_METHOD(void, Selection, (), ()); + MOCK_METHOD(void, Command, (), ()); + MOCK_METHOD(void, MsgIn, (), ()); + MOCK_METHOD(void, MsgOut, (), ()); + MOCK_METHOD(void, Send, (), ()); + MOCK_METHOD(bool, XferMsg, (int), ()); + MOCK_METHOD(bool, XferIn, (BYTE *), ()); + MOCK_METHOD(bool, XferOut, (bool), ()); + MOCK_METHOD(void, ReceiveBytes, (), ()); + MOCK_METHOD(void, Execute, (), ()); + MOCK_METHOD(void, FlushUnit, (), ()); + MOCK_METHOD(void, Receive, (), ()); + MOCK_METHOD(bool, HasUnit, (), (const override)); + MOCK_METHOD(void, SetPhase, (BUS::phase_t), (override)); + MOCK_METHOD(void, Sleep, (), ()); + + FRIEND_TEST(PrimaryDeviceTest, UnitReady); + FRIEND_TEST(PrimaryDeviceTest, Inquiry); + FRIEND_TEST(PrimaryDeviceTest, RequestSense); + FRIEND_TEST(PrimaryDeviceTest, ReportLuns); + FRIEND_TEST(PrimaryDeviceTest, UnknownCommand); + + MockScsiController(BUS *bus, int target_id) : ScsiController(bus, target_id) {} + ~MockScsiController() {} +}; + +class MockPrimaryDevice : public PrimaryDevice +{ +public: + + MOCK_METHOD(vector, InquiryInternal, (), (const)); + + MockPrimaryDevice() : PrimaryDevice("test") {} + ~MockPrimaryDevice() {} + + // Make protected methods visible for testing + + void SetReady(bool ready) { PrimaryDevice::SetReady(ready); } + void SetReset(bool reset) { PrimaryDevice::SetReset(reset); } + void SetAttn(bool attn) { PrimaryDevice::SetAttn(attn); } + vector HandleInquiry(device_type type, scsi_level level, bool is_removable) const { + return PrimaryDevice::HandleInquiry(type, level, is_removable); + } +}; + +class MockModePageDevice : public ModePageDevice +{ +public: + + MockModePageDevice() : ModePageDevice("test") { } + ~MockModePageDevice() { } + + MOCK_METHOD(vector, InquiryInternal, (), (const)); + MOCK_METHOD(int, ModeSense6, (const DWORD *, BYTE *, int), ()); + MOCK_METHOD(int, ModeSense10, (const DWORD *, BYTE *, int), ()); + + void AddModePages(map>& pages, int page, bool) const override { + // Return dummy data for other pages than page 0 + if (page) { + vector buf(255); + pages[page] = buf; + } + } + + // Make protected methods visible for testing + // TODO Why does FRIEND_TEST not work for this method? + + int AddModePages(const DWORD *cdb, BYTE *buf, int max_length) { + return ModePageDevice::AddModePages(cdb, buf, max_length); + } +}; + +class MockSCSIHD : public SCSIHD +{ + FRIEND_TEST(ModePagesTest, SCSIHD_AddModePages); + + MockSCSIHD(const unordered_set& sector_sizes) : SCSIHD(sector_sizes, false) { }; + ~MockSCSIHD() { }; +}; + +class MockSCSIHD_NEC : public SCSIHD_NEC +{ + FRIEND_TEST(ModePagesTest, SCSIHD_NEC_AddModePages); + + MockSCSIHD_NEC(const unordered_set& sector_sizes) : SCSIHD_NEC(sector_sizes) { }; + ~MockSCSIHD_NEC() { }; +}; + +class MockSCSICD : public SCSICD +{ + FRIEND_TEST(ModePagesTest, SCSICD_AddModePages); + + MockSCSICD(const unordered_set& sector_sizes) : SCSICD(sector_sizes) { }; + ~MockSCSICD() { }; +}; + +class MockSCSIMO : public SCSIMO +{ + FRIEND_TEST(ModePagesTest, SCSIMO_AddModePages); + + MockSCSIMO(const unordered_set& sector_sizes, const unordered_map& geometries) + : SCSIMO(sector_sizes, geometries) { }; + ~MockSCSIMO() { }; +}; + +class MockHostServices : public HostServices +{ + FRIEND_TEST(ModePagesTest, HostServices_AddModePages); + + MockHostServices() { }; + ~MockHostServices() { }; +};