diff --git a/doc/rascsi.1 b/doc/rascsi.1
index de8d2430..102a5496 100644
--- a/doc/rascsi.1
+++ b/doc/rascsi.1
@@ -53,7 +53,7 @@ The optional block size, either 512, 1024, 2048 or 4096 bytes. Default size is 5
The default folder for image files. For files in this folder no absolute path needs to be specified. The initial default folder is '~/images'.
.TP
.BR \-L\fI " " \fILOG_LEVEL
-The rascsi log level (trace, debug, info, warn, err, critical, off). The default log level is 'info'.
+The rascsi log level (trace, debug, info, warn, err, off). The default log level is 'info'.
.TP
.BR \-P\fI " " \fIACCESS_TOKEN_FILE
Enable authentication and read the access token from the specified file. The access token file must be owned by root and must be readable by root only.
diff --git a/doc/rascsi_man_page.txt b/doc/rascsi_man_page.txt
index 48275194..aace3a4b 100644
--- a/doc/rascsi_man_page.txt
+++ b/doc/rascsi_man_page.txt
@@ -1,92 +1,120 @@
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
-!! ------ The native file is rascsi.1. Re-run 'make docs' after updating
-
-
-rascsi(1) General Commands Manual rascsi(1)
+!! ------ The native file is rascsi.1. Re-run 'make docs' after updating\n\n
+rascsi(1) General Commands Manual rascsi(1)
NAME
rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins
SYNOPSIS
- rascsi [-F FOLDER] [-L LOG_LEVEL] [-P ACCESS_TOKEN_FILE] [-R SCAN_DEPTH] [-h] [-n VENDOR:PRODUCT:REVISION] [-p PORT] [-r
- RESERVED_IDS] [-n TYPE] [-v] [-z LOCALE] [-IDn:[u] FILE] [-HDn[:u] FILE]...
+ rascsi [-F FOLDER] [-L LOG_LEVEL] [-P ACCESS_TOKEN_FILE] [-R
+ SCAN_DEPTH] [-h] [-n VENDOR:PRODUCT:REVISION] [-p PORT] [-r RE‐
+ SERVED_IDS] [-n TYPE] [-v] [-z LOCALE] [-IDn:[u] FILE] [-HDn[:u]
+ FILE]...
DESCRIPTION
rascsi emulates SCSI devices using the Raspberry Pi GPIO pins.
- In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) devices can be specified. The number (n) after the ID or HD iden‐
- tifier 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.
+ 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.
+ RaSCSI will determine the type of device based upon the file extension
+ of the FILE argument.
hd1: SCSI Hard Disk image (generic, non-removable, SCSI-1)
hds: SCSI Hard Disk image (generic, non-removable)
hdr: SCSI Hard Disk image (generic, removable)
- hdn: SCSI Hard Disk image (NEC compatible - only used with PC-98 computers)
- hdi: SCSI Hard Disk image (Anex86 proprietary - only used with PC-98 computers)
- nhd: SCSI Hard Disk image (T98Next proprietary - only used with PC-98 computers)
- hda: SCSI Hard Disk image (Apple compatible - typically used with Macintosh computers)
- mos: SCSI Magneto-Optical image (generic - typically used with NeXT, X68000, etc.)
+ hdn: SCSI Hard Disk image (NEC compatible - only used with PC-98
+ computers)
+ hdi: SCSI Hard Disk image (Anex86 proprietary - only used with
+ PC-98 computers)
+ nhd: SCSI Hard Disk image (T98Next proprietary - only used with
+ PC-98 computers)
+ hda: SCSI Hard Disk image (Apple compatible - typically used with
+ Macintosh computers)
+ mos: SCSI Magneto-Optical image (generic - typically used with
+ NeXT, X68000, etc.)
iso: SCSI CD-ROM or DVD-ROM image (ISO 9660 image)
- For example, if you want to specify an Apple-compatible HD image on ID 0, you can use the following command:
+ 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 allow 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 initial‐
- ized, the rasctl utility can be used to send commands.
+ Once RaSCSI starts, it will open a socket (default port is 6868) to al‐
+ 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, you can kill it using an INT signal.
+ 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, either 512, 1024, 2048 or 4096 bytes. Default size is 512 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 absolute path needs to be specified. The initial de‐
- fault folder is '~/images'.
+ The default folder for image files. For files in this folder no
+ absolute path needs to be specified. The initial default folder
+ is '~/images'.
-L LOG_LEVEL
- The rascsi log level (trace, debug, info, warn, err, critical, off). The default log level is 'info'.
+ The rascsi log level (trace, debug, info, warn, err, off). The
+ default log level is 'info'.
-P ACCESS_TOKEN_FILE
- Enable authentication and read the access token from the specified file. The access token file must be owned by root
- and must be readable by root only.
+ Enable authentication and read the access token from the speci‐
+ fied file. The access token file must be owned by root and must
+ be readable by root only.
-R SCAN_DEPTH
- Scan for image files recursively, up to a depth of SCAN_DEPTH. Depth 0 means to ignore any folders within the de‐
- fault image filder. Be careful when using this option with many sub-folders in the default image folder. The default
- depth is 1.
+ Scan for image files recursively, up to a depth of SCAN_DEPTH.
+ Depth 0 means to ignore any folders within the default image
+ filder. Be careful when using this option with many sub-folders
+ in the default image folder. The default depth is 1.
-h Show a help page.
-n VENDOR:PRODUCT:REVISION
- Set 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 automatically applied. Once set the name of a device cannot be changed.
+ Set the vendor, product and revision for the device, to be re‐
+ turned with the INQUIRY data. A complete set of name components
+ must be provided. VENDOR may have up to 8, PRODUCT up to 16, RE‐
+ VISION up to 4 characters. Padding with blanks to the maxium
+ length is automatically applied. Once set the name of a device
+ cannot be changed.
-p PORT
The rascsi server port, default is 6868.
-r RESERVED_IDS
- Comma-separated list of IDs to reserve. Pass an empty list in order to not reserve anything. -p TYPE The optional
- case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP, SCLP, SCHS). If no type is specified for de‐
- vices that support an image file, rascsi tries to derive the type from the file extension.
+ Comma-separated list of IDs to reserve. Pass an empty list in
+ order to not reserve anything. -p TYPE The optional case-insen‐
+ sitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP,
+ SCLP, SCHS). If no type is specified for devices that support an
+ image file, rascsi tries to derive the type from the file exten‐
+ sion.
-v Display the rascsi version.
-z LOCALE
- Overrides the default locale for client-faces error messages. The client can override the locale.
+ Overrides the default locale for client-faces error messages.
+ The client can override the locale.
-IDn[:u] FILE
- n is the SCSI ID number (0-7). u (0-31) is the optional LUN (logical unit). The default LUN is 0.
+ n is the SCSI ID number (0-7). u (0-31) is the optional LUN
+ (logical unit). The default LUN is 0.
- 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. "inter‐
- faces=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".
+ 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 priori‐
+ tized 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".
FILE is the name of the image file to use for the SCSI device.
@@ -94,22 +122,26 @@ EXAMPLES
Launch RaSCSI with no emulated drives attached:
rascsi
- Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID 2
+ Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID
+ 2
rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso
- Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw device file /dev/hdb (e.g. a USB stick) and a DaynaPort
- network adapter as ID 6:
+ Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw de‐
+ vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter
+ as ID 6:
rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp daynaport
To create an empty, 100MiB HD image, use the following command:
dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800
- In case the fallocate command is available a much faster alternative to the dd command is:
+ In case the fallocate command is available a much faster alternative to
+ the dd command is:
fallocate -l 104857600 /path/to/newimage.hda
SEE ALSO
rasctl(1), scsimon(1), rasdump(1)
- Full documentation is available at:
+ Full documentation is available at:
+
- rascsi(1)
+ rascsi(1)
diff --git a/doc/rasctl.1 b/doc/rasctl.1
index a644934a..a75f232d 100644
--- a/doc/rasctl.1
+++ b/doc/rasctl.1
@@ -61,7 +61,7 @@ Set the default image folder.
Gets the list of reserved device IDs.
.TP
.BR \-L\fI " "\fILOG_LEVEL
-Set the rascsi log level (trace, debug, info, warn, err, critical, off).
+Set the rascsi log level (trace, debug, info, warn, err, off).
.TP
.BR \-h\fI " " \fIHOST
The rascsi host to connect to, default is 'localhost'.
diff --git a/doc/rasctl_man_page.txt b/doc/rasctl_man_page.txt
index ae2d31c9..d7ed524e 100644
--- a/doc/rasctl_man_page.txt
+++ b/doc/rasctl_man_page.txt
@@ -1,31 +1,32 @@
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
-!! ------ The native file is rasctl.1. Re-run 'make docs' after updating
-
-
-rascsi(1) General Commands Manual rascsi(1)
+!! ------ The native file is rasctl.1. Re-run 'make docs' after updating\n\n
+rascsi(1) General Commands Manual rascsi(1)
NAME
rasctl - Sends management commands to the rascsi process
SYNOPSIS
- rasctl -e | -l | -m | -o | -s | -v | -D | -I | -L | -O | -P | -T | -V | -X | [-C FILENAME:FILESIZE] [-E FILENAME] [-F IM‐
- AGE_FOLDER] [-R CURRENT_NAME:NEW_NAME] [-c CMD] [-f FILE|PARAM] [-g LOG_LEVEL] [-h HOST] [-i ID [-n NAME] [-p PORT] [-r RE‐
- SERVED_IDS] [-t TYPE] [-u UNIT] [-x CURRENT_NAME:NEW_NAME] [-z LOCALE]
+ rasctl -e | -l | -m | -o | -s | -v | -D | -I | -L | -O | -P | -T | -V |
+ -X | [-C FILENAME:FILESIZE] [-E FILENAME] [-F IMAGE_FOLDER] [-R CUR‐
+ RENT_NAME:NEW_NAME] [-c CMD] [-f FILE|PARAM] [-g LOG_LEVEL] [-h HOST]
+ [-i ID [-n NAME] [-p PORT] [-r RESERVED_IDS] [-t TYPE] [-u UNIT] [-x
+ CURRENT_NAME:NEW_NAME] [-z LOCALE]
DESCRIPTION
- rasctl sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the de‐
- vices.
+ rasctl sends commands to the rascsi process to make configuration ad‐
+ justments at runtime or to check the status of the devices.
Either the -i or -l option should be specified at one time. Not both.
You do NOT need root privileges to use rasctl.
- Note: The command and type arguments are case insensitive. Only the first letter of the command/type is evaluated by the
- tool.
+ Note: The command and type arguments are case insensitive. Only the
+ first letter of the command/type is evaluated by the tool.
OPTIONS
-C FILENAME:FILESIZE
- Create an image file in the default image folder with the specified name and size in bytes.
+ Create an image file in the default image folder with the speci‐
+ fied name and size in bytes.
-D Detach all devices.
@@ -38,22 +39,27 @@ OPTIONS
-I Gets the list of reserved device IDs.
-L LOG_LEVEL
- Set the rascsi log level (trace, debug, info, warn, err, critical, off).
+ Set the rascsi log level (trace, debug, info, warn, err, off).
-h HOST
The rascsi host to connect to, default is 'localhost'.
-e List all images files in the default image folder.
- -N Lists all available network interfaces provided that they are up.
+ -N Lists all available network interfaces provided that they are
+ up.
- -O Display the available rascsi server log levels and the current log level.
+ -O Display the available rascsi server log levels and the current
+ log level.
- -P Prompt for the access token in case rascsi requires authentication.
+ -P Prompt for the access token in case rascsi requires authentica‐
+ tion.
- -l List all of the devices that are currently being emulated by RaSCSI, as well as their current status.
+ -l List all of the devices that are currently being emulated by
+ RaSCSI, as well as their current status.
- -m List all file extensions recognized by RaSCSI and the device types they map to.
+ -m List all file extensions recognized by RaSCSI and the device
+ types they map to.
-o Display operation meta data information.
@@ -64,9 +70,11 @@ OPTIONS
The rascsi port to connect to, default is 6868.
-r RESERVED_IDS
- Comma-separated list of IDs to reserve. Pass an empty list in order to not reserve anything.
+ Comma-separated list of IDs to reserve. Pass an empty list in
+ order to not reserve anything.
- -s Display server-side settings like available images or supported device types.
+ -s Display server-side settings like available images or supported
+ device types.
-T Display all device types and their properties.
@@ -92,23 +100,28 @@ OPTIONS
d(etach): Detach disk
i(nsert): Insert media (removable media devices only)
e(ject): Eject media (removable media devices only)
- p(rotect): Write protect the medium (not for CD-ROMs, which are always read-only)
- u(nprotect): Remove write protection from the medium (not for CD-ROMs, which are always read-only)
+ p(rotect): Write protect the medium (not for CD-ROMs, which
+ are always read-only)
+ u(nprotect): Remove write protection from the medium (not for
+ CD-ROMs, which are always read-only)
s(how): Display device information
eject, protect and unprotect are idempotent.
-b BLOCK_SIZE
- The optional block size, either 512, 1024, 2048 or 4096 bytes. The default size is 512 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 parameter for a non-disk device. See the rascsi(1) man
- page for permitted file types.
+ 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‐
+ mitted file types.
-t TYPE
- 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 types there are shortcuts (only the first letter is
- required):
+ 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
+ types there are shortcuts (only the first letter is required):
hd: SCSI hard disk drive
rm: SCSI removable media drive
cd: CD-ROM
@@ -119,13 +132,17 @@ OPTIONS
services: Host services device
-n VENDOR:PRODUCT:REVISION
- The vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name compo‐
- nents 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 automatically applied. Once set the name of a device cannot be changed.
+ 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
+ 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 controller. (This is not common)
+ 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
Show a listing of all of the SCSI devices and their current status.
@@ -138,13 +155,14 @@ EXAMPLES
| 0 | 1 | SCHD | /home/pi/harddisk.hda
+----+-----+------+-------------------------------------
- Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the contents of the file system image "HDIIM‐
- AGE0.HDS".
+ Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with
+ the contents of the file system image "HDIIMAGE0.HDS".
rasctl -i 0 -f HDIIMAGE0.HDS
SEE ALSO
rascsi(1), scsimon(1), rasdump(1)
- Full documentation is available at:
+ Full documentation is available at:
+
- rascsi(1)
+ rascsi(1)
diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile
index 5a09b4aa..9a7f25a4 100644
--- a/src/raspberrypi/Makefile
+++ b/src/raspberrypi/Makefile
@@ -1,15 +1,11 @@
.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:
## arm-linux-gnueabihf-
CROSS_COMPILE =
-CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
## DEBUG=1 : A Debug build includes the debugger symbols
@@ -30,6 +26,8 @@ ifeq ("$(shell uname -s)","Linux")
CXXFLAGS += -Wno-psabi
endif
+# Depending on the GCC version the compilation flags differ
+GCCVERSION10 := $(shell expr `$(CXX) -dumpversion` \>= 10)
CXXFLAGS += -std=c++17 -iquote . -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE -MD -MP
@@ -186,7 +184,7 @@ lcov: test
docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt
-$(SRC_SHARED): $(SRC_PROTOBUF)
+$(SRC_SHARED) $(SRC_RASCSI_CORE) $(SRC_RASCTL_CORE): $(OBJ_PROTOBUF)
$(BINDIR)/$(RASCSI): $(SRC_PROTOBUF) $(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(OBJ_SHARED) $(OBJ_PROTOBUF) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(OBJ_SHARED) $(OBJ_PROTOBUF) -lpthread -lpcap -lprotobuf -lstdc++fs
diff --git a/src/raspberrypi/command_util.cpp b/src/raspberrypi/command_util.cpp
deleted file mode 100644
index 716b03c8..00000000
--- a/src/raspberrypi/command_util.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-//---------------------------------------------------------------------------
-//
-// SCSI Target Emulator RaSCSI Reloaded
-// for Raspberry Pi
-//
-// Copyright (C) 2021-2022 Uwe Seimet
-//
-//---------------------------------------------------------------------------
-
-#include "log.h"
-#include "rascsi_interface.pb.h"
-#include "protobuf_serializer.h"
-#include "command_util.h"
-#include
-
-using namespace std;
-using namespace rascsi_interface;
-
-#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
-
-static const char COMPONENT_SEPARATOR = ':';
-static const char KEY_VALUE_SEPARATOR = '=';
-
-void command_util::ParseParameters(PbDeviceDefinition& device, const string& params)
-{
- if (params.empty()) {
- return;
- }
-
- // Old style parameters, for backwards compatibility only.
- // Only one of these parameters will be used by rascsi, depending on the device type.
- if (params.find(KEY_VALUE_SEPARATOR) == string::npos) {
- AddParam(device, "file", params);
- if (params != "bridge" && params != "daynaport" && params != "printer" && params != "services") {
- AddParam(device, "interfaces", params);
- }
-
- return;
- }
-
- stringstream ss(params);
- string p;
- while (getline(ss, p, COMPONENT_SEPARATOR)) {
- if (!p.empty()) {
- size_t separator_pos = p.find(KEY_VALUE_SEPARATOR);
- if (separator_pos != string::npos) {
- AddParam(device, p.substr(0, separator_pos), string_view(p).substr(separator_pos + 1));
- }
- }
- }
-}
-
-string command_util::GetParam(const PbCommand& command, const string& key)
-{
- const auto& it = command.params().find(key);
- return it != command.params().end() ? it->second : "";
-}
-
-string command_util::GetParam(const PbDeviceDefinition& device, const string& key)
-{
- const auto& it = device.params().find(key);
- return it != device.params().end() ? it->second : "";
-}
-
-void command_util::AddParam(PbCommand& command, const string& key, string_view value)
-{
- if (!key.empty() && !value.empty()) {
- auto& map = *command.mutable_params();
- map[key] = value;
- }
-}
-
-void command_util::AddParam(PbDevice& device, const string& key, string_view value)
-{
- if (!key.empty() && !value.empty()) {
- auto& map = *device.mutable_params();
- map[key] = value;
- }
-}
-
-void command_util::AddParam(PbDeviceDefinition& device, const string& key, string_view value)
-{
- if (!key.empty() && !value.empty()) {
- auto& map = *device.mutable_params();
- map[key] = value;
- }
-}
diff --git a/src/raspberrypi/controllers/abstract_controller.cpp b/src/raspberrypi/controllers/abstract_controller.cpp
index a9d08b13..d33dc65a 100644
--- a/src/raspberrypi/controllers/abstract_controller.cpp
+++ b/src/raspberrypi/controllers/abstract_controller.cpp
@@ -11,6 +11,13 @@
#include "devices/primary_device.h"
#include "abstract_controller.h"
+void AbstractController::AllocateCmd(size_t size)
+{
+ if (size > ctrl.cmd.size()) {
+ ctrl.cmd.resize(size);
+ }
+}
+
void AbstractController::AllocateBuffer(size_t size)
{
if (size > ctrl.buffer.size()) {
@@ -18,6 +25,15 @@ void AbstractController::AllocateBuffer(size_t size)
}
}
+void AbstractController::SetByteTransfer(bool b)
+{
+ is_byte_transfer = b;
+
+ if (!is_byte_transfer) {
+ bytes_to_transfer = 0;
+ }
+}
+
unordered_set> AbstractController::GetDevices() const
{
unordered_set> devices;
@@ -88,7 +104,7 @@ void AbstractController::ProcessPhase()
default:
LOGERROR("Cannot process phase %s", BUS::GetPhaseStrRaw(GetPhase()))
- throw scsi_exception();
+ throw scsi_exception(sense_key::ABORTED_COMMAND);
break;
}
}
@@ -105,9 +121,16 @@ bool AbstractController::AddDevice(shared_ptr device)
return true;
}
-bool AbstractController::DeleteDevice(const shared_ptr device)
+bool AbstractController::RemoveDevice(const shared_ptr device)
{
- return luns.erase(device->GetLun()) == 1;
+ const size_t count = luns.erase(device->GetLun());
+ assert (count == 1);
+
+ if (count == 1) {
+ device->SetController(nullptr);
+ }
+
+ return count == 1;
}
bool AbstractController::HasDeviceForLun(int lun) const
diff --git a/src/raspberrypi/controllers/abstract_controller.h b/src/raspberrypi/controllers/abstract_controller.h
index 0007662d..0c0c3809 100644
--- a/src/raspberrypi/controllers/abstract_controller.h
+++ b/src/raspberrypi/controllers/abstract_controller.h
@@ -41,7 +41,9 @@ public:
};
using ctrl_t = struct _ctrl_t {
- vector cmd; // Command data, dynamically allocated per received command
+ // Command data, dynamically resized if required
+ vector cmd = vector(16);
+
scsi_defs::status status; // Status data
int message; // Message data
@@ -60,7 +62,6 @@ public:
scsi_defs::status = scsi_defs::status::CHECK_CONDITION) = 0;
virtual void Reset();
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;
@@ -74,7 +75,7 @@ public:
unordered_set> GetDevices() const;
shared_ptr GetDeviceForLun(int) const;
bool AddDevice(shared_ptr);
- bool DeleteDevice(const shared_ptr);
+ bool RemoveDevice(const shared_ptr);
bool HasDeviceForLun(int) const;
int ExtractInitiatorId(int) const;
@@ -84,18 +85,23 @@ public:
void SetStatus(scsi_defs::status s) { ctrl.status = s; }
uint32_t GetLength() const { return ctrl.length; }
+ bool IsByteTransfer() const { return is_byte_transfer; }
+ void SetByteTransfer(bool);
+
protected:
- scsi_defs::scsi_command GetOpcode() const { return (scsi_defs::scsi_command)ctrl.cmd[0]; }
+ scsi_defs::scsi_command GetOpcode() const { return static_cast(ctrl.cmd[0]); }
int GetLun() const { return (ctrl.cmd[1] >> 5) & 0x07; }
void ProcessPhase();
- vector& InitCmd(int size) { ctrl.cmd.resize(size); return ctrl.cmd; }
+ vector& GetCmd() { return ctrl.cmd; }
+ void AllocateCmd(size_t);
- bool HasValidLength() const { return ctrl.length != 0; }
+ bool HasValidLength() const { return ctrl.length != 0; }
int GetOffset() const { return ctrl.offset; }
void ResetOffset() { ctrl.offset = 0; }
+ void SetLength(uint32_t l) { ctrl.length = l; }
void UpdateOffsetAndLength() { ctrl.offset += ctrl.length; ctrl.length = 0; }
private:
@@ -106,7 +112,9 @@ private:
int max_luns;
- ctrl_t ctrl = {};
+ bool is_byte_transfer = false;
+ uint32_t bytes_to_transfer = 0;
+ ctrl_t ctrl = {};
ctrl_t* GetCtrl() { return &ctrl; }
};
diff --git a/src/raspberrypi/controllers/controller_manager.cpp b/src/raspberrypi/controllers/controller_manager.cpp
index ce93a4da..424e4ba6 100644
--- a/src/raspberrypi/controllers/controller_manager.cpp
+++ b/src/raspberrypi/controllers/controller_manager.cpp
@@ -17,18 +17,22 @@ using namespace std;
bool ControllerManager::AttachToScsiController(int id, shared_ptr device)
{
auto controller = FindController(id);
- if (controller == nullptr) {
+ if (controller != nullptr) {
+ return controller->AddDevice(device);
+ }
+
+ // If there is no LUN yet the first LUN must be LUN 0
+ if (device->GetLun() == 0) {
controller = make_shared(bus, id);
+
if (controller->AddDevice(device)) {
controllers[id] = controller;
return true;
}
-
- return false;
}
- return controller->AddDevice(device);
+ return false;
}
bool ControllerManager::DeleteController(shared_ptr controller)
@@ -79,7 +83,7 @@ void ControllerManager::ResetAllControllers() const
shared_ptr ControllerManager::GetDeviceByIdAndLun(int id, int lun) const
{
- if (const auto controller = FindController(id); controller != nullptr) {
+ if (const auto& controller = FindController(id); controller != nullptr) {
return controller->GetDeviceForLun(lun);
}
diff --git a/src/raspberrypi/controllers/scsi_controller.cpp b/src/raspberrypi/controllers/scsi_controller.cpp
index c5aa7cf8..483e72e9 100644
--- a/src/raspberrypi/controllers/scsi_controller.cpp
+++ b/src/raspberrypi/controllers/scsi_controller.cpp
@@ -45,8 +45,7 @@ void ScsiController::Reset()
scsi.msc = 0;
scsi.msb = {};
- is_byte_transfer = false;
- bytes_to_transfer = 0;
+ SetByteTransfer(false);
}
BUS::phase_t ScsiController::Process(int id)
@@ -81,8 +80,6 @@ BUS::phase_t ScsiController::Process(int id)
}
catch(const scsi_exception&) {
// 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();
@@ -116,15 +113,14 @@ void ScsiController::BusFree()
identified_lun = -1;
- is_byte_transfer = false;
- bytes_to_transfer = 0;
+ SetByteTransfer(false);
// When the bus is free RaSCSI or the Pi may be shut down.
// This code has to be executed in the bus free phase and thus has to be located in the controller.
switch(shutdown_mode) {
case rascsi_shutdown_mode::STOP_RASCSI:
LOGINFO("RaSCSI shutdown requested")
- exit(0);
+ exit(EXIT_SUCCESS);
break;
case rascsi_shutdown_mode::STOP_PI:
@@ -209,7 +205,7 @@ void ScsiController::Command()
return;
}
- InitCmd(command_byte_count);
+ AllocateCmd(command_byte_count);
// Command data transfer
stringstream s;
@@ -219,7 +215,7 @@ void ScsiController::Command()
}
LOGTRACE("%s CDB=$%s",__PRETTY_FUNCTION__, s.str().c_str())
- ctrl.length = 0;
+ SetLength(0);
Execute();
}
@@ -250,7 +246,13 @@ void ScsiController::Execute()
// 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));
+ if (!HasDeviceForLun(0)) {
+ LOGERROR("No LUN 0 for device %d", GetTargetId())
+
+ GetBuffer().data()[0] = 0x7f;
+
+ return;
+ }
lun = 0;
}
@@ -263,17 +265,22 @@ void ScsiController::Execute()
device->SetStatusCode(0);
}
- try {
- if (!device->Dispatch(GetOpcode())) {
- LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetTargetId(), lun, (int)GetOpcode())
-
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
- }
+ if (!device->CheckReservation(initiator_id, GetOpcode(), ctrl.cmd[4] & 0x01)) {
+ Error(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION, status::RESERVATION_CONFLICT);
}
- catch(const scsi_exception& e) { //NOSONAR This exception is handled properly
- Error(e.get_sense_key(), e.get_asc(), e.get_status());
+ else {
+ try {
+ if (!device->Dispatch(GetOpcode())) {
+ LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetTargetId(), lun, (int)GetOpcode())
- // Fall through
+ throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
+ }
+ }
+ catch(const scsi_exception& e) { //NOSONAR This exception is handled properly
+ Error(e.get_sense_key(), e.get_asc());
+
+ // Fall through
+ }
}
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
@@ -307,7 +314,7 @@ void ScsiController::Status()
// Data transfer is 1 byte x 1 block
ResetOffset();
- ctrl.length = 1;
+ SetLength(1);
ctrl.blocks = 1;
GetBuffer()[0] = (BYTE)GetStatus();
@@ -328,14 +335,10 @@ void ScsiController::MsgIn()
bus.SetCD(true);
bus.SetIO(true);
- // length, blocks are already set
- assert(HasValidLength());
- assert(ctrl.blocks > 0);
ResetOffset();
return;
}
- LOGTRACE("%s Transitioning to Send()", __PRETTY_FUNCTION__)
Send();
}
@@ -361,7 +364,7 @@ void ScsiController::MsgOut()
// Data transfer is 1 byte x 1 block
ResetOffset();
- ctrl.length = 1;
+ SetLength(1);
ctrl.blocks = 1;
return;
@@ -392,8 +395,6 @@ void ScsiController::DataIn()
bus.SetCD(false);
bus.SetIO(true);
- // length, blocks are already set
- assert(ctrl.blocks > 0);
ResetOffset();
return;
@@ -453,7 +454,16 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
int lun = GetEffectiveLun();
if (!HasDeviceForLun(lun) || asc == asc::INVALID_LUN) {
- assert(HasDeviceForLun(0));
+ if (!HasDeviceForLun(0)) {
+ LOGERROR("No LUN 0 for device %d", GetTargetId())
+
+ SetStatus(status);
+ ctrl.message = 0x00;
+
+ Status();
+
+ return;
+ }
lun = 0;
}
@@ -549,7 +559,7 @@ void ScsiController::Send()
// status phase
case BUS::phase_t::status:
// Message in phase
- ctrl.length = 1;
+ SetLength(1);
ctrl.blocks = 1;
GetBuffer()[0] = (BYTE)ctrl.message;
MsgIn();
@@ -563,7 +573,7 @@ void ScsiController::Send()
void ScsiController::Receive()
{
- if (is_byte_transfer) {
+ if (IsByteTransfer()) {
ReceiveBytes();
return;
}
@@ -755,20 +765,15 @@ bool ScsiController::XferOut(bool cont)
{
assert(IsDataOut());
- if (!is_byte_transfer) {
+ if (!IsByteTransfer()) {
return XferOutBlockOriented(cont);
}
- is_byte_transfer = false;
+ const uint32_t length = bytes_to_transfer;
+ SetByteTransfer(false);
- if (auto device = GetDeviceForLun(GetEffectiveLun());
- device != nullptr && GetOpcode() == scsi_command::eCmdWrite6) {
- return device->WriteByteSequence(GetBuffer(), bytes_to_transfer);
- }
-
- LOGWARN("%s Received unexpected command $%02X", __PRETTY_FUNCTION__, (int)GetOpcode())
-
- return false;
+ auto device = GetDeviceForLun(GetEffectiveLun());
+ return device != nullptr ? device->WriteByteSequence(GetBuffer(), length) : false;
}
void ScsiController::DataOutNonBlockOriented()
@@ -791,7 +796,7 @@ void ScsiController::DataOutNonBlockOriented()
// TODO Try to get rid of this cast
if (auto device = dynamic_pointer_cast(GetDeviceForLun(GetEffectiveLun()));
device != nullptr) {
- device->ModeSelect(ctrl.cmd, GetBuffer(), GetOffset());
+ device->ModeSelect(GetOpcode(), ctrl.cmd, GetBuffer(), GetOffset());
}
else {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
@@ -833,7 +838,7 @@ bool ScsiController::XferIn(vector& buf)
case scsi_command::eCmdRead16:
// Read from disk
try {
- ctrl.length = (dynamic_pointer_cast(GetDeviceForLun(lun)))->Read(ctrl.cmd, buf, ctrl.next);
+ SetLength(dynamic_pointer_cast(GetDeviceForLun(lun))->Read(ctrl.cmd, buf, ctrl.next));
}
catch(const scsi_exception&) {
// If there is an error, go to the status phase
@@ -873,10 +878,10 @@ bool ScsiController::XferOutBlockOriented(bool cont)
case scsi_command::eCmdModeSelect6:
case scsi_command::eCmdModeSelect10:
try {
- disk->ModeSelect(ctrl.cmd, GetBuffer(), GetOffset());
+ disk->ModeSelect(GetOpcode(), ctrl.cmd, GetBuffer(), GetOffset());
}
catch(const scsi_exception& e) {
- Error(e.get_sense_key(), e.get_asc(), e.get_status());
+ Error(e.get_sense_key(), e.get_asc());
return false;
}
break;
@@ -888,7 +893,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
case scsi_command::eCmdVerify10:
case scsi_command::eCmdVerify16:
{
- // Special case Write function for brige
+ // Special case Write function for bridge
// TODO This class must not know about SCSIBR
if (auto bridge = dynamic_pointer_cast(disk); bridge) {
if (!bridge->WriteBytes(ctrl.cmd, GetBuffer(), ctrl.length)) {
@@ -914,7 +919,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
disk->Write(ctrl.cmd, GetBuffer(), ctrl.next - 1);
}
catch(const scsi_exception& e) {
- Error(e.get_sense_key(), e.get_asc(), e.get_status());
+ Error(e.get_sense_key(), e.get_asc());
// Write failed
return false;
@@ -928,7 +933,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
// Check the next block
try {
- ctrl.length = disk->WriteCheck(ctrl.next - 1);
+ SetLength(disk->WriteCheck(ctrl.next - 1));
}
catch(const scsi_exception&) {
// Cannot write
@@ -981,6 +986,9 @@ void ScsiController::ParseMessage()
if (message_type == 0x0C) {
LOGTRACE("Received BUS DEVICE RESET message")
scsi.syncoffset = 0;
+ if (auto device = GetDeviceForLun(identified_lun); device != nullptr) {
+ device->DiscardReservation();
+ }
BusFree();
return;
}
@@ -995,7 +1003,7 @@ void ScsiController::ParseMessage()
// Check only when synchronous transfer is possible
if (!scsi.syncenable || scsi.msb[i + 2] != 0x01) {
- ctrl.length = 1;
+ SetLength(1);
ctrl.blocks = 1;
GetBuffer()[0] = 0x07;
MsgIn();
@@ -1013,7 +1021,7 @@ void ScsiController::ParseMessage()
}
// STDR response message generation
- ctrl.length = 5;
+ SetLength(5);
ctrl.blocks = 1;
GetBuffer()[0] = 0x01;
GetBuffer()[1] = 0x03;
@@ -1035,7 +1043,7 @@ void ScsiController::ProcessMessage()
if (bus.GetATN()) {
// Data transfer is 1 byte x 1 block
ResetOffset();
- ctrl.length = 1;
+ SetLength(1);
ctrl.blocks = 1;
return;
}
diff --git a/src/raspberrypi/controllers/scsi_controller.h b/src/raspberrypi/controllers/scsi_controller.h
index 52fdd5ce..5044d647 100644
--- a/src/raspberrypi/controllers/scsi_controller.h
+++ b/src/raspberrypi/controllers/scsi_controller.h
@@ -67,12 +67,23 @@ public:
scsi_defs::status status = scsi_defs::status::CHECK_CONDITION) override;
int GetInitiatorId() const override { return initiator_id; }
- void SetByteTransfer(bool b) override { is_byte_transfer = b; }
+ // Phases
+ void BusFree() override;
+ void Selection() override;
+ void Command() override;
+ void MsgIn() override;
+ void MsgOut() override;
void Status() override;
void DataIn() override;
void DataOut() override;
+ // TODO Make non-virtual private as soon as SysTimer calls do not segfault anymore on a regular PC,
+ // e.g. by using ifdef __arm__. Currently the unit tests require this method to be public.
+ virtual void Execute();
+
+ void ScheduleShutdown(rascsi_shutdown_mode mode) override { shutdown_mode = mode; }
+
private:
// Execution start time
@@ -84,16 +95,6 @@ private:
// The LUN from the IDENTIFY message
int identified_lun = -1;
- bool is_byte_transfer = false;
- uint32_t bytes_to_transfer = 0;
-
- // Phases
- void BusFree() override;
- void Selection() override;
- void Command() override;
- void MsgIn() override;
- void MsgOut() override;
-
// Data transfer
void Send();
bool XferMsg(int);
@@ -102,7 +103,6 @@ private:
bool XferOutBlockOriented(bool);
void ReceiveBytes();
- void Execute();
void DataOutNonBlockOriented();
void Receive();
@@ -110,8 +110,6 @@ private:
void ParseMessage();
void ProcessMessage();
- void ScheduleShutdown(rascsi_shutdown_mode mode) override { shutdown_mode = mode; }
-
void Sleep();
scsi_t scsi = {};
diff --git a/src/raspberrypi/devices/device.cpp b/src/raspberrypi/devices/device.cpp
index 83862157..7a68e06a 100644
--- a/src/raspberrypi/devices/device.cpp
+++ b/src/raspberrypi/devices/device.cpp
@@ -16,10 +16,8 @@
using namespace std;
-Device::Device(const string& type, int lun) : type(type), lun(lun)
+Device::Device(PbDeviceType type, int lun) : type(type), lun(lun)
{
- assert(type.length() == 4);
-
ostringstream os;
os << setw(2) << setfill('0') << rascsi_major_version << setw(2) << setfill('0') << rascsi_minor_version;
revision = os.str();
@@ -34,7 +32,7 @@ void Device::Reset()
void Device::SetProtected(bool b)
{
- if (!read_only) {
+ if (protectable && !read_only) {
write_protected = b;
}
}
diff --git a/src/raspberrypi/devices/device.h b/src/raspberrypi/devices/device.h
index edda7904..5a8fd383 100644
--- a/src/raspberrypi/devices/device.h
+++ b/src/raspberrypi/devices/device.h
@@ -9,16 +9,18 @@
#pragma once
+#include "rascsi_interface.pb.h"
#include
#include
using namespace std;
+using namespace rascsi_interface;
class Device //NOSONAR The number of fields and methods is justified, the complexity is low
{
const string DEFAULT_VENDOR = "RaSCSI";
- string type;
+ PbDeviceType type;
bool ready = false;
bool reset = false;
@@ -42,9 +44,12 @@ class Device //NOSONAR The number of fields and methods is justified, the comple
bool lockable = false;
bool locked = false;
- // Device can be created with parameters
+ // A device can be created with parameters
bool supports_params = false;
+ // A device can support an image file
+ bool supports_file = false;
+
// Immutable LUN
int lun;
@@ -74,18 +79,25 @@ protected:
bool IsAttn() const { return attn; }
void SetAttn(bool b) { attn = b; }
+ void SetRemovable(bool b) { removable = b; }
+ void SetStoppable(bool b) { stoppable = b; }
+ void SetStopped(bool b) { stopped = b; }
+ void SetLockable(bool b) { lockable = b; }
+ void SetLocked(bool b) { locked = b; }
+
int GetStatusCode() const { return status_code; }
string GetParam(const string&) const;
void SetParams(const unordered_map&);
- Device(const string&, int);
+ Device(PbDeviceType, int);
public:
virtual ~Device() = default;
- const string& GetType() const { return type; }
+ PbDeviceType GetType() const { return type; }
+ const char *GetTypeString() const { return PbDeviceType_Name(type).c_str(); }
bool IsReady() const { return ready; }
virtual void Reset();
@@ -96,20 +108,13 @@ public:
void SetProtected(bool);
bool IsReadOnly() const { return read_only; }
void SetReadOnly(bool b) { read_only = b; }
-
bool IsStoppable() const { return stoppable; }
- void SetStoppable(bool b) { stoppable = b; }
bool IsStopped() const { return stopped; }
- void SetStopped(bool b) { stopped = b; }
bool IsRemovable() const { return removable; }
- void SetRemovable(bool b) { removable = b; }
bool IsRemoved() const { return removed; }
void SetRemoved(bool b) { removed = b; }
-
bool IsLockable() const { return lockable; }
- void SetLockable(bool b) { lockable = b; }
bool IsLocked() const { return locked; }
- void SetLocked(bool b) { locked = b; }
virtual int GetId() const = 0;
int GetLun() const { return lun; }
@@ -123,8 +128,9 @@ public:
string GetPaddedName() const;
bool SupportsParams() const { return supports_params; }
- virtual bool SupportsFile() const { return !supports_params; }
+ bool SupportsFile() const { return supports_file; }
void SupportsParams(bool b) { supports_params = b; }
+ void SupportsFile(bool b) { supports_file = b; }
unordered_map GetParams() const { return params; }
void SetDefaultParams(const unordered_map& p) { default_params = p; }
diff --git a/src/raspberrypi/devices/device_factory.cpp b/src/raspberrypi/devices/device_factory.cpp
index 5a3ab52a..759cbb91 100644
--- a/src/raspberrypi/devices/device_factory.cpp
+++ b/src/raspberrypi/devices/device_factory.cpp
@@ -16,6 +16,7 @@
#include "scsi_daynaport.h"
#include "rascsi_exceptions.h"
#include "host_services.h"
+#include "rasutil.h"
#include "device_factory.h"
#include
#include
@@ -24,6 +25,7 @@
using namespace std;
using namespace rascsi_interface;
+using namespace ras_util;
DeviceFactory::DeviceFactory()
{
@@ -45,7 +47,6 @@ DeviceFactory::DeviceFactory()
default_params[SCDP]["interface"] = network_interfaces;
default_params[SCDP]["inet"] = DEFAULT_IP;
default_params[SCLP]["cmd"] = "lp -oraw %f";
- default_params[SCLP]["timeout"] = "30";
extension_mapping["hd1"] = SCHD;
extension_mapping["hds"] = SCHD;
@@ -63,20 +64,9 @@ DeviceFactory::DeviceFactory()
device_mapping["services"] = SCHS;
}
-string DeviceFactory::GetExtension(const string& filename) const
-{
- string ext;
- if (const size_t separator = filename.rfind('.'); separator != string::npos) {
- ext = filename.substr(separator + 1);
- }
- std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){ return std::tolower(c); });
-
- return ext;
-}
-
PbDeviceType DeviceFactory::GetTypeForFile(const string& filename) const
{
- if (const auto& it = extension_mapping.find(GetExtension(filename)); it != extension_mapping.end()) {
+ if (const auto& it = extension_mapping.find(GetExtensionLowerCase(filename)); it != extension_mapping.end()) {
return it->second;
}
@@ -87,7 +77,6 @@ PbDeviceType DeviceFactory::GetTypeForFile(const string& filename) const
return UNDEFINED;
}
-// ID -1 is used by rascsi to create a temporary device
shared_ptr DeviceFactory::CreateDevice(const ControllerManager& controller_manager, PbDeviceType type,
int lun, const string& filename)
{
@@ -102,7 +91,7 @@ shared_ptr DeviceFactory::CreateDevice(const ControllerManager& c
shared_ptr device;
switch (type) {
case SCHD: {
- if (const string ext = GetExtension(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") {
+ if (const string ext = GetExtensionLowerCase(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") {
device = make_shared(lun);
} else {
device = make_shared(lun, sector_sizes[SCHD], false,
@@ -114,35 +103,21 @@ shared_ptr DeviceFactory::CreateDevice(const ControllerManager& c
device->SetProduct("FIREBALL");
}
}
- device->SetProtectable(true);
- device->SetStoppable(true);
break;
}
case SCRM:
device = make_shared(lun, 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 = make_shared(lun, sector_sizes[SCMO]);
- device->SetProtectable(true);
- device->SetStoppable(true);
- device->SetRemovable(true);
- device->SetLockable(true);
device->SetProduct("SCSI MO");
break;
case SCCD:
device = make_shared(lun, sector_sizes[SCCD]);
- device->SetReadOnly(true);
- device->SetStoppable(true);
- device->SetRemovable(true);
- device->SetLockable(true);
device->SetProduct("SCSI CD-ROM");
break;
@@ -150,7 +125,6 @@ shared_ptr DeviceFactory::CreateDevice(const ControllerManager& c
device = make_shared(lun);
// Since this is an emulation for a specific driver the product name has to be set accordingly
device->SetProduct("RASCSI BRIDGE");
- device->SupportsParams(true);
device->SetDefaultParams(default_params[SCBR]);
break;
@@ -160,7 +134,6 @@ shared_ptr DeviceFactory::CreateDevice(const ControllerManager& c
device->SetVendor("Dayna");
device->SetProduct("SCSI/Link");
device->SetRevision("1.4a");
- device->SupportsParams(true);
device->SetDefaultParams(default_params[SCDP]);
break;
@@ -174,7 +147,6 @@ shared_ptr DeviceFactory::CreateDevice(const ControllerManager& c
case SCLP:
device = make_shared(lun);
device->SetProduct("SCSI PRINTER");
- device->SupportsParams(true);
device->SetDefaultParams(default_params[SCLP]);
break;
@@ -191,14 +163,6 @@ const unordered_set& DeviceFactory::GetSectorSizes(PbDeviceType type)
return it != sector_sizes.end() ? it->second : empty_set;
}
-const unordered_set& DeviceFactory::GetSectorSizes(const string& type) const
-{
- PbDeviceType t = UNDEFINED;
- PbDeviceType_Parse(type, &t);
-
- return GetSectorSizes(t);
-}
-
const unordered_map& DeviceFactory::GetDefaultParams(PbDeviceType type) const
{
const auto& it = default_params.find(type);
diff --git a/src/raspberrypi/devices/device_factory.h b/src/raspberrypi/devices/device_factory.h
index f5e06ca8..0ed0829a 100644
--- a/src/raspberrypi/devices/device_factory.h
+++ b/src/raspberrypi/devices/device_factory.h
@@ -35,15 +35,12 @@ public:
shared_ptr CreateDevice(const ControllerManager&, PbDeviceType, int, const string&);
PbDeviceType GetTypeForFile(const string&) const;
const unordered_set& GetSectorSizes(PbDeviceType type) const;
- const unordered_set& GetSectorSizes(const string&) const;
const unordered_map& GetDefaultParams(PbDeviceType type) const;
list GetNetworkInterfaces() const;
const unordered_map& GetExtensionMapping() const { return extension_mapping; }
private:
- string GetExtension(const string&) const;
-
unordered_map> sector_sizes;
unordered_map> default_params;
diff --git a/src/raspberrypi/devices/disk.cpp b/src/raspberrypi/devices/disk.cpp
index ad2f1660..823a4ccb 100644
--- a/src/raspberrypi/devices/disk.cpp
+++ b/src/raspberrypi/devices/disk.cpp
@@ -23,20 +23,19 @@
using namespace scsi_defs;
using namespace scsi_command_util;
-unordered_map Disk::reserved_files;
+const unordered_map Disk::shift_counts = { { 512, 9 }, { 1024, 10 }, { 2048, 11 }, { 4096, 12 } };
-Disk::Disk(const string& type, int lun) : ModePageDevice(type, lun)
+Disk::Disk(PbDeviceType type, int lun) : StorageDevice(type, lun)
{
- dispatcher.Add(scsi_command::eCmdRezero, "Rezero", &Disk::Rezero);
+ // REZERO implementation is identical with Seek
+ dispatcher.Add(scsi_command::eCmdRezero, "Rezero", &Disk::Seek);
dispatcher.Add(scsi_command::eCmdFormat, "FormatUnit", &Disk::FormatUnit);
- dispatcher.Add(scsi_command::eCmdReassign, "ReassignBlocks", &Disk::ReassignBlocks);
+ // REASSIGN BLOCKS implementation is identical with Seek
+ dispatcher.Add(scsi_command::eCmdReassign, "ReassignBlocks", &Disk::Seek);
dispatcher.Add(scsi_command::eCmdRead6, "Read6", &Disk::Read6);
dispatcher.Add(scsi_command::eCmdWrite6, "Write6", &Disk::Write6);
dispatcher.Add(scsi_command::eCmdSeek6, "Seek6", &Disk::Seek6);
- dispatcher.Add(scsi_command::eCmdReserve6, "Reserve6", &Disk::Reserve);
- dispatcher.Add(scsi_command::eCmdRelease6, "Release6", &Disk::Release);
dispatcher.Add(scsi_command::eCmdStartStop, "StartStopUnit", &Disk::StartStopUnit);
- dispatcher.Add(scsi_command::eCmdSendDiag, "SendDiagnostic", &Disk::SendDiagnostic);
dispatcher.Add(scsi_command::eCmdRemoval, "PreventAllowMediumRemoval", &Disk::PreventAllowMediumRemoval);
dispatcher.Add(scsi_command::eCmdReadCapacity10, "ReadCapacity10", &Disk::ReadCapacity10);
dispatcher.Add(scsi_command::eCmdRead10, "Read10", &Disk::Read10);
@@ -49,8 +48,6 @@ Disk::Disk(const string& type, int lun) : ModePageDevice(type, lun)
dispatcher.Add(scsi_command::eCmdSynchronizeCache10, "SynchronizeCache10", &Disk::SynchronizeCache);
dispatcher.Add(scsi_command::eCmdSynchronizeCache16, "SynchronizeCache16", &Disk::SynchronizeCache);
dispatcher.Add(scsi_command::eCmdReadDefectData10, "ReadDefectData10", &Disk::ReadDefectData10);
- dispatcher.Add(scsi_command::eCmdReserve10, "Reserve10", &Disk::Reserve);
- dispatcher.Add(scsi_command::eCmdRelease10, "Release10", &Disk::Release);
dispatcher.Add(scsi_command::eCmdRead16, "Read16", &Disk::Read16);
dispatcher.Add(scsi_command::eCmdWrite16, "Write16", &Disk::Write16);
dispatcher.Add(scsi_command::eCmdVerify16, "Verify16", &Disk::Verify16);
@@ -68,10 +65,10 @@ Disk::~Disk()
bool Disk::Dispatch(scsi_command cmd)
{
// Media changes must be reported on the next access, i.e. not only for TEST UNIT READY
- if (is_medium_changed) {
+ if (IsMediumChanged()) {
assert(IsRemovable());
- is_medium_changed = false;
+ SetMediumChanged(false);
throw scsi_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
}
@@ -80,46 +77,19 @@ bool Disk::Dispatch(scsi_command cmd)
return dispatcher.Dispatch(this, cmd) ? true : super::Dispatch(cmd);
}
-//---------------------------------------------------------------------------
-//
-// Open
-// * Call as a post-process after successful opening in a derived class
-//
-//---------------------------------------------------------------------------
-void Disk::Open(const Filepath& path)
+void Disk::SetUpCache(off_t image_offset, bool raw)
{
- if (blocks == 0) {
- throw io_exception("Disk has 0 blocks");
- }
-
- SetReady(true);
-
- // Can read/write open
- if (Fileio fio; fio.Open(path, Fileio::OpenMode::ReadWrite)) {
- // Write permission
- fio.Close();
- } else {
- // Permanently write-protected
- SetReadOnly(true);
- SetProtectable(false);
- SetProtected(false);
- }
-
- SetStopped(false);
- SetRemoved(false);
- SetLocked(false);
-}
-
-void Disk::SetUpCache(const Filepath& path, off_t image_offset, bool raw)
-{
- assert(cache == nullptr);
- cache = make_unique(path, size_shift_count, (uint32_t)blocks, image_offset);
+ Filepath path;
+ path.SetPath(GetFilename().c_str());
+ cache = make_unique(path, size_shift_count, (uint32_t)GetBlockCount(), image_offset);
cache->SetRawMode(raw);
}
-void Disk::ResizeCache(const Filepath& path, bool raw)
+void Disk::ResizeCache(const string& filename, bool raw)
{
- cache.reset(new DiskCache(path, GetSectorSizeShiftCount(), (uint32_t)blocks));
+ Filepath path;
+ path.SetPath(filename.c_str());
+ cache.reset(new DiskCache(path, GetSectorSizeShiftCount(), (uint32_t)GetBlockCount()));
cache->SetRawMode(raw);
}
@@ -130,11 +100,6 @@ void Disk::FlushCache()
}
}
-void Disk::Rezero()
-{
- Seek();
-}
-
void Disk::FormatUnit()
{
CheckReady();
@@ -147,11 +112,6 @@ void Disk::FormatUnit()
EnterStatusPhase();
}
-void Disk::ReassignBlocks()
-{
- Seek();
-}
-
void Disk::Read(access_mode mode)
{
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
@@ -168,21 +128,6 @@ void Disk::Read(access_mode mode)
}
}
-void Disk::Read6()
-{
- Read(RW6);
-}
-
-void Disk::Read10()
-{
- Read(RW10);
-}
-
-void Disk::Read16()
-{
- Read(RW16);
-}
-
void Disk::ReadWriteLong10()
{
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
@@ -222,21 +167,6 @@ void Disk::Write(access_mode mode)
}
}
-void Disk::Write6()
-{
- Write(RW6);
-}
-
-void Disk::Write10()
-{
- Write(RW10);
-}
-
-void Disk::Write16()
-{
- Write(RW16);
-}
-
void Disk::Verify(access_mode mode)
{
if (uint64_t start; CheckAndGetStartAndCount(start, ctrl->blocks, mode)) {
@@ -259,16 +189,6 @@ void Disk::Verify(access_mode mode)
}
}
-void Disk::Verify10()
-{
- Verify(RW10);
-}
-
-void Disk::Verify16()
-{
- Verify(RW16);
-}
-
void Disk::StartStopUnit()
{
const bool start = ctrl->cmd[4] & 0x01;
@@ -284,8 +204,6 @@ void Disk::StartStopUnit()
}
if (!start) {
- FlushCache();
-
// Look at the eject bit and eject if necessary
if (load) {
if (IsLocked()) {
@@ -295,24 +213,12 @@ void Disk::StartStopUnit()
// Eject
if (!Eject(false)) {
- throw scsi_exception();
+ throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
}
}
- }
-
- EnterStatusPhase();
-}
-
-void Disk::SendDiagnostic()
-{
- // Do not support PF bit
- if (ctrl->cmd[1] & 0x10) {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
- }
-
- // Do not support parameter list
- if ((ctrl->cmd[3] != 0) || (ctrl->cmd[4] != 0)) {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
+ else {
+ FlushCache();
+ }
}
EnterStatusPhase();
@@ -349,16 +255,6 @@ void Disk::ReadDefectData10()
EnterDataInPhase();
}
-void Disk::MediumChanged()
-{
- if (IsRemovable()) {
- is_medium_changed = true;
- }
- else {
- LOGERROR("Medium change requested for non-removable medium")
- }
-}
-
bool Disk::Eject(bool force)
{
const bool status = super::Eject(force);
@@ -395,24 +291,15 @@ int Disk::ModeSense6(const vector& cdb, vector& buf) const
// Only if ready
if (IsReady()) {
// Short LBA mode parameter block descriptor (number of blocks and block length)
- SetInt32(buf, 4, (uint32_t)blocks);
+ SetInt32(buf, 4, (uint32_t)GetBlockCount());
SetInt32(buf, 8, GetSectorSizeInBytes());
}
size = 12;
}
- size += super::AddModePages(cdb, buf, size, length - size);
- if (size > 255) {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
- }
+ size = AddModePages(cdb, buf, size, length, 255);
- // Do not return more than ALLOCATION LENGTH bytes
- if (size > length) {
- size = length;
- }
-
- // Final setting of mode data length
buf[0] = (BYTE)size;
return size;
@@ -434,7 +321,7 @@ int Disk::ModeSense10(const vector& cdb, vector& buf) const
// Add block descriptor if DBD is 0, only if ready
if ((cdb[1] & 0x08) == 0 && IsReady()) {
- uint64_t disk_blocks = blocks;
+ uint64_t disk_blocks = GetBlockCount();
uint32_t disk_size = GetSectorSizeInBytes();
// Check LLBAA for short or long block descriptor
@@ -463,17 +350,8 @@ int Disk::ModeSense10(const vector& cdb, vector& buf) const
}
}
- size += super::AddModePages(cdb, buf, size, length - size);
- if (size > 65535) {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
- }
+ size = AddModePages(cdb, buf, size, length, 65535);
- // Do not return more than ALLOCATION LENGTH bytes
- if (size > length) {
- size = length;
- }
-
- // Final setting of mode data length
SetInt16(buf, 0, size);
return size;
@@ -491,7 +369,7 @@ void Disk::SetUpModePages(map>& pages, int page, bool changeab
AddFormatPage(pages, changeable);
}
- // Page code 4 (drive parameter)
+ // Page code 4 (rigid drive page)
if (page == 0x04 || page == 0x3f) {
AddDrivePage(pages, changeable);
}
@@ -567,7 +445,7 @@ 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)
- uint64_t cylinders = blocks;
+ uint64_t cylinders = GetBlockCount();
cylinders >>= 3;
cylinders /= 25;
SetInt32(buf, 0x01, (uint32_t)cylinders);
@@ -607,12 +485,6 @@ void Disk::AddCachePage(map>& pages, bool changeable) const
pages[8] = buf;
}
-void Disk::AddVendorPage(map>&, int, bool) const
-{
- // Nothing to add by default
-}
-
-// TODO Read more than one block in a single call. Currently blocked by the the track-oriented cache
int Disk::Read(const vector&, vector& buf, uint64_t block)
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
@@ -620,7 +492,7 @@ int Disk::Read(const vector&, vector& buf, uint64_t block)
CheckReady();
// Error if the total number of blocks is exceeded
- if (block >= blocks) {
+ if (block >= GetBlockCount()) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
@@ -638,7 +510,7 @@ int Disk::WriteCheck(uint64_t block)
CheckReady();
// Error if the total number of blocks is exceeded
- if (block >= blocks) {
+ if (block >= GetBlockCount()) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
@@ -651,7 +523,6 @@ int Disk::WriteCheck(uint64_t block)
return 1 << size_shift_count;
}
-// TODO Write more than one block in a single call. Currently blocked by the track-oriented cache
void Disk::Write(const vector&, const vector& buf, uint64_t block)
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
@@ -662,7 +533,7 @@ void Disk::Write(const vector&, const vector& buf, uint64_t block)
}
// Error if the total number of blocks is exceeded
- if (block >= blocks) {
+ if (block >= GetBlockCount()) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
@@ -706,14 +577,14 @@ void Disk::ReadCapacity10()
{
CheckReady();
- if (blocks == 0) {
+ if (GetBlockCount() == 0) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
}
vector& buf = controller->GetBuffer();
// Create end of logical block address (blocks-1)
- uint64_t capacity = blocks - 1;
+ uint64_t capacity = GetBlockCount() - 1;
// If the capacity exceeds 32 bit, -1 must be returned and the client has to use READ CAPACITY(16)
if (capacity > 4294967295) {
@@ -734,14 +605,14 @@ void Disk::ReadCapacity16()
{
CheckReady();
- if (blocks == 0) {
+ if (GetBlockCount() == 0) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
}
vector& buf = controller->GetBuffer();
// Create end of logical block address (blocks-1)
- SetInt64(buf, 0, blocks - 1);
+ SetInt64(buf, 0, GetBlockCount() - 1);
// Create block length (1 << size)
SetInt32(buf, 8, 1 << size_shift_count);
@@ -775,26 +646,6 @@ void Disk::ReadCapacity16_ReadLong16()
}
}
-//---------------------------------------------------------------------------
-//
-// RESERVE/RELEASE(6/10)
-//
-// The reserve/release commands are only used in multi-initiator
-// environments. RaSCSI doesn't support this use case. However, some old
-// versions of Solaris will issue the reserve/release commands. We will
-// just respond with an OK status.
-//
-//---------------------------------------------------------------------------
-void Disk::Reserve()
-{
- EnterStatusPhase();
-}
-
-void Disk::Release()
-{
- EnterStatusPhase();
-}
-
//---------------------------------------------------------------------------
//
// Check/Get start sector and sector count for a READ/WRITE or READ/WRITE LONG operation
@@ -805,8 +656,8 @@ void Disk::ValidateBlockAddress(access_mode mode) const
{
const uint64_t block = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
- if (block > blocks) {
- LOGTRACE("%s", ("Capacity of " + to_string(blocks) + " block(s) exceeded: Trying to access block "
+ if (block > GetBlockCount()) {
+ LOGTRACE("%s", ("Capacity of " + to_string(GetBlockCount()) + " block(s) exceeded: Trying to access block "
+ to_string(block)).c_str())
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
@@ -839,7 +690,7 @@ bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mod
LOGTRACE("%s READ/WRITE/VERIFY/SEEK command record=$%08X blocks=%d", __PRETTY_FUNCTION__, (uint32_t)start, count)
// Check capacity
- if (uint64_t capacity = blocks; start > capacity || start + count > capacity) {
+ if (uint64_t capacity = GetBlockCount(); !capacity || start > capacity || start + count > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ to_string(start) + ", block count " + to_string(count)).c_str())
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
@@ -853,6 +704,12 @@ bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mod
return true;
}
+uint32_t Disk::CalculateShiftCount(uint32_t size_in_bytes)
+{
+ const auto& it = shift_counts.find(size_in_bytes);
+ return it != shift_counts.end() ? it->second : 0;
+}
+
uint32_t Disk::GetSectorSizeInBytes() const
{
return size_shift_count ? 1 << size_shift_count : 0;
@@ -861,32 +718,13 @@ uint32_t Disk::GetSectorSizeInBytes() const
void Disk::SetSectorSizeInBytes(uint32_t size_in_bytes)
{
DeviceFactory device_factory;
- if (unordered_set sizes = device_factory.GetSectorSizes(GetType());
+ if (const auto& sizes = device_factory.GetSectorSizes(GetType());
!sizes.empty() && sizes.find(size_in_bytes) == sizes.end()) {
- throw io_exception("Invalid block size of " + to_string(size_in_bytes) + " bytes");
+ throw io_exception("Invalid sector size of " + to_string(size_in_bytes) + " byte(s)");
}
- switch (size_in_bytes) {
- case 512:
- size_shift_count = 9;
- break;
-
- case 1024:
- size_shift_count = 10;
- break;
-
- case 2048:
- size_shift_count = 11;
- break;
-
- case 4096:
- size_shift_count = 12;
- break;
-
- default:
- throw io_exception("Invalid block size of " + to_string(size_in_bytes) + " bytes");
- break;
- }
+ size_shift_count = CalculateShiftCount(size_in_bytes);
+ assert(size_shift_count);
}
uint32_t Disk::GetConfiguredSectorSize() const
@@ -905,43 +743,3 @@ bool Disk::SetConfiguredSectorSize(const DeviceFactory& device_factory, uint32_t
return true;
}
-
-void Disk::ReserveFile(const Filepath& path, int id, int lun) const
-{
- reserved_files[path.GetPath()] = make_pair(id, lun);
-}
-
-void Disk::UnreserveFile() const
-{
- reserved_files.erase(diskpath.GetPath());
-}
-
-bool Disk::GetIdsForReservedFile(const Filepath& path, int& id, int& lun)
-{
- if (const auto& it = reserved_files.find(path.GetPath()); it != reserved_files.end()) {
- id = it->second.first;
- lun = it->second.second;
-
- return true;
- }
-
- return false;
-}
-
-void Disk::UnreserveAll()
-{
- reserved_files.clear();
-}
-
-bool Disk::FileExists(const Filepath& filepath)
-{
- try {
- // Disk::Open closes the file in case it exists
- Open(filepath);
- }
- catch(const file_not_found_exception&) {
- return false;
- }
-
- return true;
-}
diff --git a/src/raspberrypi/devices/disk.h b/src/raspberrypi/devices/disk.h
index 3fe55b0f..c03ee685 100644
--- a/src/raspberrypi/devices/disk.h
+++ b/src/raspberrypi/devices/disk.h
@@ -19,17 +19,15 @@
#include "device_factory.h"
#include "disk_track.h"
#include "disk_cache.h"
-#include "filepath.h"
#include "interfaces/scsi_block_commands.h"
-#include "mode_page_device.h"
+#include "storage_device.h"
#include
#include
+#include
using namespace std;
-using id_set = pair;
-
-class Disk : public ModePageDevice, private ScsiBlockCommands
+class Disk : public StorageDevice, private ScsiBlockCommands
{
enum access_mode { RW6, RW10, RW16, SEEK6, SEEK10 };
@@ -44,24 +42,13 @@ class Disk : public ModePageDevice, private ScsiBlockCommands
// Sector size shift count (9=512, 10=1024, 11=2048, 12=4096)
uint32_t size_shift_count = 0;
- // Total number of sectors
- uint64_t blocks = 0;
-
- bool is_medium_changed = false;
-
- Filepath diskpath;
-
- // The list of image files in use and the IDs and LUNs using these files
- static unordered_map reserved_files;
-
public:
- Disk(const string&, int);
+ Disk(PbDeviceType, int);
~Disk() override;
bool Dispatch(scsi_command) override;
- void MediumChanged();
bool Eject(bool) override;
// Command helpers
@@ -73,48 +60,30 @@ public:
uint32_t GetSectorSizeInBytes() const;
bool IsSectorSizeConfigurable() const { return !sector_sizes.empty(); }
bool SetConfiguredSectorSize(const DeviceFactory&, uint32_t);
- uint64_t GetBlockCount() const { return blocks; }
void FlushCache() override;
- virtual void Open(const Filepath&);
- void GetPath(Filepath& path) const { path = diskpath; }
-
- void ReserveFile(const Filepath&, int, int) const;
- void UnreserveFile() const;
- static void UnreserveAll();
- bool FileExists(const Filepath&);
-
- static unordered_map GetReservedFiles() { return reserved_files; }
- static void SetReservedFiles(const unordered_map& files_in_use) { reserved_files = files_in_use; }
- static bool GetIdsForReservedFile(const Filepath&, int&, int&);
-
private:
- using super = ModePageDevice;
+ using super = StorageDevice;
// Commands covered by the SCSI specifications (see https://www.t10.org/drafts.htm)
void StartStopUnit();
- void SendDiagnostic();
void PreventAllowMediumRemoval();
void SynchronizeCache();
void ReadDefectData10();
- virtual void Read6();
- void Read10() override;
- void Read16() override;
- virtual void Write6();
- void Write10() override;
- void Write16() override;
- void Verify10();
- void Verify16();
+ virtual void Read6() { Read(RW6); }
+ void Read10() override { Read(RW10); }
+ void Read16() override { Read(RW16); }
+ virtual void Write6() { Write(RW6); }
+ void Write10() override { Write(RW10); }
+ void Write16() override { Write(RW16); }
+ void Verify10() { Verify(RW10); }
+ void Verify16() { Verify(RW16); }
void Seek();
void Seek10();
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);
@@ -129,23 +98,24 @@ private:
int ModeSense6(const vector&, vector&) const override;
int ModeSense10(const vector&, vector&) const override;
+ static const unordered_map shift_counts;
+
protected:
- void SetUpCache(const Filepath&, off_t, bool = false);
- void ResizeCache(const Filepath&, bool);
+ void SetUpCache(off_t, bool = false);
+ void ResizeCache(const string&, bool);
void SetUpModePages(map>&, int, bool) const override;
- virtual void AddErrorPage(map>&, bool) const;
+ void AddErrorPage(map>&, bool) const;
virtual void AddFormatPage(map>&, bool) const;
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& sizes) { sector_sizes = sizes; }
void SetSectorSizeInBytes(uint32_t);
uint32_t GetSectorSizeShiftCount() const { return size_shift_count; }
void SetSectorSizeShiftCount(uint32_t count) { size_shift_count = count; }
uint32_t GetConfiguredSectorSize() const;
- void SetBlockCount(uint64_t b) { blocks = b; }
- void SetPath(const Filepath& path) { diskpath = path; }
+ static uint32_t CalculateShiftCount(uint32_t);
};
diff --git a/src/raspberrypi/devices/host_services.cpp b/src/raspberrypi/devices/host_services.cpp
index 95f12a41..9cdd8c84 100644
--- a/src/raspberrypi/devices/host_services.cpp
+++ b/src/raspberrypi/devices/host_services.cpp
@@ -32,13 +32,12 @@ using namespace scsi_defs;
using namespace scsi_command_util;
HostServices::HostServices(int lun, const ControllerManager& manager)
- : ModePageDevice("SCHS", lun), controller_manager(manager)
+ : ModePageDevice(SCHS, lun), controller_manager(manager)
{
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &HostServices::TestUnitReady);
dispatcher.Add(scsi_command::eCmdStartStop, "StartStopUnit", &HostServices::StartStopUnit);
SetReady(true);
- SetReset(false);
}
bool HostServices::Dispatch(scsi_command cmd)
@@ -96,18 +95,8 @@ int HostServices::ModeSense6(const vector& cdb, vector& buf) const
const auto length = (int)min(buf.size(), (size_t)cdb[4]);
fill_n(buf.begin(), length, 0);
- // Basic Information
- int size = 4;
-
- size += super::AddModePages(cdb, buf, size, length - size);
- if (size > 255) {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
- }
-
- // Do not return more than ALLOCATION LENGTH bytes
- if (size > length) {
- size = length;
- }
+ // 4 bytes basic information
+ int size = AddModePages(cdb, buf, 4, length, 255);
buf[0] = (BYTE)size;
@@ -124,18 +113,8 @@ int HostServices::ModeSense10(const vector& cdb, vector& buf) const
const auto length = (int)min(buf.size(), (size_t)GetInt16(cdb, 7));
fill_n(buf.begin(), length, 0);
- // Basic Information
- int size = 8;
-
- size += super::AddModePages(cdb, buf, size, length - size);
- if (size > 65535) {
- throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
- }
-
- // Do not return more than ALLOCATION LENGTH bytes
- if (size > length) {
- size = length;
- }
+ // 8 bytes basic information
+ int size = AddModePages(cdb, buf, 8, length, 65535);
SetInt16(buf, 0, size);
@@ -151,6 +130,8 @@ void HostServices::SetUpModePages(map>& pages, int page, bool
void HostServices::AddRealtimeClockPage(map>& pages, bool changeable) const
{
+ vector buf(10);
+
if (!changeable) {
time_t t = time(nullptr);
tm localtime;
@@ -167,8 +148,8 @@ void HostServices::AddRealtimeClockPage(map>& pages, bool chan
// Ignore leap second for simplicity
datetime.second = (uint8_t)(localtime.tm_sec < 60 ? localtime.tm_sec : 59);
- vector buf(10);
memcpy(&buf[2], &datetime, sizeof(datetime));
- pages[32] = buf;
}
+
+ pages[32] = buf;
}
diff --git a/src/raspberrypi/devices/host_services.h b/src/raspberrypi/devices/host_services.h
index f34a04a9..55c756a5 100644
--- a/src/raspberrypi/devices/host_services.h
+++ b/src/raspberrypi/devices/host_services.h
@@ -30,8 +30,6 @@ public:
vector InquiryInternal() const override;
void TestUnitReady() override;
- bool SupportsFile() const override { return false; }
-
protected:
void SetUpModePages(map>&, int, bool) const override;
diff --git a/src/raspberrypi/devices/interfaces/scsi_mmc_commands.h b/src/raspberrypi/devices/interfaces/scsi_mmc_commands.h
index a524b0df..d8f161e3 100644
--- a/src/raspberrypi/devices/interfaces/scsi_mmc_commands.h
+++ b/src/raspberrypi/devices/interfaces/scsi_mmc_commands.h
@@ -20,5 +20,4 @@ public:
virtual ~ScsiMmcCommands() = default;
virtual void ReadToc() = 0;
- virtual void GetEventStatusNotification() = 0;
};
diff --git a/src/raspberrypi/devices/mode_page_device.cpp b/src/raspberrypi/devices/mode_page_device.cpp
index 0bcf0636..08f58bc6 100644
--- a/src/raspberrypi/devices/mode_page_device.cpp
+++ b/src/raspberrypi/devices/mode_page_device.cpp
@@ -20,7 +20,7 @@ using namespace std;
using namespace scsi_defs;
using namespace scsi_command_util;
-ModePageDevice::ModePageDevice(const string& type, int lun) : PrimaryDevice(type, lun)
+ModePageDevice::ModePageDevice(PbDeviceType type, int lun) : PrimaryDevice(type, lun)
{
dispatcher.Add(scsi_command::eCmdModeSense6, "ModeSense6", &ModePageDevice::ModeSense6);
dispatcher.Add(scsi_command::eCmdModeSense10, "ModeSense10", &ModePageDevice::ModeSense10);
@@ -34,10 +34,11 @@ bool ModePageDevice::Dispatch(scsi_command cmd)
return dispatcher.Dispatch(this, cmd) ? true : super::Dispatch(cmd);
}
-int ModePageDevice::AddModePages(const vector& cdb, vector& buf, int offset, int max_length) const
+int ModePageDevice::AddModePages(const vector& cdb, vector& buf, int offset, int length, int max_size) const
{
+ int max_length = length - offset;
if (max_length < 0) {
- return 0;
+ return length;
}
const bool changeable = (cdb[2] & 0xc0) == 0x40;
@@ -87,11 +88,15 @@ int ModePageDevice::AddModePages(const vector& cdb, vector& buf, int
result[off + 1] = (byte)(page0.size() - 2);
}
- // Do not return more than the requested number of bytes
- size_t size = min((size_t)max_length, result.size());
+ if ((int)result.size() > max_size) {
+ throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
+ }
+
+ auto size = (int)min((size_t)max_length, result.size());
memcpy(&buf.data()[offset], result.data(), size);
- return (int)size;
+ // Do not return more than the requested number of bytes
+ return size + offset < length ? size + offset : length;
}
void ModePageDevice::ModeSense6()
@@ -108,46 +113,32 @@ void ModePageDevice::ModeSense10()
EnterDataInPhase();
}
-void ModePageDevice::ModeSelect(const vector&, const vector&, int) const
+void ModePageDevice::ModeSelect(scsi_command, const vector&, const vector&, int) const
{
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
}
void ModePageDevice::ModeSelect6()
{
- ctrl->length = ModeSelectCheck6();
+ ctrl->length = SaveParametersCheck(ctrl->cmd[4]);
EnterDataOutPhase();
}
void ModePageDevice::ModeSelect10()
{
- ctrl->length = ModeSelectCheck10();
+ const size_t length = min(controller->GetBuffer().size(), (size_t)GetInt16(ctrl->cmd, 7));
+
+ ctrl->length = SaveParametersCheck((int)length);
EnterDataOutPhase();
}
-int ModePageDevice::ModeSelectCheck(int length) const
+int ModePageDevice::SaveParametersCheck(int length) const
{
- // Error if save parameters are set for other types than SCHD, SCRM or SCMO
- // TODO The assumption above is not correct, and this code should be located elsewhere
- if (GetType() != "SCHD" && GetType() != "SCRM" && GetType() != "SCMO" && (ctrl->cmd[1] & 0x01)) {
+ if (!SupportsSaveParameters() && (ctrl->cmd[1] & 0x01)) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
return length;
}
-
-int ModePageDevice::ModeSelectCheck6() const
-{
- // Receive the data specified by the parameter length
- return ModeSelectCheck(ctrl->cmd[4]);
-}
-
-int ModePageDevice::ModeSelectCheck10() const
-{
- // Receive the data specified by the parameter length
- size_t length = min(controller->GetBuffer().size(), (size_t)GetInt16(ctrl->cmd, 7));
-
- return ModeSelectCheck((int)length);
-}
diff --git a/src/raspberrypi/devices/mode_page_device.h b/src/raspberrypi/devices/mode_page_device.h
index c1c1d4fe..454f4d60 100644
--- a/src/raspberrypi/devices/mode_page_device.h
+++ b/src/raspberrypi/devices/mode_page_device.h
@@ -18,17 +18,22 @@ class ModePageDevice: public PrimaryDevice
{
public:
- ModePageDevice(const string&, int);
- ~ModePageDevice()override = default;
+ ModePageDevice(PbDeviceType, int);
+ ~ModePageDevice() override = default;
bool Dispatch(scsi_command) override;
- virtual void ModeSelect(const vector&, const vector&, int) const;
+ virtual void ModeSelect(scsi_defs::scsi_command, const vector&, const vector&, int) const;
protected:
- int AddModePages(const vector&, vector&, int, int) const;
+ bool SupportsSaveParameters() const { return supports_save_parameters; }
+ void SupportsSaveParameters(bool b) { supports_save_parameters = b; }
+ int AddModePages(const vector&, vector&, int, int, int) const;
virtual void SetUpModePages(map>&, int, bool) const = 0;
+ virtual void AddVendorPage(map>&, int, bool) const {
+ // Nothing to add by default
+ }
private:
@@ -36,6 +41,8 @@ private:
Dispatcher dispatcher;
+ bool supports_save_parameters = false;
+
virtual int ModeSense6(const vector&, vector&) const = 0;
virtual int ModeSense10(const vector&, vector&) const = 0;
@@ -44,7 +51,5 @@ private:
void ModeSelect6();
void ModeSelect10();
- int ModeSelectCheck(int) const;
- int ModeSelectCheck6() const;
- int ModeSelectCheck10() const;
+ int SaveParametersCheck(int) const;
};
diff --git a/src/raspberrypi/devices/primary_device.cpp b/src/raspberrypi/devices/primary_device.cpp
index 0ecbf95a..d7c03366 100644
--- a/src/raspberrypi/devices/primary_device.cpp
+++ b/src/raspberrypi/devices/primary_device.cpp
@@ -17,15 +17,18 @@ using namespace std;
using namespace scsi_defs;
using namespace scsi_command_util;
-PrimaryDevice::PrimaryDevice(const string& type, int lun) : Device(type, lun)
+PrimaryDevice::PrimaryDevice(PbDeviceType type, int lun) : Device(type, lun)
{
// Mandatory SCSI primary commands
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady);
dispatcher.Add(scsi_command::eCmdInquiry, "Inquiry", &PrimaryDevice::Inquiry);
dispatcher.Add(scsi_command::eCmdReportLuns, "ReportLuns", &PrimaryDevice::ReportLuns);
- // Optional commands used by all RaSCSI devices
+ // Optional commands supported by all RaSCSI devices
dispatcher.Add(scsi_command::eCmdRequestSense, "RequestSense", &PrimaryDevice::RequestSense);
+ dispatcher.Add(scsi_command::eCmdReserve6, "ReserveUnit", &PrimaryDevice::ReserveUnit);
+ dispatcher.Add(scsi_command::eCmdRelease6, "ReleaseUnit", &PrimaryDevice::ReleaseUnit);
+ dispatcher.Add(scsi_command::eCmdSendDiag, "SendDiagnostic", &PrimaryDevice::SendDiagnostic);
}
bool PrimaryDevice::Dispatch(scsi_command cmd)
@@ -33,6 +36,13 @@ bool PrimaryDevice::Dispatch(scsi_command cmd)
return dispatcher.Dispatch(this, cmd);
}
+void PrimaryDevice::Reset()
+{
+ DiscardReservation();
+
+ Device::Reset();
+}
+
int PrimaryDevice::GetId() const
{
if (controller == nullptr) {
@@ -137,6 +147,21 @@ void PrimaryDevice::RequestSense()
EnterDataInPhase();
}
+void PrimaryDevice::SendDiagnostic()
+{
+ // Do not support PF bit
+ if (ctrl->cmd[1] & 0x10) {
+ throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
+ }
+
+ // Do not support parameter list
+ if ((ctrl->cmd[3] != 0) || (ctrl->cmd[4] != 0)) {
+ throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
+ }
+
+ EnterStatusPhase();
+}
+
void PrimaryDevice::CheckReady()
{
// Not ready if reset
@@ -216,3 +241,61 @@ bool PrimaryDevice::WriteByteSequence(vector&, uint32_t)
return false;
}
+
+void PrimaryDevice::ReserveUnit()
+{
+ reserving_initiator = controller->GetInitiatorId();
+
+ if (reserving_initiator != -1) {
+ LOGTRACE("Reserved device ID %d, LUN %d for initiator ID %d", GetId(), GetLun(), reserving_initiator)
+ }
+ else {
+ LOGTRACE("Reserved device ID %d, LUN %d for unknown initiator", GetId(), GetLun())
+ }
+
+ EnterStatusPhase();
+}
+
+void PrimaryDevice::ReleaseUnit()
+{
+ if (reserving_initiator != -1) {
+ LOGTRACE("Released device ID %d, LUN %d reserved by initiator ID %d", GetId(), GetLun(), reserving_initiator)
+ }
+ else {
+ LOGTRACE("Released device ID %d, LUN %d reserved by unknown initiator", GetId(), GetLun())
+ }
+
+ DiscardReservation();
+
+ EnterStatusPhase();
+}
+
+bool PrimaryDevice::CheckReservation(int initiator_id, scsi_command cmd, bool prevent_removal) const
+{
+ if (reserving_initiator == NOT_RESERVED || reserving_initiator == initiator_id) {
+ return true;
+ }
+
+ // A reservation is valid for all commands except those excluded below
+ if (cmd == scsi_command::eCmdInquiry || cmd == scsi_command::eCmdRequestSense || cmd == scsi_command::eCmdRelease6) {
+ return true;
+ }
+ // PREVENT ALLOW MEDIUM REMOVAL is permitted if the prevent bit is 0
+ if (cmd == scsi_command::eCmdRemoval && !prevent_removal) {
+ return true;
+ }
+
+ if (initiator_id != -1) {
+ LOGTRACE("Initiator ID %d tries to access reserved device ID %d, LUN %d", initiator_id, GetId(), GetLun())
+ }
+ else {
+ LOGTRACE("Unknown initiator tries to access reserved device ID %d, LUN %d", GetId(), GetLun())
+ }
+
+ return false;
+}
+
+void PrimaryDevice::DiscardReservation()
+{
+ reserving_initiator = NOT_RESERVED;
+}
diff --git a/src/raspberrypi/devices/primary_device.h b/src/raspberrypi/devices/primary_device.h
index 574c9046..cbb84ae1 100644
--- a/src/raspberrypi/devices/primary_device.h
+++ b/src/raspberrypi/devices/primary_device.h
@@ -21,7 +21,7 @@ class PrimaryDevice: private ScsiPrimaryCommands, public Device
{
public:
- PrimaryDevice(const string&, int);
+ PrimaryDevice(PbDeviceType, int);
~PrimaryDevice() override = default;
virtual bool Dispatch(scsi_command);
@@ -30,10 +30,15 @@ public:
void SetController(AbstractController *);
virtual bool WriteByteSequence(vector&, uint32_t);
- virtual int GetSendDelay() const { return BUS::SEND_NO_DELAY; }
- // Override for device specific initializations, to be called after all device properties have been set
- virtual bool Init(const unordered_map&) { return true; };
+ int GetSendDelay() const { return send_delay; }
+
+ bool CheckReservation(int, scsi_command, bool) const;
+ void DiscardReservation();
+
+ // Override for device specific initializations
+ virtual bool Init(const unordered_map&) { return false; };
+ void Reset() override;
virtual void FlushCache() {
// Devices with a cache have to implement this method
@@ -45,6 +50,12 @@ protected:
virtual vector InquiryInternal() const = 0;
void CheckReady();
+ void SetSendDelay(int s) { send_delay = s; }
+
+ virtual void SendDiagnostic();
+ virtual void ReserveUnit();
+ virtual void ReleaseUnit();
+
void EnterStatusPhase() { controller->Status(); }
void EnterDataInPhase() { controller->DataIn(); }
void EnterDataOutPhase() { controller->DataOut(); }
@@ -54,6 +65,8 @@ protected:
private:
+ static const int NOT_RESERVED = -2;
+
void TestUnitReady() override;
void RequestSense() override;
void ReportLuns() override;
@@ -62,4 +75,8 @@ private:
vector HandleRequestSense() const;
Dispatcher dispatcher;
+
+ int send_delay = BUS::SEND_NO_DELAY;
+
+ int reserving_initiator = NOT_RESERVED;
};
diff --git a/src/raspberrypi/devices/scsi_command_util.cpp b/src/raspberrypi/devices/scsi_command_util.cpp
index 4f1788a1..5e09617e 100644
--- a/src/raspberrypi/devices/scsi_command_util.cpp
+++ b/src/raspberrypi/devices/scsi_command_util.cpp
@@ -13,8 +13,10 @@
using namespace scsi_defs;
-void scsi_command_util::ModeSelect(const vector& cdb, const vector& buf, int length, int sector_size)
+void scsi_command_util::ModeSelect(scsi_command cmd, const vector& cdb, const vector& buf, int length,
+ int sector_size)
{
+ assert(cmd == scsi_command::eCmdModeSelect6 || cmd == scsi_command::eCmdModeSelect10);
assert(length >= 0);
// PF
@@ -25,7 +27,7 @@ void scsi_command_util::ModeSelect(const vector& cdb, const vector& b
// Skip block descriptors
int offset;
- if ((scsi_command)cdb[0] == scsi_command::eCmdModeSelect10) {
+ if (cmd == scsi_command::eCmdModeSelect10) {
offset = 8 + GetInt16(buf, 6);
}
else {
diff --git a/src/raspberrypi/devices/scsi_command_util.h b/src/raspberrypi/devices/scsi_command_util.h
index 5f95a8d2..2d44230f 100644
--- a/src/raspberrypi/devices/scsi_command_util.h
+++ b/src/raspberrypi/devices/scsi_command_util.h
@@ -11,6 +11,7 @@
#pragma once
+#include "scsi.h"
#include
#include