mirror of
https://github.com/akuker/RASCSI.git
synced 2025-02-09 18:30:38 +00:00
Resolved issues 772, 827, 909; added numerous unit tests; code cleanup (#915)
* Resolved issues 772, 827, 909 * Added numerous unit tests * Code cleanup * Improved type safety by using PbDeviceType instead of string * Do not flush cache on failed STOP UNIT * Error message and error handling updates * Removed duplicate code * Use map for mapping shift counts * Reject read/write access if the drive has 0 sectors * Updated logging configuration for tests
This commit is contained in:
parent
198c10f70a
commit
f3553c5480
@ -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.
|
||||
|
@ -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: <https://www.github.com/akuker/RASCSI/wiki/>
|
||||
Full documentation is available at:
|
||||
<https://www.github.com/akuker/RASCSI/wiki/>
|
||||
|
||||
rascsi(1)
|
||||
rascsi(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'.
|
||||
|
@ -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: <https://www.github.com/akuker/RASCSI/wiki/>
|
||||
Full documentation is available at:
|
||||
<https://www.github.com/akuker/RASCSI/wiki/>
|
||||
|
||||
rascsi(1)
|
||||
rascsi(1)
|
||||
|
@ -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
|
||||
|
@ -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 <sstream>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<shared_ptr<PrimaryDevice>> AbstractController::GetDevices() const
|
||||
{
|
||||
unordered_set<shared_ptr<PrimaryDevice>> 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<PrimaryDevice> device)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractController::DeleteDevice(const shared_ptr<PrimaryDevice> device)
|
||||
bool AbstractController::RemoveDevice(const shared_ptr<PrimaryDevice> 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
|
||||
|
@ -41,7 +41,9 @@ public:
|
||||
};
|
||||
|
||||
using ctrl_t = struct _ctrl_t {
|
||||
vector<int> cmd; // Command data, dynamically allocated per received command
|
||||
// Command data, dynamically resized if required
|
||||
vector<int> cmd = vector<int>(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<shared_ptr<PrimaryDevice>> GetDevices() const;
|
||||
shared_ptr<PrimaryDevice> GetDeviceForLun(int) const;
|
||||
bool AddDevice(shared_ptr<PrimaryDevice>);
|
||||
bool DeleteDevice(const shared_ptr<PrimaryDevice>);
|
||||
bool RemoveDevice(const shared_ptr<PrimaryDevice>);
|
||||
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<scsi_defs::scsi_command>(ctrl.cmd[0]); }
|
||||
int GetLun() const { return (ctrl.cmd[1] >> 5) & 0x07; }
|
||||
|
||||
void ProcessPhase();
|
||||
|
||||
vector<int>& InitCmd(int size) { ctrl.cmd.resize(size); return ctrl.cmd; }
|
||||
vector<int>& 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; }
|
||||
};
|
||||
|
@ -17,18 +17,22 @@ using namespace std;
|
||||
bool ControllerManager::AttachToScsiController(int id, shared_ptr<PrimaryDevice> 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<ScsiController>(bus, id);
|
||||
|
||||
if (controller->AddDevice(device)) {
|
||||
controllers[id] = controller;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return controller->AddDevice(device);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ControllerManager::DeleteController(shared_ptr<AbstractController> controller)
|
||||
@ -79,7 +83,7 @@ void ControllerManager::ResetAllControllers() const
|
||||
|
||||
shared_ptr<PrimaryDevice> 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);
|
||||
}
|
||||
|
||||
|
@ -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<ModePageDevice>(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<BYTE>& buf)
|
||||
case scsi_command::eCmdRead16:
|
||||
// Read from disk
|
||||
try {
|
||||
ctrl.length = (dynamic_pointer_cast<Disk>(GetDeviceForLun(lun)))->Read(ctrl.cmd, buf, ctrl.next);
|
||||
SetLength(dynamic_pointer_cast<Disk>(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<SCSIBR>(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;
|
||||
}
|
||||
|
@ -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 = {};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -9,16 +9,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rascsi_interface.pb.h"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
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<string, string>&);
|
||||
|
||||
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<string, string> GetParams() const { return params; }
|
||||
void SetDefaultParams(const unordered_map<string, string>& p) { default_params = p; }
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "scsi_daynaport.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "host_services.h"
|
||||
#include "rasutil.h"
|
||||
#include "device_factory.h"
|
||||
#include <ifaddrs.h>
|
||||
#include <sys/ioctl.h>
|
||||
@ -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<PrimaryDevice> DeviceFactory::CreateDevice(const ControllerManager& controller_manager, PbDeviceType type,
|
||||
int lun, const string& filename)
|
||||
{
|
||||
@ -102,7 +91,7 @@ shared_ptr<PrimaryDevice> DeviceFactory::CreateDevice(const ControllerManager& c
|
||||
shared_ptr<PrimaryDevice> 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<SCSIHD_NEC>(lun);
|
||||
} else {
|
||||
device = make_shared<SCSIHD>(lun, sector_sizes[SCHD], false,
|
||||
@ -114,35 +103,21 @@ shared_ptr<PrimaryDevice> DeviceFactory::CreateDevice(const ControllerManager& c
|
||||
device->SetProduct("FIREBALL");
|
||||
}
|
||||
}
|
||||
device->SetProtectable(true);
|
||||
device->SetStoppable(true);
|
||||
break;
|
||||
}
|
||||
|
||||
case SCRM:
|
||||
device = make_shared<SCSIHD>(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<SCSIMO>(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<SCSICD>(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<PrimaryDevice> DeviceFactory::CreateDevice(const ControllerManager& c
|
||||
device = make_shared<SCSIBR>(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<PrimaryDevice> 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<PrimaryDevice> DeviceFactory::CreateDevice(const ControllerManager& c
|
||||
case SCLP:
|
||||
device = make_shared<SCSIPrinter>(lun);
|
||||
device->SetProduct("SCSI PRINTER");
|
||||
device->SupportsParams(true);
|
||||
device->SetDefaultParams(default_params[SCLP]);
|
||||
break;
|
||||
|
||||
@ -191,14 +163,6 @@ const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(PbDeviceType type)
|
||||
return it != sector_sizes.end() ? it->second : empty_set;
|
||||
}
|
||||
|
||||
const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(const string& type) const
|
||||
{
|
||||
PbDeviceType t = UNDEFINED;
|
||||