Merge branch 'develop' into feature_bpi3

This commit is contained in:
Tony Kuker 2022-10-21 20:59:39 -05:00
commit a3b91d0e1f
180 changed files with 9215 additions and 6881 deletions

View File

@ -57,5 +57,5 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: | run: |
cd $SOURCES | sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" --define sonar.projectKey=akuker_RASCSI --define sonar.organization=rascsi --define sonar.cfamily.gcov.reportsPath=. --define sonar.cfamily.cache.enabled=false --define sonar.coverage.exclusions="tests/*" --define sonar.python.version=3 cd $SOURCES | sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" --define sonar.projectKey=akuker_RASCSI --define sonar.organization=rascsi --define sonar.cfamily.gcov.reportsPath=. --define sonar.cfamily.cache.enabled=false --define sonar.coverage.exclusions="**/test/**" --define sonar.cpd.exclusions="**test/**" --define sonar.python.version=3

View File

@ -3,13 +3,13 @@
rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins
.SH SYNOPSIS .SH SYNOPSIS
.B rascsi .B rascsi
[\fB\-F\f® \fIFOLDER\fR] [\fB\-F\fR \fIFOLDER\fR]
[\fB\-L\f® \fILOG_LEVEL\fR] [\fB\-L\fR \fILOG_LEVEL\fR]
[\fB\-P\f® \fIACCESS_TOKEN_FILE\fR] [\fB\-P\fR \fIACCESS_TOKEN_FILE\fR]
[\fB\-R\fR \fISCAN_DEPTH\fR] [\fB\-R\fR \fISCAN_DEPTH\fR]
[\fB\-h\fR] [\fB\-h\fR]
[\fB\-n\fR \fIVENDOR:PRODUCT:REVISION\fR] [\fB\-n\fR \fIVENDOR:PRODUCT:REVISION\fR]
[\fB\-p\f® \fIPORT\fR] [\fB\-p\fR \fIPORT\fR]
[\fB\-r\fR \fIRESERVED_IDS\fR] [\fB\-r\fR \fIRESERVED_IDS\fR]
[\fB\-n\fR \fITYPE\fR] [\fB\-n\fR \fITYPE\fR]
[\fB\-v\fR] [\fB\-v\fR]
@ -18,7 +18,7 @@ rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins
[\fB\-HDn[:u]\fR \fIFILE\fR]... [\fB\-HDn[:u]\fR \fIFILE\fR]...
.SH DESCRIPTION .SH DESCRIPTION
.B rascsi .B rascsi
Emulates SCSI devices using the Raspberry Pi GPIO pins. emulates SCSI devices using the Raspberry Pi GPIO pins.
.PP .PP
In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) devices can be specified. In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) devices can be specified.
The number (n) after the ID or HD identifier specifies the ID number for that device. The optional number (u) specifies the LUN (logical unit) for that device. The default LUN is 0. 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.
@ -28,12 +28,12 @@ RaSCSI will determine the type of device based upon the file extension of the FI
hd1: SCSI Hard Disk image (generic, non-removable, SCSI-1) hd1: SCSI Hard Disk image (generic, non-removable, SCSI-1)
hds: SCSI Hard Disk image (generic, non-removable) hds: SCSI Hard Disk image (generic, non-removable)
hdr: SCSI Hard Disk image (generic, removable) hdr: SCSI Hard Disk image (generic, removable)
hdn: SCSI Hard Disk image (NEC GENUINE) hdn: SCSI Hard Disk image (NEC compatible - only used with PC-98 computers)
hdi: SCSI Hard Disk image (Anex86 HD image) hdi: SCSI Hard Disk image (Anex86 proprietary - only used with PC-98 computers)
nhd: SCSI Hard Disk image (T98Next HD image) nhd: SCSI Hard Disk image (T98Next proprietary - only used with PC-98 computers)
hda: SCSI Hard Disk image (APPLE GENUINE - typically used with Mac SCSI emulation) hda: SCSI Hard Disk image (Apple compatible - typically used with Macintosh computers)
mos: SCSI Magneto-optical image (XM6 SCSI MO image - typically only used with X68000) mos: SCSI Magneto-Optical image (generic - typically used with NeXT, X68000, etc.)
iso: SCSI CD-ROM image (ISO 9660 image) 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 sudo rascsi -ID0 /path/to/drive/hdimage.hda
@ -106,6 +106,6 @@ In case the fallocate command is available a much faster alternative to the dd c
fallocate -l 104857600 /path/to/newimage.hda fallocate -l 104857600 /path/to/newimage.hda
.SH SEE ALSO .SH SEE ALSO
rasctl(1), scsimon(1), rasdump(1), sasidump(1) 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/>

View File

@ -1,117 +1,92 @@
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!! !! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
!! ------ The native file is rascsi.1. Re-run 'make docs' after updating\n\n !! ------ The native file is rascsi.1. Re-run 'make docs' after updating
rascsi(1) General Commands Manual rascsi(1)
rascsi(1) General Commands Manual rascsi(1)
NAME NAME
rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins
SYNOPSIS SYNOPSIS
rascsi [-F[u00AE] FOLDER] [-L[u00AE] LOG_LEVEL] [-P[u00AE] ACCESS_TO rascsi [-F FOLDER] [-L LOG_LEVEL] [-P ACCESS_TOKEN_FILE] [-R SCAN_DEPTH] [-h] [-n VENDOR:PRODUCT:REVISION] [-p PORT] [-r
KEN_FILE] [-R SCAN_DEPTH] [-h] [-n VENDOR:PRODUCT:REVISION] [-p[u00AE] RESERVED_IDS] [-n TYPE] [-v] [-z LOCALE] [-IDn:[u] FILE] [-HDn[:u] FILE]...
PORT] [-r RESERVED_IDS] [-n TYPE] [-v] [-z LOCALE] [-IDn:[u] FILE]
[-HDn[:u] FILE]...
DESCRIPTION DESCRIPTION
rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins. rascsi emulates SCSI devices using the Raspberry Pi GPIO pins.
In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) devices can be In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) devices can be specified. The number (n) after the ID or HD iden
specified. The number (n) after the ID or HD identifier specifies the tifier specifies the ID number for that device. The optional number (u) specifies the LUN (logical unit) for that device.
ID number for that device. The optional number (u) specifies the LUN The default LUN is 0. For SCSI: The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator"
(logical unit) for that device. The default LUN is 0. For SCSI: The ID (the host computer). The LUN is limited from 0-31.
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 RaSCSI will determine the type of device based upon the file extension of the FILE argument.
of the FILE argument.
hd1: SCSI Hard Disk image (generic, non-removable, SCSI-1) hd1: SCSI Hard Disk image (generic, non-removable, SCSI-1)
hds: SCSI Hard Disk image (generic, non-removable) hds: SCSI Hard Disk image (generic, non-removable)
hdr: SCSI Hard Disk image (generic, removable) hdr: SCSI Hard Disk image (generic, removable)
hdn: SCSI Hard Disk image (NEC GENUINE) hdn: SCSI Hard Disk image (NEC compatible - only used with PC-98 computers)
hdi: SCSI Hard Disk image (Anex86 HD image) hdi: SCSI Hard Disk image (Anex86 proprietary - only used with PC-98 computers)
nhd: SCSI Hard Disk image (T98Next HD image) nhd: SCSI Hard Disk image (T98Next proprietary - only used with PC-98 computers)
hda: SCSI Hard Disk image (APPLE GENUINE - typically used with Mac hda: SCSI Hard Disk image (Apple compatible - typically used with Macintosh computers)
SCSI emulation) mos: SCSI Magneto-Optical image (generic - typically used with NeXT, X68000, etc.)
mos: SCSI Magneto-optical image (XM6 SCSI MO image - typically only iso: SCSI CD-ROM or DVD-ROM image (ISO 9660 image)
used with X68000)
iso: SCSI CD-ROM image (ISO 9660 image)
For example, if you want to specify an Apple-compatible HD image on ID For example, if you want to specify an Apple-compatible HD image on ID 0, you can use the following command:
0, you can use the following command:
sudo rascsi -ID0 /path/to/drive/hdimage.hda sudo rascsi -ID0 /path/to/drive/hdimage.hda
Once RaSCSI starts, it will open a socket (default port is 6868) to al Once RaSCSI starts, it will open a socket (default port is 6868) to allow external management commands. If another process
low external management commands. If another process is using the is using the rascsi port, RaSCSI will terminate, since it is likely another instance of RaSCSI. Once RaSCSI has initial
rascsi port, RaSCSI will terminate, since it is likely another instance ized, the rasctl utility can be used to send commands.
of RaSCSI. Once RaSCSI has initialized, the rasctl utility can be used
to send commands.
To quit RaSCSI, press Control + C. If it is running in the background, To quit RaSCSI, press Control + C. If it is running in the background, you can kill it using an INT signal.
you can kill it using an INT signal.
OPTIONS OPTIONS
-b BLOCK_SIZE -b BLOCK_SIZE
The optional block size, either 512, 1024, 2048 or 4096 bytes. The optional block size, either 512, 1024, 2048 or 4096 bytes. Default size is 512 bytes.
Default size is 512 bytes.
-F FOLDER -F FOLDER
The default folder for image files. For files in this folder no The default folder for image files. For files in this folder no absolute path needs to be specified. The initial de
absolute path needs to be specified. The initial default folder fault folder is '~/images'.
is '~/images'.
-L LOG_LEVEL -L LOG_LEVEL
The rascsi log level (trace, debug, info, warn, err, critical, The rascsi log level (trace, debug, info, warn, err, critical, off). The default log level is 'info'.
off). The default log level is 'info'.
-P ACCESS_TOKEN_FILE -P ACCESS_TOKEN_FILE
Enable authentication and read the access token from the speci Enable authentication and read the access token from the specified file. The access token file must be owned by root
fied file. The access token file must be owned by root and must and must be readable by root only.
be readable by root only.
-R SCAN_DEPTH -R SCAN_DEPTH
Scan for image files recursively, up to a depth of SCAN_DEPTH. Scan for image files recursively, up to a depth of SCAN_DEPTH. Depth 0 means to ignore any folders within the de
Depth 0 means to ignore any folders within the default image fault image filder. Be careful when using this option with many sub-folders in the default image folder. The default
filder. Be careful when using this option with many sub-folders depth is 1.
in the default image folder. The default depth is 1.
-h Show a help page. -h Show a help page.
-n VENDOR:PRODUCT:REVISION -n VENDOR:PRODUCT:REVISION
Set the vendor, product and revision for the device, to be re Set the vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name
turned with the INQUIRY data. A complete set of name components components must be provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to 4 characters. Padding with
must be provided. VENDOR may have up to 8, PRODUCT up to 16, RE blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed.
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 -p PORT
The rascsi server port, default is 6868. The rascsi server port, default is 6868.
-r RESERVED_IDS -r RESERVED_IDS
Comma-separated list of IDs to reserve. Pass an empty list in Comma-separated list of IDs to reserve. Pass an empty list in order to not reserve anything. -p TYPE The optional
order to not reserve anything. -p TYPE The optional case-insen case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP, SCLP, SCHS). If no type is specified for de
sitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP, vices that support an image file, rascsi tries to derive the type from the file extension.
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. -v Display the rascsi version.
-z LOCALE -z LOCALE
Overrides the default locale for client-faces error messages. Overrides the default locale for client-faces error messages. The client can override the locale.
The client can override the locale.
-IDn[:u] FILE -IDn[:u] FILE
n is the SCSI ID number (0-7). u (0-31) is the optional LUN n is the SCSI ID number (0-7). u (0-31) is the optional LUN (logical unit). The default LUN is 0.
(logical unit). The default LUN is 0.
FILE is the name of the image file to use for the SCSI device. FILE is the name of the image file to use for the SCSI device. For devices that do not support an image file (SCBR,
For devices that do not support an image file (SCBR, SCDP, SCLP, SCDP, SCLP, SCHS) the filename may have a special meaning or a dummy name can be provided. For SCBR and SCDP it is
SCHS) the filename may have a special meaning or a dummy name an optioinal prioritized list of network interfaces, an optional IP address and netmask, e.g. "inter
can be provided. For SCBR and SCDP it is an optioinal priori 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
tized list of network interfaces, an optional IP address and seconds, e.g. "cmd=lp -oraw %f:timeout=60".
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. FILE is the name of the image file to use for the SCSI device.
@ -119,26 +94,22 @@ EXAMPLES
Launch RaSCSI with no emulated drives attached: Launch RaSCSI with no emulated drives attached:
rascsi rascsi
Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID 2
2
rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso 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 de 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
vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter network adapter as ID 6:
as ID 6:
rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp daynaport rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp daynaport
To create an empty, 100MiB HD image, use the following command: To create an empty, 100MiB HD image, use the following command:
dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800 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 In case the fallocate command is available a much faster alternative to the dd command is:
the dd command is:
fallocate -l 104857600 /path/to/newimage.hda fallocate -l 104857600 /path/to/newimage.hda
SEE ALSO SEE ALSO
rasctl(1), scsimon(1), rasdump(1), sasidump(1) rasctl(1), scsimon(1), rasdump(1)
Full documentation is available at: Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
<https://www.github.com/akuker/RASCSI/wiki/>
rascsi(1) rascsi(1)

View File

@ -35,7 +35,7 @@ rasctl \- Sends management commands to the rascsi process
[\fB\-z\fR \fILOCALE\fR] [\fB\-z\fR \fILOCALE\fR]
.SH DESCRIPTION .SH DESCRIPTION
.B rasctl .B rasctl
Sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the devices. sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the devices.
Either the -i or -l option should be specified at one time. Not both. Either the -i or -l option should be specified at one time. Not both.
@ -174,6 +174,6 @@ Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the cont
rasctl -i 0 -f HDIIMAGE0.HDS rasctl -i 0 -f HDIIMAGE0.HDS
.SH SEE ALSO .SH SEE ALSO
rascsi(1), scsimon(1), rasdump(1), sasidump(1) 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/>

View File

@ -1,32 +1,31 @@
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!! !! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
!! ------ The native file is rasctl.1. Re-run 'make docs' after updating\n\n !! ------ The native file is rasctl.1. Re-run 'make docs' after updating
rascsi(1) General Commands Manual rascsi(1)
rascsi(1) General Commands Manual rascsi(1)
NAME NAME
rasctl - Sends management commands to the rascsi process rasctl - Sends management commands to the rascsi process
SYNOPSIS SYNOPSIS
rasctl -e | -l | -m | -o | -s | -v | -D | -I | -L | -O | -P | -T | -V | rasctl -e | -l | -m | -o | -s | -v | -D | -I | -L | -O | -P | -T | -V | -X | [-C FILENAME:FILESIZE] [-E FILENAME] [-F IM
-X | [-C FILENAME:FILESIZE] [-E FILENAME] [-F IMAGE_FOLDER] [-R CUR 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
RENT_NAME:NEW_NAME] [-c CMD] [-f FILE|PARAM] [-g LOG_LEVEL] [-h HOST] SERVED_IDS] [-t TYPE] [-u UNIT] [-x CURRENT_NAME:NEW_NAME] [-z LOCALE]
[-i ID [-n NAME] [-p PORT] [-r RESERVED_IDS] [-t TYPE] [-u UNIT] [-x
CURRENT_NAME:NEW_NAME] [-z LOCALE]
DESCRIPTION DESCRIPTION
rasctl Sends commands to the rascsi process to make configuration ad rasctl sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the de
justments at runtime or to check the status of the devices. vices.
Either the -i or -l option should be specified at one time. Not both. Either the -i or -l option should be specified at one time. Not both.
You do NOT need root privileges to use rasctl. You do NOT need root privileges to use rasctl.
Note: The command and type arguments are case insensitive. Only the Note: The command and type arguments are case insensitive. Only the first letter of the command/type is evaluated by the
first letter of the command/type is evaluated by the tool. tool.
OPTIONS OPTIONS
-C FILENAME:FILESIZE -C FILENAME:FILESIZE
Create an image file in the default image folder with the speci Create an image file in the default image folder with the specified name and size in bytes.
fied name and size in bytes.
-D Detach all devices. -D Detach all devices.
@ -39,28 +38,22 @@ OPTIONS
-I Gets the list of reserved device IDs. -I Gets the list of reserved device IDs.
-L LOG_LEVEL -L LOG_LEVEL
Set the rascsi log level (trace, debug, info, warn, err, criti Set the rascsi log level (trace, debug, info, warn, err, critical, off).
cal, off).
-h HOST -h HOST
The rascsi host to connect to, default is 'localhost'. The rascsi host to connect to, default is 'localhost'.
-e List all images files in the default image folder. -e List all images files in the default image folder.
-N Lists all available network interfaces provided that they are -N Lists all available network interfaces provided that they are up.
up.
-O Display the available rascsi server log levels and the current -O Display the available rascsi server log levels and the current log level.
log level.
-P Prompt for the access token in case rascsi requires authentica -P Prompt for the access token in case rascsi requires authentication.
tion.
-l List all of the devices that are currently being emulated by -l List all of the devices that are currently being emulated by RaSCSI, as well as their current status.
RaSCSI, as well as their current status.
-m List all file extensions recognized by RaSCSI and the device -m List all file extensions recognized by RaSCSI and the device types they map to.
types they map to.
-o Display operation meta data information. -o Display operation meta data information.
@ -71,11 +64,9 @@ OPTIONS
The rascsi port to connect to, default is 6868. The rascsi port to connect to, default is 6868.
-r RESERVED_IDS -r RESERVED_IDS
Comma-separated list of IDs to reserve. Pass an empty list in Comma-separated list of IDs to reserve. Pass an empty list in order to not reserve anything.
order to not reserve anything.
-s Display server-side settings like available images or supported -s Display server-side settings like available images or supported device types.
device types.
-T Display all device types and their properties. -T Display all device types and their properties.
@ -101,28 +92,23 @@ OPTIONS
d(etach): Detach disk d(etach): Detach disk
i(nsert): Insert media (removable media devices only) i(nsert): Insert media (removable media devices only)
e(ject): Eject media (removable media devices only) e(ject): Eject media (removable media devices only)
p(rotect): Write protect the medium (not for CD-ROMs, which p(rotect): Write protect the medium (not for CD-ROMs, which are always read-only)
are always read-only) u(nprotect): Remove write protection from 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 s(how): Display device information
eject, protect and unprotect are idempotent. eject, protect and unprotect are idempotent.
-b BLOCK_SIZE -b BLOCK_SIZE
The optional block size, either 512, 1024, 2048 or 4096 bytes. The optional block size, either 512, 1024, 2048 or 4096 bytes. The default size is 512 bytes.
The default size is 512 bytes.
-f FILE|PARAM -f FILE|PARAM
Device-specific: Either a path to a disk image file, or a param Device-specific: Either a path to a disk image file, or a parameter for a non-disk device. See the rascsi(1) man
eter for a non-disk device. See the rascsi(1) man page for per page for permitted file types.
mitted file types.
-t TYPE -t TYPE
Specifies the device type. This type overrides the type derived Specifies the device type. This type overrides the type derived from the file extension of the specified image. See
from the file extension of the specified image. See the the rascsi(1) man page for the available device types. For some types there are shortcuts (only the first letter is
rascsi(1) man page for the available device types. For some required):
types there are shortcuts (only the first letter is required):
hd: SCSI hard disk drive hd: SCSI hard disk drive
rm: SCSI removable media drive rm: SCSI removable media drive
cd: CD-ROM cd: CD-ROM
@ -133,17 +119,13 @@ OPTIONS
services: Host services device services: Host services device
-n VENDOR:PRODUCT:REVISION -n VENDOR:PRODUCT:REVISION
The vendor, product and revision for the device, to be returned The vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name compo
with the INQUIRY data. A complete set of name components must be nents must be provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to 4 characters. Padding with blanks
provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to the maxium length is automatically applied. Once set the name of a device cannot be changed.
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 -u UNIT
Unit number (0-31). This will default to 0. This option is only Unit number (0-31). This will default to 0. This option is only used when there are multiple SCSI devices on a
used when there are multiple SCSI devices on a shared SCSI con shared SCSI controller. (This is not common)
troller. (This is not common)
EXAMPLES EXAMPLES
Show a listing of all of the SCSI devices and their current status. Show a listing of all of the SCSI devices and their current status.
@ -156,14 +138,13 @@ EXAMPLES
| 0 | 1 | SCHD | /home/pi/harddisk.hda | 0 | 1 | SCHD | /home/pi/harddisk.hda
+----+-----+------+------------------------------------- +----+-----+------+-------------------------------------
Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the contents of the file system image "HDIIM
the contents of the file system image "HDIIMAGE0.HDS". AGE0.HDS".
rasctl -i 0 -f HDIIMAGE0.HDS rasctl -i 0 -f HDIIMAGE0.HDS
SEE ALSO SEE ALSO
rascsi(1), scsimon(1), rasdump(1), sasidump(1) rascsi(1), scsimon(1), rasdump(1)
Full documentation is available at: Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
<https://www.github.com/akuker/RASCSI/wiki/>
rascsi(1) rascsi(1)

View File

@ -9,7 +9,7 @@ rasdump \- SCSI disk dumping tool for RaSCSI
[\fB\-r\fR] [\fB\-r\fR]
.SH DESCRIPTION .SH DESCRIPTION
.B rasdump .B rasdump
Samples the data on physical SCSI storage media, including hard drives and MO drives, and stores it to an image file. It can also restore from a dumped file onto physical SCSI storage media. Can be connected directly, through a STANDARD RaSCSI board, or a FULLSPEC RaSCSI board. samples the data on physical SCSI storage media, including hard drives and MO drives, and stores it to an image file. It can also restore from a dumped file onto physical SCSI storage media. Can be connected directly, through a STANDARD RaSCSI board, or a FULLSPEC RaSCSI board.
Set its own ID with the BID option. Defaults to 7 if ommitted. Set its own ID with the BID option. Defaults to 7 if ommitted.

View File

@ -5,7 +5,7 @@ scsimon \- Acts as a data capture tool for all traffic on the SCSI bus. Data is
.B scsimon .B scsimon
.SH DESCRIPTION .SH DESCRIPTION
.B scsimon .B scsimon
Monitors all of the traffic on the SCSI bus, using a RaSCSI device. The data is cached in memory while the tool is running. A circular buffer is used so that only the most recent 1,000,000 transactions are stored. The tool will continue to run until the user presses CTRL-C, or the process receives a SIGINT signal. monitors all of the traffic on the SCSI bus, using a RaSCSI device. The data is cached in memory while the tool is running. A circular buffer is used so that only the most recent 1,000,000 transactions are stored. The tool will continue to run until the user presses CTRL-C, or the process receives a SIGINT signal.
.PP .PP
The logged data is stored in a file called "log.vcd" in the current working directory from where scsimon was launched. The logged data is stored in a file called "log.vcd" in the current working directory from where scsimon was launched.
@ -22,6 +22,6 @@ Launch scsimon to capture all SCSI traffic available to the RaSCSI hardware:
scsimon scsimon
.SH SEE ALSO .SH SEE ALSO
rasctl(1), rascsi(1), rasdump(1), sasidump(1) rasctl(1), rascsi(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/>

View File

@ -2,24 +2,20 @@
!! ------ The native file is scsimon.1. Re-run 'make docs' after updating !! ------ The native file is scsimon.1. Re-run 'make docs' after updating
scsimon(1) General Commands Manual scsimon(1) scsimon(1) General Commands Manual scsimon(1)
NAME NAME
scsimon - Acts as a data capture tool for all traffic on the SCSI bus. scsimon - Acts as a data capture tool for all traffic on the SCSI bus. Data is stored in a Value Change Dump (VCD) file.
Data is stored in a Value Change Dump (VCD) file.
SYNOPSIS SYNOPSIS
scsimon scsimon
DESCRIPTION DESCRIPTION
scsimon Monitors all of the traffic on the SCSI bus, using a RaSCSI de scsimon monitors all of the traffic on the SCSI bus, using a RaSCSI device. The data is cached in memory while the tool is
vice. The data is cached in memory while the tool is running. A circu running. A circular buffer is used so that only the most recent 1,000,000 transactions are stored. The tool will continue
lar buffer is used so that only the most recent 1,000,000 transactions to run until the user presses CTRL-C, or the process receives a SIGINT signal.
are stored. The tool will continue to run until the user presses CTRL-
C, or the process receives a SIGINT signal.
The logged data is stored in a file called "log.vcd" in the current The logged data is stored in a file called "log.vcd" in the current working directory from where scsimon was launched.
working directory from where scsimon was launched.
Currently, scsimon doesn't accept any arguments. Currently, scsimon doesn't accept any arguments.
@ -29,14 +25,12 @@ OPTIONS
None None
EXAMPLES EXAMPLES
Launch scsimon to capture all SCSI traffic available to the RaSCSI Launch scsimon to capture all SCSI traffic available to the RaSCSI hardware:
hardware:
scsimon scsimon
SEE ALSO SEE ALSO
rasctl(1), rascsi(1), rasdump(1), sasidump(1) rasctl(1), rascsi(1), rasdump(1)
Full documentation is available at: Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
<https://www.github.com/akuker/RASCSI/wiki/>
scsimon(1) scsimon(1)

View File

@ -55,3 +55,17 @@ services:
"--log-level=${WEB_LOG_LEVEL:-info}", "--log-level=${WEB_LOG_LEVEL:-info}",
"--dev-mode" "--dev-mode"
] ]
pytest:
container_name: pytest
image: rascsi:pytest
pull_policy: never
profiles:
- webui-tests
build:
context: ..
dockerfile: docker/pytest/Dockerfile
volumes:
- ../python/web:/src:delegated
working_dir: /src
entrypoint: "pytest"

5
docker/pytest/Dockerfile Normal file
View File

@ -0,0 +1,5 @@
FROM python:3.7-bullseye
ENV DOCKER=1
COPY python/web/requirements-dev.txt /requirements-dev.txt
RUN pip install -r /requirements-dev.txt

View File

@ -6,7 +6,7 @@ FROM "${OS_ARCH}/${OS_DISTRO}:${OS_VERSION}"
EXPOSE 80 443 EXPOSE 80 443
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends sudo systemd rsyslog procps RUN apt-get update && apt-get install -y --no-install-recommends sudo systemd rsyslog procps man-db man2html
RUN groupadd pi RUN groupadd pi
RUN useradd --create-home --shell /bin/bash -g pi pi RUN useradd --create-home --shell /bin/bash -g pi pi

View File

@ -1 +0,0 @@
theme: jekyll-theme-dinky

View File

@ -1,37 +0,0 @@
## Welcome to GitHub Pages
You can use the [editor on GitHub](https://github.com/akuker/RASCSI/edit/master/docs/index.md) to maintain and preview the content for your website in Markdown files.
Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files.
### Markdown
Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for
```markdown
Syntax highlighted code block
# Header 1
## Header 2
### Header 3
- Bulleted
- List
1. Numbered
2. List
**Bold** and _Italic_ and `Code` text
[Link](url) and ![Image](src)
```
For more details see [GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/).
### Jekyll Themes
Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/akuker/RASCSI/settings). The name of this theme is saved in the Jekyll `_config.yml` configuration file.
### Support or Contact
Having trouble with Pages? Check out our [documentation](https://docs.github.com/categories/github-pages-basics/) or [contact support](https://support.github.com/contact) and well help you sort it out.

View File

@ -104,7 +104,8 @@ function installPackages() {
unzip \ unzip \
unar \ unar \
disktype \ disktype \
libgmock-dev libgmock-dev \
man2html
} }
# install Debian packges for RaSCSI standalone # install Debian packges for RaSCSI standalone
@ -116,7 +117,8 @@ function installPackagesStandalone() {
libprotobuf-dev \ libprotobuf-dev \
protobuf-compiler \ protobuf-compiler \
disktype \ disktype \
libgmock-dev libgmock-dev \
man2html
} }
# cache the pip packages # cache the pip packages
@ -832,6 +834,20 @@ function installNetatalk() {
NETATALK_VERSION="2-220801" NETATALK_VERSION="2-220801"
AFP_SHARE_PATH="$HOME/afpshare" AFP_SHARE_PATH="$HOME/afpshare"
AFP_SHARE_NAME="Pi File Server" AFP_SHARE_NAME="Pi File Server"
NETATALK_CONFIG_PATH="/etc/netatalk"
if [ -d "$NETATALK_CONFIG_PATH" ]; then
echo
echo "WARNING: Netatalk configuration dir $NETATALK_CONFIG_PATH already exists."
echo "This installation process will overwrite existing Netatalk applications and configurations."
echo "No shared files will be deleted, but you may have to manually restore your settings after the installation."
echo
echo "Do you want to proceed with the installation? [y/N]"
read -r REPLY
if ! [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then
exit 0
fi
fi
echo "Downloading netatalk-$NETATALK_VERSION to $HOME" echo "Downloading netatalk-$NETATALK_VERSION to $HOME"
cd $HOME || exit 1 cd $HOME || exit 1
@ -842,6 +858,39 @@ function installNetatalk() {
./debian_install.sh -j="${CORES:-1}" -n="$AFP_SHARE_NAME" -p="$AFP_SHARE_PATH" || exit 1 ./debian_install.sh -j="${CORES:-1}" -n="$AFP_SHARE_NAME" -p="$AFP_SHARE_PATH" || exit 1
} }
# Appends the images dir as a shared Netatalk volume
function shareImagesWithNetatalk() {
APPLEVOLUMES_PATH="/etc/netatalk/AppleVolumes.default"
if ! [ -f "$APPLEVOLUMES_PATH" ]; then
echo "Could not find $APPLEVOLUMES_PATH ... is Netatalk installed?"
exit 1
fi
if [ "$(grep -c "$VIRTUAL_DRIVER_PATH" "$APPLEVOLUMES_PATH")" -ge 1 ]; then
echo "The $VIRTUAL_DRIVER_PATH dir is already shared in $APPLEVOLUMES_PATH"
echo "Do you want to turn off the sharing? [y/N]"
read -r REPLY
if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then
sudo systemctl stop afpd
sudo sed -i '\,^'"$VIRTUAL_DRIVER_PATH"',d' "$APPLEVOLUMES_PATH"
echo "Sharing for $VIRTUAL_DRIVER_PATH disabled!"
sudo systemctl start afpd
exit 0
fi
exit 0
fi
sudo systemctl stop afpd
echo "Appended to AppleVolumes.default:"
echo "$VIRTUAL_DRIVER_PATH \"RaSCSI Images\"" | sudo tee -a "$APPLEVOLUMES_PATH"
sudo systemctl start afpd
echo
echo "WARNING: Do not inadvertently move or rename image files that are in use by RaSCSI."
echo "Doing so may lead to data loss."
echo
}
# Downloads, compiles, and installs Macproxy (web proxy) # Downloads, compiles, and installs Macproxy (web proxy)
function installMacproxy { function installMacproxy {
PORT=5000 PORT=5000
@ -1301,6 +1350,10 @@ function runChoice() {
showRaScsiCtrlBoardStatus showRaScsiCtrlBoardStatus
echo "Installing / Updating RaSCSI Control Board UI - Complete!" echo "Installing / Updating RaSCSI Control Board UI - Complete!"
;; ;;
13)
shareImagesWithNetatalk
echo "Configuring AppleShare File Server - Complete!"
;;
-h|--help|h|help) -h|--help|h|help)
showMenu showMenu
;; ;;
@ -1314,8 +1367,8 @@ function runChoice() {
function readChoice() { function readChoice() {
choice=-1 choice=-1
until [ $choice -ge "0" ] && [ $choice -le "12" ]; do until [ $choice -ge "0" ] && [ $choice -le "13" ]; do
echo -n "Enter your choice (0-12) or CTRL-C to exit: " echo -n "Enter your choice (0-13) or CTRL-C to exit: "
read -r choice read -r choice
done done
@ -1345,6 +1398,7 @@ function showMenu() {
echo " 11) configure the RaSCSI Web Interface stand-alone" echo " 11) configure the RaSCSI Web Interface stand-alone"
echo "EXPERIMENTAL FEATURES" echo "EXPERIMENTAL FEATURES"
echo " 12) install or update RaSCSI Control Board UI (requires hardware)" echo " 12) install or update RaSCSI Control Board UI (requires hardware)"
echo " 13) share the images dir over AppleShare (requires Netatalk)"
} }
# parse arguments passed to the script # parse arguments passed to the script

View File

@ -23,4 +23,7 @@ ARCHIVE_FILE_SUFFIXES = [
# The RESERVATIONS list is used to keep track of the reserved ID memos. # The RESERVATIONS list is used to keep track of the reserved ID memos.
# Initialize with a list of 8 empty strings. # Initialize with a list of 8 empty strings.
RESERVATIONS = ["" for x in range(0, 8)] RESERVATIONS = ["" for _ in range(0, 8)]
# Standard error message for shell commands
SHELL_ERROR = "Shell command: \"%s\" led to error: %s"

View File

@ -6,22 +6,33 @@ import os
import logging import logging
import asyncio import asyncio
from functools import lru_cache from functools import lru_cache
from pathlib import PurePath from pathlib import PurePath, Path
from zipfile import ZipFile, is_zipfile from zipfile import ZipFile, is_zipfile
from time import time from time import time
from subprocess import run, CalledProcessError from subprocess import run, CalledProcessError
from json import dump, load from json import dump, load
from shutil import copyfile from shutil import copyfile
from urllib.parse import quote
import requests import requests
import rascsi_interface_pb2 as proto import rascsi_interface_pb2 as proto
from rascsi.common_settings import CFG_DIR, CONFIG_FILE_SUFFIX, PROPERTIES_SUFFIX, ARCHIVE_FILE_SUFFIXES, RESERVATIONS from rascsi.common_settings import (
CFG_DIR,
CONFIG_FILE_SUFFIX,
PROPERTIES_SUFFIX,
ARCHIVE_FILE_SUFFIXES,
RESERVATIONS,
SHELL_ERROR,
)
from rascsi.ractl_cmds import RaCtlCmds from rascsi.ractl_cmds import RaCtlCmds
from rascsi.return_codes import ReturnCodes from rascsi.return_codes import ReturnCodes
from rascsi.socket_cmds import SocketCmds from rascsi.socket_cmds import SocketCmds
from util import unarchiver from util import unarchiver
FILE_READ_ERROR = "Unhandled exception when reading file: %s"
FILE_WRITE_ERROR = "Unhandled exception when writing to file: %s"
URL_SAFE = "/:?&"
class FileCmds: class FileCmds:
""" """
@ -94,7 +105,9 @@ class FileCmds:
for file in result.image_files_info.image_files: for file in result.image_files_info.image_files:
# Add properties meta data for the image, if applicable # Add properties meta data for the image, if applicable
if file.name in prop_files: if file.name in prop_files:
process = self.read_drive_properties(f"{CFG_DIR}/{file.name}.{PROPERTIES_SUFFIX}") process = self.read_drive_properties(
Path(CFG_DIR) / f"{file.name}.{PROPERTIES_SUFFIX}"
)
prop = process["conf"] prop = process["conf"]
else: else:
prop = False prop = False
@ -148,7 +161,7 @@ class FileCmds:
command.params["token"] = self.token command.params["token"] = self.token
command.params["locale"] = self.locale command.params["locale"] = self.locale
command.params["file"] = file_name + "." + file_type command.params["file"] = f"{file_name}.{file_type}"
command.params["size"] = str(size) command.params["size"] = str(size)
command.params["read_only"] = "false" command.params["read_only"] = "false"
@ -216,14 +229,15 @@ class FileCmds:
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
def delete_file(self, file_path): def delete_file(self, file_path):
""" """
Takes (str) file_path with the full path to the file to delete Takes (Path) file_path for the file to delete
Returns (dict) with (bool) status and (str) msg Returns (dict) with (bool) status and (str) msg
""" """
parameters = { parameters = {
"file_path": file_path "file_path": file_path
} }
if os.path.exists(file_path):
os.remove(file_path) if file_path.exists():
file_path.unlink()
return { return {
"status": True, "status": True,
"return_code": ReturnCodes.DELETEFILE_SUCCESS, "return_code": ReturnCodes.DELETEFILE_SUCCESS,
@ -238,14 +252,16 @@ class FileCmds:
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
def rename_file(self, file_path, target_path): def rename_file(self, file_path, target_path):
""" """
Takes (str) file_path and (str) target_path Takes:
- (Path) file_path for the file to rename
- (Path) target_path for the name to rename
Returns (dict) with (bool) status and (str) msg Returns (dict) with (bool) status and (str) msg
""" """
parameters = { parameters = {
"target_path": target_path "target_path": target_path
} }
if os.path.exists(PurePath(target_path).parent): if target_path.parent.exists:
os.rename(file_path, target_path) file_path.rename(target_path)
return { return {
"status": True, "status": True,
"return_code": ReturnCodes.RENAMEFILE_SUCCESS, "return_code": ReturnCodes.RENAMEFILE_SUCCESS,
@ -260,14 +276,16 @@ class FileCmds:
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
def copy_file(self, file_path, target_path): def copy_file(self, file_path, target_path):
""" """
Takes (str) file_path and (str) target_path Takes:
- (Path) file_path for the file to copy from
- (Path) target_path for the name to copy to
Returns (dict) with (bool) status and (str) msg Returns (dict) with (bool) status and (str) msg
""" """
parameters = { parameters = {
"target_path": target_path "target_path": target_path
} }
if os.path.exists(PurePath(target_path).parent): if target_path.parent.exists:
copyfile(file_path, target_path) copyfile(str(file_path), str(target_path))
return { return {
"status": True, "status": True,
"return_code": ReturnCodes.WRITEFILE_SUCCESS, "return_code": ReturnCodes.WRITEFILE_SUCCESS,
@ -305,21 +323,22 @@ class FileCmds:
properties_files_moved = [] properties_files_moved = []
if move_properties_files_to_config: if move_properties_files_to_config:
for file in extract_result["extracted"]: for file in extract_result["extracted"]:
if file.get("name").endswith(".properties"): if file.get("name").endswith(f".{PROPERTIES_SUFFIX}"):
prop_path = Path(CFG_DIR) / file["name"]
if (self.rename_file( if (self.rename_file(
file["absolute_path"], Path(file["absolute_path"]),
f"{CFG_DIR}/{file['name']}" prop_path,
)): )):
properties_files_moved.append({ properties_files_moved.append({
"status": True, "status": True,
"name": file["path"], "name": file["path"],
"path": f"{CFG_DIR}/{file['name']}", "path": str(prop_path),
}) })
else: else:
properties_files_moved.append({ properties_files_moved.append({
"status": False, "status": False,
"name": file["path"], "name": file["path"],
"path": f"{CFG_DIR}/{file['name']}", "path": str(prop_path),
}) })
return { return {
@ -362,7 +381,7 @@ class FileCmds:
tmp_full_path = tmp_dir + file_name tmp_full_path = tmp_dir + file_name
iso_filename = f"{server_info['image_dir']}/{file_name}.iso" iso_filename = f"{server_info['image_dir']}/{file_name}.iso"
req_proc = self.download_to_dir(url, tmp_dir, file_name) req_proc = self.download_to_dir(quote(url, safe=URL_SAFE), tmp_dir, file_name)
if not req_proc["status"]: if not req_proc["status"]:
return {"status": False, "msg": req_proc["msg"]} return {"status": False, "msg": req_proc["msg"]}
@ -386,7 +405,7 @@ class FileCmds:
"%s was successfully unzipped. Deleting the zipfile.", "%s was successfully unzipped. Deleting the zipfile.",
tmp_full_path, tmp_full_path,
) )
self.delete_file(tmp_full_path) self.delete_file(Path(tmp_full_path))
try: try:
run( run(
@ -401,8 +420,7 @@ class FileCmds:
check=True, check=True,
) )
except CalledProcessError as error: except CalledProcessError as error:
logging.warning("Executed shell command: %s", " ".join(error.cmd)) logging.warning(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
logging.warning("Got error: %s", error.stderr.decode("utf-8"))
return {"status": False, "msg": error.stderr.decode("utf-8")} return {"status": False, "msg": error.stderr.decode("utf-8")}
parameters = { parameters = {
@ -424,7 +442,11 @@ class FileCmds:
logging.info("Making a request to download %s", url) logging.info("Making a request to download %s", url)
try: try:
with requests.get(url, stream=True, headers={"User-Agent": "Mozilla/5.0"}) as req: with requests.get(
quote(url, safe=URL_SAFE),
stream=True,
headers={"User-Agent": "Mozilla/5.0"},
) as req:
req.raise_for_status() req.raise_for_status()
with open(f"{save_dir}/{file_name}", "wb") as download: with open(f"{save_dir}/{file_name}", "wb") as download:
for chunk in req.iter_content(chunk_size=8192): for chunk in req.iter_content(chunk_size=8192):
@ -452,9 +474,9 @@ class FileCmds:
Takes (str) file_name Takes (str) file_name
Returns (dict) with (bool) status and (str) msg Returns (dict) with (bool) status and (str) msg
""" """
file_name = f"{CFG_DIR}/{file_name}" file_path = f"{CFG_DIR}/{file_name}"
try: try:
with open(file_name, "w", encoding="ISO-8859-1") as json_file: with open(file_path, "w", encoding="ISO-8859-1") as json_file:
version = self.ractl.get_server_info()["version"] version = self.ractl.get_server_info()["version"]
devices = self.ractl.list_devices()["device_list"] devices = self.ractl.list_devices()["device_list"]
for device in devices: for device in devices:
@ -485,7 +507,7 @@ class FileCmds:
indent=4 indent=4
) )
parameters = { parameters = {
"target_path": file_name "target_path": file_path
} }
return { return {
"status": True, "status": True,
@ -494,28 +516,21 @@ class FileCmds:
} }
except (IOError, ValueError, EOFError, TypeError) as error: except (IOError, ValueError, EOFError, TypeError) as error:
logging.error(str(error)) logging.error(str(error))
self.delete_file(file_name) self.delete_file(Path(file_path))
return {"status": False, "msg": str(error)} return {"status": False, "msg": str(error)}
except: except:
logging.error("Could not write to file: %s", file_name) logging.error(FILE_WRITE_ERROR, file_name)
self.delete_file(file_name) self.delete_file(Path(file_path))
parameters = { raise
"file_name": file_name
}
return {
"status": False,
"return_code": ReturnCodes.WRITEFILE_COULD_NOT_WRITE,
"parameters": parameters,
}
def read_config(self, file_name): def read_config(self, file_name):
""" """
Takes (str) file_name Takes (str) file_name
Returns (dict) with (bool) status and (str) msg Returns (dict) with (bool) status and (str) msg
""" """
file_name = f"{CFG_DIR}/{file_name}" file_path = Path(CFG_DIR) / file_name
try: try:
with open(file_name, encoding="ISO-8859-1") as json_file: with open(file_path, encoding="ISO-8859-1") as json_file:
config = load(json_file) config = load(json_file)
# If the config file format changes again in the future, # If the config file format changes again in the future,
# introduce more sophisticated format detection logic here. # introduce more sophisticated format detection logic here.
@ -577,15 +592,8 @@ class FileCmds:
logging.error(str(error)) logging.error(str(error))
return {"status": False, "msg": str(error)} return {"status": False, "msg": str(error)}
except: except:
logging.error("Could not read file: %s", file_name) logging.error(FILE_READ_ERROR, str(file_path))
parameters = { raise
"file_name": file_name
}
return {
"status": False,
"return_code": ReturnCodes.READCONFIG_COULD_NOT_READ,
"parameters": parameters
}
def write_drive_properties(self, file_name, conf): def write_drive_properties(self, file_name, conf):
""" """
@ -593,12 +601,12 @@ class FileCmds:
Takes file name base (str) and (list of dicts) conf as arguments Takes file name base (str) and (list of dicts) conf as arguments
Returns (dict) with (bool) status and (str) msg Returns (dict) with (bool) status and (str) msg
""" """
file_path = f"{CFG_DIR}/{file_name}" file_path = Path(CFG_DIR) / file_name
try: try:
with open(file_path, "w") as json_file: with open(file_path, "w") as json_file:
dump(conf, json_file, indent=4) dump(conf, json_file, indent=4)
parameters = { parameters = {
"target_path": file_path "target_path": str(file_path)
} }
return { return {
"status": True, "status": True,
@ -610,29 +618,22 @@ class FileCmds:
self.delete_file(file_path) self.delete_file(file_path)
return {"status": False, "msg": str(error)} return {"status": False, "msg": str(error)}
except: except:
logging.error("Could not write to file: %s", file_path) logging.error(FILE_WRITE_ERROR, str(file_path))
self.delete_file(file_path) self.delete_file(file_path)
parameters = { raise
"target_path": file_path
}
return {
"status": False,
"return_code": ReturnCodes.WRITEFILE_COULD_NOT_WRITE,
"parameters": parameters,
}
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
def read_drive_properties(self, file_path): def read_drive_properties(self, file_path):
""" """
Reads drive properties from json formatted file. Reads drive properties from json formatted file.
Takes (str) file_path as argument. Takes (Path) file_path as argument.
Returns (dict) with (bool) status, (str) msg, (dict) conf Returns (dict) with (bool) status, (str) msg, (dict) conf
""" """
try: try:
with open(file_path) as json_file: with open(file_path) as json_file:
conf = load(json_file) conf = load(json_file)
parameters = { parameters = {
"file_path": file_path "file_path": str(file_path)
} }
return { return {
"status": True, "status": True,
@ -644,15 +645,8 @@ class FileCmds:
logging.error(str(error)) logging.error(str(error))
return {"status": False, "msg": str(error)} return {"status": False, "msg": str(error)}
except: except:
logging.error("Could not read file: %s", file_path) logging.error(FILE_READ_ERROR, str(file_path))
parameters = { raise
"file_path": file_path
}
return {
"status": False,
"return_codes": ReturnCodes.READDRIVEPROPS_COULD_NOT_READ,
"parameters": parameters,
}
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
async def run_async(self, program, args): async def run_async(self, program, args):
@ -687,5 +681,6 @@ class FileCmds:
""" """
try: try:
return unarchiver.inspect_archive(file_path) return unarchiver.inspect_archive(file_path)
except (unarchiver.LsarCommandError, unarchiver.LsarOutputError): except (unarchiver.LsarCommandError, unarchiver.LsarOutputError) as error:
logging.error(str(error))
raise raise

View File

@ -224,16 +224,13 @@ class RaCtlCmds:
devices = proto.PbDeviceDefinition() devices = proto.PbDeviceDefinition()
devices.id = int(scsi_id) devices.id = int(scsi_id)
if "device_type" in kwargs.keys(): if kwargs.get("device_type"):
if kwargs["device_type"]: devices.type = proto.PbDeviceType.Value(str(kwargs["device_type"]))
devices.type = proto.PbDeviceType.Value(str(kwargs["device_type"])) if kwargs.get("unit"):
if "unit" in kwargs.keys(): devices.unit = kwargs["unit"]
if kwargs["unit"]: if kwargs.get("params") and isinstance(kwargs["params"], dict):
devices.unit = kwargs["unit"] for param in kwargs["params"]:
if "params" in kwargs.keys(): devices.params[param] = kwargs["params"][param]
if isinstance(kwargs["params"], dict):
for param in kwargs["params"]:
devices.params[param] = kwargs["params"][param]
# Handling the inserting of media into an attached removable type device # Handling the inserting of media into an attached removable type device
device_type = kwargs.get("device_type", None) device_type = kwargs.get("device_type", None)
@ -260,18 +257,14 @@ class RaCtlCmds:
# Handling attaching a new device # Handling attaching a new device
else: else:
command.operation = proto.PbOperation.ATTACH command.operation = proto.PbOperation.ATTACH
if "vendor" in kwargs.keys(): if kwargs.get("vendor"):
if kwargs["vendor"]: devices.vendor = kwargs["vendor"]
devices.vendor = kwargs["vendor"] if kwargs.get("product"):
if "product" in kwargs.keys(): devices.product = kwargs["product"]
if kwargs["product"]: if kwargs.get("revision"):
devices.product = kwargs["product"] devices.revision = kwargs["revision"]
if "revision" in kwargs.keys(): if kwargs.get("block_size"):
if kwargs["revision"]: devices.block_size = int(kwargs["block_size"])
devices.revision = kwargs["revision"]
if "block_size" in kwargs.keys():
if kwargs["block_size"]:
devices.block_size = int(kwargs["block_size"])
command.devices.append(devices) command.devices.append(devices)

View File

@ -8,6 +8,7 @@ from shutil import disk_usage
from re import findall, match from re import findall, match
from socket import socket, gethostname, AF_INET, SOCK_DGRAM from socket import socket, gethostname, AF_INET, SOCK_DGRAM
from rascsi.common_settings import SHELL_ERROR
class SysCmds: class SysCmds:
""" """
@ -32,8 +33,7 @@ class SysCmds:
.strip() .strip()
) )
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
logging.warning("Executed shell command: %s", " ".join(error.cmd)) logging.warning(SHELL_ERROR, error.cmd, error.stderr.decode("utf-8"))
logging.warning("Got error: %s", error.stderr.decode("utf-8"))
ra_git_version = "" ra_git_version = ""
try: try:
@ -47,9 +47,8 @@ class SysCmds:
.strip() .strip()
) )
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
logging.warning("Executed shell command: %s", " ".join(error.cmd)) logging.warning(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
logging.warning("Got error: %s", error.stderr.decode("utf-8")) pi_version = "?"
pi_version = "Unknown"
return {"git": ra_git_version, "env": pi_version} return {"git": ra_git_version, "env": pi_version}
@ -70,8 +69,7 @@ class SysCmds:
.strip() .strip()
) )
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
logging.warning("Executed shell command: %s", " ".join(error.cmd)) logging.warning(SHELL_ERROR, error.cmd, error.stderr.decode("utf-8"))
logging.warning("Got error: %s", error.stderr.decode("utf-8"))
processes = "" processes = ""
matching_processes = findall(daemon, processes) matching_processes = findall(daemon, processes)
@ -93,8 +91,7 @@ class SysCmds:
.strip() .strip()
) )
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
logging.warning("Executed shell command: %s", " ".join(error.cmd)) logging.warning(SHELL_ERROR, error.cmd, error.stderr.decode("utf-8"))
logging.warning("Got error: %s", error.stderr.decode("utf-8"))
bridges = "" bridges = ""
if "rascsi_bridge" in bridges: if "rascsi_bridge" in bridges:
@ -181,3 +178,18 @@ class SysCmds:
return process.returncode, process.stdout.decode("utf-8") return process.returncode, process.stdout.decode("utf-8")
return process.returncode, process.stderr.decode("utf-8") return process.returncode, process.stderr.decode("utf-8")
@staticmethod
def get_manpage(file_path):
"""
Takes (str) file_path path to image file to generate manpage for.
Returns either the man2html output, or the stderr output.
"""
process = run(
["man2html", file_path, "-M", "/"],
capture_output=True,
)
if process.returncode == 0:
return process.returncode, process.stdout.decode("utf-8")
return process.returncode, process.stderr.decode("utf-8")

View File

@ -69,7 +69,7 @@ def extract_archive(file_path, **kwargs):
unar_result_success = r'^Successfully extracted to "(?P<destination>.+)".$' unar_result_success = r'^Successfully extracted to "(?P<destination>.+)".$'
unar_result_no_files = "No files extracted." unar_result_no_files = "No files extracted."
unar_file_extracted = \ unar_file_extracted = \
r"^ (?P<path>.+). \(((?P<size>[0-9]+) B)?(?P<types>(dir)?(, )?(rsrc)?)\)\.\.\. (?P<status>[A-Z]+)\.$" r"^ {2}(?P<path>.+). \(((?P<size>\d+) B)?(?P<types>(dir)?(, )?(rsrc)?)\)\.\.\. (?P<status>[A-Z]+)\.$"
lines = process["stdout"].rstrip("\n").split("\n") lines = process["stdout"].rstrip("\n").split("\n")
@ -141,7 +141,7 @@ def extract_archive(file_path, **kwargs):
# The parent dir may not be specified as a member, so ensure it exists # The parent dir may not be specified as a member, so ensure it exists
target_path.parent.mkdir(parents=True, exist_ok=True) target_path.parent.mkdir(parents=True, exist_ok=True)
logging.debug("Moving temp file: %s -> %s", source_path, target_path) logging.debug("Moving temp file: %s -> %s", source_path, target_path)
move(source_path, target_path) move(str(source_path), str(target_path))
moved.append(member) moved.append(member)
return { return {
@ -152,7 +152,7 @@ def extract_archive(file_path, **kwargs):
raise UnarUnexpectedOutputError(lines[-1]) raise UnarUnexpectedOutputError(lines[-1])
def inspect_archive(file_path, **kwargs): def inspect_archive(file_path):
""" """
Calls `lsar` to inspect the contents of an archive Calls `lsar` to inspect the contents of an archive
Takes (str) file_path Takes (str) file_path

View File

@ -2,3 +2,5 @@ pytest==7.1.3
pytest-httpserver==1.0.6 pytest-httpserver==1.0.6
black==22.8.0 black==22.8.0
flake8==5.0.4 flake8==5.0.4
watchdog==2.1.9
requests==2.28.1

View File

@ -1,4 +1,5 @@
<html> <!doctype html>
<html lang="en">
<head> <head>
<title>RaSCSI-Web is Starting</title> <title>RaSCSI-Web is Starting</title>
<meta http-equiv="refresh" content="2"> <meta http-equiv="refresh" content="2">

View File

@ -434,7 +434,7 @@
{ {
"device_type": "SCCD", "device_type": "SCCD",
"vendor": null, "vendor": null,
"product": null, "product": "SCSI CD-ROM 512",
"revision": null, "revision": null,
"block_size": 512, "block_size": 512,
"size": null, "size": null,

View File

@ -2,19 +2,7 @@ body {
color: black; color: black;
background-color: white; background-color: white;
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
text-decoration:none; text-decoration: none;
}
h1 {
color: white;
font-size:20px;
background-color:black;
}
h2 {
color: black;
font-size:16px;
margin: 0px;
} }
a { a {
@ -27,10 +15,50 @@ form {
table, tr, td { table, tr, td {
border: 1px solid black; border: 1px solid black;
border-collapse:collapse; border-collapse: collapse;
margin: none; margin: none;
} }
h1 {
color: white;
font-size: 20px;
}
h2 {
color: black;
font-size: large;
font-weight: bold;
margin: 3px;
}
summary.heading {
color: black;
font-size: large;
font-weight: bold;
margin: 3px;
}
div.header {
color: white;
background-color: black;
}
div.footer {
font-family: monospace;
}
div.logged_in {
background-color: green;
}
div.logged_out {
background-color: red;
}
input.lun {
width: 36px;
}
div.flash { div.flash {
margin-top: 5px; margin-top: 5px;
margin-bottom: 5px; margin-bottom: 5px;
@ -61,71 +89,76 @@ div.flash div.info {
} }
td.inactive { td.inactive {
text-align:center; text-align: center;
background-color:tan; background-color: tan;
} }
summary.heading { ul.inline_list {
color: black; list-style: none;
font-size: large;
font-weight: bold;
margin: 0px;
} }
.dropzone, .dropzone * { .dropzone, .dropzone * {
box-sizing: border-box; box-sizing: border-box;
} }
.dropzone { .dropzone {
position: relative; position: relative;
}
.dropzone .dz-button {
position: relative;
background-color: white;
color: black;
border: 2px dashed blue;
padding: 12px 28px;
} }
.dropzone .dz-preview { .dropzone .dz-preview {
position: relative; position: relative;
display: inline-block; display: inline-block;
width: 120px; width: 120px;
margin: .5em; margin: .5em;
} }
.dropzone .dz-preview .dz-progress { .dropzone .dz-preview .dz-progress {
display: block; display: block;
height: 15px; height: 15px;
border: 1px solid #aaa; border: 1px solid #aaa;
} }
.dropzone .dz-preview .dz-progress .dz-upload { .dropzone .dz-preview .dz-progress .dz-upload {
display: block; display: block;
height: 100%; height: 100%;
width: 0; width: 0;
background: green; background: green;
} }
.dropzone .dz-preview .dz-error-message { .dropzone .dz-preview .dz-error-message {
color: red; color: red;
display: none; display: none;
} }
.dropzone .dz-preview.dz-error .dz-error-message { .dropzone .dz-preview.dz-error .dz-error-message {
display: block; display: block;
} }
.dropzone .dz-preview.dz-error .dz-error-mark { .dropzone .dz-preview.dz-error .dz-error-mark {
display: block; display: block;
filter: drop-shadow(0px 0px 2px red); filter: drop-shadow(0px 0px 2px red);
opacity: 0;
} }
.dropzone .dz-preview.dz-success .dz-success-mark { .dropzone .dz-preview.dz-success .dz-success-mark {
display: block; display: block;
filter: drop-shadow(0px 0px 2px green); filter: drop-shadow(0px 0px 2px green);
} }
.dropzone .dz-preview .dz-error-mark, .dropzone .dz-preview .dz-success-mark { .dropzone .dz-preview .dz-error-mark, .dropzone .dz-preview .dz-success-mark {
position: absolute; position: absolute;
display: none; display: none;
left: 30px; top: 30px;
top: 30px; width: 54px;
width: 54px; height: 58px;
height: 58px; left: 50%;
left: 50%; margin-left: -27px;
margin-left: -27px;
} }

View File

@ -1,80 +1,76 @@
<!doctype html> <!doctype html>
<html> <html lang="{{ env["locale"] }}">
<head> <head>
<title>{{ _("RaSCSI Reloaded Control Page") }} [{{ env["host"] }}]</title> <title>{{ _("RaSCSI Reloaded Control Page") }} [{{ env["host"] }}]</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<link rel="apple-touch-icon" sizes="57x57" href="/pwa/apple-icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/pwa/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/pwa/apple-icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/pwa/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/pwa/apple-icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/pwa/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/pwa/apple-icon-76x76.png"> <link rel="apple-touch-icon" sizes="76x76" href="/pwa/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/pwa/apple-icon-114x114.png"> <link rel="apple-touch-icon" sizes="114x114" href="/pwa/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/pwa/apple-icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/pwa/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/pwa/apple-icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/pwa/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/pwa/apple-icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/pwa/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/pwa/apple-icon-180x180.png"> <link rel="apple-touch-icon" sizes="180x180" href="/pwa/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="/pwa/android-icon-192x192.png"> <link rel="icon" type="image/png" sizes="192x192" href="/pwa/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/pwa/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="/pwa/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/pwa/favicon-96x96.png"> <link rel="icon" type="image/png" sizes="96x96" href="/pwa/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/pwa/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="16x16" href="/pwa/favicon-16x16.png">
<link rel="manifest" href="/pwa/manifest.json"> <link rel="manifest" href="/pwa/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff"> <meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/pwa/ms-icon-144x144.png"> <meta name="msapplication-TileImage" content="/pwa/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<script type="application/javascript"> <script type="application/javascript">
var processNotify = function(Notification) { var processNotify = function(Notification) {
document.getElementById("flash").innerHTML = "<div class=\"info\">" + Notification + "{{ _(" This process may take a while, and will continue in the background if you navigate away from this page.") }}</div>"; document.getElementById("flash").innerHTML = "<div class=\"info\">" + Notification + "{{ _(" This process may take a while, and will continue in the background if you navigate away from this page.") }}</div>";
window.scrollTo(0,0); window.scrollTo(0,0);
} }
var shutdownNotify = function(Notification) { var shutdownNotify = function(Notification) {
document.getElementById("flash").innerHTML = "<div class=\"info\">" + Notification + "{{ _(" The Web Interface will become unresponsive momentarily. Reload this page after the Pi has started up again.") }}</div>"; document.getElementById("flash").innerHTML = "<div class=\"info\">" + Notification + "{{ _(" The Web Interface will become unresponsive momentarily. Reload this page after the Pi has started up again.") }}</div>";
window.scrollTo(0,0); window.scrollTo(0,0);
} }
</script> </script>
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.js"></script>
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.js"></script>
</head> </head>
<body> <body>
<div class="content">
<div class="header"> <div class="header">
{% if env["auth_active"] %} {% if env["auth_active"] %}
{% if env["username"] %} {% if env["username"] %}
<span style="display: inline-block; width: 100%; color: white; background-color: green; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">{{ _("Logged in as <em>%(username)s</em>", username=env["username"]) }} &#8211; <a href="/logout">{{ _("Log Out") }}</a></span> <div align="center" class="logged_in">
{{ _("Logged in as <em>%(username)s</em>", username=env["username"]) }} - <a href="/logout">{{ _("Log Out") }}</a>
</div>
{% else %} {% else %}
<span style="display: inline-block; width: 100%; color: white; background-color: red; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;"> <div align="center" class="logged_out">
<form method="POST" action="/login"> <form method="POST" action="/login">
<div>{{ _("Log In to Use Web Interface") }}</div> <div>{{ _("Log In to Use Web Interface") }}</div>
<input type="text" name="username" placeholder="{{ _("Username") }}"> <label for="username">{{ _("Username") }}</label>
<input type="password" name="password" placeholder="{{ _("Password") }}"> <input type="text" name="username" id="username">
<label for="password">{{ _("Password") }}</label>
<input type="password" name="password" id="password">
<input type="submit" value="Login"> <input type="submit" value="Login">
</form> </form>
</span> </div>
{% endif %} {% endif %}
{% else %} {% else %}
<span style="display: inline-block; width: 100%; color: white; background-color: green; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">{{ _("Web Interface Authentication Disabled") }} &#8211; {{ _("See <a href=\"%(url)s\" target=\"_blank\">Wiki</a> for more information", url="https://github.com/akuker/RASCSI/wiki/Web-Interface#enable-authentication") }}</span> <div align="center" class="logged_out">
{{ _("Web Interface Authentication Disabled") }} - {{ _("See <a href=\"%(url)s\" target=\"_blank\">Wiki</a> for more information", url="https://github.com/akuker/RASCSI/wiki/Web-Interface#enable-authentication") }}
</div>
{% endif %} {% endif %}
<table width="100%" style="background-color: black;"> <div align="center">
<tbody> <a href="/">
<tr align="center"> <h1>{{ _("RaSCSI Reloaded Control Page") }}</h1>
<td> </a>
<a href="/"> </div>
<h1>{{ _("RaSCSI Reloaded Control Page") }}</h1> <div>
</a> hostname: {{ env["host"] }} ip: {{ env["ip_addr"] }}
</td> </div>
</tr>
<tr>
<td style="color: white;">
hostname: {{ env["host"] }} ip: {{ env["ip_addr"] }}
</td>
</tr>
</tbody>
</table>
</div> </div>
<div class="flash" id="flash"> <div class="flash" id="flash">
{% for category, message in get_flashed_messages(with_categories=true) %} {% for category, message in get_flashed_messages(with_categories=true) %}
@ -88,9 +84,27 @@
<div class="content"> <div class="content">
{% block content %}{% endblock content %} {% block content %}{% endblock content %}
</div> </div>
<div class="footer"> <div align="center" class="footer">
<center><tt>{{ _("RaSCSI Reloaded version: ") }}<strong>{{ version }} <a href="https://github.com/akuker/RASCSI/commit/{{ env["running_env"]["git"] }}" target="_blank">{{ env["running_env"]["git"][:7] }}</a></strong></tt></center> <div>
<center><tt>{{ _("Pi environment: ") }}{{ env["running_env"]["env"] }}</tt></center> {% if env["netatalk_configured"] == 1 %}
{{ _("The AppleShare server is running. No active connections.") }}
{% endif %}
{% if env["netatalk_configured"] == 2 %}
{{ _("%(value)d active AFP connection", value=(env["netatalk_configured"] - 1)) }}
{% elif env["netatalk_configured"] > 2 %}
{{ _("%(value)d active AFP connections", value=(env["netatalk_configured"] - 1)) }}
{% endif %}
</div>
<div>
{% if env["macproxy_configured"] %}
{{ _("Macproxy is running at %(ip_addr)s (default port 5000)", ip_addr=env['ip_addr']) }}
{% endif %}
</div>
<div>
{{ _("RaSCSI Reloaded version: ") }}<b>{{ env["version"] }} <a href="https://github.com/akuker/RASCSI/commit/{{ env["running_env"]["git"] }}" target="_blank">{{ env["running_env"]["git"][:7] }}</a></b>
</div>
<div>
{{ _("Pi environment: ") }}{{ env["running_env"]["env"] }}
</div>
</div> </div>
</div>
</body> </body>

View File

@ -1,52 +1,52 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<h3>{{ _("Detailed Info for Attached Devices") }}</h3> <h2>{{ _("Detailed Info for Attached Devices") }}</h2>
{% for device in devices %} {% for device in devices %}
<p> <p>
<table border="black" cellpadding="3"> <table border="black" cellpadding="3" summary="Detailed information for attached devices">
<tr> <tr>
<td><i>{{ _("SCSI ID") }}</i></td> <th scope="row">{{ _("SCSI ID") }}</th>
<td>{{ device["id"] }}</td> <td>{{ device["id"] }}</td>
</tr> </tr>
<tr> <tr>
<td><i>{{ _("LUN") }}</i></td> <th scope="row">{{ _("LUN") }}</th>
<td>{{ device["unit"] }}</td> <td>{{ device["unit"] }}</td>
</tr> </tr>
<tr> <tr>
<td><i>{{ _("Type") }}</i></td> <th scope="row">{{ _("Type") }}</th>
<td>{{ device["device_type"] }}</td> <td>{{ device["device_type"] }}</td>
</tr> </tr>
<tr> <tr>
<td><i>{{ _("Status") }}</i></td> <th scope="row">{{ _("Status") }}</th>
<td>{{ device["status"] }}</td> <td>{{ device["status"] }}</td>
</tr> </tr>
<tr> <tr>
<td><i>{{ _("File") }}</i></td> <th scope="row">{{ _("File") }}</th>
<td>{{ device["image"] }}</td> <td>{{ device["image"] }}</td>
</tr> </tr>
<tr> <tr>
<td><i>{{ _("Parameters") }}</i></td> <th scope="row">{{ _("Parameters") }}</th>
<td>{{ device["params"] }}</td> <td>{{ device["params"] }}</td>
</tr> </tr>
<tr> <tr>
<td><i>{{ _("Vendor") }}</i></td> <th scope="row">{{ _("Vendor") }}</th>
<td>{{ device["vendor"] }}</td> <td>{{ device["vendor"] }}</td>
</tr> </tr>
<tr> <tr>
<td><i>{{ _("Product") }}</i></td> <th scope="row">{{ _("Product") }}</th>
<td>{{ device["product"] }}</td> <td>{{ device["product"] }}</td>
</tr> </tr>
<tr> <tr>
<td><i>{{ _("Revision") }}</i></td> <th scope="row">{{ _("Revision") }}</th>
<td>{{ device["revision"] }}</td> <td>{{ device["revision"] }}</td>
</tr> </tr>
<tr> <tr>
<td><i>{{ _("Block Size") }}</i></td> <th scope="row">{{ _("Block Size") }}</th>
<td>{{ device["block_size"] }}</td> <td>{{ device["block_size"] }}</td>
</tr> </tr>
<tr> <tr>
<td><i>{{ _("Image Size") }}</i></td> <th scope="row">{{ _("Image Size") }}</th>
<td>{{ device["size"] }}</td> <td>{{ device["size"] }}</td>
</tr> </tr>
</table> </table>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<h3>{{ _("Disk Image Details: %(file_name)s", file_name=file_name) }}</h3> <h2>{{ _("Disk Image Details: %(file_name)s", file_name=file_name) }}</h2>
<p><pre>{{ diskinfo }}</pre></p> <p><pre>{{ diskinfo }}</pre></p>
<p><a href="/">{{ _("Go to Home") }}</a></p> <p><a href="/">{{ _("Go to Home") }}</a></p>

View File

@ -5,38 +5,30 @@
<p>{{ _("These device profiles are provided as-is with no guarantee to work equally to the actual physical device they are named after. You may need to provide appropirate device drivers and/or configuration parameters for them to function properly. If you would like to see data modified, or have additional devices to add to the list, please raise an issue ticket at <a href=\"%(url)s\">GitHub</a>.", url="https://github.com/akuker/RASCSI/issues") }}</p> <p>{{ _("These device profiles are provided as-is with no guarantee to work equally to the actual physical device they are named after. You may need to provide appropirate device drivers and/or configuration parameters for them to function properly. If you would like to see data modified, or have additional devices to add to the list, please raise an issue ticket at <a href=\"%(url)s\">GitHub</a>.", url="https://github.com/akuker/RASCSI/issues") }}</p>
<h2>{{ _("Hard Disk Drives") }}</h2> <h2>{{ _("Hard Disk Drives") }}</h2>
<table cellpadding="3" border="black"> <table cellpadding="3" border="black" summary="List of hard drives">
<tbody> <tbody>
<tr> <tr>
<td><b>{{ _("Name") }}</b></td> <th scope="col">{{ _("Name") }}</th>
<td><b>{{ _("Size (MiB)") }}</b></td> <th scope="col">{{ _("Size (MiB)") }}</th>
<td><b>{{ _("Description") }}</b></td> <th scope="col">{{ _("Description") }}</th>
<td><b>{{ _("Ref.") }}</b></td> <th scope="col">{{ _("Action") }}</th>
<td><b>{{ _("Action") }}</b></td>
</tr> </tr>
{% for hd in drive_properties['hd_conf']|sort(attribute='name') %} {% for hd in env['drive_properties']['hd_conf']|sort(attribute='name') %}
<tr> <tr>
<td style="text-align:center">{{ hd.name }}</td> <td align="center">
<td style="text-align:center">{{ hd.size_mb }}</td> {% if hd.url != "" %}
<td style="text-align:left">{{ hd.description }}</td> <a href="{{ hd.url }}">{{ hd.name }}</a>
<td style="text-align:left"> {% else %}
{% if hd.url != "" %} {{ hd.name }}
<a href="{{ hd.url }}">{{ _("Link") }}</a> {% endif %}
{% else %} </td>
- <td align="center">{{ hd.size_mb }}</td>
{% endif %} <td>{{ hd.description }}</td>
</td> <td>
<td style="text-align:left">
<form action="/drive/create" method="post"> <form action="/drive/create" method="post">
<input type="hidden" name="vendor" value="{{ hd.vendor }}"> <input type="hidden" name="drive_name" value="{{ hd.name }}">
<input type="hidden" name="product" value="{{ hd.product }}"> <label for="file_name_{{ hd.name }}">{{ _("Save as:") }}</label>
<input type="hidden" name="revision" value="{{ hd.revision }}"> <input type="text" name="file_name" id="file_name_{{ hd.name }}" value="{{ hd.secure_name }}" required />.{{ hd.file_type }}
<input type="hidden" name="blocks" value="{{ hd.blocks }}">
<input type="hidden" name="block_size" value="{{ hd.block_size }}">
{{ _("Size:") }} <input type="number" name="size" min="512" max="274877906944" step="512" value="{{ hd.size }}">{{ _("B") }}
<input type="hidden" name="file_type" value="{{ hd.file_type }}">
<label for="file_name">{{ _("Save as:") }}</label>
<input type="text" name="file_name" value="{{ hd.secure_name }}" required />.{{ hd.file_type }}
<input type="submit" value="{{ _("Create") }}" /> <input type="submit" value="{{ _("Create") }}" />
</form> </form>
</td> </td>
@ -49,41 +41,34 @@
<h2>{{ _("CD/DVD Drives") }}</h2> <h2>{{ _("CD/DVD Drives") }}</h2>
<p><em>{{ _("This will create a properties file for the given CD-ROM or DVD image. No new image file will be created.") }}</em></p> <p><em>{{ _("This will create a properties file for the given CD-ROM or DVD image. No new image file will be created.") }}</em></p>
<table cellpadding="3" border="black"> <table cellpadding="3" border="black" summary="List of CD-ROM or DVD drives">
<tbody> <tbody>
<tr> <tr>
<td><b>{{ _("Name") }}</b></td> <th scope="col">{{ _("Name") }}</th>
<td><b>{{ _("Size (MiB)") }}</b></td> <th scope="col">{{ _("Description") }}</th>
<td><b>{{ _("Description") }}</b></td> <th scope="col">{{ _("Action") }}</th>
<td><b>{{ _("Ref.") }}</b></td>
<td><b>{{ _("Action") }}</b></td>
</tr> </tr>
{% for cd in drive_properties['cd_conf']|sort(attribute='name') %} {% for cd in env['drive_properties']['cd_conf']|sort(attribute='name') %}
<tr> <tr>
<td style="text-align:center">{{ cd.name }}</td> <td align="center">
<td style="text-align:center">{{ cd.size_mb }}</td> {% if cd.url != "" %}
<td style="text-align:left">{{ cd.description }}</td> <a href="{{ cd.url }}">{{ cd.name }}</a>
<td style="text-align:left"> {% else %}
{% if cd.url != "" %} {{ cd.name }}
<a href="{{ cd.url }}">{{ _("Link") }}</a> {% endif %}
{% else %}
-
{% endif %}
</td> </td>
<td style="text-align:left"> <td>{{ cd.description }}</td>
<td>
<form action="/drive/cdrom" method="post"> <form action="/drive/cdrom" method="post">
<input type="hidden" name="vendor" value="{{ cd.vendor }}"> <input type="hidden" name="drive_name" value="{{ cd.name }}">
<input type="hidden" name="product" value="{{ cd.product }}"> <label for="file_name_{{ cd.name }}">{{ _("Create for:") }}</label>
<input type="hidden" name="revision" value="{{ cd.revision }}"> <select type="select" name="file_name" id="file_name_{{ cd.name }}">
<input type="hidden" name="block_size" value="{{ cd.block_size }}">
<label for="file_name">{{ _("Create for:") }}</label>
<select type="select" name="file_name">
{% for file in files|sort(attribute='name') %} {% for file in files|sort(attribute='name') %}
{% if file["name"].lower().endswith(cdrom_file_suffix) %} {% if file["name"].lower().endswith(env['cd_suffixes']) %}
<option value="{{ file["name"] }}">{{ file["name"].replace(base_dir, '') }}</option> <option value="{{ file["name"] }}">{{ file["name"].replace(env["image_dir"], '') }}</option>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</select> </select>
<input type="submit" value="{{ _("Create") }}" /> <input type="submit" value="{{ _("Create") }}" />
</form> </form>
</td> </td>
@ -95,38 +80,30 @@
<hr/> <hr/>
<h2>{{ _("Removable Disk Drives") }}</h2> <h2>{{ _("Removable Disk Drives") }}</h2>
<table cellpadding="3" border="black"> <table cellpadding="3" border="black" summary="List of removable disk drives">
<tbody> <tbody>
<tr> <tr>
<td><b>{{ _("Name") }}</b></td> <th scope="col">{{ _("Name") }}</th>
<td><b>{{ _("Size (MiB)") }}</b></td> <th scope="col">{{ _("Size (MiB)") }}</th>
<td><b>{{ _("Description") }}</b></td> <th scope="col">{{ _("Description") }}</th>
<td><b>{{ _("Ref.") }}</b></td> <th scope="col">{{ _("Action") }}</th>
<td><b>{{ _("Action") }}</b></td>
</tr> </tr>
{% for rm in drive_properties['rm_conf']|sort(attribute='name') %} {% for rm in env['drive_properties']['rm_conf']|sort(attribute='name') %}
<tr> <tr>
<td style="text-align:center">{{ rm.name }}</td> <td align="center">
<td style="text-align:center">{{ rm.size_mb }}</td> {% if rm.url != "" %}
<td style="text-align:left">{{ rm.description }}</td> <a href="{{ rm.url }}">{{ rm.name }}</a>
<td style="text-align:left"> {% else %}
{% if rm.url != "" %} {{ rm.name }}
<a href="{{ rm.url }}">{{ _("Link") }}</a> {% endif %}
{% else %}
-
{% endif %}
</td> </td>
<td style="text-align:left"> <td align="center">{{ rm.size_mb }}</td>
<td>{{ rm.description }}</td>
<td>
<form action="/drive/create" method="post"> <form action="/drive/create" method="post">
<input type="hidden" name="vendor" value="{{ rm.vendor }}"> <input type="hidden" name="drive_name" value="{{ rm.name }}">
<input type="hidden" name="product" value="{{ rm.product }}"> <label for="file_name_{{ rm.name }}">{{ _("Save as:") }}</label>
<input type="hidden" name="revision" value="{{ rm.revision }}"> <input type="text" name="file_name" id="file_name_{{ rm.name }}" value="{{ rm.secure_name }}" required />.{{ rm.file_type }}
<input type="hidden" name="blocks" value="{{ rm.blocks }}">
<input type="hidden" name="block_size" value="{{ rm.block_size }}">
{{ _("Size:") }} <input type="number" name="size" min="512" max="274877906944" step="512" value="{{ rm.size }}">{{ _("B") }}
<input type="hidden" name="file_type" value="{{ rm.file_type }}">
<label for="file_name">{{ _("Save as:") }}</label>
<input type="text" name="file_name" value="{{ rm.secure_name }}" required />.{{ rm.file_type }}
<input type="submit" value="{{ _("Create") }}" /> <input type="submit" value="{{ _("Create") }}" />
</form> </form>
</td> </td>

View File

@ -6,18 +6,19 @@
{{ _("Current RaSCSI Configuration") }} {{ _("Current RaSCSI Configuration") }}
</summary> </summary>
<ul> <ul>
<li>{{ _("Displays the currently attached devices for each available SCSI ID.") }}</li> <li>{{ _("Save and load device configurations, stored as json files in <tt>%(config_dir)s</tt>", config_dir=CFG_DIR) }}</li>
<li>{{ _("Save and load device configurations, stored as json files in <tt>%(config_dir)s</tt>", config_dir=CFG_DIR) }}</tt></li>
<li>{{ _("To have a particular device configuration load when RaSCSI starts, save it as <em>default</em>.") }}</li> <li>{{ _("To have a particular device configuration load when RaSCSI starts, save it as <em>default</em>.") }}</li>
</ul> </ul>
</details> </details>
<p><form action="/config/load" method="post"> <p>
<select name="name" required="" width="14"> <form action="/config/load" method="post">
<label for="config_load_name">{{ _("File name") }}</label>
<select name="name" id="config_load_name" required="" width="14">
{% if config_files %} {% if config_files %}
{% for config in config_files|sort %} {% for config in config_files|sort %}
<option value="{{ config }}"> <option value="{{ config }}">
{{ config.replace(".json", '') }} {{ config }}
</option> </option>
{% endfor %} {% endfor %}
{% else %} {% else %}
@ -28,55 +29,59 @@
</select> </select>
<input name="load" type="submit" value="{{ _("Load") }}" onclick="return confirm('{{ _("Detach all current device and Load configuration?") }}')"> <input name="load" type="submit" value="{{ _("Load") }}" onclick="return confirm('{{ _("Detach all current device and Load configuration?") }}')">
<input name="delete" type="submit" value="{{ _("Delete") }}" onclick="return confirm('{{ _("Delete configuration file?") }}')"> <input name="delete" type="submit" value="{{ _("Delete") }}" onclick="return confirm('{{ _("Delete configuration file?") }}')">
</form></p> </form>
</p>
<p><form action="/config/save" method="post"> <p>
<input name="name" placeholder="default" size="20"> <form action="/config/save" method="post">
<label for="config_save_name">{{ _("File name") }}</label>
<input name="name" id="config_save_name" placeholder="default" size="20">
.{{ CONFIG_FILE_SUFFIX }}
<input type="submit" value="{{ _("Save") }}"> <input type="submit" value="{{ _("Save") }}">
</form></p> </form>
</p>
<table border="black" cellpadding="3"> <table border="black" cellpadding="3" summary="List of attached devices">
<tbody> <tbody>
<tr> <tr>
<td><b>{{ _("ID") }}</b></td> <th scope="col">{{ _("ID") }}</th>
{% if units %} {% if units %}
<td><b>{{ _("LUN") }}</b></td> <th scope="col">{{ _("LUN") }}</th>
{% endif %} {% endif %}
<td><b>{{ _("Type") }}</b></td> <th scope="col">{{ _("Device") }}</th>
<td><b>{{ _("Status") }}</b></td> <th scope="col">{{ _("Parameters") }}</th>
<td><b>{{ _("File") }}</b></td> <th scope="col">{{ _("Product") }}</th>
<td><b>{{ _("Product") }}</b></td> <th scope="col">{{ _("Actions") }}</th>
<td><b>{{ _("Actions") }}</b></td>
</tr> </tr>
{% for device in devices %} {% for device in devices | sort(attribute='id') %}
<tr> <tr>
{% if device["id"] not in reserved_scsi_ids %} {% if device["id"] not in reserved_scsi_ids %}
<td style="text-align:center">{{ device.id }}</td> <td align="center">{{ device.id }}</td>
{% if units %} {% if units %}
<td style="text-align:center">{{ device.unit }}</td> <td align="center">{{ device.unit }}</td>
{% endif %} {% endif %}
<td style="text-align:center">{{ device.device_type }}</td> <td align="center">{{ device.device_name }}</td>
<td style="text-align:center">{{ device.status }}</td> <td>
<td style="text-align:left">
{% if "No Media" in device.status %} {% if "No Media" in device.status %}
<form action="/scsi/attach" method="post"> <form action="/scsi/attach" method="post">
<input name="scsi_id" type="hidden" value="{{ device.id }}"> <input name="scsi_id" type="hidden" value="{{ device.id }}">
<input name="unit" type="hidden" value="{{ device.unit }}"> <input name="unit" type="hidden" value="{{ device.unit }}">
<input name="type" type="hidden" value="{{ device.device_type }}"> <input name="type" type="hidden" value="{{ device.device_type }}">
<input name="file_size" type="hidden" value="{{ device.size }}"> <input name="file_size" type="hidden" value="{{ device.size }}">
<select type="select" name="file_name"> <label for="device_list_file_name_{{ device.id }}_{{ device.unit }}">{{ _("File name") }}</label>
<select type="select" name="file_name" id="device_list_file_name_{{ device.id }}_{{ device.unit }}">
{% for f in files|sort(attribute='name') %} {% for f in files|sort(attribute='name') %}
{% if device.device_type == "SCCD" %} {% if device.device_type == "SCCD" %}
{% if f["name"].lower().endswith(cdrom_file_suffix) %} {% if f["name"].lower().endswith(env['cd_suffixes']) %}
<option value="{{ f["name"] }}">{{ f["name"].replace(base_dir, '') }}</option> <option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %} {% endif %}
{% elif device.device_type == "SCRM" %} {% elif device.device_type == "SCRM" %}
{% if f["name"].lower().endswith(removable_file_suffix) %} {% if f["name"].lower().endswith(env['rm_suffixes']) %}
<option value="{{ f["name"] }}">{{ f["name"].replace(base_dir, '') }}</option> <option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %} {% endif %}
{% elif device.device_type == "SCMO" %} {% elif device.device_type == "SCMO" %}
{% if f["name"].lower().endswith(mo_file_suffix) %} {% if f["name"].lower().endswith(env['mo_suffixes']) %}
<option value="{{ f["name"] }}">{{ f["name"].replace(base_dir, '') }}</option> <option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
@ -84,16 +89,32 @@
<input type="submit" value="{{ _("Attach") }}"> <input type="submit" value="{{ _("Attach") }}">
</form> </form>
{% else %} {% else %}
{% if device.params %}
{% for key in device.params %}
{% if key == "interface" %}
({{device.params[key]}})
{% elif key == "timeout" %}
({{key}}:{{device.params[key]}})
{% else %}
{{device.params[key]}}
{% endif %}
{% endfor %}
{% elif device.file %}
{{ device.file }} {{ device.file }}
{% endif %} {% endif %}
</td>
{% if device.vendor == "RaSCSI" %}
<td style="text-align:center">{{ device.product }}</td>
{% else %}
<td style="text-align:center">{{ device.vendor }} {{ device.product }}</td>
{% endif %} {% endif %}
<td style="text-align:center"> </td>
{% if device.device_type != "-" %} <td align="center">
{% if device.vendor != "RaSCSI" %}
{{ device.vendor }}
{% endif %}
{{ device.product }}
{% if device.vendor != "RaSCSI" %}
{{ device.revision }}
{% endif %}
</td>
<td align="center">
{% if device.id in scsi_ids["occupied_ids"] %}
{% if device.device_type in REMOVABLE_DEVICE_TYPES and "No Media" not in device.status %} {% if device.device_type in REMOVABLE_DEVICE_TYPES and "No Media" not in device.status %}
<form action="/scsi/eject" method="post" onsubmit="return confirm('{{ _("Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!") }}')"> <form action="/scsi/eject" method="post" onsubmit="return confirm('{{ _("Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!") }}')">
<input name="scsi_id" type="hidden" value="{{ device.id }}"> <input name="scsi_id" type="hidden" value="{{ device.id }}">
@ -106,20 +127,19 @@
<input name="unit" type="hidden" value="{{ device.unit }}"> <input name="unit" type="hidden" value="{{ device.unit }}">
<input type="submit" value="{{ _("Detach") }}"> <input type="submit" value="{{ _("Detach") }}">
</form> </form>
{% else %} {% else %}
<form action="/scsi/reserve" method="post" onsubmit="var memo = prompt('{{ _("Enter a memo for this reservation") }}'); if (memo === null) event.preventDefault(); document.getElementById('memo_{{ device.id }}').value = memo;"> <form action="/scsi/reserve" method="post" onsubmit="var memo = prompt('{{ _("Enter a memo for this reservation") }}'); if (memo === null) event.preventDefault(); document.getElementById('memo_{{ device.id }}').value = memo;">
<input name="scsi_id" type="hidden" value="{{ device.id }}"> <input name="scsi_id" type="hidden" value="{{ device.id }}">
<input name="memo" id="memo_{{ device.id }}" type="hidden" value=""> <input name="memo" id="memo_{{ device.id }}" type="hidden" value="">
<input type="submit" value="{{ _("Reserve") }}"> <input type="submit" value="{{ _("Reserve") }}">
</form> </form>
{% endif %} {% endif %}
</td> </td>
{% else %} {% else %}
<td class="inactive">{{ device.id }}</td> <td class="inactive">{{ device.id }}</td>
{% if units %} {% if units %}
<td class="inactive"></td> <td class="inactive"></td>
{% endif %} {% endif %}
<td class="inactive"></td>
<td class="inactive">{{ _("Reserved ID") }}</td> <td class="inactive">{{ _("Reserved ID") }}</td>
<td class="inactive">{{ RESERVATIONS[device.id] }}</td> <td class="inactive">{{ RESERVATIONS[device.id] }}</td>
<td class="inactive"></td> <td class="inactive"></td>
@ -135,20 +155,14 @@
</tbody> </tbody>
</table> </table>
<table style="border: none;" cellpadding="3"> <p>
<tr style="border: none;"> <form action="/scsi/detach_all" method="post" onsubmit="return confirm('{{ _("Detach all SCSI Devices?") }}')">
<td style="border: none;"> <input type="submit" value="{{ _("Detach All Devices") }}">
<form action="/scsi/detach_all" method="post" onsubmit="return confirm('{{ _("Detach all SCSI Devices?") }}')"> </form>
<input type="submit" value="{{ _("Detach All Devices") }}"> <form action="/scsi/info" method="post">
</form> <input type="submit" value="{{ _("Show Device Info") }}">
</td> </form>
<td style="border: none;"> </p>
<form action="/scsi/info" method="post">
<input type="submit" value="{{ _("Show Device Info") }}">
</form>
</td>
</tr>
</table>
<hr/> <hr/>
@ -157,10 +171,9 @@
{{ _("Image File Management") }} {{ _("Image File Management") }}
</summary> </summary>
<ul> <ul>
<li>{{ _("Manage image files in the active RaSCSI image directory: <tt>%(directory)s</tt> with a scan depth of %(scan_depth)s.", directory=base_dir, scan_depth=scan_depth) }}</li> <li>{{ _("Manage image files in the active RaSCSI image directory: <tt>%(directory)s</tt> with a scan depth of %(scan_depth)s.", directory=env["image_dir"], scan_depth=scan_depth) }}</li>
<li>{{ _("Select a valid SCSI ID and <a href=\"%(url)s\">LUN</a> to attach to. Unless you know what you're doing, always use LUN 0.", url="https://en.wikipedia.org/wiki/Logical_unit_number") }} <li>{{ _("Select a valid SCSI ID and <a href=\"%(url)s\" target=\"_blank\">LUN</a> to attach to. Unless you know what you're doing, always use LUN 0.", url="https://en.wikipedia.org/wiki/Logical_unit_number") }}
</li> </li>
<li>{{ _("If RaSCSI was unable to detect the media type associated with the image, you get to choose the type from the dropdown.") }}</li>
<li> <li>
{{ _("Recognized image file types:") }} {{ _("Recognized image file types:") }}
{% set comma = joiner(", ") %} {% set comma = joiner(", ") %}
@ -174,12 +187,12 @@
</ul> </ul>
</details> </details>
<table border="black" cellpadding="3"> <table border="black" cellpadding="3" summary="List of files in the image directory">
<tbody> <tbody>
<tr style="font-weight: bold;"> <tr>
<td>{{ _("File") }}</td> <th scope="col">{{ _("File") }}</th>
<td>{{ _("Size") }}</td> <th scope="col">{{ _("Size") }}</th>
<td>{{ _("Parameters and Actions") }}</td> <th scope="col">{{ _("Actions") }}</th>
</tr> </tr>
{% for file in files|sort(attribute='name') %} {% for file in files|sort(attribute='name') %}
<tr> <tr>
@ -189,12 +202,12 @@
<summary> <summary>
{{ file["name"] }} {{ file["name"] }}
</summary> </summary>
<ul style="list-style: none;"> <ul class="inline_list">
{% for key in file["prop"] %} {% for key in file["prop"] %}
<li>{{ key }}: {{ file['prop'][key] }}</li> <li>{{ key }}: {{ file['prop'][key] }}</li>
{% endfor %} {% endfor %}
<form action="/files/download" method="post"> <form action="/files/download" method="post">
<input name="file" type="hidden" value="{{ CFG_DIR }}/{{ file['name'].replace(base_dir, '') }}.{{ PROPERTIES_SUFFIX }}"> <input name="file" type="hidden" value="{{ CFG_DIR }}/{{ file['name'].replace(env['image_dir'], '') }}.{{ PROPERTIES_SUFFIX }}">
<input type="submit" value="{{ _("Properties File") }} &#8595;"> <input type="submit" value="{{ _("Properties File") }} &#8595;">
</form> </form>
</ul> </ul>
@ -206,7 +219,7 @@
<summary> <summary>
{{ file["name"] }} {{ file["name"] }}
</summary> </summary>
<ul style="list-style: none;"> <ul class="inline_list">
{% for member in file["archive_contents"] %} {% for member in file["archive_contents"] %}
{% if not member["is_properties_file"] %} {% if not member["is_properties_file"] %}
<li> <li>
@ -220,7 +233,7 @@
<input type="submit" value="{{ _("Extract") }}" onclick="processNotify('{{ _("Extracting a single file...") }}')"> <input type="submit" value="{{ _("Extract") }}" onclick="processNotify('{{ _("Extracting a single file...") }}')">
</form> </form>
</summary> </summary>
<ul style="list-style: none;"> <ul class="inline_list">
<li>{{ member["related_properties_file"] }}</li> <li>{{ member["related_properties_file"] }}</li>
</ul> </ul>
</details> </details>
@ -241,9 +254,9 @@
{% else %} {% else %}
<td>{{ file["name"] }}</td> <td>{{ file["name"] }}</td>
{% endif %} {% endif %}
<td style="text-align:center"> <td align="center">
<form action="/files/download" method="post"> <form action="/files/download" method="post">
<input name="file" type="hidden" value="{{ base_dir }}/{{ file['name'] }}"> <input name="file" type="hidden" value="{{ file['name'] }}">
<input type="submit" value="{{ file['size_mb'] }} {{ _("MiB") }} &#8595;"> <input type="submit" value="{{ file['size_mb'] }} {{ _("MiB") }} &#8595;">
</form> </form>
</td> </td>
@ -262,23 +275,24 @@
<form action="/scsi/attach" method="post"> <form action="/scsi/attach" method="post">
<input name="file_name" type="hidden" value="{{ file['name'] }}"> <input name="file_name" type="hidden" value="{{ file['name'] }}">
<input name="file_size" type="hidden" value="{{ file['size'] }}"> <input name="file_size" type="hidden" value="{{ file['size'] }}">
<label for="id">{{ _("ID") }}</label> <label for="image_list_scsi_id_{{ file["name"] }}">{{ _("ID") }}</label>
<select name="scsi_id"> <select name="scsi_id" id="image_list_scsi_id_{{ file["name"] }}">
{% for id in scsi_ids %} {% for id in scsi_ids["valid_ids"] %}
<option name="id" value="{{id}}"{% if id == recommended_id %} selected{% endif %}> <option name="id" value="{{id}}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
{{ id }} {{ id }}
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
<label for="unit">{{ _("LUN") }}</label> <label for="image_list_unit_{{ file["name"] }}">{{ _("LUN") }}</label>
<input name="unit" type="number" value="0" min="0" max="31" step="1"> <input class="lun" name="unit" id="image_list_unit_{{ file["name"] }}" type="number" value="0" min="0" max="31" step="1" size="3">
{% if file["detected_type"] != "UNDEFINED" %} {% if file["detected_type"] != "UNDEFINED" %}
<input name="type" type="hidden" value="{{ file['detected_type'] }}"> <input name="type" type="hidden" value="{{ file['detected_type'] }}">
{{ file['detected_type_name'] }} {{ file['detected_type_name'] }}
{% else %} {% else %}
<select name="type"> <label for="image_list_type_{{ file["name"] }}">{{ _("Type") }}</label>
<select name="type" id="image_list_type_{{ file["name"] }}">
<option selected disabled value=""> <option selected disabled value="">
{{ _("Select media type") }} {{ _("Unknown") }}
</option> </option>
{% for key, value in device_types.items() %} {% for key, value in device_types.items() %}
{% if key in DISK_DEVICE_TYPES %} {% if key in DISK_DEVICE_TYPES %}
@ -326,27 +340,23 @@
{{ _("Attach Peripheral Device") }} {{ _("Attach Peripheral Device") }}
</summary> </summary>
<ul> <ul>
<li>{{ _("Before using a networking device, it is recommended to run easyinstall.sh from the command line to configure your Raspberry Pi.") }}
</li> </li>
<ul>
{% if bridge_configured %} {% if bridge_configured %}
<li>{{ _("The <tt>rascsi_bridge</tt> network bridge is active and ready to be used by an emulated network adapter!") }}</li> <li>{{ _("The <tt>rascsi_bridge</tt> network bridge is active and ready to be used by an emulated network adapter!") }}</li>
{% else %} {% else %}
<li>{{ _("Please configure the <tt>rascsi_bridge</tt> network bridge before attaching an emulated network adapter!") }}</li> <li>{{ _("Please configure the <tt>rascsi_bridge</tt> network bridge before attaching an emulated network adapter!") }}</li>
{% endif %} {% endif %}
<li>{{ _("If you have a DHCP setup, choose only the interface you have configured the bridge with. You can ignore the inet field when attaching.") }}</li> <li>{{ _("To browse the modern web, install a vintage web proxy such as <a href=\"%(url)s\" target=\"_blank\">Macproxy</a>.", url="https://github.com/akuker/RASCSI/wiki/Vintage-Web-Proxy#macproxy") }}</li>
<li>{{ _("To browse the modern web, install a vintage web proxy such as <a href=\"%(url)s\">Macproxy</a>.", url="https://github.com/akuker/RASCSI/wiki/Vintage-Web-Proxy#macproxy") }}</li>
</li> </li>
</ul> <li>{{ _("Read more about <a href=\"%(url)s\" target=\"_blank\">supported device types</a> on the wiki.", url="https://github.com/akuker/RASCSI/wiki/Supported-Device-Types") }}
<li>{{ _("Read more about <a href=\"%(url)s\">supported device types</a> on the wiki.", url="https://github.com/akuker/RASCSI/wiki/Supported-Device-Types") }}
</li> </li>
</ul> </ul>
</details> </details>
<table border="black" cellpadding="3"> <table border="black" cellpadding="3" summary="List of peripheral devices">
<tr style="font-weight: bold;"> <tr>
<td>{{ _("Device") }}</td> <th scope="col">{{ _("Device") }}</th>
<td>{{ _("Code") }}</td> <th scope="col">{{ _("Key") }}</th>
<td>{{ _("Parameters and Actions") }}</td> <th scope="col">{{ _("Parameters and Actions") }}</th>
</tr> </tr>
{% for type in REMOVABLE_DEVICE_TYPES + PERIPHERAL_DEVICE_TYPES %} {% for type in REMOVABLE_DEVICE_TYPES + PERIPHERAL_DEVICE_TYPES %}
<tr> <tr>
@ -360,11 +370,11 @@
<form action="/scsi/attach_device" method="post"> <form action="/scsi/attach_device" method="post">
<input name="type" type="hidden" value="{{ type }}"> <input name="type" type="hidden" value="{{ type }}">
{% for key, value in device_types[type]["params"] | dictsort %} {% for key, value in device_types[type]["params"] | dictsort %}
<label for="{{ key }}">{{ key }}:</label> <label for="param_{{ type }}_{{ key }}">{{ key }}:</label>
{% if value.isnumeric() %} {% if value.isnumeric() %}
<input name="{{ key }}" type="number" value="{{ value }}"> <input name="param_{{ key }}" id="param_{{ type }}_{{ key }}" type="number" value="{{ value }}">
{% elif key == "interface" %} {% elif key == "interface" %}
<select name="interface"> <select name="param_{{ key }}" id="param_{{ type }}_{{ key }}">
{% for if in netinfo["ifs"] %} {% for if in netinfo["ifs"] %}
<option value="{{ if }}"> <option value="{{ if }}">
{{ if }} {{ if }}
@ -372,31 +382,31 @@
{% endfor %} {% endfor %}
</select> </select>
{% else %} {% else %}
<input name="{{ key }}" type="text" size="{{ value|length }}" placeholder="{{ value }}"> <input name="param_{{ key }}" id="param_{{ type }}_{{ key }}" type="text" size="{{ value|length }}" placeholder="{{ value }}">
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if type in REMOVABLE_DEVICE_TYPES %} {% if type in REMOVABLE_DEVICE_TYPES %}
<label for="drive_name">{{ _("Masquerade as:") }}</label> <label for="{{ type }}_drive_name">{{ _("Masquerade as:") }}</label>
<select name="drive_name"> <select name="drive_name" id="{{ type }}_drive_name">
<option value=""> <option value="">
{{ _("None") }} {{ _("None") }}
</option> </option>
{% if type == "SCCD" %} {% if type == "SCCD" %}
{% for drive in drive_properties["cd_conf"] | sort(attribute='name') %} {% for drive in env["drive_properties"]["cd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}"> <option value="{{ drive.name }}">
{{ drive.name }} {{ drive.name }}
</option> </option>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% if type == "SCRM" %} {% if type == "SCRM" %}
{% for drive in drive_properties["rm_conf"] | sort(attribute='name') %} {% for drive in env["drive_properties"]["rm_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}"> <option value="{{ drive.name }}">
{{ drive.name }} {{ drive.name }}
</option> </option>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% if type == "SCMO" %} {% if type == "SCMO" %}
{% for drive in drive_properties["mo_conf"] | sort(attribute='name') %} {% for drive in env["drive_properties"]["mo_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}"> <option value="{{ drive.name }}">
{{ drive.name }} {{ drive.name }}
</option> </option>
@ -404,16 +414,16 @@
{% endif %} {% endif %}
</select> </select>
{% endif %} {% endif %}
<label for="scsi_id">{{ _("SCSI ID:") }}</label> <label for="{{ type }}_scsi_id">{{ _("ID") }}</label>
<select name="scsi_id"> <select name="scsi_id" id="{{ type }}_scsi_id">
{% for id in scsi_ids %} {% for id in scsi_ids["valid_ids"] %}
<option value="{{ id }}"{% if id == recommended_id %} selected{% endif %}> <option value="{{ id }}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
{{ id }} {{ id }}
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
<label for="unit">{{ _("LUN") }}</label> <label for="{{ type }}_unit">{{ _("LUN") }}</label>
<input name="unit" type="number" value="0" min="0" max="31" step="1"> <input class="lun" name="unit" id="{{ type }}_unit" type="number" value="0" min="0" max="31" step="1" size="3">
<input type="submit" value="{{ _("Attach") }}"> <input type="submit" value="{{ _("Attach") }}">
</form> </form>
</td> </td>
@ -429,25 +439,20 @@
<ul> <ul>
<li>{{ _("The largest file size accepted in this form is %(max_file_size)s MiB. Use other file transfer means for larger files.", max_file_size=max_file_size) }}</li> <li>{{ _("The largest file size accepted in this form is %(max_file_size)s MiB. Use other file transfer means for larger files.", max_file_size=max_file_size) }}</li>
<li>{{ _("File uploads will progress only if you stay on this page. If you navigate away before the transfer is completed, you will end up with an incomplete file.") }}</li> <li>{{ _("File uploads will progress only if you stay on this page. If you navigate away before the transfer is completed, you will end up with an incomplete file.") }}</li>
<li>{{ _("Install <a href=\"%(url)s\">Netatalk</a> to use the AFP File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}</li> <li>{{ _("Install <a href=\"%(url)s\" target=\"_blank\">Netatalk</a> to use the AFP File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}</li>
</ul> </ul>
</details> </details>
<table style="border: none;"> <form name="dropper" action="/files/upload" method="post" class="dropzone dz-clickable" enctype="multipart/form-data" id="dropper">
<tr style="border: none;"> <p>
<td style="border: none; vertical-align:top;"> <label for="upload_destination">{{ _("Target directory:") }}</label>
<form name="dropper" action="/files/upload" method="post" class="dropzone dz-clickable" enctype="multipart/form-data" id="dropper"> <select name="destination" id="upload_destination">
<p> <option value="images">Images - {{ env["image_dir"] }}</option>
<label for="destination">{{ _("Target directory:") }}</label> <option value="afp">AppleShare - {{ AFP_DIR }}</option>
<select name="destination"> </select>
<option value="images">{{ base_dir }}</option> </p>
<option value="afp">{{ AFP_DIR }}</option> </form>
</select>
</p>
</form>
</td>
</tr>
</table>
<script type="application/javascript"> <script type="application/javascript">
Dropzone.options.dropper = { Dropzone.options.dropper = {
paramName: 'file', paramName: 'file',
@ -484,27 +489,20 @@
{{ _("Download File from the Web") }} {{ _("Download File from the Web") }}
</summary> </summary>
<ul> <ul>
<li>{{ _("Choose the desination directory and download a file from the Web to your Raspberry Pi.") }}</li> <li>{{ _("Install <a href=\"%(url)s\" target=\"_blank\">Netatalk</a> to use the AFP File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}</li>
<li>{{ _("Install <a href=\"%(url)s\">Netatalk</a> to use the AFP File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}</li>
</ul> </ul>
</details> </details>
<table style="border: none"> <form action="/files/download_url" method="post">
<tr style="border: none"> <label for="download_destination">{{ _("Target directory:") }}</label>
<td style="border: none; vertical-align:top;"> <select name="destination" id="download_destination">
<form action="/files/download_url" method="post"> <option value="images">Images - {{ env["image_dir"] }}</option>
<label for="destination">{{ _("Target directory:") }}</label> <option value="afp">AppleShare - {{ AFP_DIR }}</option>
<select name="destination"> </select>
<option value="images">{{ base_dir }}</option> <label for="download_url">{{ _("URL:") }}</label>
<option value="afp">{{ AFP_DIR }}</option> <input name="url" id="download_url" required="" type="url">
</select> <input type="submit" value="{{ _("Download") }}" onclick="processNotify('{{ _("Downloading File...") }}')">
<label for="url">{{ _("URL:") }}</label> </form>
<input name="url" placeholder="{{ _("URL") }}" required="" type="url">
<input type="submit" value="{{ _("Download") }}" onclick="processNotify('{{ _("Downloading File...") }}')">
</form>
</td>
</tr>
</table>
<hr/> <hr/>
@ -518,46 +516,41 @@
<li>{{ _("If the downloaded file is a zip archive, we will attempt to unzip it and store the resulting files.") }}</li> <li>{{ _("If the downloaded file is a zip archive, we will attempt to unzip it and store the resulting files.") }}</li>
</ul> </ul>
</details> </details>
<table style="border: none">
<tr style="border: none"> <form action="/files/download_to_iso" method="post">
<td style="border: none; vertical-align:top;"> <label for="iso_url">{{ _("URL:") }}</label>
<label for="scsi_id">{{ _("SCSI ID:") }}</label> <input name="url" id="iso_url" required="" type="url">
<form action="/files/download_to_iso" method="post"> <label for="iso_type">{{ _("Type:") }}</label>
<select name="scsi_id"> <select name="type" id="iso_type">
{% for id in scsi_ids %} <option value="-hfs">
<option value="{{ id }}"{% if id == recommended_id %} selected{% endif %}> HFS
{{ id }} </option>
</option> <option value="-iso-level 1">
{% endfor %} ISO-9660 Level 1
</select> </option>
<label for="url">{{ _("URL:") }}</label> <option value="-iso-level 2">
<input name="url" placeholder="{{ _("URL") }}" required="" type="url"> ISO-9660 Level 2
<label for="type">{{ _("Type:") }}</label> </option>
<select name="type"> <option value="-iso-level 3">
<option value="-hfs"> ISO-9660 Level 3
HFS </option>
</option> <option value="-J">
<option value="-iso-level 1"> Joliet
ISO-9660 Level 1 </option>
</option> <option value="-r">
<option value="-iso-level 2"> Rock Ridge
ISO-9660 Level 2 </option>
</option> </select>
<option value="-iso-level 3"> <label for="iso_scsi_id">{{ _("ID") }}</label>
ISO-9660 Level 3 <select name="scsi_id" id="iso_scsi_id">
</option> {% for id in scsi_ids["valid_ids"] %}
<option value="-J"> <option value="{{ id }}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
Joliet {{ id }}
</option> </option>
<option value="-r"> {% endfor %}
Rock Ridge </select>
</option> <input type="submit" value="{{ _("Download and Mount CD-ROM image") }}" onclick="processNotify('{{ _("Downloading File and generating CD-ROM image...") }}')">
</select> </form>
<input type="submit" value="{{ _("Download and Mount CD-ROM image") }}" onclick="processNotify('{{ _("Downloading File and generating CD-ROM image...") }}')">
</form>
</td>
</tr>
</table>
<hr/> <hr/>
@ -566,46 +559,37 @@
{{ _("Create Empty Disk Image File") }} {{ _("Create Empty Disk Image File") }}
</summary> </summary>
<ul> <ul>
<li>{{ _("The Generic Hard Disk image type is recommended for most computer platforms.") }}</li> <li>{{ _("Please refer to <a href=\"%(url)s\" target=\"_blank\">wiki documentation</a> to learn more about the supported image file types.", url="https://github.com/akuker/RASCSI/wiki/Supported-Device-Types#image-types") }}</li>
<li>{{ _("The Apple image type improves compatibility with Apple Macintosh computers.") }}</li>
<li>{{ _("The NEC image type improves compatibility with NEC PC-98 computers.") }}</li>
<li>{{ _("The SCSI-1 image type makes RaSCSI behave like a legacy SCSI-1 device, which may improve compatibility with very old SCSI controllers.") }}</li>
<li>{{ _("The Removable Disk image type can be used with SCSI floppy drives, SyQuest drives, Zip drives, etc.") }}</li>
</ul> </ul>
</details> </details>
<table style="border: none">
<tr style="border: none">
<td style="border: none; vertical-align:top;">
<form action="/files/create" method="post">
<label for="file_name">{{ _("File Name:") }}</label>
<input name="file_name" placeholder="{{ _("File Name") }}" required="" type="text">
<label for="type">{{ _("Type:") }}</label>
<select name="type">
{% for key, value in image_suffixes_to_create.items() %}
<option value="{{ key }}">
{{ value }} [.{{ key }}]
</option>
{% endfor %}
</select>
<label for="size">{{ _("Size:") }}</label>
<input name="size" type="number" placeholder="{{ _("MiB") }}" min="1" max="262144" required>
<input type="submit" value="{{ _("Create") }}">
</form>
</td>
</tr>
</table>
<hr/> <form action="/files/create" method="post">
<label for="image_create_file_name">{{ _("File Name:") }}</label>
<input name="file_name" id="image_create_file_name" required="" type="text">
<label for="image_create_type">{{ _("Type:") }}</label>
<select name="type" id="image_create_type">
{% for key, value in image_suffixes_to_create.items() %}
<option value="{{ key }}">
{{ value }} [.{{ key }}]
</option>
{% endfor %}
</select>
<label for="image_create_size">{{ _("Size:") }}</label>
<input name="size" id="image_create_size" type="number" placeholder="{{ _("MiB") }}" min="1" max="262144" required>
<label for="image_create_drive_name">{{ _("Masquerade as:") }}</label>
<select name="drive_name" id="image_create_drive_name">
<option value="">
{{ _("None") }}
</option>
{% for drive in env["drive_properties"]["hd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
</select>
<input type="submit" value="{{ _("Create") }}">
</form>
<details>
<summary class="heading">
{{ _("Create Named Drive") }}
</summary>
<ul>
<li>{{ _("Create pairs of images and properties files from a list of real-life drives.") }}</li>
<li>{{ _("This will make RaSCSI use certain vendor strings and block sizes that may improve compatibility with certain systems.") }}</li>
</ul>
</details>
<p><a href="/drive/list">{{ _("Create a named disk image that mimics real-life drives") }}</a></p> <p><a href="/drive/list">{{ _("Create a named disk image that mimics real-life drives") }}</a></p>
<hr/> <hr/>
@ -615,67 +599,49 @@
{{ _("Logging") }} {{ _("Logging") }}
</summary> </summary>
<ul> <ul>
<li>{{ _("Fetch a certain number of lines of system logs with the given scope.") }}</li>
</ul>
</details>
<table style="border: none">
<tr style="border: none">
<td style="border: none; vertical-align:top;">
<form action="/logs/show" method="post">
<label for="lines">{{ _("Log Lines:") }}</label>
<input name="lines" type="number" value="200" min="0" max="99999" step="100">
<label for="scope">{{ _("Scope:") }}</label>
<select name="scope">
<option value="">
{{ _("All logs") }}
</option>
<option value="rascsi">
rascsi
</option>
<option value="rascsi-web">
rascsi-web
</option>
<option value="rascsi-oled">
rascsi-oled
</option>
<option value="rascsi-ctrlboard">
rascsi-ctrlboard
</option>
</select>
<input type="submit" value="{{ _("Show Logs") }}">
</form>
</td>
</tr>
</table>
<hr/>
<details>
<summary class="heading">
{{ _("Server Log Level") }}
</summary>
<ul>
<li>{{ _("Change the log level of the RaSCSI backend process.") }}</li>
<li>{{ _("The current dropdown selection indicates the active log level.") }}</li> <li>{{ _("The current dropdown selection indicates the active log level.") }}</li>
</ul> </ul>
</details> </details>
<table style="border: none">
<tr style="border: none"> <div>
<td style="border: none; vertical-align:top;"> <form action="/logs/show" method="post">
<form action="/logs/level" method="post"> <label for="log_lines">{{ _("Log Lines:") }}</label>
<label for="level">{{ _("Log Level:") }}</label> <input name="lines" id="log_lines" type="number" value="200" min="0" max="99999" step="100">
<select name="level"> <label for="log_scope">{{ _("Scope:") }}</label>
{% for level in log_levels %} <select name="scope" id="log_scope">
<option value="{{ level }}"{% if level == current_log_level %} selected{% endif %}> <option value="">
{{ level }} {{ _("All logs") }}
</option> </option>
{% endfor %} <option value="rascsi">
</select> rascsi
<input type="submit" value="{{ _("Set Log Level") }}"> </option>
</form> <option value="rascsi-web">
</td> rascsi-web
</tr> </option>
</table> <option value="rascsi-oled">
rascsi-oled
</option>
<option value="rascsi-ctrlboard">
rascsi-ctrlboard
</option>
</select>
<input type="submit" value="{{ _("Show Logs") }}">
</form>
</div>
<div>
<form action="/logs/level" method="post">
<label for="log_level">{{ _("Log Level:") }}</label>
<select name="level" id="log_level">
{% for level in log_levels %}
<option value="{{ level }}"{% if level == current_log_level %} selected{% endif %}>
{{ level }}
</option>
{% endfor %}
</select>
<input type="submit" value="{{ _("Set Log Level") }}">
</form>
</div>
<hr/> <hr/>
@ -687,23 +653,18 @@
<li>{{ _("Change the Web Interface language.") }}</li> <li>{{ _("Change the Web Interface language.") }}</li>
</ul> </ul>
</details> </details>
<table style="border: none">
<tr style="border: none"> <form action="/language" method="post">
<td style="border: none; vertical-align:top;"> <label for="locale">{{ _("Language:") }}</label>
<form action="/language" method="post"> <select name="locale" id="locale">
<label for="language">{{ _("Language:") }}</label> {% for locale in locales %}
<select name="locale"> <option value="{{ locale.language }}">
{% for locale in locales %} {{ locale.language }} - {{ locale.display_name }}
<option value="{{ locale.language }}"> </option>
{{ locale.language }} - {{ locale.display_name }} {% endfor %}
</option> </select>
{% endfor %} <input type="submit" value="{{ _("Change Language") }}">
</select> </form>
<input type="submit" value="{{ _("Change Language") }}">
</form>
</td>
</tr>
</table>
<hr/> <hr/>
@ -712,39 +673,19 @@
{{ _("Raspberry Pi Operations") }} {{ _("Raspberry Pi Operations") }}
</summary> </summary>
<ul> <ul>
<li>{{ _("Reboot or shut down the Raspberry Pi that RaSCSI is running on.") }}</li>
<li>{{ _("IMPORTANT: Always shut down the Pi before turning off the power. Failing to do so may lead to data loss.") }}</li> <li>{{ _("IMPORTANT: Always shut down the Pi before turning off the power. Failing to do so may lead to data loss.") }}</li>
</ul> </ul>
</details> </details>
<table style="border: none">
<tr style="border: none">
<td style="border: none; vertical-align:top;">
<form action="/pi/reboot" method="post" onclick="if (confirm('{{ _("Reboot the Raspberry Pi?") }}')) shutdownNotify('{{ _("Rebooting the Raspberry Pi...") }}'); else event.preventDefault();">
<input type="submit" value="{{ _("Reboot Raspberry Pi") }}">
</form>
</td>
<td style="border: none; vertical-align:top;">
<form action="/pi/shutdown" method="post" onclick="if (confirm('{{ _("Shut down the Raspberry Pi?") }}')) shutdownNotify('{{ _("Shutting down the Raspberry Pi...") }}'); else event.preventDefault();">
<input type="submit" value="{{ _("Shut Down Raspberry Pi") }}">
</form>
</td>
</tr>
</table>
<center><tt> <form action="/pi/reboot" method="post" onclick="if (confirm('{{ _("Reboot the Raspberry Pi?") }}')) shutdownNotify('{{ _("Rebooting the Raspberry Pi...") }}'); else event.preventDefault();">
{% if netatalk_configured == 1 %} <input type="submit" value="{{ _("Reboot Raspberry Pi") }}">
{{ _("The AppleShare server is running. No active connections.") }} </form>
{% endif %} <form action="/pi/shutdown" method="post" onclick="if (confirm('{{ _("Shut down the Raspberry Pi?") }}')) shutdownNotify('{{ _("Shutting down the Raspberry Pi...") }}'); else event.preventDefault();">
{% if netatalk_configured == 2 %} <input type="submit" value="{{ _("Shut Down Raspberry Pi") }}">
{{ _("%(value)d active AFP connection", value=(netatalk_configured - 1)) }} </form>
{% elif netatalk_configured > 2 %}
{{ _("%(value)d active AFP connections", value=(netatalk_configured - 1)) }} <hr/>
{% endif %}
</center></tt> <a href="/sys/manpage?app=rascsi"><p>{{ _("Read the RaSCSI Manual") }}</p></a>
<center><tt>
{% if macproxy_configured %}
<center><tt>{{ _("Macproxy is running at %(ip_addr)s (default port 5000)", ip_addr=env['ip_addr']) }}</tt></center>
{% endif %}
</center></tt>
{% endblock content %} {% endblock content %}

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<h3>{{ _("System Logs: %(scope)s %(lines)s lines", scope=scope, lines=lines) }}</h3> <h2>{{ _("System Logs: %(scope)s %(lines)s lines", scope=scope, lines=lines) }}</h2>
<p><pre>{{ logs }}</pre></p> <p><pre>{{ logs }}</pre></p>
<p><a href="/">{{ _("Go to Home") }}</a></p> <p><a href="/">{{ _("Go to Home") }}</a></p>

View File

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block content %}
<h2>{{ _("Manual for %(app)s", app=app) }}</h2>
{{ manpage | safe }}
<p><a href="/">{{ _("Go to Home") }}</a></p>
{% endblock content %}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,6 @@ from flask import (
flash, flash,
url_for, url_for,
redirect, redirect,
send_file,
send_from_directory, send_from_directory,
make_response, make_response,
session, session,
@ -52,11 +51,13 @@ from web_utils import (
get_device_name, get_device_name,
map_image_file_descriptions, map_image_file_descriptions,
format_drive_properties, format_drive_properties,
get_properties_by_drive_name,
auth_active, auth_active,
is_bridge_configured, is_bridge_configured,
upload_with_dropzonejs, upload_with_dropzonejs,
) )
from settings import ( from settings import (
WEB_DIR,
AFP_DIR, AFP_DIR,
MAX_FILE_SIZE, MAX_FILE_SIZE,
DEFAULT_CONFIG, DEFAULT_CONFIG,
@ -73,6 +74,7 @@ def get_env_info():
""" """
Get information about the app/host environment Get information about the app/host environment
""" """
server_info = ractl_cmd.get_server_info()
ip_addr, host = sys_cmd.get_ip_and_host() ip_addr, host = sys_cmd.get_ip_and_host()
if "username" in session: if "username" in session:
@ -87,6 +89,15 @@ def get_env_info():
"ip_addr": ip_addr, "ip_addr": ip_addr,
"host": host, "host": host,
"free_disk_space": int(sys_cmd.disk_space()["free"] / 1024 / 1024), "free_disk_space": int(sys_cmd.disk_space()["free"] / 1024 / 1024),
"locale": get_locale(),
"version": server_info["version"],
"image_dir": server_info["image_dir"],
"netatalk_configured": sys_cmd.running_proc("afpd"),
"macproxy_configured": sys_cmd.running_proc("macproxy"),
"cd_suffixes": tuple(server_info["sccd"]),
"rm_suffixes": tuple(server_info["scrm"]),
"mo_suffixes": tuple(server_info["scmo"]),
"drive_properties": format_drive_properties(APP.config["RASCSI_DRIVE_PROPERTIES"]),
} }
@ -139,7 +150,7 @@ def get_locale():
language = session["language"] language = session["language"]
except KeyError: except KeyError:
language = "" language = ""
logging.warning("The default locale could not be detected. Falling back to English.") logging.info("The default locale could not be detected. Falling back to English.")
if language: if language:
return language return language
# Hardcoded fallback to "en" when the user agent does not send an accept-language header # Hardcoded fallback to "en" when the user agent does not send an accept-language header
@ -195,7 +206,7 @@ def index():
units += int(device["unit"]) units += int(device["unit"])
reserved_scsi_ids = server_info["reserved_ids"] reserved_scsi_ids = server_info["reserved_ids"]
scsi_ids, recommended_id = get_valid_scsi_ids(devices["device_list"], reserved_scsi_ids) scsi_ids = get_valid_scsi_ids(devices["device_list"], reserved_scsi_ids)
formatted_devices = sort_and_format_devices(devices["device_list"]) formatted_devices = sort_and_format_devices(devices["device_list"])
image_suffixes_to_create = map_image_file_descriptions( image_suffixes_to_create = map_image_file_descriptions(
@ -218,49 +229,31 @@ def index():
server_info["sccd"] server_info["sccd"]
) )
try:
drive_properties = format_drive_properties(APP.config["RASCSI_DRIVE_PROPERTIES"])
except:
drive_properties = {
"hd_conf": [],
"cd_conf": [],
"rm_conf": [],
"mo_conf": [],
}
return response( return response(
template="index.html", template="index.html",
locales=get_supported_locales(), locales=get_supported_locales(),
netinfo=ractl_cmd.get_network_info(),
bridge_configured=sys_cmd.is_bridge_setup(), bridge_configured=sys_cmd.is_bridge_setup(),
netatalk_configured=sys_cmd.running_proc("afpd"),
macproxy_configured=sys_cmd.running_proc("macproxy"),
devices=formatted_devices, devices=formatted_devices,
attached_images=attached_images,
files=extended_image_files, files=extended_image_files,
config_files=config_files, config_files=config_files,
base_dir=server_info["image_dir"], device_types=device_types,
scan_depth=server_info["scan_depth"], scan_depth=server_info["scan_depth"],
CFG_DIR=CFG_DIR,
AFP_DIR=AFP_DIR,
scsi_ids=scsi_ids,
recommended_id=recommended_id,
attached_images=attached_images,
units=units,
reserved_scsi_ids=reserved_scsi_ids,
RESERVATIONS=RESERVATIONS,
max_file_size=int(int(MAX_FILE_SIZE) / 1024 / 1024),
version=server_info["version"],
log_levels=server_info["log_levels"], log_levels=server_info["log_levels"],
current_log_level=server_info["current_log_level"], current_log_level=server_info["current_log_level"],
netinfo=ractl_cmd.get_network_info(), scsi_ids=scsi_ids,
device_types=device_types, units=units,
reserved_scsi_ids=reserved_scsi_ids,
image_suffixes_to_create=image_suffixes_to_create, image_suffixes_to_create=image_suffixes_to_create,
valid_image_suffixes=valid_image_suffixes, valid_image_suffixes=valid_image_suffixes,
cdrom_file_suffix=tuple(server_info["sccd"]), max_file_size=int(int(MAX_FILE_SIZE) / 1024 / 1024),
removable_file_suffix=tuple(server_info["scrm"]), RESERVATIONS=RESERVATIONS,
mo_file_suffix=tuple(server_info["scmo"]), CFG_DIR=CFG_DIR,
drive_properties=drive_properties, AFP_DIR=AFP_DIR,
PROPERTIES_SUFFIX=PROPERTIES_SUFFIX, PROPERTIES_SUFFIX=PROPERTIES_SUFFIX,
ARCHIVE_FILE_SUFFIXES=ARCHIVE_FILE_SUFFIXES, ARCHIVE_FILE_SUFFIXES=ARCHIVE_FILE_SUFFIXES,
CONFIG_FILE_SUFFIX=CONFIG_FILE_SUFFIX,
REMOVABLE_DEVICE_TYPES=ractl_cmd.get_removable_device_types(), REMOVABLE_DEVICE_TYPES=ractl_cmd.get_removable_device_types(),
DISK_DEVICE_TYPES=ractl_cmd.get_disk_device_types(), DISK_DEVICE_TYPES=ractl_cmd.get_disk_device_types(),
PERIPHERAL_DEVICE_TYPES=ractl_cmd.get_peripheral_device_types(), PERIPHERAL_DEVICE_TYPES=ractl_cmd.get_peripheral_device_types(),
@ -280,25 +273,10 @@ def drive_list():
""" """
Sets up the data structures and kicks off the rendering of the drive list page Sets up the data structures and kicks off the rendering of the drive list page
""" """
try:
drive_properties = format_drive_properties(APP.config["RASCSI_DRIVE_PROPERTIES"])
except:
drive_properties = {
"hd_conf": [],
"cd_conf": [],
"rm_conf": [],
"mo_conf": [],
}
server_info = ractl_cmd.get_server_info()
return response( return response(
template="drives.html", template="drives.html",
files=file_cmd.list_images()["files"], files=file_cmd.list_images()["files"],
base_dir=server_info["image_dir"],
drive_properties=drive_properties,
version=server_info["version"],
cdrom_file_suffix=tuple(server_info["sccd"]),
) )
@ -311,10 +289,9 @@ def login():
password = request.form["password"] password = request.form["password"]
groups = [g.gr_name for g in getgrall() if username in g.gr_mem] groups = [g.gr_name for g in getgrall() if username in g.gr_mem]
if AUTH_GROUP in groups: if AUTH_GROUP in groups and authenticate(str(username), str(password)):
if authenticate(str(username), str(password)): session["username"] = request.form["username"]
session["username"] = request.form["username"] return response(env=get_env_info())
return response(env=get_env_info())
return response(error=True, status_code=401, message=_( return response(error=True, status_code=401, message=_(
"You must log in with valid credentials for a user in the '%(group)s' group", "You must log in with valid credentials for a user in the '%(group)s' group",
@ -358,34 +335,36 @@ def drive_create():
""" """
Creates the image and properties file pair Creates the image and properties file pair
""" """
vendor = request.form.get("vendor") drive_name = request.form.get("drive_name")
product = request.form.get("product")
revision = request.form.get("revision")
block_size = request.form.get("block_size")
size = request.form.get("size")
file_type = request.form.get("file_type")
file_name = request.form.get("file_name") file_name = request.form.get("file_name")
full_file_name = file_name + "." + file_type
properties = get_properties_by_drive_name(
APP.config["RASCSI_DRIVE_PROPERTIES"],
drive_name
)
# Creating the image file # Creating the image file
process = file_cmd.create_new_image(file_name, file_type, size) process = file_cmd.create_new_image(
file_name,
properties["file_type"],
properties["size"],
)
if not process["status"]: if not process["status"]:
return response(error=True, message=process["msg"]) return response(error=True, message=process["msg"])
# Creating the drive properties file # Creating the drive properties file
prop_file_name = f"{file_name}.{file_type}.{PROPERTIES_SUFFIX}" full_file_name = f"{file_name}.{properties['file_type']}"
properties = { prop_file_name = f"{full_file_name}.{PROPERTIES_SUFFIX}"
"vendor": vendor,
"product": product,
"revision": revision,
"block_size": block_size,
}
process = file_cmd.write_drive_properties(prop_file_name, properties) process = file_cmd.write_drive_properties(prop_file_name, properties)
process = ReturnCodeMapper.add_msg(process) process = ReturnCodeMapper.add_msg(process)
if not process["status"]: if not process["status"]:
return response(error=True, message=process["msg"]) return response(error=True, message=process["msg"])
return response(message=_("Image file created: %(file_name)s", file_name=full_file_name)) # TODO: Refactor the return messages into one string
return response(message=[
(_("Image file created: %(file_name)s", file_name=full_file_name), "success"),
(process["msg"], "success"),
])
@APP.route("/drive/cdrom", methods=["POST"]) @APP.route("/drive/cdrom", methods=["POST"])
@ -394,20 +373,15 @@ def drive_cdrom():
""" """
Creates a properties file for a CD-ROM image Creates a properties file for a CD-ROM image
""" """
vendor = request.form.get("vendor") drive_name = request.form.get("drive_name")
product = request.form.get("product")
revision = request.form.get("revision")
block_size = request.form.get("block_size")
file_name = request.form.get("file_name") file_name = request.form.get("file_name")
# Creating the drive properties file # Creating the drive properties file
file_name = f"{file_name}.{PROPERTIES_SUFFIX}" file_name = f"{file_name}.{PROPERTIES_SUFFIX}"
properties = { properties = get_properties_by_drive_name(
"vendor": vendor, APP.config["RASCSI_DRIVE_PROPERTIES"],
"product": product, drive_name
"revision": revision, )
"block_size": block_size,
}
process = file_cmd.write_drive_properties(file_name, properties) process = file_cmd.write_drive_properties(file_name, properties)
process = ReturnCodeMapper.add_msg(process) process = ReturnCodeMapper.add_msg(process)
if process["status"]: if process["status"]:
@ -450,7 +424,8 @@ def config_load():
return response(error=True, message=process["msg"]) return response(error=True, message=process["msg"])
if "delete" in request.form: if "delete" in request.form:
process = file_cmd.delete_file(f"{CFG_DIR}/{file_name}") file_path = Path(CFG_DIR) / file_name
process = file_cmd.delete_file(file_path)
process = ReturnCodeMapper.add_msg(process) process = ReturnCodeMapper.add_msg(process)
if process["status"]: if process["status"]:
return response(message=process["msg"]) return response(message=process["msg"])
@ -478,7 +453,6 @@ def show_diskinfo():
template="diskinfo.html", template="diskinfo.html",
file_name=file_name, file_name=file_name,
diskinfo=diskinfo, diskinfo=diskinfo,
version=server_info["version"],
) )
return response( return response(
@ -487,6 +461,56 @@ def show_diskinfo():
) )
@APP.route("/sys/manpage", methods=["GET"])
def show_manpage():
"""
Displays manpage
"""
app_allowlist = ["rascsi", "rasctl", "rasdump", "scsimon"]
app = request.args.get("app", type = str)
if app not in app_allowlist:
return response(
error=True,
message=_("%(app)s is not a recognized RaSCSI app", app=app)
)
file_path = f"{WEB_DIR}/../../../doc/{app}.1"
html_to_strip = [
"Content-type",
"!DOCTYPE",
"<HTML>",
"<HEAD>",
"<BODY>",
"<H1>",
]
returncode, manpage = sys_cmd.get_manpage(file_path)
if returncode == 0:
formatted_manpage = ""
for line in manpage.splitlines(True):
# Make URIs compatible with the Flask webapp
if "/?1+" in line:
line = line.replace("/?1+", "manpage?app=")
# Strip out useless hyperlink
elif "man2html" in line:
line = line.replace("<A HREF=\"/\">man2html</A>", "man2html")
if not any(ele in line for ele in html_to_strip):
formatted_manpage += line
return response(
template="manpage.html",
app=app,
manpage=formatted_manpage,
)
return response(
error=True,
message=_("An error occurred when accessing man page: %(error)s", error=manpage)
)
@APP.route("/logs/show", methods=["POST"]) @APP.route("/logs/show", methods=["POST"])
def show_logs(): def show_logs():
""" """
@ -497,13 +521,11 @@ def show_logs():
returncode, logs = sys_cmd.get_logs(lines, scope) returncode, logs = sys_cmd.get_logs(lines, scope)
if returncode == 0: if returncode == 0:
server_info = ractl_cmd.get_server_info()
return response( return response(
template="logs.html", template="logs.html",
scope=scope, scope=scope,
lines=lines, lines=lines,
logs=logs, logs=logs,
version=server_info["version"],
) )
return response( return response(
@ -533,35 +555,40 @@ def attach_device():
""" """
Attaches a peripheral device that doesn't take an image file as argument Attaches a peripheral device that doesn't take an image file as argument
""" """
params = {} scsi_id = request.form.get("scsi_id")
unit = request.form.get("unit")
device_type = request.form.get("type")
drive_name = request.form.get("drive_name")
if not scsi_id:
return response(error=True, message=_("No SCSI ID specified"))
# Attempt to fetch the drive properties based on drive name
drive_props = None drive_props = None
if drive_name:
for drive in APP.config["RASCSI_DRIVE_PROPERTIES"]:
if drive["name"] == drive_name:
drive_props = drive
break
# Collect device parameters into a dictionary
PARAM_PREFIX = "param_"
params = {}
for item in request.form: for item in request.form:
if item == "scsi_id": if item.startswith(PARAM_PREFIX):
scsi_id = request.form.get(item)
elif item == "unit":
unit = request.form.get(item)
elif item == "type":
device_type = request.form.get(item)
elif item == "drive_name":
drive_name = request.form.get(item)
for drive in APP.config["RASCSI_DRIVE_PROPERTIES"]:
if drive["name"] == drive_name:
drive_props = drive
break
else:
param = request.form.get(item) param = request.form.get(item)
if param: if param:
params.update({item: param}) params.update({item.replace(PARAM_PREFIX, ""): param})
error_url = "https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link" error_url = "https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link"
error_msg = _("Please follow the instructions at %(url)s", url=error_url) error_msg = _("Please follow the instructions at %(url)s", url=error_url)
if "interface" in params.keys(): if "interface" in params.keys():
# Note: is_bridge_configured returns False if the bridge is configured
bridge_status = is_bridge_configured(params["interface"]) bridge_status = is_bridge_configured(params["interface"])
if bridge_status: if not bridge_status["status"]:
# TODO: Refactor the return messages into one string
return response(error=True, message=[ return response(error=True, message=[
(bridge_status, "error"), (bridge_status["msg"], "error"),
(error_msg, "error") (error_msg, "error")
]) ])
@ -601,6 +628,11 @@ def attach_image():
unit = request.form.get("unit") unit = request.form.get("unit")
device_type = request.form.get("type") device_type = request.form.get("type")
if not scsi_id:
return response(error=True, message=_("No SCSI ID specified"))
if not file_name:
return response(error=True, message=_("No image file to insert"))
kwargs = {"unit": int(unit), "params": {"file": file_name}} kwargs = {"unit": int(unit), "params": {"file": file_name}}
if device_type: if device_type:
@ -646,6 +678,7 @@ def attach_image():
return response(message=response_messages) return response(message=response_messages)
# TODO: Refactor the return messages into one string
return response(error=True, message=[ return response(error=True, message=[
(_("Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s", (_("Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s",
file_name=file_name, id_number=scsi_id, unit_number=unit), "error"), file_name=file_name, id_number=scsi_id, unit_number=unit), "error"),
@ -679,6 +712,7 @@ def detach():
return response(message=_("Detached SCSI ID %(id_number)s LUN %(unit_number)s", return response(message=_("Detached SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit)) id_number=scsi_id, unit_number=unit))
# TODO: Refactor the return messages into one string
return response(error=True, message=[ return response(error=True, message=[
(_("Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s", (_("Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit), "error"), id_number=scsi_id, unit_number=unit), "error"),
@ -700,6 +734,7 @@ def eject():
return response(message=_("Ejected SCSI ID %(id_number)s LUN %(unit_number)s", return response(message=_("Ejected SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit)) id_number=scsi_id, unit_number=unit))
# TODO: Refactor the return messages into one string
return response(error=True, message=[ return response(error=True, message=[
(_("Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s", (_("Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit), "error"), id_number=scsi_id, unit_number=unit), "error"),
@ -712,13 +747,11 @@ def device_info():
""" """
Displays detailed info for all attached devices Displays detailed info for all attached devices
""" """
server_info = ractl_cmd.get_server_info()
process = ractl_cmd.list_devices() process = ractl_cmd.list_devices()
if process["status"]: if process["status"]:
return response( return response(
template="deviceinfo.html", template="deviceinfo.html",
devices=process["device_list"], devices=process["device_list"],
version=server_info["version"],
) )
return response(error=True, message=_("No devices attached")) return response(error=True, message=_("No devices attached"))
@ -739,6 +772,7 @@ def reserve_id():
RESERVATIONS[int(scsi_id)] = memo RESERVATIONS[int(scsi_id)] = memo
return response(message=_("Reserved SCSI ID %(id_number)s", id_number=scsi_id)) return response(message=_("Reserved SCSI ID %(id_number)s", id_number=scsi_id))
# TODO: Refactor the return messages into one string
return response(error=True, message=[ return response(error=True, message=[
(_("Failed to reserve SCSI ID %(id_number)s", id_number=scsi_id), "error"), (_("Failed to reserve SCSI ID %(id_number)s", id_number=scsi_id), "error"),
(process["msg"], "error"), (process["msg"], "error"),
@ -759,6 +793,7 @@ def release_id():
RESERVATIONS[int(scsi_id)] = "" RESERVATIONS[int(scsi_id)] = ""
return response(message=_("Released the reservation for SCSI ID %(id_number)s", id_number=scsi_id)) return response(message=_("Released the reservation for SCSI ID %(id_number)s", id_number=scsi_id))
# TODO: Refactor the return messages into one string
return response(error=True, message=[ return response(error=True, message=[
(_("Failed to release the reservation for SCSI ID %(id_number)s", id_number=scsi_id), "error"), (_("Failed to release the reservation for SCSI ID %(id_number)s", id_number=scsi_id), "error"),
(process["msg"], "error"), (process["msg"], "error"),
@ -799,6 +834,7 @@ def download_to_iso():
process = file_cmd.download_file_to_iso(url, *iso_args) process = file_cmd.download_file_to_iso(url, *iso_args)
process = ReturnCodeMapper.add_msg(process) process = ReturnCodeMapper.add_msg(process)
if not process["status"]: if not process["status"]:
# TODO: Refactor the return messages into one string
return response(error=True, message=[ return response(error=True, message=[
(_("Failed to create CD-ROM image from %(url)s", url=url), "error"), (_("Failed to create CD-ROM image from %(url)s", url=url), "error"),
(process["msg"], "error"), (process["msg"], "error"),
@ -817,6 +853,7 @@ def download_to_iso():
response_messages.append((_("Attached to SCSI ID %(id_number)s", id_number=scsi_id), "success")) response_messages.append((_("Attached to SCSI ID %(id_number)s", id_number=scsi_id), "success"))
return response(message=response_messages) return response(message=response_messages)
# TODO: Refactor the return messages into one string
return response(error=True, message=[ return response(error=True, message=[
(_("Failed to attach image to SCSI ID %(id_number)s. Try attaching it manually.", id_number=scsi_id), "error"), (_("Failed to attach image to SCSI ID %(id_number)s. Try attaching it manually.", id_number=scsi_id), "error"),
(process_attach["msg"], "error"), (process_attach["msg"], "error"),
@ -841,6 +878,7 @@ def download_file():
if process["status"]: if process["status"]:
return response(message=process["msg"]) return response(message=process["msg"])
# TODO: Refactor the return messages into one string
return response(error=True, message=[ return response(error=True, message=[
(_("Failed to download file from %(url)s", url=url), "error"), (_("Failed to download file from %(url)s", url=url), "error"),
(process["msg"], "error"), (process["msg"], "error"),
@ -876,17 +914,34 @@ def create_file():
file_name = request.form.get("file_name") file_name = request.form.get("file_name")
size = (int(request.form.get("size")) * 1024 * 1024) size = (int(request.form.get("size")) * 1024 * 1024)
file_type = request.form.get("type") file_type = request.form.get("type")
drive_name = request.form.get("drive_name")
full_file_name = file_name + "." + file_type full_file_name = file_name + "." + file_type
process = file_cmd.create_new_image(file_name, file_type, size) process = file_cmd.create_new_image(file_name, file_type, size)
if process["status"]: if not process["status"]:
return response( return response(error=True, message=process["msg"])
status_code=201,
message=_("Image file created: %(file_name)s", file_name=full_file_name),
image=full_file_name,
)
return response(error=True, message=process["msg"]) # Creating the drive properties file, if one is chosen
if drive_name:
properties = get_properties_by_drive_name(
APP.config["RASCSI_DRIVE_PROPERTIES"],
drive_name
)
prop_file_name = f"{full_file_name}.{PROPERTIES_SUFFIX}"
process = file_cmd.write_drive_properties(prop_file_name, properties)
process = ReturnCodeMapper.add_msg(process)
if not process["status"]:
return response(error=True, message=process["msg"])
return response(
status_code=201,
# TODO: Refactor the return messages into one string
message=[
(_("Image file created: %(file_name)s", file_name=full_file_name), "success"),
(process["msg"], "success"),
],
image=full_file_name,
)
@APP.route("/files/download", methods=["POST"]) @APP.route("/files/download", methods=["POST"])
@ -895,8 +950,9 @@ def download():
""" """
Downloads a file from the Pi to the local computer Downloads a file from the Pi to the local computer
""" """
image = request.form.get("file") file_name = request.form.get("file")
return send_file(image, as_attachment=True) server_info = ractl_cmd.get_server_info()
return send_from_directory(server_info["image_dir"], file_name, as_attachment=True)
@APP.route("/files/delete", methods=["POST"]) @APP.route("/files/delete", methods=["POST"])
@ -915,8 +971,8 @@ def delete():
(_("Image file deleted: %(file_name)s", file_name=file_name), "success")] (_("Image file deleted: %(file_name)s", file_name=file_name), "success")]
# Delete the drive properties file, if it exists # Delete the drive properties file, if it exists
prop_file_path = f"{CFG_DIR}/{file_name}.{PROPERTIES_SUFFIX}" prop_file_path = Path(CFG_DIR) / f"{file_name}.{PROPERTIES_SUFFIX}"
if Path(prop_file_path).is_file(): if prop_file_path.is_file():
process = file_cmd.delete_file(prop_file_path) process = file_cmd.delete_file(prop_file_path)
process = ReturnCodeMapper.add_msg(process) process = ReturnCodeMapper.add_msg(process)
if process["status"]: if process["status"]:
@ -944,9 +1000,9 @@ def rename():
(_("Image file renamed to: %(file_name)s", file_name=new_file_name), "success")] (_("Image file renamed to: %(file_name)s", file_name=new_file_name), "success")]
# Rename the drive properties file, if it exists # Rename the drive properties file, if it exists
prop_file_path = f"{CFG_DIR}/{file_name}.{PROPERTIES_SUFFIX}" prop_file_path = Path(CFG_DIR) / f"{file_name}.{PROPERTIES_SUFFIX}"
new_prop_file_path = f"{CFG_DIR}/{new_file_name}.{PROPERTIES_SUFFIX}" new_prop_file_path = Path(CFG_DIR) / f"{new_file_name}.{PROPERTIES_SUFFIX}"
if Path(prop_file_path).is_file(): if prop_file_path.is_file():
process = file_cmd.rename_file(prop_file_path, new_prop_file_path) process = file_cmd.rename_file(prop_file_path, new_prop_file_path)
process = ReturnCodeMapper.add_msg(process) process = ReturnCodeMapper.add_msg(process)
if process["status"]: if process["status"]:
@ -974,9 +1030,9 @@ def copy():
(_("Copy of image file saved as: %(file_name)s", file_name=new_file_name), "success")] (_("Copy of image file saved as: %(file_name)s", file_name=new_file_name), "success")]
# Create a copy of the drive properties file, if it exists # Create a copy of the drive properties file, if it exists
prop_file_path = f"{CFG_DIR}/{file_name}.{PROPERTIES_SUFFIX}" prop_file_path = Path(CFG_DIR) / f"{file_name}.{PROPERTIES_SUFFIX}"
new_prop_file_path = f"{CFG_DIR}/{new_file_name}.{PROPERTIES_SUFFIX}" new_prop_file_path = Path(CFG_DIR) / f"{new_file_name}.{PROPERTIES_SUFFIX}"
if Path(prop_file_path).is_file(): if prop_file_path.is_file():
process = file_cmd.copy_file(prop_file_path, new_prop_file_path) process = file_cmd.copy_file(prop_file_path, new_prop_file_path)
process = ReturnCodeMapper.add_msg(process) process = ReturnCodeMapper.add_msg(process)
if process["status"]: if process["status"]:
@ -1112,8 +1168,10 @@ if __name__ == "__main__":
if process["status"]: if process["status"]:
APP.config["RASCSI_DRIVE_PROPERTIES"] = process["conf"] APP.config["RASCSI_DRIVE_PROPERTIES"] = process["conf"]
else: else:
APP.config["RASCSI_DRIVE_PROPERTIES"] = []
logging.error(process["msg"]) logging.error(process["msg"])
else: else:
APP.config["RASCSI_DRIVE_PROPERTIES"] = []
logging.warning("Could not read drive properties from %s", DRIVE_PROPERTIES_FILE) logging.warning("Could not read drive properties from %s", DRIVE_PROPERTIES_FILE)
logging.basicConfig(stream=sys.stdout, logging.basicConfig(stream=sys.stdout,

View File

@ -18,6 +18,7 @@ def get_valid_scsi_ids(devices, reserved_ids):
Takes a list of (dict)s devices, and list of (int)s reserved_ids. Takes a list of (dict)s devices, and list of (int)s reserved_ids.
Returns: Returns:
- (list) of (int)s valid_ids, which are the SCSI ids that are not reserved - (list) of (int)s valid_ids, which are the SCSI ids that are not reserved
- (list) of (int)s occupied_ids, which are the SCSI ids were a device is attached
- (int) recommended_id, which is the id that the Web UI should default to recommend - (int) recommended_id, which is the id that the Web UI should default to recommend
""" """
occupied_ids = [] occupied_ids = []
@ -33,11 +34,15 @@ def get_valid_scsi_ids(devices, reserved_ids):
recommended_id = unoccupied_ids[-1] recommended_id = unoccupied_ids[-1]
else: else:
if occupied_ids: if occupied_ids:
recommended_id = occupied_ids.pop(0) recommended_id = occupied_ids[0]
else: else:
recommended_id = 0 recommended_id = 0
return valid_ids, recommended_id return {
"valid_ids": valid_ids,
"occupied_ids": occupied_ids,
"recommended_id": recommended_id,
}
def sort_and_format_devices(devices): def sort_and_format_devices(devices):
@ -47,18 +52,25 @@ def sort_and_format_devices(devices):
For SCSI IDs where no device is attached, inject a (dict) with placeholder text. For SCSI IDs where no device is attached, inject a (dict) with placeholder text.
""" """
occupied_ids = [] occupied_ids = []
formatted_devices = []
for device in devices: for device in devices:
occupied_ids.append(device["id"]) occupied_ids.append(device["id"])
device["device_name"] = get_device_name(device["device_type"])
formatted_devices.append(device)
formatted_devices = devices # Add placeholder data for non-occupied IDs
for scsi_id in range(8):
# Add padding devices and sort the list if scsi_id not in occupied_ids:
for i in range(8): formatted_devices.append(
if i not in occupied_ids: {
formatted_devices.append({"id": i, "device_type": "-", \ "id": scsi_id,
"status": "-", "file": "-", "product": "-"}) "unit": "-",
# Sort list of devices by id "device_name": "-",
formatted_devices.sort(key=lambda dic: str(dic["id"])) "status": "-",
"file": "-",
"product": "-",
}
)
return formatted_devices return formatted_devices
@ -91,7 +103,7 @@ def get_device_name(device_type):
if device_type == "SCBR": if device_type == "SCBR":
return _("Host Bridge") return _("Host Bridge")
if device_type == "SCDP": if device_type == "SCDP":
return _("DaynaPORT SCSI/Link") return _("Ethernet Adapter")
if device_type == "SCLP": if device_type == "SCLP":
return _("Printer") return _("Printer")
if device_type == "SCHS": if device_type == "SCHS":
@ -144,6 +156,12 @@ def format_drive_properties(drive_properties):
FORMAT_FILTER = "{:,.2f}" FORMAT_FILTER = "{:,.2f}"
for device in drive_properties: for device in drive_properties:
# Add fallback device names, since other code relies on this data for display
if not device["name"]:
if device["product"]:
device["name"] = device["product"]
else:
device["name"] = "Unknown Device"
if device["device_type"] == "SCHD": if device["device_type"] == "SCHD":
device["secure_name"] = secure_filename(device["name"]) device["secure_name"] = secure_filename(device["name"])
device["size_mb"] = FORMAT_FILTER.format(device["size"] / 1024 / 1024) device["size_mb"] = FORMAT_FILTER.format(device["size"] / 1024 / 1024)
@ -167,6 +185,41 @@ def format_drive_properties(drive_properties):
"mo_conf": mo_conf, "mo_conf": mo_conf,
} }
def get_properties_by_drive_name(drives, drive_name):
"""
Takes (list) of (dict) drives, and (str) drive_name
Returns (dict) with the collection of drive properties that matches drive_name
"""
drives.sort(key=lambda item: item.get("name"))
drive_props = None
prev_drive = {"name": ""}
for drive in drives:
# TODO: Make this check into an integration test
if "name" not in drive:
logging.warning(
"Device without a name exists in the drive properties database. This is a bug."
)
break
# TODO: Make this check into an integration test
if drive["name"] == prev_drive["name"]:
logging.warning(
"Device with duplicate name \"%s\" in drive properties database. This is a bug.",
drive["name"],
)
prev_drive = drive
if drive["name"] == drive_name:
drive_props = drive
return {
"file_type": drive_props["file_type"],
"vendor": drive_props["vendor"],
"product": drive_props["product"],
"revision": drive_props["revision"],
"block_size": drive_props["block_size"],
"size": drive_props["size"],
}
def auth_active(group): def auth_active(group):
""" """
Inspects if the group defined in (str) group exists on the system. Inspects if the group defined in (str) group exists on the system.
@ -185,24 +238,31 @@ def auth_active(group):
def is_bridge_configured(interface): def is_bridge_configured(interface):
""" """
Takes (str) interface of a network device being attached. Takes (str) interface of a network device being attached.
Returns (bool) False if the network bridge is configured. Returns a (dict) with (bool) status and (str) msg
Returns (str) with an error message if the network bridge is not configured.
""" """
# TODO: Reduce the nesting of these checks, and streamline how the results are notified
status = True
return_msg = ""
sys_cmd = SysCmds() sys_cmd = SysCmds()
if interface.startswith("wlan"): if interface.startswith("wlan"):
if not sys_cmd.introspect_file("/etc/sysctl.conf", r"^net\.ipv4\.ip_forward=1$"): if not sys_cmd.introspect_file("/etc/sysctl.conf", r"^net\.ipv4\.ip_forward=1$"):
return _("Configure IPv4 forwarding before using a wireless network device.") status = False
if not Path("/etc/iptables/rules.v4").is_file(): return_msg = _("Configure IPv4 forwarding before using a wireless network device.")
return _("Configure NAT before using a wireless network device.") elif not Path("/etc/iptables/rules.v4").is_file():
status = False
return_msg = _("Configure NAT before using a wireless network device.")
else: else:
if not sys_cmd.introspect_file( if not sys_cmd.introspect_file(
"/etc/dhcpcd.conf", "/etc/dhcpcd.conf",
r"^denyinterfaces " + interface + r"$", r"^denyinterfaces " + interface + r"$",
): ):
return _("Configure the network bridge before using a wired network device.") status = False
if not Path("/etc/network/interfaces.d/rascsi_bridge").is_file(): return_msg = _("Configure the network bridge before using a wired network device.")
return _("Configure the network bridge before using a wired network device.") elif not Path("/etc/network/interfaces.d/rascsi_bridge").is_file():
return False status = False
return_msg = _("Configure the network bridge before using a wired network device.")
return {"status": status, "msg": return_msg + f" ({interface})"}
def upload_with_dropzonejs(image_dir): def upload_with_dropzonejs(image_dir):
@ -235,18 +295,7 @@ def upload_with_dropzonejs(image_dir):
if current_chunk + 1 == total_chunks: if current_chunk + 1 == total_chunks:
# Validate the resulting file size after writing the last chunk # Validate the resulting file size after writing the last chunk
if path.getsize(save_path) != int(request.form["dztotalfilesize"]): if path.getsize(save_path) != int(request.form["dztotalfilesize"]):
log.error( log.error("File size mismatch between the original file and transferred file.")
"Finished transferring %s, "
"but it has a size mismatch with the original file. "
"Got %s but we expected %s.",
file_object.filename,
path.getsize(save_path),
request.form['dztotalfilesize'],
)
return make_response(_("Transferred file corrupted!"), 500) return make_response(_("Transferred file corrupted!"), 500)
log.info("File %s has been uploaded successfully", file_object.filename)
log.debug("Chunk %s of %s for file %s completed.",
current_chunk + 1, total_chunks, file_object.filename)
return make_response(_("File upload successful!"), 200) return make_response(_("File upload successful!"), 200)

View File

@ -2,9 +2,6 @@ import pytest
import uuid import uuid
import warnings import warnings
CFG_DIR = "/home/pi/.config/rascsi"
IMAGES_DIR = "/home/pi/images"
AFP_DIR = "/home/pi/afpshare"
SCSI_ID = 6 SCSI_ID = 6
FILE_SIZE_1_MIB = 1048576 FILE_SIZE_1_MIB = 1048576
STATUS_SUCCESS = "success" STATUS_SUCCESS = "success"

View File

@ -3,6 +3,7 @@ from conftest import STATUS_SUCCESS, STATUS_ERROR
# route("/login", methods=["POST"]) # route("/login", methods=["POST"])
def test_login_with_valid_credentials(pytestconfig, http_client_unauthenticated): def test_login_with_valid_credentials(pytestconfig, http_client_unauthenticated):
# Note: This test depends on the rascsi group existing and 'username' a member the group
response = http_client_unauthenticated.post( response = http_client_unauthenticated.post(
"/login", "/login",
data={ data={

View File

@ -42,8 +42,8 @@ def test_attach_image(http_client, create_test_image, detach_devices):
{ {
"type": "SCRM", "type": "SCRM",
"drive_props": { "drive_props": {
"vendor": "VENDOR", "vendor": "HD VENDOR",
"product": "PRODUCT", "product": "HD PRODUCT",
"revision": "0123", "revision": "0123",
"block_size": "512", "block_size": "512",
}, },
@ -54,8 +54,8 @@ def test_attach_image(http_client, create_test_image, detach_devices):
{ {
"type": "SCMO", "type": "SCMO",
"drive_props": { "drive_props": {
"vendor": "VENDOR", "vendor": "MO VENDOR",
"product": "PRODUCT", "product": "MO PRODUCT",
"revision": "0123", "revision": "0123",
"block_size": "512", "block_size": "512",
}, },
@ -66,20 +66,25 @@ def test_attach_image(http_client, create_test_image, detach_devices):
{ {
"type": "SCCD", "type": "SCCD",
"drive_props": { "drive_props": {
"vendor": "VENDOR", "vendor": "CD VENDOR",
"product": "PRODUCT", "product": "CD PRODUCT",
"revision": "0123", "revision": "0123",
"block_size": "512", "block_size": "512",
}, },
}, },
), ),
("Host Bridge", {"type": "SCBR", "interface": "eth0", "inet": "10.10.20.1/24"}), # TODO: Find a portable way to detect network interfaces for testing
("DaynaPORT SCSI/Link", {"type": "SCDP", "interface": "eth0", "inet": "10.10.20.1/24"}), ("Host Bridge", {"type": "SCBR", "param_inet": "192.168.0.1/24"}),
# TODO: Find a portable way to detect network interfaces for testing
("Ethernet Adapter", {"type": "SCDP", "param_inet": "192.168.0.1/24"}),
("Host Services", {"type": "SCHS"}), ("Host Services", {"type": "SCHS"}),
("Printer", {"type": "SCLP", "timeout": 30, "cmd": "lp -oraw %f"}), ("Printer", {"type": "SCLP", "param_timeout": 60, "param_cmd": "lp -fart %f"}),
], ],
) )
def test_attach_device(http_client, detach_devices, device_name, device_config): def test_attach_device(env, http_client, detach_devices, device_name, device_config):
if env["is_docker"] and device_name == "Host Bridge":
pytest.skip("Test not supported in Docker environment.")
device_config["scsi_id"] = SCSI_ID device_config["scsi_id"] = SCSI_ID
device_config["unit"] = 0 device_config["unit"] = 0

View File

@ -3,7 +3,6 @@ import uuid
import os import os
from conftest import ( from conftest import (
IMAGES_DIR,
SCSI_ID, SCSI_ID,
FILE_SIZE_1_MIB, FILE_SIZE_1_MIB,
STATUS_SUCCESS, STATUS_SUCCESS,
@ -21,6 +20,7 @@ def test_create_file(http_client, list_files, delete_file):
"file_name": file_prefix, "file_name": file_prefix,
"type": "hds", "type": "hds",
"size": 1, "size": 1,
"drive_name": "DEC RZ22",
}, },
) )
@ -85,7 +85,7 @@ def test_copy_file(http_client, create_test_image, list_files, delete_file):
# route("/files/delete", methods=["POST"]) # route("/files/delete", methods=["POST"])
def test_delete_file(http_client, create_test_image, list_files): def test_delete_file(http_client, create_test_image, list_files):
file_name = create_test_image() file_name = create_test_image(auto_delete=False)
response = http_client.post("/files/delete", data={"file_name": file_name}) response = http_client.post("/files/delete", data={"file_name": file_name})
@ -200,7 +200,7 @@ def test_upload_file(http_client, delete_file):
def test_download_file(http_client, create_test_image): def test_download_file(http_client, create_test_image):
file_name = create_test_image() file_name = create_test_image()
response = http_client.post("/files/download", data={"file": f"{IMAGES_DIR}/{file_name}"}) response = http_client.post("/files/download", data={"file": file_name})
assert response.status_code == 200 assert response.status_code == 200
assert response.headers["content-type"] == "application/octet-stream" assert response.headers["content-type"] == "application/octet-stream"
@ -209,7 +209,7 @@ def test_download_file(http_client, create_test_image):
# route("/files/download_url", methods=["POST"]) # route("/files/download_url", methods=["POST"])
def test_download_url_to_dir(httpserver, http_client, list_files, delete_file): def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_file):
file_name = str(uuid.uuid4()) file_name = str(uuid.uuid4())
http_path = f"/images/{file_name}" http_path = f"/images/{file_name}"
url = httpserver.url_for(http_path) url = httpserver.url_for(http_path)
@ -235,7 +235,9 @@ def test_download_url_to_dir(httpserver, http_client, list_files, delete_file):
assert response.status_code == 200 assert response.status_code == 200
assert response_data["status"] == STATUS_SUCCESS assert response_data["status"] == STATUS_SUCCESS
assert file_name in list_files() assert file_name in list_files()
assert response_data["messages"][0]["message"] == f"{file_name} downloaded to {IMAGES_DIR}" assert (
response_data["messages"][0]["message"] == f"{file_name} downloaded to {env['images_dir']}"
)
# Cleanup # Cleanup
delete_file(file_name) delete_file(file_name)
@ -243,6 +245,7 @@ def test_download_url_to_dir(httpserver, http_client, list_files, delete_file):
# route("/files/download_to_iso", methods=["POST"]) # route("/files/download_to_iso", methods=["POST"])
def test_download_url_to_iso( def test_download_url_to_iso(
env,
httpserver, httpserver,
http_client, http_client,
list_files, list_files,
@ -282,7 +285,7 @@ def test_download_url_to_iso(
m = response_data["messages"] m = response_data["messages"]
assert m[0]["message"] == 'Created CD-ROM ISO image with arguments "-hfs"' assert m[0]["message"] == 'Created CD-ROM ISO image with arguments "-hfs"'
assert m[1]["message"] == f"Saved image as: {IMAGES_DIR}/{iso_file_name}" assert m[1]["message"] == f"Saved image as: {env['images_dir']}/{iso_file_name}"
assert m[2]["message"] == f"Attached to SCSI ID {SCSI_ID}" assert m[2]["message"] == f"Attached to SCSI ID {SCSI_ID}"
# Cleanup # Cleanup

View File

@ -1,7 +1,6 @@
import uuid import uuid
from conftest import ( from conftest import (
CFG_DIR,
FILE_SIZE_1_MIB, FILE_SIZE_1_MIB,
STATUS_SUCCESS, STATUS_SUCCESS,
) )
@ -42,20 +41,17 @@ def test_show_named_drive_presets(http_client):
assert response.status_code == 200 assert response.status_code == 200
assert response_data["status"] == STATUS_SUCCESS assert response_data["status"] == STATUS_SUCCESS
assert "drive_properties" in response_data["data"] assert "files" in response_data["data"]
# route("/drive/cdrom", methods=["POST"]) # route("/drive/cdrom", methods=["POST"])
def test_create_cdrom_properties_file(http_client): def test_create_cdrom_properties_file(env, http_client):
file_name = f"{uuid.uuid4()}.iso" file_name = f"{uuid.uuid4()}.iso"
response = http_client.post( response = http_client.post(
"/drive/cdrom", "/drive/cdrom",
data={ data={
"vendor": "TEST_AAA", "drive_name": "Sony CDU-8012",
"product": "TEST_BBB",
"revision": "1.0A",
"block_size": 2048,
"file_name": file_name, "file_name": file_name,
}, },
) )
@ -65,7 +61,7 @@ def test_create_cdrom_properties_file(http_client):
assert response.status_code == 200 assert response.status_code == 200
assert response_data["status"] == STATUS_SUCCESS assert response_data["status"] == STATUS_SUCCESS
assert response_data["messages"][0]["message"] == ( assert response_data["messages"][0]["message"] == (
f"File created: {CFG_DIR}/{file_name}.properties" f"File created: {env['cfg_dir']}/{file_name}.properties"
) )
@ -77,12 +73,8 @@ def test_create_image_with_properties_file(http_client, delete_file):
response = http_client.post( response = http_client.post(
"/drive/create", "/drive/create",
data={ data={
"vendor": "TEST_AAA", "drive_name": "Miniscribe M8425",
"product": "TEST_BBB",
"revision": "1.0A",
"block_size": 512,
"size": FILE_SIZE_1_MIB, "size": FILE_SIZE_1_MIB,
"file_type": "hds",
"file_name": file_prefix, "file_name": file_prefix,
}, },
) )
@ -95,3 +87,12 @@ def test_create_image_with_properties_file(http_client, delete_file):
# Cleanup # Cleanup
delete_file(file_name) delete_file(file_name)
# route("/sys/manpage", methods=["POST"])
def test_show_manpage(http_client):
response = http_client.get("/sys/manpage?app=rascsi")
response_data = response.json()
assert response.status_code == 200
assert "rascsi" in response_data["data"]["manpage"]

View File

@ -1,7 +1,7 @@
import pytest import pytest
import uuid import uuid
from conftest import CFG_DIR, STATUS_SUCCESS from conftest import STATUS_SUCCESS
# route("/language", methods=["POST"]) # route("/language", methods=["POST"])
@ -74,7 +74,7 @@ def test_show_logs(http_client):
# route("/config/save", methods=["POST"]) # route("/config/save", methods=["POST"])
# route("/config/load", methods=["POST"]) # route("/config/load", methods=["POST"])
def test_save_load_and_delete_configs(http_client): def test_save_load_and_delete_configs(env, http_client):
config_name = str(uuid.uuid4()) config_name = str(uuid.uuid4())
config_json_file = f"{config_name}.json" config_json_file = f"{config_name}.json"
reserved_scsi_id = 0 reserved_scsi_id = 0
@ -96,7 +96,7 @@ def test_save_load_and_delete_configs(http_client):
assert response.status_code == 200 assert response.status_code == 200
assert response_data["status"] == STATUS_SUCCESS assert response_data["status"] == STATUS_SUCCESS
assert response_data["messages"][0]["message"] == ( assert response_data["messages"][0]["message"] == (
f"File created: {CFG_DIR}/{config_json_file}" f"File created: {env['cfg_dir']}/{config_json_file}"
) )
assert config_json_file in http_client.get("/").json()["data"]["config_files"] assert config_json_file in http_client.get("/").json()["data"]["config_files"]
@ -126,7 +126,7 @@ def test_save_load_and_delete_configs(http_client):
assert response.status_code == 200 assert response.status_code == 200
assert response_data["status"] == STATUS_SUCCESS assert response_data["status"] == STATUS_SUCCESS
assert response_data["messages"][0]["message"] == ( assert response_data["messages"][0]["message"] == (
f"Loaded configurations from: {CFG_DIR}/{config_json_file}" f"Loaded configurations from: {config_json_file}"
) )
# Confirm the application has returned to its initial state # Confirm the application has returned to its initial state
@ -146,7 +146,7 @@ def test_save_load_and_delete_configs(http_client):
assert response.status_code == 200 assert response.status_code == 200
assert response_data["status"] == STATUS_SUCCESS assert response_data["status"] == STATUS_SUCCESS
assert response_data["messages"][0]["message"] == ( assert response_data["messages"][0]["message"] == (
f"File deleted: {CFG_DIR}/{config_json_file}" f"File deleted: {env['cfg_dir']}/{config_json_file}"
) )
assert config_json_file not in http_client.get("/").json()["data"]["config_files"] assert config_json_file not in http_client.get("/").json()["data"]["config_files"]

View File

@ -1,15 +1,32 @@
import pytest import pytest
import requests import requests
import socket
import os
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addoption("--base_url", action="store", default="http://localhost:8080") default_base_url = "http://rascsi_web" if os.getenv("DOCKER") else "http://localhost:8080"
parser.addoption("--httpserver_host", action="store", default="host.docker.internal")
parser.addoption("--httpserver_listen_address", action="store", default="127.0.0.1") parser.addoption("--home_dir", action="store", default="/home/pi")
parser.addoption("--base_url", action="store", default=default_base_url)
parser.addoption("--httpserver_host", action="store", default=socket.gethostname())
parser.addoption("--httpserver_listen_address", action="store", default="0.0.0.0")
parser.addoption("--rascsi_username", action="store", default="pi") parser.addoption("--rascsi_username", action="store", default="pi")
parser.addoption("--rascsi_password", action="store", default="rascsi") parser.addoption("--rascsi_password", action="store", default="rascsi")
@pytest.fixture(scope="session")
def env(pytestconfig):
home_dir = pytestconfig.getoption("home_dir")
return {
"is_docker": bool(os.getenv("DOCKER")),
"home_dir": home_dir,
"cfg_dir": f"{home_dir}/.config/rascsi",
"images_dir": f"{home_dir}/images",
"afp_dir": f"{home_dir}/afpshare",
}
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def httpserver_listen_address(pytestconfig): def httpserver_listen_address(pytestconfig):
return (pytestconfig.getoption("httpserver_listen_address"), 0) return (pytestconfig.getoption("httpserver_listen_address"), 0)
@ -17,8 +34,7 @@ def httpserver_listen_address(pytestconfig):
@pytest.fixture(scope="function", autouse=True) @pytest.fixture(scope="function", autouse=True)
def set_httpserver_hostname(pytestconfig, httpserver): def set_httpserver_hostname(pytestconfig, httpserver):
# The HTTP requests are made by Python from within the container so we need # We need httpserver.url_for() to generate URLs pointing to the correct host
# httpserver.url_for to generate URLs which point to the Docker host
httpserver.host = pytestconfig.getoption("httpserver_host") httpserver.host = pytestconfig.getoption("httpserver_host")

View File

@ -89,45 +89,37 @@ SRC_PROTOC = \
SRC_PROTOBUF = \ SRC_PROTOBUF = \
rascsi_interface.pb.cpp rascsi_interface.pb.cpp
SRC_RASCSI_CORE = \ SRC_SHARED = \
scsi.cpp \
filepath.cpp \
fileio.cpp \
rascsi_version.cpp \ rascsi_version.cpp \
rascsi_image.cpp \
rascsi_response.cpp \
rasutil.cpp \ rasutil.cpp \
command_util.cpp \ protobuf_util.cpp \
socket_connector.cpp \ protobuf_serializer.cpp
localizer.cpp
SRC_RASCSI_CORE = \
bus.cpp \
filepath.cpp \
fileio.cpp
SRC_RASCSI_CORE += $(shell find ./rascsi -name '*.cpp')
SRC_RASCSI_CORE += $(shell find ./controllers -name '*.cpp') SRC_RASCSI_CORE += $(shell find ./controllers -name '*.cpp')
SRC_RASCSI_CORE += $(shell find ./devices -name '*.cpp') SRC_RASCSI_CORE += $(shell find ./devices -name '*.cpp')
SRC_RASCSI_CORE += $(shell find ./hal -name '*.cpp') SRC_RASCSI_CORE += $(shell find ./hal -name '*.cpp')
SRC_RASCSI_CORE += $(SRC_PROTOBUF)
SRC_RASCSI = rascsi.cpp SRC_RASCSI = rascsi.cpp
SRC_SCSIMON = \ SRC_SCSIMON = \
scsimon.cpp \ scsimon.cpp \
scsi.cpp \ bus.cpp \
rascsi_version.cpp rascsi_version.cpp
SRC_SCSIMON += $(shell find ./monitor -name '*.cpp') SRC_SCSIMON += $(shell find ./monitor -name '*.cpp')
SRC_SCSIMON += $(shell find ./hal -name '*.cpp') SRC_SCSIMON += $(shell find ./hal -name '*.cpp')
SRC_RASCTL = \ SRC_RASCTL_CORE = $(shell find ./rasctl -name '*.cpp')
rasctl.cpp\
rasctl_commands.cpp \ SRC_RASCTL = rasctl.cpp
rasctl_display.cpp \
rascsi_version.cpp \
rasutil.cpp \
command_util.cpp \
socket_connector.cpp \
localizer.cpp
SRC_RASCTL += $(SRC_PROTOBUF)
SRC_RASDUMP = \ SRC_RASDUMP = \
rasdump.cpp \ rasdump.cpp \
scsi.cpp \ bus.cpp \
filepath.cpp \ filepath.cpp \
fileio.cpp \ fileio.cpp \
rascsi_version.cpp rascsi_version.cpp
@ -140,18 +132,21 @@ SRC_GPIO_TEST = \
gpiotest.cpp gpiotest.cpp
SRC_GPIO_TEST += $(shell find ./hal -name '*.cpp') SRC_GPIO_TEST += $(shell find ./hal -name '*.cpp')
vpath %.h ./ ./controllers ./devices ./monitor ./hal ../BPI-WiringPi2/wiringPi/ vpath %.h ./ ./controllers ./devices ./monitor ./hal ./rascsi ./rasctl ../BPI-WiringPi2/wiringPi/
vpath %.cpp ./ ./controllers ./devices ./monitor ./test ./hal vpath %.cpp ./ ./controllers ./devices ./monitor ./test ./hal ./rascsi ./rasctl
vpath %.o ./$(OBJDIR) vpath %.o ./$(OBJDIR)
vpath ./$(BINDIR) vpath ./$(BINDIR)
OBJ_RASCSI_CORE := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI_CORE:%.cpp=%.o))) OBJ_RASCSI_CORE := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI_CORE:%.cpp=%.o)))
OBJ_RASCSI := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI:%.cpp=%.o))) OBJ_RASCSI := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI:%.cpp=%.o)))
OBJ_RASCTL_CORE := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCTL_CORE:%.cpp=%.o)))
OBJ_RASCTL := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCTL:%.cpp=%.o))) OBJ_RASCTL := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCTL:%.cpp=%.o)))
OBJ_RASDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASDUMP:%.cpp=%.o))) OBJ_RASDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASDUMP:%.cpp=%.o)))
OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o))) OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o)))
OBJ_RASCSI_TEST := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI_TEST:%.cpp=%.o))) OBJ_RASCSI_TEST := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI_TEST:%.cpp=%.o)))
OBJ_SHARED := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SHARED:%.cpp=%.o)))
OBJ_PROTOBUF := $(addprefix $(OBJDIR)/,$(notdir $(SRC_PROTOBUF:%.cpp=%.o)))
OBJ_GPIO_TEST := $(addprefix $(OBJDIR)/,$(notdir $(SRC_GPIO_TEST:%.cpp=%.o))) OBJ_GPIO_TEST := $(addprefix $(OBJDIR)/,$(notdir $(SRC_GPIO_TEST:%.cpp=%.o)))
GEN_PROTOBUF := $(SRC_PROTOBUF) rascsi_interface.pb.h GEN_PROTOBUF := $(SRC_PROTOBUF) rascsi_interface.pb.h
@ -160,7 +155,7 @@ LIB_BPI_WIRINGPI := $(OBJDIR)/libwiringPi.a
# The following will include all of the auto-generated dependency files (*.d) # The following will include all of the auto-generated dependency files (*.d)
# if they exist. This will trigger a rebuild of a source file if a header changes # if they exist. This will trigger a rebuild of a source file if a header changes
ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_SCSIMON) $(OBJ_RASCSI_TEST)) ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI_CORE) $(OBJ_RASCTL_CORE) $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_SCSIMON) $(OBJ_RASCSI_TEST))
-include $(ALL_DEPS) -include $(ALL_DEPS)
$(OBJDIR) $(BINDIR): $(OBJDIR) $(BINDIR):
@ -201,11 +196,13 @@ lcov: test
docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt
$(BINDIR)/$(RASCSI): $(SRC_PROTOBUF) $(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(LIB_BPI_WIRINGPI) | $(BINDIR) $(SRC_SHARED): $(SRC_PROTOBUF)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(LIB_BPI_WIRINGPI) -lpthread -lpcap -lprotobuf -lstdc++fs
$(BINDIR)/$(RASCTL): $(SRC_PROTOBUF) $(OBJ_RASCTL) $(LIB_BPI_WIRINGPI) | $(BINDIR) $(BINDIR)/$(RASCSI): $(SRC_PROTOBUF) $(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(LIB_BPI_WIRINGPI) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) -lpthread -lprotobuf $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(LIB_BPI_WIRINGPI) -lpthread -lpcap -lprotobuf -lstdc++fs
$(BINDIR)/$(RASCTL): $(SRC_PROTOBUF) $(OBJ_RASCTL_CORE) $(OBJ_RASCTL) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(LIB_BPI_WIRINGPI) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL_CORE) $(OBJ_RASCTL) $(OBJ_SHARED) $(OBJ_PROTOBUF) -lpthread -lprotobuf
$(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) $(LIB_BPI_WIRINGPI) | $(BINDIR) $(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) $(LIB_BPI_WIRINGPI) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP) $(LIB_BPI_WIRINGPI) -lpthread $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP) $(LIB_BPI_WIRINGPI) -lpthread
@ -213,14 +210,13 @@ $(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) $(LIB_BPI_WIRINGPI) | $(BINDIR)
$(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) $(LIB_BPI_WIRINGPI) | $(BINDIR) $(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) $(LIB_BPI_WIRINGPI) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) $(LIB_BPI_WIRINGPI) -lpthread $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) $(LIB_BPI_WIRINGPI) -lpthread
$(BINDIR)/$(RASCSI_TEST): $(SRC_PROTOBUF) $(OBJ_RASCSI_CORE) $(OBJ_RASCSI_TEST) $(LIB_BPI_WIRINGPI) | $(BINDIR) $(BINDIR)/$(RASCSI_TEST): $(SRC_PROTOBUF) $(OBJ_RASCSI_CORE) $(OBJ_RASCTL_CORE) $(OBJ_RASCSI_TEST) $(OBJ_RASCTL_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(LIB_BPI_WIRINGPI) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCSI_TEST) $(LIB_BPI_WIRINGPI) -lpthread -lpcap -lprotobuf -lstdc++fs -lgmock -lgtest $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCTL_CORE) $(OBJ_RASCSI_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(LIB_BPI_WIRINGPI) -lpthread -lpcap -lprotobuf -lstdc++fs -lgmock -lgtest
$(BINDIR)/$(GPIO_TEST): $(OBJ_GPIO_TEST) $(LIB_BPI_WIRINGPI) | $(BINDIR) $(BINDIR)/$(GPIO_TEST): $(OBJ_GPIO_TEST) $(LIB_BPI_WIRINGPI) | $(BINDIR)
echo $(OBJ_GPIO_TEST) echo $(OBJ_GPIO_TEST)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_GPIO_TEST) $(LIB_BPI_WIRINGPI) -lpthread $(CXX) $(CXXFLAGS) -o $@ $(OBJ_GPIO_TEST) $(LIB_BPI_WIRINGPI) -lpthread
# Phony rules for building individual utilities # Phony rules for building individual utilities
.PHONY: $(RASCSI) $(RASCTL) $(RASDUMP) $(SCSIMON) $(GPIO_TEST) .PHONY: $(RASCSI) $(RASCTL) $(RASDUMP) $(SCSIMON) $(GPIO_TEST)
$(RASCSI) : $(BINDIR)/$(RASCSI) $(RASCSI) : $(BINDIR)/$(RASCSI)

View File

@ -1,15 +1,14 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// X68000 EMULATOR "XM6" // X68000 EMULATOR "XM6"
// //
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp) // Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// // Copyright (C) 2022 Uwe Seimet
// [ SCSI Common Functionality ]
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "scsi.h" #include "bus.h"
using namespace std; using namespace std;
@ -31,9 +30,9 @@ BUS::phase_t BUS::GetPhase()
} }
// Get target phase from bus signal line // Get target phase from bus signal line
DWORD mci = GetMSG() ? 0x04 : 0x00; int mci = GetMSG() ? 0b100 : 0b000;
mci |= GetCD() ? 0x02 : 0x00; mci |= GetCD() ? 0b010 : 0b000;
mci |= GetIO() ? 0x01 : 0x00; mci |= GetIO() ? 0b001 : 0b000;
return GetPhase(mci); return GetPhase(mci);
} }
@ -42,13 +41,9 @@ BUS::phase_t BUS::GetPhase()
// Determine Phase String phase enum // Determine Phase String phase enum
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
const char* BUS::GetPhaseStrRaw(phase_t current_phase){ const char* BUS::GetPhaseStrRaw(phase_t current_phase) {
if(current_phase <= phase_t::reserved) { const auto& it = phase_str_mapping.find(current_phase);
return phase_str_table[(int)current_phase]; return it != phase_str_mapping.end() ? it->second : "INVALID";
}
else {
return "INVALID";
}
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -70,24 +65,21 @@ const array<BUS::phase_t, 8> BUS::phase_table = {
phase_t::msgin // | 1 | 1 | 1 | phase_t::msgin // | 1 | 1 | 1 |
}; };
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// Phase Table // Phase string to phase mapping
// This MUST be kept in sync with the phase_t enum type!
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
const array<const char*, 12> BUS::phase_str_table = { const unordered_map<BUS::phase_t, const char*> BUS::phase_str_mapping = {
"busfree", { phase_t::busfree, "busfree" },
"arbitration", { phase_t::arbitration, "arbitration" },
"selection", { phase_t::selection, "selection" },
"reselection", { phase_t::reselection, "reselection" },
"command", { phase_t::command, "command" },
"execute", { phase_t::datain, "datain" },
"datain", { phase_t::dataout, "dataout" },
"dataout", { phase_t::status, "status" },
"status", { phase_t::msgin, "msgin" },
"msgin", { phase_t::msgout, "msgout" },
"msgout", { phase_t::reserved, "reserved" }
"reserved"
}; };

113
src/raspberrypi/bus.h Normal file
View File

@ -0,0 +1,113 @@
//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
//
//---------------------------------------------------------------------------
#pragma once
#include "os.h"
#include "scsi.h"
#include <array>
#include <unordered_map>
using namespace std;
class BUS
{
public:
// Operation modes definition
enum class mode_e {
TARGET = 0,
INITIATOR = 1,
MONITOR = 2,
};
// Phase definitions
enum class phase_t : int {
busfree,
arbitration,
selection,
reselection,
command,
datain,
dataout,
status,
msgin,
msgout,
reserved
};
BUS() = default;
virtual ~BUS() = default;
// Basic Functions
virtual bool Init(mode_e mode) = 0;
virtual void Reset() = 0;
virtual void Cleanup() = 0;
phase_t GetPhase();
static phase_t GetPhase(int mci)
{
return phase_table[mci];
}
// Get the string phase name, based upon the raw data
static const char* GetPhaseStrRaw(phase_t current_phase);
// Extract as specific pin field from a raw data capture
static inline uint32_t GetPinRaw(uint32_t raw_data, uint32_t pin_num)
{
return ((raw_data >> pin_num) & 1);
}
virtual bool GetBSY() const = 0;
virtual void SetBSY(bool ast) = 0;
virtual bool GetSEL() const = 0;
virtual void SetSEL(bool ast) = 0;
virtual bool GetATN() const = 0;
virtual void SetATN(bool ast) = 0;
virtual bool GetACK() const = 0;
virtual void SetACK(bool ast) = 0;
virtual bool GetRST() const = 0;
virtual void SetRST(bool ast) = 0;
virtual bool GetMSG() const = 0;
virtual void SetMSG(bool ast) = 0;
virtual bool GetCD() const = 0;
virtual void SetCD(bool ast) = 0;
virtual bool GetIO() = 0;
virtual void SetIO(bool ast) = 0;
virtual bool GetREQ() const = 0;
virtual void SetREQ(bool ast) = 0;
virtual BYTE GetDAT() = 0;
virtual void SetDAT(BYTE dat) = 0;
virtual bool GetDP() const = 0; // Get parity signal
virtual uint32_t Acquire() = 0;
virtual int CommandHandShake(BYTE *buf) = 0;
virtual int ReceiveHandShake(BYTE *buf, int count) = 0;
virtual int SendHandShake(BYTE *buf, int count, int delay_after_bytes) = 0;
virtual bool GetSignal(int pin) const = 0;
// Get SCSI input signal value
virtual void SetSignal(int pin, bool ast) = 0;
// Set SCSI output signal value
static const int SEND_NO_DELAY = -1;
// Passed into SendHandShake when we don't want to delay
private:
static const array<phase_t, 8> phase_table;
static const unordered_map<phase_t, const char *> phase_str_mapping;
};

View File

@ -1,27 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include <string>
class SocketConnector;
class Localizer;
struct CommandContext
{
CommandContext(const SocketConnector& c, const Localizer& l, int f, const std::string& s)
: connector(c), localizer(l), fd(f), locale(s) {}
~CommandContext() = default;
const SocketConnector& connector;
const Localizer& localizer;
int fd;
std::string locale;
};

View File

@ -9,8 +9,7 @@
#include "log.h" #include "log.h"
#include "rascsi_interface.pb.h" #include "rascsi_interface.pb.h"
#include "localizer.h" #include "protobuf_serializer.h"
#include "socket_connector.h"
#include "command_util.h" #include "command_util.h"
#include <sstream> #include <sstream>
@ -86,51 +85,3 @@ void command_util::AddParam(PbDeviceDefinition& device, const string& key, strin
map[key] = value; map[key] = value;
} }
} }
bool command_util::ReturnLocalizedError(const CommandContext& context, LocalizationKey key,
const string& arg1, const string& arg2, const string& arg3)
{
return ReturnLocalizedError(context, key, NO_ERROR_CODE, arg1, arg2, arg3);
}
bool command_util::ReturnLocalizedError(const CommandContext& context, LocalizationKey key,
PbErrorCode error_code, const string& arg1, const string& arg2, const string& arg3)
{
// For the logfile always use English
LOGERROR("%s", context.localizer.Localize(key, "en", arg1, arg2, arg3).c_str())
return ReturnStatus(context, false, context.localizer.Localize(key, context.locale, arg1, arg2, arg3), error_code,
false);
}
bool command_util::ReturnStatus(const CommandContext& context, bool status, const string& msg,
PbErrorCode error_code, bool log)
{
// Do not log twice if logging has already been done in the localized error handling above
if (log && !status && !msg.empty()) {
LOGERROR("%s", msg.c_str())
}
if (context.fd == -1) {
if (!msg.empty()) {
if (status) {
FPRT(stderr, "Error: ");
FPRT(stderr, "%s", msg.c_str());
FPRT(stderr, "\n");
}
else {
FPRT(stdout, "%s", msg.c_str());
FPRT(stderr, "\n");
}
}
}
else {
PbResult result;
result.set_status(status);
result.set_error_code(error_code);
result.set_msg(msg);
context.connector.SerializeMessage(context.fd, result);
}
return status;
}

View File

@ -20,6 +20,6 @@
#define USE_SEL_EVENT_ENABLE // Check SEL signal by event #define USE_SEL_EVENT_ENABLE // Check SEL signal by event
// This avoids an indefinite loop with warnings if there is no RaSCSI hardware // This avoids an indefinite loop with warnings if there is no RaSCSI hardware
// and thus helps with running rasctl and unit test on x86 hardware. // and thus helps with running rasctl and unit test on x86 hardware.
#if defined(__x86_64__) || defined(__X86__) || !defined(__linux) #if defined(__x86_64__) || defined(__X86__) || !defined(__linux__)
#undef USE_SEL_EVENT_ENABLE #undef USE_SEL_EVENT_ENABLE
#endif #endif

View File

@ -7,8 +7,9 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "abstract_controller.h" #include "rascsi_exceptions.h"
#include "devices/primary_device.h" #include "devices/primary_device.h"
#include "abstract_controller.h"
void AbstractController::AllocateBuffer(size_t size) void AbstractController::AllocateBuffer(size_t size)
{ {
@ -17,7 +18,18 @@ void AbstractController::AllocateBuffer(size_t size)
} }
} }
PrimaryDevice *AbstractController::GetDeviceForLun(int lun) const { unordered_set<shared_ptr<PrimaryDevice>> AbstractController::GetDevices() const
{
unordered_set<shared_ptr<PrimaryDevice>> devices;
for (const auto& [id, lun] : luns) {
devices.insert(lun);
}
return devices;
}
shared_ptr<PrimaryDevice> AbstractController::GetDeviceForLun(int lun) const {
const auto& it = luns.find(lun); const auto& it = luns.find(lun);
return it == luns.end() ? nullptr : it->second; return it == luns.end() ? nullptr : it->second;
} }
@ -26,7 +38,7 @@ void AbstractController::Reset()
{ {
SetPhase(BUS::phase_t::busfree); SetPhase(BUS::phase_t::busfree);
ctrl.status = 0x00; ctrl.status = status::GOOD;
ctrl.message = 0x00; ctrl.message = 0x00;
ctrl.blocks = 0; ctrl.blocks = 0;
ctrl.next = 0; ctrl.next = 0;
@ -75,18 +87,15 @@ void AbstractController::ProcessPhase()
break; break;
default: default:
assert(false); LOGERROR("Cannot process phase %s", BUS::GetPhaseStrRaw(GetPhase()))
throw scsi_exception();
break; break;
} }
} }
bool AbstractController::AddDevice(PrimaryDevice *device) bool AbstractController::AddDevice(shared_ptr<PrimaryDevice> device)
{ {
if (device->GetLun() >= GetMaxLuns()) { if (device->GetLun() < 0 || device->GetLun() >= GetMaxLuns() || HasDeviceForLun(device->GetLun())) {
return false;
}
if (HasDeviceForLun(device->GetLun())) {
return false; return false;
} }
@ -96,7 +105,7 @@ bool AbstractController::AddDevice(PrimaryDevice *device)
return true; return true;
} }
bool AbstractController::DeleteDevice(const PrimaryDevice *device) bool AbstractController::DeleteDevice(const shared_ptr<PrimaryDevice> device)
{ {
return luns.erase(device->GetLun()) == 1; return luns.erase(device->GetLun()) == 1;
} }

View File

@ -12,23 +12,24 @@
#pragma once #pragma once
#include "scsi.h" #include "scsi.h"
#include "bus.h"
#include "phase_handler.h"
#include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <memory> #include <memory>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class PrimaryDevice; class PrimaryDevice;
class AbstractController class AbstractController : public PhaseHandler
{ {
friend class PrimaryDevice; friend class PrimaryDevice;
friend class ScsiController; friend class ScsiController;
BUS::phase_t phase = BUS::phase_t::busfree; // Logical units of this controller mapped to their LUN numbers
unordered_map<int, shared_ptr<PrimaryDevice>> luns;
// Logical units of this device controller mapped to their LUN numbers
unordered_map<int, PrimaryDevice *> luns;
public: public:
@ -41,7 +42,7 @@ public:
using ctrl_t = struct _ctrl_t { using ctrl_t = struct _ctrl_t {
vector<int> cmd; // Command data, dynamically allocated per received command vector<int> cmd; // Command data, dynamically allocated per received command
uint32_t status; // Status data scsi_defs::status status; // Status data
int message; // Message data int message; // Message data
// Transfer // Transfer
@ -52,21 +53,8 @@ public:
uint32_t length; // Transfer remaining length uint32_t length; // Transfer remaining length
}; };
AbstractController(shared_ptr<BUS> bus, int target_id, int luns) : target_id(target_id), bus(bus), max_luns(luns) {} AbstractController(BUS& bus, int target_id, int max_luns) : target_id(target_id), bus(bus), max_luns(max_luns) {}
virtual ~AbstractController() = default; ~AbstractController() override = default;
AbstractController(AbstractController&) = delete;
AbstractController& operator=(const AbstractController&) = delete;
virtual void BusFree() = 0;
virtual void Selection() = 0;
virtual void Command() = 0;
virtual void Status() = 0;
virtual void DataIn() = 0;
virtual void DataOut() = 0;
virtual void MsgIn() = 0;
virtual void MsgOut() = 0;
virtual BUS::phase_t Process(int) = 0;
virtual void Error(scsi_defs::sense_key, scsi_defs::asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION, virtual void Error(scsi_defs::sense_key, scsi_defs::asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
scsi_defs::status = scsi_defs::status::CHECK_CONDITION) = 0; scsi_defs::status = scsi_defs::status::CHECK_CONDITION) = 0;
@ -81,19 +69,19 @@ public:
int GetTargetId() const { return target_id; } int GetTargetId() const { return target_id; }
int GetMaxLuns() const { return max_luns; } int GetMaxLuns() const { return max_luns; }
bool HasLuns() const { return !luns.empty(); } int GetLunCount() const { return (int)luns.size(); }
PrimaryDevice *GetDeviceForLun(int) const; unordered_set<shared_ptr<PrimaryDevice>> GetDevices() const;
bool AddDevice(PrimaryDevice *); shared_ptr<PrimaryDevice> GetDeviceForLun(int) const;
bool DeleteDevice(const PrimaryDevice *); bool AddDevice(shared_ptr<PrimaryDevice>);
bool DeleteDevice(const shared_ptr<PrimaryDevice>);
bool HasDeviceForLun(int) const; bool HasDeviceForLun(int) const;
int ExtractInitiatorId(int id_data) const; int ExtractInitiatorId(int) const;
void AllocateBuffer(size_t); void AllocateBuffer(size_t);
vector<BYTE>& GetBuffer() { return ctrl.buffer; } vector<BYTE>& GetBuffer() { return ctrl.buffer; }
size_t GetBufferSize() const { return ctrl.buffer.size(); } scsi_defs::status GetStatus() const { return ctrl.status; }
uint32_t GetStatus() const { return ctrl.status; } void SetStatus(scsi_defs::status s) { ctrl.status = s; }
void SetStatus(uint32_t s) { ctrl.status = s; }
uint32_t GetLength() const { return ctrl.length; } uint32_t GetLength() const { return ctrl.length; }
protected: protected:
@ -106,25 +94,15 @@ protected:
vector<int>& InitCmd(int size) { ctrl.cmd.resize(size); return ctrl.cmd; } vector<int>& InitCmd(int size) { ctrl.cmd.resize(size); return ctrl.cmd; }
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 ResetOffset() { ctrl.offset = 0; }
void UpdateOffsetAndLength() { ctrl.offset += ctrl.length; ctrl.length = 0; } void UpdateOffsetAndLength() { ctrl.offset += ctrl.length; ctrl.length = 0; }
BUS::phase_t GetPhase() const { return phase; }
void SetPhase(BUS::phase_t p) { phase = p; }
bool IsSelection() const { return phase == BUS::phase_t::selection; }
bool IsBusFree() const { return phase == BUS::phase_t::busfree; }
bool IsCommand() const { return phase == BUS::phase_t::command; }
bool IsStatus() const { return phase == BUS::phase_t::status; }
bool IsDataIn() const { return phase == BUS::phase_t::datain; }
bool IsDataOut() const { return phase == BUS::phase_t::dataout; }
bool IsMsgIn() const { return phase == BUS::phase_t::msgin; }
bool IsMsgOut() const { return phase == BUS::phase_t::msgout; }
private: private:
int target_id; int target_id;
shared_ptr<BUS> bus; BUS& bus;
int max_luns; int max_luns;

View File

@ -9,23 +9,33 @@
#include "devices/device_factory.h" #include "devices/device_factory.h"
#include "devices/primary_device.h" #include "devices/primary_device.h"
#include "devices/file_support.h"
#include "scsi_controller.h" #include "scsi_controller.h"
#include "controller_manager.h" #include "controller_manager.h"
using namespace std; using namespace std;
bool ControllerManager::CreateScsiController(shared_ptr<BUS> bus, PrimaryDevice *device) bool ControllerManager::AttachToScsiController(int id, shared_ptr<PrimaryDevice> device)
{ {
shared_ptr<AbstractController> controller = FindController(device->GetId()); auto controller = FindController(id);
if (controller == nullptr) { if (controller == nullptr) {
controller = make_shared<ScsiController>(bus, device->GetId()); controller = make_shared<ScsiController>(bus, id);
controllers[device->GetId()] = controller; if (controller->AddDevice(device)) {
controllers[id] = controller;
return true;
}
return false;
} }
return controller->AddDevice(device); return controller->AddDevice(device);
} }
bool ControllerManager::DeleteController(shared_ptr<AbstractController> controller)
{
return controllers.erase(controller->GetTargetId()) == 1;
}
shared_ptr<AbstractController> ControllerManager::IdentifyController(int data) const shared_ptr<AbstractController> ControllerManager::IdentifyController(int data) const
{ {
for (const auto& [id, controller] : controllers) { for (const auto& [id, controller] : controllers) {
@ -43,6 +53,18 @@ shared_ptr<AbstractController> ControllerManager::FindController(int target_id)
return it == controllers.end() ? nullptr : it->second; return it == controllers.end() ? nullptr : it->second;
} }
unordered_set<shared_ptr<PrimaryDevice>> ControllerManager::GetAllDevices() const
{
unordered_set<shared_ptr<PrimaryDevice>> devices;
for (const auto& [id, controller] : controllers) {
const auto& d = controller->GetDevices();
devices.insert(d.begin(), d.end());
}
return devices;
}
void ControllerManager::DeleteAllControllers() void ControllerManager::DeleteAllControllers()
{ {
controllers.clear(); controllers.clear();
@ -55,7 +77,7 @@ void ControllerManager::ResetAllControllers() const
} }
} }
PrimaryDevice *ControllerManager::GetDeviceByIdAndLun(int id, int lun) 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); return controller->GetDeviceForLun(lun);

View File

@ -12,9 +12,10 @@
#pragma once #pragma once
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <memory> #include <memory>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class BUS; class BUS;
class AbstractController; class AbstractController;
@ -22,23 +23,24 @@ class PrimaryDevice;
class ControllerManager class ControllerManager
{ {
BUS& bus;
unordered_map<int, shared_ptr<AbstractController>> controllers;
public: public:
ControllerManager() = default; explicit ControllerManager(BUS& bus) : bus(bus) {}
~ControllerManager() = default; ~ControllerManager() = default;
ControllerManager(ControllerManager&) = delete;
ControllerManager& operator=(const ControllerManager&) = delete;
// Maximum number of controller devices // Maximum number of controller devices
static const int DEVICE_MAX = 8; static const int DEVICE_MAX = 8;
bool CreateScsiController(shared_ptr<BUS>, PrimaryDevice *); bool AttachToScsiController(int, shared_ptr<PrimaryDevice>);
bool DeleteController(shared_ptr<AbstractController>);
shared_ptr<AbstractController> IdentifyController(int) const; shared_ptr<AbstractController> IdentifyController(int) const;
shared_ptr<AbstractController> FindController(int) const; shared_ptr<AbstractController> FindController(int) const;
unordered_set<shared_ptr<PrimaryDevice>> GetAllDevices() const;
void DeleteAllControllers(); void DeleteAllControllers();
void ResetAllControllers() const; void ResetAllControllers() const;
PrimaryDevice *GetDeviceByIdAndLun(int, int) const; shared_ptr<PrimaryDevice> GetDeviceByIdAndLun(int, int) const;
unordered_map<int, shared_ptr<AbstractController>> controllers;
}; };

View File

@ -0,0 +1,46 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include "scsi.h"
class PhaseHandler
{
BUS::phase_t phase = BUS::phase_t::busfree;
public:
PhaseHandler() = default;
virtual ~PhaseHandler() = default;
virtual void BusFree() = 0;
virtual void Selection() = 0;
virtual void Command() = 0;
virtual void Status() = 0;
virtual void DataIn() = 0;
virtual void DataOut() = 0;
virtual void MsgIn() = 0;
virtual void MsgOut() = 0;
virtual BUS::phase_t Process(int) = 0;
protected:
BUS::phase_t GetPhase() const { return phase; }
void SetPhase(BUS::phase_t p) { phase = p; }
bool IsSelection() const { return phase == BUS::phase_t::selection; }
bool IsBusFree() const { return phase == BUS::phase_t::busfree; }
bool IsCommand() const { return phase == BUS::phase_t::command; }
bool IsStatus() const { return phase == BUS::phase_t::status; }
bool IsDataIn() const { return phase == BUS::phase_t::datain; }
bool IsDataOut() const { return phase == BUS::phase_t::dataout; }
bool IsMsgIn() const { return phase == BUS::phase_t::msgin; }
bool IsMsgOut() const { return phase == BUS::phase_t::msgout; }
};

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -21,13 +21,13 @@
#include "scsi_controller.h" #include "scsi_controller.h"
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#ifdef __linux #ifdef __linux__
#include <linux/if_tun.h> #include <linux/if_tun.h>
#endif #endif
using namespace scsi_defs; using namespace scsi_defs;
ScsiController::ScsiController(shared_ptr<BUS> bus, int target_id) : AbstractController(bus, target_id, LUN_MAX) ScsiController::ScsiController(BUS& bus, int target_id) : AbstractController(bus, target_id, LUN_MAX)
{ {
// The initial buffer size will default to either the default buffer size OR // The initial buffer size will default to either the default buffer size OR
// the size of an Ethernet message, whichever is larger. // the size of an Ethernet message, whichever is larger.
@ -52,17 +52,17 @@ void ScsiController::Reset()
BUS::phase_t ScsiController::Process(int id) BUS::phase_t ScsiController::Process(int id)
{ {
// Get bus information // Get bus information
bus->Acquire(); bus.Acquire();
// Check to see if the reset signal was asserted // Check to see if the reset signal was asserted
if (bus->GetRST()) { if (bus.GetRST()) {
LOGWARN("RESET signal received!") LOGWARN("RESET signal received!")
// Reset the controller // Reset the controller
Reset(); Reset();
// Reset the bus // Reset the bus
bus->Reset(); bus.Reset();
return GetPhase(); return GetPhase();
} }
@ -79,14 +79,14 @@ BUS::phase_t ScsiController::Process(int id)
try { try {
ProcessPhase(); ProcessPhase();
} }
catch(const scsi_error_exception&) { catch(const scsi_exception&) {
// Any exception should have been handled during the phase processing // Any exception should have been handled during the phase processing
assert(false); assert(false);
LOGERROR("%s Unhandled SCSI error, resetting controller and bus and entering bus free phase", __PRETTY_FUNCTION__) LOGERROR("%s Unhandled SCSI error, resetting controller and bus and entering bus free phase", __PRETTY_FUNCTION__)
Reset(); Reset();
bus->Reset(); bus.Reset();
BusFree(); BusFree();
} }
@ -101,14 +101,14 @@ void ScsiController::BusFree()
SetPhase(BUS::phase_t::busfree); SetPhase(BUS::phase_t::busfree);
bus->SetREQ(false); bus.SetREQ(false);
bus->SetMSG(false); bus.SetMSG(false);
bus->SetCD(false); bus.SetCD(false);
bus->SetIO(false); bus.SetIO(false);
bus->SetBSY(false); bus.SetBSY(false);
// Initialize status and message // Initialize status and message
SetStatus(0); SetStatus(status::GOOD);
ctrl.message = 0x00; ctrl.message = 0x00;
// Initialize ATN message reception status // Initialize ATN message reception status
@ -120,21 +120,21 @@ void ScsiController::BusFree()
bytes_to_transfer = 0; bytes_to_transfer = 0;
// When the bus is free RaSCSI or the Pi may be shut down. // When the bus is free RaSCSI or the Pi may be shut down.
// TODO Try to find a better place for this code without breaking encapsulation // This code has to be executed in the bus free phase and thus has to be located in the controller.
switch(shutdown_mode) { switch(shutdown_mode) {
case AbstractController::rascsi_shutdown_mode::STOP_RASCSI: case rascsi_shutdown_mode::STOP_RASCSI:
LOGINFO("RaSCSI shutdown requested") LOGINFO("RaSCSI shutdown requested")
exit(0); exit(0);
break; break;
case AbstractController::rascsi_shutdown_mode::STOP_PI: case rascsi_shutdown_mode::STOP_PI:
LOGINFO("Raspberry Pi shutdown requested") LOGINFO("Raspberry Pi shutdown requested")
if (system("init 0") == -1) { if (system("init 0") == -1) {
LOGERROR("Raspberry Pi shutdown failed: %s", strerror(errno)) LOGERROR("Raspberry Pi shutdown failed: %s", strerror(errno))
} }
break; break;
case AbstractController::rascsi_shutdown_mode::RESTART_PI: case rascsi_shutdown_mode::RESTART_PI:
LOGINFO("Raspberry Pi restart requested") LOGINFO("Raspberry Pi restart requested")
if (system("init 6") == -1) { if (system("init 6") == -1) {
LOGERROR("Raspberry Pi restart failed: %s", strerror(errno)) LOGERROR("Raspberry Pi restart failed: %s", strerror(errno))
@ -149,7 +149,7 @@ void ScsiController::BusFree()
} }
// Move to selection phase // Move to selection phase
if (bus->GetSEL() && !bus->GetBSY()) { if (bus.GetSEL() && !bus.GetBSY()) {
Selection(); Selection();
} }
} }
@ -158,12 +158,12 @@ void ScsiController::Selection()
{ {
if (!IsSelection()) { if (!IsSelection()) {
// A different device controller was selected // A different device controller was selected
if (int id = 1 << GetTargetId(); ((int)bus->GetDAT() & id) == 0) { if (int id = 1 << GetTargetId(); ((int)bus.GetDAT() & id) == 0) {
return; return;
} }
// Abort if there is no LUN for this controller // Abort if there is no LUN for this controller
if (!HasLuns()) { if (!GetLunCount()) {
return; return;
} }
@ -172,14 +172,14 @@ void ScsiController::Selection()
SetPhase(BUS::phase_t::selection); SetPhase(BUS::phase_t::selection);
// Raise BSY and respond // Raise BSY and respond
bus->SetBSY(true); bus.SetBSY(true);
return; return;
} }
// Selection completed // Selection completed
if (!bus->GetSEL() && bus->GetBSY()) { if (!bus.GetSEL() && bus.GetBSY()) {
// Message out phase if ATN=1, otherwise command phase // Message out phase if ATN=1, otherwise command phase
if (bus->GetATN()) { if (bus.GetATN()) {
MsgOut(); MsgOut();
} else { } else {
Command(); Command();
@ -194,12 +194,12 @@ void ScsiController::Command()
SetPhase(BUS::phase_t::command); SetPhase(BUS::phase_t::command);
bus->SetMSG(false); bus.SetMSG(false);
bus->SetCD(true); bus.SetCD(true);
bus->SetIO(false); bus.SetIO(false);
int actual_count = bus->CommandHandShake(GetBuffer().data()); const int actual_count = bus.CommandHandShake(GetBuffer().data());
int command_byte_count = GPIOBUS::GetCommandByteCount(GetBuffer()[0]); const int command_byte_count = GPIOBUS::GetCommandByteCount(GetBuffer()[0]);
// If not able to receive all, move to the status phase // If not able to receive all, move to the status phase
if (actual_count != command_byte_count) { if (actual_count != command_byte_count) {
@ -229,8 +229,6 @@ void ScsiController::Execute()
{ {
LOGDEBUG("++++ CMD ++++ %s Executing command $%02X", __PRETTY_FUNCTION__, (int)GetOpcode()) LOGDEBUG("++++ CMD ++++ %s Executing command $%02X", __PRETTY_FUNCTION__, (int)GetOpcode())
SetPhase(BUS::phase_t::execute);
// Initialization for data transfer // Initialization for data transfer
ResetOffset(); ResetOffset();
ctrl.blocks = 1; ctrl.blocks = 1;
@ -238,7 +236,7 @@ void ScsiController::Execute()
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE // Discard pending sense data from the previous command if the current command is not REQUEST SENSE
if (GetOpcode() != scsi_command::eCmdRequestSense) { if (GetOpcode() != scsi_command::eCmdRequestSense) {
SetStatus(0); SetStatus(status::GOOD);
} }
int lun = GetEffectiveLun(); int lun = GetEffectiveLun();
@ -258,21 +256,21 @@ void ScsiController::Execute()
} }
} }
PrimaryDevice *device = GetDeviceForLun(lun); auto device = GetDeviceForLun(lun);
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE // Discard pending sense data from the previous command if the current command is not REQUEST SENSE
if (GetOpcode() != scsi_command::eCmdRequestSense) { if (GetOpcode() != scsi_command::eCmdRequestSense) {
device->SetStatusCode(0); device->SetStatusCode(0);
} }
try { try {
if (!device->Dispatch(GetOpcode())) { if (!device->Dispatch(GetOpcode())) {
LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetTargetId(), lun, (int)GetOpcode()) LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetTargetId(), lun, (int)GetOpcode())
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
} }
} }
catch(const scsi_error_exception& e) { //NOSONAR This exception is handled properly catch(const scsi_exception& e) { //NOSONAR This exception is handled properly
Error(e.get_sense_key(), e.get_asc(), e.get_status()); Error(e.get_sense_key(), e.get_asc(), e.get_status());
// Fall through // Fall through
@ -298,14 +296,14 @@ void ScsiController::Status()
SysTimer::instance().SleepUsec(5); SysTimer::instance().SleepUsec(5);
} }
LOGTRACE("%s Status Phase $%02X",__PRETTY_FUNCTION__, GetStatus()) LOGTRACE("%s Status Phase, status is $%02X",__PRETTY_FUNCTION__, (int)GetStatus())
SetPhase(BUS::phase_t::status); SetPhase(BUS::phase_t::status);
// Signal line operated by the target // Signal line operated by the target
bus->SetMSG(false); bus.SetMSG(false);
bus->SetCD(true); bus.SetCD(true);
bus->SetIO(true); bus.SetIO(true);
// Data transfer is 1 byte x 1 block // Data transfer is 1 byte x 1 block
ResetOffset(); ResetOffset();
@ -326,9 +324,9 @@ void ScsiController::MsgIn()
SetPhase(BUS::phase_t::msgin); SetPhase(BUS::phase_t::msgin);
bus->SetMSG(true); bus.SetMSG(true);
bus->SetCD(true); bus.SetCD(true);
bus->SetIO(true); bus.SetIO(true);
// length, blocks are already set // length, blocks are already set
assert(HasValidLength()); assert(HasValidLength());
@ -357,9 +355,9 @@ void ScsiController::MsgOut()
SetPhase(BUS::phase_t::msgout); SetPhase(BUS::phase_t::msgout);
bus->SetMSG(true); bus.SetMSG(true);
bus->SetCD(true); bus.SetCD(true);
bus->SetIO(false); bus.SetIO(false);
// Data transfer is 1 byte x 1 block // Data transfer is 1 byte x 1 block
ResetOffset(); ResetOffset();
@ -390,9 +388,9 @@ void ScsiController::DataIn()
SetPhase(BUS::phase_t::datain); SetPhase(BUS::phase_t::datain);
bus->SetMSG(false); bus.SetMSG(false);
bus->SetCD(false); bus.SetCD(false);
bus->SetIO(true); bus.SetIO(true);
// length, blocks are already set // length, blocks are already set
assert(ctrl.blocks > 0); assert(ctrl.blocks > 0);
@ -423,9 +421,9 @@ void ScsiController::DataOut()
SetPhase(BUS::phase_t::dataout); SetPhase(BUS::phase_t::dataout);
// Signal line operated by the target // Signal line operated by the target
bus->SetMSG(false); bus.SetMSG(false);
bus->SetCD(false); bus.SetCD(false);
bus->SetIO(false); bus.SetIO(false);
ResetOffset(); ResetOffset();
return; return;
@ -437,12 +435,12 @@ void ScsiController::DataOut()
void ScsiController::Error(sense_key sense_key, asc asc, status status) void ScsiController::Error(sense_key sense_key, asc asc, status status)
{ {
// Get bus information // Get bus information
bus->Acquire(); bus.Acquire();
// Reset check // Reset check
if (bus->GetRST()) { if (bus.GetRST()) {
Reset(); Reset();
bus->Reset(); bus.Reset();
return; return;
} }
@ -461,11 +459,13 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
} }
if (sense_key != sense_key::NO_SENSE || asc != asc::NO_ADDITIONAL_SENSE_INFORMATION) { if (sense_key != sense_key::NO_SENSE || asc != asc::NO_ADDITIONAL_SENSE_INFORMATION) {
LOGDEBUG("Error status: Sense Key $%02X, ASC $%02X", (int)sense_key, (int)asc)
// Set Sense Key and ASC for a subsequent REQUEST SENSE // Set Sense Key and ASC for a subsequent REQUEST SENSE
GetDeviceForLun(lun)->SetStatusCode(((int)sense_key << 16) | ((int)asc << 8)); GetDeviceForLun(lun)->SetStatusCode(((int)sense_key << 16) | ((int)asc << 8));
} }
SetStatus((uint32_t)status); SetStatus(status);
ctrl.message = 0x00; ctrl.message = 0x00;
LOGTRACE("%s Error (to status phase)", __PRETTY_FUNCTION__) LOGTRACE("%s Error (to status phase)", __PRETTY_FUNCTION__)
@ -475,16 +475,16 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
void ScsiController::Send() void ScsiController::Send()
{ {
assert(!bus->GetREQ()); assert(!bus.GetREQ());
assert(bus->GetIO()); assert(bus.GetIO());
if (HasValidLength()) { if (HasValidLength()) {
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Sending handhake with offset " + to_string(ctrl.offset) + ", length " LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Sending handhake with offset " + to_string(GetOffset()) + ", length "
+ to_string(ctrl.length)).c_str()) + to_string(ctrl.length)).c_str())
// TODO The delay has to be taken from ctrl.unit[lun], but as there are currently no Daynaport drivers for // TODO The delay has to be taken from ctrl.unit[lun], but as there are currently no Daynaport drivers for
// LUNs other than 0 this work-around works. // LUNs other than 0 this work-around works.
if (int len = bus->SendHandShake(GetBuffer().data() + ctrl.offset, ctrl.length, if (const int len = bus.SendHandShake(GetBuffer().data() + ctrl.offset, ctrl.length,
HasDeviceForLun(0) ? GetDeviceForLun(0)->GetSendDelay() : 0); HasDeviceForLun(0) ? GetDeviceForLun(0)->GetSendDelay() : 0);
len != (int)ctrl.length) { len != (int)ctrl.length) {
// If you cannot send all, move to status phase // If you cannot send all, move to status phase
@ -571,15 +571,15 @@ void ScsiController::Receive()
LOGTRACE("%s",__PRETTY_FUNCTION__) LOGTRACE("%s",__PRETTY_FUNCTION__)
// REQ is low // REQ is low
assert(!bus->GetREQ()); assert(!bus.GetREQ());
assert(!bus->GetIO()); assert(!bus.GetIO());
// Length != 0 if received // Length != 0 if received
if (HasValidLength()) { if (HasValidLength()) {
LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length) LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length)
// If not able to receive all, move to status phase // If not able to receive all, move to status phase
if (int len = bus->ReceiveHandShake(GetBuffer().data() + ctrl.offset, ctrl.length); if (int len = bus.ReceiveHandShake(GetBuffer().data() + GetOffset(), ctrl.length);
len != (int)ctrl.length) { len != (int)ctrl.length) {
LOGERROR("%s Not able to receive %d bytes of data, only received %d",__PRETTY_FUNCTION__, ctrl.length, len) LOGERROR("%s Not able to receive %d bytes of data, only received %d",__PRETTY_FUNCTION__, ctrl.length, len)
Error(sense_key::ABORTED_COMMAND); Error(sense_key::ABORTED_COMMAND);
@ -625,6 +625,7 @@ void ScsiController::Receive()
} }
// If result FALSE, move to status phase // If result FALSE, move to status phase
// TODO Check whether we can raise scsi_exception here in order to simplify the error handling
if (!result) { if (!result) {
Error(sense_key::ABORTED_COMMAND); Error(sense_key::ABORTED_COMMAND);
return; return;
@ -648,7 +649,8 @@ void ScsiController::Receive()
break; break;
case BUS::phase_t::dataout: case BUS::phase_t::dataout:
FlushUnit(); // Block-oriented data have been handled above
DataOutNonBlockOriented();
Status(); Status();
break; break;
@ -675,14 +677,14 @@ bool ScsiController::XferMsg(int msg)
void ScsiController::ReceiveBytes() void ScsiController::ReceiveBytes()
{ {
assert(!bus->GetREQ()); assert(!bus.GetREQ());
assert(!bus->GetIO()); assert(!bus.GetIO());
if (HasValidLength()) { if (HasValidLength()) {
LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length) LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length)
// If not able to receive all, move to status phase // If not able to receive all, move to status phase
if (uint32_t len = bus->ReceiveHandShake(GetBuffer().data() + ctrl.offset, ctrl.length); if (uint32_t len = bus.ReceiveHandShake(GetBuffer().data() + GetOffset(), ctrl.length);
len != ctrl.length) { len != ctrl.length) {
LOGERROR("%s Not able to receive %d bytes of data, only received %d", LOGERROR("%s Not able to receive %d bytes of data, only received %d",
__PRETTY_FUNCTION__, ctrl.length, len) __PRETTY_FUNCTION__, ctrl.length, len)
@ -769,17 +771,12 @@ bool ScsiController::XferOut(bool cont)
return false; return false;
} }
void ScsiController::FlushUnit() void ScsiController::DataOutNonBlockOriented()
{ {
assert(IsDataOut()); assert(IsDataOut());
auto disk = dynamic_cast<Disk *>(GetDeviceForLun(GetEffectiveLun()));
if (disk == nullptr) {
return;
}
// WRITE system only
switch (GetOpcode()) { switch (GetOpcode()) {
// TODO Check why these cases are needed
case scsi_command::eCmdWrite6: case scsi_command::eCmdWrite6:
case scsi_command::eCmdWrite10: case scsi_command::eCmdWrite10:
case scsi_command::eCmdWrite16: case scsi_command::eCmdWrite16:
@ -790,26 +787,24 @@ void ScsiController::FlushUnit()
break; break;
case scsi_command::eCmdModeSelect6: case scsi_command::eCmdModeSelect6:
case scsi_command::eCmdModeSelect10: case scsi_command::eCmdModeSelect10: {
// TODO What is this special handling of ModeSelect good for? // TODO Try to get rid of this cast
// Without it we would not need this method at all. if (auto device = dynamic_pointer_cast<ModePageDevice>(GetDeviceForLun(GetEffectiveLun()));
// ModeSelect is already handled in XferOutBlockOriented(). Why would it have to be handled once more? device != nullptr) {
try { device->ModeSelect(ctrl.cmd, GetBuffer(), GetOffset());
disk->ModeSelect(ctrl.cmd, GetBuffer(), ctrl.offset); }
else {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
}
} }
catch(const scsi_error_exception& e) { break;
LOGWARN("Error occured while processing Mode Select command %02X\n", (int)GetOpcode())
Error(e.get_sense_key(), e.get_asc(), e.get_status());
return;
}
break;
case scsi_command::eCmdSetMcastAddr: case scsi_command::eCmdSetMcastAddr:
// TODO: Eventually, we should store off the multicast address configuration data here... // TODO: Eventually, we should store off the multicast address configuration data here...
break; break;
default: default:
LOGWARN("Received an unexpected flush command $%02X\n", (int)GetOpcode()) LOGWARN("Unexpected Data Out phase for command $%02X", (int)GetOpcode())
break; break;
} }
} }
@ -838,9 +833,9 @@ bool ScsiController::XferIn(vector<BYTE>& buf)
case scsi_command::eCmdRead16: case scsi_command::eCmdRead16:
// Read from disk // Read from disk
try { try {
ctrl.length = (static_cast<Disk *>(GetDeviceForLun(lun)))->Read(ctrl.cmd, buf, ctrl.next); ctrl.length = (dynamic_pointer_cast<Disk>(GetDeviceForLun(lun)))->Read(ctrl.cmd, buf, ctrl.next);
} }
catch(const scsi_error_exception&) { catch(const scsi_exception&) {
// If there is an error, go to the status phase // If there is an error, go to the status phase
return false; return false;
} }
@ -868,7 +863,7 @@ bool ScsiController::XferIn(vector<BYTE>& buf)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool ScsiController::XferOutBlockOriented(bool cont) bool ScsiController::XferOutBlockOriented(bool cont)
{ {
auto disk = dynamic_cast<Disk *>(GetDeviceForLun(GetEffectiveLun())); auto disk = dynamic_pointer_cast<Disk>(GetDeviceForLun(GetEffectiveLun()));
if (disk == nullptr) { if (disk == nullptr) {
return false; return false;
} }
@ -878,9 +873,9 @@ bool ScsiController::XferOutBlockOriented(bool cont)
case scsi_command::eCmdModeSelect6: case scsi_command::eCmdModeSelect6:
case scsi_command::eCmdModeSelect10: case scsi_command::eCmdModeSelect10:
try { try {
disk->ModeSelect(ctrl.cmd, GetBuffer(), ctrl.offset); disk->ModeSelect(ctrl.cmd, GetBuffer(), GetOffset());
} }
catch(const scsi_error_exception& e) { catch(const scsi_exception& e) {
Error(e.get_sense_key(), e.get_asc(), e.get_status()); Error(e.get_sense_key(), e.get_asc(), e.get_status());
return false; return false;
} }
@ -895,7 +890,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
{ {
// Special case Write function for brige // Special case Write function for brige
// TODO This class must not know about SCSIBR // TODO This class must not know about SCSIBR
if (auto bridge = dynamic_cast<SCSIBR *>(disk); bridge) { if (auto bridge = dynamic_pointer_cast<SCSIBR>(disk); bridge) {
if (!bridge->WriteBytes(ctrl.cmd, GetBuffer(), ctrl.length)) { if (!bridge->WriteBytes(ctrl.cmd, GetBuffer(), ctrl.length)) {
// Write failed // Write failed
return false; return false;
@ -907,7 +902,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
// Special case Write function for DaynaPort // Special case Write function for DaynaPort
// TODO This class must not know about DaynaPort // TODO This class must not know about DaynaPort
if (auto daynaport = dynamic_cast<SCSIDaynaPort *>(disk); daynaport) { if (auto daynaport = dynamic_pointer_cast<SCSIDaynaPort>(disk); daynaport) {
daynaport->WriteBytes(ctrl.cmd, GetBuffer(), 0); daynaport->WriteBytes(ctrl.cmd, GetBuffer(), 0);
ResetOffset(); ResetOffset();
@ -918,7 +913,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
try { try {
disk->Write(ctrl.cmd, GetBuffer(), ctrl.next - 1); disk->Write(ctrl.cmd, GetBuffer(), ctrl.next - 1);
} }
catch(const scsi_error_exception& e) { catch(const scsi_exception& e) {
Error(e.get_sense_key(), e.get_asc(), e.get_status()); Error(e.get_sense_key(), e.get_asc(), e.get_status());
// Write failed // Write failed
@ -935,7 +930,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
try { try {
ctrl.length = disk->WriteCheck(ctrl.next - 1); ctrl.length = disk->WriteCheck(ctrl.next - 1);
} }
catch(const scsi_error_exception&) { catch(const scsi_exception&) {
// Cannot write // Cannot write
return false; return false;
} }
@ -961,9 +956,10 @@ void ScsiController::ProcessCommand()
uint32_t len = GPIOBUS::GetCommandByteCount(GetBuffer()[0]); uint32_t len = GPIOBUS::GetCommandByteCount(GetBuffer()[0]);
stringstream s; stringstream s;
s << setfill('0') << setw(2) << hex;
for (uint32_t i = 0; i < len; i++) { for (uint32_t i = 0; i < len; i++) {
ctrl.cmd[i] = GetBuffer()[i]; ctrl.cmd[i] = GetBuffer()[i];
s << setfill('0') << setw(2) << hex << ctrl.cmd[i]; s << ctrl.cmd[i];
} }
LOGTRACE("%s CDB=$%s",__PRETTY_FUNCTION__, s.str().c_str()) LOGTRACE("%s CDB=$%s",__PRETTY_FUNCTION__, s.str().c_str())
@ -974,7 +970,7 @@ void ScsiController::ParseMessage()
{ {
int i = 0; int i = 0;
while (i < scsi.msc) { while (i < scsi.msc) {
BYTE message_type = scsi.msb[i]; const BYTE message_type = scsi.msb[i];
if (message_type == 0x06) { if (message_type == 0x06) {
LOGTRACE("Received ABORT message") LOGTRACE("Received ABORT message")
@ -1036,7 +1032,7 @@ void ScsiController::ParseMessage()
void ScsiController::ProcessMessage() void ScsiController::ProcessMessage()
{ {
// Continue message out phase as long as ATN keeps asserting // Continue message out phase as long as ATN keeps asserting
if (bus->GetATN()) { if (bus.GetATN()) {
// Data transfer is 1 byte x 1 block // Data transfer is 1 byte x 1 block
ResetOffset(); ResetOffset();
ctrl.length = 1; ctrl.length = 1;
@ -1062,8 +1058,8 @@ int ScsiController::GetEffectiveLun() const
void ScsiController::Sleep() void ScsiController::Sleep()
{ {
if (uint32_t time = SysTimer::instance().GetTimerLow() - execstart; time < MIN_EXEC_TIME) { if (const uint32_t time = SysTimer::GetTimerLow() - execstart; time < MIN_EXEC_TIME) {
SysTimer::instance().SleepUsec(MIN_EXEC_TIME - time); SysTimer::SleepUsec(MIN_EXEC_TIME - time);
} }
execstart = 0; execstart = 0;
} }

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -54,10 +54,8 @@ public:
// Maximum number of logical units // Maximum number of logical units
static const int LUN_MAX = 32; static const int LUN_MAX = 32;
ScsiController(shared_ptr<BUS>, int); ScsiController(BUS&, int);
~ScsiController() override = default; ~ScsiController() override = default;
ScsiController(ScsiController&) = delete;
ScsiController& operator=(const ScsiController&) = delete;
void Reset() override; void Reset() override;
@ -105,7 +103,7 @@ private:
void ReceiveBytes(); void ReceiveBytes();
void Execute(); void Execute();
void FlushUnit(); void DataOutNonBlockOriented();
void Receive(); void Receive();
void ProcessCommand(); void ProcessCommand();

View File

@ -7,12 +7,13 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "cd_track.h" #include "cd_track.h"
#include <cassert>
void CDTrack::Init(int track, uint32_t first, uint32_t last) void CDTrack::Init(int track, uint32_t first, uint32_t last)
{ {

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -22,10 +22,8 @@ public:
CDTrack() = default; CDTrack() = default;
~CDTrack() = default; ~CDTrack() = default;
CDTrack(CDTrack&) = delete;
CDTrack& operator=(const CDTrack&) = delete;
void Init(int track, DWORD first, DWORD last); void Init(int track, uint32_t first, uint32_t last);
// Properties // Properties
void SetPath(bool cdda, const Filepath& path); // Set the path void SetPath(bool cdda, const Filepath& path); // Set the path
@ -34,7 +32,7 @@ public:
uint32_t GetLast() const; // Get the last LBA uint32_t GetLast() const; // Get the last LBA
uint32_t GetBlocks() const; // Get the number of blocks uint32_t GetBlocks() const; // Get the number of blocks
int GetTrackNo() const; // Get the track number int GetTrackNo() const; // Get the track number
bool IsValid(DWORD lba) const; // Is this a valid LBA? bool IsValid(uint32_t lba) const; // Is this a valid LBA?
bool IsAudio() const; // Is this an audio track? bool IsAudio() const; // Is this an audio track?
private: private:

View File

@ -48,7 +48,7 @@ static void convert(char const *src, char const *dest,
return; return;
} }
if (size_t ret = iconv(cd, &inbuf, &in, &outbuf, &out); ret == (size_t)-1) { if (const size_t ret = iconv(cd, &inbuf, &in, &outbuf, &out); ret == (size_t)-1) {
return; return;
} }
@ -166,7 +166,7 @@ void Human68k::namests_t::GetCopyFilename(BYTE* szFilename) const
if (i >= 8) { if (i >= 8) {
// Transfer the extraneous part // Transfer the extraneous part
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
BYTE c = add[i]; const BYTE c = add[i];
if (c == '\0') if (c == '\0')
break; break;
*p++ = c; *p++ = c;
@ -178,7 +178,7 @@ void Human68k::namests_t::GetCopyFilename(BYTE* szFilename) const
if (ext[0] != ' ' || ext[1] != ' ' || ext[2] != ' ') { if (ext[0] != ' ' || ext[1] != ' ' || ext[2] != ' ') {
*p++ = '.'; *p++ = '.';
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
BYTE c = ext[i]; const BYTE c = ext[i];
if (c == ' ') { if (c == ' ') {
// Check that the file extension continues after a space is detected // Check that the file extension continues after a space is detected
/// TODO: Should change this function to be compatible with 8+3 chars and TwentyOne /// TODO: Should change this function to be compatible with 8+3 chars and TwentyOne
@ -226,7 +226,7 @@ CHostDrv::~CHostDrv()
// Initialization (device boot and load) // Initialization (device boot and load)
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag) void CHostDrv::Init(const TCHAR* szBase, uint32_t nFlag)
{ {
assert(szBase); assert(szBase);
assert(strlen(szBase) < FILEPATH_MAX); assert(strlen(szBase) < FILEPATH_MAX);
@ -253,7 +253,7 @@ void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag)
TCHAR* pClear = nullptr; TCHAR* pClear = nullptr;
TCHAR* p = m_szBase; TCHAR* p = m_szBase;
for (;;) { for (;;) {
TCHAR c = *p; const TCHAR c = *p;
if (c == '\0') if (c == '\0')
break; break;
if (c == '/' || c == '\\') { if (c == '/' || c == '\\') {
@ -301,7 +301,7 @@ BYTE CHostDrv::GetMediaByte() const
// Get drive status // Get drive status
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
DWORD CHostDrv::GetStatus() const uint32_t CHostDrv::GetStatus() const
{ {
return 0x40 | (m_bEnable ? (m_bWriteProtect ? 0x08 : 0) | 0x02 : 0); return 0x40 | (m_bEnable ? (m_bWriteProtect ? 0x08 : 0) | 0x02 : 0);
} }
@ -408,15 +408,15 @@ bool CHostDrv::GetVolumeCache(TCHAR* szLabel) const
return m_bVolumeCache; return m_bVolumeCache;
} }
DWORD CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity) uint32_t CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity)
{ {
assert(pCapacity); assert(pCapacity);
assert(m_bEnable); assert(m_bEnable);
DWORD nFree = 0x7FFF8000; const uint32_t nFree = 0x7FFF8000;
DWORD freearea; uint32_t freearea;
DWORD clusters; uint32_t clusters;
DWORD sectors; uint32_t sectors;
freearea = 0xFFFF; freearea = 0xFFFF;
clusters = 0xFFFF; clusters = 0xFFFF;
@ -428,9 +428,9 @@ DWORD CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity)
assert(sectors <= 64); assert(sectors <= 64);
// Update cache // Update cache
m_capCache.freearea = (WORD)freearea; m_capCache.freearea = (uint16_t)freearea;
m_capCache.clusters = (WORD)clusters; m_capCache.clusters = (uint16_t)clusters;
m_capCache.sectors = (WORD)sectors; m_capCache.sectors = (uint16_t)sectors;
m_capCache.bytes = 512; m_capCache.bytes = 512;
// Transfer contents // Transfer contents
@ -808,7 +808,7 @@ void CHostFilename::ConvertHuman(int nCount)
} }
size_t nMax = 18; // Number of bytes for the base segment (base name and extension) size_t nMax = 18; // Number of bytes for the base segment (base name and extension)
DWORD nOption = CFileSys::GetFileOption(); uint32_t nOption = CFileSys::GetFileOption();
if (nOption & WINDRV_OPT_CONVERT_LENGTH) if (nOption & WINDRV_OPT_CONVERT_LENGTH)
nMax = 8; nMax = 8;
@ -817,7 +817,7 @@ void CHostFilename::ConvertHuman(int nCount)
BYTE* pNumber = nullptr; BYTE* pNumber = nullptr;
if (nCount >= 0) { if (nCount >= 0) {
pNumber = &szNumber[8]; pNumber = &szNumber[8];
for (DWORD i = 0; i < 5; i++) { // Max 5+1 digits (always leave the first 2 bytes of the base name) for (uint32_t i = 0; i < 5; i++) { // Max 5+1 digits (always leave the first 2 bytes of the base name)
int n = nCount % 36; int n = nCount % 36;
nMax--; nMax--;
pNumber--; pNumber--;
@ -1082,7 +1082,7 @@ bool CHostFilename::isReduce() const
/// Evaluate Human68k directory entry attribute /// Evaluate Human68k directory entry attribute
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CHostFilename::CheckAttribute(DWORD nHumanAttribute) const int CHostFilename::CheckAttribute(uint32_t nHumanAttribute) const
{ {
BYTE nAttribute = m_dirHuman.attr; BYTE nAttribute = m_dirHuman.attr;
if ((nAttribute & (Human68k::AT_ARCHIVE | Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) == 0) if ((nAttribute & (Human68k::AT_ARCHIVE | Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) == 0)
@ -1099,7 +1099,7 @@ int CHostFilename::CheckAttribute(DWORD nHumanAttribute) const
const BYTE* CHostFilename::SeparateExt(const BYTE* szHuman) // static const BYTE* CHostFilename::SeparateExt(const BYTE* szHuman) // static
{ {
// Obtain the file name length // Obtain the file name length
size_t nLength = strlen((const char*)szHuman); const size_t nLength = strlen((const char*)szHuman);
const BYTE* pFirst = szHuman; const BYTE* pFirst = szHuman;
const BYTE* pLast = pFirst + nLength; const BYTE* pLast = pFirst + nLength;
@ -1124,7 +1124,7 @@ const BYTE* CHostFilename::SeparateExt(const BYTE* szHuman) // static
// //
//=========================================================================== //===========================================================================
DWORD CHostPath::g_nId; ///< Identifier creation counter uint32_t CHostPath::g_nId; ///< Identifier creation counter
CHostPath::~CHostPath() CHostPath::~CHostPath()
{ {
@ -1144,7 +1144,7 @@ CHostPath::ring_t* CHostPath::Alloc(size_t nLength) // static
{ {
assert(nLength < FILEPATH_MAX); assert(nLength < FILEPATH_MAX);
size_t n = offsetof(ring_t, f) + CHostFilename::Offset() + (nLength + 1) * sizeof(TCHAR); const size_t n = offsetof(ring_t, f) + CHostFilename::Offset() + (nLength + 1) * sizeof(TCHAR);
auto p = (ring_t*)malloc(n); auto p = (ring_t*)malloc(n);
assert(p); assert(p);
@ -1295,8 +1295,8 @@ bool CHostPath::isSameHuman(const BYTE* szHuman) const
assert(szHuman); assert(szHuman);
// Calulate number of chars // Calulate number of chars
size_t nLength = strlen((const char*)m_szHuman); const size_t nLength = strlen((const char*)m_szHuman);
size_t n = strlen((const char*)szHuman); const size_t n = strlen((const char*)szHuman);
// Check number of chars // Check number of chars
if (nLength != n) if (nLength != n)
@ -1331,7 +1331,7 @@ bool CHostPath::isSameChild(const BYTE* szHuman) const
/// Make sure to lock from the top. /// Make sure to lock from the top.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
const CHostFilename* CHostPath::FindFilename(const BYTE* szHuman, DWORD nHumanAttribute) const const CHostFilename* CHostPath::FindFilename(const BYTE* szHuman, uint32_t nHumanAttribute) const
{ {
assert(szHuman); assert(szHuman);
@ -1368,7 +1368,7 @@ const CHostFilename* CHostPath::FindFilename(const BYTE* szHuman, DWORD nHumanAt
/// Make sure to lock from the top. /// Make sure to lock from the top.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD nHumanAttribute, find_t* pFind) const const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, uint32_t nHumanAttribute, find_t* pFind) const
{ {
assert(szHuman); assert(szHuman);
assert(pFind); assert(pFind);
@ -1386,7 +1386,7 @@ const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD
p = pFind->pos; p = pFind->pos;
} else { } else {
// Find the start position in the directory entry contents // Find the start position in the directory entry contents
DWORD n = 0; uint32_t n = 0;
for (;; p = (const ring_t*)p->r.Next()) { for (;; p = (const ring_t*)p->r.Next()) {
if (p == (const ring_t*)&m_cRing) { if (p == (const ring_t*)&m_cRing) {
// Extrapolate from the count when the same entry isn't found (just in case) // Extrapolate from the count when the same entry isn't found (just in case)
@ -1547,7 +1547,7 @@ void CHostPath::Refresh()
// - No duplicated names in previous entries // - No duplicated names in previous entries
// - No entity with the same name exists // - No entity with the same name exists
if (pFilename->isReduce() || !pFilename->isCorrect()) { // Confirm that file name update is required if (pFilename->isReduce() || !pFilename->isCorrect()) { // Confirm that file name update is required
for (DWORD n = 0; n < XM6_HOST_FILENAME_PATTERN_MAX; n++) { for (uint32_t n = 0; n < XM6_HOST_FILENAME_PATTERN_MAX; n++) {
// Confirm file name validity // Confirm file name validity
if (pFilename->isCorrect()) { if (pFilename->isCorrect()) {
// Confirm match with previous entry // Confirm match with previous entry
@ -1583,14 +1583,14 @@ void CHostPath::Refresh()
nHumanAttribute |= Human68k::AT_READONLY; nHumanAttribute |= Human68k::AT_READONLY;
pFilename->SetEntryAttribute(nHumanAttribute); pFilename->SetEntryAttribute(nHumanAttribute);
auto nHumanSize = (DWORD)sb.st_size; auto nHumanSize = (uint32_t)sb.st_size;
pFilename->SetEntrySize(nHumanSize); pFilename->SetEntrySize(nHumanSize);
WORD nHumanDate = 0; uint16_t nHumanDate = 0;
WORD nHumanTime = 0; uint16_t nHumanTime = 0;
if (tm pt = {}; localtime_r(&sb.st_mtime, &pt) != nullptr) { if (tm pt = {}; localtime_r(&sb.st_mtime, &pt) != nullptr) {
nHumanDate = (WORD)(((pt.tm_year - 80) << 9) | ((pt.tm_mon + 1) << 5) | pt.tm_mday); nHumanDate = (uint16_t)(((pt.tm_year - 80) << 9) | ((pt.tm_mon + 1) << 5) | pt.tm_mday);
nHumanTime = (WORD)((pt.tm_hour << 11) | (pt.tm_min << 5) | (pt.tm_sec >> 1)); nHumanTime = (uint16_t)((pt.tm_hour << 11) | (pt.tm_min << 5) | (pt.tm_sec >> 1));
} }
pFilename->SetEntryDate(nHumanDate); pFilename->SetEntryDate(nHumanDate);
pFilename->SetEntryTime(nHumanTime); pFilename->SetEntryTime(nHumanTime);
@ -1760,7 +1760,7 @@ void CHostEntry::CleanCache() const
/// Update the cache for the specified unit /// Update the cache for the specified unit
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void CHostEntry::CleanCache(DWORD nUnit) const void CHostEntry::CleanCache(uint32_t nUnit) const
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit]); assert(m_pDrv[nUnit]);
@ -1773,7 +1773,7 @@ void CHostEntry::CleanCache(DWORD nUnit) const
/// Update the cache for the specified path /// Update the cache for the specified path
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void CHostEntry::CleanCache(DWORD nUnit, const BYTE* szHumanPath) const void CHostEntry::CleanCache(uint32_t nUnit, const BYTE* szHumanPath) const
{ {
assert(szHumanPath); assert(szHumanPath);
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
@ -1787,7 +1787,7 @@ void CHostEntry::CleanCache(DWORD nUnit, const BYTE* szHumanPath) const
/// Update all cache for the specified path and below /// Update all cache for the specified path and below
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void CHostEntry::CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath) const void CHostEntry::CleanCacheChild(uint32_t nUnit, const BYTE* szHumanPath) const
{ {
assert(szHumanPath); assert(szHumanPath);
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
@ -1801,7 +1801,7 @@ void CHostEntry::CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath) const
/// Delete cache for the specified path /// Delete cache for the specified path
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void CHostEntry::DeleteCache(DWORD nUnit, const BYTE* szHumanPath) const void CHostEntry::DeleteCache(uint32_t nUnit, const BYTE* szHumanPath) const
{ {
assert(szHumanPath); assert(szHumanPath);
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
@ -1815,7 +1815,7 @@ void CHostEntry::DeleteCache(DWORD nUnit, const BYTE* szHumanPath) const
/// Find host side names (path name + file name (can be abbreviated) + attribute) /// Find host side names (path name + file name (can be abbreviated) + attribute)
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CHostEntry::Find(DWORD nUnit, CHostFiles* pFiles) const bool CHostEntry::Find(uint32_t nUnit, CHostFiles* pFiles) const
{ {
assert(pFiles); assert(pFiles);
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
@ -1829,7 +1829,7 @@ bool CHostEntry::Find(DWORD nUnit, CHostFiles* pFiles) const
/// Drive settings /// Drive settings
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void CHostEntry::SetDrv(DWORD nUnit, CHostDrv* pDrv) void CHostEntry::SetDrv(uint32_t nUnit, CHostDrv* pDrv)
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit] == nullptr); assert(m_pDrv[nUnit] == nullptr);
@ -1842,7 +1842,7 @@ void CHostEntry::SetDrv(DWORD nUnit, CHostDrv* pDrv)
/// Is it write-protected? /// Is it write-protected?
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CHostEntry::isWriteProtect(DWORD nUnit) const bool CHostEntry::isWriteProtect(uint32_t nUnit) const
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit]); assert(m_pDrv[nUnit]);
@ -1855,7 +1855,7 @@ bool CHostEntry::isWriteProtect(DWORD nUnit) const
/// Is it accessible? /// Is it accessible?
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CHostEntry::isEnable(DWORD nUnit) const bool CHostEntry::isEnable(uint32_t nUnit) const
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit]); assert(m_pDrv[nUnit]);
@ -1868,7 +1868,7 @@ bool CHostEntry::isEnable(DWORD nUnit) const
/// Media check /// Media check
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CHostEntry::isMediaOffline(DWORD nUnit) const bool CHostEntry::isMediaOffline(uint32_t nUnit) const
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit]); assert(m_pDrv[nUnit]);
@ -1881,7 +1881,7 @@ bool CHostEntry::isMediaOffline(DWORD nUnit) const
/// Get media byte /// Get media byte
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
BYTE CHostEntry::GetMediaByte(DWORD nUnit) const BYTE CHostEntry::GetMediaByte(uint32_t nUnit) const
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit]); assert(m_pDrv[nUnit]);
@ -1894,7 +1894,7 @@ BYTE CHostEntry::GetMediaByte(DWORD nUnit) const
/// Get drive status /// Get drive status
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
DWORD CHostEntry::GetStatus(DWORD nUnit) const uint32_t CHostEntry::GetStatus(uint32_t nUnit) const
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit]); assert(m_pDrv[nUnit]);
@ -1907,7 +1907,7 @@ DWORD CHostEntry::GetStatus(DWORD nUnit) const
/// Media change check /// Media change check
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CHostEntry::CheckMedia(DWORD nUnit) const bool CHostEntry::CheckMedia(uint32_t nUnit) const
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit]); assert(m_pDrv[nUnit]);
@ -1920,7 +1920,7 @@ bool CHostEntry::CheckMedia(DWORD nUnit) const
/// Eject /// Eject
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void CHostEntry::Eject(DWORD nUnit) const void CHostEntry::Eject(uint32_t nUnit) const
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit]); assert(m_pDrv[nUnit]);
@ -1933,7 +1933,7 @@ void CHostEntry::Eject(DWORD nUnit) const
/// Get volume label /// Get volume label
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void CHostEntry::GetVolume(DWORD nUnit, TCHAR* szLabel) const void CHostEntry::GetVolume(uint32_t nUnit, TCHAR* szLabel) const
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit]); assert(m_pDrv[nUnit]);
@ -1946,7 +1946,7 @@ void CHostEntry::GetVolume(DWORD nUnit, TCHAR* szLabel) const
/// Get volume label from cache /// Get volume label from cache
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CHostEntry::GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const bool CHostEntry::GetVolumeCache(uint32_t nUnit, TCHAR* szLabel) const
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit]); assert(m_pDrv[nUnit]);
@ -1959,7 +1959,7 @@ bool CHostEntry::GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const
/// Get capacity /// Get capacity
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
DWORD CHostEntry::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) const uint32_t CHostEntry::GetCapacity(uint32_t nUnit, Human68k::capacity_t* pCapacity) const
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit]); assert(m_pDrv[nUnit]);
@ -1972,7 +1972,7 @@ DWORD CHostEntry::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) cons
/// Get cluster size from cache /// Get cluster size from cache
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CHostEntry::GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) const bool CHostEntry::GetCapacityCache(uint32_t nUnit, Human68k::capacity_t* pCapacity) const
{ {
assert(nUnit < DRIVE_MAX); assert(nUnit < DRIVE_MAX);
assert(m_pDrv[nUnit]); assert(m_pDrv[nUnit]);
@ -2056,7 +2056,7 @@ void CHostFiles::SetPath(const Human68k::namests_t* pNamests)
/// Find file on the Human68k side and create data on the host side /// Find file on the Human68k side and create data on the host side
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CHostFiles::Find(DWORD nUnit, const CHostEntry* pEntry) bool CHostFiles::Find(uint32_t nUnit, const CHostEntry* pEntry)
{ {
assert(pEntry); assert(pEntry);
@ -2159,7 +2159,7 @@ void CHostFilesManager::Init()
assert(m_cRing.Prev() == &m_cRing); assert(m_cRing.Prev() == &m_cRing);
// Allocate memory // Allocate memory
for (DWORD i = 0; i < XM6_HOST_FILES_MAX; i++) { for (uint32_t i = 0; i < XM6_HOST_FILES_MAX; i++) {
auto p = new ring_t(); auto p = new ring_t();
p->r.Insert(&m_cRing); p->r.Insert(&m_cRing);
} }
@ -2180,7 +2180,7 @@ void CHostFilesManager::Clean()
} }
} }
CHostFiles* CHostFilesManager::Alloc(DWORD nKey) CHostFiles* CHostFilesManager::Alloc(uint32_t nKey)
{ {
assert(nKey); assert(nKey);
@ -2195,7 +2195,7 @@ CHostFiles* CHostFilesManager::Alloc(DWORD nKey)
return &p->f; return &p->f;
} }
CHostFiles* CHostFilesManager::Search(DWORD nKey) CHostFiles* CHostFilesManager::Search(uint32_t nKey)
{ {
// assert(nKey); // The search key may become 0 due to DPB damage // assert(nKey); // The search key may become 0 due to DPB damage
@ -2235,7 +2235,7 @@ void CHostFilesManager::Free(CHostFiles* pFiles)
/// Set file open mode /// Set file open mode
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CHostFcb::SetMode(DWORD nHumanMode) bool CHostFcb::SetMode(uint32_t nHumanMode)
{ {
switch (nHumanMode & Human68k::OP_MASK) { switch (nHumanMode & Human68k::OP_MASK) {
case Human68k::OP_READ: case Human68k::OP_READ:
@ -2279,7 +2279,7 @@ void CHostFcb::SetHumanPath(const BYTE* szHumanPath)
/// Return false if error is thrown. /// Return false if error is thrown.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CHostFcb::Create(DWORD, bool bForce) bool CHostFcb::Create(uint32_t, bool bForce)
{ {
assert((Human68k::AT_DIRECTORY | Human68k::AT_VOLUME) == 0); assert((Human68k::AT_DIRECTORY | Human68k::AT_VOLUME) == 0);
assert(strlen(m_szFilename) > 0); assert(strlen(m_szFilename) > 0);
@ -2328,7 +2328,7 @@ bool CHostFcb::Open()
/// Return -1 if error is thrown. /// Return -1 if error is thrown.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
DWORD CHostFcb::Read(BYTE* pBuffer, DWORD nSize) uint32_t CHostFcb::Read(BYTE* pBuffer, uint32_t nSize)
{ {
assert(pBuffer); assert(pBuffer);
assert(m_pFile); assert(m_pFile);
@ -2337,7 +2337,7 @@ DWORD CHostFcb::Read(BYTE* pBuffer, DWORD nSize)
if (ferror(m_pFile)) if (ferror(m_pFile))
nResult = (size_t)-1; nResult = (size_t)-1;
return (DWORD)nResult; return (uint32_t)nResult;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -2348,7 +2348,7 @@ DWORD CHostFcb::Read(BYTE* pBuffer, DWORD nSize)
/// Return -1 if error is thrown. /// Return -1 if error is thrown.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
DWORD CHostFcb::Write(const BYTE* pBuffer, DWORD nSize) uint32_t CHostFcb::Write(const BYTE* pBuffer, uint32_t nSize)
{ {
assert(pBuffer); assert(pBuffer);
assert(m_pFile); assert(m_pFile);
@ -2357,7 +2357,7 @@ DWORD CHostFcb::Write(const BYTE* pBuffer, DWORD nSize)
if (ferror(m_pFile)) if (ferror(m_pFile))
nResult = (size_t)-1; nResult = (size_t)-1;
return (DWORD)nResult; return (uint32_t)nResult;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -2381,18 +2381,16 @@ bool CHostFcb::Truncate() const
/// Return -1 if error is thrown. /// Return -1 if error is thrown.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
DWORD CHostFcb::Seek(DWORD nOffset, DWORD nHumanSeek) uint32_t CHostFcb::Seek(uint32_t nOffset, Human68k::seek_t nHumanSeek)
{ {
assert(nHumanSeek == Human68k::SK_BEGIN ||
nHumanSeek == Human68k::SK_CURRENT || nHumanSeek == Human68k::SK_END);
assert(m_pFile); assert(m_pFile);
int nSeek; int nSeek;
switch (nHumanSeek) { switch (nHumanSeek) {
case Human68k::SK_BEGIN: case Human68k::seek_t::SK_BEGIN:
nSeek = SEEK_SET; nSeek = SEEK_SET;
break; break;
case Human68k::SK_CURRENT: case Human68k::seek_t::SK_CURRENT:
nSeek = SEEK_CUR; nSeek = SEEK_CUR;
break; break;
// case SK_END: // case SK_END:
@ -2401,9 +2399,9 @@ DWORD CHostFcb::Seek(DWORD nOffset, DWORD nHumanSeek)
break; break;
} }
if (fseek(m_pFile, nOffset, nSeek)) if (fseek(m_pFile, nOffset, nSeek))
return (DWORD)-1; return (uint32_t)-1;
return (DWORD)ftell(m_pFile); return (uint32_t)ftell(m_pFile);
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -2413,7 +2411,7 @@ DWORD CHostFcb::Seek(DWORD nOffset, DWORD nHumanSeek)
/// Return false if error is thrown. /// Return false if error is thrown.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CHostFcb::TimeStamp(DWORD nHumanTime) const bool CHostFcb::TimeStamp(uint32_t nHumanTime) const
{ {
assert(m_pFile || m_bFlag); assert(m_pFile || m_bFlag);
@ -2482,7 +2480,7 @@ void CHostFcbManager::Init()
assert(m_cRing.Prev() == &m_cRing); assert(m_cRing.Prev() == &m_cRing);
// Memory allocation // Memory allocation
for (DWORD i = 0; i < XM6_HOST_FCB_MAX; i++) { for (uint32_t i = 0; i < XM6_HOST_FCB_MAX; i++) {
auto p = new ring_t; auto p = new ring_t;
assert(p); assert(p);
p->r.Insert(&m_cRing); p->r.Insert(&m_cRing);
@ -2503,7 +2501,7 @@ void CHostFcbManager::Clean() const
} }
} }
CHostFcb* CHostFcbManager::Alloc(DWORD nKey) CHostFcb* CHostFcbManager::Alloc(uint32_t nKey)
{ {
assert(nKey); assert(nKey);
@ -2525,7 +2523,7 @@ CHostFcb* CHostFcbManager::Alloc(DWORD nKey)
return &p->f; return &p->f;
} }
CHostFcb* CHostFcbManager::Search(DWORD nKey) CHostFcb* CHostFcbManager::Search(uint32_t nKey)
{ {
assert(nKey); assert(nKey);
@ -2562,7 +2560,7 @@ void CHostFcbManager::Free(CHostFcb* pFcb)
// //
//=========================================================================== //===========================================================================
DWORD CFileSys::g_nOption; // File name conversion flag uint32_t CFileSys::g_nOption; // File name conversion flag
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
@ -2609,7 +2607,7 @@ void CFileSys::Init()
m_cEntry.Init(); m_cEntry.Init();
// Evaluate per-path setting validity // Evaluate per-path setting validity
DWORD nDrives = m_nDrives; uint32_t nDrives = m_nDrives;
if (nDrives == 0) { if (nDrives == 0) {
// Use root directory instead of per-path settings // Use root directory instead of per-path settings
strcpy(m_szBase[0], "/"); strcpy(m_szBase[0], "/");
@ -2618,8 +2616,8 @@ void CFileSys::Init()
} }
// Register file system // Register file system
DWORD nUnit = 0; uint32_t nUnit = 0;
for (DWORD n = 0; n < nDrives; n++) { for (uint32_t n = 0; n < nDrives; n++) {
// Don't register is base path do not exist // Don't register is base path do not exist
if (m_szBase[n][0] == '\0') if (m_szBase[n][0] == '\0')
continue; continue;
@ -2644,7 +2642,7 @@ void CFileSys::Init()
/// $40 - Device startup /// $40 - Device startup
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
DWORD CFileSys::InitDevice(const Human68k::argument_t* pArgument) uint32_t CFileSys::InitDevice(const Human68k::argument_t* pArgument)
{ {
InitOption(pArgument); InitOption(pArgument);
@ -2659,7 +2657,7 @@ DWORD CFileSys::InitDevice(const Human68k::argument_t* pArgument)
/// $41 - Directory check /// $41 - Directory check
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests) const int CFileSys::CheckDir(uint32_t nUnit, const Human68k::namests_t* pNamests) const
{ {
assert(pNamests); assert(pNamests);
@ -2686,7 +2684,7 @@ int CFileSys::CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests) const
/// $42 - Create directory /// $42 - Create directory
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) const int CFileSys::MakeDir(uint32_t nUnit, const Human68k::namests_t* pNamests) const
{ {
assert(pNamests); assert(pNamests);
@ -2728,7 +2726,7 @@ int CFileSys::MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) const
/// $43 - Delete directory /// $43 - Delete directory
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) const int CFileSys::RemoveDir(uint32_t nUnit, const Human68k::namests_t* pNamests) const
{ {
assert(pNamests); assert(pNamests);
@ -2778,7 +2776,7 @@ int CFileSys::RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) const
/// $44 - Change file name /// $44 - Change file name
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew) const int CFileSys::Rename(uint32_t nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew) const
{ {
assert(pNamests); assert(pNamests);
@ -2836,7 +2834,7 @@ int CFileSys::Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Hum
/// $45 - Delete file /// $45 - Delete file
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Delete(DWORD nUnit, const Human68k::namests_t* pNamests) const int CFileSys::Delete(uint32_t nUnit, const Human68k::namests_t* pNamests) const
{ {
assert(pNamests); assert(pNamests);
@ -2876,7 +2874,7 @@ int CFileSys::Delete(DWORD nUnit, const Human68k::namests_t* pNamests) const
/// $46 - Get/set file attribute /// $46 - Get/set file attribute
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD nHumanAttribute) const int CFileSys::Attribute(uint32_t nUnit, const Human68k::namests_t* pNamests, uint32_t nHumanAttribute) const
{ {
assert(pNamests); assert(pNamests);
@ -2910,7 +2908,7 @@ int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD
return FS_FATAL_WRITEPROTECT; return FS_FATAL_WRITEPROTECT;
// Generate attribute // Generate attribute
if (DWORD nAttribute = (nHumanAttribute & Human68k::AT_READONLY) | (f.GetAttribute() & ~Human68k::AT_READONLY); if (uint32_t nAttribute = (nHumanAttribute & Human68k::AT_READONLY) | (f.GetAttribute() & ~Human68k::AT_READONLY);
f.GetAttribute() != nAttribute) { f.GetAttribute() != nAttribute) {
struct stat sb; struct stat sb;
if (stat(S2U(f.GetPath()), &sb)) if (stat(S2U(f.GetPath()), &sb))
@ -2941,7 +2939,7 @@ int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD
/// $47 - File search /// $47 - File search
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles) int CFileSys::Files(uint32_t nUnit, uint32_t nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles)
{ {
assert(pNamests); assert(pNamests);
assert(nKey); assert(nKey);
@ -3027,7 +3025,7 @@ int CFileSys::Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests
/// $48 - Search next file /// $48 - Search next file
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles) int CFileSys::NFiles(uint32_t nUnit, uint32_t nKey, Human68k::files_t* pFiles)
{ {
assert(nKey); assert(nKey);
assert(pFiles); assert(pFiles);
@ -3071,7 +3069,7 @@ int CFileSys::NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles)
/// $49 - Create new file /// $49 - Create new file
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, DWORD nHumanAttribute, bool bForce) int CFileSys::Create(uint32_t nUnit, uint32_t nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, uint32_t nHumanAttribute, bool bForce)
{ {
assert(pNamests); assert(pNamests);
assert(nKey); assert(nKey);
@ -3116,7 +3114,7 @@ int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamest
pHostFcb->SetHumanPath(f.GetHumanPath()); pHostFcb->SetHumanPath(f.GetHumanPath());
// Set open mode // Set open mode
pFcb->mode = (WORD)((pFcb->mode & ~Human68k::OP_MASK) | Human68k::OP_FULL); pFcb->mode = (uint16_t)((pFcb->mode & ~Human68k::OP_MASK) | Human68k::OP_FULL);
if (!pHostFcb->SetMode(pFcb->mode)) { if (!pHostFcb->SetMode(pFcb->mode)) {
m_cFcb.Free(pHostFcb); m_cFcb.Free(pHostFcb);
return FS_ILLEGALMOD; return FS_ILLEGALMOD;
@ -3139,7 +3137,7 @@ int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamest
/// $4A - File open /// $4A - File open
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb) int CFileSys::Open(uint32_t nUnit, uint32_t nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb)
{ {
assert(pNamests); assert(pNamests);
assert(nKey); assert(nKey);
@ -3210,7 +3208,7 @@ int CFileSys::Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests,
/// $4B - File close /// $4B - File close
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Close(DWORD nUnit, DWORD nKey, const Human68k::fcb_t* /* pFcb */) int CFileSys::Close(uint32_t nUnit, uint32_t nKey, const Human68k::fcb_t* /* pFcb */)
{ {
assert(nKey); assert(nKey);
@ -3248,7 +3246,7 @@ int CFileSys::Close(DWORD nUnit, DWORD nKey, const Human68k::fcb_t* /* pFcb */)
/// Clean exit when 0 bytes are read. /// Clean exit when 0 bytes are read.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pBuffer, DWORD nSize) int CFileSys::Read(uint32_t nKey, Human68k::fcb_t* pFcb, BYTE* pBuffer, uint32_t nSize)
{ {
assert(nKey); assert(nKey);
assert(pFcb); assert(pFcb);
@ -3267,9 +3265,8 @@ int CFileSys::Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pBuffer, DWORD nSize
} }
// Read // Read
DWORD nResult; const uint32_t nResult = pHostFcb->Read(pBuffer, nSize);
nResult = pHostFcb->Read(pBuffer, nSize); if (nResult == (uint32_t)-1) {
if (nResult == (DWORD)-1) {
m_cFcb.Free(pHostFcb); m_cFcb.Free(pHostFcb);
return FS_INVALIDFUNC; // TODO: Should return error code 10 (read error) as well here return FS_INVALIDFUNC; // TODO: Should return error code 10 (read error) as well here
} }
@ -3288,7 +3285,7 @@ int CFileSys::Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pBuffer, DWORD nSize
/// Truncate file if 0 bytes are written. /// Truncate file if 0 bytes are written.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWORD nSize) int CFileSys::Write(uint32_t nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, uint32_t nSize)
{ {
assert(nKey); assert(nKey);
assert(pFcb); assert(pFcb);
@ -3300,7 +3297,7 @@ int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWOR
if (pHostFcb == nullptr) if (pHostFcb == nullptr)
return FS_NOTOPENED; return FS_NOTOPENED;
DWORD nResult; uint32_t nResult;
if (nSize == 0) { if (nSize == 0) {
// Truncate // Truncate
if (!pHostFcb->Truncate()) { if (!pHostFcb->Truncate()) {
@ -3321,7 +3318,7 @@ int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWOR
// Write // Write
nResult = pHostFcb->Write(pBuffer, nSize); nResult = pHostFcb->Write(pBuffer, nSize);
if (nResult == (DWORD)-1) { if (nResult == (uint32_t)-1) {
m_cFcb.Free(pHostFcb); m_cFcb.Free(pHostFcb);
return FS_CANTWRITE; /// TODO: Should return error code 11 (write error) as well here return FS_CANTWRITE; /// TODO: Should return error code 11 (write error) as well here
} }
@ -3346,7 +3343,7 @@ int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWOR
/// $4E - File seek /// $4E - File seek
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset) int CFileSys::Seek(uint32_t nKey, Human68k::fcb_t* pFcb, uint32_t nSeek, int nOffset)
{ {
assert(pFcb); assert(pFcb);
@ -3356,14 +3353,14 @@ int CFileSys::Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset)
return FS_NOTOPENED; return FS_NOTOPENED;
// Parameter check // Parameter check
if (nSeek > Human68k::SK_END) { if (nSeek > (uint32_t)Human68k::seek_t::SK_END) {
m_cFcb.Free(pHostFcb); m_cFcb.Free(pHostFcb);
return FS_INVALIDPRM; return FS_INVALIDPRM;
} }
// File seek // File seek
DWORD nResult = pHostFcb->Seek(nOffset, nSeek); uint32_t nResult = pHostFcb->Seek(nOffset, (Human68k::seek_t)nSeek);
if (nResult == (DWORD)-1) { if (nResult == (uint32_t)-1) {
m_cFcb.Free(pHostFcb); m_cFcb.Free(pHostFcb);
return FS_CANTSEEK; return FS_CANTSEEK;
} }
@ -3381,14 +3378,14 @@ int CFileSys::Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset)
/// Throw error when the top 16 bits are $FFFF. /// Throw error when the top 16 bits are $FFFF.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD nHumanTime) uint32_t CFileSys::TimeStamp(uint32_t nUnit, uint32_t nKey, Human68k::fcb_t* pFcb, uint32_t nHumanTime)
{ {
assert(nKey); assert(nKey);
assert(pFcb); assert(pFcb);
// Get only // Get only
if (nHumanTime == 0) if (nHumanTime == 0)
return ((DWORD)pFcb->date << 16) | pFcb->time; return ((uint32_t)pFcb->date << 16) | pFcb->time;
// Unit check // Unit check
if (nUnit >= CHostEntry::DRIVE_MAX) if (nUnit >= CHostEntry::DRIVE_MAX)
@ -3415,8 +3412,8 @@ DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD
m_cFcb.Free(pHostFcb); m_cFcb.Free(pHostFcb);
return FS_INVALIDPRM; return FS_INVALIDPRM;
} }
pFcb->date = (WORD)(nHumanTime >> 16); pFcb->date = (uint16_t)(nHumanTime >> 16);
pFcb->time = (WORD)nHumanTime; pFcb->time = (uint16_t)nHumanTime;
// Update cache // Update cache
m_cEntry.CleanCache(nUnit, pHostFcb->GetHumanPath()); m_cEntry.CleanCache(nUnit, pHostFcb->GetHumanPath());
@ -3429,7 +3426,7 @@ DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD
/// $50 - Get capacity /// $50 - Get capacity
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) const int CFileSys::GetCapacity(uint32_t nUnit, Human68k::capacity_t* pCapacity) const
{ {
assert(pCapacity); assert(pCapacity);
@ -3453,7 +3450,7 @@ int CFileSys::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) const
/// $51 - Inspect/control drive status /// $51 - Inspect/control drive status
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive) const int CFileSys::CtrlDrive(uint32_t nUnit, Human68k::ctrldrive_t* pCtrlDrive) const
{ {
assert(pCtrlDrive); assert(pCtrlDrive);
@ -3496,7 +3493,7 @@ int CFileSys::CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive) const
/// Therefore, treat even a unit out of bounds as normal operation. /// Therefore, treat even a unit out of bounds as normal operation.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) const int CFileSys::GetDPB(uint32_t nUnit, Human68k::dpb_t* pDpb) const
{ {
assert(pDpb); assert(pDpb);
@ -3530,8 +3527,8 @@ int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) const
} }
// Calculate number of shifts // Calculate number of shifts
DWORD nSize = 1; uint32_t nSize = 1;
DWORD nShift = 0; uint32_t nShift = 0;
for (;;) { for (;;) {
if (nSize >= cap.sectors) if (nSize >= cap.sectors)
break; break;
@ -3546,23 +3543,23 @@ int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) const
// Cluster 1: FAT // Cluster 1: FAT
// Cluster 2: Root directory // Cluster 2: Root directory
// Cluster 3: Data memory (pseudo-sector) // Cluster 3: Data memory (pseudo-sector)
DWORD nFat = cap.sectors; const uint32_t nFat = cap.sectors;
DWORD nRoot = cap.sectors * 2; const uint32_t nRoot = cap.sectors * 2;
DWORD nData = cap.sectors * 3; const uint32_t nData = cap.sectors * 3;
// Set DPB // Set DPB
pDpb->sector_size = cap.bytes; // Bytes per sector pDpb->sector_size = cap.bytes; // Bytes per sector
pDpb->cluster_size = pDpb->cluster_size =
(BYTE)(cap.sectors - 1); // Sectors per cluster - 1 (BYTE)(cap.sectors - 1); // Sectors per cluster - 1
pDpb->shift = (BYTE)nShift; // Number of cluster → sector shifts pDpb->shift = (BYTE)nShift; // Number of cluster → sector shifts
pDpb->fat_sector = (WORD)nFat; // First FAT sector number pDpb->fat_sector = (uint16_t)nFat; // First FAT sector number
pDpb->fat_max = 1; // Number of FAT memory spaces pDpb->fat_max = 1; // Number of FAT memory spaces
pDpb->fat_size = (BYTE)cap.sectors; // Number of sectors controlled by FAT (excluding copies) pDpb->fat_size = (BYTE)cap.sectors; // Number of sectors controlled by FAT (excluding copies)
pDpb->file_max = pDpb->file_max =
(WORD)(cap.sectors * cap.bytes / 0x20); // Number of files in the root directory (uint16_t)(cap.sectors * cap.bytes / 0x20); // Number of files in the root directory
pDpb->data_sector = (WORD)nData; // First sector number of data memory pDpb->data_sector = (uint16_t)nData; // First sector number of data memory
pDpb->cluster_max = cap.clusters; // Total number of clusters + 1 pDpb->cluster_max = cap.clusters; // Total number of clusters + 1
pDpb->root_sector = (WORD)nRoot; // First sector number of the root directory pDpb->root_sector = (uint16_t)nRoot; // First sector number of the root directory
pDpb->media = media; // Media byte pDpb->media = media; // Media byte
return 0; return 0;
@ -3576,7 +3573,7 @@ int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) const
/// Buffer size is hard coded to $200 byte. /// Buffer size is hard coded to $200 byte.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize) int CFileSys::DiskRead(uint32_t nUnit, BYTE* pBuffer, uint32_t nSector, uint32_t nSize)
{ {
assert(pBuffer); assert(pBuffer);
@ -3613,7 +3610,7 @@ int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize)
// Note that in lzdsys the sector number to read is calculated by the following formula: // Note that in lzdsys the sector number to read is calculated by the following formula:
// (dirent.cluster - 2) * (dpb.cluster_size + 1) + dpb.data_sector // (dirent.cluster - 2) * (dpb.cluster_size + 1) + dpb.data_sector
/// @warning little endian only /// @warning little endian only
dir->cluster = (WORD)(m_nHostSectorCount + 2); // Pseudo-sector number dir->cluster = (uint16_t)(m_nHostSectorCount + 2); // Pseudo-sector number
m_nHostSectorBuffer[m_nHostSectorCount] = nSector; // Entity that points to the pseudo-sector m_nHostSectorBuffer[m_nHostSectorCount] = nSector; // Entity that points to the pseudo-sector
m_nHostSectorCount++; m_nHostSectorCount++;
m_nHostSectorCount %= XM6_HOST_PSEUDO_CLUSTER_MAX; m_nHostSectorCount %= XM6_HOST_PSEUDO_CLUSTER_MAX;
@ -3622,8 +3619,8 @@ int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize)
} }
// Calculate the sector number from the cluster number // Calculate the sector number from the cluster number
DWORD n = nSector - (3 * cap.sectors); uint32_t n = nSector - (3 * cap.sectors);
DWORD nMod = 1; uint32_t nMod = 1;
if (cap.sectors) { if (cap.sectors) {
// Beware that cap.sectors becomes 0 when media does not exist // Beware that cap.sectors becomes 0 when media does not exist
nMod = n % cap.sectors; nMod = n % cap.sectors;
@ -3641,9 +3638,9 @@ int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize)
if (!f.Open()) if (!f.Open())
return FS_INVALIDPRM; return FS_INVALIDPRM;
memset(pBuffer, 0, 0x200); memset(pBuffer, 0, 0x200);
DWORD nResult = f.Read(pBuffer, 0x200); uint32_t nResult = f.Read(pBuffer, 0x200);
f.Close(); f.Close();
if (nResult == (DWORD)-1) if (nResult == (uint32_t)-1)
return FS_INVALIDPRM; return FS_INVALIDPRM;
return 0; return 0;
@ -3658,7 +3655,7 @@ int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize)
/// $54 - Write sector /// $54 - Write sector
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::DiskWrite(DWORD nUnit) const int CFileSys::DiskWrite(uint32_t nUnit) const
{ {
// Unit check // Unit check
if (nUnit >= CHostEntry::DRIVE_MAX) if (nUnit >= CHostEntry::DRIVE_MAX)
@ -3684,7 +3681,7 @@ int CFileSys::DiskWrite(DWORD nUnit) const
/// $55 - IOCTRL /// $55 - IOCTRL
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl) int CFileSys::Ioctrl(uint32_t nUnit, uint32_t nFunction, Human68k::ioctrl_t* pIoctrl)
{ {
assert(pIoctrl); assert(pIoctrl);
@ -3707,7 +3704,7 @@ int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl)
case 2: case 2:
switch (pIoctrl->param) { switch (pIoctrl->param) {
case (DWORD)-1: case (uint32_t)-1:
// Re-identify media // Re-identify media
m_cEntry.isMediaOffline(nUnit); m_cEntry.isMediaOffline(nUnit);
return 0; return 0;
@ -3722,17 +3719,17 @@ int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl)
} }
break; break;
case (DWORD)-1: case (uint32_t)-1:
// Resident evaluation // Resident evaluation
memcpy(pIoctrl->buffer, "WindrvXM", 8); memcpy(pIoctrl->buffer, "WindrvXM", 8);
return 0; return 0;
case (DWORD)-2: case (uint32_t)-2:
// Set options // Set options
SetOption(pIoctrl->param); SetOption(pIoctrl->param);
return 0; return 0;
case (DWORD)-3: case (uint32_t)-3:
// Get options // Get options
pIoctrl->param = GetOption(); pIoctrl->param = GetOption();
return 0; return 0;
@ -3749,7 +3746,7 @@ int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl)
/// $56 - Flush /// $56 - Flush
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Flush(DWORD nUnit) const int CFileSys::Flush(uint32_t nUnit) const
{ {
// Unit check // Unit check
if (nUnit >= CHostEntry::DRIVE_MAX) if (nUnit >= CHostEntry::DRIVE_MAX)
@ -3766,7 +3763,7 @@ int CFileSys::Flush(DWORD nUnit) const
/// $57 - Media change check /// $57 - Media change check
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::CheckMedia(DWORD nUnit) const int CFileSys::CheckMedia(uint32_t nUnit) const
{ {
// Unit check // Unit check
if (nUnit >= CHostEntry::DRIVE_MAX) if (nUnit >= CHostEntry::DRIVE_MAX)
@ -3788,7 +3785,7 @@ int CFileSys::CheckMedia(DWORD nUnit) const
/// $58 - Lock /// $58 - Lock
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int CFileSys::Lock(DWORD nUnit) const int CFileSys::Lock(uint32_t nUnit) const
{ {
// Unit check // Unit check
if (nUnit >= CHostEntry::DRIVE_MAX) if (nUnit >= CHostEntry::DRIVE_MAX)
@ -3810,7 +3807,7 @@ int CFileSys::Lock(DWORD nUnit) const
/// Set options /// Set options
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void CFileSys::SetOption(DWORD nOption) void CFileSys::SetOption(uint32_t nOption)
{ {
// Clear cache when option settings change // Clear cache when option settings change
if (m_nOption ^ nOption) if (m_nOption ^ nOption)
@ -3835,7 +3832,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument)
const BYTE* pp = pArgument->buf; const BYTE* pp = pArgument->buf;
pp += strlen((const char*)pp) + 1; pp += strlen((const char*)pp) + 1;
DWORD nOption = m_nOptionDefault; uint32_t nOption = m_nOptionDefault;
for (;;) { for (;;) {
assert(pp < pArgument->buf + sizeof(*pArgument)); assert(pp < pArgument->buf + sizeof(*pArgument));
const BYTE* p = pp; const BYTE* p = pp;
@ -3843,7 +3840,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument)
if (c == '\0') if (c == '\0')
break; break;
DWORD nMode; uint32_t nMode;
if (c == '+') { if (c == '+') {
nMode = 1; nMode = 1;
} else if (c == '-') { } else if (c == '-') {
@ -3868,7 +3865,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument)
if (c == '\0') if (c == '\0')
break; break;
DWORD nBit = 0; uint32_t nBit = 0;
switch (c) { switch (c) {
case 'A': case 'a': nBit = WINDRV_OPT_CONVERT_LENGTH; break; case 'A': case 'a': nBit = WINDRV_OPT_CONVERT_LENGTH; break;
case 'T': case 't': nBit = WINDRV_OPT_COMPARE_LENGTH; nMode ^= 1; break; case 'T': case 't': nBit = WINDRV_OPT_COMPARE_LENGTH; nMode ^= 1; break;
@ -3911,7 +3908,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument)
/// Get volume label /// Get volume label
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CFileSys::FilesVolume(DWORD nUnit, Human68k::files_t* pFiles) const bool CFileSys::FilesVolume(uint32_t nUnit, Human68k::files_t* pFiles) const
{ {
assert(pFiles); assert(pFiles);

View File

@ -92,39 +92,14 @@ namespace Human68k {
}; };
/// Seek types /// Seek types
enum seek_t { enum class seek_t {
SK_BEGIN = 0, ///< From the beginning of a file SK_BEGIN = 0, ///< From the beginning of a file
SK_CURRENT = 1, ///< From the current location SK_CURRENT = 1, ///< From the current location
SK_END = 2, ///< From the end of the file SK_END = 2, ///< From the end of the file
}; };
/// Media byte // Media byte
enum media_t { const static int MEDIA_REMOTE = 0xF3; ///< Remote drive
MEDIA_2DD_10 = 0xE0, ///< 2DD/10 sector
MEDIA_1D_9 = 0xE5, ///< 1D/9 sector
MEDIA_2D_9 = 0xE6, ///< 2D/9 sector
MEDIA_1D_8 = 0xE7, ///< 1D/8 sector
MEDIA_2D_8 = 0xE8, ///< 2D/8 sector
MEDIA_2HT = 0xEA, ///< 2HT
MEDIA_2HS = 0xEB, ///< 2HS
MEDIA_2HDE = 0xEC, ///< 2DDE
MEDIA_1DD_9 = 0xEE, ///< 1DD/9 sector
MEDIA_1DD_8 = 0xEF, ///< 1DD/8 sector
MEDIA_MANUAL = 0xF1, ///< Remote drive (manual eject)
MEDIA_REMOVABLE = 0xF2, ///< Remote drive (removable)
MEDIA_REMOTE = 0xF3, ///< Remote drive
MEDIA_DAT = 0xF4, ///< SCSI-DAT
MEDIA_CDROM = 0xF5, ///< SCSI-CDROM
MEDIA_MO = 0xF6, ///< SCSI-MO
MEDIA_SCSI_HD = 0xF7, ///< SCSI-HD
MEDIA_SASI_HD = 0xF8, ///< SASI-HD
MEDIA_RAMDISK = 0xF9, ///< RAM disk
MEDIA_2HQ = 0xFA, ///< 2HQ
MEDIA_2DD_8 = 0xFB, ///< 2DD/8 sector
MEDIA_2DD_9 = 0xFC, ///< 2DD/9 sector
MEDIA_2HC = 0xFD, ///< 2HC
MEDIA_2HD = 0xFE, ///< 2HD
};
struct namests_t { struct namests_t {
BYTE wildcard; ///< Wildcard character length BYTE wildcard; ///< Wildcard character length
@ -141,41 +116,41 @@ namespace Human68k {
struct files_t { struct files_t {
BYTE fatr; ///< + 0 search attribute; read-only BYTE fatr; ///< + 0 search attribute; read-only
// BYTE drive; ///< + 1 drive number; read-only // BYTE drive; ///< + 1 drive number; read-only
DWORD sector; ///< + 2 directory sector; DOS _FILES first address substitute uint32_t sector; ///< + 2 directory sector; DOS _FILES first address substitute
// WORD cluster; ///< + 6 directory cluster; details unknown (unused) // uint16_t cluster; ///< + 6 directory cluster; details unknown (unused)
WORD offset; ///< + 8 directory entry; write-only uint16_t offset; ///< + 8 directory entry; write-only
// BYTE name[8]; ///< +10 working file name; write-only (unused) // BYTE name[8]; ///< +10 working file name; write-only (unused)
// BYTE ext[3]; ///< +18 working extension; write-only (unused) // BYTE ext[3]; ///< +18 working extension; write-only (unused)
BYTE attr; ///< +21 file attribute; write-only BYTE attr; ///< +21 file attribute; write-only
WORD time; ///< +22 last change time of day; write-only uint16_t time; ///< +22 last change time of day; write-only
WORD date; ///< +24 last change date; write-only uint16_t date; ///< +24 last change date; write-only
DWORD size; ///< +26 file size; write-only uint32_t size; ///< +26 file size; write-only
BYTE full[23]; ///< +30 full name; write-only BYTE full[23]; ///< +30 full name; write-only
}; };
struct fcb_t { struct fcb_t {
// BYTE pad00[6]; ///< + 0~+ 5 (unused) // BYTE pad00[6]; ///< + 0~+ 5 (unused)
DWORD fileptr; ///< + 6~+ 9 file pointer uint32_t fileptr; ///< + 6~+ 9 file pointer
// BYTE pad01[4]; ///< +10~+13 (unused) // BYTE pad01[4]; ///< +10~+13 (unused)
WORD mode; ///< +14~+15 open mode uint16_t mode; ///< +14~+15 open mode
// BYTE pad02[16]; ///< +16~+31 (unused) // BYTE pad02[16]; ///< +16~+31 (unused)
// DWORD zero; ///< +32~+35 zeros are written when opened (unused) // uint32_t zero; ///< +32~+35 zeros are written when opened (unused)
// BYTE name[8]; ///< +36~+43 file name (PADDING 0x20) (unused) // BYTE name[8]; ///< +36~+43 file name (PADDING 0x20) (unused)
// BYTE ext[3]; ///< +44~+46 extension (PADDING 0x20) (unused) // BYTE ext[3]; ///< +44~+46 extension (PADDING 0x20) (unused)
BYTE attr; ///< +47 file attribute BYTE attr; ///< +47 file attribute
// BYTE add[10]; ///< +48~+57 file name addition (PADDING 0x00) (unused) // BYTE add[10]; ///< +48~+57 file name addition (PADDING 0x00) (unused)
WORD time; ///< +58~+59 last change time of day uint16_t time; ///< +58~+59 last change time of day
WORD date; ///< +60~+61 last change date uint16_t date; ///< +60~+61 last change date
// WORD cluster; ///< +62~+63 cluster number (unused) // uint16_t cluster; ///< +62~+63 cluster number (unused)
DWORD size; ///< +64~+67 file size uint32_t size; ///< +64~+67 file size
// BYTE pad03[28]; ///< +68~+95 FAT cache (unused) // BYTE pad03[28]; ///< +68~+95 FAT cache (unused)
}; };
struct capacity_t { struct capacity_t {
WORD freearea; ///< + 0 Number of available clusters uint16_t freearea; ///< + 0 Number of available clusters
WORD clusters; ///< + 2 Total number of clusters uint16_t clusters; ///< + 2 Total number of clusters
WORD sectors; ///< + 4 Number of sectors per cluster uint16_t sectors; ///< + 4 Number of sectors per cluster
WORD bytes; ///< + 6 Number of bytes per sector uint16_t bytes; ///< + 6 Number of bytes per sector
}; };
struct ctrldrive_t { struct ctrldrive_t {
@ -184,17 +159,17 @@ namespace Human68k {
}; };
struct dpb_t { struct dpb_t {
WORD sector_size; ///< + 0 Number of bytes in one sector uint16_t sector_size; ///< + 0 Number of bytes in one sector
BYTE cluster_size; ///< + 2 Number sectors in one cluster -1 BYTE cluster_size; ///< + 2 Number sectors in one cluster -1
BYTE shift; ///< + 3 Number of cluster→sector shifts BYTE shift; ///< + 3 Number of cluster→sector shifts
WORD fat_sector; ///< + 4 FAT first sector number uint16_t fat_sector; ///< + 4 FAT first sector number
BYTE fat_max; ///< + 6 FAT storage quantity BYTE fat_max; ///< + 6 FAT storage quantity
BYTE fat_size; ///< + 7 FAT controlled sector number (excluding duplicates) BYTE fat_size; ///< + 7 FAT controlled sector number (excluding duplicates)
WORD file_max; ///< + 8 Number of files in the root directory uint16_t file_max; ///< + 8 Number of files in the root directory
WORD data_sector; ///< +10 First sector number of data storage uint16_t data_sector; ///< +10 First sector number of data storage
WORD cluster_max; ///< +12 Total number of clusters +1 uint16_t cluster_max; ///< +12 Total number of clusters +1
WORD root_sector; ///< +14 First sector number of root directory uint16_t root_sector; ///< +14 First sector number of root directory
// DWORD driverentry; ///< +16 Device driver pointer (unused) // uint32_t driverentry; ///< +16 Device driver pointer (unused)
BYTE media; ///< +20 Media identifier BYTE media; ///< +20 Media identifier
// BYTE flag; ///< +21 Flag used by DPB (unused) // BYTE flag; ///< +21 Flag used by DPB (unused)
}; };
@ -205,22 +180,22 @@ namespace Human68k {
BYTE ext[3]; ///< + 8 Extension (PADDING 0x20) BYTE ext[3]; ///< + 8 Extension (PADDING 0x20)
BYTE attr; ///< +11 File attribute BYTE attr; ///< +11 File attribute
BYTE add[10]; ///< +12 File name addition (PADDING 0x00) BYTE add[10]; ///< +12 File name addition (PADDING 0x00)
WORD time; ///< +22 Last change time of day uint16_t time; ///< +22 Last change time of day
WORD date; ///< +24 Last change date uint16_t date; ///< +24 Last change date
WORD cluster; ///< +26 Cluster number uint16_t cluster; ///< +26 Cluster number
DWORD size; ///< +28 File size uint32_t size; ///< +28 File size
}; };
/// IOCTRL parameter union /// IOCTRL parameter union
union ioctrl_t { union ioctrl_t {
BYTE buffer[8]; ///< Access in byte units BYTE buffer[8]; ///< Access in byte units
DWORD param; ///< Parameter (First 4 bytes) uint32_t param; ///< Parameter (First 4 bytes)
WORD media; ///< Media byte (First 2 bytes) uint16_t media; ///< Media byte (First 2 bytes)
}; };
/// Command line parameter struct /// Command line parameter struct
/** /**
The driver itself is included in the beginning of the argument, The driver itself is included in the beginning of the argument,
so setting to a length longer than HUMAN68K_PATH_MAX so setting to a length longer than HUMAN68K_PATH_MAX
*/ */
struct argument_t { struct argument_t {
@ -257,9 +232,9 @@ static const int XM6_HOST_PSEUDO_CLUSTER_MAX = 10;
/// Number of caches for directory entries /// Number of caches for directory entries
/** /**
Human68k carries out a large number of checks of directory entries when doing an operation Human68k carries out a large number of checks of directory entries when doing an operation
inside a subdirectory. This specifies the number of caches used to speed up this operation. inside a subdirectory. This specifies the number of caches used to speed up this operation.
Cache is allocated per drive. The more you add the faster it gets, but use too many Cache is allocated per drive. The more you add the faster it gets, but use too many
and the host OS gets under a heavy load, so be careful. and the host OS gets under a heavy load, so be careful.
Default is 16. Default is 16.
@ -268,10 +243,10 @@ static const int XM6_HOST_DIRENTRY_CACHE_MAX = 16;
/// Max number of entries that can be stored per directory /// Max number of entries that can be stored per directory
/** /**
When a large number of files are stored in a directory, a larger amount of data than When a large number of files are stored in a directory, a larger amount of data than
contemporanous applications can handle will be returned. This may lead to errors such as contemporanous applications can handle will be returned. This may lead to errors such as
partial data being recognized, performance dropping significantly, or OOM crashes. partial data being recognized, performance dropping significantly, or OOM crashes.
To guard against this, an upper limit is defined here. In the case of a particular To guard against this, an upper limit is defined here. In the case of a particular
file manager, the upper limit is 2560 files. This is one good example to use as reference. file manager, the upper limit is 2560 files. This is one good example to use as reference.
Default is around 60000 entries. (Upper limit of the FAT root directory) Default is around 60000 entries. (Upper limit of the FAT root directory)
@ -280,17 +255,17 @@ static const int XM6_HOST_DIRENTRY_FILE_MAX = 65535;
/// Max number of patterns for file name deduplication /// Max number of patterns for file name deduplication
/** /**
The file names on the Human68k side are automatically created based on the file system on The file names on the Human68k side are automatically created based on the file system on
the host side. However, Human68k have stricter file name length restrictions than the host has. the host side. However, Human68k have stricter file name length restrictions than the host has.
Because of this, there is a risk that file name duplication will occur. When this happens, Because of this, there is a risk that file name duplication will occur. When this happens,
WindrvXM will use a certain renaming heuristic to generate alternate file names to resolve WindrvXM will use a certain renaming heuristic to generate alternate file names to resolve
the duplication. Theoretically, there are over 60 million (36^5) unique file names that the duplication. Theoretically, there are over 60 million (36^5) unique file names that
can be generated by this method. However, in reality any more than a few hundred can be generated by this method. However, in reality any more than a few hundred
deduplications will take excessive processing time. So here an upper limit to deduplication deduplications will take excessive processing time. So here an upper limit to deduplication
is set in order to maintain system performance. If a system is operated with common sense, is set in order to maintain system performance. If a system is operated with common sense,
you should only need a few dozen deduplication patterns, so this value can be kept low you should only need a few dozen deduplication patterns, so this value can be kept low
to further improve performance. In the case deduplication is not carried out, multiple files to further improve performance. In the case deduplication is not carried out, multiple files
with the same name will be created. When trying to access such files, with the same name will be created. When trying to access such files,
only the first entry will ever be accessed. only the first entry will ever be accessed.
Default is 36 patterns. Default is 36 patterns.
@ -338,11 +313,7 @@ Normal is 0. Becomes 1 if attempting to mount in read-only mode.
Reserving the other values for future use. Reserving the other values for future use.
Insurance against hard-to-detect devices such as homemade USB storage. Insurance against hard-to-detect devices such as homemade USB storage.
*/ */
enum { static const uint32_t FSFLAG_WRITE_PROTECT = 0x00000001; ///< Bit0: Force write protect
FSFLAG_WRITE_PROTECT = 0x00000001, ///< Bit0: Force write protect
FSFLAG_REMOVABLE = 0x00000002, ///< Bit1: Force removable media
FSFLAG_MANUAL = 0x00000004, ///< Bit2: Force manual eject
};
//=========================================================================== //===========================================================================
// //
@ -357,8 +328,8 @@ class CRing {
public: public:
CRing() { Init(); } CRing() { Init(); }
~CRing() { Remove(); } ~CRing() { Remove(); }
CRing(CRing&) = delete; CRing(CRing&) = default;
CRing& operator=(const CRing&) = delete; CRing& operator=(const CRing&) = default;
void Init() { next = prev = this; } void Init() { next = prev = this; }
@ -442,8 +413,6 @@ class CHostFilename {
public: public:
CHostFilename() = default; CHostFilename() = default;
~CHostFilename() = default; ~CHostFilename() = default;
CHostFilename(CHostFilename&) = delete;
CHostFilename& operator=(const CHostFilename&) = delete;
static size_t Offset() { return offsetof(CHostFilename, m_szHost); } ///< Get offset location static size_t Offset() { return offsetof(CHostFilename, m_szHost); } ///< Get offset location
@ -460,17 +429,17 @@ public:
void SetEntryName(); ///< Set Human68k directory entry void SetEntryName(); ///< Set Human68k directory entry
void SetEntryAttribute(BYTE nHumanAttribute) void SetEntryAttribute(BYTE nHumanAttribute)
{ m_dirHuman.attr = nHumanAttribute; } ///< Set Human68k directory entry { m_dirHuman.attr = nHumanAttribute; } ///< Set Human68k directory entry
void SetEntrySize(DWORD nHumanSize) void SetEntrySize(uint32_t nHumanSize)
{ m_dirHuman.size = nHumanSize; } ///< Set Human68k directory entry { m_dirHuman.size = nHumanSize; } ///< Set Human68k directory entry
void SetEntryDate(WORD nHumanDate) void SetEntryDate(uint16_t nHumanDate)
{ m_dirHuman.date = nHumanDate; } ///< Set Human68k directory entry { m_dirHuman.date = nHumanDate; } ///< Set Human68k directory entry
void SetEntryTime(WORD nHumanTime) void SetEntryTime(uint16_t nHumanTime)
{ m_dirHuman.time = nHumanTime; } ///< Set Human68k directory entry { m_dirHuman.time = nHumanTime; } ///< Set Human68k directory entry
void SetEntryCluster(WORD nHumanCluster) void SetEntryCluster(uint16_t nHumanCluster)
{ m_dirHuman.cluster = nHumanCluster; } ///< Set Human68k directory entry { m_dirHuman.cluster = nHumanCluster; } ///< Set Human68k directory entry
const Human68k::dirent_t* GetEntry() const const Human68k::dirent_t* GetEntry() const
{ return &m_dirHuman; } ///< Get Human68k directory entry { return &m_dirHuman; } ///< Get Human68k directory entry
int CheckAttribute(DWORD nHumanAttribute) const; ///< Determine Human68k directory entry attributes int CheckAttribute(uint32_t nHumanAttribute) const; ///< Determine Human68k directory entry attributes
bool isSameEntry(const Human68k::dirent_t* pdirHuman) const bool isSameEntry(const Human68k::dirent_t* pdirHuman) const
{ assert(pdirHuman); return memcmp(&m_dirHuman, pdirHuman, sizeof(m_dirHuman)) == 0; } { assert(pdirHuman); return memcmp(&m_dirHuman, pdirHuman, sizeof(m_dirHuman)) == 0; }
///< Determine Human68k directory entry match ///< Determine Human68k directory entry match
@ -505,7 +474,7 @@ get updated for new directories created as a result of file operations, which
triggers updates to directory entires. triggers updates to directory entires.
However, on the host file system, new directories do typically get an updated time stamp. However, on the host file system, new directories do typically get an updated time stamp.
The unfortunate outcome is that when copying a directory for instance, the time stamp The unfortunate outcome is that when copying a directory for instance, the time stamp
will get overwritten even if the application did not intend for the time stamp to get updated. will get overwritten even if the application did not intend for the time stamp to get updated.
Here follows an implementation of a directory cache FAT time stamp emulation feature. Here follows an implementation of a directory cache FAT time stamp emulation feature.
@ -522,8 +491,8 @@ class CHostPath: public CRing {
public: public:
/// Search buffer /// Search buffer
struct find_t { struct find_t {
DWORD count; ///< Search execution count + 1 (When 0 the below value is invalid) uint32_t count; ///< Search execution count + 1 (When 0 the below value is invalid)
DWORD id; ///< Entry unique ID for the path of the next search uint32_t id; ///< Entry unique ID for the path of the next search
const ring_t* pos; ///< Position of the next search (When identical to unique ID) const ring_t* pos; ///< Position of the next search (When identical to unique ID)
Human68k::dirent_t entry; ///< Contents of the next seach entry Human68k::dirent_t entry; ///< Contents of the next seach entry
@ -532,8 +501,8 @@ public:
CHostPath() = default; CHostPath() = default;
~CHostPath(); ~CHostPath();
CHostPath(CHostPath&) = delete; CHostPath(CHostPath&) = default;
CHostPath& operator=(const CHostPath&) = delete; CHostPath& operator=(const CHostPath&) = default;
void Clean(); ///< Initialialize for reuse void Clean(); ///< Initialialize for reuse
@ -542,9 +511,9 @@ public:
bool isSameHuman(const BYTE* szHuman) const; ///< Compare the name on the Human68k side bool isSameHuman(const BYTE* szHuman) const; ///< Compare the name on the Human68k side
bool isSameChild(const BYTE* szHuman) const; ///< Compare the name on the Human68k side bool isSameChild(const BYTE* szHuman) const; ///< Compare the name on the Human68k side
const TCHAR* GetHost() const { return m_szHost; } ///< Obtain the name on the host side const TCHAR* GetHost() const { return m_szHost; } ///< Obtain the name on the host side
const CHostFilename* FindFilename(const BYTE* szHuman, DWORD nHumanAttribute = Human68k::AT_ALL) const; const CHostFilename* FindFilename(const BYTE* szHuman, uint32_t nHumanAttribute = Human68k::AT_ALL) const;
///< Find file name ///< Find file name
const CHostFilename* FindFilenameWildcard(const BYTE* szHuman, DWORD nHumanAttribute, find_t* pFind) const; const CHostFilename* FindFilenameWildcard(const BYTE* szHuman, uint32_t nHumanAttribute, find_t* pFind) const;
///< Find file name (with support for wildcards) ///< Find file name (with support for wildcards)
bool isRefresh() const; ///< Check that the file change has been done bool isRefresh() const; ///< Check that the file change has been done
void Refresh(); ///< Refresh file void Refresh(); ///< Refresh file
@ -564,20 +533,20 @@ private:
CRing m_cRing; ///< For CHostFilename linking CRing m_cRing; ///< For CHostFilename linking
time_t m_tBackup = 0; ///< For time stamp restoration time_t m_tBackup = 0; ///< For time stamp restoration
bool m_bRefresh = true; ///< Refresh flag bool m_bRefresh = true; ///< Refresh flag
DWORD m_nId = 0; ///< Unique ID (When the value has changed, it means an update has been made) uint32_t m_nId = 0; ///< Unique ID (When the value has changed, it means an update has been made)
BYTE m_szHuman[HUMAN68K_PATH_MAX]; ///< The internal Human68k name for the relevant entry BYTE m_szHuman[HUMAN68K_PATH_MAX]; ///< The internal Human68k name for the relevant entry
TCHAR m_szHost[FILEPATH_MAX]; ///< The host side name for the relevant entry TCHAR m_szHost[FILEPATH_MAX]; ///< The host side name for the relevant entry
static DWORD g_nId; ///< Counter for the unique ID generation static uint32_t g_nId; ///< Counter for the unique ID generation
}; };
//=========================================================================== //===========================================================================
// //
/// File search processing /// File search processing
/// ///
/// It's pretty much impossible to process Human68k file names as Unicode internally. /// It's pretty much impossible to process Human68k file names as Unicode internally.
/// So, we carry out binary conversion for processing. We leave it up to the /// So, we carry out binary conversion for processing. We leave it up to the
/// directory entry cache to handle the conversion, which allows WINDRV to read /// directory entry cache to handle the conversion, which allows WINDRV to read
/// everything as Shift-JIS. Additionally, it allows Human68k names to be /// everything as Shift-JIS. Additionally, it allows Human68k names to be
/// fully independent of base path assignments. /// fully independent of base path assignments.
/// ///
@ -596,20 +565,18 @@ class CHostFiles {
public: public:
CHostFiles() = default; CHostFiles() = default;
~CHostFiles() = default; ~CHostFiles() = default;
CHostFiles(CHostFiles&) = delete;
CHostFiles& operator=(const CHostFiles&) = delete;
void Init(); void Init();
void SetKey(DWORD nKey) { m_nKey = nKey; } ///< Set search key void SetKey(uint32_t nKey) { m_nKey = nKey; } ///< Set search key
bool isSameKey(DWORD nKey) const { return m_nKey == nKey; } ///< Compare search key bool isSameKey(uint32_t nKey) const { return m_nKey == nKey; } ///< Compare search key
void SetPath(const Human68k::namests_t* pNamests); ///< Create path and file name internally void SetPath(const Human68k::namests_t* pNamests); ///< Create path and file name internally
bool isRootPath() const { return m_szHumanPath[1] == '\0'; } ///< Check if root directory bool isRootPath() const { return m_szHumanPath[1] == '\0'; } ///< Check if root directory
void SetPathWildcard() { m_nHumanWildcard = 1; } ///< Enable file search using wildcards void SetPathWildcard() { m_nHumanWildcard = 1; } ///< Enable file search using wildcards
void SetPathOnly() { m_nHumanWildcard = 0xFF; } ///< Enable only path names void SetPathOnly() { m_nHumanWildcard = 0xFF; } ///< Enable only path names
bool isPathOnly() const { return m_nHumanWildcard == 0xFF; } ///< Check if set to only path names bool isPathOnly() const { return m_nHumanWildcard == 0xFF; } ///< Check if set to only path names
void SetAttribute(DWORD nHumanAttribute) { m_nHumanAttribute = nHumanAttribute; } ///< Set search attribute void SetAttribute(uint32_t nHumanAttribute) { m_nHumanAttribute = nHumanAttribute; } ///< Set search attribute
bool Find(DWORD nUnit, const class CHostEntry* pEntry); ///< Find files on the Human68k side, generating data on the host side bool Find(uint32_t nUnit, const class CHostEntry* pEntry); ///< Find files on the Human68k side, generating data on the host side
const CHostFilename* Find(const CHostPath* pPath); ///< Find file name const CHostFilename* Find(const CHostPath* pPath); ///< Find file name
void SetEntry(const CHostFilename* pFilename); ///< Store search results on the Human68k side void SetEntry(const CHostFilename* pFilename); ///< Store search results on the Human68k side
void SetResult(const TCHAR* szPath); ///< Set names on the host side void SetResult(const TCHAR* szPath); ///< Set names on the host side
@ -620,18 +587,18 @@ public:
const Human68k::dirent_t* GetEntry() const { return &m_dirHuman; }///< Get Human68k directory entry const Human68k::dirent_t* GetEntry() const { return &m_dirHuman; }///< Get Human68k directory entry
DWORD GetAttribute() const { return m_dirHuman.attr; } ///< Get Human68k attribute uint32_t GetAttribute() const { return m_dirHuman.attr; } ///< Get Human68k attribute
WORD GetDate() const { return m_dirHuman.date; } ///< Get Human68k date uint16_t GetDate() const { return m_dirHuman.date; } ///< Get Human68k date
WORD GetTime() const { return m_dirHuman.time; } ///< Get Human68k time uint16_t GetTime() const { return m_dirHuman.time; } ///< Get Human68k time
DWORD GetSize() const { return m_dirHuman.size; } ///< Get Human68k file size uint32_t GetSize() const { return m_dirHuman.size; } ///< Get Human68k file size
const BYTE* GetHumanFilename() const { return m_szHumanFilename; }///< Get Human68k file name const BYTE* GetHumanFilename() const { return m_szHumanFilename; }///< Get Human68k file name
const BYTE* GetHumanResult() const { return m_szHumanResult; } ///< Get Human68k file name search results const BYTE* GetHumanResult() const { return m_szHumanResult; } ///< Get Human68k file name search results
const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Get Human68k path name const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Get Human68k path name
private: private:
DWORD m_nKey = 0; ///< FILES buffer address for Human68k; 0 is unused uint32_t m_nKey = 0; ///< FILES buffer address for Human68k; 0 is unused
DWORD m_nHumanWildcard = 0; ///< Human68k wildcard data uint32_t m_nHumanWildcard = 0; ///< Human68k wildcard data
DWORD m_nHumanAttribute = 0; ///< Human68k search attribute uint32_t m_nHumanAttribute = 0; ///< Human68k search attribute
CHostPath::find_t m_findNext = {}; ///< Next search location data CHostPath::find_t m_findNext = {}; ///< Next search location data
Human68k::dirent_t m_dirHuman = {}; ///< Search results: Human68k file data Human68k::dirent_t m_dirHuman = {}; ///< Search results: Human68k file data
BYTE m_szHumanFilename[24] = {}; ///< Human68k file name BYTE m_szHumanFilename[24] = {}; ///< Human68k file name
@ -653,8 +620,8 @@ public:
void Init(); ///< Initialization (when the driver is installed) void Init(); ///< Initialization (when the driver is installed)
void Clean(); ///< Release (when starting up or resetting) void Clean(); ///< Release (when starting up or resetting)
CHostFiles* Alloc(DWORD nKey); CHostFiles* Alloc(uint32_t nKey);
CHostFiles* Search(DWORD nKey); CHostFiles* Search(uint32_t nKey);
void Free(CHostFiles* pFiles); void Free(CHostFiles* pFiles);
private: private:
/// For memory management /// For memory management
@ -675,31 +642,31 @@ class CHostFcb {
public: public:
CHostFcb() = default; CHostFcb() = default;
~CHostFcb() { Close(); } ~CHostFcb() { Close(); }
CHostFcb(CHostFcb&) = delete; CHostFcb(CHostFcb&) = default;
CHostFcb& operator=(const CHostFcb&) = delete; CHostFcb& operator=(const CHostFcb&) = default;
void Init(); void Init();
void SetKey(DWORD nKey) { m_nKey = nKey; } ///< Set search key void SetKey(uint32_t nKey) { m_nKey = nKey; } ///< Set search key
bool isSameKey(DWORD nKey) const { return m_nKey == nKey; } ///< Compare search key bool isSameKey(uint32_t nKey) const { return m_nKey == nKey; } ///< Compare search key
void SetUpdate() { m_bUpdate = true; } ///< Update void SetUpdate() { m_bUpdate = true; } ///< Update
bool isUpdate() const { return m_bUpdate; } ///< Get update state bool isUpdate() const { return m_bUpdate; } ///< Get update state
bool SetMode(DWORD nHumanMode); ///< Set file open mode bool SetMode(uint32_t nHumanMode); ///< Set file open mode
void SetFilename(const TCHAR* szFilename); ///< Set file name void SetFilename(const TCHAR* szFilename); ///< Set file name
void SetHumanPath(const BYTE* szHumanPath); ///< Set Human68k path name void SetHumanPath(const BYTE* szHumanPath); ///< Set Human68k path name
const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Get Human68k path name const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Get Human68k path name
bool Create(DWORD nHumanAttribute, bool bForce); ///< Create file bool Create(uint32_t nHumanAttribute, bool bForce); ///< Create file
bool Open(); ///< Open file bool Open(); ///< Open file
DWORD Read(BYTE* pBuffer, DWORD nSize); ///< Read file uint32_t Read(BYTE* pBuffer, uint32_t nSize); ///< Read file
DWORD Write(const BYTE* pBuffer, DWORD nSize); ///< Write file uint32_t Write(const BYTE* pBuffer, uint32_t nSize); ///< Write file
bool Truncate() const; ///< Truncate file bool Truncate() const; ///< Truncate file
DWORD Seek(DWORD nOffset, DWORD nHumanSeek); ///< Seek file uint32_t Seek(uint32_t nOffset, Human68k::seek_t nHumanSeek); ///< Seek file
bool TimeStamp(DWORD nHumanTime) const; ///< Set file time stamp bool TimeStamp(uint32_t nHumanTime) const; ///< Set file time stamp
void Close(); ///< Close file void Close(); ///< Close file
private: private:
DWORD m_nKey = 0; ///< Human68k FCB buffer address (0 if unused) uint32_t m_nKey = 0; ///< Human68k FCB buffer address (0 if unused)
bool m_bUpdate = false; ///< Update flag bool m_bUpdate = false; ///< Update flag
FILE* m_pFile = nullptr; ///< Host side file object FILE* m_pFile = nullptr; ///< Host side file object
const char* m_pszMode = nullptr; ///< Host side file open mode const char* m_pszMode = nullptr; ///< Host side file open mode
@ -721,8 +688,8 @@ public:
void Init(); ///< Initialization (when the driver is installed) void Init(); ///< Initialization (when the driver is installed)
void Clean() const; ///< Release (when starting up or resetting) void Clean() const; ///< Release (when starting up or resetting)
CHostFcb* Alloc(DWORD nKey); CHostFcb* Alloc(uint32_t nKey);
CHostFcb* Search(DWORD nKey); CHostFcb* Search(uint32_t nKey);
void Free(CHostFcb* p); void Free(CHostFcb* p);
private: private:
@ -747,23 +714,23 @@ class CHostDrv
public: public:
CHostDrv() = default; CHostDrv() = default;
~CHostDrv(); ~CHostDrv();
CHostDrv(CHostDrv&) = delete; CHostDrv(CHostDrv&) = default;
CHostDrv& operator=(const CHostDrv&) = delete; CHostDrv& operator=(const CHostDrv&) = default;
void Init(const TCHAR* szBase, DWORD nFlag); ///< Initialization (device startup and load) void Init(const TCHAR* szBase, uint32_t nFlag); ///< Initialization (device startup and load)
bool isWriteProtect() const { return m_bWriteProtect; } bool isWriteProtect() const { return m_bWriteProtect; }
bool isEnable() const { return m_bEnable; } ///< Is it accessible? bool isEnable() const { return m_bEnable; } ///< Is it accessible?
bool isMediaOffline() const; bool isMediaOffline() const;
BYTE GetMediaByte() const; BYTE GetMediaByte() const;
DWORD GetStatus() const; uint32_t GetStatus() const;
void SetEnable(bool); ///< Set media status void SetEnable(bool); ///< Set media status
bool CheckMedia(); ///< Check if media was changed bool CheckMedia(); ///< Check if media was changed
void Update(); ///< Update media status void Update(); ///< Update media status
void Eject(); void Eject();
void GetVolume(TCHAR* szLabel); ///< Get volume label void GetVolume(TCHAR* szLabel); ///< Get volume label
bool GetVolumeCache(TCHAR* szLabel) const; ///< Get volume label from cache bool GetVolumeCache(TCHAR* szLabel) const; ///< Get volume label from cache
DWORD GetCapacity(Human68k::capacity_t* pCapacity); uint32_t GetCapacity(Human68k::capacity_t* pCapacity);
bool GetCapacityCache(Human68k::capacity_t* pCapacity) const; ///< Get capacity from cache bool GetCapacityCache(Human68k::capacity_t* pCapacity) const; ///< Get capacity from cache
// Cache operations // Cache operations
@ -789,7 +756,7 @@ private:
bool m_bWriteProtect = false; ///< TRUE if write-protected bool m_bWriteProtect = false; ///< TRUE if write-protected
bool m_bEnable = false; ///< TRUE if media is usable bool m_bEnable = false; ///< TRUE if media is usable
DWORD m_nRing = 0; ///< Number of stored path names uint32_t m_nRing = 0; ///< Number of stored path names
CRing m_cRing; ///< For attaching to CHostPath CRing m_cRing; ///< For attaching to CHostPath
Human68k::capacity_t m_capCache; ///< Sector data cache: if "sectors == 0" then not cached Human68k::capacity_t m_capCache; ///< Sector data cache: if "sectors == 0" then not cached
bool m_bVolumeCache = false; ///< TRUE if the volume label has been read bool m_bVolumeCache = false; ///< TRUE if the volume label has been read
@ -811,39 +778,39 @@ public:
CHostEntry() = default; CHostEntry() = default;
~CHostEntry(); ~CHostEntry();
CHostEntry(CHostEntry&) = delete; CHostEntry(CHostEntry&) = default;
CHostEntry& operator=(const CHostEntry&) = delete; CHostEntry& operator=(const CHostEntry&) = default;
void Init() const; ///< Initialization (when the driver is installed) void Init() const; ///< Initialization (when the driver is installed)
void Clean(); ///< Release (when starting up or resetting) void Clean(); ///< Release (when starting up or resetting)
// Cache operations // Cache operations
void CleanCache() const; ///< Update all cache void CleanCache() const; ///< Update all cache
void CleanCache(DWORD nUnit) const; ///< Update cache for the specified unit void CleanCache(uint32_t nUnit) const; ///< Update cache for the specified unit
void CleanCache(DWORD nUnit, const BYTE* szHumanPath) const; ///< Update cache for the specified path void CleanCache(uint32_t nUnit, const BYTE* szHumanPath) const; ///< Update cache for the specified path
void CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath) const; ///< Update cache below the specified path void CleanCacheChild(uint32_t nUnit, const BYTE* szHumanPath) const; ///< Update cache below the specified path
void DeleteCache(DWORD nUnit, const BYTE* szHumanPath) const; ///< Delete cache for the specified path void DeleteCache(uint32_t nUnit, const BYTE* szHumanPath) const; ///< Delete cache for the specified path
bool Find(DWORD nUnit, CHostFiles* pFiles) const; ///< Find host side name (path + file name (can be abbreviated) + attribute) bool Find(uint32_t nUnit, CHostFiles* pFiles) const; ///< Find host side name (path + file name (can be abbreviated) + attribute)
void ShellNotify(DWORD nEvent, const TCHAR* szPath); ///< Notify status change in the host side file system void ShellNotify(uint32_t nEvent, const TCHAR* szPath); ///< Notify status change in the host side file system
// Drive object operations // Drive object operations
void SetDrv(DWORD nUnit, CHostDrv* pDrv); void SetDrv(uint32_t nUnit, CHostDrv* pDrv);
bool isWriteProtect(DWORD nUnit) const; bool isWriteProtect(uint32_t nUnit) const;
bool isEnable(DWORD nUnit) const; ///< Is it accessible? bool isEnable(uint32_t nUnit) const; ///< Is it accessible?
bool isMediaOffline(DWORD nUnit) const; bool isMediaOffline(uint32_t nUnit) const;
BYTE GetMediaByte(DWORD nUnit) const; BYTE GetMediaByte(uint32_t nUnit) const;
DWORD GetStatus(DWORD nUnit) const; ///< Get drive status uint32_t GetStatus(uint32_t nUnit) const; ///< Get drive status
bool CheckMedia(DWORD nUnit) const; ///< Media change check bool CheckMedia(uint32_t nUnit) const; ///< Media change check
void Eject(DWORD nUnit) const; void Eject(uint32_t nUnit) const;
void GetVolume(DWORD nUnit, TCHAR* szLabel) const; ///< Get volume label void GetVolume(uint32_t nUnit, TCHAR* szLabel) const; ///< Get volume label
bool GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const; ///< Get volume label from cache bool GetVolumeCache(uint32_t nUnit, TCHAR* szLabel) const; ///< Get volume label from cache
DWORD GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) const; uint32_t GetCapacity(uint32_t nUnit, Human68k::capacity_t* pCapacity) const;
bool GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) const; ///< Get cluster size from cache bool GetCapacityCache(uint32_t nUnit, Human68k::capacity_t* pCapacity) const; ///< Get cluster size from cache
private: private:
CHostDrv* m_pDrv[DRIVE_MAX] = {}; ///< Host side drive object CHostDrv* m_pDrv[DRIVE_MAX] = {}; ///< Host side drive object
DWORD m_nTimeout = 0; ///< Last time a timeout check was carried out uint32_t m_nTimeout = 0; ///< Last time a timeout check was carried out
}; };
//=========================================================================== //===========================================================================
@ -854,13 +821,13 @@ private:
/** @note /** @note
Current state of affairs: Current state of affairs:
While it violates the design philosophy of XM6, we should find a way for While it violates the design philosophy of XM6, we should find a way for
'class Windrv' and 'class CWindrv' to have a direct pointer to 'class CFileSys'. 'class Windrv' and 'class CWindrv' to have a direct pointer to 'class CFileSys'.
This way, we get the following benefits. This way, we get the following benefits.
Benefit no. 1 Benefit no. 1
Makes it possible to manage a large number of command handler methods in one place. Makes it possible to manage a large number of command handler methods in one place.
There is a high chance the command handlers will change drastically because of There is a high chance the command handlers will change drastically because of
host system architectural changes, so we will save a huge amount of maintenance work host system architectural changes, so we will save a huge amount of maintenance work
in the long run. in the long run.
@ -870,7 +837,7 @@ It is not feasible to implement code in XM6 for simultaneous use of file system
Therefore file system object polymorphism is a waste of CPU cycles. Therefore file system object polymorphism is a waste of CPU cycles.
I made the change as an experiment. Performance did improve. I made the change as an experiment. Performance did improve.
The improvement was obvious from looking at the source the compiler spit out The improvement was obvious from looking at the source the compiler spit out
after changing the FILESYS_FAST_STRUCTURE value in windrv.h. after changing the FILESYS_FAST_STRUCTURE value in windrv.h.
You may understand now why I decided to rant here. You may understand now why I decided to rant here.
@ -887,71 +854,71 @@ public:
void Init(); ///< Initialization (device startup and load) void Init(); ///< Initialization (device startup and load)
// Command handlers // Command handlers
DWORD InitDevice(const Human68k::argument_t* pArgument); ///< $40 - Device startup uint32_t InitDevice(const Human68k::argument_t* pArgument); ///< $40 - Device startup
int CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests) const; ///< $41 - Directory check int CheckDir(uint32_t nUnit, const Human68k::namests_t* pNamests) const; ///< $41 - Directory check
int MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) const; ///< $42 - Create directory int MakeDir(uint32_t nUnit, const Human68k::namests_t* pNamests) const; ///< $42 - Create directory
int RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) const; ///< $43 - Delete directory int RemoveDir(uint32_t nUnit, const Human68k::namests_t* pNamests) const; ///< $43 - Delete directory
int Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew) const; int Rename(uint32_t nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew) const;
///< $44 - Change file name ///< $44 - Change file name
int Delete(DWORD nUnit, const Human68k::namests_t* pNamests) const; ///< $45 - Delete file int Delete(uint32_t nUnit, const Human68k::namests_t* pNamests) const; ///< $45 - Delete file
int Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD nHumanAttribute) const; int Attribute(uint32_t nUnit, const Human68k::namests_t* pNamests, uint32_t nHumanAttribute) const;
///< $46 - Get / set file attribute ///< $46 - Get / set file attribute
int Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles); int Files(uint32_t nUnit, uint32_t nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles);
///< $47 - Find file ///< $47 - Find file
int NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles); ///< $48 - Find next file int NFiles(uint32_t nUnit, uint32_t nKey, Human68k::files_t* pFiles); ///< $48 - Find next file
int Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, DWORD nHumanAttribute, bool bForce); int Create(uint32_t nUnit, uint32_t nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, uint32_t nHumanAttribute, bool bForce);
///< $49 - Create file ///< $49 - Create file
int Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb); int Open(uint32_t nUnit, uint32_t nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb);
///< $4A - Open file ///< $4A - Open file
int Close(DWORD nUnit, DWORD nKey, const Human68k::fcb_t* pFcb); ///< $4B - Close file int Close(uint32_t nUnit, uint32_t nKey, const Human68k::fcb_t* pFcb); ///< $4B - Close file
int Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pAddress, DWORD nSize); int Read(uint32_t nKey, Human68k::fcb_t* pFcb, BYTE* pAddress, uint32_t nSize);
///< $4C - Read file ///< $4C - Read file
int Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pAddress, DWORD nSize); int Write(uint32_t nKey, Human68k::fcb_t* pFcb, const BYTE* pAddress, uint32_t nSize);
///< $4D - Write file ///< $4D - Write file
int Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset); ///< $4E - Seek file int Seek(uint32_t nKey, Human68k::fcb_t* pFcb, uint32_t nSeek, int nOffset); ///< $4E - Seek file
DWORD TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD nHumanTime); uint32_t TimeStamp(uint32_t nUnit, uint32_t nKey, Human68k::fcb_t* pFcb, uint32_t nHumanTime);
///< $4F - Get / set file timestamp ///< $4F - Get / set file timestamp
int GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) const; ///< $50 - Get capacity int GetCapacity(uint32_t nUnit, Human68k::capacity_t* pCapacity) const; ///< $50 - Get capacity
int CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive) const; ///< $51 - Inspect / control drive status int CtrlDrive(uint32_t nUnit, Human68k::ctrldrive_t* pCtrlDrive) const; ///< $51 - Inspect / control drive status
int GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) const; ///< $52 - Get DPB int GetDPB(uint32_t nUnit, Human68k::dpb_t* pDpb) const; ///< $52 - Get DPB
int DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize); ///< $53 - Read sectors int DiskRead(uint32_t nUnit, BYTE* pBuffer, uint32_t nSector, uint32_t nSize); ///< $53 - Read sectors
int DiskWrite(DWORD nUnit) const; ///< $54 - Write sectors int DiskWrite(uint32_t nUnit) const; ///< $54 - Write sectors
int Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl); ///< $55 - IOCTRL int Ioctrl(uint32_t nUnit, uint32_t nFunction, Human68k::ioctrl_t* pIoctrl); ///< $55 - IOCTRL
int Flush(DWORD nUnit) const; ///< $56 - Flush int Flush(uint32_t nUnit) const; ///< $56 - Flush
int CheckMedia(DWORD nUnit) const; ///< $57 - Media change check int CheckMedia(uint32_t nUnit) const; ///< $57 - Media change check
int Lock(DWORD nUnit) const; ///< $58 - Lock int Lock(uint32_t nUnit) const; ///< $58 - Lock
void SetOption(DWORD nOption); ///< Set option void SetOption(uint32_t nOption); ///< Set option
DWORD GetOption() const { return m_nOption; } ///< Get option uint32_t GetOption() const { return m_nOption; } ///< Get option
DWORD GetDefault() const { return m_nOptionDefault; } ///< Get default options uint32_t GetDefault() const { return m_nOptionDefault; } ///< Get default options
static DWORD GetFileOption() { return g_nOption; } ///< Get file name change option static uint32_t GetFileOption() { return g_nOption; } ///< Get file name change option
static const int DriveMax = CHostEntry::DRIVE_MAX; ///< Max number of drive candidates static const int DriveMax = CHostEntry::DRIVE_MAX; ///< Max number of drive candidates
private: private:
void InitOption(const Human68k::argument_t* pArgument); void InitOption(const Human68k::argument_t* pArgument);
bool FilesVolume(DWORD nUnit, Human68k::files_t* pFiles) const; ///< Get volume label bool FilesVolume(uint32_t nUnit, Human68k::files_t* pFiles) const; ///< Get volume label
DWORD m_nUnits = 0; ///< Number of current drive objects (Changes for every resume) uint32_t m_nUnits = 0; ///< Number of current drive objects (Changes for every resume)
DWORD m_nOption = 0; ///< Current runtime flag uint32_t m_nOption = 0; ///< Current runtime flag
DWORD m_nOptionDefault = 0; ///< Runtime flag at reset uint32_t m_nOptionDefault = 0; ///< Runtime flag at reset
DWORD m_nDrives = 0; ///< Number of candidates for base path status restoration (scan every time if 0) uint32_t m_nDrives = 0; ///< Number of candidates for base path status restoration (scan every time if 0)
DWORD m_nKernel = 0; ///< Counter for kernel check uint32_t m_nKernel = 0; ///< Counter for kernel check
DWORD m_nKernelSearch = 0; ///< Initial address for NUL device uint32_t m_nKernelSearch = 0; ///< Initial address for NUL device
DWORD m_nHostSectorCount = 0; ///< Virtual sector identifier uint32_t m_nHostSectorCount = 0; ///< Virtual sector identifier
CHostFilesManager m_cFiles; ///< File search memory CHostFilesManager m_cFiles; ///< File search memory
CHostFcbManager m_cFcb; ///< FCB operation memory CHostFcbManager m_cFcb; ///< FCB operation memory
CHostEntry m_cEntry; ///< Drive object and directory entry CHostEntry m_cEntry; ///< Drive object and directory entry
DWORD m_nHostSectorBuffer[XM6_HOST_PSEUDO_CLUSTER_MAX]; uint32_t m_nHostSectorBuffer[XM6_HOST_PSEUDO_CLUSTER_MAX];
///< Entity that the virtual sector points to ///< Entity that the virtual sector points to
DWORD m_nFlag[DriveMax] = {}; ///< Candidate runtime flag for base path restoration uint32_t m_nFlag[DriveMax] = {}; ///< Candidate runtime flag for base path restoration
TCHAR m_szBase[DriveMax][FILEPATH_MAX] = {}; ///< Candidate for base path restoration TCHAR m_szBase[DriveMax][FILEPATH_MAX] = {}; ///< Candidate for base path restoration
static DWORD g_nOption; ///< File name change flag static uint32_t g_nOption; ///< File name change flag
}; };

View File

@ -37,7 +37,7 @@ using namespace ras_util;
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
static bool br_setif(int br_socket_fd, const char* bridgename, const char* ifname, bool add) { static bool br_setif(int br_socket_fd, const char* bridgename, const char* ifname, bool add) {
#ifndef __linux #ifndef __linux__
return false; return false;
#else #else
ifreq ifr; ifreq ifr;
@ -83,7 +83,7 @@ CTapDriver::~CTapDriver()
} }
static bool ip_link(int fd, const char* ifname, bool up) { static bool ip_link(int fd, const char* ifname, bool up) {
#ifndef __linux #ifndef __linux__
return false; return false;
#else #else
ifreq ifr; ifreq ifr;
@ -126,7 +126,7 @@ static bool is_interface_up(string_view interface) {
bool CTapDriver::Init(const unordered_map<string, string>& const_params) bool CTapDriver::Init(const unordered_map<string, string>& const_params)
{ {
#ifndef __linux #ifndef __linux__
return false; return false;
#else #else
unordered_map<string, string> params = const_params; unordered_map<string, string> params = const_params;
@ -158,7 +158,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
} }
LOGTRACE("Opened tap device %d", m_hTAP) LOGTRACE("Opened tap device %d", m_hTAP)
// IFF_NO_PI for no extra packet information // IFF_NO_PI for no extra packet information
ifreq ifr = {}; ifreq ifr = {};
ifr.ifr_flags = IFF_TAP | IFF_NO_PI; ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
@ -177,7 +177,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
LOGTRACE("Return code from ioctl was %d", ret) LOGTRACE("Return code from ioctl was %d", ret)
int ip_fd = socket(PF_INET, SOCK_DGRAM, 0); const int ip_fd = socket(PF_INET, SOCK_DGRAM, 0);
if (ip_fd < 0) { if (ip_fd < 0) {
LOGERROR("Can't open ip socket: %s", strerror(errno)) LOGERROR("Can't open ip socket: %s", strerror(errno))
@ -185,7 +185,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
return false; return false;
} }
int br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0); const int br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (br_socket_fd < 0) { if (br_socket_fd < 0) {
LOGERROR("Can't open bridge socket: %s", strerror(errno)) LOGERROR("Can't open bridge socket: %s", strerror(errno))
@ -245,7 +245,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
} }
else { else {
string address = inet; string address = inet;
string netmask = "255.255.255.0"; string netmask = "255.255.255.0"; //NOSONAR This hardcoded IP address is safe
if (size_t separatorPos = inet.find('/'); separatorPos != string::npos) { if (size_t separatorPos = inet.find('/'); separatorPos != string::npos) {
address = inet.substr(0, separatorPos); address = inet.substr(0, separatorPos);
@ -260,7 +260,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
} }
// long long is required for compatibility with 32 bit platforms // long long is required for compatibility with 32 bit platforms
auto mask = (long long)(pow(2, 32) - (1 << (32 - m))); const auto mask = (long long)(pow(2, 32) - (1 << (32 - m)));
netmask = to_string((mask >> 24) & 0xff) + '.' + to_string((mask >> 16) & 0xff) + '.' + netmask = to_string((mask >> 24) & 0xff) + '.' + to_string((mask >> 16) & 0xff) + '.' +
to_string((mask >> 8) & 0xff) + '.' + to_string(mask & 0xff); to_string((mask >> 8) & 0xff) + '.' + to_string(mask & 0xff);
@ -391,18 +391,18 @@ void CTapDriver::OpenDump(const Filepath& path) {
bool CTapDriver::Enable() const bool CTapDriver::Enable() const
{ {
int fd = socket(PF_INET, SOCK_DGRAM, 0); const int fd = socket(PF_INET, SOCK_DGRAM, 0);
LOGDEBUG("%s: ip link set ras0 up", __PRETTY_FUNCTION__) LOGDEBUG("%s: ip link set ras0 up", __PRETTY_FUNCTION__)
bool result = ip_link(fd, "ras0", true); const bool result = ip_link(fd, "ras0", true);
close(fd); close(fd);
return result; return result;
} }
bool CTapDriver::Disable() const bool CTapDriver::Disable() const
{ {
int fd = socket(PF_INET, SOCK_DGRAM, 0); const int fd = socket(PF_INET, SOCK_DGRAM, 0);
LOGDEBUG("%s: ip link set ras0 down", __PRETTY_FUNCTION__) LOGDEBUG("%s: ip link set ras0 down", __PRETTY_FUNCTION__)
bool result = ip_link(fd, "ras0", false); const bool result = ip_link(fd, "ras0", false);
close(fd); close(fd);
return result; return result;
} }
@ -447,12 +447,12 @@ bool CTapDriver::PendingPackets() const
} }
// See https://stackoverflow.com/questions/21001659/crc32-algorithm-implementation-in-c-without-a-look-up-table-and-with-a-public-li // See https://stackoverflow.com/questions/21001659/crc32-algorithm-implementation-in-c-without-a-look-up-table-and-with-a-public-li
uint32_t crc32(const BYTE *buf, int length) { uint32_t CTapDriver::Crc32(const BYTE *buf, int length) {
uint32_t crc = 0xffffffff; uint32_t crc = 0xffffffff;
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
crc ^= buf[i]; crc ^= buf[i];
for (int j = 0; j < 8; j++) { for (int j = 0; j < 8; j++) {
uint32_t mask = -(crc & 1); const uint32_t mask = -((int)crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask); crc = (crc >> 1) ^ (0xEDB88320 & mask);
} }
} }
@ -469,8 +469,8 @@ int CTapDriver::Receive(BYTE *buf)
} }
// Receive // Receive
auto dwReceived = (DWORD)read(m_hTAP, buf, ETH_FRAME_LEN); auto dwReceived = (uint32_t)read(m_hTAP, buf, ETH_FRAME_LEN);
if (dwReceived == (DWORD)-1) { if (dwReceived == (uint32_t)-1) {
LOGWARN("%s Error occured while receiving a packet", __PRETTY_FUNCTION__) LOGWARN("%s Error occured while receiving a packet", __PRETTY_FUNCTION__)
return 0; return 0;
} }
@ -480,7 +480,7 @@ int CTapDriver::Receive(BYTE *buf)
// We need to add the Frame Check Status (FCS) CRC back onto the end of the packet. // We need to add the Frame Check Status (FCS) CRC back onto the end of the packet.
// The Linux network subsystem removes it, since most software apps shouldn't ever // The Linux network subsystem removes it, since most software apps shouldn't ever
// need it. // need it.
int crc = crc32(buf, dwReceived); const int crc = Crc32(buf, dwReceived);
buf[dwReceived + 0] = (BYTE)((crc >> 0) & 0xFF); buf[dwReceived + 0] = (BYTE)((crc >> 0) & 0xFF);
buf[dwReceived + 1] = (BYTE)((crc >> 8) & 0xFF); buf[dwReceived + 1] = (BYTE)((crc >> 8) & 0xFF);

View File

@ -19,24 +19,20 @@
#include <string> #include <string>
#include <array> #include <array>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class CTapDriver class CTapDriver
{ {
friend class SCSIDaynaPort;
friend class SCSIBR;
static constexpr const char *BRIDGE_NAME = "rascsi_bridge"; static constexpr const char *BRIDGE_NAME = "rascsi_bridge";
CTapDriver() = default;
~CTapDriver();
CTapDriver(CTapDriver&) = delete;
CTapDriver& operator=(const CTapDriver&) = delete;
bool Init(const unordered_map<string, string>&);
public: public:
CTapDriver() = default;
~CTapDriver();
CTapDriver(CTapDriver&) = default;
CTapDriver& operator=(const CTapDriver&) = default;
bool Init(const unordered_map<string, string>&);
void OpenDump(const Filepath& path); // Capture packets void OpenDump(const Filepath& path); // Capture packets
void GetMacAddr(BYTE *mac) const; void GetMacAddr(BYTE *mac) const;
int Receive(BYTE *buf); int Receive(BYTE *buf);
@ -46,6 +42,8 @@ public:
bool Disable() const; // Disable the ras0 interface bool Disable() const; // Disable the ras0 interface
void Flush(); // Purge all of the packets that are waiting to be processed void Flush(); // Purge all of the packets that are waiting to be processed
static uint32_t Crc32(const BYTE *, int);
private: private:
array<byte, 6> m_MacAddr; // MAC Address array<byte, 6> m_MacAddr; // MAC Address

View File

@ -7,17 +7,16 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include <cassert>
#include "rascsi_version.h" #include "rascsi_version.h"
#include "log.h" #include "log.h"
#include "rascsi_exceptions.h"
#include "device.h" #include "device.h"
#include <cassert>
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
using namespace std; using namespace std;
Device::Device(const string& t) : type(t) Device::Device(const string& type, int lun) : type(type), lun(lun)
{ {
assert(type.length() == 4); assert(type.length() == 4);
@ -43,7 +42,7 @@ void Device::SetProtected(bool b)
void Device::SetVendor(const string& v) void Device::SetVendor(const string& v)
{ {
if (v.empty() || v.length() > 8) { if (v.empty() || v.length() > 8) {
throw illegal_argument_exception("Vendor '" + v + "' must be between 1 and 8 characters"); throw invalid_argument("Vendor '" + v + "' must be between 1 and 8 characters");
} }
vendor = v; vendor = v;
@ -52,10 +51,10 @@ void Device::SetVendor(const string& v)
void Device::SetProduct(const string& p, bool force) void Device::SetProduct(const string& p, bool force)
{ {
if (p.empty() || p.length() > 16) { if (p.empty() || p.length() > 16) {
throw illegal_argument_exception("Product '" + p + "' must be between 1 and 16 characters"); throw invalid_argument("Product '" + p + "' must be between 1 and 16 characters");
} }
// Changing the device name is not SCSI compliant // Changing vital product data is not SCSI compliant
if (!product.empty() && !force) { if (!product.empty() && !force) {
return; return;
} }
@ -66,7 +65,7 @@ void Device::SetProduct(const string& p, bool force)
void Device::SetRevision(const string& r) void Device::SetRevision(const string& r)
{ {
if (r.empty() || r.length() > 4) { if (r.empty() || r.length() > 4) {
throw illegal_argument_exception("Revision '" + r + "' must be between 1 and 4 characters"); throw invalid_argument("Revision '" + r + "' must be between 1 and 4 characters");
} }
revision = r; revision = r;
@ -74,13 +73,10 @@ void Device::SetRevision(const string& r)
string Device::GetPaddedName() const string Device::GetPaddedName() const
{ {
string name = vendor; ostringstream os;
name.append(8 - vendor.length(), ' '); os << left << setfill(' ') << setw(8) << vendor << setw(16) << product << setw(4) << revision;
name += product;
name.append(16 - product.length(), ' ');
name += revision;
name.append(4 - revision.length(), ' ');
const string name = os.str();
assert(name.length() == 28); assert(name.length() == 28);
return name; return name;
@ -107,15 +103,6 @@ void Device::SetParams(const unordered_map<string, string>& set_params)
} }
} }
void Device::SetStatusCode(int s)
{
if (s) {
LOGDEBUG("Error status: Sense Key $%02X, ASC $%02X, ASCQ $%02X", s >> 16, (s >> 8 &0xff), s & 0xff)
}
status_code = s;
}
bool Device::Start() bool Device::Start()
{ {
if (!ready) { if (!ready) {

View File

@ -9,16 +9,13 @@
#pragma once #pragma once
#include "scsi.h"
#include <unordered_map> #include <unordered_map>
#include <string> #include <string>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class Device class Device //NOSONAR The number of fields and methods is justified, the complexity is low
{ {
friend class DeviceFactory;
const string DEFAULT_VENDOR = "RaSCSI"; const string DEFAULT_VENDOR = "RaSCSI";
string type; string type;
@ -45,15 +42,11 @@ class Device
bool lockable = false; bool lockable = false;
bool locked = false; bool locked = false;
// The block size is configurable
bool block_size_configurable = false;
// Device can be created with parameters // Device can be created with parameters
bool supports_params = false; bool supports_params = false;
// Device ID and LUN // Immutable LUN
int32_t id = 0; int lun;
int32_t lun = 0;
// Device identifier (for INQUIRY) // Device identifier (for INQUIRY)
string vendor = DEFAULT_VENDOR; string vendor = DEFAULT_VENDOR;
@ -86,21 +79,16 @@ protected:
string GetParam(const string&) const; string GetParam(const string&) const;
void SetParams(const unordered_map<string, string>&); void SetParams(const unordered_map<string, string>&);
explicit Device(const string&); Device(const string&, int);
public: public:
virtual ~Device() = default; virtual ~Device() = default;
Device(Device&) = delete;
Device& operator=(const Device&) = delete;
// Override for device specific initializations, to be called after all device properties have been set
virtual bool Init(const unordered_map<string, string>&) { return true; };
const string& GetType() const { return type; } const string& GetType() const { return type; }
bool IsReady() const { return ready; } bool IsReady() const { return ready; }
void Reset(); virtual void Reset();
bool IsProtectable() const { return protectable; } bool IsProtectable() const { return protectable; }
void SetProtectable(bool b) { protectable = b; } void SetProtectable(bool b) { protectable = b; }
@ -123,10 +111,8 @@ public:
bool IsLocked() const { return locked; } bool IsLocked() const { return locked; }
void SetLocked(bool b) { locked = b; } void SetLocked(bool b) { locked = b; }
int32_t GetId() const { return id; } virtual int GetId() const = 0;
void SetId(int32_t i) { id = i; } int GetLun() const { return lun; }
int32_t GetLun() const { return lun; }
void SetLun(int32_t l) { lun = l; }
string GetVendor() const { return vendor; } string GetVendor() const { return vendor; }
void SetVendor(const string&); void SetVendor(const string&);
@ -142,12 +128,9 @@ public:
unordered_map<string, string> GetParams() const { return params; } unordered_map<string, string> GetParams() const { return params; }
void SetDefaultParams(const unordered_map<string, string>& p) { default_params = p; } void SetDefaultParams(const unordered_map<string, string>& p) { default_params = p; }
void SetStatusCode(int); void SetStatusCode(int s) { status_code = s; }
bool Start(); bool Start();
void Stop(); void Stop();
virtual bool Eject(bool); virtual bool Eject(bool);
virtual void FlushCache() {
// Devices with a cache have to implement this method
}
}; };

View File

@ -25,8 +25,6 @@
using namespace std; using namespace std;
using namespace rascsi_interface; using namespace rascsi_interface;
multimap<int, unique_ptr<PrimaryDevice>> DeviceFactory::devices;
DeviceFactory::DeviceFactory() DeviceFactory::DeviceFactory()
{ {
sector_sizes[SCHD] = { 512, 1024, 2048, 4096 }; sector_sizes[SCHD] = { 512, 1024, 2048, 4096 };
@ -43,9 +41,9 @@ DeviceFactory::DeviceFactory()
} }
default_params[SCBR]["interface"] = network_interfaces; default_params[SCBR]["interface"] = network_interfaces;
default_params[SCBR]["inet"] = "10.10.20.1/24"; default_params[SCBR]["inet"] = DEFAULT_IP;
default_params[SCDP]["interface"] = network_interfaces; default_params[SCDP]["interface"] = network_interfaces;
default_params[SCDP]["inet"] = "10.10.20.1/24"; default_params[SCDP]["inet"] = DEFAULT_IP;
default_params[SCLP]["cmd"] = "lp -oraw %f"; default_params[SCLP]["cmd"] = "lp -oraw %f";
default_params[SCLP]["timeout"] = "30"; default_params[SCLP]["timeout"] = "30";
@ -58,51 +56,17 @@ DeviceFactory::DeviceFactory()
extension_mapping["hdr"] = SCRM; extension_mapping["hdr"] = SCRM;
extension_mapping["mos"] = SCMO; extension_mapping["mos"] = SCMO;
extension_mapping["iso"] = SCCD; extension_mapping["iso"] = SCCD;
}
void DeviceFactory::DeleteDevice(const PrimaryDevice& device) const device_mapping["bridge"] = SCBR;
{ device_mapping["daynaport"] = SCDP;
auto [begin, end] = devices.equal_range(device.GetId()); device_mapping["printer"] = SCLP;
for (auto& it = begin; it != end; ++it) { device_mapping["services"] = SCHS;
if (it->second->GetLun() == device.GetLun()) {
devices.erase(it);
break;
}
}
}
void DeviceFactory::DeleteAllDevices() const
{
devices.clear();
}
const PrimaryDevice *DeviceFactory::GetDeviceByIdAndLun(int i, int lun) const
{
for (const auto& [id, device] : devices) {
if (device->GetId() == i && device->GetLun() == lun) {
return device.get();
}
}
return nullptr;
}
list<PrimaryDevice *> DeviceFactory::GetAllDevices() const
{
list<PrimaryDevice *> result;
for (const auto& [id, device] : devices) {
result.push_back(device.get());
}
return result;
} }
string DeviceFactory::GetExtension(const string& filename) const string DeviceFactory::GetExtension(const string& filename) const
{ {
string ext; string ext;
if (size_t separator = filename.rfind('.'); separator != string::npos) { if (const size_t separator = filename.rfind('.'); separator != string::npos) {
ext = filename.substr(separator + 1); ext = filename.substr(separator + 1);
} }
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){ return std::tolower(c); }); std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){ return std::tolower(c); });
@ -115,24 +79,17 @@ 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(GetExtension(filename)); it != extension_mapping.end()) {
return it->second; return it->second;
} }
else if (filename == "bridge") {
return SCBR; if (const auto& it = device_mapping.find(filename); it != device_mapping.end()) {
} return it->second;
else if (filename == "daynaport") {
return SCDP;
}
else if (filename == "printer") {
return SCLP;
}
else if (filename == "services") {
return SCHS;
} }
return UNDEFINED; return UNDEFINED;
} }
// ID -1 is used by rascsi to create a temporary device // ID -1 is used by rascsi to create a temporary device
PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename, int id) shared_ptr<PrimaryDevice> DeviceFactory::CreateDevice(const ControllerManager& controller_manager, PbDeviceType type,
int lun, const string& filename)
{ {
// If no type was specified try to derive the device type from the filename // If no type was specified try to derive the device type from the filename
if (type == UNDEFINED) { if (type == UNDEFINED) {
@ -142,13 +99,14 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
} }
} }
unique_ptr<PrimaryDevice> device; shared_ptr<PrimaryDevice> device;
switch (type) { switch (type) {
case SCHD: { case SCHD: {
if (string ext = GetExtension(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") { if (const string ext = GetExtension(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") {
device = make_unique<SCSIHD_NEC>(); device = make_shared<SCSIHD_NEC>(lun);
} else { } else {
device = make_unique<SCSIHD>(sector_sizes[SCHD], false, ext == "hd1" ? scsi_level::SCSI_1_CCS : scsi_level::SCSI_2); device = make_shared<SCSIHD>(lun, sector_sizes[SCHD], false,
ext == "hd1" ? scsi_level::SCSI_1_CCS : scsi_level::SCSI_2);
// Some Apple tools require a particular drive identification // Some Apple tools require a particular drive identification
if (ext == "hda") { if (ext == "hda") {
@ -162,7 +120,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
} }
case SCRM: case SCRM:
device = make_unique<SCSIHD>(sector_sizes[SCRM], true); device = make_shared<SCSIHD>(lun, sector_sizes[SCRM], true);
device->SetProtectable(true); device->SetProtectable(true);
device->SetStoppable(true); device->SetStoppable(true);
device->SetRemovable(true); device->SetRemovable(true);
@ -171,7 +129,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
break; break;
case SCMO: case SCMO:
device = make_unique<SCSIMO>(sector_sizes[SCMO]); device = make_shared<SCSIMO>(lun, sector_sizes[SCMO]);
device->SetProtectable(true); device->SetProtectable(true);
device->SetStoppable(true); device->SetStoppable(true);
device->SetRemovable(true); device->SetRemovable(true);
@ -180,7 +138,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
break; break;
case SCCD: case SCCD:
device = make_unique<SCSICD>(sector_sizes[SCCD]); device = make_shared<SCSICD>(lun, sector_sizes[SCCD]);
device->SetReadOnly(true); device->SetReadOnly(true);
device->SetStoppable(true); device->SetStoppable(true);
device->SetRemovable(true); device->SetRemovable(true);
@ -189,14 +147,15 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
break; break;
case SCBR: case SCBR:
device = make_unique<SCSIBR>(); 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->SetProduct("RASCSI BRIDGE");
device->SupportsParams(true); device->SupportsParams(true);
device->SetDefaultParams(default_params[SCBR]); device->SetDefaultParams(default_params[SCBR]);
break; break;
case SCDP: case SCDP:
device = make_unique<SCSIDaynaPort>(); device = make_shared<SCSIDaynaPort>(lun);
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly // Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
device->SetVendor("Dayna"); device->SetVendor("Dayna");
device->SetProduct("SCSI/Link"); device->SetProduct("SCSI/Link");
@ -206,14 +165,14 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
break; break;
case SCHS: case SCHS:
device = make_unique<HostServices>(*this); device = make_shared<HostServices>(lun, controller_manager);
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly // Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
device->SetVendor("RaSCSI"); device->SetVendor("RaSCSI");
device->SetProduct("Host Services"); device->SetProduct("Host Services");
break; break;
case SCLP: case SCLP:
device = make_unique<SCSIPrinter>(); device = make_shared<SCSIPrinter>(lun);
device->SetProduct("SCSI PRINTER"); device->SetProduct("SCSI PRINTER");
device->SupportsParams(true); device->SupportsParams(true);
device->SetDefaultParams(default_params[SCLP]); device->SetDefaultParams(default_params[SCLP]);
@ -223,17 +182,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
break; break;
} }
if (device != nullptr) { return device;
PrimaryDevice *d = device.release();
d->SetId(id);
devices.emplace(id, d);
return d;
}
return nullptr;
} }
const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(PbDeviceType type) const const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(PbDeviceType type) const
@ -260,7 +209,7 @@ list<string> DeviceFactory::GetNetworkInterfaces() const
{ {
list<string> network_interfaces; list<string> network_interfaces;
#ifdef __linux #ifdef __linux__
ifaddrs *addrs; ifaddrs *addrs;
getifaddrs(&addrs); getifaddrs(&addrs);
ifaddrs *tmp = addrs; ifaddrs *tmp = addrs;
@ -268,10 +217,10 @@ list<string> DeviceFactory::GetNetworkInterfaces() const
while (tmp) { while (tmp) {
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET && if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET &&
strcmp(tmp->ifa_name, "lo") && strcmp(tmp->ifa_name, "rascsi_bridge")) { strcmp(tmp->ifa_name, "lo") && strcmp(tmp->ifa_name, "rascsi_bridge")) {
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); const int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
ifreq ifr = {}; ifreq ifr = {};
strcpy(ifr.ifr_name, tmp->ifa_name); strcpy(ifr.ifr_name, tmp->ifa_name); //NOSONAR Using strcpy is safe here
// Only list interfaces that are up // Only list interfaces that are up
if (!ioctl(fd, SIOCGIFFLAGS, &ifr) && (ifr.ifr_flags & IFF_UP)) { if (!ioctl(fd, SIOCGIFFLAGS, &ifr) && (ifr.ifr_flags & IFF_UP)) {
network_interfaces.emplace_back(tmp->ifa_name); network_interfaces.emplace_back(tmp->ifa_name);

View File

@ -11,50 +11,46 @@
#pragma once #pragma once
#include <string>
#include <list> #include <list>
#include <vector>
#include <unordered_set> #include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <string>
#include "rascsi_interface.pb.h" #include "rascsi_interface.pb.h"
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi using namespace rascsi_interface;
class ControllerManager;
class PrimaryDevice; class PrimaryDevice;
class DeviceFactory class DeviceFactory
{ {
const string DEFAULT_IP = "10.10.20.1/24"; //NOSONAR This hardcoded IP address is safe
public: public:
DeviceFactory(); DeviceFactory();
~DeviceFactory() = default; ~DeviceFactory() = default;
DeviceFactory(DeviceFactory&) = delete;
DeviceFactory& operator=(const DeviceFactory&) = delete;
PrimaryDevice *CreateDevice(PbDeviceType, const string&, int); shared_ptr<PrimaryDevice> CreateDevice(const ControllerManager&, PbDeviceType, int, const string&);
void DeleteDevice(const PrimaryDevice&) const;
void DeleteAllDevices() const;
const PrimaryDevice *GetDeviceByIdAndLun(int, int) const;
list<PrimaryDevice *> GetAllDevices() const;
PbDeviceType GetTypeForFile(const string&) const; PbDeviceType GetTypeForFile(const string&) const;
const unordered_set<uint32_t>& GetSectorSizes(PbDeviceType type) const; const unordered_set<uint32_t>& GetSectorSizes(PbDeviceType type) const;
const unordered_set<uint32_t>& GetSectorSizes(const string&) const; const unordered_set<uint32_t>& GetSectorSizes(const string&) const;
const unordered_map<string, string>& GetDefaultParams(PbDeviceType type) const; const unordered_map<string, string>& GetDefaultParams(PbDeviceType type) const;
list<string> GetNetworkInterfaces() const; list<string> GetNetworkInterfaces() const;
unordered_map<string, PbDeviceType> GetExtensionMapping() const { return extension_mapping; } const unordered_map<string, PbDeviceType>& GetExtensionMapping() const { return extension_mapping; }
private: private:
string GetExtension(const string&) const;
unordered_map<PbDeviceType, unordered_set<uint32_t>> sector_sizes; unordered_map<PbDeviceType, unordered_set<uint32_t>> sector_sizes;
unordered_map<PbDeviceType, unordered_map<string, string>> default_params; unordered_map<PbDeviceType, unordered_map<string, string>> default_params;
unordered_map<string, PbDeviceType> extension_mapping; unordered_map<string, PbDeviceType> extension_mapping;
string GetExtension(const string&) const; unordered_map<string, PbDeviceType> device_mapping;
static std::multimap<int, unique_ptr<PrimaryDevice>> devices;
unordered_set<uint32_t> empty_set; unordered_set<uint32_t> empty_set;
unordered_map<string, string> empty_map; unordered_map<string, string> empty_map;

View File

@ -14,9 +14,7 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "os.h"
#include "fileio.h" #include "fileio.h"
#include "file_support.h"
#include "rascsi_exceptions.h" #include "rascsi_exceptions.h"
#include "dispatcher.h" #include "dispatcher.h"
#include "scsi_command_util.h" #include "scsi_command_util.h"
@ -25,7 +23,9 @@
using namespace scsi_defs; using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
Disk::Disk(const string& id) : ModePageDevice(id) unordered_map<string, id_set> Disk::reserved_files;
Disk::Disk(const string& type, int lun) : ModePageDevice(type, lun)
{ {
dispatcher.Add(scsi_command::eCmdRezero, "Rezero", &Disk::Rezero); dispatcher.Add(scsi_command::eCmdRezero, "Rezero", &Disk::Rezero);
dispatcher.Add(scsi_command::eCmdFormat, "FormatUnit", &Disk::FormatUnit); dispatcher.Add(scsi_command::eCmdFormat, "FormatUnit", &Disk::FormatUnit);
@ -73,7 +73,7 @@ bool Disk::Dispatch(scsi_command cmd)
is_medium_changed = false; is_medium_changed = false;
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE); throw scsi_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
} }
// The superclass handles the less specific commands // The superclass handles the less specific commands
@ -88,7 +88,9 @@ bool Disk::Dispatch(scsi_command cmd)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void Disk::Open(const Filepath& path) void Disk::Open(const Filepath& path)
{ {
assert(blocks > 0); if (blocks == 0) {
throw io_exception("Disk has 0 blocks");
}
SetReady(true); SetReady(true);
@ -108,10 +110,17 @@ void Disk::Open(const Filepath& path)
SetLocked(false); SetLocked(false);
} }
void Disk::SetUpCache(const Filepath& path, off_t image_offset) void Disk::SetUpCache(const Filepath& path, off_t image_offset, bool raw)
{ {
assert(cache == nullptr); assert(cache == nullptr);
cache = make_unique<DiskCache>(path, size_shift_count, (uint32_t)blocks, image_offset); cache = make_unique<DiskCache>(path, size_shift_count, (uint32_t)blocks, image_offset);
cache->SetRawMode(raw);
}
void Disk::ResizeCache(const Filepath& path, bool raw)
{
cache.reset(new DiskCache(path, GetSectorSizeShiftCount(), (uint32_t)blocks));
cache->SetRawMode(raw);
} }
void Disk::FlushCache() void Disk::FlushCache()
@ -132,7 +141,7 @@ void Disk::FormatUnit()
// FMTDATA=1 is not supported (but OK if there is no DEFECT LIST) // FMTDATA=1 is not supported (but OK if there is no DEFECT LIST)
if ((ctrl->cmd[1] & 0x10) != 0 && ctrl->cmd[4] != 0) { if ((ctrl->cmd[1] & 0x10) != 0 && ctrl->cmd[4] != 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
EnterStatusPhase(); EnterStatusPhase();
@ -178,7 +187,7 @@ void Disk::ReadWriteLong10()
{ {
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard // Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
if (GetInt16(ctrl->cmd, 7) != 0) { if (GetInt16(ctrl->cmd, 7) != 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
ValidateBlockAddress(RW10); ValidateBlockAddress(RW10);
@ -190,7 +199,7 @@ void Disk::ReadWriteLong16()
{ {
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard // Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
if (GetInt16(ctrl->cmd, 12) != 0) { if (GetInt16(ctrl->cmd, 12) != 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
ValidateBlockAddress(RW16); ValidateBlockAddress(RW16);
@ -262,14 +271,14 @@ void Disk::Verify16()
void Disk::StartStopUnit() void Disk::StartStopUnit()
{ {
bool start = ctrl->cmd[4] & 0x01; const bool start = ctrl->cmd[4] & 0x01;
bool load = ctrl->cmd[4] & 0x02; const bool load = ctrl->cmd[4] & 0x02;
if (load) { if (load) {
LOGTRACE("%s", start ? "Loading medium" : "Ejecting medium") LOGTRACE(start ? "Loading medium" : "Ejecting medium")
} }
else { else {
LOGTRACE("%s", start ? "Starting unit" : "Stopping unit") LOGTRACE(start ? "Starting unit" : "Stopping unit")
SetStopped(!start); SetStopped(!start);
} }
@ -281,12 +290,12 @@ void Disk::StartStopUnit()
if (load) { if (load) {
if (IsLocked()) { if (IsLocked()) {
// Cannot be ejected because it is locked // Cannot be ejected because it is locked
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
} }
// Eject // Eject
if (!Eject(false)) { if (!Eject(false)) {
throw scsi_error_exception(); throw scsi_exception();
} }
} }
} }
@ -298,12 +307,12 @@ void Disk::SendDiagnostic()
{ {
// Do not support PF bit // Do not support PF bit
if (ctrl->cmd[1] & 0x10) { if (ctrl->cmd[1] & 0x10) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
// Do not support parameter list // Do not support parameter list
if ((ctrl->cmd[3] != 0) || (ctrl->cmd[4] != 0)) { if ((ctrl->cmd[3] != 0) || (ctrl->cmd[4] != 0)) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
EnterStatusPhase(); EnterStatusPhase();
@ -313,9 +322,9 @@ void Disk::PreventAllowMediumRemoval()
{ {
CheckReady(); CheckReady();
bool lock = ctrl->cmd[4] & 0x01; const bool lock = ctrl->cmd[4] & 0x01;
LOGTRACE("%s", lock ? "Locking medium" : "Unlocking medium") LOGTRACE(lock ? "Locking medium" : "Unlocking medium")
SetLocked(lock); SetLocked(lock);
@ -331,7 +340,7 @@ void Disk::SynchronizeCache()
void Disk::ReadDefectData10() void Disk::ReadDefectData10()
{ {
size_t allocation_length = min((size_t)GetInt16(ctrl->cmd, 7), (size_t)4); const size_t allocation_length = min((size_t)GetInt16(ctrl->cmd, 7), (size_t)4);
// The defect list is empty // The defect list is empty
fill_n(controller->GetBuffer().begin(), allocation_length, 0); fill_n(controller->GetBuffer().begin(), allocation_length, 0);
@ -346,30 +355,28 @@ void Disk::MediumChanged()
is_medium_changed = true; is_medium_changed = true;
} }
else { else {
LOGWARN("%s Medium change requested for non-reomvable medium", __PRETTY_FUNCTION__) LOGERROR("Medium change requested for non-removable medium")
} }
} }
bool Disk::Eject(bool force) bool Disk::Eject(bool force)
{ {
bool status = super::Eject(force); const bool status = super::Eject(force);
if (status) { if (status) {
FlushCache(); FlushCache();
cache.reset(); cache.reset();
// The image file for this drive is not in use anymore // The image file for this drive is not in use anymore
if (auto file_support = dynamic_cast<FileSupport *>(this); file_support) { UnreserveFile();
file_support->UnreserveFile();
}
} }
return status; return status;
} }
int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_length) const int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf) const
{ {
// Get length, clear buffer // Get length, clear buffer
auto length = (int)min((size_t)max_length, (size_t)cdb[4]); const auto length = (int)min(buf.size(), (size_t)cdb[4]);
fill_n(buf.begin(), length, 0); fill_n(buf.begin(), length, 0);
// DEVICE SPECIFIC PARAMETER // DEVICE SPECIFIC PARAMETER
@ -378,7 +385,7 @@ int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
} }
// Basic information // Basic information
int info_size = 4; int size = 4;
// Add block descriptor if DBD is 0 // Add block descriptor if DBD is 0
if ((cdb[1] & 0x08) == 0) { if ((cdb[1] & 0x08) == 0) {
@ -388,33 +395,33 @@ int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
// Only if ready // Only if ready
if (IsReady()) { if (IsReady()) {
// Short LBA mode parameter block descriptor (number of blocks and block length) // Short LBA mode parameter block descriptor (number of blocks and block length)
SetInt32(buf, 4, (uint32_t)GetBlockCount()); SetInt32(buf, 4, (uint32_t)blocks);
SetInt32(buf, 8, GetSectorSizeInBytes()); SetInt32(buf, 8, GetSectorSizeInBytes());
} }
info_size = 12; size = 12;
} }
info_size += super::AddModePages(cdb, buf, info_size, length - info_size); size += super::AddModePages(cdb, buf, size, length - size);
if (info_size > 255) { if (size > 255) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
// Do not return more than ALLOCATION LENGTH bytes // Do not return more than ALLOCATION LENGTH bytes
if (info_size > length) { if (size > length) {
info_size = length; size = length;
} }
// Final setting of mode data length // Final setting of mode data length
buf[0] = (BYTE)info_size; buf[0] = (BYTE)size;
return info_size; return size;
} }
int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length) const int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf) const
{ {
// Get length, clear buffer // Get length, clear buffer
auto length = (int)min((size_t)max_length, (size_t)GetInt16(cdb, 7)); const auto length = (int)min(buf.size(), (size_t)GetInt16(cdb, 7));
fill_n(buf.begin(), length, 0); fill_n(buf.begin(), length, 0);
// DEVICE SPECIFIC PARAMETER // DEVICE SPECIFIC PARAMETER
@ -423,11 +430,11 @@ int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
} }
// Basic Information // Basic Information
int info_size = 8; int size = 8;
// Add block descriptor if DBD is 0, only if ready // Add block descriptor if DBD is 0, only if ready
if ((cdb[1] & 0x08) == 0 && IsReady()) { if ((cdb[1] & 0x08) == 0 && IsReady()) {
uint64_t disk_blocks = GetBlockCount(); uint64_t disk_blocks = blocks;
uint32_t disk_size = GetSectorSizeInBytes(); uint32_t disk_size = GetSectorSizeInBytes();
// Check LLBAA for short or long block descriptor // Check LLBAA for short or long block descriptor
@ -439,7 +446,7 @@ int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
SetInt32(buf, 8, (uint32_t)disk_blocks); SetInt32(buf, 8, (uint32_t)disk_blocks);
SetInt32(buf, 12, disk_size); SetInt32(buf, 12, disk_size);
info_size = 16; size = 16;
} }
else { else {
// Mode parameter header, LONGLBA // Mode parameter header, LONGLBA
@ -452,24 +459,24 @@ int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
SetInt64(buf, 8, disk_blocks); SetInt64(buf, 8, disk_blocks);
SetInt32(buf, 20, disk_size); SetInt32(buf, 20, disk_size);
info_size = 24; size = 24;
} }
} }
info_size += super::AddModePages(cdb, buf, info_size, length - info_size); size += super::AddModePages(cdb, buf, size, length - size);
if (info_size > 65535) { if (size > 65535) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
// Do not return more than ALLOCATION LENGTH bytes // Do not return more than ALLOCATION LENGTH bytes
if (info_size > length) { if (size > length) {
info_size = length; size = length;
} }
// Final setting of mode data length // Final setting of mode data length
SetInt16(buf, 0, info_size); SetInt16(buf, 0, size);
return info_size; return size;
} }
void Disk::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const void Disk::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
@ -614,12 +621,12 @@ int Disk::Read(const vector<int>&, vector<BYTE>& buf, uint64_t block)
// Error if the total number of blocks is exceeded // Error if the total number of blocks is exceeded
if (block >= blocks) { if (block >= blocks) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
// leave it to the cache // leave it to the cache
if (!cache->ReadSector(buf, (uint32_t)block)) { if (!cache->ReadSector(buf, (uint32_t)block)) {
throw scsi_error_exception(sense_key::MEDIUM_ERROR, asc::READ_FAULT); throw scsi_exception(sense_key::MEDIUM_ERROR, asc::READ_FAULT);
} }
// Success // Success
@ -632,12 +639,12 @@ int Disk::WriteCheck(uint64_t block)
// Error if the total number of blocks is exceeded // Error if the total number of blocks is exceeded
if (block >= blocks) { if (block >= blocks) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
// Error if write protected // Error if write protected
if (IsProtected()) { if (IsProtected()) {
throw scsi_error_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED); throw scsi_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
} }
// Success // Success
@ -651,22 +658,22 @@ void Disk::Write(const vector<int>&, const vector<BYTE>& buf, uint64_t block)
// Error if not ready // Error if not ready
if (!IsReady()) { if (!IsReady()) {
throw scsi_error_exception(sense_key::NOT_READY); throw scsi_exception(sense_key::NOT_READY);
} }
// Error if the total number of blocks is exceeded // Error if the total number of blocks is exceeded
if (block >= blocks) { if (block >= blocks) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
} }
// Error if write protected // Error if write protected
if (IsProtected()) { if (IsProtected()) {
throw scsi_error_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED); throw scsi_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
} }
// Leave it to the cache // Leave it to the cache
if (!cache->WriteSector(buf, (uint32_t)block)) { if (!cache->WriteSector(buf, (uint32_t)block)) {
throw scsi_error_exception(sense_key::MEDIUM_ERROR, asc::WRITE_FAULT); throw scsi_exception(sense_key::MEDIUM_ERROR, asc::WRITE_FAULT);
} }
} }
@ -700,7 +707,7 @@ void Disk::ReadCapacity10()
CheckReady(); CheckReady();
if (blocks == 0) { if (blocks == 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
} }
vector<BYTE>& buf = controller->GetBuffer(); vector<BYTE>& buf = controller->GetBuffer();
@ -728,7 +735,7 @@ void Disk::ReadCapacity16()
CheckReady(); CheckReady();
if (blocks == 0) { if (blocks == 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
} }
vector<BYTE>& buf = controller->GetBuffer(); vector<BYTE>& buf = controller->GetBuffer();
@ -763,7 +770,7 @@ void Disk::ReadCapacity16_ReadLong16()
break; break;
default: default:
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
break; break;
} }
} }
@ -796,14 +803,12 @@ void Disk::Release()
void Disk::ValidateBlockAddress(access_mode mode) const void Disk::ValidateBlockAddress(access_mode mode) const
{ {
uint64_t block = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2); const uint64_t block = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
uint64_t capacity = GetBlockCount(); if (block > blocks) {
LOGTRACE("%s", ("Capacity of " + to_string(blocks) + " block(s) exceeded: Trying to access block "
if (block > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ to_string(block)).c_str()) + to_string(block)).c_str())
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
} }
} }
@ -834,10 +839,10 @@ 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) LOGTRACE("%s READ/WRITE/VERIFY/SEEK command record=$%08X blocks=%d", __PRETTY_FUNCTION__, (uint32_t)start, count)
// Check capacity // Check capacity
if (uint64_t capacity = GetBlockCount(); start > capacity || start + count > capacity) { if (uint64_t capacity = blocks; start > capacity || start + count > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block " LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ to_string(start) + ", block count " + to_string(count)).c_str()) + to_string(start) + ", block count " + to_string(count)).c_str())
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
} }
// Do not process 0 blocks // Do not process 0 blocks
@ -879,7 +884,7 @@ void Disk::SetSectorSizeInBytes(uint32_t size_in_bytes)
break; break;
default: default:
assert(false); throw io_exception("Invalid block size of " + to_string(size_in_bytes) + " bytes");
break; break;
} }
} }
@ -900,3 +905,43 @@ bool Disk::SetConfiguredSectorSize(const DeviceFactory& device_factory, uint32_t
return true; 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;
}

View File

@ -25,12 +25,18 @@
#include <string> #include <string>
#include <unordered_set> #include <unordered_set>
class Disk : public ModePageDevice, public ScsiBlockCommands using namespace std;
using id_set = pair<int, int>;
class Disk : public ModePageDevice, private ScsiBlockCommands
{ {
enum access_mode { RW6, RW10, RW16, SEEK6, SEEK10 }; enum access_mode { RW6, RW10, RW16, SEEK6, SEEK10 };
Dispatcher<Disk> dispatcher; Dispatcher<Disk> dispatcher;
unique_ptr<DiskCache> cache;
// The supported configurable sector sizes, empty if not configurable // The supported configurable sector sizes, empty if not configurable
unordered_set<uint32_t> sector_sizes; unordered_set<uint32_t> sector_sizes;
uint32_t configured_sector_size = 0; uint32_t configured_sector_size = 0;
@ -43,12 +49,15 @@ class Disk : public ModePageDevice, public ScsiBlockCommands
bool is_medium_changed = false; 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<string, id_set> reserved_files;
public: public:
explicit Disk(const string&); Disk(const string&, int);
~Disk() override; ~Disk() override;
Disk(Disk&) = delete;
Disk& operator=(const Disk&) = delete;
bool Dispatch(scsi_command) override; bool Dispatch(scsi_command) override;
@ -67,6 +76,18 @@ public:
uint64_t GetBlockCount() const { return blocks; } uint64_t GetBlockCount() const { return blocks; }
void FlushCache() override; 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<string, id_set> GetReservedFiles() { return reserved_files; }
static void SetReservedFiles(const unordered_map<string, id_set>& files_in_use) { reserved_files = files_in_use; }
static bool GetIdsForReservedFile(const Filepath&, int&, int&);
private: private:
using super = ModePageDevice; using super = ModePageDevice;
@ -105,13 +126,13 @@ private:
void ValidateBlockAddress(access_mode) const; void ValidateBlockAddress(access_mode) const;
bool CheckAndGetStartAndCount(uint64_t&, uint32_t&, access_mode) const; bool CheckAndGetStartAndCount(uint64_t&, uint32_t&, access_mode) const;
int ModeSense6(const vector<int>&, vector<BYTE>&, int) const override; int ModeSense6(const vector<int>&, vector<BYTE>&) const override;
int ModeSense10(const vector<int>&, vector<BYTE>&, int) const override; int ModeSense10(const vector<int>&, vector<BYTE>&) const override;
protected: protected:
virtual void Open(const Filepath&); void SetUpCache(const Filepath&, off_t, bool = false);
void SetUpCache(const Filepath&, off_t = 0); void ResizeCache(const Filepath&, bool);
void SetUpModePages(map<int, vector<byte>>&, int, bool) const override; void SetUpModePages(map<int, vector<byte>>&, int, bool) const override;
virtual void AddErrorPage(map<int, vector<byte>>&, bool) const; virtual void AddErrorPage(map<int, vector<byte>>&, bool) const;
@ -126,6 +147,5 @@ protected:
void SetSectorSizeShiftCount(uint32_t count) { size_shift_count = count; } void SetSectorSizeShiftCount(uint32_t count) { size_shift_count = count; }
uint32_t GetConfiguredSectorSize() const; uint32_t GetConfiguredSectorSize() const;
void SetBlockCount(uint64_t b) { blocks = b; } void SetBlockCount(uint64_t b) { blocks = b; }
void SetPath(const Filepath& path) { diskpath = path; }
unique_ptr<DiskCache> cache;
}; };

View File

@ -16,6 +16,8 @@
#include "disk_track.h" #include "disk_track.h"
#include "disk_cache.h" #include "disk_cache.h"
#include <cstdlib>
#include <cassert>
DiskCache::DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff) DiskCache::DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff)
: sec_size(size), sec_blocks(blocks), imgoffset(imgoff) : sec_size(size), sec_blocks(blocks), imgoffset(imgoff)

View File

@ -19,7 +19,7 @@
#include <array> #include <array>
#include <memory> #include <memory>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class DiskCache class DiskCache
{ {
@ -36,8 +36,6 @@ public:
DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff = 0); DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff = 0);
~DiskCache() = default; ~DiskCache() = default;
DiskCache(DiskCache&) = delete;
DiskCache& operator=(const DiskCache&) = delete;
void SetRawMode(bool b) { cd_raw = b; } // CD-ROM raw mode setting void SetRawMode(bool b) { cd_raw = b; } // CD-ROM raw mode setting

View File

@ -21,9 +21,7 @@
DiskTrack::~DiskTrack() DiskTrack::~DiskTrack()
{ {
// Release memory, but do not save automatically // Release memory, but do not save automatically
if (dt.buffer) { free(dt.buffer);
free(dt.buffer);
}
} }
void DiskTrack::Init(int track, int size, int sectors, bool raw, off_t imgoff) void DiskTrack::Init(int track, int size, int sectors, bool raw, off_t imgoff)
@ -70,7 +68,7 @@ bool DiskTrack::Load(const Filepath& path)
offset += dt.imgoffset; offset += dt.imgoffset;
// Calculate length (data size of this track) // Calculate length (data size of this track)
int length = dt.sectors << dt.size; const int length = dt.sectors << dt.size;
// Allocate buffer memory // Allocate buffer memory
assert((dt.sectors > 0) && (dt.sectors <= 0x100)); assert((dt.sectors > 0) && (dt.sectors <= 0x100));
@ -87,7 +85,7 @@ bool DiskTrack::Load(const Filepath& path)
} }
// Reallocate if the buffer length is different // Reallocate if the buffer length is different
if (dt.length != (DWORD)length) { if (dt.length != (uint32_t)length) {
free(dt.buffer); free(dt.buffer);
if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) { if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) {
LOGWARN("%s posix_memalign failed", __PRETTY_FUNCTION__) LOGWARN("%s posix_memalign failed", __PRETTY_FUNCTION__)
@ -169,7 +167,7 @@ bool DiskTrack::Save(const Filepath& path)
offset += dt.imgoffset; offset += dt.imgoffset;
// Calculate length per sector // Calculate length per sector
int length = 1 << dt.size; const int length = 1 << dt.size;
// Open file // Open file
Fileio fio; Fileio fio;
@ -267,8 +265,8 @@ bool DiskTrack::WriteSector(const vector<BYTE>& buf, int sec)
} }
// Calculate offset and length // Calculate offset and length
int offset = sec << dt.size; const int offset = sec << dt.size;
int length = 1 << dt.size; const int length = 1 << dt.size;
// Compare // Compare
assert(dt.buffer); assert(dt.buffer);

View File

@ -16,9 +16,10 @@
#pragma once #pragma once
#include "filepath.h" #include "filepath.h"
#include <cstdlib>
#include <vector> #include <vector>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class DiskTrack class DiskTrack
{ {
@ -26,7 +27,7 @@ class DiskTrack
int track; // Track Number int track; // Track Number
int size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096) int size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096)
int sectors; // Number of sectors(<0x100) int sectors; // Number of sectors(<0x100)
DWORD length; // Data buffer length uint32_t length; // Data buffer length
BYTE *buffer; // Data buffer BYTE *buffer; // Data buffer
bool init; // Is it initilized? bool init; // Is it initilized?
bool changed; // Changed flag bool changed; // Changed flag

View File

@ -14,8 +14,8 @@
#include "log.h" #include "log.h"
#include <unordered_map> #include <unordered_map>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
using namespace scsi_defs; //NOSONAR Not relevant for rascsi using namespace scsi_defs;
template<class T> template<class T>
class Dispatcher class Dispatcher
@ -24,8 +24,6 @@ public:
Dispatcher() = default; Dispatcher() = default;
~Dispatcher() = default; ~Dispatcher() = default;
Dispatcher(Dispatcher&) = delete;
Dispatcher& operator=(const Dispatcher&) = delete;
using operation = void (T::*)(); using operation = void (T::*)();
using command_t = struct _command_t { using command_t = struct _command_t {

View File

@ -1,41 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "file_support.h"
using namespace std;
unordered_map<string, id_set> FileSupport::reserved_files;
void FileSupport::ReserveFile(const Filepath& path, int id, int lun) const
{
reserved_files[path.GetPath()] = make_pair(id, lun);
}
void FileSupport::UnreserveFile() const
{
reserved_files.erase(diskpath.GetPath());
}
bool FileSupport::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 FileSupport::UnreserveAll()
{
reserved_files.clear();
}

View File

@ -1,49 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
// Devices inheriting from FileSupport support image files
//
//---------------------------------------------------------------------------
#pragma once
#include <unordered_map>
#include <string>
#include "filepath.h"
using namespace std; //NOSONAR Not relevant for rascsi
using id_set = pair<int, int>;
class FileSupport
{
Filepath diskpath;
// The list of image files in use and the IDs and LUNs using these files
static unordered_map<string, id_set> reserved_files;
public:
FileSupport() = default;
virtual ~FileSupport() = default;
FileSupport(FileSupport&) = delete;
FileSupport& operator=(const FileSupport&) = delete;
void GetPath(Filepath& path) const { path = diskpath; }
void SetPath(const Filepath& path) { diskpath = path; }
void ReserveFile(const Filepath&, int, int) const;
void UnreserveFile() const;
static void UnreserveAll();
static unordered_map<string, id_set> GetReservedFiles() { return reserved_files; }
static void SetReservedFiles(const unordered_map<string, id_set>& files_in_use)
{ FileSupport::reserved_files = files_in_use; }
static bool GetIdsForReservedFile(const Filepath&, int&, int&);
virtual void Open(const Filepath&) = 0;
};

View File

@ -20,8 +20,9 @@
// c) start && load (LOAD): Reboot the Raspberry Pi // c) start && load (LOAD): Reboot the Raspberry Pi
// //
#include "controllers/controller_manager.h"
#include "controllers/scsi_controller.h"
#include "rascsi_exceptions.h" #include "rascsi_exceptions.h"
#include "device_factory.h"
#include "scsi_command_util.h" #include "scsi_command_util.h"
#include "dispatcher.h" #include "dispatcher.h"
#include "host_services.h" #include "host_services.h"
@ -30,10 +31,14 @@
using namespace scsi_defs; using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
HostServices::HostServices(const DeviceFactory& factory) : ModePageDevice("SCHS"), device_factory(factory) HostServices::HostServices(int lun, const ControllerManager& manager)
: ModePageDevice("SCHS", lun), controller_manager(manager)
{ {
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &HostServices::TestUnitReady); dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &HostServices::TestUnitReady);
dispatcher.Add(scsi_command::eCmdStartStop, "StartStopUnit", &HostServices::StartStopUnit); dispatcher.Add(scsi_command::eCmdStartStop, "StartStopUnit", &HostServices::StartStopUnit);
SetReady(true);
SetReset(false);
} }
bool HostServices::Dispatch(scsi_command cmd) bool HostServices::Dispatch(scsi_command cmd)
@ -55,45 +60,40 @@ vector<byte> HostServices::InquiryInternal() const
void HostServices::StartStopUnit() void HostServices::StartStopUnit()
{ {
bool start = ctrl->cmd[4] & 0x01; const bool start = ctrl->cmd[4] & 0x01;
bool load = ctrl->cmd[4] & 0x02; const bool load = ctrl->cmd[4] & 0x02;
if (!start) { if (!start) {
// Flush any caches // Flush any caches
for (PrimaryDevice *device : device_factory.GetAllDevices()) { for (const auto& device : controller_manager.GetAllDevices()) {
device->FlushCache(); device->FlushCache();
} }
if (load) { if (load) {
controller->ScheduleShutdown(ScsiController::rascsi_shutdown_mode::STOP_PI); controller->ScheduleShutdown(AbstractController::rascsi_shutdown_mode::STOP_PI);
} }
else { else {
controller->ScheduleShutdown(ScsiController::rascsi_shutdown_mode::STOP_RASCSI); controller->ScheduleShutdown(AbstractController::rascsi_shutdown_mode::STOP_RASCSI);
} }
}
EnterStatusPhase(); else if (load) {
return; controller->ScheduleShutdown(AbstractController::rascsi_shutdown_mode::RESTART_PI);
} }
else { else {
if (load) { throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
controller->ScheduleShutdown(ScsiController::rascsi_shutdown_mode::RESTART_PI);
EnterStatusPhase();
return;
}
} }
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); EnterStatusPhase();
} }
int HostServices::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_length) const int HostServices::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf) const
{ {
// Block descriptors cannot be returned // Block descriptors cannot be returned
if (!(cdb[1] & 0x08)) { if (!(cdb[1] & 0x08)) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
auto length = (int)min((size_t)max_length, (size_t)cdb[4]); const auto length = (int)min(buf.size(), (size_t)cdb[4]);
fill_n(buf.begin(), length, 0); fill_n(buf.begin(), length, 0);
// Basic Information // Basic Information
@ -101,7 +101,7 @@ int HostServices::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_
size += super::AddModePages(cdb, buf, size, length - size); size += super::AddModePages(cdb, buf, size, length - size);
if (size > 255) { if (size > 255) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
// Do not return more than ALLOCATION LENGTH bytes // Do not return more than ALLOCATION LENGTH bytes
@ -114,14 +114,14 @@ int HostServices::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_
return size; return size;
} }
int HostServices::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length) const int HostServices::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf) const
{ {
// Block descriptors cannot be returned // Block descriptors cannot be returned
if (!(cdb[1] & 0x08)) { if (!(cdb[1] & 0x08)) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
auto length = (int)min((size_t)max_length, (size_t)GetInt16(cdb, 7)); const auto length = (int)min(buf.size(), (size_t)GetInt16(cdb, 7));
fill_n(buf.begin(), length, 0); fill_n(buf.begin(), length, 0);
// Basic Information // Basic Information
@ -129,7 +129,7 @@ int HostServices::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max
size += super::AddModePages(cdb, buf, size, length - size); size += super::AddModePages(cdb, buf, size, length - size);
if (size > 65535) { if (size > 65535) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
// Do not return more than ALLOCATION LENGTH bytes // Do not return more than ALLOCATION LENGTH bytes

View File

@ -15,23 +15,20 @@
#include <vector> #include <vector>
#include <map> #include <map>
class DeviceFactory; class ControllerManager;
class HostServices: public ModePageDevice class HostServices: public ModePageDevice
{ {
public: public:
explicit HostServices(const DeviceFactory&); HostServices(int, const ControllerManager&);
~HostServices() override = default; ~HostServices() override = default;
HostServices(HostServices&) = delete;
HostServices& operator=(const HostServices&) = delete;
bool Dispatch(scsi_command) override; bool Dispatch(scsi_command) override;
vector<byte> InquiryInternal() const override; vector<byte> InquiryInternal() const override;
void TestUnitReady() override; void TestUnitReady() override;
void StartStopUnit();
bool SupportsFile() const override { return false; } bool SupportsFile() const override { return false; }
@ -58,10 +55,11 @@ private:
Dispatcher<HostServices> dispatcher; Dispatcher<HostServices> dispatcher;
int ModeSense6(const vector<int>&, vector<BYTE>&, int) const override; const ControllerManager& controller_manager;
int ModeSense10(const vector<int>&, vector<BYTE>&, int) const override;
void StartStopUnit();
int ModeSense6(const vector<int>&, vector<BYTE>&) const override;
int ModeSense10(const vector<int>&, vector<BYTE>&) const override;
void AddRealtimeClockPage(map<int, vector<byte>>&, bool) const; void AddRealtimeClockPage(map<int, vector<byte>>&, bool) const;
const DeviceFactory& device_factory;
}; };

View File

@ -20,7 +20,7 @@ using namespace std;
using namespace scsi_defs; using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
ModePageDevice::ModePageDevice(const string& id) : PrimaryDevice(id) ModePageDevice::ModePageDevice(const string& type, int lun) : PrimaryDevice(type, lun)
{ {
dispatcher.Add(scsi_command::eCmdModeSense6, "ModeSense6", &ModePageDevice::ModeSense6); dispatcher.Add(scsi_command::eCmdModeSense6, "ModeSense6", &ModePageDevice::ModeSense6);
dispatcher.Add(scsi_command::eCmdModeSense10, "ModeSense10", &ModePageDevice::ModeSense10); dispatcher.Add(scsi_command::eCmdModeSense10, "ModeSense10", &ModePageDevice::ModeSense10);
@ -40,10 +40,10 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
return 0; return 0;
} }
bool changeable = (cdb[2] & 0xc0) == 0x40; const bool changeable = (cdb[2] & 0xc0) == 0x40;
// Get page code (0x3f means all pages) // Get page code (0x3f means all pages)
int page = cdb[2] & 0x3f; const int page = cdb[2] & 0x3f;
LOGTRACE("%s Requesting mode page $%02X", __PRETTY_FUNCTION__, page) LOGTRACE("%s Requesting mode page $%02X", __PRETTY_FUNCTION__, page)
@ -53,7 +53,7 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
if (pages.empty()) { if (pages.empty()) {
LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, page) LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, page)
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
// Holds all mode page data // Holds all mode page data
@ -63,14 +63,14 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
for (auto const& [index, data] : pages) { for (auto const& [index, data] : pages) {
// The specification mandates that page 0 must be returned after all others // The specification mandates that page 0 must be returned after all others
if (index) { if (index) {
size_t offset = result.size(); const size_t off = result.size();
// Page data // Page data
result.insert(result.end(), data.begin(), data.end()); result.insert(result.end(), data.begin(), data.end());
// Page code, PS bit may already have been set // Page code, PS bit may already have been set
result[offset] |= (byte)index; result[off] |= (byte)index;
// Page payload size // Page payload size
result[offset + 1] = (byte)(data.size() - 2); result[off + 1] = (byte)(data.size() - 2);
} }
else { else {
page0 = data; page0 = data;
@ -79,10 +79,12 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
// Page 0 must be last // Page 0 must be last
if (!page0.empty()) { if (!page0.empty()) {
size_t off = result.size();
// Page data // Page data
result.insert(result.end(), page0.begin(), page0.end()); result.insert(result.end(), page0.begin(), page0.end());
// Page payload size // Page payload size
result[result.size() + 1] = (byte)(page0.size() - 2); result[off + 1] = (byte)(page0.size() - 2);
} }
// Do not return more than the requested number of bytes // Do not return more than the requested number of bytes
@ -94,21 +96,21 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
void ModePageDevice::ModeSense6() void ModePageDevice::ModeSense6()
{ {
ctrl->length = ModeSense6(ctrl->cmd, controller->GetBuffer(), (int)controller->GetBufferSize()); ctrl->length = ModeSense6(ctrl->cmd, controller->GetBuffer());
EnterDataInPhase(); EnterDataInPhase();
} }
void ModePageDevice::ModeSense10() void ModePageDevice::ModeSense10()
{ {
ctrl->length = ModeSense10(ctrl->cmd, controller->GetBuffer(), (int)controller->GetBufferSize()); ctrl->length = ModeSense10(ctrl->cmd, controller->GetBuffer());
EnterDataInPhase(); EnterDataInPhase();
} }
void ModePageDevice::ModeSelect(const vector<int>&, const vector<BYTE>&, int) const void ModePageDevice::ModeSelect(const vector<int>&, const vector<BYTE>&, int) const
{ {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
} }
void ModePageDevice::ModeSelect6() void ModePageDevice::ModeSelect6()
@ -130,7 +132,7 @@ int ModePageDevice::ModeSelectCheck(int length) const
// Error if save parameters are set for other types than SCHD, SCRM or SCMO // 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 // 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 (GetType() != "SCHD" && GetType() != "SCRM" && GetType() != "SCMO" && (ctrl->cmd[1] & 0x01)) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
return length; return length;
@ -145,7 +147,7 @@ int ModePageDevice::ModeSelectCheck6() const
int ModePageDevice::ModeSelectCheck10() const int ModePageDevice::ModeSelectCheck10() const
{ {
// Receive the data specified by the parameter length // Receive the data specified by the parameter length
size_t length = min(controller->GetBufferSize(), (size_t)GetInt16(ctrl->cmd, 7)); size_t length = min(controller->GetBuffer().size(), (size_t)GetInt16(ctrl->cmd, 7));
return ModeSelectCheck((int)length); return ModeSelectCheck((int)length);
} }

View File

@ -18,10 +18,8 @@ class ModePageDevice: public PrimaryDevice
{ {
public: public:
explicit ModePageDevice(const string&); ModePageDevice(const string&, int);
~ModePageDevice()override = default; ~ModePageDevice()override = default;
ModePageDevice(ModePageDevice&) = delete;
ModePageDevice& operator=(const ModePageDevice&) = delete;
bool Dispatch(scsi_command) override; bool Dispatch(scsi_command) override;
@ -38,8 +36,8 @@ private:
Dispatcher<ModePageDevice> dispatcher; Dispatcher<ModePageDevice> dispatcher;
virtual int ModeSense6(const vector<int>&, vector<BYTE>&, int) const = 0; virtual int ModeSense6(const vector<int>&, vector<BYTE>&) const = 0;
virtual int ModeSense10(const vector<int>&, vector<BYTE>&, int) const = 0; virtual int ModeSense10(const vector<int>&, vector<BYTE>&) const = 0;
void ModeSense6(); void ModeSense6();
void ModeSense10(); void ModeSense10();

View File

@ -17,7 +17,7 @@ using namespace std;
using namespace scsi_defs; using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
PrimaryDevice::PrimaryDevice(const string& id) : Device(id) PrimaryDevice::PrimaryDevice(const string& type, int lun) : Device(type, lun)
{ {
// Mandatory SCSI primary commands // Mandatory SCSI primary commands
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady); dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady);
@ -33,6 +33,15 @@ bool PrimaryDevice::Dispatch(scsi_command cmd)
return dispatcher.Dispatch(this, cmd); return dispatcher.Dispatch(this, cmd);
} }
int PrimaryDevice::GetId() const
{
if (controller == nullptr) {
LOGERROR("Device is missing its controller")
}
return controller != nullptr ? controller->GetTargetId() : -1;
}
void PrimaryDevice::SetController(AbstractController *c) void PrimaryDevice::SetController(AbstractController *c)
{ {
controller = c; controller = c;
@ -50,12 +59,12 @@ void PrimaryDevice::Inquiry()
{ {
// EVPD and page code check // EVPD and page code check
if ((ctrl->cmd[1] & 0x01) || ctrl->cmd[2]) { if ((ctrl->cmd[1] & 0x01) || ctrl->cmd[2]) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
vector<byte> buf = InquiryInternal(); vector<byte> buf = InquiryInternal();
size_t allocation_length = min(buf.size(), (size_t)GetInt16(ctrl->cmd, 3)); const size_t allocation_length = min(buf.size(), (size_t)GetInt16(ctrl->cmd, 3));
memcpy(controller->GetBuffer().data(), buf.data(), allocation_length); memcpy(controller->GetBuffer().data(), buf.data(), allocation_length);
ctrl->length = (uint32_t)allocation_length; ctrl->length = (uint32_t)allocation_length;
@ -65,7 +74,7 @@ void PrimaryDevice::Inquiry()
LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, GetId()) LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, GetId())
// Signal that the requested LUN does not exist // Signal that the requested LUN does not exist
controller->GetBuffer()[0] |= 0x7f; controller->GetBuffer().data()[0] = 0x7f;
} }
EnterDataInPhase(); EnterDataInPhase();
@ -75,13 +84,13 @@ void PrimaryDevice::ReportLuns()
{ {
// Only SELECT REPORT mode 0 is supported // Only SELECT REPORT mode 0 is supported
if (ctrl->cmd[2]) { if (ctrl->cmd[2]) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
uint32_t allocation_length = GetInt32(ctrl->cmd, 6); const uint32_t allocation_length = GetInt32(ctrl->cmd, 6);
vector<BYTE>& buf = controller->GetBuffer(); vector<BYTE>& buf = controller->GetBuffer();
fill_n(buf.begin(), min(controller->GetBufferSize(), (size_t)allocation_length), 0); fill_n(buf.begin(), min(buf.size(), (size_t)allocation_length), 0);
uint32_t size = 0; uint32_t size = 0;
for (int lun = 0; lun < controller->GetMaxLuns(); lun++) { for (int lun = 0; lun < controller->GetMaxLuns(); lun++) {
@ -115,12 +124,12 @@ void PrimaryDevice::RequestSense()
// Do not raise an exception here because the rest of the code must be executed // Do not raise an exception here because the rest of the code must be executed
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN); controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN);
controller->SetStatus(0); controller->SetStatus(status::GOOD);
} }
vector<byte> buf = controller->GetDeviceForLun(lun)->HandleRequestSense(); vector<byte> buf = controller->GetDeviceForLun(lun)->HandleRequestSense();
size_t allocation_length = min(buf.size(), (size_t)ctrl->cmd[4]); const size_t allocation_length = min(buf.size(), (size_t)ctrl->cmd[4]);
memcpy(controller->GetBuffer().data(), buf.data(), allocation_length); memcpy(controller->GetBuffer().data(), buf.data(), allocation_length);
ctrl->length = (uint32_t)allocation_length; ctrl->length = (uint32_t)allocation_length;
@ -134,20 +143,20 @@ void PrimaryDevice::CheckReady()
if (IsReset()) { if (IsReset()) {
SetReset(false); SetReset(false);
LOGTRACE("%s Device in reset", __PRETTY_FUNCTION__) LOGTRACE("%s Device in reset", __PRETTY_FUNCTION__)
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::POWER_ON_OR_RESET); throw scsi_exception(sense_key::UNIT_ATTENTION, asc::POWER_ON_OR_RESET);
} }
// Not ready if it needs attention // Not ready if it needs attention
if (IsAttn()) { if (IsAttn()) {
SetAttn(false); SetAttn(false);
LOGTRACE("%s Device in needs attention", __PRETTY_FUNCTION__) LOGTRACE("%s Device in needs attention", __PRETTY_FUNCTION__)
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE); throw scsi_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
} }
// Return status if not ready // Return status if not ready
if (!IsReady()) { if (!IsReady()) {
LOGTRACE("%s Device not ready", __PRETTY_FUNCTION__) LOGTRACE("%s Device not ready", __PRETTY_FUNCTION__)
throw scsi_error_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT); throw scsi_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT);
} }
// Initialization with no error // Initialization with no error
@ -171,7 +180,7 @@ vector<byte> PrimaryDevice::HandleInquiry(device_type type, scsi_level level, bo
buf[4] = (byte)0x1F; buf[4] = (byte)0x1F;
// Padded vendor, product, revision // Padded vendor, product, revision
memcpy(&buf[8], GetPaddedName().c_str(), 28); memcpy(&buf.data()[8], GetPaddedName().c_str(), 28);
return buf; return buf;
} }
@ -180,7 +189,7 @@ vector<byte> PrimaryDevice::HandleRequestSense() const
{ {
// Return not ready only if there are no errors // Return not ready only if there are no errors
if (!GetStatusCode() && !IsReady()) { if (!GetStatusCode() && !IsReady()) {
throw scsi_error_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT); throw scsi_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT);
} }
// Set 18 bytes including extended sense data // Set 18 bytes including extended sense data
@ -195,7 +204,7 @@ vector<byte> PrimaryDevice::HandleRequestSense() const
buf[12] = (byte)(GetStatusCode() >> 8); buf[12] = (byte)(GetStatusCode() >> 8);
buf[13] = (byte)GetStatusCode(); buf[13] = (byte)GetStatusCode();
LOGTRACE("%s Status $%02X, Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, controller->GetStatus(), LOGTRACE("%s Status $%02X, Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, (int)controller->GetStatus(),
(int)buf[2], (int)buf[12]) (int)buf[2], (int)buf[12])
return buf; return buf;

View File

@ -12,27 +12,33 @@
#pragma once #pragma once
#include "interfaces/scsi_primary_commands.h" #include "interfaces/scsi_primary_commands.h"
#include "controllers/scsi_controller.h" #include "controllers/abstract_controller.h"
#include "device.h" #include "device.h"
#include "dispatcher.h" #include "dispatcher.h"
#include <string> #include <string>
class PrimaryDevice: public ScsiPrimaryCommands, public Device class PrimaryDevice: private ScsiPrimaryCommands, public Device
{ {
public: public:
explicit PrimaryDevice(const string&); PrimaryDevice(const string&, int);
~PrimaryDevice() override = default; ~PrimaryDevice() override = default;
PrimaryDevice(PrimaryDevice&) = delete;
PrimaryDevice& operator=(const PrimaryDevice&) = delete;
virtual bool Dispatch(scsi_command); virtual bool Dispatch(scsi_command);
int GetId() const override;
void SetController(AbstractController *); void SetController(AbstractController *);
virtual bool WriteByteSequence(vector<BYTE>&, uint32_t); virtual bool WriteByteSequence(vector<BYTE>&, uint32_t);
virtual int GetSendDelay() const { return BUS::SEND_NO_DELAY; } 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<string, string>&) { return true; };
virtual void FlushCache() {
// Devices with a cache have to implement this method
}
protected: protected:
vector<byte> HandleInquiry(scsi_defs::device_type, scsi_level, bool) const; vector<byte> HandleInquiry(scsi_defs::device_type, scsi_level, bool) const;

View File

@ -20,39 +20,37 @@ void scsi_command_util::ModeSelect(const vector<int>& cdb, const vector<BYTE>& b
// PF // PF
if (!(cdb[1] & 0x10)) { if (!(cdb[1] & 0x10)) {
// Vendor-specific parameters (SCSI-1) are not supported // Vendor-specific parameters (SCSI-1) are not supported
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
} }
// Skip block descriptors
int offset;
if ((scsi_command)cdb[0] == scsi_command::eCmdModeSelect10) {
offset = 8 + GetInt16(buf, 6);
}
else {
offset = 4 + buf[3];
}
length -= offset;
bool has_valid_page_code = false; bool has_valid_page_code = false;
// Mode Parameter header // Parse the pages
int offset = 0;
if (length >= 12) {
// Check the block length
if (buf[9] != (BYTE)(sector_size >> 16) || buf[10] != (BYTE)(sector_size >> 8) ||
buf[11] != (BYTE)sector_size) {
// See below for details
LOGWARN("In order to change the sector size use the -b option when launching rascsi")
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
}
offset += 12;
length -= 12;
}
// Parsing the page
// TODO The length handling is wrong in case of length < size
while (length > 0) { while (length > 0) {
// Format device page // Format device page
if (int page = buf[offset]; page == 0x03) { if (int page = buf[offset]; page == 0x03) {
if (length < 14) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
}
// With this page the sector size for a subsequent FORMAT can be selected, but only very few // With this page the sector size for a subsequent FORMAT can be selected, but only very few
// drives support this, e.g FUJITSU M2624S // drives support this, e.g FUJITSU M2624S
// We are fine as long as the current sector size remains unchanged // We are fine as long as the current sector size remains unchanged
if (buf[offset + 0xc] != (BYTE)(sector_size >> 8) || buf[offset + 0xd] != (BYTE)sector_size) { if (GetInt16(buf, offset + 12) != sector_size) {
// With rascsi it is not possible to permanently (by formatting) change the sector size, // With rascsi it is not possible to permanently (by formatting) change the sector size,
// because the size is an externally configurable setting only // because the size is an externally configurable setting only
LOGWARN("In order to change the sector size use the -b option when launching rascsi") LOGWARN("In order to change the sector size use the -b option when launching rascsi")
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
} }
has_valid_page_code = true; has_valid_page_code = true;
@ -63,12 +61,13 @@ void scsi_command_util::ModeSelect(const vector<int>& cdb, const vector<BYTE>& b
// Advance to the next page // Advance to the next page
int size = buf[offset + 1] + 2; int size = buf[offset + 1] + 2;
length -= size; length -= size;
offset += size; offset += size;
} }
if (!has_valid_page_code) { if (!has_valid_page_code) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
} }
} }
@ -92,30 +91,45 @@ void scsi_command_util::AddAppleVendorModePage(map<int, vector<byte>>& pages, bo
// No changeable area // No changeable area
if (!changeable) { if (!changeable) {
const char APPLE_DATA[] = "APPLE COMPUTER, INC "; const char APPLE_DATA[] = "APPLE COMPUTER, INC ";
memcpy(&buf[2], APPLE_DATA, sizeof(APPLE_DATA)); memcpy(&buf.data()[2], APPLE_DATA, sizeof(APPLE_DATA));
} }
pages[48] = buf; pages[48] = buf;
} }
int scsi_command_util::GetInt16(const vector<BYTE>& buf, int offset)
{
assert(buf.size() > (size_t)offset + 1);
return ((int)buf[offset] << 8) | buf[offset + 1];
}
int scsi_command_util::GetInt16(const vector<int>& buf, int offset) int scsi_command_util::GetInt16(const vector<int>& buf, int offset)
{ {
assert(buf.size() > (size_t)offset + 1);
return (buf[offset] << 8) | buf[offset + 1]; return (buf[offset] << 8) | buf[offset + 1];
} }
int scsi_command_util::GetInt24(const vector<int>& buf, int offset) int scsi_command_util::GetInt24(const vector<int>& buf, int offset)
{ {
assert(buf.size() > (size_t)offset + 2);
return (buf[offset] << 16) | (buf[offset + 1] << 8) | buf[offset + 2]; return (buf[offset] << 16) | (buf[offset + 1] << 8) | buf[offset + 2];
} }
uint32_t scsi_command_util::GetInt32(const vector<int>& buf, int offset) uint32_t scsi_command_util::GetInt32(const vector<int>& buf, int offset)
{ {
assert(buf.size() > (size_t)offset + 3);
return ((uint32_t)buf[offset] << 24) | ((uint32_t)buf[offset + 1] << 16) | return ((uint32_t)buf[offset] << 24) | ((uint32_t)buf[offset + 1] << 16) |
((uint32_t)buf[offset + 2] << 8) | (uint32_t)buf[offset + 3]; ((uint32_t)buf[offset + 2] << 8) | (uint32_t)buf[offset + 3];
} }
uint64_t scsi_command_util::GetInt64(const vector<int>& buf, int offset) uint64_t scsi_command_util::GetInt64(const vector<int>& buf, int offset)
{ {
assert(buf.size() > (size_t)offset + 7);
return ((uint64_t)buf[offset] << 56) | ((uint64_t)buf[offset + 1] << 48) | return ((uint64_t)buf[offset] << 56) | ((uint64_t)buf[offset + 1] << 48) |
((uint64_t)buf[offset + 2] << 40) | ((uint64_t)buf[offset + 3] << 32) | ((uint64_t)buf[offset + 2] << 40) | ((uint64_t)buf[offset + 3] << 32) |
((uint64_t)buf[offset + 4] << 24) | ((uint64_t)buf[offset + 5] << 16) | ((uint64_t)buf[offset + 4] << 24) | ((uint64_t)buf[offset + 5] << 16) |
@ -124,12 +138,16 @@ uint64_t scsi_command_util::GetInt64(const vector<int>& buf, int offset)
void scsi_command_util::SetInt16(vector<byte>& buf, int offset, int value) void scsi_command_util::SetInt16(vector<byte>& buf, int offset, int value)
{ {
assert(buf.size() > (size_t)offset + 1);
buf[offset] = (byte)(value >> 8); buf[offset] = (byte)(value >> 8);
buf[offset + 1] = (byte)value; buf[offset + 1] = (byte)value;
} }
void scsi_command_util::SetInt32(vector<byte>& buf, int offset, uint32_t value) void scsi_command_util::SetInt32(vector<byte>& buf, int offset, uint32_t value)
{ {
assert(buf.size() > (size_t)offset + 3);
buf[offset] = (byte)(value >> 24); buf[offset] = (byte)(value >> 24);
buf[offset + 1] = (byte)(value >> 16); buf[offset + 1] = (byte)(value >> 16);
buf[offset + 2] = (byte)(value >> 8); buf[offset + 2] = (byte)(value >> 8);
@ -138,12 +156,16 @@ void scsi_command_util::SetInt32(vector<byte>& buf, int offset, uint32_t value)
void scsi_command_util::SetInt16(vector<BYTE>& buf, int offset, int value) void scsi_command_util::SetInt16(vector<BYTE>& buf, int offset, int value)
{ {
assert(buf.size() > (size_t)offset + 1);
buf[offset] = (BYTE)(value >> 8); buf[offset] = (BYTE)(value >> 8);
buf[offset + 1] = (BYTE)value; buf[offset + 1] = (BYTE)value;
} }
void scsi_command_util::SetInt32(vector<BYTE>& buf, int offset, uint32_t value) void scsi_command_util::SetInt32(vector<BYTE>& buf, int offset, uint32_t value)
{ {
assert(buf.size() > (size_t)offset + 3);
buf[offset] = (BYTE)(value >> 24); buf[offset] = (BYTE)(value >> 24);
buf[offset + 1] = (BYTE)(value >> 16); buf[offset + 1] = (BYTE)(value >> 16);
buf[offset + 2] = (BYTE)(value >> 8); buf[offset + 2] = (BYTE)(value >> 8);
@ -152,6 +174,8 @@ void scsi_command_util::SetInt32(vector<BYTE>& buf, int offset, uint32_t value)
void scsi_command_util::SetInt64(vector<BYTE>& buf, int offset, uint64_t value) void scsi_command_util::SetInt64(vector<BYTE>& buf, int offset, uint64_t value)
{ {
assert(buf.size() > (size_t)offset + 7);
buf[offset] = (BYTE)(value >> 56); buf[offset] = (BYTE)(value >> 56);
buf[offset + 1] = (BYTE)(value >> 48); buf[offset + 1] = (BYTE)(value >> 48);
buf[offset + 2] = (BYTE)(value >> 40); buf[offset + 2] = (BYTE)(value >> 40);

View File

@ -14,7 +14,7 @@
#include <vector> #include <vector>
#include <map> #include <map>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
namespace scsi_command_util namespace scsi_command_util
{ {
@ -22,6 +22,7 @@ namespace scsi_command_util
void EnrichFormatPage(map<int, vector<byte>>&, bool, int); void EnrichFormatPage(map<int, vector<byte>>&, bool, int);
void AddAppleVendorModePage(map<int, vector<byte>>&, bool); void AddAppleVendorModePage(map<int, vector<byte>>&, bool);
int GetInt16(const vector<BYTE>&, int);
int GetInt16(const vector<int>&, int); int GetInt16(const vector<int>&, int);
int GetInt24(const vector<int>&, int); int GetInt24(const vector<int>&, int);
uint32_t GetInt32(const vector<int>&, int); uint32_t GetInt32(const vector<int>&, int);

View File

@ -7,23 +7,20 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp) // Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ Emulation of the DaynaPort SCSI Link Ethernet interface ] // [ Emulation of the DaynaPort SCSI Link Ethernet interface ]
// //
// This design is derived from the SLINKCMD.TXT file, as well as David Kuder's // This design is derived from the SLINKCMD.TXT file, as well as David Kuder's
// Tiny SCSI Emulator // Tiny SCSI Emulator
// - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT // - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT
// - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator // - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator
// //
// Additional documentation and clarification is available at the // Additional documentation and clarification is available at the
// following link: // following link:
// - https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link // - https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link
// //
// This does NOT include the file system functionality that is present
// in the Sharp X68000 host bridge.
//
// Note: This requires a DaynaPort SCSI Link driver. // Note: This requires a DaynaPort SCSI Link driver.
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -36,7 +33,7 @@ using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
// TODO Disk must not be the superclass // TODO Disk must not be the superclass
SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP") SCSIDaynaPort::SCSIDaynaPort(int lun) : Disk("SCDP", lun)
{ {
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIDaynaPort::TestUnitReady); dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIDaynaPort::TestUnitReady);
dispatcher.Add(scsi_command::eCmdRead6, "Read6", &SCSIDaynaPort::Read6); dispatcher.Add(scsi_command::eCmdRead6, "Read6", &SCSIDaynaPort::Read6);
@ -133,13 +130,12 @@ vector<byte> SCSIDaynaPort::InquiryInternal() const
int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t) int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
{ {
int rx_packet_size = 0; int rx_packet_size = 0;
auto response = (scsi_resp_read_t*)buf.data(); const auto response = (scsi_resp_read_t*)buf.data();
int requested_length = cdb[4]; const int requested_length = cdb[4];
LOGTRACE("%s Read maximum length %d, (%04X)", __PRETTY_FUNCTION__, requested_length, requested_length) LOGTRACE("%s Read maximum length %d, (%04X)", __PRETTY_FUNCTION__, requested_length, requested_length)
// At host startup, it will send a READ(6) command with a length of 1. We should
// At host startup, it will send a READ(6) command with a length of 1. We should
// respond by going into the status mode with a code of 0x02 // respond by going into the status mode with a code of 0x02
if (requested_length == 1) { if (requested_length == 1) {
return 0; return 0;
@ -169,7 +165,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
LOGTRACE("%s Packet Sz %d (%08X) read: %d", __PRETTY_FUNCTION__, (unsigned int) rx_packet_size, (unsigned int) rx_packet_size, read_count) LOGTRACE("%s Packet Sz %d (%08X) read: %d", __PRETTY_FUNCTION__, (unsigned int) rx_packet_size, (unsigned int) rx_packet_size, read_count)
// This is a very basic filter to prevent unnecessary packets from // This is a very basic filter to prevent unnecessary packets from
// being sent to the SCSI initiator. // being sent to the SCSI initiator.
send_message_to_host = false; send_message_to_host = false;
// The following doesn't seem to work with unicast messages. Temporarily removing the filtering // The following doesn't seem to work with unicast messages. Temporarily removing the filtering
@ -192,7 +188,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
/////// } /////// }
send_message_to_host = true; send_message_to_host = true;
// TODO: We should check to see if this message is in the multicast // TODO: We should check to see if this message is in the multicast
// configuration from SCSI command 0x0D // configuration from SCSI command 0x0D
if (!send_message_to_host) { if (!send_message_to_host) {
@ -207,8 +203,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
// If there are pending packets to be processed, we'll tell the host that the read // If there are pending packets to be processed, we'll tell the host that the read
// length was 0. // length was 0.
if (!m_tap.PendingPackets()) if (!m_tap.PendingPackets()) {
{
response->length = 0; response->length = 0;
response->flags = read_data_flags_t::e_no_more_data; response->flags = read_data_flags_t::e_no_more_data;
return DAYNAPORT_READ_HEADER_SZ; return DAYNAPORT_READ_HEADER_SZ;
@ -239,7 +234,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
// The CRC was already appended by the ctapdriver // The CRC was already appended by the ctapdriver
return size + DAYNAPORT_READ_HEADER_SZ; return size + DAYNAPORT_READ_HEADER_SZ;
} }
// If we got to this point, there are still messages in the queue, so // If we got to this point, there are still messages in the queue, so
// we should loop back and get the next one. // we should loop back and get the next one.
} // end while } // end while
@ -252,8 +247,8 @@ int SCSIDaynaPort::WriteCheck(uint64_t)
{ {
CheckReady(); CheckReady();
if (!m_bTapEnable){ if (!m_bTapEnable) {
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::MEDIUM_NOT_PRESENT); throw scsi_exception(sense_key::UNIT_ATTENTION, asc::MEDIUM_NOT_PRESENT);
} }
return 1; return 1;
@ -279,28 +274,26 @@ int SCSIDaynaPort::WriteCheck(uint64_t)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool SCSIDaynaPort::WriteBytes(const vector<int>& cdb, const vector<BYTE>& buf, uint64_t) bool SCSIDaynaPort::WriteBytes(const vector<int>& cdb, const vector<BYTE>& buf, uint64_t)
{ {
int data_format = cdb[5]; const int data_format = cdb[5];
int data_length = GetInt16(cdb, 3); int data_length = GetInt16(cdb, 3);
if (data_format == 0x00){ if (data_format == 0x00) {
m_tap.Send(buf.data(), data_length); m_tap.Send(buf.data(), data_length);
LOGTRACE("%s Transmitted %u bytes (00 format)", __PRETTY_FUNCTION__, data_length) LOGTRACE("%s Transmitted %u bytes (00 format)", __PRETTY_FUNCTION__, data_length)
} }
else if (data_format == 0x80){ else if (data_format == 0x80) {
// The data length is specified in the first 2 bytes of the payload // The data length is specified in the first 2 bytes of the payload
data_length=buf[1] + (buf[0] << 8); data_length = buf[1] + (((int)buf[0] & 0xff) << 8);
m_tap.Send(&(buf.data()[4]), data_length); m_tap.Send(&(buf.data()[4]), data_length);
LOGTRACE("%s Transmitted %u bytes (80 format)", __PRETTY_FUNCTION__, data_length) LOGTRACE("%s Transmitted %u bytes (80 format)", __PRETTY_FUNCTION__, data_length)
} }
else else {
{
// LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)command->format)
LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, data_format) LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, data_format)
} }
return true; return true;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// RetrieveStats // RetrieveStats
@ -321,45 +314,7 @@ int SCSIDaynaPort::RetrieveStats(const vector<int>& cdb, vector<BYTE>& buf) cons
{ {
memcpy(buf.data(), &m_scsi_link_stats, sizeof(m_scsi_link_stats)); memcpy(buf.data(), &m_scsi_link_stats, sizeof(m_scsi_link_stats));
return (int)min(sizeof(m_scsi_link_stats), (size_t)GetInt16(cdb, 4)); return (int)min(sizeof(m_scsi_link_stats), (size_t)GetInt16(cdb, 3));
}
//---------------------------------------------------------------------------
//
// Enable or Disable the interface
//
// Command: 0e 00 00 00 00 XX (XX = 80 or 00)
// Function: Enable (80) / disable (00) Ethernet interface
// Type: No data transferred
// Notes: After issuing an Enable, the initiator should avoid sending
// any subsequent commands to the device for approximately 0.5
// seconds
//
//---------------------------------------------------------------------------
bool SCSIDaynaPort::EnableInterface(const vector<int>& cdb)
{
bool result;
if (cdb[5] & 0x80) {
result = m_tap.Enable();
if (result) {
LOGINFO("The DaynaPort interface has been ENABLED.")
}
else{
LOGWARN("Unable to enable the DaynaPort Interface")
}
m_tap.Flush();
}
else {
result = m_tap.Disable();
if (result) {
LOGINFO("The DaynaPort interface has been DISABLED.")
}
else{
LOGWARN("Unable to disable the DaynaPort Interface")
}
}
return result;
} }
void SCSIDaynaPort::TestUnitReady() void SCSIDaynaPort::TestUnitReady()
@ -371,14 +326,14 @@ void SCSIDaynaPort::TestUnitReady()
void SCSIDaynaPort::Read6() void SCSIDaynaPort::Read6()
{ {
// Get record number and block number // Get record number and block number
uint32_t record = GetInt24(ctrl->cmd, 1) & 0x1fffff; const uint32_t record = GetInt24(ctrl->cmd, 1) & 0x1fffff;
ctrl->blocks=1; ctrl->blocks=1;
// If any commands have a bogus control value, they were probably not // If any commands have a bogus control value, they were probably not
// generated by the DaynaPort driver so ignore them // generated by the DaynaPort driver so ignore them
if (ctrl->cmd[5] != 0xc0 && ctrl->cmd[5] != 0x80) { if (ctrl->cmd[5] != 0xc0 && ctrl->cmd[5] != 0x80) {
LOGTRACE("%s Control value %d, (%04X), returning invalid CDB", __PRETTY_FUNCTION__, ctrl->cmd[5], ctrl->cmd[5]) LOGTRACE("%s Control value %d, (%04X), returning invalid CDB", __PRETTY_FUNCTION__, ctrl->cmd[5], ctrl->cmd[5])
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
LOGTRACE("%s READ(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, record, ctrl->blocks) LOGTRACE("%s READ(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, record, ctrl->blocks)
@ -397,21 +352,21 @@ void SCSIDaynaPort::Write6()
// Ensure a sufficient buffer size (because it is not transfer for each block) // Ensure a sufficient buffer size (because it is not transfer for each block)
controller->AllocateBuffer(DAYNAPORT_BUFFER_SIZE); controller->AllocateBuffer(DAYNAPORT_BUFFER_SIZE);
int data_format = ctrl->cmd[5]; const int data_format = ctrl->cmd[5];
if (data_format == 0x00) { if (data_format == 0x00) {
ctrl->length = GetInt16(ctrl->cmd, 3); ctrl->length = GetInt16(ctrl->cmd, 3);
} }
else if (data_format == 0x80) { else if (data_format == 0x80) {
ctrl->length = GetInt16(ctrl->cmd, 3 + 8); ctrl->length = GetInt16(ctrl->cmd, 3) + 8;
} }
else { else {
LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, data_format) LOGWARN("%s Unknown data format $%02X", __PRETTY_FUNCTION__, data_format)
} }
LOGTRACE("%s length: %04X (%d) format: %02X", __PRETTY_FUNCTION__, ctrl->length, ctrl->length, data_format) LOGTRACE("%s length: $%04X (%d) format: $%02X", __PRETTY_FUNCTION__, ctrl->length, ctrl->length, data_format)
if (ctrl->length <= 0) { if (ctrl->length <= 0) {
throw scsi_error_exception(); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
// Set next block // Set next block
@ -478,10 +433,12 @@ void SCSIDaynaPort::SetInterfaceMode()
case CMD_SCSILINK_ENABLE: case CMD_SCSILINK_ENABLE:
case CMD_SCSILINK_SET: case CMD_SCSILINK_SET:
LOGWARN("%s Unsupported SetInterface command received: %02X", __PRETTY_FUNCTION__, ctrl->cmd[5]) LOGWARN("%s Unsupported SetInterface command received: %02X", __PRETTY_FUNCTION__, ctrl->cmd[5])
throw scsi_exception();
break; break;
default: default:
LOGWARN("%s Unknown SetInterface command received: %02X", __PRETTY_FUNCTION__, ctrl->cmd[5]) LOGWARN("%s Unknown SetInterface command received: %02X", __PRETTY_FUNCTION__, ctrl->cmd[5])
throw scsi_exception();
break; break;
} }
} }
@ -492,16 +449,45 @@ void SCSIDaynaPort::SetMcastAddr()
if (ctrl->length == 0) { if (ctrl->length == 0) {
LOGWARN("%s Not supported SetMcastAddr Command %02X", __PRETTY_FUNCTION__, ctrl->cmd[2]) LOGWARN("%s Not supported SetMcastAddr Command %02X", __PRETTY_FUNCTION__, ctrl->cmd[2])
throw scsi_error_exception(); throw scsi_exception();
} }
EnterDataOutPhase(); EnterDataOutPhase();
} }
//---------------------------------------------------------------------------
//
// Enable or Disable the interface
//
// Command: 0e 00 00 00 00 XX (XX = 80 or 00)
// Function: Enable (80) / disable (00) Ethernet interface
// Type: No data transferred
// Notes: After issuing an Enable, the initiator should avoid sending
// any subsequent commands to the device for approximately 0.5
// seconds
//
//---------------------------------------------------------------------------
void SCSIDaynaPort::EnableInterface() void SCSIDaynaPort::EnableInterface()
{ {
if (!EnableInterface(ctrl->cmd)) { if (ctrl->cmd[5] & 0x80) {
throw scsi_error_exception(); if (!m_tap.Enable()) {
LOGWARN("Unable to enable the DaynaPort Interface")
throw scsi_exception();
}
m_tap.Flush();
LOGINFO("The DaynaPort interface has been ENABLED")
}
else {
if (!m_tap.Disable()) {
LOGWARN("Unable to disable the DaynaPort Interface")
throw scsi_exception();
}
LOGINFO("The DaynaPort interface has been DISABLED")
} }
EnterStatusPhase(); EnterStatusPhase();

View File

@ -7,12 +7,12 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp) // Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ Emulation of the DaynaPort SCSI Link Ethernet interface ] // [ Emulation of the DaynaPort SCSI Link Ethernet interface ]
// //
// This design is derived from the SLINKCMD.TXT file, as well as David Kuder's // This design is derived from the SLINKCMD.TXT file, as well as David Kuder's
// Tiny SCSI Emulator // Tiny SCSI Emulator
// - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT // - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT
// - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator // - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator
@ -41,14 +41,12 @@
// DaynaPort SCSI Link // DaynaPort SCSI Link
// //
//=========================================================================== //===========================================================================
class SCSIDaynaPort final : public Disk class SCSIDaynaPort : public Disk
{ {
public: public:
SCSIDaynaPort(); explicit SCSIDaynaPort(int);
~SCSIDaynaPort() override = default; ~SCSIDaynaPort() override = default;
SCSIDaynaPort(SCSIDaynaPort&) = delete;
SCSIDaynaPort& operator=(const SCSIDaynaPort&) = delete;
bool Init(const unordered_map<string, string>&) override; bool Init(const unordered_map<string, string>&) override;
void Open(const Filepath& path) override; void Open(const Filepath& path) override;
@ -60,7 +58,6 @@ public:
int WriteCheck(uint64_t block) override; int WriteCheck(uint64_t block) override;
int RetrieveStats(const vector<int>&, vector<BYTE>&) const; int RetrieveStats(const vector<int>&, vector<BYTE>&) const;
bool EnableInterface(const vector<int>&);
void TestUnitReady() override; void TestUnitReady() override;
void Read6() override; void Read6() override;
@ -90,9 +87,10 @@ public:
// The READ response has a header which consists of: // The READ response has a header which consists of:
// 2 bytes - payload size // 2 bytes - payload size
// 4 bytes - status flags // 4 bytes - status flags
static const DWORD DAYNAPORT_READ_HEADER_SZ = 2 + 4; static const uint32_t DAYNAPORT_READ_HEADER_SZ = 2 + 4;
private: private:
using super = Disk; using super = Disk;
Dispatcher<SCSIDaynaPort> dispatcher; Dispatcher<SCSIDaynaPort> dispatcher;

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI Host Bridge for the Sharp X68000 ] // [ SCSI Host Bridge for the Sharp X68000 ]
@ -27,7 +27,7 @@ using namespace std;
using namespace scsi_defs; using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
SCSIBR::SCSIBR() : Disk("SCBR") SCSIBR::SCSIBR(int lun) : Disk("SCBR", lun)
{ {
// Create host file system // Create host file system
fs.Reset(); fs.Reset();
@ -41,7 +41,7 @@ bool SCSIBR::Init(const unordered_map<string, string>& params)
{ {
SetParams(params); SetParams(params);
#ifdef __linux #ifdef __linux__
// TAP Driver Generation // TAP Driver Generation
m_bTapEnable = tap.Init(GetParams()); m_bTapEnable = tap.Init(GetParams());
if (!m_bTapEnable){ if (!m_bTapEnable){
@ -107,13 +107,13 @@ void SCSIBR::TestUnitReady()
int SCSIBR::GetMessage10(const vector<int>& cdb, vector<BYTE>& buf) int SCSIBR::GetMessage10(const vector<int>& cdb, vector<BYTE>& buf)
{ {
// Type // Type
int type = cdb[2]; const int type = cdb[2];
// Function number // Function number
int func = cdb[3]; const int func = cdb[3];
// Phase // Phase
int phase = cdb[9]; const int phase = cdb[9];
switch (type) { switch (type) {
case 1: // Ethernet case 1: // Ethernet
@ -195,16 +195,16 @@ int SCSIBR::GetMessage10(const vector<int>& cdb, vector<BYTE>& buf)
bool SCSIBR::WriteBytes(const vector<int>& cdb, vector<BYTE>& buf, uint64_t) bool SCSIBR::WriteBytes(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
{ {
// Type // Type
int type = cdb[2]; const int type = cdb[2];
// Function number // Function number
int func = cdb[3]; const int func = cdb[3];
// Phase // Phase
int phase = cdb[9]; const int phase = cdb[9];
// Get the number of lights // Get the number of lights
int len = GetInt24(cdb, 6); const int len = GetInt24(cdb, 6);
switch (type) { switch (type) {
case 1: // Ethernet case 1: // Ethernet
@ -257,7 +257,7 @@ void SCSIBR::GetMessage10()
ctrl->length = GetMessage10(ctrl->cmd, controller->GetBuffer()); ctrl->length = GetMessage10(ctrl->cmd, controller->GetBuffer());
if (ctrl->length <= 0) { if (ctrl->length <= 0) {
throw scsi_error_exception(); throw scsi_exception();
} }
// Set next block // Set next block
@ -278,7 +278,7 @@ void SCSIBR::SendMessage10()
{ {
ctrl->length = GetInt24(ctrl->cmd, 6); ctrl->length = GetInt24(ctrl->cmd, 6);
if (ctrl->length <= 0) { if (ctrl->length <= 0) {
throw scsi_error_exception(); throw scsi_exception();
} }
// Ensure a sufficient buffer size (because it is not a transfer for each block) // Ensure a sufficient buffer size (because it is not a transfer for each block)
@ -368,8 +368,8 @@ void SCSIBR::FS_InitDevice(vector<BYTE>& buf)
void SCSIBR::FS_CheckDir(vector<BYTE>& buf) void SCSIBR::FS_CheckDir(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); const int i = sizeof(uint32_t);
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]); const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
@ -384,8 +384,8 @@ void SCSIBR::FS_CheckDir(vector<BYTE>& buf)
void SCSIBR::FS_MakeDir(vector<BYTE>& buf) void SCSIBR::FS_MakeDir(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); const int i = sizeof(uint32_t);
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]); const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
@ -400,8 +400,8 @@ void SCSIBR::FS_MakeDir(vector<BYTE>& buf)
void SCSIBR::FS_RemoveDir(vector<BYTE>& buf) void SCSIBR::FS_RemoveDir(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); const int i = sizeof(uint32_t);
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]); const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
@ -416,7 +416,7 @@ void SCSIBR::FS_RemoveDir(vector<BYTE>& buf)
void SCSIBR::FS_Rename(vector<BYTE>& buf) void SCSIBR::FS_Rename(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]); const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
@ -435,8 +435,8 @@ void SCSIBR::FS_Rename(vector<BYTE>& buf)
void SCSIBR::FS_Delete(vector<BYTE>& buf) void SCSIBR::FS_Delete(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); const int i = sizeof(uint32_t);
const auto *pNamests = (Human68k::namests_t*)&(buf.data()[i]); const auto *pNamests = (Human68k::namests_t*)&(buf.data()[i]);
@ -451,7 +451,7 @@ void SCSIBR::FS_Delete(vector<BYTE>& buf)
void SCSIBR::FS_Attribute(vector<BYTE>& buf) void SCSIBR::FS_Attribute(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]); const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
@ -471,11 +471,11 @@ void SCSIBR::FS_Attribute(vector<BYTE>& buf)
void SCSIBR::FS_Files(vector<BYTE>& buf) void SCSIBR::FS_Files(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
uint32_t nKey = ntohl(*dp); const uint32_t nKey = ntohl(*dp);
i += sizeof(uint32_t); i += sizeof(uint32_t);
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]); const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
@ -512,11 +512,11 @@ void SCSIBR::FS_Files(vector<BYTE>& buf)
void SCSIBR::FS_NFiles(vector<BYTE>& buf) void SCSIBR::FS_NFiles(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
uint32_t nKey = ntohl(*dp); const uint32_t nKey = ntohl(*dp);
i += sizeof(uint32_t); i += sizeof(uint32_t);
auto files = (Human68k::files_t*)&(buf.data()[i]); auto files = (Human68k::files_t*)&(buf.data()[i]);
@ -550,11 +550,11 @@ void SCSIBR::FS_NFiles(vector<BYTE>& buf)
void SCSIBR::FS_Create(vector<BYTE>& buf) void SCSIBR::FS_Create(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
uint32_t nKey = ntohl(*dp); const uint32_t nKey = ntohl(*dp);
i += sizeof(uint32_t); i += sizeof(uint32_t);
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]); const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
@ -564,11 +564,11 @@ void SCSIBR::FS_Create(vector<BYTE>& buf)
i += sizeof(Human68k::fcb_t); i += sizeof(Human68k::fcb_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
uint32_t nAttribute = ntohl(*dp); const uint32_t nAttribute = ntohl(*dp);
i += sizeof(uint32_t); i += sizeof(uint32_t);
auto bp = (int*)&(buf.data()[i]); auto bp = (int*)&(buf.data()[i]);
uint32_t bForce = ntohl(*bp); const uint32_t bForce = ntohl(*bp);
pFcb->fileptr = ntohl(pFcb->fileptr); pFcb->fileptr = ntohl(pFcb->fileptr);
pFcb->mode = ntohs(pFcb->mode); pFcb->mode = ntohs(pFcb->mode);
@ -599,11 +599,11 @@ void SCSIBR::FS_Create(vector<BYTE>& buf)
void SCSIBR::FS_Open(vector<BYTE>& buf) void SCSIBR::FS_Open(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
uint32_t nKey = ntohl(*dp); const uint32_t nKey = ntohl(*dp);
i += sizeof(uint32_t); i += sizeof(uint32_t);
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]); const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
@ -640,11 +640,11 @@ void SCSIBR::FS_Open(vector<BYTE>& buf)
void SCSIBR::FS_Close(vector<BYTE>& buf) void SCSIBR::FS_Close(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
uint32_t nKey = ntohl(*dp); const uint32_t nKey = ntohl(*dp);
i += sizeof(uint32_t); i += sizeof(uint32_t);
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]); auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
@ -678,14 +678,14 @@ void SCSIBR::FS_Close(vector<BYTE>& buf)
void SCSIBR::FS_Read(vector<BYTE>& buf) void SCSIBR::FS_Read(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nKey = ntohl(*dp); const uint32_t nKey = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]); auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
i += sizeof(Human68k::fcb_t); i += sizeof(Human68k::fcb_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
uint32_t nSize = ntohl(*dp); const uint32_t nSize = ntohl(*dp);
pFcb->fileptr = ntohl(pFcb->fileptr); pFcb->fileptr = ntohl(pFcb->fileptr);
pFcb->mode = ntohs(pFcb->mode); pFcb->mode = ntohs(pFcb->mode);
@ -718,14 +718,14 @@ void SCSIBR::FS_Read(vector<BYTE>& buf)
void SCSIBR::FS_Write(vector<BYTE>& buf) void SCSIBR::FS_Write(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nKey = ntohl(*dp); const uint32_t nKey = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]); auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
i += sizeof(Human68k::fcb_t); i += sizeof(Human68k::fcb_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
uint32_t nSize = ntohl(*dp); const uint32_t nSize = ntohl(*dp);
pFcb->fileptr = ntohl(pFcb->fileptr); pFcb->fileptr = ntohl(pFcb->fileptr);
pFcb->mode = ntohs(pFcb->mode); pFcb->mode = ntohs(pFcb->mode);
@ -756,14 +756,14 @@ void SCSIBR::FS_Write(vector<BYTE>& buf)
void SCSIBR::FS_Seek(vector<BYTE>& buf) void SCSIBR::FS_Seek(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nKey = ntohl(*dp); const uint32_t nKey = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]); auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
i += sizeof(Human68k::fcb_t); i += sizeof(Human68k::fcb_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
uint32_t nMode = ntohl(*dp); const uint32_t nMode = ntohl(*dp);
i += sizeof(uint32_t); i += sizeof(uint32_t);
auto ip = (const int*)&(buf.data()[i]); auto ip = (const int*)&(buf.data()[i]);
@ -798,11 +798,11 @@ void SCSIBR::FS_Seek(vector<BYTE>& buf)
void SCSIBR::FS_TimeStamp(vector<BYTE>& buf) void SCSIBR::FS_TimeStamp(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
uint32_t nKey = ntohl(*dp); const uint32_t nKey = ntohl(*dp);
i += sizeof(uint32_t); i += sizeof(uint32_t);
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]); auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
@ -840,7 +840,7 @@ void SCSIBR::FS_TimeStamp(vector<BYTE>& buf)
void SCSIBR::FS_GetCapacity(vector<BYTE>& buf) void SCSIBR::FS_GetCapacity(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
Human68k::capacity_t cap; Human68k::capacity_t cap;
fsresult = fs.GetCapacity(nUnit, &cap); fsresult = fs.GetCapacity(nUnit, &cap);
@ -862,8 +862,8 @@ void SCSIBR::FS_GetCapacity(vector<BYTE>& buf)
void SCSIBR::FS_CtrlDrive(vector<BYTE>& buf) void SCSIBR::FS_CtrlDrive(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); const int i = sizeof(uint32_t);
auto pCtrlDrive = (Human68k::ctrldrive_t*)&(buf.data()[i]); auto pCtrlDrive = (Human68k::ctrldrive_t*)&(buf.data()[i]);
@ -881,7 +881,7 @@ void SCSIBR::FS_CtrlDrive(vector<BYTE>& buf)
void SCSIBR::FS_GetDPB(vector<BYTE>& buf) void SCSIBR::FS_GetDPB(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
Human68k::dpb_t dpb; Human68k::dpb_t dpb;
fsresult = fs.GetDPB(nUnit, &dpb); fsresult = fs.GetDPB(nUnit, &dpb);
@ -905,11 +905,11 @@ void SCSIBR::FS_GetDPB(vector<BYTE>& buf)
void SCSIBR::FS_DiskRead(vector<BYTE>& buf) void SCSIBR::FS_DiskRead(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
uint32_t nSector = ntohl(*dp); const uint32_t nSector = ntohl(*dp);
i += sizeof(uint32_t); i += sizeof(uint32_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
@ -927,7 +927,7 @@ void SCSIBR::FS_DiskRead(vector<BYTE>& buf)
void SCSIBR::FS_DiskWrite(vector<BYTE>& buf) void SCSIBR::FS_DiskWrite(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
fsresult = fs.DiskWrite(nUnit); fsresult = fs.DiskWrite(nUnit);
} }
@ -940,18 +940,18 @@ void SCSIBR::FS_DiskWrite(vector<BYTE>& buf)
void SCSIBR::FS_Ioctrl(vector<BYTE>& buf) void SCSIBR::FS_Ioctrl(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
int i = sizeof(uint32_t); int i = sizeof(uint32_t);
dp = (uint32_t*)&(buf.data()[i]); dp = (uint32_t*)&(buf.data()[i]);
uint32_t nFunction = ntohl(*dp); const uint32_t nFunction = ntohl(*dp);
i += sizeof(uint32_t); i += sizeof(uint32_t);
auto pIoctrl = (Human68k::ioctrl_t*)&(buf.data()[i]); auto pIoctrl = (Human68k::ioctrl_t*)&(buf.data()[i]);
switch (nFunction) { switch (nFunction) {
case 2: case 2:
case (DWORD)-2: case (uint32_t)-2:
pIoctrl->param = htonl(pIoctrl->param); pIoctrl->param = htonl(pIoctrl->param);
break; break;
default: default:
@ -965,7 +965,7 @@ void SCSIBR::FS_Ioctrl(vector<BYTE>& buf)
pIoctrl->media = htons(pIoctrl->media); pIoctrl->media = htons(pIoctrl->media);
break; break;
case 1: case 1:
case (DWORD)-3: case (uint32_t)-3:
pIoctrl->param = htonl(pIoctrl->param); pIoctrl->param = htonl(pIoctrl->param);
break; break;
default: default:
@ -986,7 +986,7 @@ void SCSIBR::FS_Ioctrl(vector<BYTE>& buf)
void SCSIBR::FS_Flush(vector<BYTE>& buf) void SCSIBR::FS_Flush(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
fsresult = fs.Flush(nUnit); fsresult = fs.Flush(nUnit);
} }
@ -999,7 +999,7 @@ void SCSIBR::FS_Flush(vector<BYTE>& buf)
void SCSIBR::FS_CheckMedia(vector<BYTE>& buf) void SCSIBR::FS_CheckMedia(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
fsresult = fs.CheckMedia(nUnit); fsresult = fs.CheckMedia(nUnit);
} }
@ -1012,7 +1012,7 @@ void SCSIBR::FS_CheckMedia(vector<BYTE>& buf)
void SCSIBR::FS_Lock(vector<BYTE>& buf) void SCSIBR::FS_Lock(vector<BYTE>& buf)
{ {
auto dp = (uint32_t*)buf.data(); auto dp = (uint32_t*)buf.data();
uint32_t nUnit = ntohl(*dp); const uint32_t nUnit = ntohl(*dp);
fsresult = fs.Lock(nUnit); fsresult = fs.Lock(nUnit);
} }

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI Host Bridge for the Sharp X68000 ] // [ SCSI Host Bridge for the Sharp X68000 ]
@ -24,18 +24,16 @@
#include <string> #include <string>
#include <array> #include <array>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class SCSIBR final : public Disk class SCSIBR : public Disk
{ {
static constexpr const array<BYTE, 6> bcast_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static constexpr const array<BYTE, 6> bcast_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
public: public:
SCSIBR(); explicit SCSIBR(int);
~SCSIBR() override = default; ~SCSIBR() override = default;
SCSIBR(SCSIBR&) = delete;
SCSIBR& operator=(const SCSIBR&) = delete;
bool Init(const unordered_map<string, string>&) override; bool Init(const unordered_map<string, string>&) override;
bool Dispatch(scsi_command) override; bool Dispatch(scsi_command) override;
@ -101,9 +99,9 @@ private:
void FS_Lock(vector<BYTE>&); // $58 - get exclusive control void FS_Lock(vector<BYTE>&); // $58 - get exclusive control
CFileSys fs; // File system accessor CFileSys fs; // File system accessor
DWORD fsresult = 0; // File system access result code uint32_t fsresult = 0; // File system access result code
array<BYTE, 0x800> fsout; // File system access result buffer array<BYTE, 0x800> fsout; // File system access result buffer
DWORD fsoutlen = 0; // File system access result buffer size uint32_t fsoutlen = 0; // File system access result buffer size
array<BYTE, 0x1000000> fsopt; // File system access buffer array<BYTE, 0x1000000> fsopt; // File system access buffer
DWORD fsoptlen = 0; // File system access buffer size uint32_t fsoptlen = 0; // File system access buffer size
}; };

View File

@ -51,7 +51,7 @@ using namespace scsi_defs;
using namespace ras_util; using namespace ras_util;
using namespace scsi_command_util; using namespace scsi_command_util;
SCSIPrinter::SCSIPrinter() : PrimaryDevice("SCLP") SCSIPrinter::SCSIPrinter(int lun) : PrimaryDevice("SCLP", lun)
{ {
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIPrinter::TestUnitReady); dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIPrinter::TestUnitReady);
dispatcher.Add(scsi_command::eCmdReserve6, "ReserveUnit", &SCSIPrinter::ReserveUnit); dispatcher.Add(scsi_command::eCmdReserve6, "ReserveUnit", &SCSIPrinter::ReserveUnit);
@ -60,6 +60,9 @@ SCSIPrinter::SCSIPrinter() : PrimaryDevice("SCLP")
dispatcher.Add(scsi_command::eCmdSynchronizeBuffer, "SynchronizeBuffer", &SCSIPrinter::SynchronizeBuffer); dispatcher.Add(scsi_command::eCmdSynchronizeBuffer, "SynchronizeBuffer", &SCSIPrinter::SynchronizeBuffer);
dispatcher.Add(scsi_command::eCmdSendDiag, "SendDiagnostic", &SCSIPrinter::SendDiagnostic); dispatcher.Add(scsi_command::eCmdSendDiag, "SendDiagnostic", &SCSIPrinter::SendDiagnostic);
dispatcher.Add(scsi_command::eCmdStartStop, "StopPrint", &SCSIPrinter::StopPrint); dispatcher.Add(scsi_command::eCmdStartStop, "StopPrint", &SCSIPrinter::StopPrint);
SetReady(true);
SetReset(false);
} }
SCSIPrinter::~SCSIPrinter() SCSIPrinter::~SCSIPrinter()
@ -145,15 +148,15 @@ void SCSIPrinter::Print()
{ {
CheckReservation(); CheckReservation();
uint32_t length = GetInt24(ctrl->cmd, 2); const uint32_t length = GetInt24(ctrl->cmd, 2);
LOGTRACE("Receiving %d byte(s) to be printed", length) LOGTRACE("Receiving %d byte(s) to be printed", length)
if (length > controller->GetBufferSize()) { if (length > controller->GetBuffer().size()) {
LOGERROR("%s", string("Transfer buffer overflow: Buffer size is " + to_string(controller->GetBufferSize()) + LOGERROR("%s", ("Transfer buffer overflow: Buffer size is " + to_string(controller->GetBuffer().size()) +
" bytes, " + to_string(length) + " bytes expected").c_str()) " bytes, " + to_string(length) + " bytes expected").c_str())
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
ctrl->length = length; ctrl->length = length;
@ -167,11 +170,11 @@ void SCSIPrinter::SynchronizeBuffer()
CheckReservation(); CheckReservation();
if (fd == -1) { if (fd == -1) {
throw scsi_error_exception(); throw scsi_exception();
} }
// Make the file readable for the lp user // Make the file readable for the lp user
fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); //NOSONAR Granting permissions to "others" is required here
struct stat st; struct stat st;
fstat(fd, &st); fstat(fd, &st);
@ -180,7 +183,7 @@ void SCSIPrinter::SynchronizeBuffer()
fd = -1; fd = -1;
string cmd = GetParam("cmd"); string cmd = GetParam("cmd");
size_t file_position = cmd.find("%f"); const size_t file_position = cmd.find("%f");
assert(file_position != string::npos); assert(file_position != string::npos);
cmd.replace(file_position, 2, filename); cmd.replace(file_position, 2, filename);
cmd = "sudo -u lp " + cmd; cmd = "sudo -u lp " + cmd;
@ -194,7 +197,7 @@ void SCSIPrinter::SynchronizeBuffer()
unlink(filename); unlink(filename);
throw scsi_error_exception(); throw scsi_exception();
} }
unlink(filename); unlink(filename);
@ -217,7 +220,7 @@ void SCSIPrinter::StopPrint()
bool SCSIPrinter::WriteByteSequence(vector<BYTE>& buf, uint32_t length) bool SCSIPrinter::WriteByteSequence(vector<BYTE>& buf, uint32_t length)
{ {
if (fd == -1) { if (fd == -1) {
strcpy(filename, TMP_FILE_PATTERN); strcpy(filename, TMP_FILE_PATTERN); //NOSONAR Using strcpy is safe here
fd = mkstemp(filename); fd = mkstemp(filename);
if (fd == -1) { if (fd == -1) {
LOGERROR("Can't create printer output file '%s': %s", filename, strerror(errno)) LOGERROR("Can't create printer output file '%s': %s", filename, strerror(errno))
@ -229,7 +232,7 @@ bool SCSIPrinter::WriteByteSequence(vector<BYTE>& buf, uint32_t length)
LOGTRACE("Appending %d byte(s) to printer output file '%s'", length, filename) LOGTRACE("Appending %d byte(s) to printer output file '%s'", length, filename)
auto num_written = (uint32_t)write(fd, buf.data(), length); const auto num_written = (uint32_t)write(fd, buf.data(), length);
return num_written == length; return num_written == length;
} }
@ -248,7 +251,7 @@ void SCSIPrinter::CheckReservation()
LOGTRACE("Unknown initiator tries to access reserved device ID %d, LUN %d", GetId(), GetLun()) LOGTRACE("Unknown initiator tries to access reserved device ID %d, LUN %d", GetId(), GetLun())
} }
throw scsi_error_exception(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION, throw scsi_exception(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION,
status::RESERVATION_CONFLICT); status::RESERVATION_CONFLICT);
} }

View File

@ -15,19 +15,17 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
class SCSIPrinter final : public PrimaryDevice, public ScsiPrinterCommands //NOSONAR Custom destructor cannot be removed class SCSIPrinter : public PrimaryDevice, ScsiPrinterCommands //NOSONAR Custom destructor cannot be removed
{ {
static constexpr const char *TMP_FILE_PATTERN = "/tmp/rascsi_sclp-XXXXXX"; static constexpr const char *TMP_FILE_PATTERN = "/tmp/rascsi_sclp-XXXXXX"; //NOSONAR Using /tmp is safe
static const int TMP_FILENAME_LENGTH = string_view(TMP_FILE_PATTERN).size(); static const int TMP_FILENAME_LENGTH = string_view(TMP_FILE_PATTERN).size();
static const int NOT_RESERVED = -2; static const int NOT_RESERVED = -2;
public: public:
SCSIPrinter(); explicit SCSIPrinter(int);
~SCSIPrinter() override; ~SCSIPrinter() override;
SCSIPrinter(SCSIPrinter&) = delete;
SCSIPrinter& operator=(const SCSIPrinter&) = delete;
bool Dispatch(scsi_command) override; bool Dispatch(scsi_command) override;

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI CD-ROM ] // [ SCSI CD-ROM ]
@ -24,7 +24,7 @@
using namespace scsi_defs; using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
SCSICD::SCSICD(const unordered_set<uint32_t>& sector_sizes) : Disk("SCCD") SCSICD::SCSICD(int lun, const unordered_set<uint32_t>& sector_sizes) : Disk("SCCD", lun)
{ {
SetSectorSizes(sector_sizes); SetSectorSizes(sector_sizes);
@ -90,12 +90,9 @@ void SCSICD::Open(const Filepath& path)
assert(GetBlockCount() > 0); assert(GetBlockCount() > 0);
super::Open(path); super::Open(path);
FileSupport::SetPath(path); SetPath(path);
SetUpCache(path); SetUpCache(path, 0, rawfile);
// Set RAW flag
cache->SetRawMode(rawfile);
// Attention if ready // Attention if ready
if (IsReady()) { if (IsReady()) {
@ -103,7 +100,7 @@ void SCSICD::Open(const Filepath& path)
} }
} }
void SCSICD::OpenCue(const Filepath& /*path*/) const void SCSICD::OpenCue(const Filepath&) const
{ {
throw io_exception("Opening CUE CD-ROM files is not supported"); throw io_exception("Opening CUE CD-ROM files is not supported");
} }
@ -117,8 +114,8 @@ void SCSICD::OpenIso(const Filepath& path)
} }
// Get file size // Get file size
off_t file_size = fio.GetFileSize(); const off_t size = fio.GetFileSize();
if (file_size < 0x800) { if (size < 0x800) {
fio.Close(); fio.Close();
throw io_exception("ISO CD-ROM file size must be at least 2048 bytes"); throw io_exception("ISO CD-ROM file size must be at least 2048 bytes");
} }
@ -157,16 +154,16 @@ void SCSICD::OpenIso(const Filepath& path)
if (rawfile) { if (rawfile) {
// Size must be a multiple of 2536 // Size must be a multiple of 2536
if (file_size % 2536) { if (size % 2536) {
throw io_exception("Raw ISO CD-ROM file size must be a multiple of 2536 bytes but is " throw io_exception("Raw ISO CD-ROM file size must be a multiple of 2536 bytes but is "
+ to_string(file_size) + " bytes"); + to_string(size) + " bytes");
} }
// Set the number of blocks // Set the number of blocks
SetBlockCount((DWORD)(file_size / 0x930)); SetBlockCount((uint32_t)(size / 0x930));
} else { } else {
// Set the number of blocks // Set the number of blocks
SetBlockCount((DWORD)(file_size >> GetSectorSizeShiftCount())); SetBlockCount((uint32_t)(size >> GetSectorSizeShiftCount()));
} }
// Create only one data track // Create only one data track
@ -200,7 +197,7 @@ void SCSICD::OpenPhysical(const Filepath& path)
size = (size / 512) * 512; size = (size / 512) * 512;
// Set the number of blocks // Set the number of blocks
SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount())); SetBlockCount((uint32_t)(size >> GetSectorSizeShiftCount()));
// Create only one data track // Create only one data track
assert(!tracks.size()); assert(!tracks.size());
@ -278,11 +275,11 @@ int SCSICD::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t block)
CheckReady(); CheckReady();
// Search for the track // Search for the track
int index = SearchTrack((int)block); const int index = SearchTrack((int)block);
// If invalid, out of range // If invalid, out of range
if (index < 0) { if (index < 0) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
} }
assert(tracks[index]); assert(tracks[index]);
@ -296,9 +293,9 @@ int SCSICD::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t block)
// Recreate the disk cache // Recreate the disk cache
Filepath path; Filepath path;
tracks[index]->GetPath(path); tracks[index]->GetPath(path);
// Re-assign disk cache (no need to save) // Re-assign disk cache (no need to save)
cache.reset(new DiskCache(path, GetSectorSizeShiftCount(), (uint32_t)GetBlockCount())); ResizeCache(path, rawfile);
cache->SetRawMode(rawfile);
// Reset data index // Reset data index
dataindex = index; dataindex = index;
@ -318,17 +315,17 @@ int SCSICD::ReadTocInternal(const vector<int>& cdb, vector<BYTE>& buf)
assert(tracks[0]); assert(tracks[0]);
// Get allocation length, clear buffer // Get allocation length, clear buffer
int length = GetInt16(cdb, 7); const int length = GetInt16(cdb, 7);
fill_n(buf.data(), length, 0); fill_n(buf.data(), length, 0);
// Get MSF Flag // Get MSF Flag
bool msf = cdb[1] & 0x02; const bool msf = cdb[1] & 0x02;
// Get and check the last track number // Get and check the last track number
int last = tracks[tracks.size() - 1]->GetTrackNo(); const int last = tracks[tracks.size() - 1]->GetTrackNo();
// Except for AA // Except for AA
if (cdb[6] > last && cdb[6] != 0xaa) { if (cdb[6] > last && cdb[6] != 0xaa) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
// Check start index // Check start index
@ -361,12 +358,12 @@ int SCSICD::ReadTocInternal(const vector<int>& cdb, vector<BYTE>& buf)
} }
// Otherwise, error // Otherwise, error
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
} }
// Number of track descriptors returned this time (number of loops) // Number of track descriptors returned this time (number of loops)
int loop = last - tracks[index]->GetTrackNo() + 1; const int loop = last - tracks[index]->GetTrackNo() + 1;
assert(loop >= 1); assert(loop >= 1);
// Create header // Create header
@ -410,11 +407,11 @@ void SCSICD::GetEventStatusNotification()
{ {
if (!(ctrl->cmd[1] & 0x01)) { if (!(ctrl->cmd[1] & 0x01)) {
// Asynchronous notification is optional and not supported by rascsi // Asynchronous notification is optional and not supported by rascsi
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
LOGTRACE("Received request for event polling, which is currently not supported") LOGTRACE("Received request for event polling, which is currently not supported")
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -427,7 +424,7 @@ void SCSICD::LBAtoMSF(uint32_t lba, BYTE *msf) const
// 75 and 75*60 get the remainder // 75 and 75*60 get the remainder
uint32_t m = lba / (75 * 60); uint32_t m = lba / (75 * 60);
uint32_t s = lba % (75 * 60); uint32_t s = lba % (75 * 60);
uint32_t f = s % 75; const uint32_t f = s % 75;
s /= 75; s /= 75;
// The base point is M=0, S=2, F=0 // The base point is M=0, S=2, F=0
@ -462,7 +459,7 @@ void SCSICD::ClearTrack()
// * Returns -1 if not found // * Returns -1 if not found
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int SCSICD::SearchTrack(DWORD lba) const int SCSICD::SearchTrack(uint32_t lba) const
{ {
// Track loop // Track loop
for (size_t i = 0; i < tracks.size(); i++) { for (size_t i = 0; i < tracks.size(); i++) {

View File

@ -7,30 +7,24 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI CD-ROM ]
//
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#pragma once #pragma once
#include "disk.h"
#include "filepath.h" #include "filepath.h"
#include "cd_track.h" #include "cd_track.h"
#include "file_support.h" #include "disk.h"
#include "interfaces/scsi_mmc_commands.h" #include "interfaces/scsi_mmc_commands.h"
#include "interfaces/scsi_primary_commands.h"
class SCSICD : public Disk, public ScsiMmcCommands, public FileSupport class SCSICD : public Disk, private ScsiMmcCommands
{ {
public: public:
explicit SCSICD(const unordered_set<uint32_t>&); SCSICD(int, const unordered_set<uint32_t>&);
~SCSICD() override = default; ~SCSICD() override = default;
SCSICD(SCSICD&) = delete;
SCSICD& operator=(const SCSICD&) = delete;
bool Dispatch(scsi_command) override; bool Dispatch(scsi_command) override;
@ -70,7 +64,7 @@ private:
// Track management // Track management
void ClearTrack(); // Clear the track void ClearTrack(); // Clear the track
int SearchTrack(DWORD lba) const; // Track search int SearchTrack(uint32_t lba) const; // Track search
vector<unique_ptr<CDTrack>> tracks; // Track opbject references vector<unique_ptr<CDTrack>> tracks; // Track opbject references
int dataindex = -1; // Current data track int dataindex = -1; // Current data track
int audioindex = -1; // Current audio track int audioindex = -1; // Current audio track

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI hard disk ] // [ SCSI hard disk ]
@ -22,11 +22,9 @@
using namespace scsi_command_util; using namespace scsi_command_util;
SCSIHD::SCSIHD(const unordered_set<uint32_t>& sector_sizes, bool removable, scsi_defs::scsi_level level) SCSIHD::SCSIHD(int lun, const unordered_set<uint32_t>& sector_sizes, bool removable, scsi_defs::scsi_level level)
: Disk(removable ? "SCRM" : "SCHD") : Disk(removable ? "SCRM" : "SCHD", lun), scsi_level(level)
{ {
scsi_level = level;
SetSectorSizes(sector_sizes); SetSectorSizes(sector_sizes);
} }
@ -41,8 +39,14 @@ void SCSIHD::FinalizeSetup(const Filepath &path, off_t size, off_t image_offset)
if (!IsRemovable()) { if (!IsRemovable()) {
uint64_t capacity = GetBlockCount() * GetSectorSizeInBytes(); uint64_t capacity = GetBlockCount() * GetSectorSizeInBytes();
string unit; string unit;
if (capacity >= 1048576) { // 10 GiB and more
capacity /= 1048576; if (capacity >= 1'099'511'627'776) {
capacity /= 1'099'511'627'776;
unit = "GiB";
}
// 1 MiB and more
else if (capacity >= 1'048'576) {
capacity /= 1'048'576;
unit = "MiB"; unit = "MiB";
} }
else { else {
@ -58,8 +62,8 @@ void SCSIHD::FinalizeSetup(const Filepath &path, off_t size, off_t image_offset)
SetProtectable(true); SetProtectable(true);
SetProtected(false); SetProtected(false);
Disk::Open(path); super::Open(path);
FileSupport::SetPath(path); SetPath(path);
SetUpCache(path, image_offset); SetUpCache(path, image_offset);
} }
@ -75,17 +79,17 @@ void SCSIHD::Open(const Filepath& path)
} }
// Get file size // Get file size
off_t file_size = fio.GetFileSize(); off_t size = fio.GetFileSize();
fio.Close(); fio.Close();
// Sector size (default 512 bytes) and number of blocks // Sector size (default 512 bytes) and number of blocks
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512); SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512);
SetBlockCount((DWORD)(file_size >> GetSectorSizeShiftCount())); SetBlockCount((uint32_t)(size >> GetSectorSizeShiftCount()));
// Effective size must be a multiple of the sector size // Effective size must be a multiple of the sector size
file_size = (file_size / GetSectorSizeInBytes()) * GetSectorSizeInBytes(); size = (size / GetSectorSizeInBytes()) * GetSectorSizeInBytes();
FinalizeSetup(path, file_size); FinalizeSetup(path, size);
} }
vector<byte> SCSIHD::InquiryInternal() const vector<byte> SCSIHD::InquiryInternal() const

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI hard disk ] // [ SCSI hard disk ]
@ -17,19 +17,16 @@
#pragma once #pragma once
#include "disk.h" #include "disk.h"
#include "file_support.h"
#include "filepath.h" #include "filepath.h"
class SCSIHD : public Disk, public FileSupport class SCSIHD : public Disk
{ {
static constexpr const char *DEFAULT_PRODUCT = "SCSI HD"; static constexpr const char *DEFAULT_PRODUCT = "SCSI HD";
public: public:
SCSIHD(const unordered_set<uint32_t>&, bool, scsi_defs::scsi_level = scsi_level::SCSI_2); SCSIHD(int, const unordered_set<uint32_t>&, bool, scsi_defs::scsi_level = scsi_level::SCSI_2);
~SCSIHD() override = default; ~SCSIHD() override = default;
SCSIHD(SCSIHD&) = delete;
SCSIHD& operator=(const SCSIHD&) = delete;
void FinalizeSetup(const Filepath&, off_t, off_t = 0); void FinalizeSetup(const Filepath&, off_t, off_t = 0);
@ -44,5 +41,7 @@ public:
private: private:
using super = Disk;
scsi_defs::scsi_level scsi_level; scsi_defs::scsi_level scsi_level;
}; };

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI NEC "Genuine" Hard Disk] // [ SCSI NEC "Genuine" Hard Disk]
@ -54,18 +54,18 @@ void SCSIHD_NEC::Open(const Filepath& path)
} }
// Get file size // Get file size
off_t file_size = fio.GetFileSize(); off_t size = fio.GetFileSize();
// NEC root sector // NEC root sector
array<BYTE, 512> root_sector; array<BYTE, 512> root_sector;
if (file_size >= (off_t)root_sector.size() && !fio.Read(root_sector.data(), root_sector.size())) { if (size >= (off_t)root_sector.size() && !fio.Read(root_sector.data(), root_sector.size())) {
fio.Close(); fio.Close();
throw io_exception("Can't read NEC hard disk file root sector"); throw io_exception("Can't read NEC hard disk file root sector");
} }
fio.Close(); fio.Close();
// Effective size must be a multiple of 512 // Effective size must be a multiple of 512
file_size = (file_size / 512) * 512; size = (size / 512) * 512;
int image_size = 0; int image_size = 0;
int sector_size = 0; int sector_size = 0;
@ -76,11 +76,11 @@ void SCSIHD_NEC::Open(const Filepath& path)
if (const char *ext = path.GetFileExt(); !strcasecmp(ext, ".hdn")) { if (const char *ext = path.GetFileExt(); !strcasecmp(ext, ".hdn")) {
// Assuming sector size 512, number of sectors 25, number of heads 8 as default settings // Assuming sector size 512, number of sectors 25, number of heads 8 as default settings
image_offset = 0; image_offset = 0;
image_size = (int)file_size; image_size = (int)size;
sector_size = 512; sector_size = 512;
sectors = 25; sectors = 25;
heads = 8; heads = 8;
cylinders = (int)(file_size >> 9); cylinders = (int)(size >> 9);
cylinders >>= 3; cylinders >>= 3;
cylinders /= 25; cylinders /= 25;
} }
@ -113,23 +113,23 @@ void SCSIHD_NEC::Open(const Filepath& path)
} }
// Image size consistency check // Image size consistency check
if (image_offset + image_size > file_size || image_size % sector_size != 0) { if (image_offset + image_size > size || image_size % sector_size != 0) {
throw io_exception("Image size consistency check failed"); throw io_exception("Image size consistency check failed");
} }
// Calculate sector size // Calculate sector size
for (file_size = 16; file_size > 0; --file_size) { for (size = 16; size > 0; --size) {
if ((1 << file_size) == sector_size) if ((1 << size) == sector_size)
break; break;
} }
if (file_size <= 0 || file_size > 16) { if (size <= 0 || size > 16) {
throw io_exception("Invalid NEC disk size"); throw io_exception("Invalid NEC disk size");
} }
SetSectorSizeShiftCount((uint32_t)file_size); SetSectorSizeShiftCount((uint32_t)size);
SetBlockCount(image_size >> GetSectorSizeShiftCount()); SetBlockCount(image_size >> GetSectorSizeShiftCount());
FinalizeSetup(path, file_size, image_offset); FinalizeSetup(path, size, image_offset);
} }
vector<byte> SCSIHD_NEC::InquiryInternal() const vector<byte> SCSIHD_NEC::InquiryInternal() const

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI NEC "Genuine" Hard Disk] // [ SCSI NEC "Genuine" Hard Disk]
@ -20,7 +20,7 @@
#include <unordered_set> #include <unordered_set>
#include <map> #include <map>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
//=========================================================================== //===========================================================================
// //
@ -31,10 +31,8 @@ class SCSIHD_NEC : public SCSIHD
{ {
public: public:
SCSIHD_NEC() : SCSIHD(sector_sizes, false) {} explicit SCSIHD_NEC(int lun) : SCSIHD(lun, sector_sizes, false) {}
~SCSIHD_NEC() override = default; ~SCSIHD_NEC() override = default;
SCSIHD_NEC(SCSIHD_NEC&) = delete;
SCSIHD_NEC& operator=(const SCSIHD_NEC&) = delete;
void Open(const Filepath&) override; void Open(const Filepath&) override;

View File

@ -19,7 +19,7 @@
using namespace scsi_command_util; using namespace scsi_command_util;
SCSIMO::SCSIMO(const unordered_set<uint32_t>& sector_sizes) : Disk("SCMO") SCSIMO::SCSIMO(int lun, const unordered_set<uint32_t>& sector_sizes) : Disk("SCMO", lun)
{ {
SetSectorSizes(sector_sizes); SetSectorSizes(sector_sizes);
@ -45,24 +45,24 @@ void SCSIMO::Open(const Filepath& path)
} }
// Get file size // Get file size
off_t file_size = fio.GetFileSize(); off_t size = fio.GetFileSize();
fio.Close(); fio.Close();
// For some capacities there are hard-coded, well-defined sector sizes and block counts // For some capacities there are hard-coded, well-defined sector sizes and block counts
if (!SetGeometryForCapacity(file_size)) { if (!SetGeometryForCapacity(size)) {
// Sector size (default 512 bytes) and number of blocks // Sector size (default 512 bytes) and number of blocks
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512); SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512);
SetBlockCount(file_size >> GetSectorSizeShiftCount()); SetBlockCount(size >> GetSectorSizeShiftCount());
} }
SetReadOnly(false); SetReadOnly(false);
SetProtectable(true); SetProtectable(true);
SetProtected(false); SetProtected(false);
Disk::Open(path); super::Open(path);
FileSupport::SetPath(path); SetPath(path);
SetUpCache(path); SetUpCache(path, 0);
// Attention if ready // Attention if ready
if (IsReady()) { if (IsReady()) {

View File

@ -15,19 +15,16 @@
#pragma once #pragma once
#include "disk.h" #include "disk.h"
#include "file_support.h"
#include "filepath.h" #include "filepath.h"
using Geometry = pair<uint32_t, uint32_t>; using Geometry = pair<uint32_t, uint32_t>;
class SCSIMO : public Disk, public FileSupport class SCSIMO : public Disk
{ {
public: public:
explicit SCSIMO(const unordered_set<uint32_t>&); SCSIMO(int, const unordered_set<uint32_t>&);
~SCSIMO() override = default; ~SCSIMO() override = default;
SCSIMO(SCSIMO&) = delete;
SCSIMO& operator=(const SCSIMO&) = delete;
void Open(const Filepath&) override; void Open(const Filepath&) override;
@ -42,6 +39,8 @@ protected:
private: private:
using super = Disk;
void AddOptionPage(map<int, vector<byte>>&, bool) const; void AddOptionPage(map<int, vector<byte>>&, bool) const;
bool SetGeometryForCapacity(uint64_t); bool SetGeometryForCapacity(uint64_t);

View File

@ -11,6 +11,7 @@
#include "fileio.h" #include "fileio.h"
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <cassert>
Fileio::~Fileio() Fileio::~Fileio()
{ {
@ -30,7 +31,7 @@ bool Fileio::Open(const char *fname, OpenMode mode, bool directIO)
} }
// Default mode // Default mode
mode_t omode = directIO ? O_DIRECT : 0; const mode_t omode = directIO ? O_DIRECT : 0;
switch (mode) { switch (mode) {
case OpenMode::ReadOnly: case OpenMode::ReadOnly:
@ -111,10 +112,10 @@ off_t Fileio::GetFileSize() const
assert(handle >= 0); assert(handle >= 0);
// Get file position in 64bit // Get file position in 64bit
off_t cur = lseek(handle, 0, SEEK_CUR); const off_t cur = lseek(handle, 0, SEEK_CUR);
// Get file size in64bitで // Get file size in64bitで
off_t end = lseek(handle, 0, SEEK_END); const off_t end = lseek(handle, 0, SEEK_END);
// Return to start position // Return to start position
Seek(cur); Seek(cur);

View File

@ -11,7 +11,7 @@
#pragma once #pragma once
#include "filepath.h" #include "filepath.h"
#include <sys/types.h> #include <cstdlib>
class Fileio class Fileio
{ {
@ -25,8 +25,8 @@ public:
Fileio() = default; Fileio() = default;
virtual ~Fileio(); virtual ~Fileio();
Fileio(Fileio&) = delete; Fileio(Fileio&) = default;
Fileio& operator=(const Fileio&) = delete; Fileio& operator=(const Fileio&) = default;
bool Open(const char *fname, OpenMode mode); bool Open(const char *fname, OpenMode mode);
bool Open(const Filepath& path, OpenMode mode); bool Open(const Filepath& path, OpenMode mode);

View File

@ -8,16 +8,11 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "os.h"
#include "filepath.h" #include "filepath.h"
#include "config.h"
#include "fileio.h"
#include <libgen.h> #include <libgen.h>
#include <cstdlib>
Filepath::Filepath() #include <cstring>
{ #include <cassert>
Clear();
}
Filepath& Filepath::operator=(const Filepath& path) Filepath& Filepath::operator=(const Filepath& path)
{ {
@ -27,15 +22,6 @@ Filepath& Filepath::operator=(const Filepath& path)
return *this; return *this;
} }
void Filepath::Clear()
{
// Clear the path and each part
m_szPath[0] = '\0';
m_szDir[0] = '\0';
m_szFile[0] = '\0';
m_szExt[0] = '\0';
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// File settings (user) for MBCS // File settings (user) for MBCS
@ -69,7 +55,7 @@ void Filepath::Split()
char *pDir = strdup(m_szPath); char *pDir = strdup(m_szPath);
const char *pDirName = dirname(pDir); const char *pDirName = dirname(pDir);
char *pBase = strdup(m_szPath); char *pBase = strdup(m_szPath);
char *pBaseName = basename(pBase); const char *pBaseName = basename(pBase);
const char *pExtName = strrchr(pBaseName, '.'); const char *pExtName = strrchr(pBaseName, '.');
// Transmit // Transmit

View File

@ -14,8 +14,6 @@
using TCHAR = char; using TCHAR = char;
class Fileio;
static const int _MAX_EXT = 256; static const int _MAX_EXT = 256;
static const int _MAX_DIR = 256; static const int _MAX_DIR = 256;
static const int _MAX_PATH = 260; static const int _MAX_PATH = 260;
@ -30,15 +28,13 @@ static const int FILEPATH_MAX = _MAX_PATH;
//=========================================================================== //===========================================================================
class Filepath class Filepath
{ {
public: public:
Filepath(); Filepath() = default;
virtual ~Filepath() = default; ~Filepath() = default;
Filepath(Filepath&) = delete; Filepath(Filepath&) = default;
Filepath& operator=(const Filepath&); Filepath& operator=(const Filepath&);
void Clear();
void SetPath(const char *); // File settings (user) for MBCS void SetPath(const char *); // File settings (user) for MBCS
const char *GetPath() const { return m_szPath; } // Get path name const char *GetPath() const { return m_szPath; } // Get path name
const char *GetFileExt() const; // Get short name (LPCTSTR) const char *GetFileExt() const; // Get short name (LPCTSTR)
@ -46,10 +42,10 @@ public:
private: private:
void Split(); // Split the path void Split(); // Split the path
TCHAR m_szPath[_MAX_PATH]; // File path TCHAR m_szPath[_MAX_PATH] = {}; // File path
TCHAR m_szDir[_MAX_DIR]; // Directory TCHAR m_szDir[_MAX_DIR] = {}; // Directory
TCHAR m_szFile[_MAX_FNAME]; // File TCHAR m_szFile[_MAX_FNAME] = {}; // File
TCHAR m_szExt[_MAX_EXT]; // Extension TCHAR m_szExt[_MAX_EXT] = {}; // Extension
static TCHAR FileExt[_MAX_FNAME + _MAX_DIR]; // Short name (TCHAR) static TCHAR FileExt[_MAX_FNAME + _MAX_DIR]; // Short name (TCHAR)
}; };

View File

@ -20,7 +20,7 @@
#include "config.h" #include "config.h"
#include "log.h" #include "log.h"
#include <array> #include <array>
#ifdef __linux #ifdef __linux__
#include <sys/epoll.h> #include <sys/epoll.h>
#endif #endif

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