mirror of
https://github.com/akuker/RASCSI.git
synced 2024-11-25 20:33:35 +00:00
Refactoring, device handling extensions, additional settings, improved error handling, 64 bit OS support, fixed issues (#184)
* Device type unification, support of removable media
* Added support for .hdr extension
* Removable flag cleanup
* Manpage update
* Enriched PbOperation with PbDevice
* Added file size to PbImageFile
* Added device list support
* Set image_file
* Make remote interface more robust by ignoring SIGPIPE
* Return status only once
* Fixed typo
* Error handling update
* When starting rascsi parse everything before attaching devices
* Added dry run mode
* Comment update
* Updated logging
* Added Device base class, Disk class inherits from it
* Renaming
* Use vectors for controllers and disks, as preparation for using maps
* Updated file support handling
* Comment update
* DaynaPort and Bridge inherit from Device instead of Disk
* ProcessCmd() now works with devices instead of disks
* Renaming
* Added DeviceFactory
* Improved factory
* Comment update
* protected disk_t
* Code cleanup, added translations
* Device name can be set for rascsi
* rasctl can set device name
* Manpage update
* Manpage update
* Formatting update
* Check for missing name
* Initialize fd
* Initialize type
* Fixed string length issue
* Updated capacity formatting
* Fixed typo
* Split PbDevice into device and device definition
* Added TODO
* Renaming
* Renaming
* Device types can be explicitly specified with -t (no FILE:TYPE syntax anymore)
* Fixed compile-time issue
* Removed unused Append mode, updated read-only handling
* Type handling and manpage update
* Cleanup
* rasctl parser cleanup
* Review
* Constructor update
* Added .hdr (SCRM) support to web interface, tested web interface
* Default folder can be set remotely
* Removed deprecated operation
* DETACH supports all parameters in order to detach all devices
* include cleanup
* Logging should not depend on NDEBUG, for RaSCSI it is not peformance-critical
* INFO is default log level
* Exception renaming
* Updated GetPaddedName()
* Inheritance update
* Added BlockDevice class
* Removed unused code
* Updated typedefs
* Revert "Updated typedefs"
This reverts commit 546b46215a
.
* Removed unused code
* Fixed warnign
* Use standard C++ integer types, use streams to resolve printf data type issues
* Added TODOs
* Added TODO
* Renaming
* Added TODO
* Added TODO
* Improved dry-run
* Code cleanup
* Updated handling of unknown options, code review and cleanup
* Manpage update
* Added PrimaryDevice
* Include cleanup
* Added pure virtual methods
* Comment updates
* Split rasutil
* Replaced some occurrences of BOOL
* Removed obsolete RASCSI definition in xm6.h
* Removed unused code, updated TODOs, replaced BOOL
* Added capacity check (issue #192)
* Fixed (most likely) https://github.com/akuker/RASCSI/issues/191
* Fixed wrong error messages
* For root the default image folder is /home/pi/images, updated error handling
* Dynaport code review
* Improved error handling
* Implemented READ CAPACITY(16)
* Comment update
* Commands can be 16 bytes long
* Implemented READ/WRITE/VERIFY(16)
* Comment update
* Renamed method to reflect the name of the respective SCSI command
* Do not created devices during dryRun
* Fixed padding of SCSIHD_APPLE vendor and product
* Initial implementation
* Updated ReportLuns
* Byte count update
* Fixed typo
* Finalized REPORT LUNS
* Removed TODO
* Updated TODO
* TODO update
* Updated device factory
* Comment update
* 64 bit update, tested on Ubuntu 64 bit system
* Removed assertion
* SCSI hard disks always have Apple specific mode pages (resolves issue #193)
* Error messsage update, 64 bit cleanup
* Reduced streams usage
* Updated handling of device flags
* MOs are protectable
* Removed duplicate error code handling
* Removed duplicate code
* Fixed CmdReadToc buffer overflow (https://github.com/akuker/RASCSI/issues/194)
* Added naive implementation of GET EVENT STATUS NOTIFICATION to avoid wranings
* HD must set removable device bit if the media is removable
* Removed duplicate logging
* Updated daynaport additional length
* Removed broken daynaport REQUEST SENSE. Successfully tested with my Mac.
* EnableInterface should not always return TRUE
* Updated Inquiry
* Updated LUN handling
* Replaced incorrect free by delete
* Updated comments and write-protection handling
* Made default HD name consistent
* STATUS_NOERROR is default
* Fixed Eject
* More eject handling updates
* Manpage updates
* Logging update
* Changed debug level
* Logging update
* Log capacity of all media types
* Logging update
* Encapsulated disk.blocks
* Encapsulated sector size
* Added overrides
* Added more overrides
* Fixed error message
* Fixed typos
* Fixed logging
* Added logging
* Use PrimaryDevice when calling Inquiry
* Comment update
* Changed default buffer size for testing
* Reverted last change
* Removed debug output
* De-inlined methods because optimized code did not work with them inlined
* Web interface can attach Daynaport again
* Improved handling of read-only hard disks
* Fixed issue with "all" semantics of DETACH
* rasctl supports adding removable media devices without providing a filename
* Removed unused flag in PbDeviceDefinition
* Updated rasctl output for ecjected media (resolves issue #199)
* Validate default folder name when changing default folder
This commit is contained in:
parent
1c8c3600a7
commit
0bd12e93f5
25
doc/rascsi.1
25
doc/rascsi.1
@ -6,7 +6,9 @@ rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins
|
||||
[\fB\-f\f® \fIFOLDER\fR]
|
||||
[\fB\-g\f® \fILOG_LEVEL\fR]
|
||||
[\fB\-h\fR]
|
||||
[\fB\-n\fR \fIVENDOR:PRODUCT:REVISION\fR]
|
||||
[\fB\-p\f® \fIPORT\fR]
|
||||
[\fB\-n\fR \fITYPE\fR]
|
||||
[\fB\-v\fR]
|
||||
[\fB\-IDn\fR \fIFILE\fR]
|
||||
[\fB\-HDn\fR \fIFILE\fR]...
|
||||
@ -20,7 +22,8 @@ For SCSI: The ID is limited from 0-7. However, typically SCSI ID 7 is reserved f
|
||||
.PP
|
||||
RaSCSI will determine the type of device based upon the file extension of the FILE argument.
|
||||
hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used with X68000)
|
||||
hds: SCSI Hard Disk image (generic)
|
||||
hds: SCSI Hard Disk image (generic, non-removable)
|
||||
hdr: SCSI Hard Disk image (generic, removable)
|
||||
hdn: SCSI Hard Disk image (NEC GENUINE)
|
||||
hdi: SCSI Hard Disk image (Anex86 HD image)
|
||||
nhd: SCSI Hard Disk image (T98Next HD image)
|
||||
@ -40,29 +43,35 @@ To quit RaSCSI, press Control + C. If it is running in the background, you can k
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-f\fI " " \fIFOLDER
|
||||
The default folder for image files. For files in this folder no absolute path needs to be specified. The default folder is '/home/pi/images'.
|
||||
The default folder for image files. For files in this folder no absolute path needs to be specified. The initial default folder is '~/images'.
|
||||
.TP
|
||||
.BR \-g\fI " " \fILOG_LEVEL
|
||||
The rascsi log level (trace, debug, info, warn, err, critical, off). The default log level is 'trace'.
|
||||
The rascsi log level (trace, debug, info, warn, err, critical, off). The default log level is 'info'.
|
||||
.TP
|
||||
.BR \-h\fI " " \fI
|
||||
Show a help page.
|
||||
.TP
|
||||
.BR \-n\fI " " \fIVENDOR:PRODUCT:REVISION
|
||||
Set the vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name components must be provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed.
|
||||
.TP
|
||||
.BR \-p\fI " " \fIPORT
|
||||
The rascsi server port, default is 6868.
|
||||
.TP
|
||||
.BR \-p\fI " " \fITYPE
|
||||
The optional case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP). If no type is specified for devices that support an image file, rascsi tries to derive the type from the file extension.
|
||||
.TP
|
||||
.BR \-v\fI " " \fI
|
||||
Display the rascsi version.
|
||||
.TP
|
||||
.BR \-ID\fIn " " \fIFILE[:TYPE]
|
||||
.BR \-ID\fIn " " \fIFILE
|
||||
n is the SCSI ID number (0-7)
|
||||
.IP
|
||||
FILE is the name of the image file to attach to that ID. An optional explicit device type (SASI_HD, SCSI_HD, CD, MO, BR, NUVOLINK, DAYNAPORT) can be provided after a colon.
|
||||
FILE is the name of the image file to use for the SCSI device. For devices that do not support an image file (SCBR, SCDP) a dummy name must be provided.
|
||||
.TP
|
||||
.BR \-HD\fIn " " \fIFILE
|
||||
n is the SASI ID number (0-15)
|
||||
.IP
|
||||
FILE is the name of the image file to attach to that ID. If FILE starts with '/dev/' the extension, which encodes the device type, is stripped, so that device files can conveniently be used as image files.
|
||||
FILE is the name of the image file to use for the SASI device.
|
||||
.IP
|
||||
Note: SASI usage is rare, and is typically limited to early Sharp X68000 systems.
|
||||
|
||||
@ -73,8 +82,8 @@ Launch RaSCSI with no emulated drives attached:
|
||||
Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID 2
|
||||
rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso
|
||||
|
||||
Launch RaSCSI with a SCSI hard drive image as ID 0 and the raw device file /dev/hdb (e.g. a USB stick) as a SCSI hard disk image file:
|
||||
rascsi -ID0 /dev/hdb:SCSI_HD
|
||||
Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw device file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter as ID 6:
|
||||
rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp DUMMY_FILENAME
|
||||
|
||||
To create an empty, 100MB HD image, use the following command:
|
||||
dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800
|
||||
|
@ -6,82 +6,92 @@ NAME
|
||||
rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins
|
||||
|
||||
SYNOPSIS
|
||||
rascsi [-f[u00AE] FOLDER] [-g[u00AE] LOG_LEVEL] [-h] [-p[u00AE] PORT]
|
||||
[-v] [-IDn FILE] [-HDn FILE]...
|
||||
rascsi [-f[u00AE] FOLDER] [-g[u00AE] LOG_LEVEL] [-h] [-n VENDOR:PROD‐
|
||||
UCT:REVISION] [-p[u00AE] PORT] [-n TYPE] [-v] [-IDn FILE] [-HDn
|
||||
FILE]...
|
||||
|
||||
DESCRIPTION
|
||||
rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins.
|
||||
|
||||
In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) de‐
|
||||
vices can be specified. The number (n) after the ID or HD identifier
|
||||
specifies the ID number for that device. For SCSI: The ID is limited
|
||||
from 0-7. However, typically SCSI ID 7 is reserved for the "initiator"
|
||||
In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) de‐
|
||||
vices can be specified. The number (n) after the ID or HD identifier
|
||||
specifies the ID number for that device. For SCSI: The ID is limited
|
||||
from 0-7. However, typically SCSI ID 7 is reserved for the "initiator"
|
||||
(the host computer). Note that SASI is considered rare and only used on
|
||||
very early Sharp X68000 computers.
|
||||
|
||||
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.
|
||||
hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used
|
||||
hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used
|
||||
with X68000)
|
||||
hds: SCSI Hard Disk image (generic)
|
||||
hds: SCSI Hard Disk image (generic, non-removable)
|
||||
hdr: SCSI Hard Disk image (generic, removable)
|
||||
hdn: SCSI Hard Disk image (NEC GENUINE)
|
||||
hdi: SCSI Hard Disk image (Anex86 HD image)
|
||||
nhd: SCSI Hard Disk image (T98Next HD image)
|
||||
hda: SCSI Hard Disk image (APPLE GENUINE - typically used with Mac
|
||||
hda: SCSI Hard Disk image (APPLE GENUINE - typically used with Mac
|
||||
SCSI emulation)
|
||||
mos: SCSI Magneto-optical image (XM6 SCSI MO image - typically only
|
||||
used with X68000)
|
||||
iso: SCSI CD-ROM image (ISO 9660 image)
|
||||
|
||||
For example, if you want to specify an Apple-compatible HD image on ID
|
||||
For example, if you want to specify an Apple-compatible HD image on ID
|
||||
0, you can use the following command:
|
||||
sudo rascsi -ID0 /path/to/drive/hdimage.hda
|
||||
|
||||
Once RaSCSI starts, it will open a socket (default port is 6868) to al‐
|
||||
low external management commands. If another process is using the
|
||||
low external management commands. If another process is using the
|
||||
rascsi port, RaSCSI will terminate, since it is likely another instance
|
||||
of RaSCSI. Once RaSCSI has initialized, the rasctl utility can be used
|
||||
to send commands.
|
||||
|
||||
To quit RaSCSI, press Control + C. If it is running in the background,
|
||||
To quit RaSCSI, press Control + C. If it is running in the background,
|
||||
you can kill it using an INT signal.
|
||||
|
||||
OPTIONS
|
||||
-f FOLDER
|
||||
The default folder for image files. For files in this folder no
|
||||
absolute path needs to be specified. The default folder is
|
||||
'/home/pi/images'.
|
||||
The default folder for image files. For files in this folder no
|
||||
absolute path needs to be specified. The initial default folder
|
||||
is '~/images'.
|
||||
|
||||
-g LOG_LEVEL
|
||||
The rascsi log level (trace, debug, info, warn, err, critical,
|
||||
off). The default log level is 'trace'.
|
||||
The rascsi log level (trace, debug, info, warn, err, critical,
|
||||
off). The default log level is 'info'.
|
||||
|
||||
-h Show a help page.
|
||||
|
||||
-n VENDOR:PRODUCT:REVISION
|
||||
Set the vendor, product and revision for the device, to be re‐
|
||||
turned with the INQUIRY data. A complete set of name components
|
||||
must be provided. VENDOR may have up to 8, PRODUCT up to 16, RE‐
|
||||
VISION up to 4 characters. Padding with blanks to the maxium
|
||||
length is automatically applied. Once set the name of a device
|
||||
cannot be changed.
|
||||
|
||||
-p PORT
|
||||
The rascsi server port, default is 6868.
|
||||
|
||||
-p TYPE
|
||||
The optional case-insensitive device type (SAHD, SCHD, SCRM,
|
||||
SCCD, SCMO, SCBR, SCDP). If no type is specified for devices
|
||||
that support an image file, rascsi tries to derive the type from
|
||||
the file extension.
|
||||
|
||||
-v Display the rascsi version.
|
||||
|
||||
-IDn FILE[:TYPE]
|
||||
-IDn FILE
|
||||
n is the SCSI ID number (0-7)
|
||||
|
||||
FILE is the name of the image file to attach to that ID. If FILE
|
||||
starts with '/dev/' the extension, which encodes the device
|
||||
type, is stripped, so that device files can conveniently be used
|
||||
as image files. An optional explicit device type (SASI_HD,
|
||||
SCSI_HD, CD, MO, BR, NUVOLINK, DAYNAPORT) can be provided after
|
||||
a colon.
|
||||
FILE is the name of the image file to use for the SCSI device.
|
||||
For devices that do not support an image file (SCBR, SCDP) a
|
||||
dummy name must be provided.
|
||||
|
||||
-HDn FILE
|
||||
n is the SASI ID number (0-15)
|
||||
|
||||
FILE is the name of the image file to attach to that ID. If FILE
|
||||
starts with '/dev/' the extension, which encodes the device
|
||||
type, is stripped, so that device files can conveniently be used
|
||||
as image files.
|
||||
FILE is the name of the image file to use for the SASI device.
|
||||
|
||||
Note: SASI usage is rare, and is typically limited to early
|
||||
Note: SASI usage is rare, and is typically limited to early
|
||||
Sharp X68000 systems.
|
||||
|
||||
EXAMPLES
|
||||
@ -92,9 +102,10 @@ EXAMPLES
|
||||
2
|
||||
rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso
|
||||
|
||||
Launch RaSCSI with a SCSI hard drive image as ID 0 and the raw device
|
||||
file /dev/hdb (e.g. a USB stick) as an image file:
|
||||
rascsi -ID0 /dev/hdb.hds
|
||||
Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw de‐
|
||||
vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter
|
||||
as ID 6:
|
||||
rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp DUMMY_FILENAME
|
||||
|
||||
To create an empty, 100MB HD image, use the following command:
|
||||
dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800
|
||||
|
18
doc/rasctl.1
18
doc/rasctl.1
@ -12,6 +12,7 @@ rasctl \- Sends management commands to the rascsi process
|
||||
\fB\-i\fR \fIID\fR
|
||||
[\fB\-c\fR \fICMD\fR]
|
||||
[\fB\-f\fR \fIFILE\fR]
|
||||
[\fB\-n\fR \fINAME\fR]
|
||||
[\fB\-t\fR \fITYPE\fR]
|
||||
[\fB\-u\fR \fIUNIT\fR]
|
||||
.SH DESCRIPTION
|
||||
@ -51,23 +52,26 @@ ID is the SCSI ID that you want to control. (0-7)
|
||||
Command is the operation being requested. options are:
|
||||
attach: attach disk
|
||||
detach: detach disk
|
||||
insert: insert media (Magneto-Optical and CD only)
|
||||
eject: eject media (Magneto-Optical and CD only)
|
||||
protect: Write protect the media (Magneto-Optical only)
|
||||
unprotect: Remove write protection from the media (Magneto-Optical only)
|
||||
insert: insert media (removable media devices only)
|
||||
eject: eject media (removable media devices only)
|
||||
protect: write protect the media (not for CD-ROMs, which are always read-only)
|
||||
unprotect: remove write protection from the media (not for CD-ROMs, which are always read-only)
|
||||
.IP
|
||||
When the command is omitted, rasctl will default to the 'attach' command.
|
||||
eject, protect and unprotect are idempotent.
|
||||
.TP
|
||||
.BR \-f\fI " " \fIFILE
|
||||
Path to the disk image file. See the rascsi(1) man page for allowable file types.
|
||||
.TP
|
||||
.BR \-t\fI " " \fITYPE
|
||||
Specifies the type of disk. If this disagrees with the file extension of the specified image, the TYPE argument is ignored. Available drive types are:
|
||||
Specifies the device type. This type overrides the type derived from the file extension of the specified image. See the rascsi(1) man page for the available device types. Legacy drive types are:
|
||||
hd: Hard disk (SCSI or SASI)
|
||||
mo: Magneto-Optical disk)
|
||||
mo: Magneto-Optical disk
|
||||
cd: CD-ROM
|
||||
bridge: Bridge device (This is only applicable to the Sharp X68000)
|
||||
.TP
|
||||
.BR \-u\fI " " \fIVENDOR:PRODUCT:REVISION
|
||||
The vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name components must be provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed.
|
||||
.TP
|
||||
.BR \-u\fI " " \fIUNIT
|
||||
Unit number (0 or 1). This will default to 0. This option is only used when there are multiple SCSI devices on a shared SCSI controller. (This is not common)
|
||||
|
||||
|
@ -7,7 +7,7 @@ NAME
|
||||
|
||||
SYNOPSIS
|
||||
rasctl -l | -s | [-g LOG_LEVEL] [-h HOST] [-p PORT] [-v] -i ID [-c CMD]
|
||||
[-f FILE] [-t TYPE] [-u UNIT]
|
||||
[-f FILE] [-n NAME] [-t TYPE] [-u UNIT]
|
||||
|
||||
DESCRIPTION
|
||||
rasctl Sends commands to the rascsi process to make configuration ad‐
|
||||
@ -43,29 +43,38 @@ OPTIONS
|
||||
-c CMD Command is the operation being requested. options are:
|
||||
attach: attach disk
|
||||
detach: detach disk
|
||||
insert: insert media (Magneto-Optical and CD only)
|
||||
eject: eject media (Magneto-Optical and CD only)
|
||||
protect: Write protect the media (Magneto-Optical only)
|
||||
unprotect: Remove write protection from the media (Magneto-
|
||||
Optical only)
|
||||
insert: insert media (removable media devices only)
|
||||
eject: eject media (removable media devices only)
|
||||
protect: write protect the media (not for CD-ROMs, which are
|
||||
always read-only)
|
||||
unprotect: remove write protection from the media (not for
|
||||
CD-ROMs, which are always read-only)
|
||||
|
||||
When the command is omitted, rasctl will default to the 'attach'
|
||||
command.
|
||||
eject, protect and unprotect are idempotent.
|
||||
|
||||
-f FILE
|
||||
Path to the disk image file. See the rascsi(1) man page for al‐
|
||||
lowable file types.
|
||||
|
||||
-t TYPE
|
||||
Specifies the type of disk. If this disagrees with the file ex‐
|
||||
tension of the specified image, the TYPE argument is ignored.
|
||||
Available drive types are:
|
||||
Specifies the device type. This type overrides the type derived
|
||||
from the file extension of the specified image. See the
|
||||
rascsi(1) man page for the available device types. Legacy drive
|
||||
types are:
|
||||
hd: Hard disk (SCSI or SASI)
|
||||
mo: Magneto-Optical disk)
|
||||
mo: Magneto-Optical disk
|
||||
cd: CD-ROM
|
||||
bridge: Bridge device (This is only applicable to the Sharp
|
||||
bridge: Bridge device (This is only applicable to the Sharp
|
||||
X68000)
|
||||
|
||||
-u VENDOR:PRODUCT:REVISION
|
||||
The vendor, product and revision for the device, to be returned
|
||||
with the INQUIRY data. A complete set of name components must be
|
||||
provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up
|
||||
to 4 characters. Padding with blanks to the maxium length is au‐
|
||||
tomatically applied. Once set the name of a device cannot be
|
||||
changed.
|
||||
|
||||
-u UNIT
|
||||
Unit number (0 or 1). This will default to 0. This option is
|
||||
only used when there are multiple SCSI devices on a shared SCSI
|
||||
|
@ -82,7 +82,8 @@ SRC_RASCSI = \
|
||||
filepath.cpp \
|
||||
fileio.cpp\
|
||||
rascsi_version.cpp \
|
||||
rasutil.cpp
|
||||
rasutil.cpp \
|
||||
protobuf_util.cpp
|
||||
SRC_RASCSI += $(shell find ./controllers -name '*.cpp')
|
||||
SRC_RASCSI += $(shell find ./devices -name '*.cpp')
|
||||
SRC_RASCSI += $(SRC_PROTOBUF)
|
||||
@ -98,7 +99,8 @@ SRC_SCSIMON = \
|
||||
SRC_RASCTL = \
|
||||
rasctl.cpp\
|
||||
rascsi_version.cpp \
|
||||
rasutil.cpp
|
||||
rasutil.cpp \
|
||||
protobuf_util.cpp
|
||||
SRC_RASCTL += $(SRC_PROTOBUF)
|
||||
|
||||
SRC_RASDUMP = \
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "devices/scsi_host_bridge.h"
|
||||
#include "devices/scsi_daynaport.h"
|
||||
#include "exceptions.h"
|
||||
#include <sstream>
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
@ -33,8 +34,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
SASIDEV::SASIDEV()
|
||||
{
|
||||
int i;
|
||||
|
||||
// Work initialization
|
||||
ctrl.phase = BUS::busfree;
|
||||
ctrl.m_scsi_id = UNKNOWN_SCSI_ID;
|
||||
@ -54,7 +53,7 @@ SASIDEV::SASIDEV()
|
||||
ctrl.length = 0;
|
||||
|
||||
// Logical unit initialization
|
||||
for (i = 0; i < UnitMax; i++) {
|
||||
for (int i = 0; i < UnitMax; i++) {
|
||||
ctrl.unit[i] = NULL;
|
||||
}
|
||||
}
|
||||
@ -269,7 +268,7 @@ void SASIDEV::BusFree()
|
||||
ctrl.bus->SetMSG(FALSE);
|
||||
ctrl.bus->SetCD(FALSE);
|
||||
ctrl.bus->SetIO(FALSE);
|
||||
ctrl.bus->SetBSY(FALSE);
|
||||
ctrl.bus->SetBSY(false);
|
||||
|
||||
// Initialize status and message
|
||||
ctrl.status = 0x00;
|
||||
@ -309,7 +308,7 @@ void SASIDEV::Selection()
|
||||
ctrl.phase = BUS::selection;
|
||||
|
||||
// Raiase BSY and respond
|
||||
ctrl.bus->SetBSY(TRUE);
|
||||
ctrl.bus->SetBSY(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -326,9 +325,6 @@ void SASIDEV::Selection()
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::Command()
|
||||
{
|
||||
int count;
|
||||
int i;
|
||||
|
||||
// Phase change
|
||||
if (ctrl.phase != BUS::command) {
|
||||
LOGTRACE("%s Command Phase", __PRETTY_FUNCTION__);
|
||||
@ -347,7 +343,7 @@ void SASIDEV::Command()
|
||||
ctrl.blocks = 1;
|
||||
|
||||
// Command reception handshake (10 bytes are automatically received at the first command)
|
||||
count = ctrl.bus->CommandHandShake(ctrl.buffer);
|
||||
int count = ctrl.bus->CommandHandShake(ctrl.buffer);
|
||||
|
||||
// If no byte can be received move to the status phase
|
||||
if (count == 0) {
|
||||
@ -355,10 +351,7 @@ void SASIDEV::Command()
|
||||
return;
|
||||
}
|
||||
|
||||
// Check 10-byte CDB
|
||||
if (ctrl.buffer[0] >= 0x20 && ctrl.buffer[0] <= 0x7D) {
|
||||
ctrl.length = 10;
|
||||
}
|
||||
ctrl.length = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]);
|
||||
|
||||
// If not able to receive all, move to the status phase
|
||||
if (count != (int)ctrl.length) {
|
||||
@ -367,7 +360,7 @@ void SASIDEV::Command()
|
||||
}
|
||||
|
||||
// Command data transfer
|
||||
for (i = 0; i < (int)ctrl.length; i++) {
|
||||
for (int i = 0; i < (int)ctrl.length; i++) {
|
||||
ctrl.cmd[i] = (DWORD)ctrl.buffer[i];
|
||||
}
|
||||
|
||||
@ -376,14 +369,14 @@ void SASIDEV::Command()
|
||||
ctrl.blocks = 0;
|
||||
|
||||
// Execution Phase
|
||||
try {
|
||||
Execute();
|
||||
}
|
||||
catch (lunexception& e) {
|
||||
LOGINFO("%s Invalid LUN %d", __PRETTY_FUNCTION__, (int)e.getlun());
|
||||
try {
|
||||
Execute();
|
||||
}
|
||||
catch (lun_exception& e) {
|
||||
LOGINFO("%s Invalid LUN %d for ID %d", __PRETTY_FUNCTION__, e.getlun(), GetSCSIID());
|
||||
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_LUN);
|
||||
}
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_LUN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -406,11 +399,11 @@ void SASIDEV::Execute()
|
||||
|
||||
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE
|
||||
if ((SASIDEV::scsi_command)ctrl.cmd[0] != eCmdRequestSense) {
|
||||
ctrl.sense_key = 0;
|
||||
ctrl.asc = 0;
|
||||
ctrl.status = 0;
|
||||
}
|
||||
|
||||
// Process by command
|
||||
// TODO This code does not belong here. Each device type needs such a dispatcher, which the controller has to call.
|
||||
switch ((SASIDEV::scsi_command)ctrl.cmd[0]) {
|
||||
// TEST UNIT READY
|
||||
case SASIDEV::eCmdTestUnitReady:
|
||||
@ -427,9 +420,7 @@ void SASIDEV::Execute()
|
||||
CmdRequestSense();
|
||||
return;
|
||||
|
||||
// FORMAT UNIT
|
||||
// This doesn't exist in the SCSI Spec, but was in the original RaSCSI code.
|
||||
// leaving it here for now....
|
||||
// FORMAT
|
||||
case SASIDEV::eCmdFormat:
|
||||
CmdFormat();
|
||||
return;
|
||||
@ -454,7 +445,7 @@ void SASIDEV::Execute()
|
||||
CmdSeek6();
|
||||
return;
|
||||
|
||||
// ASSIGN(SASIのみ)
|
||||
// ASSIGN (SASI only)
|
||||
// This doesn't exist in the SCSI Spec, but was in the original RaSCSI code.
|
||||
// leaving it here for now....
|
||||
case SASIDEV::eCmdSasiCmdAssign:
|
||||
@ -471,7 +462,7 @@ void SASIDEV::Execute()
|
||||
CmdReleaseUnit();
|
||||
return;
|
||||
|
||||
// SPECIFY(SASIのみ)
|
||||
// SPECIFY (SASI only)
|
||||
// This doesn't exist in the SCSI Spec, but was in the original RaSCSI code.
|
||||
// leaving it here for now....
|
||||
case SASIDEV::eCmdInvalid:
|
||||
@ -482,7 +473,6 @@ void SASIDEV::Execute()
|
||||
}
|
||||
|
||||
// Unsupported command
|
||||
LOGWARN("%s Unsupported command $%02X", __PRETTY_FUNCTION__, (WORD)ctrl.cmd[0]);
|
||||
CmdInvalid();
|
||||
}
|
||||
|
||||
@ -493,15 +483,12 @@ void SASIDEV::Execute()
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::Status()
|
||||
{
|
||||
DWORD min_exec_time;
|
||||
DWORD time;
|
||||
|
||||
// Phase change
|
||||
if (ctrl.phase != BUS::status) {
|
||||
// Minimum execution time
|
||||
if (ctrl.execstart > 0) {
|
||||
min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
|
||||
time = SysTimer::GetTimerLow() - ctrl.execstart;
|
||||
DWORD min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
|
||||
DWORD time = SysTimer::GetTimerLow() - ctrl.execstart;
|
||||
if (time < min_exec_time) {
|
||||
SysTimer::SleepUsec(min_exec_time - time);
|
||||
}
|
||||
@ -571,17 +558,14 @@ void SASIDEV::MsgIn()
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::DataIn()
|
||||
{
|
||||
DWORD min_exec_time;
|
||||
DWORD time;
|
||||
|
||||
ASSERT(ctrl.length >= 0);
|
||||
|
||||
// Phase change
|
||||
if (ctrl.phase != BUS::datain) {
|
||||
// Minimum execution time
|
||||
if (ctrl.execstart > 0) {
|
||||
min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
|
||||
time = SysTimer::GetTimerLow() - ctrl.execstart;
|
||||
DWORD min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
|
||||
DWORD time = SysTimer::GetTimerLow() - ctrl.execstart;
|
||||
if (time < min_exec_time) {
|
||||
SysTimer::SleepUsec(min_exec_time - time);
|
||||
}
|
||||
@ -622,17 +606,14 @@ void SASIDEV::DataIn()
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::DataOut()
|
||||
{
|
||||
DWORD min_exec_time;
|
||||
DWORD time;
|
||||
|
||||
ASSERT(ctrl.length >= 0);
|
||||
|
||||
// Phase change
|
||||
if (ctrl.phase != BUS::dataout) {
|
||||
// Minimum execution time
|
||||
if (ctrl.execstart > 0) {
|
||||
min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
|
||||
time = SysTimer::GetTimerLow() - ctrl.execstart;
|
||||
DWORD min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
|
||||
DWORD time = SysTimer::GetTimerLow() - ctrl.execstart;
|
||||
if (time < min_exec_time) {
|
||||
SysTimer::SleepUsec(min_exec_time - time);
|
||||
}
|
||||
@ -692,14 +673,18 @@ void SASIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc)
|
||||
return;
|
||||
}
|
||||
|
||||
LOGTRACE("%s Sense Key and ASC for subsequent REQUEST SENSE: $%02X, $%02X", __PRETTY_FUNCTION__, sense_key, asc);
|
||||
|
||||
// Set Sense Key and ASC for a subsequent REQUEST SENSE
|
||||
ctrl.sense_key = sense_key;
|
||||
ctrl.asc = asc;
|
||||
|
||||
// Logical Unit
|
||||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||||
if (!ctrl.unit[lun] || asc == ERROR_CODES::INVALID_LUN) {
|
||||
lun = 0;
|
||||
}
|
||||
|
||||
LOGTRACE("%s Sense Key and ASC for subsequent REQUEST SENSE: $%02X, $%02X", __PRETTY_FUNCTION__, sense_key, asc);
|
||||
|
||||
if (sense_key || asc) {
|
||||
// Set Sense Key and ASC for a subsequent REQUEST SENSE
|
||||
ctrl.unit[lun]->SetStatusCode((sense_key << 16) | (asc << 8));
|
||||
}
|
||||
|
||||
// Set status and message(CHECK CONDITION)
|
||||
ctrl.status = (lun << 5) | 0x02;
|
||||
@ -721,7 +706,7 @@ void SASIDEV::CmdTestUnitReady()
|
||||
{
|
||||
LOGTRACE("%s TEST UNIT READY Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->TestUnitReady(ctrl.cmd);
|
||||
@ -744,7 +729,7 @@ void SASIDEV::CmdRezero()
|
||||
{
|
||||
LOGTRACE( "%s REZERO UNIT Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->Rezero(ctrl.cmd);
|
||||
@ -771,9 +756,7 @@ void SASIDEV::CmdRequestSense()
|
||||
try {
|
||||
lun = GetLun();
|
||||
}
|
||||
catch(const lunexception& e) {
|
||||
LOGINFO("%s Non-existing LUN %d", __PRETTY_FUNCTION__, (int)e.getlun());
|
||||
|
||||
catch(const lun_exception& e) {
|
||||
// Note: According to the SCSI specs the LUN handling for REQUEST SENSE is special.
|
||||
// Non-existing LUNs do *not* result in CHECK CONDITION.
|
||||
// Only the Sense Key and ASC are set in order to signal the non-existing LUN.
|
||||
@ -781,22 +764,13 @@ void SASIDEV::CmdRequestSense()
|
||||
// LUN 0 can be assumed to be present (required to call RequestSense() below)
|
||||
lun = 0;
|
||||
|
||||
ctrl.sense_key = ERROR_CODES::sense_key::ILLEGAL_REQUEST;
|
||||
ctrl.asc = ERROR_CODES::asc::INVALID_LUN;
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_LUN);
|
||||
}
|
||||
|
||||
ctrl.length = ctrl.unit[lun]->RequestSense(ctrl.cmd, ctrl.buffer);
|
||||
ctrl.length = ctrl.unit[lun]->RequestSense(ctrl.cmd, ctrl.buffer);
|
||||
ASSERT(ctrl.length > 0);
|
||||
|
||||
LOGTRACE("%s Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, ctrl.sense_key, ctrl.asc);
|
||||
|
||||
// REQUEST SENSE returns the sense data set by the previous (failed) command
|
||||
ctrl.buffer[2] = ctrl.sense_key;
|
||||
ctrl.buffer[12] = ctrl.asc;
|
||||
|
||||
// The sense data are only valid once
|
||||
ctrl.sense_key = 0;
|
||||
ctrl.asc = 0;
|
||||
LOGTRACE("%s Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, ctrl.buffer[2], ctrl.buffer[12]);
|
||||
|
||||
// Read phase
|
||||
DataIn();
|
||||
@ -811,7 +785,7 @@ void SASIDEV::CmdFormat()
|
||||
{
|
||||
LOGTRACE( "%s FORMAT UNIT Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->Format(ctrl.cmd);
|
||||
@ -834,7 +808,7 @@ void SASIDEV::CmdReassign()
|
||||
{
|
||||
LOGTRACE("%s REASSIGN BLOCKS Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->Reassign(ctrl.cmd);
|
||||
@ -891,7 +865,7 @@ void SASIDEV::CmdReleaseUnit()
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::CmdRead6()
|
||||
{
|
||||
DWORD lun = GetLun();
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Get record number and block number
|
||||
DWORD record = ctrl.cmd[1] & 0x1f;
|
||||
@ -904,13 +878,26 @@ void SASIDEV::CmdRead6()
|
||||
ctrl.blocks = 0x100;
|
||||
}
|
||||
|
||||
// TODO This class must not know about SCDP
|
||||
if(ctrl.unit[lun]->IsDaynaPort()){
|
||||
// The DaynaPort only wants one block.
|
||||
// ctrl.cmd[4] and ctrl.cmd[5] are used to specify the maximum buffer size for the DaynaPort
|
||||
ctrl.blocks=1;
|
||||
}
|
||||
else {
|
||||
// Check capacity
|
||||
DWORD capacity = ctrl.unit[lun]->GetBlockCount();
|
||||
if (record > capacity || record + ctrl.blocks > capacity) {
|
||||
ostringstream s;
|
||||
s << "ID " << GetSCSIID() << ": Media capacity of " << capacity << " blocks exceeded: "
|
||||
<< "Trying to read block " << record << ", block count " << ctrl.blocks;
|
||||
LOGWARN("%s", s.str().c_str());
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::LBA_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOGTRACE("%s READ(6) command record=%06X blocks=%d ID %s", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl.blocks, ctrl.unit[lun]->GetID().c_str());
|
||||
LOGTRACE("%s READ(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl.blocks);
|
||||
|
||||
// Command processing on drive
|
||||
ctrl.length = ctrl.unit[lun]->Read(ctrl.cmd, ctrl.buffer, record);
|
||||
@ -933,13 +920,14 @@ void SASIDEV::CmdRead6()
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// This Send Message command is used by the DaynaPort SCSI/Link
|
||||
// TODO This class must not know about SCDP
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::DaynaPortWrite()
|
||||
{
|
||||
DWORD lun = GetLun();
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Error if not a host bridge
|
||||
// Error if not a DaynaPort device
|
||||
if (!ctrl.unit[lun]->IsDaynaPort()) {
|
||||
LOGERROR("Received DaynaPortWrite for a non-DaynaPort device");
|
||||
Error();
|
||||
@ -1008,14 +996,24 @@ void SASIDEV::CmdWrite6()
|
||||
ctrl.blocks = 0x100;
|
||||
}
|
||||
|
||||
LOGTRACE("%s WRITE(6) command record=%06X blocks=%d", __PRETTY_FUNCTION__, (WORD)record, (WORD)ctrl.blocks);
|
||||
// Check capacity
|
||||
DWORD capacity = ctrl.unit[lun]->GetBlockCount();
|
||||
if (record > capacity || record + ctrl.blocks > capacity) {
|
||||
ostringstream s;
|
||||
s << "ID " << GetSCSIID() << ": Media capacity of " << capacity << " blocks exceeded: "
|
||||
<< "Trying to write block " << record << ", block count " << ctrl.blocks;
|
||||
LOGWARN("%s", s.str().c_str());
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::LBA_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGTRACE("%s WRITE(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, (WORD)record, (WORD)ctrl.blocks);
|
||||
|
||||
// Command processing on drive
|
||||
ctrl.length = ctrl.unit[lun]->WriteCheck(record);
|
||||
if (ctrl.length <= 0) {
|
||||
LOGDEBUG("WriteCheck failed");
|
||||
// Failure (Error)
|
||||
Error();
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::WRITE_PROTECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1035,7 +1033,7 @@ void SASIDEV::CmdSeek6()
|
||||
{
|
||||
LOGTRACE("%s SEEK(6) Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->Seek(ctrl.cmd);
|
||||
@ -1084,7 +1082,7 @@ void SASIDEV::CmdSpecify()
|
||||
{
|
||||
LOGTRACE("%s SPECIFY Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->Assign(ctrl.cmd);
|
||||
@ -1108,7 +1106,7 @@ void SASIDEV::CmdSpecify()
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::CmdInvalid()
|
||||
{
|
||||
LOGWARN("%s Command not supported", __PRETTY_FUNCTION__);
|
||||
LOGWARN("%s ID %d received unsupported command: $%02X", __PRETTY_FUNCTION__, GetSCSIID(), (BYTE)ctrl.cmd[0]);
|
||||
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
}
|
||||
@ -1314,10 +1312,9 @@ BOOL SASIDEV::XferIn(BYTE *buf)
|
||||
|
||||
// Limited to read commands
|
||||
switch (ctrl.cmd[0]) {
|
||||
// READ(6)
|
||||
case eCmdRead6:
|
||||
// READ(10)
|
||||
case eCmdRead10:
|
||||
case eCmdRead16:
|
||||
// Read from disk
|
||||
ctrl.length = ctrl.unit[lun]->Read(ctrl.cmd, buf, ctrl.next);
|
||||
ctrl.next++;
|
||||
@ -1350,8 +1347,6 @@ BOOL SASIDEV::XferIn(BYTE *buf)
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SASIDEV::XferOut(BOOL cont)
|
||||
{
|
||||
SCSIBR *bridge;
|
||||
|
||||
ASSERT(ctrl.phase == BUS::dataout);
|
||||
|
||||
// Logical Unit
|
||||
@ -1372,12 +1367,13 @@ BOOL SASIDEV::XferOut(BOOL cont)
|
||||
|
||||
case SASIDEV::eCmdWrite6:
|
||||
case SASIDEV::eCmdWrite10:
|
||||
case SASIDEV::eCmdWriteAndVerify10:
|
||||
// If we're a host bridge, use the host bridge's SendMessage10
|
||||
// function
|
||||
case SASIDEV::eCmdWrite16:
|
||||
case SASIDEV::eCmdVerify10:
|
||||
case SASIDEV::eCmdVerify16:
|
||||
// If we're a host bridge, use the host bridge's SendMessage10 function
|
||||
// TODO This class must not know about SCSIBR
|
||||
if (ctrl.unit[lun]->IsBridge()) {
|
||||
bridge = (SCSIBR*)ctrl.unit[lun];
|
||||
if (!bridge->SendMessage10(ctrl.cmd, ctrl.buffer)) {
|
||||
if (!((SCSIBR*)ctrl.unit[lun])->SendMessage10(ctrl.cmd, ctrl.buffer)) {
|
||||
// write failed
|
||||
return FALSE;
|
||||
}
|
||||
@ -1388,6 +1384,7 @@ BOOL SASIDEV::XferOut(BOOL cont)
|
||||
}
|
||||
|
||||
// Special case Write function for DaynaPort
|
||||
// TODO This class must not know about SCSIDP
|
||||
if (ctrl.unit[lun]->IsDaynaPort()) {
|
||||
LOGTRACE("%s Doing special case write for DaynaPort", __PRETTY_FUNCTION__);
|
||||
if (!(SCSIDaynaPort*)ctrl.unit[lun]->Write(ctrl.cmd, ctrl.buffer, ctrl.length)) {
|
||||
@ -1402,7 +1399,7 @@ BOOL SASIDEV::XferOut(BOOL cont)
|
||||
break;
|
||||
}
|
||||
|
||||
LOGTRACE("%s eCmdWriteAndVerify10 Calling Write... cmd: %02X next: %d", __PRETTY_FUNCTION__, (WORD)ctrl.cmd[0], (int)ctrl.next);
|
||||
LOGTRACE("%s eCmdVerify Calling Write... cmd: %02X next: %d", __PRETTY_FUNCTION__, (WORD)ctrl.cmd[0], (int)ctrl.next);
|
||||
if (!ctrl.unit[lun]->Write(ctrl.cmd, ctrl.buffer, ctrl.next - 1)) {
|
||||
// Write failed
|
||||
return FALSE;
|
||||
@ -1432,6 +1429,7 @@ BOOL SASIDEV::XferOut(BOOL cont)
|
||||
case SASIDEV::eCmdSetMcastAddr:
|
||||
LOGTRACE("%s Done with DaynaPort Set Multicast Address", __PRETTY_FUNCTION__);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGWARN("Received an unexpected command (%02X) in %s", (WORD)ctrl.cmd[0] , __PRETTY_FUNCTION__)
|
||||
ASSERT(FALSE);
|
||||
@ -1461,15 +1459,14 @@ void SASIDEV::FlushUnit()
|
||||
switch ((SASIDEV::scsi_command)ctrl.cmd[0]) {
|
||||
case SASIDEV::eCmdWrite6:
|
||||
case SASIDEV::eCmdWrite10:
|
||||
case SASIDEV::eCmdWriteAndVerify10:
|
||||
// Flush
|
||||
if (!ctrl.unit[lun]->IsCacheWB()) {
|
||||
ctrl.unit[lun]->Flush();
|
||||
}
|
||||
case SASIDEV::eCmdWrite16:
|
||||
case SASIDEV::eCmdVerify10:
|
||||
case SASIDEV::eCmdVerify16:
|
||||
break;
|
||||
|
||||
case SASIDEV::eCmdModeSelect:
|
||||
case SASIDEV::eCmdModeSelect10:
|
||||
// Debug code related to Issue #2 on github, where we get an unhandled Model select when
|
||||
// Debug code related to Issue #2 on github, where we get an unhandled Mode Select when
|
||||
// the mac is rebooted
|
||||
// https://github.com/akuker/RASCSI/issues/2
|
||||
LOGWARN("Received \'Mode Select\'\n");
|
||||
@ -1562,10 +1559,10 @@ void SASIDEV::GetPhaseStr(char *str)
|
||||
//---------------------------------------------------------------------------
|
||||
DWORD SASIDEV::GetLun()
|
||||
{
|
||||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||||
if (!ctrl.unit[lun]) {
|
||||
throw lunexception(lun);
|
||||
}
|
||||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||||
if (!ctrl.unit[lun]) {
|
||||
throw lun_exception(lun);
|
||||
}
|
||||
|
||||
return lun;
|
||||
return lun;
|
||||
}
|
||||
|
@ -77,11 +77,11 @@ protected:
|
||||
eCmdStartStop = 0x1B,
|
||||
eCmdSendDiag = 0x1D,
|
||||
eCmdRemoval = 0x1E,
|
||||
eCmdReadCapacity = 0x25,
|
||||
eCmdReadCapacity10 = 0x25,
|
||||
eCmdRead10 = 0x28,
|
||||
eCmdWrite10 = 0x2A,
|
||||
eCmdSeek10 = 0x2B,
|
||||
eCmdWriteAndVerify10 = 0x2E,
|
||||
eCmdVerify10 = 0x2E,
|
||||
eCmdVerify = 0x2F,
|
||||
eCmdSynchronizeCache = 0x35,
|
||||
eCmdReadDefectData10 = 0x37,
|
||||
@ -89,12 +89,18 @@ protected:
|
||||
eCmdPlayAudio10 = 0x45,
|
||||
eCmdPlayAudioMSF = 0x47,
|
||||
eCmdPlayAudioTrack = 0x48,
|
||||
eCmdGetEventStatusNotification = 0x4a,
|
||||
eCmdModeSelect10 = 0x55,
|
||||
eCmdReserve10 = 0x56,
|
||||
eCmdRelease10 = 0x57,
|
||||
eCmdModeSense10 = 0x5A,
|
||||
eCmdRead16 = 0x88,
|
||||
eCmdWrite16 = 0x8A,
|
||||
eCmdVerify16 = 0x8F,
|
||||
eCmdReadCapacity16 = 0x9E,
|
||||
eCmdReportLuns = 0xA0,
|
||||
eCmdInvalid = 0xC2, // (SASI only/Suppress warning when using SxSI)
|
||||
eCmdSasiCmdAssign = 0x0e, // This isn't used by SCSI, and can probably be removed.
|
||||
eCmdSasiCmdAssign = 0x0E, // This isn't used by SCSI, and can probably be removed.
|
||||
};
|
||||
|
||||
public:
|
||||
@ -103,7 +109,7 @@ public:
|
||||
};
|
||||
|
||||
const int UNKNOWN_SCSI_ID = -1;
|
||||
const int DEFAULT_BUFFER_SIZE = 0x800;
|
||||
const int DEFAULT_BUFFER_SIZE = 0x1000;
|
||||
const int DAYNAPORT_BUFFER_SIZE = 0x1000000;
|
||||
|
||||
// For timing adjustments
|
||||
@ -120,7 +126,7 @@ public:
|
||||
BUS *bus; // Bus
|
||||
|
||||
// commands
|
||||
DWORD cmd[10]; // Command data
|
||||
DWORD cmd[16]; // Command data
|
||||
DWORD status; // Status data
|
||||
DWORD message; // Message data
|
||||
|
||||
@ -137,10 +143,6 @@ public:
|
||||
|
||||
// Logical unit
|
||||
Disk *unit[UnitMax];
|
||||
|
||||
// Sense Key and Additional Sense Code (ASC) of the previous command
|
||||
int sense_key;
|
||||
int asc;
|
||||
} ctrl_t;
|
||||
|
||||
public:
|
||||
@ -154,7 +156,7 @@ public:
|
||||
|
||||
// Connect
|
||||
void Connect(int id, BUS *sbus); // Controller connection
|
||||
Disk* GetUnit(int no); // Get logical unit
|
||||
Disk* GetUnit(int no); // Get logical unit
|
||||
void SetUnit(int no, Disk *dev); // Logical unit setting
|
||||
BOOL HasUnit(); // Has a valid logical unit
|
||||
|
||||
@ -171,7 +173,7 @@ public:
|
||||
ctrl_t* GetWorkAddr() { return &ctrl; } // Get the internal information address
|
||||
virtual BOOL IsSASI() const {return TRUE;} // SASI Check
|
||||
virtual BOOL IsSCSI() const {return FALSE;} // SCSI check
|
||||
Disk* GetBusyUnit(); // Get the busy unit
|
||||
Disk* GetBusyUnit(); // Get the busy unit
|
||||
|
||||
protected:
|
||||
// Phase processing
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "devices/scsi_host_bridge.h"
|
||||
#include "devices/scsi_daynaport.h"
|
||||
#include "exceptions.h"
|
||||
#include <sstream>
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
@ -41,46 +42,52 @@ SCSIDEV::SCSIDEV() : SASIDEV()
|
||||
scsi.msc = 0;
|
||||
memset(scsi.msb, 0x00, sizeof(scsi.msb));
|
||||
|
||||
SetupCommand(eCmdTestUnitReady, "CmdTestUnitReady", &SCSIDEV::CmdTestUnitReady);
|
||||
SetupCommand(eCmdRezero, "CmdRezero", &SCSIDEV::CmdRezero);
|
||||
SetupCommand(eCmdRequestSense, "CmdRequestSense", &SCSIDEV::CmdRequestSense);
|
||||
SetupCommand(eCmdFormat, "CmdFormat", &SCSIDEV::CmdFormat);
|
||||
SetupCommand(eCmdReassign, "CmdReassign", &SCSIDEV::CmdReassign);
|
||||
SetupCommand(eCmdRead6, "CmdRead6", &SCSIDEV::CmdRead6);
|
||||
SetupCommand(eCmdWrite6, "CmdWrite6", &SCSIDEV::CmdWrite6);
|
||||
SetupCommand(eCmdSeek6, "CmdSeek6", &SCSIDEV::CmdSeek6);
|
||||
SetupCommand(eCmdInquiry, "CmdInquiry", &SCSIDEV::CmdInquiry);
|
||||
SetupCommand(eCmdModeSelect, "CmdModeSelect", &SCSIDEV::CmdModeSelect);
|
||||
SetupCommand(eCmdReserve6, "CmdReserve6", &SCSIDEV::CmdReserve6);
|
||||
SetupCommand(eCmdRelease6, "CmdRelease6", &SCSIDEV::CmdRelease6);
|
||||
SetupCommand(eCmdModeSense, "CmdModeSense", &SCSIDEV::CmdModeSense);
|
||||
SetupCommand(eCmdStartStop, "CmdStartStop", &SCSIDEV::CmdStartStop);
|
||||
SetupCommand(eCmdSendDiag, "CmdSendDiag", &SCSIDEV::CmdSendDiag);
|
||||
SetupCommand(eCmdRemoval, "CmdRemoval", &SCSIDEV::CmdRemoval);
|
||||
SetupCommand(eCmdReadCapacity, "CmdReadCapacity", &SCSIDEV::CmdReadCapacity);
|
||||
SetupCommand(eCmdRead10, "CmdRead10", &SCSIDEV::CmdRead10);
|
||||
SetupCommand(eCmdWrite10, "CmdWrite10", &SCSIDEV::CmdWrite10);
|
||||
SetupCommand(eCmdWriteAndVerify10, "CmdWriteAndVerify10", &SCSIDEV::CmdWrite10);
|
||||
SetupCommand(eCmdSeek10, "CmdSeek10", &SCSIDEV::CmdSeek10);
|
||||
SetupCommand(eCmdVerify, "CmdVerify", &SCSIDEV::CmdVerify);
|
||||
SetupCommand(eCmdSynchronizeCache, "CmdSynchronizeCache", &SCSIDEV::CmdSynchronizeCache);
|
||||
SetupCommand(eCmdReadDefectData10, "CmdReadDefectData10", &SCSIDEV::CmdReadDefectData10);
|
||||
SetupCommand(eCmdModeSelect10, "CmdModeSelect10", &SCSIDEV::CmdModeSelect10);
|
||||
SetupCommand(eCmdReserve10, "CmdReserve10", &SCSIDEV::CmdReserve10);
|
||||
SetupCommand(eCmdRelease10, "CmdRelease10", &SCSIDEV::CmdRelease10);
|
||||
SetupCommand(eCmdModeSense10, "CmdModeSense10", &SCSIDEV::CmdModeSense10);
|
||||
SetUpCommand(eCmdTestUnitReady, "CmdTestUnitReady", &SCSIDEV::CmdTestUnitReady);
|
||||
SetUpCommand(eCmdRezero, "CmdRezero", &SCSIDEV::CmdRezero);
|
||||
SetUpCommand(eCmdRequestSense, "CmdRequestSense", &SCSIDEV::CmdRequestSense);
|
||||
SetUpCommand(eCmdFormat, "CmdFormat", &SCSIDEV::CmdFormat);
|
||||
SetUpCommand(eCmdReassign, "CmdReassign", &SCSIDEV::CmdReassign);
|
||||
SetUpCommand(eCmdRead6, "CmdRead6", &SCSIDEV::CmdRead6);
|
||||
SetUpCommand(eCmdWrite6, "CmdWrite6", &SCSIDEV::CmdWrite6);
|
||||
SetUpCommand(eCmdSeek6, "CmdSeek6", &SCSIDEV::CmdSeek6);
|
||||
SetUpCommand(eCmdInquiry, "CmdInquiry", &SCSIDEV::CmdInquiry);
|
||||
SetUpCommand(eCmdModeSelect, "CmdModeSelect", &SCSIDEV::CmdModeSelect);
|
||||
SetUpCommand(eCmdReserve6, "CmdReserve6", &SCSIDEV::CmdReserve6);
|
||||
SetUpCommand(eCmdRelease6, "CmdRelease6", &SCSIDEV::CmdRelease6);
|
||||
SetUpCommand(eCmdModeSense, "CmdModeSense", &SCSIDEV::CmdModeSense);
|
||||
SetUpCommand(eCmdStartStop, "CmdStartStop", &SCSIDEV::CmdStartStop);
|
||||
SetUpCommand(eCmdSendDiag, "CmdSendDiag", &SCSIDEV::CmdSendDiag);
|
||||
SetUpCommand(eCmdRemoval, "CmdRemoval", &SCSIDEV::CmdRemoval);
|
||||
SetUpCommand(eCmdReadCapacity10, "CmdReadCapacity10", &SCSIDEV::CmdReadCapacity10);
|
||||
SetUpCommand(eCmdRead10, "CmdRead10", &SCSIDEV::CmdRead10);
|
||||
SetUpCommand(eCmdWrite10, "CmdWrite10", &SCSIDEV::CmdWrite10);
|
||||
SetUpCommand(eCmdVerify10, "CmdVerify10", &SCSIDEV::CmdWrite10);
|
||||
SetUpCommand(eCmdSeek10, "CmdSeek10", &SCSIDEV::CmdSeek10);
|
||||
SetUpCommand(eCmdVerify, "CmdVerify", &SCSIDEV::CmdVerify);
|
||||
SetUpCommand(eCmdSynchronizeCache, "CmdSynchronizeCache", &SCSIDEV::CmdSynchronizeCache);
|
||||
SetUpCommand(eCmdReadDefectData10, "CmdReadDefectData10", &SCSIDEV::CmdReadDefectData10);
|
||||
SetUpCommand(eCmdModeSelect10, "CmdModeSelect10", &SCSIDEV::CmdModeSelect10);
|
||||
SetUpCommand(eCmdReserve10, "CmdReserve10", &SCSIDEV::CmdReserve10);
|
||||
SetUpCommand(eCmdRelease10, "CmdRelease10", &SCSIDEV::CmdRelease10);
|
||||
SetUpCommand(eCmdModeSense10, "CmdModeSense10", &SCSIDEV::CmdModeSense10);
|
||||
SetUpCommand(eCmdRead16, "CmdRead16", &SCSIDEV::CmdRead16);
|
||||
SetUpCommand(eCmdWrite16, "CmdWrite16", &SCSIDEV::CmdWrite16);
|
||||
SetUpCommand(eCmdVerify16, "CmdVerify16", &SCSIDEV::CmdWrite16);
|
||||
SetUpCommand(eCmdReadCapacity16, "CmdReadCapacity16", &SCSIDEV::CmdReadCapacity16);
|
||||
SetUpCommand(eCmdReportLuns, "CmdReportLuns", &SCSIDEV::CmdReportLuns);
|
||||
|
||||
// MMC specific. TODO Move to separate class
|
||||
SetupCommand(eCmdReadToc, "CmdReadToc", &SCSIDEV::CmdReadToc);
|
||||
SetupCommand(eCmdPlayAudio10, "CmdPlayAudio10", &SCSIDEV::CmdPlayAudio10);
|
||||
SetupCommand(eCmdPlayAudioMSF, "CmdPlayAudioMSF", &SCSIDEV::CmdPlayAudioMSF);
|
||||
SetupCommand(eCmdPlayAudioTrack, "CmdPlayAudioTrack", &SCSIDEV::CmdPlayAudioTrack);
|
||||
SetUpCommand(eCmdReadToc, "CmdReadToc", &SCSIDEV::CmdReadToc);
|
||||
SetUpCommand(eCmdPlayAudio10, "CmdPlayAudio10", &SCSIDEV::CmdPlayAudio10);
|
||||
SetUpCommand(eCmdPlayAudioMSF, "CmdPlayAudioMSF", &SCSIDEV::CmdPlayAudioMSF);
|
||||
SetUpCommand(eCmdPlayAudioTrack, "CmdPlayAudioTrack", &SCSIDEV::CmdPlayAudioTrack);
|
||||
SetUpCommand(eCmdGetEventStatusNotification, "CmdGetEventStatusNotification", &SCSIDEV::CmdGetEventStatusNotification);
|
||||
|
||||
// DaynaPort specific. TODO Move to separate class
|
||||
SetupCommand(eCmdRetrieveStats, "CmdRetrieveStats", &SCSIDEV::CmdRetrieveStats);
|
||||
SetupCommand(eCmdSetIfaceMode, "CmdSetIfaceMode", &SCSIDEV::CmdSetIfaceMode);
|
||||
SetupCommand(eCmdSetMcastAddr, "CmdSetMcastAddr", &SCSIDEV::CmdSetMcastAddr);
|
||||
SetupCommand(eCmdEnableInterface, "CmdEnableInterface", &SCSIDEV::CmdEnableInterface);
|
||||
SetUpCommand(eCmdRetrieveStats, "CmdRetrieveStats", &SCSIDEV::CmdRetrieveStats);
|
||||
SetUpCommand(eCmdSetIfaceMode, "CmdSetIfaceMode", &SCSIDEV::CmdSetIfaceMode);
|
||||
SetUpCommand(eCmdSetMcastAddr, "CmdSetMcastAddr", &SCSIDEV::CmdSetMcastAddr);
|
||||
SetUpCommand(eCmdEnableInterface, "CmdEnableInterface", &SCSIDEV::CmdEnableInterface);
|
||||
}
|
||||
|
||||
SCSIDEV::~SCSIDEV()
|
||||
@ -90,7 +97,7 @@ SCSIDEV::~SCSIDEV()
|
||||
}
|
||||
}
|
||||
|
||||
void SCSIDEV::SetupCommand(scsi_command opcode, const char* name, void (SCSIDEV::*execute)(void))
|
||||
void SCSIDEV::SetUpCommand(scsi_command opcode, const char* name, void (SCSIDEV::*execute)(void))
|
||||
{
|
||||
scsi_commands[opcode] = new command_t(name, execute);
|
||||
}
|
||||
@ -204,7 +211,6 @@ void SCSIDEV::BusFree()
|
||||
{
|
||||
// Phase change
|
||||
if (ctrl.phase != BUS::busfree) {
|
||||
|
||||
LOGTRACE( "%s Bus free phase", __PRETTY_FUNCTION__);
|
||||
|
||||
// Phase setting
|
||||
@ -215,7 +221,7 @@ void SCSIDEV::BusFree()
|
||||
ctrl.bus->SetMSG(FALSE);
|
||||
ctrl.bus->SetCD(FALSE);
|
||||
ctrl.bus->SetIO(FALSE);
|
||||
ctrl.bus->SetBSY(FALSE);
|
||||
ctrl.bus->SetBSY(false);
|
||||
|
||||
// Initialize status and message
|
||||
ctrl.status = 0x00;
|
||||
@ -258,7 +264,7 @@ void SCSIDEV::Selection()
|
||||
ctrl.phase = BUS::selection;
|
||||
|
||||
// Raise BSY and respond
|
||||
ctrl.bus->SetBSY(TRUE);
|
||||
ctrl.bus->SetBSY(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -280,7 +286,7 @@ void SCSIDEV::Selection()
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIDEV::Execute()
|
||||
{
|
||||
LOGTRACE( "%s Execution phase command $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]);
|
||||
LOGTRACE("%s Execution phase command $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]);
|
||||
|
||||
// Phase Setting
|
||||
ctrl.phase = BUS::execute;
|
||||
@ -292,20 +298,13 @@ void SCSIDEV::Execute()
|
||||
|
||||
// If the command is valid it must be contained in the command map
|
||||
if (!scsi_commands.count(static_cast<scsi_command>(ctrl.cmd[0]))) {
|
||||
LOGWARN("%s Received unsupported command: $%02X", __PRETTY_FUNCTION__, (BYTE)ctrl.cmd[0]);
|
||||
CmdInvalid();
|
||||
return;
|
||||
}
|
||||
|
||||
command_t* command = scsi_commands[static_cast<scsi_command>(ctrl.cmd[0])];
|
||||
|
||||
LOGDEBUG("++++ CMD ++++ %s Received %s ($%02X)", __PRETTY_FUNCTION__, command->name, (unsigned int)ctrl.cmd[0]);
|
||||
|
||||
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE
|
||||
if ((unsigned int)ctrl.cmd[0] != 0x03) {
|
||||
ctrl.sense_key = 0;
|
||||
ctrl.asc = 0;
|
||||
}
|
||||
LOGDEBUG("++++ CMD ++++ %s ID %d received %s ($%02X)", __PRETTY_FUNCTION__, GetSCSIID(), command->name, (unsigned int)ctrl.cmd[0]);
|
||||
|
||||
// Process by command
|
||||
(this->*command->execute)();
|
||||
@ -318,14 +317,13 @@ void SCSIDEV::Execute()
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIDEV::MsgOut()
|
||||
{
|
||||
LOGTRACE("%s ID: %d",__PRETTY_FUNCTION__, this->GetSCSIID());
|
||||
LOGTRACE("%s ID %d",__PRETTY_FUNCTION__, GetSCSIID());
|
||||
|
||||
// Phase change
|
||||
if (ctrl.phase != BUS::msgout) {
|
||||
|
||||
LOGTRACE("Message Out Phase");
|
||||
|
||||
// process the IDENTIFY message
|
||||
// process the IDENTIFY message
|
||||
if (ctrl.phase == BUS::selection) {
|
||||
scsi.atnmsg = TRUE;
|
||||
scsi.msc = 0;
|
||||
@ -378,14 +376,22 @@ void SCSIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc)
|
||||
return;
|
||||
}
|
||||
|
||||
// Logical Unit
|
||||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||||
if (!ctrl.unit[lun] || asc == ERROR_CODES::INVALID_LUN) {
|
||||
lun = 0;
|
||||
}
|
||||
|
||||
LOGTRACE("%s Sense Key and ASC for subsequent REQUEST SENSE: $%02X, $%02X", __PRETTY_FUNCTION__, sense_key, asc);
|
||||
|
||||
// Set Sense Key and ASC for a subsequent REQUEST SENSE
|
||||
ctrl.sense_key = sense_key;
|
||||
ctrl.asc = asc;
|
||||
if (sense_key || asc) {
|
||||
// Set Sense Key and ASC for a subsequent REQUEST SENSE
|
||||
ctrl.unit[lun]->SetStatusCode((sense_key << 16) | (asc << 8));
|
||||
}
|
||||
|
||||
// Set status (CHECK CONDITION only for valid LUNs for non-REQUEST SENSE)
|
||||
ctrl.status = (ctrl.cmd[0] == eCmdRequestSense && asc == ERROR_CODES::asc::INVALID_LUN) ? 0x00 : 0x02;
|
||||
|
||||
// Set status and message(CHECK CONDITION)
|
||||
ctrl.status = 0x02;
|
||||
ctrl.message = 0x00;
|
||||
|
||||
LOGTRACE("%s Error (to status phase)", __PRETTY_FUNCTION__);
|
||||
@ -410,24 +416,19 @@ void SCSIDEV::CmdInquiry()
|
||||
LOGTRACE("%s INQUIRY Command", __PRETTY_FUNCTION__);
|
||||
|
||||
// Find a valid unit
|
||||
// TODO The code below is most likely wrong. It results in the same INQUIRY data being
|
||||
// TODO The code below is probably wrong. It results in the same INQUIRY data being
|
||||
// used for all LUNs, even though each LUN has its individual set of INQUIRY data.
|
||||
Disk *disk = NULL;
|
||||
int valid_lun;
|
||||
for (valid_lun = 0; valid_lun < UnitMax; valid_lun++) {
|
||||
PrimaryDevice *device = NULL;
|
||||
for (int valid_lun = 0; valid_lun < UnitMax; valid_lun++) {
|
||||
if (ctrl.unit[valid_lun]) {
|
||||
disk = ctrl.unit[valid_lun];
|
||||
device = ctrl.unit[valid_lun];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Processed on the disk side (it is originally processed by the controller)
|
||||
if (disk) {
|
||||
DWORD major = (DWORD)(RASCSI >> 8);
|
||||
DWORD minor = (DWORD)(RASCSI & 0xff);
|
||||
LOGTRACE("%s Buffer size is %d",__PRETTY_FUNCTION__, ctrl.bufsize);
|
||||
ctrl.length =
|
||||
ctrl.unit[valid_lun]->Inquiry(ctrl.cmd, ctrl.buffer, major, minor);
|
||||
if (device) {
|
||||
ctrl.length = device->Inquiry(ctrl.cmd, ctrl.buffer);
|
||||
} else {
|
||||
ctrl.length = 0;
|
||||
}
|
||||
@ -586,7 +587,7 @@ void SCSIDEV::CmdStartStop()
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->StartStop(ctrl.cmd);
|
||||
bool status = ctrl.unit[lun]->StartStop(ctrl.cmd);
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
@ -609,7 +610,7 @@ void SCSIDEV::CmdSendDiag()
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->SendDiag(ctrl.cmd);
|
||||
bool status = ctrl.unit[lun]->SendDiag(ctrl.cmd);
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
@ -632,7 +633,7 @@ void SCSIDEV::CmdRemoval()
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->Removal(ctrl.cmd);
|
||||
bool status = ctrl.unit[lun]->Removal(ctrl.cmd);
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
@ -648,15 +649,34 @@ void SCSIDEV::CmdRemoval()
|
||||
// READ CAPACITY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIDEV::CmdReadCapacity()
|
||||
void SCSIDEV::CmdReadCapacity10()
|
||||
{
|
||||
LOGTRACE( "%s READ CAPACITY Command ", __PRETTY_FUNCTION__);
|
||||
LOGTRACE( "%s READ CAPACITY(10) Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
int length = ctrl.unit[lun]->ReadCapacity(ctrl.cmd, ctrl.buffer);
|
||||
ASSERT(length >= 0);
|
||||
int length = ctrl.unit[lun]->ReadCapacity10(ctrl.cmd, ctrl.buffer);
|
||||
if (length <= 0) {
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::MEDIUM_NOT_PRESENT);
|
||||
return;
|
||||
}
|
||||
|
||||
// Length setting
|
||||
ctrl.length = length;
|
||||
|
||||
// Data-in Phase
|
||||
DataIn();
|
||||
}
|
||||
|
||||
void SCSIDEV::CmdReadCapacity16()
|
||||
{
|
||||
LOGTRACE( "%s READ CAPACITY(16) Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
int length = ctrl.unit[lun]->ReadCapacity16(ctrl.cmd, ctrl.buffer);
|
||||
if (length <= 0) {
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::MEDIUM_NOT_PRESENT);
|
||||
return;
|
||||
@ -696,7 +716,90 @@ void SCSIDEV::CmdRead10()
|
||||
ctrl.blocks <<= 8;
|
||||
ctrl.blocks |= ctrl.cmd[8];
|
||||
|
||||
LOGTRACE("%s READ(10) command record=%08X block=%d", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl.blocks);
|
||||
// Check capacity
|
||||
DWORD capacity = ctrl.unit[lun]->GetBlockCount();
|
||||
if (record > capacity || record + ctrl.blocks > capacity) {
|
||||
ostringstream s;
|
||||
s << "ID " << GetSCSIID() << ": Media capacity of " << capacity << " blocks exceeded: "
|
||||
<< "Trying to read block " << record << ", block count " << ctrl.blocks;
|
||||
LOGWARN("%s", s.str().c_str());
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::LBA_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGTRACE("%s READ(10) command record=%d blocks=%d", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl.blocks);
|
||||
|
||||
// Do not process 0 blocks
|
||||
if (ctrl.blocks == 0) {
|
||||
Status();
|
||||
return;
|
||||
}
|
||||
|
||||
// Command processing on drive
|
||||
ctrl.length = ctrl.unit[lun]->Read(ctrl.cmd, ctrl.buffer, record);
|
||||
if (ctrl.length <= 0) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set next block
|
||||
ctrl.next = record + 1;
|
||||
|
||||
// Data-in Phase
|
||||
DataIn();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// READ(16)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIDEV::CmdRead16()
|
||||
{
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Receive message if host bridge
|
||||
if (ctrl.unit[lun]->IsBridge()) {
|
||||
Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Report an error as long as big drives are not supported
|
||||
if (ctrl.cmd[2] || ctrl.cmd[3] || ctrl.cmd[4] || ctrl.cmd[5]) {
|
||||
LOGWARN("Can't execute READ(16) with 64 bit sector number");
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::LBA_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get record number and block number
|
||||
DWORD record = ctrl.cmd[6];
|
||||
record <<= 8;
|
||||
record |= ctrl.cmd[7];
|
||||
record <<= 8;
|
||||
record |= ctrl.cmd[8];
|
||||
record <<= 8;
|
||||
record |= ctrl.cmd[9];
|
||||
ctrl.blocks = ctrl.cmd[10];
|
||||
ctrl.blocks <<= 8;
|
||||
ctrl.blocks |= ctrl.cmd[11];
|
||||
ctrl.blocks <<= 8;
|
||||
ctrl.blocks |= ctrl.cmd[12];
|
||||
ctrl.blocks <<= 8;
|
||||
ctrl.blocks |= ctrl.cmd[13];
|
||||
|
||||
// Check capacity
|
||||
DWORD capacity = ctrl.unit[lun]->GetBlockCount();
|
||||
if (record > capacity || record + ctrl.blocks > capacity) {
|
||||
ostringstream s;
|
||||
s << "ID " << GetSCSIID() << ": Media capacity of " << capacity << " blocks exceeded: "
|
||||
<< "Trying to read block " << record << ", block count " << ctrl.blocks;
|
||||
LOGWARN("%s", s.str().c_str());
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::LBA_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGTRACE("%s READ(16) command record=%d blocks=%d", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl.blocks);
|
||||
|
||||
// Do not process 0 blocks
|
||||
if (ctrl.blocks == 0) {
|
||||
@ -746,7 +849,18 @@ void SCSIDEV::CmdWrite10()
|
||||
ctrl.blocks <<= 8;
|
||||
ctrl.blocks |= ctrl.cmd[8];
|
||||
|
||||
LOGTRACE("%s WRTIE(10) command record=%08X blocks=%d",__PRETTY_FUNCTION__, (unsigned int)record, (unsigned int)ctrl.blocks);
|
||||
// Check capacity
|
||||
DWORD capacity = ctrl.unit[lun]->GetBlockCount();
|
||||
if (record > capacity || record + ctrl.blocks > capacity) {
|
||||
ostringstream s;
|
||||
s << "ID " << GetSCSIID() << ": Media capacity of " << capacity << " blocks exceeded: "
|
||||
<< "Trying to read block " << record << ", block count " << ctrl.blocks;
|
||||
LOGWARN("%s", s.str().c_str());
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::LBA_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGTRACE("%s WRITE(10) command record=%d blocks=%d",__PRETTY_FUNCTION__, (unsigned int)record, (unsigned int)ctrl.blocks);
|
||||
|
||||
// Do not process 0 blocks
|
||||
if (ctrl.blocks == 0) {
|
||||
@ -758,10 +872,82 @@ void SCSIDEV::CmdWrite10()
|
||||
ctrl.length = ctrl.unit[lun]->WriteCheck(record);
|
||||
if (ctrl.length <= 0) {
|
||||
// Failure (Error)
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::WRITE_PROTECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set next block
|
||||
ctrl.next = record + 1;
|
||||
|
||||
// Data out phase
|
||||
DataOut();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// WRITE(16)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIDEV::CmdWrite16()
|
||||
{
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Receive message if host bridge
|
||||
if (ctrl.unit[lun]->IsBridge()) {
|
||||
Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Report an error as long as big drives are not supported
|
||||
if (ctrl.cmd[2] || ctrl.cmd[3] || ctrl.cmd[4] || ctrl.cmd[5]) {
|
||||
LOGWARN("Can't execute WRITE(16) with 64 bit sector number");
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::LBA_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get record number and block number
|
||||
DWORD record = ctrl.cmd[6];
|
||||
record <<= 8;
|
||||
record |= ctrl.cmd[7];
|
||||
record <<= 8;
|
||||
record |= ctrl.cmd[8];
|
||||
record <<= 8;
|
||||
record |= ctrl.cmd[9];
|
||||
ctrl.blocks = ctrl.cmd[10];
|
||||
ctrl.blocks <<= 8;
|
||||
ctrl.blocks |= ctrl.cmd[11];
|
||||
ctrl.blocks <<= 8;
|
||||
ctrl.blocks |= ctrl.cmd[12];
|
||||
ctrl.blocks <<= 8;
|
||||
ctrl.blocks |= ctrl.cmd[13];
|
||||
|
||||
// Check capacity
|
||||
DWORD capacity = ctrl.unit[lun]->GetBlockCount();
|
||||
if (record > capacity || record + ctrl.blocks > capacity) {
|
||||
ostringstream s;
|
||||
s << "ID " << GetSCSIID() << ": Media capacity of " << capacity << " blocks exceeded: "
|
||||
<< "Trying to read block " << record << ", block count " << ctrl.blocks;
|
||||
LOGWARN("%s", s.str().c_str());
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::LBA_OUT_OF_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGTRACE("%s WRITE(16) command record=%d blocks=%d",__PRETTY_FUNCTION__, (unsigned int)record, (unsigned int)ctrl.blocks);
|
||||
|
||||
// Do not process 0 blocks
|
||||
if (ctrl.blocks == 0) {
|
||||
Status();
|
||||
return;
|
||||
}
|
||||
|
||||
// Command processing on drive
|
||||
ctrl.length = ctrl.unit[lun]->WriteCheck(record);
|
||||
if (ctrl.length <= 0) {
|
||||
// Failure (Error)
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::WRITE_PROTECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set next block
|
||||
ctrl.next = record + 1;
|
||||
|
||||
@ -853,6 +1039,28 @@ void SCSIDEV::CmdVerify()
|
||||
DataOut();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// REPORT LUNS
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIDEV::CmdReportLuns()
|
||||
{
|
||||
DWORD lun = GetLun();
|
||||
|
||||
int length = ctrl.unit[lun]->ReportLuns(ctrl.cmd, ctrl.buffer);
|
||||
if (length <= 0) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
return;
|
||||
}
|
||||
|
||||
ctrl.length = length;
|
||||
|
||||
// Data in phase
|
||||
DataIn();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SYNCHRONIZE CACHE
|
||||
@ -923,7 +1131,7 @@ void SCSIDEV::CmdPlayAudio10()
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->PlayAudio(ctrl.cmd);
|
||||
bool status = ctrl.unit[lun]->PlayAudio(ctrl.cmd);
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
@ -944,7 +1152,7 @@ void SCSIDEV::CmdPlayAudioMSF()
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->PlayAudioMSF(ctrl.cmd);
|
||||
bool status = ctrl.unit[lun]->PlayAudioMSF(ctrl.cmd);
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
@ -965,7 +1173,7 @@ void SCSIDEV::CmdPlayAudioTrack()
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->PlayAudioTrack(ctrl.cmd);
|
||||
bool status = ctrl.unit[lun]->PlayAudioTrack(ctrl.cmd);
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
@ -976,6 +1184,19 @@ void SCSIDEV::CmdPlayAudioTrack()
|
||||
Status();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// GET EVENT STATUS NOTIFICATION
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIDEV::CmdGetEventStatusNotification()
|
||||
{
|
||||
GetLun();
|
||||
|
||||
// This naive (but legal) implementation avoids constant warnings in the logs
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// MODE SELECT10
|
||||
@ -1037,7 +1258,7 @@ void SCSIDEV::CmdGetMessage10()
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Error if not a host bridge
|
||||
if (!ctrl.unit[lun]->IsBridge() && !ctrl.unit[lun]->IsNuvolink()) {
|
||||
if (!ctrl.unit[lun]->IsBridge()) {
|
||||
LOGWARN("Received a GetMessage10 command for a non-bridge unit");
|
||||
Error();
|
||||
return;
|
||||
@ -1124,9 +1345,9 @@ void SCSIDEV::CmdRetrieveStats()
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Error if not a DaynaPort SCSI Link
|
||||
if (ctrl.unit[lun]->IsDaynaPort()) {
|
||||
LOGWARN("Received a CmdRetrieveStats command for a non-daynaport unit %s", ctrl.unit[lun]->GetID().c_str());
|
||||
Error();
|
||||
if (!ctrl.unit[lun]->IsDaynaPort()) {
|
||||
LOGWARN("Received a CmdRetrieveStats command for a non-daynaport unit %s", ctrl.unit[lun]->GetType().c_str());
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1161,15 +1382,14 @@ void SCSIDEV::CmdSetIfaceMode()
|
||||
|
||||
// Error if not a DaynaPort SCSI Link
|
||||
if (!ctrl.unit[lun]->IsDaynaPort()) {
|
||||
LOGWARN("%s Received a CmdRetrieveStats command for a non-daynaport unit %s", __PRETTY_FUNCTION__, ctrl.unit[lun]->GetID().c_str());
|
||||
Error();
|
||||
LOGWARN("%s Received a CmdSetIfaceMode command for a non-daynaport unit %s", __PRETTY_FUNCTION__, ctrl.unit[lun]->GetType().c_str());
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
SCSIDaynaPort *dayna_port = (SCSIDaynaPort*)ctrl.unit[lun];
|
||||
|
||||
// Check whether this command is telling us to "Set Interface Mode"
|
||||
// or "Set MAC Address"
|
||||
// Check whether this command is telling us to "Set Interface Mode" or "Set MAC Address"
|
||||
|
||||
ctrl.length = dayna_port->RetrieveStats(ctrl.cmd, ctrl.buffer);
|
||||
switch(ctrl.cmd[5]){
|
||||
@ -1201,7 +1421,7 @@ void SCSIDEV::CmdSetMcastAddr()
|
||||
|
||||
if (!ctrl.unit[lun]->IsDaynaPort()) {
|
||||
LOGWARN("Received a SetMcastAddress command for a non-daynaport unit");
|
||||
Error();
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1233,8 +1453,8 @@ void SCSIDEV::CmdEnableInterface()
|
||||
|
||||
// Error if not a DaynaPort SCSI Link
|
||||
if (!ctrl.unit[lun]->IsDaynaPort()) {
|
||||
LOGWARN("%s Received a CmdRetrieveStats command for a non-daynaport unit %s", __PRETTY_FUNCTION__, ctrl.unit[lun]->GetID().c_str());
|
||||
Error();
|
||||
LOGWARN("%s Received a CmdEnableInterface command for a non-daynaport unit %s", __PRETTY_FUNCTION__, ctrl.unit[lun]->GetType().c_str());
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1274,7 +1494,9 @@ void SCSIDEV::Send()
|
||||
|
||||
//if Length! = 0, send
|
||||
if (ctrl.length != 0) {
|
||||
LOGTRACE("%s sending handhake with offset %lu, length %lu", __PRETTY_FUNCTION__, ctrl.offset, ctrl.length);
|
||||
ostringstream s;
|
||||
s << __PRETTY_FUNCTION__ << " sending handhake with offset " << ctrl.offset << ", length " << ctrl.length;
|
||||
LOGTRACE("%s", s.str().c_str());
|
||||
|
||||
// The Daynaport needs to have a delay after the size/flags field
|
||||
// of the read response. In the MacOS driver, it looks like the
|
||||
@ -1310,7 +1532,9 @@ void SCSIDEV::Send()
|
||||
if (ctrl.blocks != 0) {
|
||||
// set next buffer (set offset, length)
|
||||
result = XferIn(ctrl.buffer);
|
||||
LOGTRACE("%s processing after data collection. Blocks: %lu", __PRETTY_FUNCTION__, ctrl.blocks);
|
||||
ostringstream s;
|
||||
s << __PRETTY_FUNCTION__ << " processing after data collection. Blocks: " << ctrl.blocks;
|
||||
LOGTRACE("%s", s.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1322,7 +1546,9 @@ void SCSIDEV::Send()
|
||||
|
||||
// Continue sending if block !=0
|
||||
if (ctrl.blocks != 0){
|
||||
LOGTRACE("%s Continuing to send. blocks = %lu", __PRETTY_FUNCTION__, ctrl.blocks);
|
||||
ostringstream s;
|
||||
s << __PRETTY_FUNCTION__ << " Continuing to send. blocks = " << ctrl.blocks;
|
||||
LOGTRACE("%s", s.str().c_str());
|
||||
ASSERT(ctrl.length > 0);
|
||||
ASSERT(ctrl.offset == 0);
|
||||
return;
|
||||
@ -1376,7 +1602,6 @@ void SCSIDEV::Send()
|
||||
void SCSIDEV::Receive()
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
BYTE data;
|
||||
|
||||
LOGTRACE("%s",__PRETTY_FUNCTION__);
|
||||
@ -1389,8 +1614,7 @@ void SCSIDEV::Receive()
|
||||
if (ctrl.length != 0) {
|
||||
LOGTRACE("%s length was != 0", __PRETTY_FUNCTION__);
|
||||
// Receive
|
||||
len = ctrl.bus->ReceiveHandShake(
|
||||
&ctrl.buffer[ctrl.offset], ctrl.length);
|
||||
len = ctrl.bus->ReceiveHandShake(&ctrl.buffer[ctrl.offset], ctrl.length);
|
||||
|
||||
// If not able to receive all, move to status phase
|
||||
if (len != (int)ctrl.length) {
|
||||
@ -1458,26 +1682,22 @@ void SCSIDEV::Receive()
|
||||
switch (ctrl.phase) {
|
||||
// Command phase
|
||||
case BUS::command:
|
||||
// Command data transfer
|
||||
len = 6;
|
||||
if (ctrl.buffer[0] >= 0x20 && ctrl.buffer[0] <= 0x7D) {
|
||||
// 10 byte CDB
|
||||
len = 10;
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
len = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
ctrl.cmd[i] = (DWORD)ctrl.buffer[i];
|
||||
LOGTRACE("%s Command $%02X",__PRETTY_FUNCTION__, (int)ctrl.cmd[i]);
|
||||
LOGTRACE("%s Command $%02X",__PRETTY_FUNCTION__, ctrl.cmd[i]);
|
||||
}
|
||||
|
||||
// Execution Phase
|
||||
try {
|
||||
Execute();
|
||||
}
|
||||
catch (const lunexception& e) {
|
||||
LOGINFO("%s Invalid LUN %d", __PRETTY_FUNCTION__, (int)e.getlun());
|
||||
try {
|
||||
Execute();
|
||||
}
|
||||
catch (const lun_exception& e) {
|
||||
LOGINFO("%s Invalid LUN %d", __PRETTY_FUNCTION__, e.getlun());
|
||||
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_LUN);
|
||||
}
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_LUN);
|
||||
}
|
||||
break;
|
||||
|
||||
// Message out phase
|
||||
@ -1493,7 +1713,7 @@ void SCSIDEV::Receive()
|
||||
|
||||
// Parsing messages sent by ATN
|
||||
if (scsi.atnmsg) {
|
||||
i = 0;
|
||||
int i = 0;
|
||||
while (i < scsi.msc) {
|
||||
// Message type
|
||||
data = scsi.msb[i];
|
||||
|
@ -67,7 +67,7 @@ public:
|
||||
BOOL IsSCSI() const {return TRUE;} // SCSI check
|
||||
|
||||
private:
|
||||
void SetupCommand(scsi_command, const char*, void (SCSIDEV::*)(void));
|
||||
void SetUpCommand(scsi_command, const char*, void (SCSIDEV::*)(void));
|
||||
|
||||
// Phase
|
||||
void BusFree(); // Bus free phase
|
||||
@ -88,7 +88,7 @@ private:
|
||||
void CmdStartStop(); // START STOP UNIT command
|
||||
void CmdSendDiag(); // SEND DIAGNOSTIC command
|
||||
void CmdRemoval(); // PREVENT/ALLOW MEDIUM REMOVAL command
|
||||
void CmdReadCapacity(); // READ CAPACITY command
|
||||
void CmdReadCapacity10(); // READ CAPACITY(10) command
|
||||
void CmdRead10(); // READ(10) command
|
||||
void CmdWrite10(); // WRITE(10) command
|
||||
void CmdSeek10(); // SEEK(10) command
|
||||
@ -99,8 +99,13 @@ private:
|
||||
void CmdPlayAudio10(); // PLAY AUDIO(10) command
|
||||
void CmdPlayAudioMSF(); // PLAY AUDIO MSF command
|
||||
void CmdPlayAudioTrack(); // PLAY AUDIO TRACK INDEX command
|
||||
void CmdGetEventStatusNotification();
|
||||
void CmdModeSelect10(); // MODE SELECT(10) command
|
||||
void CmdModeSense10(); // MODE SENSE(10) command
|
||||
void CmdReadCapacity16(); // READ CAPACITY(16) command
|
||||
void CmdRead16(); // READ(16) command
|
||||
void CmdWrite16(); // WRITE(16) command
|
||||
void CmdReportLuns(); // REPORT LUNS command
|
||||
void CmdGetMessage10(); // GET MESSAGE(10) command
|
||||
void CmdSendMessage10(); // SEND MESSAGE(10) command
|
||||
void CmdRetrieveStats(); // DaynaPort specific command
|
||||
|
46
src/raspberrypi/devices/block_device.h
Normal file
46
src/raspberrypi/devices/block_device.h
Normal file
@ -0,0 +1,46 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
// A BlockDevice supports SCSI block commands (see https://www.t10.org/drafts.htm, SBC-5)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "primary_device.h"
|
||||
|
||||
class BlockDevice : public PrimaryDevice
|
||||
{
|
||||
public:
|
||||
|
||||
BlockDevice(const string& id) : PrimaryDevice(id) {};
|
||||
virtual ~BlockDevice() {};
|
||||
|
||||
// Mandatory commands
|
||||
virtual bool TestUnitReady(const DWORD *cdb) = 0;
|
||||
virtual int Inquiry(const DWORD *cdb, BYTE *buf) = 0;
|
||||
virtual int ReportLuns(const DWORD *cdb, BYTE *buf) = 0;
|
||||
virtual bool Format(const DWORD *cdb) = 0;
|
||||
// READ(6), READ(10)
|
||||
virtual int Read(const DWORD *cdb, BYTE *buf, DWORD block) = 0;
|
||||
// WRITE(6), WRITE(10)
|
||||
virtual bool Write(const DWORD *cdb, const BYTE *buf, DWORD block) = 0;
|
||||
virtual int ReadCapacity10(const DWORD *cdb, BYTE *buf) = 0;
|
||||
virtual int ReadCapacity16(const DWORD *cdb, BYTE *buf) = 0;
|
||||
// TODO Uncomment as soon as there is a clean separation between controllers and devices
|
||||
//virtual int Read16(const DWORD *cdb, BYTE *buf, DWORD block) = 0;
|
||||
//virtual int Write16(const DWORD *cdb, BYTE *buf, DWORD block) = 0;
|
||||
//virtual int Verify16(const DWORD *cdb, BYTE *buf, DWORD block) = 0;
|
||||
|
||||
// Implemented optional commands
|
||||
virtual int RequestSense(const DWORD *cdb, BYTE *buf) = 0;
|
||||
virtual int ModeSense(const DWORD *cdb, BYTE *buf) = 0;
|
||||
virtual int ModeSense10(const DWORD *cdb, BYTE *buf) = 0;
|
||||
virtual bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) = 0;
|
||||
|
||||
// TODO Add the other optional commands currently implemented
|
||||
};
|
@ -26,7 +26,7 @@
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 漢字コード変換
|
||||
// Kanji code conversion
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#define IC_BUF_SIZE 1024
|
||||
@ -46,28 +46,23 @@ static void convert(char const *src, char const *dest,
|
||||
#endif
|
||||
{
|
||||
#ifndef __APPLE__
|
||||
iconv_t cd;
|
||||
size_t in;
|
||||
size_t out;
|
||||
size_t ret;
|
||||
|
||||
*outbuf = '\0';
|
||||
in = strlen(inbuf);
|
||||
out = outsize - 1;
|
||||
size_t in = strlen(inbuf);
|
||||
size_t out = outsize - 1;
|
||||
|
||||
cd = iconv_open(dest, src);
|
||||
iconv_t cd = iconv_open(dest, src);
|
||||
if (cd == (iconv_t)-1) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = iconv(cd, &inbuf, &in, &outbuf, &out);
|
||||
size_t ret = iconv(cd, &inbuf, &in, &outbuf, &out);
|
||||
if (ret == (size_t)-1) {
|
||||
return;
|
||||
}
|
||||
|
||||
iconv_close(cd);
|
||||
*outbuf = '\0';
|
||||
#endif //ifndef __macintosh__
|
||||
#endif //ifndef __APPLE__
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -202,33 +197,33 @@ void Human68k::namests_t::GetCopyFilename(BYTE* szFilename) const
|
||||
if (ext[j] != ' ')
|
||||
goto next_ext;
|
||||
}
|
||||
// 拡張子終端なら転送終了
|
||||
// If the extension ends, the transfer ends
|
||||
break;
|
||||
}
|
||||
next_ext:
|
||||
*p++ = c;
|
||||
}
|
||||
// 全ての文字を読み込むと、ここで i >= 3 となる
|
||||
// When all the characters are read, here i >= 3
|
||||
}
|
||||
|
||||
// 番兵追加
|
||||
// Add sentinel
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ホスト側ドライブ
|
||||
// Host side drive
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// デフォルトコンストラクタ
|
||||
// Default constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
CHostDrv::CHostDrv()
|
||||
{
|
||||
// 初期化
|
||||
// Initialization
|
||||
m_bWriteProtect = FALSE;
|
||||
m_bEnable = FALSE;
|
||||
m_capCache.sectors = 0;
|
||||
@ -240,7 +235,7 @@ CHostDrv::CHostDrv()
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// デストラクタ final
|
||||
// Final destructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
CHostDrv::~CHostDrv()
|
||||
@ -252,7 +247,7 @@ CHostDrv::~CHostDrv()
|
||||
m_nRing--;
|
||||
}
|
||||
|
||||
// 実体が存在しないことを確認 (念のため)
|
||||
// Confirm that the entity does not exist (just in case)
|
||||
ASSERT(m_cRing.Next() == &m_cRing);
|
||||
ASSERT(m_cRing.Prev() == &m_cRing);
|
||||
ASSERT(m_nRing == 0);
|
||||
@ -260,7 +255,7 @@ CHostDrv::~CHostDrv()
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// 初期化 (デバイス起動とロード)
|
||||
// Initialization (device boot and load)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag)
|
||||
@ -273,18 +268,18 @@ void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag)
|
||||
ASSERT(m_bVolumeCache == FALSE);
|
||||
ASSERT(m_szVolumeCache[0] == _T('\0'));
|
||||
|
||||
// 実体が存在しないことを確認 (念のため)
|
||||
// Confirm that the entity does not exist (just in case)
|
||||
ASSERT(m_cRing.Next() == &m_cRing);
|
||||
ASSERT(m_cRing.Prev() == &m_cRing);
|
||||
ASSERT(m_nRing == 0);
|
||||
|
||||
// パラメータを受け取る
|
||||
// Receive parameters
|
||||
if (nFlag & FSFLAG_WRITE_PROTECT)
|
||||
m_bWriteProtect = TRUE;
|
||||
strcpy(m_szBase, szBase);
|
||||
|
||||
// ベースパスの最後のパス区切りマークを削除する
|
||||
/// @warning Unicode利用時は修正が必要
|
||||
// Remove the last path delimiter in the base path
|
||||
// @warning needs to be modified when using Unicode
|
||||
TCHAR* pClear = NULL;
|
||||
TCHAR* p = m_szBase;
|
||||
for (;;) {
|
||||
@ -306,56 +301,52 @@ void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag)
|
||||
if (pClear)
|
||||
*pClear = _T('\0');
|
||||
|
||||
// 状態更新
|
||||
// Status update
|
||||
m_bEnable = TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// メディアチェック
|
||||
// Media check
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL CHostDrv::isMediaOffline()
|
||||
{
|
||||
|
||||
// オフライン状態チェック
|
||||
// Offline status check
|
||||
return m_bEnable == FALSE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// メディアバイトの取得
|
||||
// Get media bytes
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BYTE CHostDrv::GetMediaByte() const
|
||||
{
|
||||
|
||||
return Human68k::MEDIA_REMOTE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// ドライブ状態の取得
|
||||
// Get drive status
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DWORD CHostDrv::GetStatus() const
|
||||
{
|
||||
|
||||
return 0x40 | (m_bEnable ? (m_bWriteProtect ? 0x08 : 0) | 0x02 : 0);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// メディア状態設定
|
||||
// Media status settings
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostDrv::SetEnable(BOOL bEnable)
|
||||
{
|
||||
|
||||
m_bEnable = bEnable;
|
||||
|
||||
if (bEnable == FALSE) {
|
||||
// キャッシュ消去
|
||||
// Clear cache
|
||||
m_capCache.sectors = 0;
|
||||
m_bVolumeCache = FALSE;
|
||||
m_szVolumeCache[0] = _T('\0');
|
||||
@ -364,13 +355,12 @@ void CHostDrv::SetEnable(BOOL bEnable)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// メディア交換チェック
|
||||
// Media change check
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL CHostDrv::CheckMedia()
|
||||
{
|
||||
|
||||
// 状態更新
|
||||
// Status update
|
||||
Update();
|
||||
if (m_bEnable == FALSE)
|
||||
CleanCache();
|
||||
@ -380,38 +370,36 @@ BOOL CHostDrv::CheckMedia()
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// メディア状態更新
|
||||
// Media status update
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostDrv::Update()
|
||||
{
|
||||
|
||||
// メディア挿入とみなす
|
||||
// Considered as media insertion
|
||||
BOOL bEnable = TRUE;
|
||||
|
||||
// メディア状態反映
|
||||
// Media status reflected
|
||||
SetEnable(bEnable);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// イジェクト
|
||||
// Eject
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostDrv::Eject()
|
||||
{
|
||||
|
||||
// メディア排出
|
||||
// Media discharge
|
||||
CleanCache();
|
||||
SetEnable(FALSE);
|
||||
|
||||
// 状態更新
|
||||
// Status update
|
||||
Update();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// ボリュームラベルの取得
|
||||
// Get volume label
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostDrv::GetVolume(TCHAR* szLabel)
|
||||
@ -419,7 +407,7 @@ void CHostDrv::GetVolume(TCHAR* szLabel)
|
||||
ASSERT(szLabel);
|
||||
ASSERT(m_bEnable);
|
||||
|
||||
// ボリュームラベルの取得
|
||||
// Get volume label
|
||||
strcpy(m_szVolumeCache, "RASDRV ");
|
||||
if (m_szBase[0]) {
|
||||
strcat(m_szVolumeCache, m_szBase);
|
||||
@ -427,10 +415,10 @@ void CHostDrv::GetVolume(TCHAR* szLabel)
|
||||
strcat(m_szVolumeCache, "/");
|
||||
}
|
||||
|
||||
// キャッシュ更新
|
||||
// Cache update
|
||||
m_bVolumeCache = TRUE;
|
||||
|
||||
// 内容を転送
|
||||
// Transfer content
|
||||
strcpy(szLabel, m_szVolumeCache);
|
||||
}
|
||||
|
||||
@ -513,7 +501,6 @@ BOOL CHostDrv::GetCapacityCache(Human68k::capacity_t* pCapacity) const
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostDrv::CleanCache()
|
||||
{
|
||||
|
||||
Lock();
|
||||
for (CHostPath* p = (CHostPath*)m_cRing.Next(); p != &m_cRing;) {
|
||||
p->Release();
|
||||
@ -4002,7 +3989,7 @@ int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl)
|
||||
|
||||
case 1:
|
||||
// Human68k互換のためのダミー
|
||||
pIoctrl->param = (unsigned long)-1;
|
||||
pIoctrl->param = -1;
|
||||
return 0;
|
||||
|
||||
case 2:
|
||||
|
@ -66,9 +66,8 @@ static BOOL br_setif(int br_socket_fd, const char* bridgename, const char* ifnam
|
||||
|
||||
static BOOL ip_link(int fd, const char* ifname, BOOL up) {
|
||||
struct ifreq ifr;
|
||||
int err;
|
||||
strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1); // Need to save room for null terminator
|
||||
err = ioctl(fd, SIOCGIFFLAGS, &ifr);
|
||||
int err = ioctl(fd, SIOCGIFFLAGS, &ifr);
|
||||
if (err) {
|
||||
LOGERROR("Error: can't ioctl SIOCGIFFLAGS. Errno: %d %s", errno, strerror(errno));
|
||||
return FALSE;
|
||||
@ -257,7 +256,7 @@ void CTapDriver::OpenDump(const Filepath& path) {
|
||||
m_pcap_dumper = pcap_dump_open(m_pcap, path.GetPath());
|
||||
if (m_pcap_dumper == NULL) {
|
||||
LOGERROR("Error: can't open pcap file: %s", pcap_geterr(m_pcap));
|
||||
throw ioexception("Can't open pcap file");
|
||||
throw io_exception("Can't open pcap file");
|
||||
}
|
||||
|
||||
LOGTRACE("%s Opened %s for dumping", __PRETTY_FUNCTION__, path.GetPath());
|
||||
@ -302,10 +301,10 @@ void CTapDriver::Cleanup()
|
||||
// Enable
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL CTapDriver::Enable(){
|
||||
bool CTapDriver::Enable(){
|
||||
int fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
LOGDEBUG("%s: ip link set ras0 up", __PRETTY_FUNCTION__);
|
||||
BOOL result = ip_link(fd, "ras0", TRUE);
|
||||
bool result = ip_link(fd, "ras0", TRUE);
|
||||
close(fd);
|
||||
return result;
|
||||
}
|
||||
@ -315,10 +314,10 @@ BOOL CTapDriver::Enable(){
|
||||
// Disable
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL CTapDriver::Disable(){
|
||||
bool CTapDriver::Disable(){
|
||||
int fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
LOGDEBUG("%s: ip link set ras0 down", __PRETTY_FUNCTION__);
|
||||
BOOL result = ip_link(fd, "ras0", FALSE);
|
||||
bool result = ip_link(fd, "ras0", FALSE);
|
||||
close(fd);
|
||||
return result;
|
||||
}
|
||||
@ -409,7 +408,7 @@ int CTapDriver::Rx(BYTE *buf)
|
||||
buf[dwReceived + 2] = (BYTE)((crc >> 16) & 0xFF);
|
||||
buf[dwReceived + 3] = (BYTE)((crc >> 24) & 0xFF);
|
||||
|
||||
LOGDEBUG("%s CRC is %08lX - %02X %02X %02X %02X\n", __PRETTY_FUNCTION__, crc, buf[dwReceived+0], buf[dwReceived+1], buf[dwReceived+2], buf[dwReceived+3]);
|
||||
LOGDEBUG("%s CRC is %08X - %02X %02X %02X %02X\n", __PRETTY_FUNCTION__, crc, buf[dwReceived+0], buf[dwReceived+1], buf[dwReceived+2], buf[dwReceived+3]);
|
||||
|
||||
// Add FCS size to the received message size
|
||||
dwReceived += 4;
|
||||
|
@ -41,8 +41,8 @@ public:
|
||||
int Rx(BYTE *buf); // Receive
|
||||
int Tx(const BYTE *buf, int len); // Send
|
||||
BOOL PendingPackets(); // Check if there are IP packets available
|
||||
BOOL Enable(); // Enable the ras0 interface
|
||||
BOOL Disable(); // Disable the ras0 interface
|
||||
bool Enable(); // Enable the ras0 interface
|
||||
bool Disable(); // Disable the ras0 interface
|
||||
BOOL Flush(); // Purge all of the packets that are waiting to be processed
|
||||
|
||||
private:
|
||||
|
140
src/raspberrypi/devices/device.cpp
Normal file
140
src/raspberrypi/devices/device.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include "rascsi_version.h"
|
||||
#include "os.h"
|
||||
#include "log.h"
|
||||
#include "exceptions.h"
|
||||
#include "device.h"
|
||||
|
||||
Device::Device(const string& type)
|
||||
{
|
||||
assert(type.length() == 4);
|
||||
|
||||
this->type = type;
|
||||
|
||||
vendor = DEFAULT_VENDOR;
|
||||
char rev[5];
|
||||
sprintf(rev, "%02d%02d", rascsi_major_version, rascsi_minor_version);
|
||||
revision = rev;
|
||||
|
||||
ready = false;
|
||||
reset = false;
|
||||
attn = false;
|
||||
protectable = false;
|
||||
write_protected = false;
|
||||
read_only = false;
|
||||
removable = false;
|
||||
removed = false;
|
||||
lockable = false;
|
||||
locked = false;
|
||||
|
||||
id = 0;
|
||||
lun = 0;
|
||||
|
||||
status_code = STATUS_NOERROR;
|
||||
}
|
||||
|
||||
void Device::Reset()
|
||||
{
|
||||
locked = false;
|
||||
attn = false;
|
||||
reset = false;
|
||||
}
|
||||
|
||||
void Device::SetProtected(bool write_protected)
|
||||
{
|
||||
if (!read_only) {
|
||||
this->write_protected = write_protected;
|
||||
}
|
||||
}
|
||||
|
||||
void Device::SetVendor(const string& vendor)
|
||||
{
|
||||
if (vendor.empty() || vendor.length() > 8) {
|
||||
ostringstream error;
|
||||
error << "Vendor '" << vendor << "' must be between 1 and 8 characters";
|
||||
throw illegal_argument_exception(error.str());
|
||||
}
|
||||
|
||||
this->vendor = vendor;
|
||||
}
|
||||
|
||||
void Device::SetProduct(const string& product, bool force)
|
||||
{
|
||||
// Changing the device name is not SCSI compliant
|
||||
if (!this->product.empty() && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (product.empty() || product.length() > 16) {
|
||||
ostringstream error;
|
||||
error << "Product '" << product << "' must be between 1 and 16 characters";
|
||||
throw illegal_argument_exception(error.str());
|
||||
}
|
||||
|
||||
this->product = product;
|
||||
}
|
||||
|
||||
void Device::SetRevision(const string& revision)
|
||||
{
|
||||
if (revision.empty() || revision.length() > 4) {
|
||||
ostringstream error;
|
||||
error << "Revision '" << revision << "' must be between 1 and 4 characters";
|
||||
throw illegal_argument_exception(error.str());
|
||||
}
|
||||
|
||||
this->revision = revision;
|
||||
}
|
||||
|
||||
const string Device::GetPaddedName() const
|
||||
{
|
||||
string name = vendor;
|
||||
name.append(8 - vendor.length(), ' ');
|
||||
name += product;
|
||||
name.append(16 - product.length(), ' ');
|
||||
name += revision;
|
||||
name.append(4 - revision.length(), ' ');
|
||||
|
||||
assert(name.length() == 28);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
void Device::SetStatusCode(int status_code)
|
||||
{
|
||||
LOGTRACE("Setting status: Sense Key: $%02X, ASC: $%02X, ASCQ: $%02X", status_code >> 16, (status_code >> 8 &0xff), status_code & 0xff);
|
||||
|
||||
this->status_code = status_code;
|
||||
}
|
||||
|
||||
// TODO This implementation appears to be wrong: If a device is locked there
|
||||
// is no way to eject the medium without unlocking. In other words, there is
|
||||
// no "force" mode.
|
||||
bool Device::Eject(bool force)
|
||||
{
|
||||
if (!ready || !removable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must be unlocked if there is no force flag
|
||||
if (!force && locked) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ready = false;
|
||||
attn = false;
|
||||
removed = true;
|
||||
write_protected = false;
|
||||
locked = false;
|
||||
|
||||
return true;
|
||||
}
|
137
src/raspberrypi/devices/device.h
Normal file
137
src/raspberrypi/devices/device.h
Normal file
@ -0,0 +1,137 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define DEFAULT_VENDOR "RaSCSI"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Error definition (sense code returned by REQUEST SENSE)
|
||||
//
|
||||
// MSB Reserved (0x00)
|
||||
// Sense Key
|
||||
// Additional Sense Code (ASC)
|
||||
// LSB Additional Sense Code Qualifier(ASCQ)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#define STATUS_NOERROR 0x00000000 // NO ADDITIONAL SENSE INFO.
|
||||
#define STATUS_DEVRESET 0x00062900 // POWER ON OR RESET OCCURED
|
||||
#define STATUS_NOTREADY 0x00023a00 // MEDIUM NOT PRESENT
|
||||
#define STATUS_ATTENTION 0x00062800 // MEDIUM MAY HAVE CHANGED
|
||||
#define STATUS_PREVENT 0x00045302 // MEDIUM REMOVAL PREVENTED
|
||||
#define STATUS_READFAULT 0x00031100 // UNRECOVERED READ ERROR
|
||||
#define STATUS_WRITEFAULT 0x00030300 // PERIPHERAL DEVICE WRITE FAULT
|
||||
#define STATUS_WRITEPROTECT 0x00042700 // WRITE PROTECTED
|
||||
#define STATUS_MISCOMPARE 0x000e1d00 // MISCOMPARE DURING VERIFY
|
||||
#define STATUS_INVALIDCMD 0x00052000 // INVALID COMMAND OPERATION CODE
|
||||
#define STATUS_INVALIDLBA 0x00052100 // LOGICAL BLOCK ADDR. OUT OF RANGE
|
||||
#define STATUS_INVALIDCDB 0x00052400 // INVALID FIELD IN CDB
|
||||
#define STATUS_INVALIDLUN 0x00052500 // LOGICAL UNIT NOT SUPPORTED
|
||||
#define STATUS_INVALIDPRM 0x00052600 // INVALID FIELD IN PARAMETER LIST
|
||||
#define STATUS_INVALIDMSG 0x00054900 // INVALID MESSAGE ERROR
|
||||
#define STATUS_PARAMLEN 0x00051a00 // PARAMETERS LIST LENGTH ERROR
|
||||
#define STATUS_PARAMNOT 0x00052601 // PARAMETERS NOT SUPPORTED
|
||||
#define STATUS_PARAMVALUE 0x00052602 // PARAMETERS VALUE INVALID
|
||||
#define STATUS_PARAMSAVE 0x00053900 // SAVING PARAMETERS NOT SUPPORTED
|
||||
#define STATUS_NODEFECT 0x00010000 // DEFECT LIST NOT FOUND
|
||||
|
||||
class Device
|
||||
{
|
||||
private:
|
||||
|
||||
string type;
|
||||
|
||||
bool ready;
|
||||
bool reset;
|
||||
bool attn;
|
||||
|
||||
// Device is protectable/write-protected
|
||||
bool protectable;
|
||||
bool write_protected;
|
||||
// Device is permanently read-only
|
||||
bool read_only;
|
||||
|
||||
// Device is removable/removed
|
||||
bool removable;
|
||||
bool removed;
|
||||
|
||||
// Device is lockable/locked
|
||||
bool lockable;
|
||||
bool locked;
|
||||
|
||||
// Device ID and LUN
|
||||
unsigned int id;
|
||||
unsigned int lun;
|
||||
|
||||
string vendor;
|
||||
string product;
|
||||
string revision;
|
||||
|
||||
// Sense Key, ASC and ASCQ
|
||||
int status_code;
|
||||
|
||||
public:
|
||||
|
||||
Device(const string&);
|
||||
virtual ~Device() {};
|
||||
|
||||
const string& GetType() const { return type; }
|
||||
|
||||
bool IsReady() const { return ready; }
|
||||
void SetReady(bool ready) { this->ready = ready; }
|
||||
bool IsReset() const { return reset; }
|
||||
void SetReset(bool reset) { this->reset = reset; }
|
||||
void Reset();
|
||||
bool IsAttn() const { return attn; }
|
||||
void SetAttn(bool attn) { this->attn = attn; }
|
||||
|
||||
bool IsProtectable() const { return protectable; }
|
||||
void SetProtectable(bool protectable) { this->protectable = protectable; }
|
||||
bool IsProtected() const { return write_protected; }
|
||||
void SetProtected(bool);
|
||||
bool IsReadOnly() const { return read_only; }
|
||||
void SetReadOnly(bool read_only) { this->read_only = read_only; }
|
||||
|
||||
bool IsRemovable() const { return removable; }
|
||||
void SetRemovable(bool removable) { this->removable = removable; }
|
||||
bool IsRemoved() const { return removed; }
|
||||
void SetRemoved(bool removed) { this->removed = removed; }
|
||||
|
||||
bool IsLockable() const { return lockable; }
|
||||
void SetLockable(bool lockable) { this->lockable = lockable; }
|
||||
bool IsLocked() const { return locked; }
|
||||
void SetLocked(bool locked) { this->locked = locked; }
|
||||
|
||||
unsigned int GetId() const { return id; }
|
||||
void SetId(unsigned int id) { this->id = id; }
|
||||
unsigned int GetLun() const { return lun; }
|
||||
void SetLun(unsigned int lun) { this->lun = lun; }
|
||||
|
||||
void SetVendor(const string&);
|
||||
void SetProduct(const string&, bool = true);
|
||||
void SetRevision(const string&);
|
||||
const string GetPaddedName() const;
|
||||
|
||||
int GetStatusCode() const { return status_code; }
|
||||
void SetStatusCode(int status_code);
|
||||
|
||||
virtual bool Eject(bool);
|
||||
|
||||
bool IsSASI() const { return type == "SAHD"; }
|
||||
bool IsSCSI() const { return type == "SCHD" || type == "SCRM"; }
|
||||
bool IsCdRom() const { return type == "SCCD"; }
|
||||
bool IsMo() const { return type == "SCMO"; }
|
||||
bool IsBridge() const { return type == "SCBR"; }
|
||||
bool IsDaynaPort() const { return type == "SCDP"; }
|
||||
};
|
76
src/raspberrypi/devices/device_factory.cpp
Normal file
76
src/raspberrypi/devices/device_factory.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "sasihd.h"
|
||||
#include "scsihd.h"
|
||||
#include "scsihd_nec.h"
|
||||
#include "scsimo.h"
|
||||
#include "scsicd.h"
|
||||
#include "scsi_host_bridge.h"
|
||||
#include "scsi_daynaport.h"
|
||||
#include "device_factory.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace rascsi_interface;
|
||||
|
||||
Device *DeviceFactory::CreateDevice(PbDeviceType& type, const string& filename, const string& ext)
|
||||
{
|
||||
// If no type was specified try to derive the device type from the filename and extension
|
||||
if (type == UNDEFINED) {
|
||||
if (ext == "hdf") {
|
||||
type = SAHD;
|
||||
}
|
||||
else if (ext == "hds" || ext == "hdn" || ext == "hdi" || ext == "nhd" || ext == "hda") {
|
||||
type = SCHD;
|
||||
}
|
||||
else if (ext == "hdr") {
|
||||
type = SCRM;
|
||||
} else if (ext == "mos") {
|
||||
type = SCMO;
|
||||
} else if (ext == "iso") {
|
||||
type = SCCD;
|
||||
}
|
||||
else if (filename == "bridge") {
|
||||
type = SCBR;
|
||||
}
|
||||
else if (filename == "daynaport") {
|
||||
type = SCDP;
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SAHD:
|
||||
return new SASIHD();
|
||||
|
||||
case SCHD:
|
||||
if (ext == "hdn" || ext == "hdi" || ext == "nhd") {
|
||||
return new SCSIHD_NEC();
|
||||
} else {
|
||||
return new SCSIHD();
|
||||
}
|
||||
|
||||
case SCRM:
|
||||
return new SCSIHD(true);
|
||||
|
||||
case SCMO:
|
||||
return new SCSIMO();
|
||||
|
||||
case SCCD:
|
||||
return new SCSICD();
|
||||
|
||||
case SCBR:
|
||||
return new SCSIBR();
|
||||
|
||||
case SCDP:
|
||||
return new SCSIDaynaPort();
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
27
src/raspberrypi/devices/device_factory.h
Normal file
27
src/raspberrypi/devices/device_factory.h
Normal file
@ -0,0 +1,27 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
// A DeviceFactory creates devices based on their type and the extension of their image file
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "rascsi_interface.pb.h"
|
||||
|
||||
class Device;
|
||||
|
||||
class DeviceFactory
|
||||
{
|
||||
public:
|
||||
|
||||
DeviceFactory() { };
|
||||
~DeviceFactory() { };
|
||||
|
||||
static Device *CreateDevice(rascsi_interface::PbDeviceType& type, const std::string& filename, const std::string& ext);
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -20,49 +20,11 @@
|
||||
#include "xm6.h"
|
||||
#include "log.h"
|
||||
#include "scsi.h"
|
||||
#include "block_device.h"
|
||||
#include "file_support.h"
|
||||
#include "filepath.h"
|
||||
#include <string>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Error definition (sense code returned by REQUEST SENSE)
|
||||
//
|
||||
// MSB Reserved (0x00)
|
||||
// Sense Key
|
||||
// Additional Sense Code (ASC)
|
||||
// LSB Additional Sense Code Qualifier(ASCQ)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#define DISK_NOERROR 0x00000000 // NO ADDITIONAL SENSE INFO.
|
||||
#define DISK_DEVRESET 0x00062900 // POWER ON OR RESET OCCURED
|
||||
#define DISK_NOTREADY 0x00023a00 // MEDIUM NOT PRESENT
|
||||
#define DISK_ATTENTION 0x00062800 // MEDIUM MAY HAVE CHANGED
|
||||
#define DISK_PREVENT 0x00045302 // MEDIUM REMOVAL PREVENTED
|
||||
#define DISK_READFAULT 0x00031100 // UNRECOVERED READ ERROR
|
||||
#define DISK_WRITEFAULT 0x00030300 // PERIPHERAL DEVICE WRITE FAULT
|
||||
#define DISK_WRITEPROTECT 0x00042700 // WRITE PROTECTED
|
||||
#define DISK_MISCOMPARE 0x000e1d00 // MISCOMPARE DURING VERIFY
|
||||
#define DISK_INVALIDCMD 0x00052000 // INVALID COMMAND OPERATION CODE
|
||||
#define DISK_INVALIDLBA 0x00052100 // LOGICAL BLOCK ADDR. OUT OF RANGE
|
||||
#define DISK_INVALIDCDB 0x00052400 // INVALID FIELD IN CDB
|
||||
#define DISK_INVALIDLUN 0x00052500 // LOGICAL UNIT NOT SUPPORTED
|
||||
#define DISK_INVALIDPRM 0x00052600 // INVALID FIELD IN PARAMETER LIST
|
||||
#define DISK_INVALIDMSG 0x00054900 // INVALID MESSAGE ERROR
|
||||
#define DISK_PARAMLEN 0x00051a00 // PARAMETERS LIST LENGTH ERROR
|
||||
#define DISK_PARAMNOT 0x00052601 // PARAMETERS NOT SUPPORTED
|
||||
#define DISK_PARAMVALUE 0x00052602 // PARAMETERS VALUE INVALID
|
||||
#define DISK_PARAMSAVE 0x00053900 // SAVING PARAMETERS NOT SUPPORTED
|
||||
#define DISK_NODEFECT 0x00010000 // DEFECT LIST NOT FOUND
|
||||
|
||||
#if 0
|
||||
#define DISK_AUDIOPROGRESS 0x00??0011 // AUDIO PLAY IN PROGRESS
|
||||
#define DISK_AUDIOPAUSED 0x00??0012 // AUDIO PLAY PAUSED
|
||||
#define DISK_AUDIOSTOPPED 0x00??0014 // AUDIO PLAY STOPPED DUE TO ERROR
|
||||
#define DISK_AUDIOCOMPLETE 0x00??0013 // AUDIO PLAY SUCCESSFULLY COMPLETED
|
||||
#endif
|
||||
|
||||
#define BENDER_SIGNATURE "RaSCSI"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Disk Track
|
||||
@ -83,14 +45,14 @@ public:
|
||||
DWORD maplen; // Changed map length
|
||||
BOOL *changemap; // Changed map
|
||||
BOOL raw; // RAW mode flag
|
||||
off64_t imgoffset; // Offset to actual data
|
||||
off_t imgoffset; // Offset to actual data
|
||||
} disktrk_t;
|
||||
|
||||
public:
|
||||
// Basic Functions
|
||||
DiskTrack(); // Constructor
|
||||
virtual ~DiskTrack(); // Destructor
|
||||
void Init(int track, int size, int sectors, BOOL raw = FALSE, off64_t imgoff = 0);// Initialization
|
||||
void Init(int track, int size, int sectors, BOOL raw = FALSE, off_t imgoff = 0);// Initialization
|
||||
BOOL Load(const Filepath& path); // Load
|
||||
BOOL Save(const Filepath& path); // Save
|
||||
|
||||
@ -128,7 +90,7 @@ public:
|
||||
|
||||
public:
|
||||
// Basic Functions
|
||||
DiskCache(const Filepath& path, int size, int blocks,off64_t imgoff = 0);// Constructor
|
||||
DiskCache(const Filepath& path, int size, int blocks,off_t imgoff = 0);// Constructor
|
||||
virtual ~DiskCache(); // Destructor
|
||||
void SetRawMode(BOOL raw); // CD-ROM raw mode setting
|
||||
|
||||
@ -152,7 +114,7 @@ private:
|
||||
int sec_size; // Sector size (8 or 9 or 11)
|
||||
int sec_blocks; // Blocks per sector
|
||||
BOOL cd_raw; // CD-ROM RAW mode
|
||||
off64_t imgoffset; // Offset to actual data
|
||||
off_t imgoffset; // Offset to actual data
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
@ -160,116 +122,77 @@ private:
|
||||
// Disk
|
||||
//
|
||||
//===========================================================================
|
||||
class Disk
|
||||
class Disk : public BlockDevice
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
// Internal data structure
|
||||
typedef struct {
|
||||
std::string id; // Media ID
|
||||
BOOL ready; // Valid Disk
|
||||
bool readonly; // Read only
|
||||
bool protectable;
|
||||
bool writep; // Write protected
|
||||
bool removable; // Removable
|
||||
bool removed;
|
||||
bool lockable;
|
||||
bool locked; // Locked
|
||||
// TODO Non-disk devices must not inherit from Disk class
|
||||
bool supports_file;
|
||||
bool attn; // Attention
|
||||
bool reset; // Reset
|
||||
int size; // Sector Size
|
||||
DWORD blocks; // Total number of sectors
|
||||
DWORD lun; // LUN
|
||||
DWORD code; // Status code
|
||||
DiskCache *dcache; // Disk cache
|
||||
off64_t imgoffset; // Offset to actual data
|
||||
off_t imgoffset; // Offset to actual data
|
||||
} disk_t;
|
||||
|
||||
public:
|
||||
// Basic Functions
|
||||
Disk(std::string); // Constructor
|
||||
virtual ~Disk(); // Destructor
|
||||
virtual void Reset(); // Device Reset
|
||||
|
||||
// ID
|
||||
const std::string& GetID() const; // Get media ID
|
||||
bool IsSASI() const; // SASI Check
|
||||
bool IsSCSI() const; // SASI Check
|
||||
bool IsCdRom() const;
|
||||
bool IsMo() const;
|
||||
bool IsBridge() const;
|
||||
bool IsDaynaPort() const;
|
||||
bool IsNuvolink() const;
|
||||
|
||||
// Media Operations
|
||||
virtual void Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
virtual void Open(const Filepath& path); // Open
|
||||
void GetPath(Filepath& path) const; // Get the path
|
||||
bool Eject(bool); // Eject
|
||||
bool IsReady() const { return disk.ready; } // Ready check
|
||||
bool IsProtectable() const { return disk.protectable; }
|
||||
bool WriteP(bool); // Set Write Protect flag
|
||||
bool IsWriteP() const { return disk.writep; } // Get write protect flag
|
||||
bool IsReadOnly() const { return disk.readonly; } // Get read only flag
|
||||
bool IsRemovable() const { return disk.removable; } // Get is removable flag
|
||||
bool IsRemoved() const { return disk.removed; }
|
||||
bool IsLockable() const { return disk.lockable; }
|
||||
bool IsLocked() const { return disk.locked; } // Get locked status
|
||||
bool SupportsFile() const { return disk.supports_file; }
|
||||
bool IsAttn() const { return disk.attn; } // Get attention flag
|
||||
bool Eject(bool) override; // Eject
|
||||
bool Flush(); // Flush the cache
|
||||
|
||||
// Properties
|
||||
void SetLUN(DWORD lun) { disk.lun = lun; } // LUN set
|
||||
DWORD GetLUN() { return disk.lun; } // LUN get
|
||||
|
||||
// commands
|
||||
virtual int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor);// INQUIRY command
|
||||
virtual int RequestSense(const DWORD *cdb, BYTE *buf); // REQUEST SENSE command
|
||||
virtual bool TestUnitReady(const DWORD *cdb) override; // TEST UNIT READY command
|
||||
virtual int Inquiry(const DWORD *cdb, BYTE *buf) override; // INQUIRY command
|
||||
virtual int RequestSense(const DWORD *cdb, BYTE *buf) override; // REQUEST SENSE command
|
||||
int SelectCheck(const DWORD *cdb); // SELECT check
|
||||
int SelectCheck10(const DWORD *cdb); // SELECT(10) check
|
||||
virtual BOOL ModeSelect(const DWORD *cdb, const BYTE *buf, int length);// MODE SELECT command
|
||||
virtual int ModeSense(const DWORD *cdb, BYTE *buf); // MODE SENSE command
|
||||
virtual int ModeSense10(const DWORD *cdb, BYTE *buf); // MODE SENSE(10) command
|
||||
virtual bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override;// MODE SELECT command
|
||||
virtual int ModeSense(const DWORD *cdb, BYTE *buf) override; // MODE SENSE command
|
||||
virtual int ModeSense10(const DWORD *cdb, BYTE *buf) override; // MODE SENSE(10) command
|
||||
int ReadDefectData10(const DWORD *cdb, BYTE *buf); // READ DEFECT DATA(10) command
|
||||
virtual BOOL TestUnitReady(const DWORD *cdb); // TEST UNIT READY command
|
||||
BOOL Rezero(const DWORD *cdb); // REZERO command
|
||||
BOOL Format(const DWORD *cdb); // FORMAT UNIT command
|
||||
BOOL Reassign(const DWORD *cdb); // REASSIGN UNIT command
|
||||
virtual int Read(const DWORD *cdb, BYTE *buf, DWORD block); // READ command
|
||||
bool Rezero(const DWORD *cdb); // REZERO command
|
||||
bool Format(const DWORD *cdb) override; // FORMAT UNIT command
|
||||
bool Reassign(const DWORD *cdb); // REASSIGN UNIT command
|
||||
virtual int Read(const DWORD *cdb, BYTE *buf, DWORD block) override; // READ command
|
||||
virtual int WriteCheck(DWORD block); // WRITE check
|
||||
virtual BOOL Write(const DWORD *cdb, const BYTE *buf, DWORD block); // WRITE command
|
||||
BOOL Seek(const DWORD *cdb); // SEEK command
|
||||
BOOL Assign(const DWORD *cdb); // ASSIGN command
|
||||
BOOL Specify(const DWORD *cdb); // SPECIFY command
|
||||
BOOL StartStop(const DWORD *cdb); // START STOP UNIT command
|
||||
BOOL SendDiag(const DWORD *cdb); // SEND DIAGNOSTIC command
|
||||
BOOL Removal(const DWORD *cdb); // PREVENT/ALLOW MEDIUM REMOVAL command
|
||||
int ReadCapacity(const DWORD *cdb, BYTE *buf); // READ CAPACITY command
|
||||
BOOL Verify(const DWORD *cdb); // VERIFY command
|
||||
virtual bool Write(const DWORD *cdb, const BYTE *buf, DWORD block) override; // WRITE command
|
||||
bool Seek(const DWORD *cdb); // SEEK command
|
||||
bool Assign(const DWORD *cdb); // ASSIGN command
|
||||
bool Specify(const DWORD *cdb); // SPECIFY command
|
||||
bool StartStop(const DWORD *cdb); // START STOP UNIT command
|
||||
bool SendDiag(const DWORD *cdb); // SEND DIAGNOSTIC command
|
||||
bool Removal(const DWORD *cdb); // PREVENT/ALLOW MEDIUM REMOVAL command
|
||||
int ReadCapacity10(const DWORD *cdb, BYTE *buf) override; // READ CAPACITY(10) command
|
||||
int ReadCapacity16(const DWORD *cdb, BYTE *buf) override; // READ CAPACITY(16) command
|
||||
int ReportLuns(const DWORD *cdb, BYTE *buf); // REPORT LUNS command
|
||||
int GetSectorSize() const;
|
||||
void SetSectorSize(int);
|
||||
DWORD GetBlockCount() const;
|
||||
void SetBlockCount(DWORD);
|
||||
// TODO Currently not called
|
||||
bool Verify(const DWORD *cdb); // VERIFY command
|
||||
virtual int ReadToc(const DWORD *cdb, BYTE *buf); // READ TOC command
|
||||
virtual BOOL PlayAudio(const DWORD *cdb); // PLAY AUDIO command
|
||||
virtual BOOL PlayAudioMSF(const DWORD *cdb); // PLAY AUDIO MSF command
|
||||
virtual BOOL PlayAudioTrack(const DWORD *cdb); // PLAY AUDIO TRACK command
|
||||
|
||||
// Other
|
||||
bool IsCacheWB(); // Get cache writeback mode
|
||||
void SetCacheWB(BOOL enable); // Set cache writeback mode
|
||||
virtual bool PlayAudio(const DWORD *cdb); // PLAY AUDIO command
|
||||
virtual bool PlayAudioMSF(const DWORD *cdb); // PLAY AUDIO MSF command
|
||||
virtual bool PlayAudioTrack(const DWORD *cdb); // PLAY AUDIO TRACK command
|
||||
|
||||
protected:
|
||||
// Internal processing
|
||||
virtual int AddError(BOOL change, BYTE *buf); // Add error
|
||||
virtual int AddFormat(BOOL change, BYTE *buf); // Add format
|
||||
virtual int AddDrive(BOOL change, BYTE *buf); // Add drive
|
||||
int AddOpt(BOOL change, BYTE *buf); // Add optical
|
||||
int AddCache(BOOL change, BYTE *buf); // Add cache
|
||||
int AddCDROM(BOOL change, BYTE *buf); // Add CD-ROM
|
||||
int AddCDDA(BOOL change, BYTE *buf); // Add CD_DA
|
||||
virtual int AddVendor(int page, BOOL change, BYTE *buf); // Add vendor special info
|
||||
virtual int AddError(bool change, BYTE *buf); // Add error
|
||||
virtual int AddFormat(bool change, BYTE *buf); // Add format
|
||||
virtual int AddDrive(bool change, BYTE *buf); // Add drive
|
||||
int AddOpt(bool change, BYTE *buf); // Add optical
|
||||
int AddCache(bool change, BYTE *buf); // Add cache
|
||||
int AddCDROM(bool change, BYTE *buf); // Add CD-ROM
|
||||
int AddCDDA(bool, BYTE *buf); // Add CD_DA
|
||||
virtual int AddVendor(int page, bool change, BYTE *buf); // Add vendor special info
|
||||
BOOL CheckReady(); // Check if ready
|
||||
|
||||
// Internal data
|
||||
disk_t disk; // Internal disk data
|
||||
Filepath diskpath; // File path (for GetPath)
|
||||
BOOL cache_wb; // Cache mode
|
||||
};
|
||||
|
30
src/raspberrypi/devices/file_support.h
Normal file
30
src/raspberrypi/devices/file_support.h
Normal file
@ -0,0 +1,30 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
// Devices inheriting from FileSupport support device image files
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "filepath.h"
|
||||
|
||||
class FileSupport
|
||||
{
|
||||
private:
|
||||
Filepath diskpath;
|
||||
|
||||
public:
|
||||
|
||||
FileSupport() {};
|
||||
virtual ~FileSupport() {};
|
||||
|
||||
void GetPath(Filepath& path) const { path = diskpath; }
|
||||
void SetPath(const Filepath& path) { diskpath = path; }
|
||||
|
||||
virtual void Open(const Filepath&) = 0;
|
||||
};
|
33
src/raspberrypi/devices/primary_device.h
Normal file
33
src/raspberrypi/devices/primary_device.h
Normal file
@ -0,0 +1,33 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
// A PrimaryDevice supports SCSI primary commands (see https://www.t10.org/drafts.htm, SPC-6)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "device.h"
|
||||
|
||||
class PrimaryDevice : public Device
|
||||
{
|
||||
public:
|
||||
|
||||
PrimaryDevice(const string& id) : Device(id) {};
|
||||
virtual ~PrimaryDevice() {};
|
||||
|
||||
// Mandatory commands
|
||||
virtual bool TestUnitReady(const DWORD *cdb) = 0;
|
||||
virtual int Inquiry(const DWORD *cdb, BYTE *buf) = 0;
|
||||
virtual int ReportLuns(const DWORD *cdb, BYTE *buf) = 0;
|
||||
|
||||
// Implemented optional commands
|
||||
virtual int RequestSense(const DWORD *cdb, BYTE *buf) = 0;
|
||||
virtual int ModeSense(const DWORD *cdb, BYTE *buf) = 0;
|
||||
virtual int ModeSense10(const DWORD *cdb, BYTE *buf) = 0;
|
||||
virtual bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) = 0;
|
||||
};
|
@ -31,7 +31,7 @@
|
||||
//---------------------------------------------------------------------------
|
||||
SASIHD::SASIHD() : Disk("SAHD")
|
||||
{
|
||||
disk.protectable = true;
|
||||
SetProtectable(true);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -42,12 +42,12 @@ SASIHD::SASIHD() : Disk("SAHD")
|
||||
void SASIHD::Reset()
|
||||
{
|
||||
// Unlock, clear attention
|
||||
disk.locked = FALSE;
|
||||
disk.attn = FALSE;
|
||||
SetLocked(false);
|
||||
SetAttn(false);
|
||||
|
||||
// Reset, clear the code
|
||||
disk.reset = FALSE;
|
||||
disk.code = 0x00;
|
||||
SetReset(false);
|
||||
SetStatusCode(STATUS_NOERROR);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -55,18 +55,18 @@ void SASIHD::Reset()
|
||||
// Open
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
void SASIHD::Open(const Filepath& path)
|
||||
{
|
||||
ASSERT(!disk.ready);
|
||||
ASSERT(!IsReady());
|
||||
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw ioexception("Can't open hard disk file read-only");
|
||||
throw io_exception("Can't open hard disk file read-only");
|
||||
}
|
||||
|
||||
// Get file size
|
||||
off64_t size = fio.GetFileSize();
|
||||
off_t size = fio.GetFileSize();
|
||||
fio.Close();
|
||||
|
||||
#if defined(USE_MZ1F23_1024_SUPPORT)
|
||||
@ -74,28 +74,28 @@ void SASIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
// 20M(22437888 BS=1024 C=21912)
|
||||
if (size == 0x1566000) {
|
||||
// Sector size and number of blocks
|
||||
disk.size = 10;
|
||||
disk.blocks = (DWORD)(size >> 10);
|
||||
SetSectorSize(10);
|
||||
SetBlockCount((DWORD)(size >> 10));
|
||||
|
||||
// Call the base class
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
}
|
||||
#endif // USE_MZ1F23_1024_SUPPORT
|
||||
|
||||
#if defined(REMOVE_FIXED_SASIHD_SIZE)
|
||||
// Must be in 256-byte units
|
||||
if (size & 0xff) {
|
||||
throw ioexception("File size must be a multiple of 512 bytes");
|
||||
throw io_exception("File size must be a multiple of 256 bytes");
|
||||
}
|
||||
|
||||
// 10MB or more
|
||||
if (size < 0x9f5400) {
|
||||
throw ioexception("File size must be at least 10 MB");
|
||||
throw io_exception("File size must be at least 10 MB");
|
||||
}
|
||||
|
||||
// Limit to about 512MB
|
||||
if (size > 512 * 1024 * 1024) {
|
||||
throw ioexception("File size must not exceed 512 MB");
|
||||
throw io_exception("File size must not exceed 512 MB");
|
||||
}
|
||||
#else
|
||||
// 10MB, 20MB, 40MBのみ
|
||||
@ -114,13 +114,13 @@ void SASIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
|
||||
// Other (Not supported )
|
||||
default:
|
||||
throw ioexception("Unsupported file size");
|
||||
throw io_exception("Unsupported file size");
|
||||
}
|
||||
#endif // REMOVE_FIXED_SASIHD_SIZE
|
||||
|
||||
// Sector size and number of blocks
|
||||
disk.size = 8;
|
||||
disk.blocks = (DWORD)(size >> 8);
|
||||
// Sector size 256 bytes and number of blocks
|
||||
SetSectorSize(8);
|
||||
SetBlockCount((DWORD)(size >> 8));
|
||||
|
||||
// Call the base class
|
||||
Disk::Open(path);
|
||||
@ -147,11 +147,8 @@ int SASIHD::RequestSense(const DWORD *cdb, BYTE *buf)
|
||||
|
||||
// SASI fixed to non-extended format
|
||||
memset(buf, 0, size);
|
||||
buf[0] = (BYTE)(disk.code >> 16);
|
||||
buf[1] = (BYTE)(disk.lun << 5);
|
||||
|
||||
// Clear the code
|
||||
disk.code = 0x00;
|
||||
buf[0] = (BYTE)(GetStatusCode() >> 16);
|
||||
buf[1] = (BYTE)(GetLun() << 5);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
@ -24,13 +24,13 @@
|
||||
// SASI Hard Disk
|
||||
//
|
||||
//===========================================================================
|
||||
class SASIHD : public Disk
|
||||
class SASIHD : public Disk, public FileSupport
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
SASIHD(); // Constructor
|
||||
void Reset(); // Reset
|
||||
void Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
void Open(const Filepath& path); // Open
|
||||
|
||||
// commands
|
||||
int RequestSense(const DWORD *cdb, BYTE *buf); // REQUEST SENSE command
|
||||
|
@ -28,6 +28,7 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "scsi_daynaport.h"
|
||||
#include <sstream>
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
@ -49,9 +50,12 @@ const BYTE SCSIDaynaPort::m_apple_talk_addr[6] = { 0x09, 0x00, 0x07, 0xff, 0xff,
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP")
|
||||
{
|
||||
disk.supports_file = false;
|
||||
SetRemovable(false);
|
||||
|
||||
#ifdef __linux__
|
||||
SetVendor("Dayna");
|
||||
SetProduct("SCSI/Link");
|
||||
|
||||
#ifdef __linux__
|
||||
// TAP Driver Generation
|
||||
m_tap = new CTapDriver();
|
||||
m_bTapEnable = m_tap->Init();
|
||||
@ -63,8 +67,8 @@ SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP")
|
||||
|
||||
LOGTRACE("%s this->reset()", __PRETTY_FUNCTION__);
|
||||
this->Reset();
|
||||
disk.ready = true;
|
||||
disk.reset = false;
|
||||
SetReady(true);
|
||||
SetReset(false);
|
||||
|
||||
// Generate MAC Address
|
||||
LOGTRACE("%s memset(m_mac_addr, 0x00, 6);", __PRETTY_FUNCTION__);
|
||||
@ -115,14 +119,13 @@ SCSIDaynaPort::~SCSIDaynaPort()
|
||||
// INQUIRY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIDaynaPort::Inquiry(const DWORD *cdb, BYTE *buffer, DWORD major, DWORD minor)
|
||||
int SCSIDaynaPort::Inquiry(const DWORD *cdb, BYTE *buffer)
|
||||
{
|
||||
// scsi_cdb_6_byte_t command;
|
||||
// memcpy(&command,cdb,sizeof(command));
|
||||
|
||||
ASSERT(cdb);
|
||||
ASSERT(buffer);
|
||||
ASSERT(cdb[0] == 0x12);
|
||||
|
||||
//allocation_length = command->length;
|
||||
DWORD allocation_length = cdb[4] + (((DWORD)cdb[3]) << 8);
|
||||
@ -132,19 +135,7 @@ int SCSIDaynaPort::Inquiry(const DWORD *cdb, BYTE *buffer, DWORD major, DWORD mi
|
||||
// LOGWARN(":::::::::: Doing runtime pointer conversion: %04X", ((scsi_cdb_6_byte_t*)cdb)->length);
|
||||
// }
|
||||
|
||||
LOGTRACE("%s Inquiry with major %ld, minor %ld. Allocation length: %d",__PRETTY_FUNCTION__, major, minor, (int)allocation_length);
|
||||
|
||||
// Work-around in order to report an error for LUNs > 0
|
||||
DWORD lun = (cdb[1] >> 5) & 0x07;
|
||||
if (lun) {
|
||||
disk.code = DISK_INVALIDLUN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(cdb[1] & 0x1) {
|
||||
LOGERROR("EVPD bit is not supported");
|
||||
return -1;
|
||||
}
|
||||
LOGTRACE("%s Inquiry, allocation length: %d",__PRETTY_FUNCTION__, (int)allocation_length);
|
||||
|
||||
if (allocation_length > 4){
|
||||
if (allocation_length > sizeof(m_daynaport_inquiry_response)) {
|
||||
@ -153,39 +144,21 @@ int SCSIDaynaPort::Inquiry(const DWORD *cdb, BYTE *buffer, DWORD major, DWORD mi
|
||||
|
||||
// Copy the pre-canned response
|
||||
memcpy(buffer, m_daynaport_inquiry_response, allocation_length);
|
||||
|
||||
// Padded vendor, product, revision
|
||||
memcpy(&buffer[8], GetPaddedName().c_str(), 28);
|
||||
}
|
||||
|
||||
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
|
||||
if ((cdb[1] >> 5) & 0x07) {
|
||||
buffer[0] |= 0x7f;
|
||||
}
|
||||
|
||||
LOGTRACE("response size is %d", (int)allocation_length);
|
||||
|
||||
// Success
|
||||
disk.code = DISK_NOERROR;
|
||||
return allocation_length;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// RequestSense
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIDaynaPort::RequestSense(const DWORD *cdb, BYTE *buffer)
|
||||
{
|
||||
// The DaynaPort RequestSense response will always be 9 bytes.
|
||||
int size = 9;
|
||||
|
||||
LOGTRACE("%s size of sense data = %d", __PRETTY_FUNCTION__, size);
|
||||
|
||||
// Clear the buffer
|
||||
memset(buffer, 0, size);
|
||||
|
||||
// Only set the response code (70h)
|
||||
buffer[0] = 0x70;
|
||||
|
||||
// Clear the code
|
||||
disk.code = 0x00;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// READ
|
||||
@ -202,7 +175,9 @@ int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
|
||||
ASSERT(buf);
|
||||
|
||||
LOGTRACE("%s reading DaynaPort block %lu", __PRETTY_FUNCTION__, block);
|
||||
ostringstream s;
|
||||
s << __PRETTY_FUNCTION__ << " reading DaynaPort block " << block;
|
||||
LOGTRACE("%s", s.str().c_str());
|
||||
|
||||
if(command->operation_code != 0x08){
|
||||
LOGERROR("Received unexpected cdb command: %02X. Expected 0x08", (unsigned int)command->operation_code);
|
||||
@ -329,7 +304,9 @@ int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIDaynaPort::WriteCheck(DWORD block)
|
||||
{
|
||||
LOGTRACE("%s block: %lu", __PRETTY_FUNCTION__, block);
|
||||
ostringstream s;
|
||||
s << __PRETTY_FUNCTION__ << " block: " << block;
|
||||
LOGTRACE("%s", s.str().c_str());
|
||||
|
||||
// Status check
|
||||
if (!CheckReady()) {
|
||||
@ -337,7 +314,7 @@ int SCSIDaynaPort::WriteCheck(DWORD block)
|
||||
}
|
||||
|
||||
if(!m_bTapEnable){
|
||||
disk.code = DISK_NOTREADY;
|
||||
SetStatusCode(STATUS_NOTREADY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -345,13 +322,12 @@ int SCSIDaynaPort::WriteCheck(DWORD block)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Write
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSIDaynaPort::Write(const DWORD *cdb, const BYTE *buf, DWORD block)
|
||||
bool SCSIDaynaPort::Write(const DWORD *cdb, const BYTE *buf, DWORD block)
|
||||
{
|
||||
BYTE data_format;
|
||||
WORD data_length;
|
||||
@ -372,22 +348,21 @@ BOOL SCSIDaynaPort::Write(const DWORD *cdb, const BYTE *buf, DWORD block)
|
||||
if(data_format == 0x00){
|
||||
m_tap->Tx(buf, data_length);
|
||||
LOGTRACE("%s Transmitted %u bytes (00 format)", __PRETTY_FUNCTION__, data_length);
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
else if (data_format == 0x80){
|
||||
// The data length is actuall specified in the first 2 bytes of the payload
|
||||
data_length=(WORD)buf[1] + ((WORD)buf[0] << 8);
|
||||
m_tap->Tx(&buf[4], data_length);
|
||||
LOGTRACE("%s Transmitted %u bytes (80 format)", __PRETTY_FUNCTION__, data_length);
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)command->format);
|
||||
LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)data_format);
|
||||
return FALSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -444,7 +419,6 @@ int SCSIDaynaPort::RetrieveStats(const DWORD *cdb, BYTE *buffer)
|
||||
|
||||
response_size = 18;
|
||||
|
||||
|
||||
response_size = sizeof(m_scsi_link_stats);
|
||||
memcpy(buffer, &m_scsi_link_stats, sizeof(m_scsi_link_stats));
|
||||
|
||||
@ -457,7 +431,6 @@ int SCSIDaynaPort::RetrieveStats(const DWORD *cdb, BYTE *buffer)
|
||||
}
|
||||
|
||||
// Success
|
||||
disk.code = DISK_NOERROR;
|
||||
return response_size;
|
||||
// scsi_cdb_6_byte_t *command = (scsi_cdb_6_byte_t*)cdb;
|
||||
// scsi_resp_link_stats_t *response = (scsi_resp_link_stats_t*) buffer;
|
||||
@ -481,17 +454,12 @@ int SCSIDaynaPort::RetrieveStats(const DWORD *cdb, BYTE *buffer)
|
||||
// Enable or Disable the interface
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSIDaynaPort::EnableInterface(const DWORD *cdb)
|
||||
bool SCSIDaynaPort::EnableInterface(const DWORD *cdb)
|
||||
{
|
||||
int result;
|
||||
// scsi_cdb_6_byte_t *command = (scsi_cdb_6_byte_t*)cdb;
|
||||
|
||||
// if(command->control & 0x80)
|
||||
if(cdb[5] & 0x80)
|
||||
|
||||
{
|
||||
bool result;
|
||||
if (cdb[5] & 0x80) {
|
||||
result = m_tap->Enable();
|
||||
if(result){
|
||||
if (result) {
|
||||
LOGINFO("The DaynaPort interface has been ENABLED.");
|
||||
}
|
||||
else{
|
||||
@ -499,10 +467,9 @@ BOOL SCSIDaynaPort::EnableInterface(const DWORD *cdb)
|
||||
}
|
||||
m_tap->Flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
result = m_tap->Disable();
|
||||
if(result){
|
||||
if (result) {
|
||||
LOGINFO("The DaynaPort interface has been DISABLED.");
|
||||
}
|
||||
else{
|
||||
@ -510,7 +477,7 @@ BOOL SCSIDaynaPort::EnableInterface(const DWORD *cdb)
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -518,13 +485,12 @@ BOOL SCSIDaynaPort::EnableInterface(const DWORD *cdb)
|
||||
// TEST UNIT READY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSIDaynaPort::TestUnitReady(const DWORD* /*cdb*/)
|
||||
bool SCSIDaynaPort::TestUnitReady(const DWORD* /*cdb*/)
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__);
|
||||
|
||||
// TEST UNIT READY Success
|
||||
disk.code = DISK_NOERROR;
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -44,34 +44,32 @@ public:
|
||||
// Basic Functions
|
||||
SCSIDaynaPort();
|
||||
// Constructor
|
||||
virtual ~SCSIDaynaPort();
|
||||
~SCSIDaynaPort();
|
||||
// Destructor
|
||||
void Open(const Filepath& path, BOOL attn = TRUE);
|
||||
// Capture packets
|
||||
|
||||
// commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buffer, DWORD major, DWORD minor);
|
||||
int Inquiry(const DWORD *cdb, BYTE *buffer) override;
|
||||
// INQUIRY command
|
||||
BOOL TestUnitReady(const DWORD *cdb);
|
||||
bool TestUnitReady(const DWORD *cdb) override;
|
||||
// TEST UNIT READY command
|
||||
int Read(const DWORD *cdb, BYTE *buf, DWORD block) override;
|
||||
// READ command
|
||||
BOOL Write(const DWORD *cdb, const BYTE *buf, DWORD block) override;
|
||||
bool Write(const DWORD *cdb, const BYTE *buf, DWORD block) override;
|
||||
// WRITE command
|
||||
int WriteCheck(DWORD block) override;
|
||||
int WriteCheck(DWORD block);
|
||||
// WRITE check
|
||||
|
||||
int RetrieveStats(const DWORD *cdb, BYTE *buffer);
|
||||
// Retrieve DaynaPort statistics
|
||||
BOOL EnableInterface(const DWORD *cdb);
|
||||
bool EnableInterface(const DWORD *cdb);
|
||||
// Enable/Disable Interface command
|
||||
|
||||
void SetMacAddr(const DWORD *cdb, BYTE *buffer);
|
||||
// Set MAC address
|
||||
void SetMode(const DWORD *cdb, BYTE *buffer);
|
||||
// Set the mode: whether broadcast traffic is enabled or not
|
||||
int RequestSense(const DWORD *cdb, BYTE *buf) override;
|
||||
|
||||
static const BYTE CMD_SCSILINK_STATS = 0x09;
|
||||
static const BYTE CMD_SCSILINK_ENABLE = 0x0E;
|
||||
static const BYTE CMD_SCSILINK_SET = 0x0C;
|
||||
@ -161,7 +159,7 @@ private:
|
||||
//http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/pocket_scsiLINK/pocketscsilink_inq.png
|
||||
const uint8_t m_daynaport_inquiry_response[44] = {
|
||||
0x03, 0x00, 0x01, 0x00, // 4 bytes
|
||||
0x1E, 0x00, 0x00, 0x00, // 4 bytes
|
||||
0x1F, 0x00, 0x00, 0x00, // 4 bytes
|
||||
// Vendor ID (8 Bytes)
|
||||
'D','a','y','n','a',' ',' ',' ',
|
||||
// Product ID (16 Bytes)
|
||||
|
@ -34,14 +34,16 @@
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIBR::SCSIBR() : Disk("SCBR")
|
||||
{
|
||||
disk.supports_file = false;
|
||||
SetRemovable(false);
|
||||
|
||||
SetProduct("RASCSI BRIDGE");
|
||||
|
||||
fsoptlen = 0;
|
||||
fsoutlen = 0;
|
||||
fsresult = 0;
|
||||
packet_len = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
#ifdef __linux__
|
||||
// TAP Driver Generation
|
||||
tap = new CTapDriver();
|
||||
m_bTapEnable = tap->Init();
|
||||
@ -55,7 +57,7 @@ SCSIBR::SCSIBR() : Disk("SCBR")
|
||||
|
||||
// Packet reception flag OFF
|
||||
packet_enable = FALSE;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Create host file system
|
||||
fs = new CFileSys();
|
||||
@ -87,18 +89,15 @@ SCSIBR::~SCSIBR()
|
||||
// INQUIRY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIBR::Inquiry(
|
||||
const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor)
|
||||
int SCSIBR::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
{
|
||||
char rev[32];
|
||||
|
||||
ASSERT(cdb);
|
||||
ASSERT(buf);
|
||||
ASSERT(cdb[0] == 0x12);
|
||||
|
||||
// EVPD check
|
||||
if (cdb[1] & 0x01) {
|
||||
disk.code = DISK_INVALIDCDB;
|
||||
SetStatusCode(STATUS_INVALIDCDB);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -111,7 +110,7 @@ int SCSIBR::Inquiry(
|
||||
buf[0] = 0x09;
|
||||
|
||||
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
|
||||
if (((cdb[1] >> 5) & 0x07) != disk.lun) {
|
||||
if (((cdb[1] >> 5) & 0x07) != GetLun()) {
|
||||
buf[0] = 0x7f;
|
||||
}
|
||||
|
||||
@ -119,19 +118,8 @@ int SCSIBR::Inquiry(
|
||||
buf[3] = 0x02;
|
||||
buf[4] = 36 - 5 + 8; // required + 8 byte extension
|
||||
|
||||
// Fill with blanks
|
||||
memset(&buf[8], 0x20, buf[4] - 3);
|
||||
|
||||
// Vendor name
|
||||
memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE));
|
||||
|
||||
// Product name
|
||||
memcpy(&buf[16], "RASCSI BRIDGE", 13);
|
||||
|
||||
// Revision (XM6 version number)
|
||||
sprintf(rev, "0%01d%01d%01d",
|
||||
(int)major, (int)(minor >> 4), (int)(minor & 0x0f));
|
||||
memcpy(&buf[32], rev, 4);
|
||||
// Padded vendor, product, revision
|
||||
memcpy(&buf[8], GetPaddedName().c_str(), 28);
|
||||
|
||||
// Optional function valid flag
|
||||
buf[36] = '0';
|
||||
@ -153,7 +141,7 @@ int SCSIBR::Inquiry(
|
||||
}
|
||||
|
||||
// Success
|
||||
disk.code = DISK_NOERROR;
|
||||
SetStatusCode(STATUS_NOERROR);
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -162,11 +150,11 @@ int SCSIBR::Inquiry(
|
||||
// TEST UNIT READY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSIBR::TestUnitReady(const DWORD* /*cdb*/)
|
||||
bool SCSIBR::TestUnitReady(const DWORD* /*cdb*/)
|
||||
{
|
||||
// TEST UNIT READY Success
|
||||
disk.code = DISK_NOERROR;
|
||||
return TRUE;
|
||||
SetStatusCode(STATUS_NOERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -33,11 +33,11 @@ class SCSIBR : public Disk
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIBR(); // Constructor
|
||||
virtual ~SCSIBR(); // Destructor
|
||||
~SCSIBR(); // Destructor
|
||||
|
||||
// commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command
|
||||
BOOL TestUnitReady(const DWORD *cdb); // TEST UNIT READY command
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf) override; // INQUIRY command
|
||||
bool TestUnitReady(const DWORD *cdb) override; // TEST UNIT READY command
|
||||
int GetMessage10(const DWORD *cdb, BYTE *buf); // GET MESSAGE10 command
|
||||
BOOL SendMessage10(const DWORD *cdb, BYTE *buf); // SEND MESSAGE10 command
|
||||
|
||||
|
@ -62,7 +62,7 @@ CDTrack::~CDTrack()
|
||||
// Init
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL CDTrack::Init(int track, DWORD first, DWORD last)
|
||||
void CDTrack::Init(int track, DWORD first, DWORD last)
|
||||
{
|
||||
ASSERT(!valid);
|
||||
ASSERT(track >= 1);
|
||||
@ -75,8 +75,6 @@ BOOL CDTrack::Init(int track, DWORD first, DWORD last)
|
||||
// Remember LBA
|
||||
first_lba = first;
|
||||
last_lba = last;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -215,30 +213,6 @@ BOOL CDTrack::IsAudio() const
|
||||
return audio;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// CD-DA Buffer
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
CDDABuf::CDDABuf()
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
CDDABuf::~CDDABuf()
|
||||
{
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SCSI CD-ROM
|
||||
@ -252,9 +226,10 @@ CDDABuf::~CDDABuf()
|
||||
//---------------------------------------------------------------------------
|
||||
SCSICD::SCSICD() : Disk("SCCD")
|
||||
{
|
||||
disk.removable = true;
|
||||
disk.lockable = true;
|
||||
disk.writep = true;
|
||||
SetRemovable(true);
|
||||
SetReadOnly(true);
|
||||
|
||||
SetProduct("CD-ROM CDU-55S");
|
||||
|
||||
// NOT in raw format
|
||||
rawfile = FALSE;
|
||||
@ -287,22 +262,22 @@ SCSICD::~SCSICD()
|
||||
// Open
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSICD::Open(const Filepath& path, BOOL attn)
|
||||
void SCSICD::Open(const Filepath& path)
|
||||
{
|
||||
Fileio fio;
|
||||
off64_t size;
|
||||
off_t size;
|
||||
TCHAR file[5];
|
||||
|
||||
ASSERT(!disk.ready);
|
||||
ASSERT(!IsReady());
|
||||
|
||||
// Initialization, track clear
|
||||
disk.blocks = 0;
|
||||
SetBlockCount(0);
|
||||
rawfile = FALSE;
|
||||
ClearTrack();
|
||||
|
||||
// Open as read-only
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw ioexception("Can't open CD-ROM file read-only");
|
||||
throw io_exception("Can't open CD-ROM file read-only");
|
||||
}
|
||||
|
||||
// Close and transfer for physical CD access
|
||||
@ -317,7 +292,7 @@ void SCSICD::Open(const Filepath& path, BOOL attn)
|
||||
size = fio.GetFileSize();
|
||||
if (size <= 4) {
|
||||
fio.Close();
|
||||
throw ioexception("CD-ROM file size must be at least 4 bytes");
|
||||
throw io_exception("CD-ROM file size must be at least 4 bytes");
|
||||
}
|
||||
|
||||
// Judge whether it is a CUE sheet or an ISO file
|
||||
@ -336,22 +311,21 @@ void SCSICD::Open(const Filepath& path, BOOL attn)
|
||||
}
|
||||
|
||||
// Successful opening
|
||||
ASSERT(disk.blocks > 0);
|
||||
disk.size = 11;
|
||||
ASSERT(GetBlockCount() > 0);
|
||||
|
||||
// Sector size 2048 bytes
|
||||
SetSectorSize(11);
|
||||
|
||||
// Call the base class
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
|
||||
// Set RAW flag
|
||||
ASSERT(disk.dcache);
|
||||
disk.dcache->SetRawMode(rawfile);
|
||||
|
||||
// Since it is a ROM media, writing is not possible
|
||||
disk.writep = TRUE;
|
||||
|
||||
// Attention if ready
|
||||
if (disk.ready && attn) {
|
||||
disk.attn = TRUE;
|
||||
if (IsReady()) {
|
||||
SetAttn(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,7 +336,7 @@ void SCSICD::Open(const Filepath& path, BOOL attn)
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSICD::OpenCue(const Filepath& /*path*/)
|
||||
{
|
||||
throw ioexception("Opening CUE CD-ROM files is not supported");
|
||||
throw io_exception("Opening CUE CD-ROM files is not supported");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -378,20 +352,20 @@ void SCSICD::OpenIso(const Filepath& path)
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw ioexception("Can't open ISO CD-ROM file read-only");
|
||||
throw io_exception("Can't open ISO CD-ROM file read-only");
|
||||
}
|
||||
|
||||
// Get file size
|
||||
off64_t size = fio.GetFileSize();
|
||||
off_t size = fio.GetFileSize();
|
||||
if (size < 0x800) {
|
||||
fio.Close();
|
||||
throw ioexception("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");
|
||||
}
|
||||
|
||||
// Read the first 12 bytes and close
|
||||
if (!fio.Read(header, sizeof(header))) {
|
||||
fio.Close();
|
||||
throw ioexception("Can't read header of ISO CD-ROM file");
|
||||
throw io_exception("Can't read header of ISO CD-ROM file");
|
||||
}
|
||||
|
||||
// Check if it is RAW format
|
||||
@ -403,14 +377,14 @@ void SCSICD::OpenIso(const Filepath& path)
|
||||
// 00,FFx10,00, so it is presumed to be RAW format
|
||||
if (!fio.Read(header, 4)) {
|
||||
fio.Close();
|
||||
throw ioexception("Can't read header of raw ISO CD-ROM file");
|
||||
throw io_exception("Can't read header of raw ISO CD-ROM file");
|
||||
}
|
||||
|
||||
// Supports MODE1/2048 or MODE1/2352 only
|
||||
if (header[3] != 0x01) {
|
||||
// Different mode
|
||||
fio.Close();
|
||||
throw ioexception("Illegal raw ISO CD-ROM file header");
|
||||
throw io_exception("Illegal raw ISO CD-ROM file header");
|
||||
}
|
||||
|
||||
// Set to RAW file
|
||||
@ -421,31 +395,33 @@ void SCSICD::OpenIso(const Filepath& path)
|
||||
if (rawfile) {
|
||||
// Size must be a multiple of 2536 and less than 700MB
|
||||
if (size % 0x930) {
|
||||
throw ioexception("Raw ISO CD-ROM file size must be a multiple of 2536 bytes");
|
||||
throw io_exception("Raw ISO CD-ROM file size must be a multiple of 2536 bytes");
|
||||
}
|
||||
if (size > 912579600) {
|
||||
throw ioexception("Raw ISO CD-ROM file size must not exceed 700 MB");
|
||||
throw io_exception("Raw ISO CD-ROM file size must not exceed 700 MB");
|
||||
}
|
||||
|
||||
// Set the number of blocks
|
||||
disk.blocks = (DWORD)(size / 0x930);
|
||||
SetBlockCount((DWORD)(size / 0x930));
|
||||
} else {
|
||||
// Size must be a multiple of 2048 and less than 700MB
|
||||
if (size & 0x7ff) {
|
||||
throw ioexception("ISO CD-ROM file size must be a multiple of 2048 bytes");
|
||||
throw io_exception("ISO CD-ROM file size must be a multiple of 2048 bytes");
|
||||
}
|
||||
if (size > 0x2bed5000) {
|
||||
throw ioexception("ISO CD-ROM file size must not exceed 700 MB");
|
||||
throw io_exception("ISO CD-ROM file size must not exceed 700 MB");
|
||||
}
|
||||
|
||||
// Set the number of blocks
|
||||
disk.blocks = (DWORD)(size >> 11);
|
||||
SetBlockCount((DWORD)(size >> 11));
|
||||
}
|
||||
|
||||
LOGINFO("Media capacity for image file '%s': %d blocks", path.GetPath(), GetBlockCount());
|
||||
|
||||
// Create only one data track
|
||||
ASSERT(!track[0]);
|
||||
track[0] = new CDTrack(this);
|
||||
track[0]->Init(1, 0, disk.blocks - 1);
|
||||
track[0]->Init(1, 0, GetBlockCount() - 1);
|
||||
track[0]->SetPath(FALSE, path);
|
||||
tracks = 1;
|
||||
dataindex = 0;
|
||||
@ -461,14 +437,14 @@ void SCSICD::OpenPhysical(const Filepath& path)
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw ioexception("Can't open CD-ROM file read-only");
|
||||
throw io_exception("Can't open CD-ROM file read-only");
|
||||
}
|
||||
|
||||
// Get size
|
||||
off64_t size = fio.GetFileSize();
|
||||
off_t size = fio.GetFileSize();
|
||||
if (size < 0x800) {
|
||||
fio.Close();
|
||||
throw ioexception("CD-ROM file size must be at least 2048 bytes");
|
||||
throw io_exception("CD-ROM file size must be at least 2048 bytes");
|
||||
}
|
||||
|
||||
// Close
|
||||
@ -476,19 +452,19 @@ void SCSICD::OpenPhysical(const Filepath& path)
|
||||
|
||||
// Size must be a multiple of 2048 and less than 700MB
|
||||
if (size & 0x7ff) {
|
||||
throw ioexception("CD-ROM file size must be a multiple of 2048 bytes");
|
||||
throw io_exception("CD-ROM file size must be a multiple of 2048 bytes");
|
||||
}
|
||||
if (size > 0x2bed5000) {
|
||||
throw ioexception("CD-ROM file size must not exceed 700 MB");
|
||||
throw io_exception("CD-ROM file size must not exceed 700 MB");
|
||||
}
|
||||
|
||||
// Set the number of blocks
|
||||
disk.blocks = (DWORD)(size >> 11);
|
||||
SetBlockCount((DWORD)(size >> 11));
|
||||
|
||||
// Create only one data track
|
||||
ASSERT(!track[0]);
|
||||
track[0] = new CDTrack(this);
|
||||
track[0]->Init(1, 0, disk.blocks - 1);
|
||||
track[0]->Init(1, 0, GetBlockCount() - 1);
|
||||
track[0]->SetPath(FALSE, path);
|
||||
tracks = 1;
|
||||
dataindex = 0;
|
||||
@ -499,18 +475,14 @@ void SCSICD::OpenPhysical(const Filepath& path)
|
||||
// INQUIRY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSICD::Inquiry(
|
||||
const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor)
|
||||
int SCSICD::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
{
|
||||
char rev[32];
|
||||
|
||||
ASSERT(cdb);
|
||||
ASSERT(buf);
|
||||
ASSERT(cdb[0] == 0x12);
|
||||
|
||||
// EVPD check
|
||||
if (cdb[1] & 0x01) {
|
||||
disk.code = DISK_INVALIDCDB;
|
||||
SetStatusCode(STATUS_INVALIDCDB);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -524,7 +496,7 @@ int SCSICD::Inquiry(
|
||||
buf[0] = 0x05;
|
||||
|
||||
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
|
||||
if (((cdb[1] >> 5) & 0x07) != disk.lun) {
|
||||
if (((cdb[1] >> 5) & 0x07) != GetLun()) {
|
||||
buf[0] = 0x7f;
|
||||
}
|
||||
|
||||
@ -536,16 +508,9 @@ int SCSICD::Inquiry(
|
||||
// Fill with blanks
|
||||
memset(&buf[8], 0x20, buf[4] - 3);
|
||||
|
||||
// Vendor name
|
||||
memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE));
|
||||
// Padded vendor, product, revision
|
||||
memcpy(&buf[8], GetPaddedName().c_str(), 28);
|
||||
|
||||
// Product name
|
||||
memcpy(&buf[16], "CD-ROM CDU-55S", 14);
|
||||
|
||||
// Revision (XM6 version number)
|
||||
sprintf(rev, "0%01d%01d%01d",
|
||||
(int)major, (int)(minor >> 4), (int)(minor & 0x0f));
|
||||
memcpy(&buf[32], rev, 4);
|
||||
//
|
||||
// The following code worked with the modified Apple CD-ROM drivers. Need to
|
||||
// test with the original code to see if it works as well....
|
||||
@ -577,8 +542,6 @@ int SCSICD::Inquiry(
|
||||
size = (int)cdb[4];
|
||||
}
|
||||
|
||||
// Success
|
||||
disk.code = DISK_NOERROR;
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -589,8 +552,6 @@ int SCSICD::Inquiry(
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSICD::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
{
|
||||
Filepath path;
|
||||
|
||||
ASSERT(buf);
|
||||
|
||||
// Status check
|
||||
@ -603,7 +564,7 @@ int SCSICD::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
|
||||
// if invalid, out of range
|
||||
if (index < 0) {
|
||||
disk.code = DISK_INVALIDLBA;
|
||||
SetStatusCode(STATUS_INVALIDLBA);
|
||||
return 0;
|
||||
}
|
||||
ASSERT(track[index]);
|
||||
@ -615,12 +576,13 @@ int SCSICD::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
disk.dcache = NULL;
|
||||
|
||||
// Reset the number of blocks
|
||||
disk.blocks = track[index]->GetBlocks();
|
||||
ASSERT(disk.blocks > 0);
|
||||
SetBlockCount(track[index]->GetBlocks());
|
||||
ASSERT(GetBlockCount() > 0);
|
||||
|
||||
// Recreate the disk cache
|
||||
Filepath path;
|
||||
track[index]->GetPath(path);
|
||||
disk.dcache = new DiskCache(path, disk.size, disk.blocks);
|
||||
disk.dcache = new DiskCache(path, GetSectorSize(), GetBlockCount());
|
||||
disk.dcache->SetRawMode(rawfile);
|
||||
|
||||
// Reset data index
|
||||
@ -639,13 +601,7 @@ int SCSICD::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf)
|
||||
{
|
||||
int loop;
|
||||
int i;
|
||||
BOOL msf;
|
||||
DWORD lba;
|
||||
|
||||
ASSERT(cdb);
|
||||
ASSERT(cdb[0] == 0x43);
|
||||
ASSERT(buf);
|
||||
|
||||
// Check if ready
|
||||
@ -663,18 +619,14 @@ int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf)
|
||||
memset(buf, 0, length);
|
||||
|
||||
// Get MSF Flag
|
||||
if (cdb[1] & 0x02) {
|
||||
msf = TRUE;
|
||||
} else {
|
||||
msf = FALSE;
|
||||
}
|
||||
bool msf = cdb[1] & 0x02;
|
||||
|
||||
// Get and check the last track number
|
||||
int last = track[tracks - 1]->GetTrackNo();
|
||||
if ((int)cdb[6] > last) {
|
||||
// Except for AA
|
||||
if (cdb[6] != 0xaa) {
|
||||
disk.code = DISK_INVALIDCDB;
|
||||
SetStatusCode(STATUS_INVALIDCDB);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -699,7 +651,7 @@ int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf)
|
||||
buf[2] = (BYTE)track[0]->GetTrackNo();
|
||||
buf[3] = (BYTE)last;
|
||||
buf[6] = 0xaa;
|
||||
lba = track[tracks - 1]->GetLast() + 1;
|
||||
DWORD lba = track[tracks - 1]->GetLast() + 1;
|
||||
if (msf) {
|
||||
LBAtoMSF(lba, &buf[8]);
|
||||
} else {
|
||||
@ -710,13 +662,13 @@ int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf)
|
||||
}
|
||||
|
||||
// Otherwise, error
|
||||
disk.code = DISK_INVALIDCDB;
|
||||
SetStatusCode(STATUS_INVALIDCDB);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Number of track descriptors returned this time (number of loops)
|
||||
loop = last - track[index]->GetTrackNo() + 1;
|
||||
int loop = last - track[index]->GetTrackNo() + 1;
|
||||
ASSERT(loop >= 1);
|
||||
|
||||
// Create header
|
||||
@ -727,7 +679,7 @@ int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf)
|
||||
buf += 4;
|
||||
|
||||
// Loop....
|
||||
for (i = 0; i < loop; i++) {
|
||||
for (int i = 0; i < loop; i++) {
|
||||
// ADR and Control
|
||||
if (track[index]->IsAudio()) {
|
||||
// audio track
|
||||
@ -757,39 +709,6 @@ int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf)
|
||||
return length;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// PLAY AUDIO
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSICD::PlayAudio(const DWORD* /*cdb*/)
|
||||
{
|
||||
disk.code = DISK_INVALIDCDB;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// PLAY AUDIO MSF
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSICD::PlayAudioMSF(const DWORD* /*cdb*/)
|
||||
{
|
||||
disk.code = DISK_INVALIDCDB;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// PLAY AUDIO TRACK
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSICD::PlayAudioTrack(const DWORD* /*cdb*/)
|
||||
{
|
||||
disk.code = DISK_INVALIDCDB;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// LBA→MSF Conversion
|
||||
@ -892,7 +811,7 @@ int SCSICD::SearchTrack(DWORD lba) const
|
||||
// Next Frame
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSICD::NextFrame()
|
||||
bool SCSICD::NextFrame()
|
||||
{
|
||||
ASSERT((frame >= 0) && (frame < 75));
|
||||
|
||||
@ -900,20 +819,5 @@ BOOL SCSICD::NextFrame()
|
||||
frame = (frame + 1) % 75;
|
||||
|
||||
// FALSE after one lap
|
||||
if (frame != 0) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Get CD-DA buffer
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSICD::GetBuf(
|
||||
DWORD* /*buffer*/, int /*samples*/, DWORD /*rate*/)
|
||||
{
|
||||
// TODO Missing implementation?
|
||||
return frame != 0;
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
// Basic Functions
|
||||
CDTrack(SCSICD *scsicd); // Constructor
|
||||
virtual ~CDTrack(); // Destructor
|
||||
BOOL Init(int track, DWORD first, DWORD last); // Initialization
|
||||
void Init(int track, DWORD first, DWORD last); // Initialization
|
||||
|
||||
// Properties
|
||||
void SetPath(BOOL cdda, const Filepath& path); // Set the path
|
||||
@ -62,47 +62,12 @@ private:
|
||||
Filepath imgpath; // Image file path
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// CD-DA Buffer
|
||||
//
|
||||
//===========================================================================
|
||||
class CDDABuf
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
CDDABuf(); // Constructor
|
||||
virtual ~CDDABuf(); // Destructor
|
||||
#if 0
|
||||
BOOL Init(); // Initialization
|
||||
BOOL Load(const Filepath& path); // Load
|
||||
BOOL Save(const Filepath& path); // Save
|
||||
|
||||
// API
|
||||
void Clear(); // Clear the buffer
|
||||
BOOL Open(Filepath& path); // File specification
|
||||
BOOL GetBuf(DWORD *buffer, int frames); // Get the buffer
|
||||
BOOL IsValid(); // Check if Valid
|
||||
BOOL ReadReq(); // Read Request
|
||||
BOOL IsEnd() const; // Finish check
|
||||
|
||||
private:
|
||||
Filepath wavepath; // Wave path
|
||||
BOOL valid; // Open result (is it valid?)
|
||||
DWORD *buf; // Data buffer
|
||||
DWORD read; // Read pointer
|
||||
DWORD write; // Write pointer
|
||||
DWORD num; // Valid number of data
|
||||
DWORD rest; // Remaining file size
|
||||
#endif
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SCSI CD-ROM
|
||||
//
|
||||
//===========================================================================
|
||||
class SCSICD : public Disk
|
||||
class SCSICD : public Disk, public FileSupport
|
||||
{
|
||||
public:
|
||||
// Number of tracks
|
||||
@ -113,19 +78,18 @@ public:
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSICD(); // Constructor
|
||||
virtual ~SCSICD(); // Destructor
|
||||
void Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
~SCSICD(); // Destructor
|
||||
void Open(const Filepath& path); // Open
|
||||
|
||||
// commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf) override; // INQUIRY command
|
||||
int Read(const DWORD *cdb, BYTE *buf, DWORD block) override; // READ command
|
||||
int ReadToc(const DWORD *cdb, BYTE *buf); // READ TOC command
|
||||
BOOL PlayAudio(const DWORD *cdb); // PLAY AUDIO command
|
||||
BOOL PlayAudioMSF(const DWORD *cdb); // PLAY AUDIO MSF command
|
||||
BOOL PlayAudioTrack(const DWORD *cdb); // PLAY AUDIO TRACK command
|
||||
|
||||
// CD-DA
|
||||
BOOL NextFrame(); // Frame notification
|
||||
// TODO Never called
|
||||
bool NextFrame(); // Frame notification
|
||||
// TODO Never called
|
||||
void GetBuf(DWORD *buffer, int samples, DWORD rate); // Get CD-DA buffer
|
||||
|
||||
// LBA-MSF変換
|
||||
@ -148,12 +112,4 @@ private:
|
||||
int audioindex; // Current audio track
|
||||
|
||||
int frame; // Frame number
|
||||
|
||||
#if 0
|
||||
CDDABuf da_buf; // CD-DA buffer
|
||||
int da_num; // Number of CD-DA tracks
|
||||
int da_cur; // CD-DA current track
|
||||
int da_next; // CD-DA next track
|
||||
BOOL da_req; // CD-DA data request
|
||||
#endif
|
||||
};
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "xm6.h"
|
||||
#include "fileio.h"
|
||||
#include "exceptions.h"
|
||||
#include <sstream>
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
@ -29,9 +30,10 @@
|
||||
// Constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIHD::SCSIHD() : Disk("SCHD")
|
||||
SCSIHD::SCSIHD(bool removable) : Disk(removable ? "SCRM" : "SCHD")
|
||||
{
|
||||
disk.protectable = true;
|
||||
SetRemovable(removable);
|
||||
SetProtectable(true);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -42,12 +44,12 @@ SCSIHD::SCSIHD() : Disk("SCHD")
|
||||
void SCSIHD::Reset()
|
||||
{
|
||||
// Unlock and release attention
|
||||
disk.locked = FALSE;
|
||||
disk.attn = FALSE;
|
||||
SetLocked(false);
|
||||
SetAttn(false);
|
||||
|
||||
// No reset, clear code
|
||||
disk.reset = FALSE;
|
||||
disk.code = 0x00;
|
||||
SetReset(false);
|
||||
SetStatusCode(STATUS_NOERROR);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -55,38 +57,43 @@ void SCSIHD::Reset()
|
||||
// Open
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
void SCSIHD::Open(const Filepath& path)
|
||||
{
|
||||
ASSERT(!disk.ready);
|
||||
ASSERT(!IsReady());
|
||||
|
||||
// read open required
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw ioexception("Can't open hard disk file read-only");
|
||||
throw io_exception("Can't open hard disk file read-only");
|
||||
}
|
||||
|
||||
// Get file size
|
||||
off64_t size = fio.GetFileSize();
|
||||
off_t size = fio.GetFileSize();
|
||||
fio.Close();
|
||||
|
||||
// Must be 512 bytes
|
||||
// Must be a multiple of 512 bytes
|
||||
if (size & 0x1ff) {
|
||||
throw ioexception("File size must be a multiple of 512 bytes");
|
||||
throw io_exception("File size must be a multiple of 512 bytes");
|
||||
}
|
||||
|
||||
// 2TB according to xm6i
|
||||
// There is a similar one in wxw/wxw_cfg.cpp
|
||||
// Bigger files/drives require READ/WRITE(16) to be implemented
|
||||
// 2TB is the current maximum
|
||||
if (size > 2LL * 1024 * 1024 * 1024 * 1024) {
|
||||
throw ioexception("File size must not exceed 2 TB");
|
||||
throw io_exception("File size must not exceed 2 TB");
|
||||
}
|
||||
|
||||
// sector size and number of blocks
|
||||
disk.size = 9;
|
||||
disk.blocks = (DWORD)(size >> 9);
|
||||
// sector size 512 bytes and number of blocks
|
||||
SetSectorSize(9);
|
||||
SetBlockCount((DWORD)(size >> 9));
|
||||
|
||||
LOGINFO("Media capacity for image file '%s': %d blocks", path.GetPath(),GetBlockCount());
|
||||
|
||||
// Set the default product name based on the drive capacity
|
||||
stringstream product;
|
||||
product << DEFAULT_PRODUCT << " " << (GetBlockCount() >> 11) << " MB";
|
||||
SetProduct(product.str(), false);
|
||||
|
||||
// Call base class
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -94,85 +101,52 @@ void SCSIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
// INQUIRY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD:: Inquiry(
|
||||
const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor)
|
||||
int SCSIHD::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
{
|
||||
char vendor[32];
|
||||
char product[32];
|
||||
char rev[32];
|
||||
|
||||
ASSERT(cdb);
|
||||
ASSERT(buf);
|
||||
ASSERT(cdb[0] == 0x12);
|
||||
|
||||
// EVPD check
|
||||
if (cdb[1] & 0x01) {
|
||||
disk.code = DISK_INVALIDCDB;
|
||||
SetStatusCode(STATUS_INVALIDCDB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Ready check (Error if no image file)
|
||||
if (!disk.ready) {
|
||||
disk.code = DISK_NOTREADY;
|
||||
if (!IsReady()) {
|
||||
SetStatusCode(STATUS_NOTREADY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Basic data
|
||||
// buf[0] ... Direct Access Device
|
||||
// buf[1] ... Bit 7 set means removable
|
||||
// buf[2] ... SCSI-2 compliant command system
|
||||
// buf[3] ... SCSI-2 compliant Inquiry response
|
||||
// buf[4] ... Inquiry additional data
|
||||
memset(buf, 0, 8);
|
||||
|
||||
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
|
||||
if (((cdb[1] >> 5) & 0x07) != disk.lun) {
|
||||
if (((cdb[1] >> 5) & 0x07) != GetLun()) {
|
||||
buf[0] = 0x7f;
|
||||
}
|
||||
|
||||
buf[1] = IsRemovable() ? 0x80 : 0x00;
|
||||
buf[2] = 0x02;
|
||||
buf[3] = 0x02;
|
||||
buf[4] = 122 + 3; // Value close to real HDD
|
||||
|
||||
// Fill with blanks
|
||||
memset(&buf[8], 0x20, buf[4] - 3);
|
||||
|
||||
// Determine vendor name/product name
|
||||
sprintf(vendor, BENDER_SIGNATURE);
|
||||
int size = disk.blocks >> 11;
|
||||
if (size < 300)
|
||||
sprintf(product, "PRODRIVE LPS%dS", size);
|
||||
else if (size < 600)
|
||||
sprintf(product, "MAVERICK%dS", size);
|
||||
else if (size < 800)
|
||||
sprintf(product, "LIGHTNING%dS", size);
|
||||
else if (size < 1000)
|
||||
sprintf(product, "TRAILBRAZER%dS", size);
|
||||
else if (size < 2000)
|
||||
sprintf(product, "FIREBALL%dS", size);
|
||||
else
|
||||
sprintf(product, "FBSE%d.%dS", size / 1000, (size % 1000) / 100);
|
||||
|
||||
// Vendor name
|
||||
memcpy(&buf[8], vendor, strlen(vendor));
|
||||
|
||||
// Product name
|
||||
memcpy(&buf[16], product, strlen(product));
|
||||
|
||||
// Revision
|
||||
sprintf(rev, "0%01d%01d%01d",
|
||||
(int)major, (int)(minor >> 4), (int)(minor & 0x0f));
|
||||
memcpy(&buf[32], rev, 4);
|
||||
// Padded vendor, product, revision
|
||||
memcpy(&buf[8], GetPaddedName().c_str(), 28);
|
||||
|
||||
// Size of data that can be returned
|
||||
size = (buf[4] + 5);
|
||||
int size = (buf[4] + 5);
|
||||
|
||||
// Limit if the other buffer is small
|
||||
if (size > (int)cdb[4]) {
|
||||
size = (int)cdb[4];
|
||||
}
|
||||
|
||||
// Success
|
||||
disk.code = DISK_NOERROR;
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -182,7 +156,7 @@ int SCSIHD:: Inquiry(
|
||||
// *Not affected by disk.code
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
bool SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
{
|
||||
BYTE page;
|
||||
int size;
|
||||
@ -195,13 +169,13 @@ BOOL SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
// Mode Parameter header
|
||||
if (length >= 12) {
|
||||
// Check the block length bytes
|
||||
size = 1 << disk.size;
|
||||
size = 1 << GetSectorSize();
|
||||
if (buf[9] != (BYTE)(size >> 16) ||
|
||||
buf[10] != (BYTE)(size >> 8) ||
|
||||
buf[11] != (BYTE)size) {
|
||||
// currently does not allow changing sector length
|
||||
disk.code = DISK_INVALIDPRM;
|
||||
return FALSE;
|
||||
SetStatusCode(STATUS_INVALIDPRM);
|
||||
return false;
|
||||
}
|
||||
buf += 12;
|
||||
length -= 12;
|
||||
@ -216,12 +190,12 @@ BOOL SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
// format device
|
||||
case 0x03:
|
||||
// check the number of bytes in the physical sector
|
||||
size = 1 << disk.size;
|
||||
size = 1 << GetSectorSize();
|
||||
if (buf[0xc] != (BYTE)(size >> 8) ||
|
||||
buf[0xd] != (BYTE)size) {
|
||||
// currently does not allow changing sector length
|
||||
disk.code = DISK_INVALIDPRM;
|
||||
return FALSE;
|
||||
SetStatusCode(STATUS_INVALIDPRM);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -253,7 +227,31 @@ BOOL SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
}
|
||||
|
||||
// Do not generate an error for the time being (MINIX)
|
||||
disk.code = DISK_NOERROR;
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Add Vendor special page to make drive Apple compatible
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD::AddVendor(int page, bool change, BYTE *buf)
|
||||
{
|
||||
ASSERT(buf);
|
||||
|
||||
// Page code 48 or 63
|
||||
if (page != 0x30 && page != 0x3f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set the message length
|
||||
buf[0] = 0x30;
|
||||
buf[1] = 0x1c;
|
||||
|
||||
// No changeable area
|
||||
if (!change) {
|
||||
memcpy(&buf[0xa], "APPLE COMPUTER, INC.", 20);
|
||||
}
|
||||
|
||||
return 30;
|
||||
}
|
||||
|
@ -19,20 +19,25 @@
|
||||
#include "disk.h"
|
||||
#include "filepath.h"
|
||||
|
||||
#define DEFAULT_PRODUCT "SCSI HD"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SCSI Hard Disk
|
||||
//
|
||||
//===========================================================================
|
||||
class SCSIHD : public Disk
|
||||
class SCSIHD : public Disk, public FileSupport
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIHD(); // Constructor
|
||||
SCSIHD(bool = false); // Constructor
|
||||
void Reset(); // Reset
|
||||
void Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
void Open(const Filepath& path); // Open
|
||||
|
||||
// commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command
|
||||
BOOL ModeSelect(const DWORD *cdb, const BYTE *buf, int length); // MODE SELECT(6) command
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf) override; // INQUIRY command
|
||||
bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override; // MODE SELECT(6) command
|
||||
|
||||
// Internal processing
|
||||
int AddVendor(int page, bool change, BYTE *buf) override; // Add vendor special page
|
||||
};
|
||||
|
@ -1,91 +0,0 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ SCSI Hard Disk for Apple Macintosh ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "scsihd_apple.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SCSI hard disk (Macintosh Apple genuine)
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIHD_APPLE::SCSIHD_APPLE() : SCSIHD()
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// INQUIRY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD_APPLE::Inquiry(
|
||||
const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor)
|
||||
{
|
||||
char vendor[32];
|
||||
char product[32];
|
||||
|
||||
// Call the base class
|
||||
int size = SCSIHD::Inquiry(cdb, buf, major, minor);
|
||||
|
||||
// End if there is an error in the base class
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Vendor name
|
||||
sprintf(vendor, " SEAGATE");
|
||||
memcpy(&buf[8], vendor, strlen(vendor));
|
||||
|
||||
// Product name
|
||||
sprintf(product, " ST225N");
|
||||
memcpy(&buf[16], product, strlen(product));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Add Vendor special page
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD_APPLE::AddVendor(int page, BOOL change, BYTE *buf)
|
||||
{
|
||||
ASSERT(buf);
|
||||
|
||||
// Page code 48
|
||||
if ((page != 0x30) && (page != 0x3f)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set the message length
|
||||
buf[0] = 0x30;
|
||||
buf[1] = 0x1c;
|
||||
|
||||
// No changeable area
|
||||
if (change) {
|
||||
return 30;
|
||||
}
|
||||
|
||||
// APPLE COMPUTER, INC.
|
||||
memcpy(&buf[0xa], "APPLE COMPUTER, INC.", 20);
|
||||
|
||||
return 30;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ SCSI Hard Disk for Apple Macintosh ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "scsihd.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SCSI Hard Disk(Genuine Apple Macintosh)
|
||||
//
|
||||
//===========================================================================
|
||||
class SCSIHD_APPLE : public SCSIHD
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIHD_APPLE(); // Constructor
|
||||
// commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command
|
||||
|
||||
// Internal processing
|
||||
int AddVendor(int page, BOOL change, BYTE *buf); // Add vendor special page
|
||||
};
|
@ -31,6 +31,8 @@
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIHD_NEC::SCSIHD_NEC() : SCSIHD()
|
||||
{
|
||||
SetVendor("NEC");
|
||||
|
||||
// Work initialization
|
||||
cylinders = 0;
|
||||
heads = 0;
|
||||
@ -68,42 +70,39 @@ static inline DWORD getDwordLE(const BYTE *b)
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
|
||||
{
|
||||
Fileio fio;
|
||||
off64_t size;
|
||||
BYTE hdr[512];
|
||||
LPCTSTR ext;
|
||||
|
||||
ASSERT(!disk.ready);
|
||||
ASSERT(!IsReady());
|
||||
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw ioexception("Can't open hard disk file read-only");
|
||||
throw io_exception("Can't open hard disk file read-only");
|
||||
}
|
||||
|
||||
// Get file size
|
||||
size = fio.GetFileSize();
|
||||
off_t size = fio.GetFileSize();
|
||||
|
||||
// Read header
|
||||
if (size >= (off64_t)sizeof(hdr)) {
|
||||
BYTE hdr[512];
|
||||
if (size >= (off_t)sizeof(hdr)) {
|
||||
if (!fio.Read(hdr, sizeof(hdr))) {
|
||||
fio.Close();
|
||||
throw ioexception("Can't read hard disk file header");
|
||||
throw io_exception("Can't read NEC hard disk file header");
|
||||
}
|
||||
}
|
||||
fio.Close();
|
||||
|
||||
// Must be in 512 byte units
|
||||
if (size & 0x1ff) {
|
||||
throw ioexception("File size must be a multiple of 512 bytes");
|
||||
throw io_exception("File size must be a multiple of 512 bytes");
|
||||
}
|
||||
|
||||
// 2TB according to xm6i
|
||||
// 2TB is the current maximum
|
||||
if (size > 2LL * 1024 * 1024 * 1024 * 1024) {
|
||||
throw ioexception("File size must not exceed 2 TB");
|
||||
throw io_exception("File size must not exceed 2 TB");
|
||||
}
|
||||
|
||||
// Determine parameters by extension
|
||||
ext = path.GetFileExt();
|
||||
LPCTSTR ext = path.GetFileExt();
|
||||
if (strcasecmp(ext, _T(".HDN")) == 0) {
|
||||
// Assuming sector size 512, number of sectors 25, number of heads 8 as default settings
|
||||
imgoffset = 0;
|
||||
@ -128,34 +127,37 @@ void SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
|
||||
heads = getWordLE(&hdr[0x10 + 0x100 + 4 + 4]);
|
||||
sectors = getWordLE(&hdr[0x10 + 0x100 + 4 + 4 + 2]);
|
||||
sectorsize = getWordLE(&hdr[0x10 + 0x100 + 4 + 4 + 2 + 2]);
|
||||
imgsize = (off64_t)cylinders * heads * sectors * sectorsize;
|
||||
imgsize = (off_t)cylinders * heads * sectors * sectorsize;
|
||||
}
|
||||
|
||||
// Supports 256 or 512 sector sizes
|
||||
if (sectorsize != 256 && sectorsize != 512) {
|
||||
throw ioexception("Sector size must be 256 or 512 bytes");
|
||||
throw io_exception("Sector size must be 256 or 512 bytes");
|
||||
}
|
||||
|
||||
// Image size consistency check
|
||||
if (imgoffset + imgsize > size || (imgsize % sectorsize != 0)) {
|
||||
throw ioexception("Image size consistency check failed");
|
||||
throw io_exception("Image size consistency check failed");
|
||||
}
|
||||
|
||||
// Sector size
|
||||
// TODO Do not use disk.size directly
|
||||
for(disk.size = 16; disk.size > 0; --(disk.size)) {
|
||||
if ((1 << disk.size) == sectorsize)
|
||||
break;
|
||||
}
|
||||
if (disk.size <= 0 || disk.size > 16) {
|
||||
throw ioexception("Invalid disk size");
|
||||
throw io_exception("Invalid disk size");
|
||||
}
|
||||
|
||||
// Number of blocks
|
||||
disk.blocks = (DWORD)(imgsize >> disk.size);
|
||||
SetBlockCount((DWORD)(imgsize >> disk.size));
|
||||
disk.imgoffset = imgoffset;
|
||||
|
||||
// Call the base class
|
||||
LOGINFO("Media capacity for image file '%s': %d blocks", path.GetPath(), GetBlockCount());
|
||||
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -163,28 +165,14 @@ void SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
|
||||
// INQUIRY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD_NEC::Inquiry(
|
||||
const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor)
|
||||
int SCSIHD_NEC::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
{
|
||||
int size;
|
||||
int size = SCSIHD::Inquiry(cdb, buf);
|
||||
|
||||
// Base class
|
||||
size = SCSIHD::Inquiry(cdb, buf, major, minor);
|
||||
|
||||
// Exit if there is an error in the base class
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Changed to equivalent to SCSI-1
|
||||
// This drive is a SCSI-1 SCCS drive
|
||||
buf[2] = 0x01;
|
||||
buf[3] = 0x01;
|
||||
|
||||
// Replace Vendor name
|
||||
buf[8] = 'N';
|
||||
buf[9] = 'E';
|
||||
buf[10] = 'C';
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -193,7 +181,7 @@ int SCSIHD_NEC::Inquiry(
|
||||
// Error page added
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD_NEC::AddError(BOOL change, BYTE *buf)
|
||||
int SCSIHD_NEC::AddError(bool change, BYTE *buf)
|
||||
{
|
||||
ASSERT(buf);
|
||||
|
||||
@ -201,11 +189,6 @@ int SCSIHD_NEC::AddError(BOOL change, BYTE *buf)
|
||||
buf[0] = 0x01;
|
||||
buf[1] = 0x06;
|
||||
|
||||
// No changeable area
|
||||
if (change) {
|
||||
return 8;
|
||||
}
|
||||
|
||||
// The retry count is 0, and the limit time uses the default value inside the device.
|
||||
return 8;
|
||||
}
|
||||
@ -215,7 +198,7 @@ int SCSIHD_NEC::AddError(BOOL change, BYTE *buf)
|
||||
// Format page added
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD_NEC::AddFormat(BOOL change, BYTE *buf)
|
||||
int SCSIHD_NEC::AddFormat(bool change, BYTE *buf)
|
||||
{
|
||||
ASSERT(buf);
|
||||
|
||||
@ -230,7 +213,7 @@ int SCSIHD_NEC::AddFormat(BOOL change, BYTE *buf)
|
||||
return 24;
|
||||
}
|
||||
|
||||
if (disk.ready) {
|
||||
if (IsReady()) {
|
||||
// Set the number of tracks in one zone (PC-9801-55 seems to see this value)
|
||||
buf[0x2] = (BYTE)(heads >> 8);
|
||||
buf[0x3] = (BYTE)heads;
|
||||
@ -246,7 +229,7 @@ int SCSIHD_NEC::AddFormat(BOOL change, BYTE *buf)
|
||||
}
|
||||
|
||||
// Set removable attributes (remains of the old days)
|
||||
if (disk.removable) {
|
||||
if (IsRemovable()) {
|
||||
buf[20] = 0x20;
|
||||
}
|
||||
|
||||
@ -258,7 +241,7 @@ int SCSIHD_NEC::AddFormat(BOOL change, BYTE *buf)
|
||||
// Drive page added
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD_NEC::AddDrive(BOOL change, BYTE *buf)
|
||||
int SCSIHD_NEC::AddDrive(bool change, BYTE *buf)
|
||||
{
|
||||
ASSERT(buf);
|
||||
|
||||
@ -267,11 +250,7 @@ int SCSIHD_NEC::AddDrive(BOOL change, BYTE *buf)
|
||||
buf[1] = 0x12;
|
||||
|
||||
// No changeable area
|
||||
if (change) {
|
||||
return 20;
|
||||
}
|
||||
|
||||
if (disk.ready) {
|
||||
if (!change && IsReady()) {
|
||||
// Set the number of cylinders
|
||||
buf[0x2] = (BYTE)(cylinders >> 16);
|
||||
buf[0x3] = (BYTE)(cylinders >> 8);
|
||||
|
@ -26,22 +26,22 @@ class SCSIHD_NEC : public SCSIHD
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIHD_NEC(); // Constructor
|
||||
SCSIHD_NEC(); // Constructor
|
||||
void Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
|
||||
// commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf) override; // INQUIRY command
|
||||
|
||||
// Internal processing
|
||||
int AddError(BOOL change, BYTE *buf); // Add error
|
||||
int AddFormat(BOOL change, BYTE *buf); // Add format
|
||||
int AddDrive(BOOL change, BYTE *buf); // Add drive
|
||||
int AddError(bool change, BYTE *buf) override; // Add error
|
||||
int AddFormat(bool change, BYTE *buf) override; // Add format
|
||||
int AddDrive(bool change, BYTE *buf) override; // Add drive
|
||||
|
||||
private:
|
||||
int cylinders; // Number of cylinders
|
||||
int heads; // Number of heads
|
||||
int sectors; // Number of sectors
|
||||
int sectorsize; // Sector size
|
||||
off64_t imgoffset; // Image offset
|
||||
off64_t imgsize; // Image size
|
||||
off_t imgoffset; // Image offset
|
||||
off_t imgsize; // Image size
|
||||
};
|
||||
|
@ -32,9 +32,10 @@
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIMO::SCSIMO() : Disk("SCMO")
|
||||
{
|
||||
disk.removable = true;
|
||||
disk.lockable = true;
|
||||
disk.protectable = true;
|
||||
SetRemovable(true);
|
||||
SetProtectable(true);
|
||||
|
||||
SetProduct("M2513A");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -42,56 +43,61 @@ SCSIMO::SCSIMO() : Disk("SCMO")
|
||||
// Open
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIMO::Open(const Filepath& path, BOOL attn)
|
||||
void SCSIMO::Open(const Filepath& path)
|
||||
{
|
||||
ASSERT(!disk.ready);
|
||||
ASSERT(!IsReady());
|
||||
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw ioexception("Can't open MO file read-only");
|
||||
throw io_exception("Can't open MO file read-only");
|
||||
}
|
||||
|
||||
// Get file size
|
||||
off64_t size = fio.GetFileSize();
|
||||
off_t size = fio.GetFileSize();
|
||||
fio.Close();
|
||||
|
||||
switch (size) {
|
||||
// 128MB
|
||||
case 0x797f400:
|
||||
disk.size = 9;
|
||||
disk.blocks = 248826;
|
||||
// 512 bytes per sector
|
||||
SetSectorSize(9);
|
||||
SetBlockCount(248826);
|
||||
break;
|
||||
|
||||
// 230MB
|
||||
case 0xd9eea00:
|
||||
disk.size = 9;
|
||||
disk.blocks = 446325;
|
||||
// 512 bytes per sector
|
||||
SetSectorSize(9);
|
||||
SetBlockCount(446325);
|
||||
break;
|
||||
|
||||
// 540MB
|
||||
case 0x1fc8b800:
|
||||
disk.size = 9;
|
||||
disk.blocks = 1041500;
|
||||
// 512 bytes per sector
|
||||
SetSectorSize(9);
|
||||
SetBlockCount(1041500);
|
||||
break;
|
||||
|
||||
// 640MB
|
||||
case 0x25e28000:
|
||||
disk.size = 11;
|
||||
disk.blocks = 310352;
|
||||
// 2048 bytes per sector
|
||||
SetSectorSize(11);
|
||||
SetBlockCount(310352);
|
||||
break;
|
||||
|
||||
// Other (this is an error)
|
||||
default:
|
||||
throw ioexception("Invalid MO file size");
|
||||
throw io_exception("Invalid MO file size, supported sizes are 127398912 bytes (128 MB), "
|
||||
"228518400 bytes (230 MB), 533248000 bytes (540 MB), 635600896 bytes (640 MB)");
|
||||
}
|
||||
|
||||
// Call the base class
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
|
||||
// Attention if ready
|
||||
if (disk.ready && attn) {
|
||||
disk.attn = TRUE;
|
||||
if (IsReady()) {
|
||||
SetAttn(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,19 +106,14 @@ void SCSIMO::Open(const Filepath& path, BOOL attn)
|
||||
// INQUIRY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIMO::Inquiry(
|
||||
const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor)
|
||||
int SCSIMO::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
{
|
||||
int size;
|
||||
char rev[32];
|
||||
|
||||
ASSERT(cdb);
|
||||
ASSERT(buf);
|
||||
ASSERT(cdb[0] == 0x12);
|
||||
|
||||
// EVPD check
|
||||
if (cdb[1] & 0x01) {
|
||||
disk.code = DISK_INVALIDCDB;
|
||||
SetStatusCode(STATUS_INVALIDCDB);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -126,7 +127,7 @@ int SCSIMO::Inquiry(
|
||||
buf[0] = 0x07;
|
||||
|
||||
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
|
||||
if (((cdb[1] >> 5) & 0x07) != disk.lun) {
|
||||
if (((cdb[1] >> 5) & 0x07) != GetLun()) {
|
||||
buf[0] = 0x7f;
|
||||
}
|
||||
|
||||
@ -135,30 +136,17 @@ int SCSIMO::Inquiry(
|
||||
buf[3] = 0x02;
|
||||
buf[4] = 36 - 5; // required
|
||||
|
||||
// Fill with blanks
|
||||
memset(&buf[8], 0x20, buf[4] - 3);
|
||||
|
||||
// Vendor name
|
||||
memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE));
|
||||
|
||||
// Product name
|
||||
memcpy(&buf[16], "M2513A", 6);
|
||||
|
||||
// Revision (XM6 version number)
|
||||
sprintf(rev, "0%01d%01d%01d",
|
||||
(int)major, (int)(minor >> 4), (int)(minor & 0x0f));
|
||||
memcpy(&buf[32], rev, 4);
|
||||
// Padded vendor, product, revision
|
||||
memcpy(&buf[8], GetPaddedName().c_str(), 28);
|
||||
|
||||
// Size return data
|
||||
size = (buf[4] + 5);
|
||||
int size = (buf[4] + 5);
|
||||
|
||||
// Limit the size if the buffer is too small
|
||||
if (size > (int)cdb[4]) {
|
||||
size = (int)cdb[4];
|
||||
}
|
||||
|
||||
// Success
|
||||
disk.code = DISK_NOERROR;
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -168,7 +156,7 @@ int SCSIMO::Inquiry(
|
||||
// *Not affected by disk.code
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
bool SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
{
|
||||
int page;
|
||||
int size;
|
||||
@ -181,12 +169,12 @@ BOOL SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
// Mode Parameter header
|
||||
if (length >= 12) {
|
||||
// Check the block length (in bytes)
|
||||
size = 1 << disk.size;
|
||||
size = 1 << GetSectorSize();
|
||||
if (buf[9] != (BYTE)(size >> 16) ||
|
||||
buf[10] != (BYTE)(size >> 8) || buf[11] != (BYTE)size) {
|
||||
// Currently does not allow changing sector length
|
||||
disk.code = DISK_INVALIDPRM;
|
||||
return FALSE;
|
||||
SetStatusCode(STATUS_INVALIDPRM);
|
||||
return false;
|
||||
}
|
||||
buf += 12;
|
||||
length -= 12;
|
||||
@ -201,12 +189,12 @@ BOOL SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
// format device
|
||||
case 0x03:
|
||||
// Check the number of bytes in the physical sector
|
||||
size = 1 << disk.size;
|
||||
size = 1 << GetSectorSize();
|
||||
if (buf[0xc] != (BYTE)(size >> 8) ||
|
||||
buf[0xd] != (BYTE)size) {
|
||||
// Currently does not allow changing sector length
|
||||
disk.code = DISK_INVALIDPRM;
|
||||
return FALSE;
|
||||
SetStatusCode(STATUS_INVALIDPRM);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
// vendor unique format
|
||||
@ -227,9 +215,7 @@ BOOL SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
}
|
||||
|
||||
// Do not generate an error for the time being (MINIX)
|
||||
disk.code = DISK_NOERROR;
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -278,11 +264,12 @@ int SCSIMO::AddVendor(int page, BOOL change, BYTE *buf)
|
||||
further information: http://r2089.blog36.fc2.com/blog-entry-177.html
|
||||
*/
|
||||
|
||||
if (disk.ready) {
|
||||
if (IsReady()) {
|
||||
unsigned spare = 0;
|
||||
unsigned bands = 0;
|
||||
DWORD blocks = GetBlockCount();
|
||||
|
||||
if (disk.size == 9) switch (disk.blocks) {
|
||||
if (GetSectorSize() == 9) switch (blocks) {
|
||||
// 128MB
|
||||
case 248826:
|
||||
spare = 1024;
|
||||
@ -302,7 +289,7 @@ int SCSIMO::AddVendor(int page, BOOL change, BYTE *buf)
|
||||
break;
|
||||
}
|
||||
|
||||
if (disk.size == 11) switch (disk.blocks) {
|
||||
if (GetSectorSize() == 11) switch (blocks) {
|
||||
// 640MB
|
||||
case 310352:
|
||||
spare = 2244;
|
||||
@ -318,10 +305,10 @@ int SCSIMO::AddVendor(int page, BOOL change, BYTE *buf)
|
||||
|
||||
buf[2] = 0; // format mode
|
||||
buf[3] = 0; // type of format
|
||||
buf[4] = (BYTE)(disk.blocks >> 24);
|
||||
buf[5] = (BYTE)(disk.blocks >> 16);
|
||||
buf[6] = (BYTE)(disk.blocks >> 8);
|
||||
buf[7] = (BYTE)disk.blocks;
|
||||
buf[4] = (BYTE)(blocks >> 24);
|
||||
buf[5] = (BYTE)(blocks >> 16);
|
||||
buf[6] = (BYTE)(blocks >> 8);
|
||||
buf[7] = (BYTE)blocks;
|
||||
buf[8] = (BYTE)(spare >> 8);
|
||||
buf[9] = (BYTE)spare;
|
||||
buf[10] = (BYTE)(bands >> 8);
|
||||
|
@ -24,16 +24,16 @@
|
||||
// SCSI magneto-optical disk
|
||||
//
|
||||
//===========================================================================
|
||||
class SCSIMO : public Disk
|
||||
class SCSIMO : public Disk, public FileSupport
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIMO(); // Constructor
|
||||
void Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
void Open(const Filepath& path); // Open
|
||||
|
||||
// commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command
|
||||
BOOL ModeSelect(const DWORD *cdb, const BYTE *buf, int length); // MODE SELECT(6) command
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf) override; // INQUIRY command
|
||||
bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override; // MODE SELECT(6) command
|
||||
|
||||
// Internal processing
|
||||
int AddVendor(int page, BOOL change, BYTE *buf); // Add vendor special page
|
||||
|
@ -1,48 +1,56 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
// Copyright (C) 2016-2020 GIMONS
|
||||
// [ Exceptions ]
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
// Various exceptions
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#if !defined(exceptions_h)
|
||||
#define exceptions_h
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class lunexception final : public exception {
|
||||
private:
|
||||
int lun;
|
||||
|
||||
public:
|
||||
lunexception(int _lun) : lun(_lun) { }
|
||||
|
||||
~lunexception() { }
|
||||
|
||||
int getlun() const {
|
||||
return lun;
|
||||
}
|
||||
};
|
||||
|
||||
class ioexception : public exception {
|
||||
class illegal_argument_exception final : public exception {
|
||||
private:
|
||||
string msg;
|
||||
|
||||
public:
|
||||
ioexception(const string& _msg) : msg(_msg) { }
|
||||
|
||||
~ioexception() { }
|
||||
illegal_argument_exception(const string& _msg) : msg(_msg) {}
|
||||
illegal_argument_exception() {};
|
||||
|
||||
const string& getmsg() const {
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
class lun_exception final : public exception {
|
||||
private:
|
||||
int lun;
|
||||
|
||||
public:
|
||||
lun_exception(int _lun) : lun(_lun) {}
|
||||
~lun_exception() {}
|
||||
|
||||
int getlun() const {
|
||||
return lun;
|
||||
}
|
||||
};
|
||||
|
||||
class io_exception : public exception {
|
||||
private:
|
||||
string msg;
|
||||
|
||||
public:
|
||||
io_exception(const string& _msg) : msg(_msg) {}
|
||||
~io_exception() {}
|
||||
|
||||
const string& getmsg() const {
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
@ -141,11 +141,6 @@ BOOL Fileio::Open(LPCTSTR fname, OpenMode mode, BOOL directIO)
|
||||
handle = open(fname, O_RDWR | omode);
|
||||
break;
|
||||
|
||||
// アペンド
|
||||
case Append:
|
||||
handle = open(fname, O_CREAT | O_WRONLY | O_APPEND | omode, 0666);
|
||||
break;
|
||||
|
||||
// それ以外
|
||||
default:
|
||||
ASSERT(FALSE);
|
||||
@ -260,7 +255,7 @@ BOOL Fileio::Write(const void *buffer, int size)
|
||||
// シーク
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Fileio::Seek(off64_t offset, BOOL relative)
|
||||
BOOL Fileio::Seek(off_t offset, BOOL relative)
|
||||
{
|
||||
ASSERT(handle >= 0);
|
||||
ASSERT(offset >= 0);
|
||||
@ -282,10 +277,10 @@ BOOL Fileio::Seek(off64_t offset, BOOL relative)
|
||||
// ファイルサイズ取得
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
off64_t Fileio::GetFileSize()
|
||||
off_t Fileio::GetFileSize()
|
||||
{
|
||||
off64_t cur;
|
||||
off64_t end;
|
||||
off_t cur;
|
||||
off_t end;
|
||||
|
||||
ASSERT(handle >= 0);
|
||||
|
||||
@ -306,9 +301,9 @@ off64_t Fileio::GetFileSize()
|
||||
// ファイル位置取得
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
off64_t Fileio::GetFilePos() const
|
||||
off_t Fileio::GetFilePos() const
|
||||
{
|
||||
off64_t pos;
|
||||
off_t pos;
|
||||
|
||||
ASSERT(handle >= 0);
|
||||
|
||||
|
@ -39,8 +39,7 @@ public:
|
||||
enum OpenMode {
|
||||
ReadOnly, // 読み込みのみ
|
||||
WriteOnly, // 書き込みのみ
|
||||
ReadWrite, // 読み書き両方
|
||||
Append // アペンド
|
||||
ReadWrite // 読み書き両方
|
||||
};
|
||||
|
||||
public:
|
||||
@ -61,15 +60,15 @@ public:
|
||||
// オープン
|
||||
BOOL OpenDIO(const Filepath& path, OpenMode mode);
|
||||
// オープン
|
||||
BOOL Seek(off64_t offset, BOOL relative = FALSE);
|
||||
BOOL Seek(off_t offset, BOOL relative = FALSE);
|
||||
// シーク
|
||||
BOOL Read(void *buffer, int size);
|
||||
// 読み込み
|
||||
BOOL Write(const void *buffer, int size);
|
||||
// 書き込み
|
||||
off64_t GetFileSize();
|
||||
off_t GetFileSize();
|
||||
// ファイルサイズ取得
|
||||
off64_t GetFilePos() const;
|
||||
off_t GetFilePos() const;
|
||||
// ファイル位置取得
|
||||
void Close();
|
||||
// クローズ
|
||||
|
@ -487,7 +487,7 @@ void GPIOBUS::SetENB(BOOL ast)
|
||||
// Get BSY signal
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL GPIOBUS::GetBSY()
|
||||
bool GPIOBUS::GetBSY()
|
||||
{
|
||||
return GetSignal(PIN_BSY);
|
||||
}
|
||||
@ -497,7 +497,7 @@ BOOL GPIOBUS::GetBSY()
|
||||
// Set BSY signal
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void GPIOBUS::SetBSY(BOOL ast)
|
||||
void GPIOBUS::SetBSY(bool ast)
|
||||
{
|
||||
// Set BSY signal
|
||||
SetSignal(PIN_BSY, ast);
|
||||
@ -898,12 +898,7 @@ int GPIOBUS::CommandHandShake(BYTE *buf)
|
||||
goto irq_enable_exit;
|
||||
}
|
||||
|
||||
// Distinguise whether the command is 6 bytes or 10 bytes
|
||||
if (*buf >= 0x20 && *buf <= 0x7D) {
|
||||
count = 10;
|
||||
} else {
|
||||
count = 6;
|
||||
}
|
||||
count = GetCommandByteCount(*buf);
|
||||
|
||||
// Increment buffer pointer
|
||||
buf++;
|
||||
@ -1609,6 +1604,26 @@ BUS::phase_t GPIOBUS::GetPhaseRaw(DWORD raw_data)
|
||||
return GetPhase(mci);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Get the number of bytes for a command
|
||||
//
|
||||
// TODO The command length should be determined based on the bytes transferred in the COMMAND phase
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int GPIOBUS::GetCommandByteCount(BYTE opcode) {
|
||||
if (opcode == 0x88 || opcode == 0x8A || opcode == 0x8F || opcode == 0x9E) {
|
||||
return 16;
|
||||
}
|
||||
else if (opcode == 0xA0) {
|
||||
return 12;
|
||||
}
|
||||
else if (opcode >= 0x20 && opcode <= 0x7D) {
|
||||
return 10;
|
||||
} else {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
@ -509,9 +509,9 @@ public:
|
||||
void SetENB(BOOL ast);
|
||||
// Set ENB signal
|
||||
|
||||
BOOL GetBSY();
|
||||
bool GetBSY();
|
||||
// Get BSY signal
|
||||
void SetBSY(BOOL ast);
|
||||
void SetBSY(bool ast);
|
||||
// Set BSY signal
|
||||
|
||||
BOOL GetSEL();
|
||||
@ -575,7 +575,9 @@ public:
|
||||
static BUS::phase_t GetPhaseRaw(DWORD raw_data);
|
||||
// Get the phase based on raw data
|
||||
|
||||
#ifdef USE_SEL_EVENT_ENABLE
|
||||
static int GetCommandByteCount(BYTE opcode);
|
||||
|
||||
#ifdef USE_SEL_EVENT_ENABLE
|
||||
// SEL signal interrupt
|
||||
int PollSelectEvent();
|
||||
// SEL signal event polling
|
||||
|
@ -23,18 +23,11 @@
|
||||
spdlog::log(loglevel, logbuf); \
|
||||
};
|
||||
|
||||
#ifdef NDEBUG
|
||||
// If we're doing a non-debug build, we want to skip the overhead of
|
||||
// formatting the string, then calling the logger
|
||||
#define LOGTRACE(...) ((void)0)
|
||||
#define LOGDEBUG(...) ((void)0)
|
||||
#else
|
||||
#define LOGTRACE(...) SPDLOGWRAPPER(spdlog::level::trace, __VA_ARGS__)
|
||||
#define LOGDEBUG(...) SPDLOGWRAPPER(spdlog::level::debug, __VA_ARGS__)
|
||||
#endif
|
||||
#define LOGINFO(...) SPDLOGWRAPPER(spdlog::level::info, __VA_ARGS__)
|
||||
#define LOGWARN(...) SPDLOGWRAPPER(spdlog::level::warn, __VA_ARGS__)
|
||||
#define LOGERROR(...) SPDLOGWRAPPER(spdlog::level::err, __VA_ARGS__)
|
||||
#define LOGCRITICAL(...) SPDLOGWRAPPER(spdlog::level::critical, __VA_ARGS__)
|
||||
|
||||
#endif // log_h
|
||||
#endif
|
||||
|
@ -55,6 +55,7 @@
|
||||
|
||||
#include <poll.h>
|
||||
#include <dirent.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/epoll.h>
|
||||
@ -102,9 +103,9 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long QWORD;
|
||||
typedef uint16_t WORD;
|
||||
typedef uint32_t DWORD;
|
||||
typedef uint64_t QWORD;
|
||||
typedef int BOOL;
|
||||
typedef char TCHAR;
|
||||
typedef char *LPTSTR;
|
||||
@ -128,6 +129,4 @@ typedef const char *LPCSTR;
|
||||
#define _MAX_FNAME 256
|
||||
#define _MAX_EXT 256
|
||||
|
||||
#define off64_t off_t
|
||||
|
||||
#endif // os_h
|
||||
|
80
src/raspberrypi/protobuf_util.cpp
Normal file
80
src/raspberrypi/protobuf_util.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include "rascsi_interface.pb.h"
|
||||
#include "exceptions.h"
|
||||
#include "protobuf_util.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace rascsi_interface;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Serialize/Deserialize protobuf message: Length followed by the actual data.
|
||||
// Little endian is assumed.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void SerializeMessage(int fd, const google::protobuf::MessageLite& message)
|
||||
{
|
||||
string data;
|
||||
message.SerializeToString(&data);
|
||||
|
||||
// Write the size of the protobuf data as a header
|
||||
int32_t size = data.length();
|
||||
if (write(fd, &size, sizeof(size)) != sizeof(size)) {
|
||||
throw io_exception("Can't write protobuf header");
|
||||
}
|
||||
|
||||
// Write the actual protobuf data
|
||||
uint8_t buf[size];
|
||||
memcpy(buf, data.data(), size);
|
||||
if (write(fd, buf, size) != size) {
|
||||
throw io_exception("Can't write protobuf data");
|
||||
}
|
||||
}
|
||||
|
||||
void DeserializeMessage(int fd, google::protobuf::MessageLite& message)
|
||||
{
|
||||
// Read the header with the size of the protobuf data
|
||||
uint8_t header_buf[4];
|
||||
int bytes_read = ReadNBytes(fd, header_buf, 4);
|
||||
if (bytes_read < 4) {
|
||||
return;
|
||||
}
|
||||
int32_t size = (header_buf[3] << 24) + (header_buf[2] << 16) + (header_buf[1] << 8) + header_buf[0];
|
||||
|
||||
// Read the binary protobuf data
|
||||
uint8_t data_buf[size];
|
||||
bytes_read = ReadNBytes(fd, data_buf, size);
|
||||
if (bytes_read < size) {
|
||||
throw io_exception("Missing protobuf data");
|
||||
}
|
||||
|
||||
// Create protobuf message
|
||||
string data((const char *)data_buf, size);
|
||||
message.ParseFromString(data);
|
||||
}
|
||||
|
||||
int ReadNBytes(int fd, uint8_t *buf, int n)
|
||||
{
|
||||
int offset = 0;
|
||||
while (offset < n) {
|
||||
ssize_t len = read(fd, buf + offset, n - offset);
|
||||
if (!len) {
|
||||
break;
|
||||
}
|
||||
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
19
src/raspberrypi/protobuf_util.h
Normal file
19
src/raspberrypi/protobuf_util.h
Normal file
@ -0,0 +1,19 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
// Helper methods for serializing/deserializing protobuf messages
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "google/protobuf/message_lite.h"
|
||||
#include "rascsi_interface.pb.h"
|
||||
|
||||
void SerializeMessage(int, const google::protobuf::MessageLite&);
|
||||
void DeserializeMessage(int, google::protobuf::MessageLite&);
|
||||
int ReadNBytes(int, uint8_t *, int);
|
File diff suppressed because it is too large
Load Diff
@ -1,72 +1,124 @@
|
||||
//
|
||||
// Each rascsi remote interface message is preceded by a little endian 32 bit header,
|
||||
// which contains the protobuf message size.
|
||||
//
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package rascsi_interface;
|
||||
|
||||
// The supported device types
|
||||
// The available device types
|
||||
enum PbDeviceType {
|
||||
UNDEFINED = 0;
|
||||
SASI_HD = 1;
|
||||
SCSI_HD = 2;
|
||||
MO = 3;
|
||||
CD = 4;
|
||||
BR = 5;
|
||||
NUVOLINK = 6;
|
||||
DAYNAPORT = 7;
|
||||
// Non-removable SASI drive
|
||||
SAHD = 1;
|
||||
// Non-removable SCSI drive
|
||||
SCHD = 2;
|
||||
// Removable SCSI drive
|
||||
SCRM = 3;
|
||||
// Magnoto-Optical drive
|
||||
SCMO = 4;
|
||||
// CD-ROM drive
|
||||
SCCD = 5;
|
||||
// Network bridge
|
||||
SCBR = 6;
|
||||
// DaynaPort network adapter
|
||||
SCDP = 7;
|
||||
}
|
||||
|
||||
// rascsi remote operations
|
||||
enum PbOperation {
|
||||
NONE = 0;
|
||||
// Returns the full server information
|
||||
// Returns the server information
|
||||
SERVER_INFO = 1;
|
||||
// Returns the device list only (required for rasctl/logging semantics compatibility)
|
||||
LIST = 2 [deprecated = true];
|
||||
// Set the default folder for image files, PbCommand.params contains the folder name
|
||||
DEFAULT_FOLDER = 2;
|
||||
// Set server log level, PbCommand.params contains the log level
|
||||
LOG_LEVEL = 3;
|
||||
// Attach new device
|
||||
ATTACH = 4;
|
||||
// Detach device. Detach all devices if PbCommand.params == "all". In this case ID and unit are ignored.
|
||||
DETACH = 5;
|
||||
// Insert media
|
||||
INSERT = 6;
|
||||
// Eject media
|
||||
EJECT = 7;
|
||||
// Write-protect media (not possible for read-only media)
|
||||
PROTECT = 8;
|
||||
// Make media writable (not possible for read-only media)
|
||||
UNPROTECT = 9;
|
||||
}
|
||||
|
||||
// Commands rascsi can execute
|
||||
message PbCommand {
|
||||
PbOperation cmd = 1;
|
||||
int32 id = 2;
|
||||
int32 un = 3;
|
||||
PbDeviceType type = 4;
|
||||
string params = 5;
|
||||
// The image file data
|
||||
message PbImageFile {
|
||||
string name = 1;
|
||||
bool read_only = 2;
|
||||
int64 size = 3;
|
||||
}
|
||||
|
||||
// The result of a command
|
||||
message PbResult {
|
||||
bool status = 1;
|
||||
string msg = 2;
|
||||
}
|
||||
|
||||
// The device meta data
|
||||
message PbDevice {
|
||||
// The device definition, sent from the client to the server
|
||||
message PbDeviceDefinition {
|
||||
int32 id = 1;
|
||||
int32 un = 2;
|
||||
int32 unit = 2;
|
||||
PbDeviceType type = 3;
|
||||
string file = 4;
|
||||
bool read_only = 5;
|
||||
// Note: Read-only media (e.g. CD-ROMs) are not protectable
|
||||
bool protectable = 6;
|
||||
// Note: Read-only media (e.g. CD-ROMs) are not protected but just read-only
|
||||
bool protected = 7;
|
||||
bool removable = 8;
|
||||
bool removed = 9;
|
||||
bool lockable = 10;
|
||||
bool locked = 11;
|
||||
bool supports_file = 12;
|
||||
// The device name, format is VENDOR:PRODUCT:REVISION
|
||||
string name = 5;
|
||||
bool protected = 6;
|
||||
}
|
||||
|
||||
message PbDeviceDefinitions {
|
||||
repeated PbDeviceDefinition devices = 1;
|
||||
}
|
||||
|
||||
// The device data, sent from the server to the client
|
||||
message PbDevice {
|
||||
int32 id = 1;
|
||||
int32 unit = 2;
|
||||
PbDeviceType type = 3;
|
||||
PbImageFile file = 4;
|
||||
string vendor = 5;
|
||||
string product = 6;
|
||||
string revision = 7;
|
||||
// Read-only media (e.g. CD-ROMs) are not protectable but read-only all the time
|
||||
bool read_only = 8;
|
||||
// Media can be write-protected
|
||||
bool protectable = 9;
|
||||
// Media is write-protected
|
||||
bool protected = 10;
|
||||
// Media can be removed
|
||||
bool removable = 11;
|
||||
// Media is removed
|
||||
bool removed = 12;
|
||||
// Media can be locked
|
||||
bool lockable = 13;
|
||||
// Media is locked
|
||||
bool locked = 14;
|
||||
// Device supports image file
|
||||
bool supports_file = 15;
|
||||
}
|
||||
|
||||
message PbDevices {
|
||||
repeated PbDevice devices = 1;
|
||||
}
|
||||
|
||||
// Commands rascsi can execute and their parameters
|
||||
message PbCommand {
|
||||
PbOperation cmd = 1;
|
||||
// The optional device list
|
||||
PbDeviceDefinitions devices = 2;
|
||||
// The optional parameters, depending on the operation
|
||||
string params = 3;
|
||||
}
|
||||
|
||||
// The result of a command
|
||||
message PbResult {
|
||||
// false means that an error occurred
|
||||
bool status = 1;
|
||||
// The (error) message
|
||||
string msg = 2;
|
||||
}
|
||||
|
||||
// The rascsi server information
|
||||
message PbServerInfo {
|
||||
string rascsi_version = 1;
|
||||
@ -75,7 +127,7 @@ message PbServerInfo {
|
||||
string current_log_level = 3;
|
||||
string default_image_folder = 4;
|
||||
// Files in the default folder
|
||||
repeated string available_image_files = 5;
|
||||
repeated PbImageFile available_image_files = 5;
|
||||
// The attached devices
|
||||
PbDevices devices = 6;
|
||||
}
|
@ -24,7 +24,7 @@ static char rascsi_version_string[30]; // Allow for string up to "XX.XX.XXX" + n
|
||||
// Get the RaSCSI version string
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
char* rascsi_get_version_string()
|
||||
const char* rascsi_get_version_string()
|
||||
{
|
||||
if(rascsi_patch_version < 0)
|
||||
{
|
||||
@ -42,3 +42,5 @@ char* rascsi_get_version_string()
|
||||
}
|
||||
return rascsi_version_string;
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,4 +18,4 @@ extern const int rascsi_patch_version; // Patch number
|
||||
// Fetch the version string
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
extern char* rascsi_get_version_string();
|
||||
const char* rascsi_get_version_string();
|
||||
|
@ -10,10 +10,10 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <netdb.h>
|
||||
#include "google/protobuf/message_lite.h"
|
||||
#include "os.h"
|
||||
#include "rascsi_version.h"
|
||||
#include "exceptions.h"
|
||||
#include "protobuf_util.h"
|
||||
#include "rasutil.h"
|
||||
#include "rascsi_interface.pb.h"
|
||||
#include <sstream>
|
||||
@ -35,12 +35,12 @@ int SendCommand(const string& hostname, int port, const PbCommand& command)
|
||||
try {
|
||||
struct hostent *host = gethostbyname(hostname.c_str());
|
||||
if (!host) {
|
||||
throw ioexception("Can't resolve hostname '" + hostname + "'");
|
||||
throw io_exception("Can't resolve hostname '" + hostname + "'");
|
||||
}
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
throw ioexception("Can't create socket");
|
||||
throw io_exception("Can't create socket");
|
||||
}
|
||||
|
||||
struct sockaddr_in server;
|
||||
@ -53,19 +53,19 @@ int SendCommand(const string& hostname, int port, const PbCommand& command)
|
||||
if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
|
||||
ostringstream error;
|
||||
error << "Can't connect to rascsi process on host '" << hostname << "', port " << port;
|
||||
throw ioexception(error.str());
|
||||
throw io_exception(error.str());
|
||||
}
|
||||
|
||||
SerializeMessage(fd, command);
|
||||
}
|
||||
catch(const ioexception& e) {
|
||||
catch(const io_exception& e) {
|
||||
cerr << "Error: " << e.getmsg() << endl;
|
||||
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
exit(fd < 0 ? ENOTCONN : -1);
|
||||
exit(fd < 0 ? ENOTCONN : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return fd;
|
||||
@ -84,12 +84,14 @@ bool ReceiveResult(int fd)
|
||||
close(fd);
|
||||
|
||||
if (!result.status()) {
|
||||
throw ioexception(result.msg());
|
||||
throw io_exception(result.msg());
|
||||
}
|
||||
|
||||
cout << result.msg() << endl;
|
||||
if (!result.msg().empty()) {
|
||||
cout << result.msg() << endl;
|
||||
}
|
||||
}
|
||||
catch(const ioexception& e) {
|
||||
catch(const io_exception& e) {
|
||||
cerr << "Error: " << e.getmsg() << endl;
|
||||
|
||||
return false;
|
||||
@ -107,25 +109,25 @@ bool ReceiveResult(int fd)
|
||||
void CommandList(const string& hostname, int port)
|
||||
{
|
||||
PbCommand command;
|
||||
command.set_cmd(LIST);
|
||||
command.set_cmd(SERVER_INFO);
|
||||
|
||||
int fd = SendCommand(hostname.c_str(), port, command);
|
||||
|
||||
PbDevices devices;
|
||||
PbServerInfo serverInfo;
|
||||
try {
|
||||
DeserializeMessage(fd, devices);
|
||||
DeserializeMessage(fd, serverInfo);
|
||||
}
|
||||
catch(const ioexception& e) {
|
||||
catch(const io_exception& e) {
|
||||
cerr << "Error: " << e.getmsg() << endl;
|
||||
|
||||
close(fd);
|
||||
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
close (fd);
|
||||
close(fd);
|
||||
|
||||
cout << ListDevices(devices) << endl;
|
||||
cout << ListDevices(serverInfo.devices()) << endl;
|
||||
}
|
||||
|
||||
void CommandLogLevel(const string& hostname, int port, const string& log_level)
|
||||
@ -139,6 +141,17 @@ void CommandLogLevel(const string& hostname, int port, const string& log_level)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void CommandDefaultImageFolder(const string& hostname, int port, const string& folder)
|
||||
{
|
||||
PbCommand command;
|
||||
command.set_cmd(DEFAULT_FOLDER);
|
||||
command.set_params(folder);
|
||||
|
||||
int fd = SendCommand(hostname.c_str(), port, command);
|
||||
ReceiveResult(fd);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void CommandServerInfo(const string& hostname, int port)
|
||||
{
|
||||
PbCommand command;
|
||||
@ -150,12 +163,12 @@ void CommandServerInfo(const string& hostname, int port)
|
||||
try {
|
||||
DeserializeMessage(fd, serverInfo);
|
||||
}
|
||||
catch(const ioexception& e) {
|
||||
catch(const io_exception& e) {
|
||||
cerr << "Error: " << e.getmsg() << endl;
|
||||
|
||||
close(fd);
|
||||
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
@ -179,19 +192,71 @@ void CommandServerInfo(const string& hostname, int port)
|
||||
cout << " No image files available in the default folder" << endl;
|
||||
}
|
||||
else {
|
||||
list<string> sorted_image_files;
|
||||
list<string> sorted_files;
|
||||
for (int i = 0; i < serverInfo.available_image_files_size(); i++) {
|
||||
sorted_image_files.push_back(serverInfo.available_image_files(i));
|
||||
sorted_files.push_back(serverInfo.available_image_files(i).name());
|
||||
}
|
||||
sorted_image_files.sort();
|
||||
sorted_files.sort();
|
||||
|
||||
cout << "Image files available in the default folder:" << endl;
|
||||
for (auto it = sorted_image_files.begin(); it != sorted_image_files.end(); ++it) {
|
||||
for (auto it = sorted_files.begin(); it != sorted_files.end(); ++it) {
|
||||
cout << " " << *it << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PbOperation ParseOperation(const char *optarg)
|
||||
{
|
||||
switch (tolower(optarg[0])) {
|
||||
case 'a':
|
||||
return ATTACH;
|
||||
|
||||
case 'd':
|
||||
return DETACH;
|
||||
|
||||
case 'i':
|
||||
return INSERT;
|
||||
|
||||
case 'e':
|
||||
return EJECT;
|
||||
|
||||
case 'p':
|
||||
return PROTECT;
|
||||
|
||||
case 'u':
|
||||
return UNPROTECT;
|
||||
|
||||
default:
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
|
||||
PbDeviceType ParseType(const char *optarg)
|
||||
{
|
||||
string t = optarg;
|
||||
transform(t.begin(), t.end(), t.begin(), ::toupper);
|
||||
|
||||
PbDeviceType type;
|
||||
if (PbDeviceType_Parse(t, &type)) {
|
||||
return type;
|
||||
}
|
||||
else {
|
||||
// Parse legacy types
|
||||
switch (tolower(optarg[0])) {
|
||||
case 'm':
|
||||
return SCMO;
|
||||
|
||||
case 'c':
|
||||
return SCCD;
|
||||
|
||||
case 'b':
|
||||
return SCBR;
|
||||
}
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Main processing
|
||||
@ -205,12 +270,14 @@ int main(int argc, char* argv[])
|
||||
if (argc < 2) {
|
||||
cerr << "SCSI Target Emulator RaSCSI Controller" << endl;
|
||||
cerr << "version " << rascsi_get_version_string() << " (" << __DATE__ << ", " << __TIME__ << ")" << endl;
|
||||
cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-v]" << endl;
|
||||
cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-n NAME] [-f FILE] [-d DEFAULT_IMAGE_FOLDER] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-v]" << endl;
|
||||
cerr << " where ID := {0|1|2|3|4|5|6|7}" << endl;
|
||||
cerr << " UNIT := {0|1} default setting is 0." << endl;
|
||||
cerr << " CMD := {attach|detach|insert|eject|protect|unprotect}" << endl;
|
||||
cerr << " TYPE := {hd|mo|cd|bridge|daynaport}" << endl;
|
||||
cerr << " TYPE := {sahd|schd|scrm|sccd|scmo|scbr|scdp} or legacy types {hd|mo|cd|bridge}" << endl;
|
||||
cerr << " NAME := name of device to attach (VENDOR:PRODUCT:REVISION)" << endl;
|
||||
cerr << " FILE := image file path" << endl;
|
||||
cerr << " DEFAULT_IMAGE_FOLDER := default location for image files, default is '~/images'" << endl;
|
||||
cerr << " HOST := rascsi host to connect to, default is 'localhost'" << endl;
|
||||
cerr << " PORT := rascsi port to connect to, default is 6868" << endl;
|
||||
cerr << " LOG_LEVEL := log level {trace|debug|info|warn|err|critical|off}, default is 'trace'" << endl;
|
||||
@ -218,155 +285,115 @@ int main(int argc, char* argv[])
|
||||
cerr << "Usage: " << argv[0] << " -l" << endl;
|
||||
cerr << " Print device list." << endl;
|
||||
|
||||
exit(0);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// Parse the arguments
|
||||
int opt;
|
||||
int id = -1;
|
||||
int un = 0;
|
||||
PbOperation cmd = LIST;
|
||||
PbDeviceType type = UNDEFINED;
|
||||
PbCommand command;
|
||||
PbDeviceDefinitions devices;
|
||||
command.set_allocated_devices(&devices);
|
||||
PbDeviceDefinition *device = devices.add_devices();
|
||||
device->set_id(-1);
|
||||
const char *hostname = "localhost";
|
||||
int port = 6868;
|
||||
string params;
|
||||
opterr = 0;
|
||||
while ((opt = getopt(argc, argv, "i:u:c:t:f:h:p:u:g:lsv")) != -1) {
|
||||
string log_level;
|
||||
string default_folder;
|
||||
bool list = false;
|
||||
|
||||
opterr = 1;
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "i:u:c:t:f:d:h:n:p:u:g:lsv")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
id = optarg[0] - '0';
|
||||
device->set_id(optarg[0] - '0');
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
un = optarg[0] - '0';
|
||||
device->set_unit(optarg[0] - '0');
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
switch (tolower(optarg[0])) {
|
||||
case 'a':
|
||||
cmd = ATTACH;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
cmd = DETACH;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
cmd = INSERT;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
cmd = EJECT;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
cmd = PROTECT;
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
cmd = UNPROTECT;
|
||||
break;
|
||||
}
|
||||
command.set_cmd(ParseOperation(optarg));
|
||||
break;
|
||||
|
||||
case 't':
|
||||
switch (tolower(optarg[0])) {
|
||||
case 's':
|
||||
type = SASI_HD;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
type = SCSI_HD;
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
type = MO;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
type = CD;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
type = BR;
|
||||
break;
|
||||
|
||||
// case 'n':
|
||||
// type = NUVOLINK;
|
||||
// break;
|
||||
|
||||
case 'd':
|
||||
type = DAYNAPORT;
|
||||
break;
|
||||
}
|
||||
device->set_type(ParseType(optarg));
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
params = optarg;
|
||||
device->set_file(optarg);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
cmd = LIST;
|
||||
case 'd':
|
||||
command.set_cmd(DEFAULT_FOLDER);
|
||||
default_folder = optarg;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
hostname = optarg;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
list = true;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
device->set_name(optarg);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
if (port <= 0 || port > 65535) {
|
||||
cerr << "Invalid port " << optarg << ", port must be between 1 and 65535" << endl;
|
||||
exit(-1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
cmd = LOG_LEVEL;
|
||||
params = optarg;
|
||||
command.set_cmd(LOG_LEVEL);
|
||||
log_level = optarg;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
cmd = SERVER_INFO;
|
||||
command.set_cmd(SERVER_INFO);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
cout << rascsi_get_version_string() << endl;
|
||||
exit(0);
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd == LOG_LEVEL) {
|
||||
CommandLogLevel(hostname, port, params);
|
||||
exit(0);
|
||||
if (optopt) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (cmd == SERVER_INFO) {
|
||||
if (command.cmd() == LOG_LEVEL) {
|
||||
CommandLogLevel(hostname, port, log_level);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if (command.cmd() == DEFAULT_FOLDER) {
|
||||
CommandDefaultImageFolder(hostname, port, default_folder);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if (command.cmd() == SERVER_INFO) {
|
||||
CommandServerInfo(hostname, port);
|
||||
exit(0);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// List display only
|
||||
if (cmd == LIST || (id < 0 && type == UNDEFINED && params.empty())) {
|
||||
if (list) {
|
||||
CommandList(hostname, port);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Generate the command and send it
|
||||
PbCommand command;
|
||||
command.set_id(id);
|
||||
command.set_un(un);
|
||||
command.set_cmd(cmd);
|
||||
command.set_type(type);
|
||||
if (!params.empty()) {
|
||||
command.set_params(params);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// Send the command
|
||||
int fd = SendCommand(hostname, port, command);
|
||||
bool status = ReceiveResult(fd);
|
||||
close(fd);
|
||||
|
||||
// All done!
|
||||
exit(status ? 0 : -1);
|
||||
exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ GPIOBUS bus; // Bus
|
||||
int targetid; // Target ID
|
||||
int boardid; // Board ID (own ID)
|
||||
Filepath hdsfile; // HDS file
|
||||
BOOL restore; // Restore flag
|
||||
bool restore; // Restore flag
|
||||
BYTE buffer[BUFSIZE]; // Work Buffer
|
||||
int result; // Result Code
|
||||
|
||||
@ -60,7 +60,7 @@ void KillHandler(int sig)
|
||||
// Banner Output
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Banner(int argc, char* argv[])
|
||||
bool Banner(int argc, char* argv[])
|
||||
{
|
||||
printf("RaSCSI hard disk dump utility ");
|
||||
printf("version %s (%s, %s)\n",
|
||||
@ -74,10 +74,10 @@ BOOL Banner(int argc, char* argv[])
|
||||
printf(" BID is rascsi board SCSI ID {0|1|2|3|4|5|6|7}. Default is 7.\n");
|
||||
printf(" FILE is HDS file path.\n");
|
||||
printf(" -r is restore operation.\n");
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -85,30 +85,30 @@ BOOL Banner(int argc, char* argv[])
|
||||
// Initialization
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Init()
|
||||
bool Init()
|
||||
{
|
||||
// Interrupt handler setting
|
||||
if (signal(SIGINT, KillHandler) == SIG_ERR) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
if (signal(SIGHUP, KillHandler) == SIG_ERR) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
if (signal(SIGTERM, KillHandler) == SIG_ERR) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// GPIO Initialization
|
||||
if (!bus.Init(BUS::INITIATOR)) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Work Intitialization
|
||||
targetid = -1;
|
||||
boardid = 7;
|
||||
restore = FALSE;
|
||||
restore = false;
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -138,7 +138,7 @@ void Reset()
|
||||
// Argument processing
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL ParseArgument(int argc, char* argv[])
|
||||
bool ParseArgument(int argc, char* argv[])
|
||||
{
|
||||
int opt;
|
||||
char *file;
|
||||
@ -163,7 +163,7 @@ BOOL ParseArgument(int argc, char* argv[])
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
restore = TRUE;
|
||||
restore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -172,33 +172,33 @@ BOOL ParseArgument(int argc, char* argv[])
|
||||
if (targetid < 0 || targetid > 7) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid target id range\n");
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// BOARD ID check
|
||||
if (boardid < 0 || boardid > 7) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid board id range\n");
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target and Board ID duplication check
|
||||
if (targetid == boardid) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid target or board id\n");
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// File Check
|
||||
if (!file) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid file path\n");
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
hdsfile.SetPath(file);
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -206,7 +206,7 @@ BOOL ParseArgument(int argc, char* argv[])
|
||||
// Wait Phase
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL WaitPhase(BUS::phase_t phase)
|
||||
bool WaitPhase(BUS::phase_t phase)
|
||||
{
|
||||
DWORD now;
|
||||
|
||||
@ -215,11 +215,11 @@ BOOL WaitPhase(BUS::phase_t phase)
|
||||
while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) {
|
||||
bus.Aquire();
|
||||
if (bus.GetREQ() && bus.GetPhase() == phase) {
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -238,7 +238,7 @@ void BusFree()
|
||||
// Selection Phase
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Selection(int id)
|
||||
bool Selection(int id)
|
||||
{
|
||||
BYTE data;
|
||||
int count;
|
||||
@ -272,13 +272,13 @@ BOOL Selection(int id)
|
||||
// Command Phase
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Command(BYTE *buf, int length)
|
||||
bool Command(BYTE *buf, int length)
|
||||
{
|
||||
int count;
|
||||
|
||||
// Waiting for Phase
|
||||
if (!WaitPhase(BUS::command)) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send Command
|
||||
@ -287,11 +287,11 @@ BOOL Command(BYTE *buf, int length)
|
||||
// Success if the transmission result is the same as the number
|
||||
// of requests
|
||||
if (count == length) {
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return error
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -808,7 +808,7 @@ int main(int argc, char* argv[])
|
||||
DWORD dnum;
|
||||
Fileio fio;
|
||||
Fileio::OpenMode omode;
|
||||
off64_t size;
|
||||
off_t size;
|
||||
|
||||
// Banner output
|
||||
if (!Banner(argc, argv)) {
|
||||
@ -918,9 +918,9 @@ int main(int argc, char* argv[])
|
||||
if (restore) {
|
||||
size = fio.GetFileSize();
|
||||
printf("Restore file size : %d bytes", (int)size);
|
||||
if (size > (off64_t)(bsiz * bnum)) {
|
||||
if (size > (off_t)(bsiz * bnum)) {
|
||||
printf("(WARNING : File size is larger than disk size)");
|
||||
} else if (size < (off64_t)(bsiz * bnum)) {
|
||||
} else if (size < (off_t)(bsiz * bnum)) {
|
||||
printf("(ERROR : File size is smaller than disk size)\n");
|
||||
goto cleanup_exit;
|
||||
}
|
||||
|
@ -1,86 +1,19 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
// Copyright (C) 2016-2020 GIMONS
|
||||
// Copyright (C) 2020 akuker
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include "rascsi_interface.pb.h"
|
||||
#include "exceptions.h"
|
||||
#include "rasutil.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace rascsi_interface;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Serialize/Deserialize protobuf message: Length followed by the actual data.
|
||||
// Little endian is assumed.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void SerializeMessage(int fd, const google::protobuf::MessageLite& message)
|
||||
{
|
||||
string data;
|
||||
message.SerializeToString(&data);
|
||||
|
||||
// Write the size of the protobuf data as a header
|
||||
int32_t size = data.length();
|
||||
if (write(fd, &size, sizeof(size)) != sizeof(size)) {
|
||||
throw ioexception("Can't write protobuf header");
|
||||
}
|
||||
|
||||
// Write the actual protobuf data
|
||||
uint8_t buf[size];
|
||||
memcpy(buf, data.data(), size);
|
||||
if (write(fd, buf, size) != size) {
|
||||
throw ioexception("Can't write protobuf data");
|
||||
}
|
||||
}
|
||||
|
||||
void DeserializeMessage(int fd, google::protobuf::MessageLite& message)
|
||||
{
|
||||
// Read the header with the size of the protobuf data
|
||||
uint8_t header_buf[4];
|
||||
int bytes_read = ReadNBytes(fd, header_buf, 4);
|
||||
if (bytes_read < 4) {
|
||||
return;
|
||||
}
|
||||
int32_t size = (header_buf[3] << 24) + (header_buf[2] << 16) + (header_buf[1] << 8) + header_buf[0];
|
||||
|
||||
// Read the binary protobuf data
|
||||
uint8_t data_buf[size];
|
||||
bytes_read = ReadNBytes(fd, data_buf, size);
|
||||
if (bytes_read < size) {
|
||||
throw ioexception("Missing protobuf data");
|
||||
}
|
||||
|
||||
// Create protobuf message
|
||||
string data((const char *)data_buf, size);
|
||||
message.ParseFromString(data);
|
||||
}
|
||||
|
||||
int ReadNBytes(int fd, uint8_t *buf, int n)
|
||||
{
|
||||
int offset = 0;
|
||||
while (offset < n) {
|
||||
ssize_t len = read(fd, buf + offset, n - offset);
|
||||
if (!len) {
|
||||
break;
|
||||
}
|
||||
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// List devices
|
||||
@ -91,82 +24,38 @@ string ListDevices(const PbDevices& devices)
|
||||
ostringstream s;
|
||||
|
||||
if (devices.devices_size()) {
|
||||
s << endl
|
||||
<< "+----+----+------+-------------------------------------" << endl
|
||||
<< "| ID | UN | TYPE | DEVICE STATUS" << endl
|
||||
s << "+----+----+------+-------------------------------------" << endl
|
||||
<< "| ID | UN | TYPE | DEVICE STATUS" << endl
|
||||
<< "+----+----+------+-------------------------------------" << endl;
|
||||
}
|
||||
else {
|
||||
return "No images currently attached.\n";
|
||||
return "No images currently attached.";
|
||||
}
|
||||
|
||||
for (int i = 0; i < devices.devices_size() ; i++) {
|
||||
PbDevice device = devices.devices(i);
|
||||
|
||||
s << "| " << device.id() << " | " << device.un() << " | " << MapTypeToId(device.type()) << " | "
|
||||
<< (device.file().empty() ? "NO MEDIA" : device.file())
|
||||
<< (device.read_only() ? " (WRITEPROTECT)" : "") << endl;
|
||||
string filename;
|
||||
switch (device.type()) {
|
||||
case SCBR:
|
||||
filename = "X68000 HOST BRIDGE";
|
||||
break;
|
||||
|
||||
case SCDP:
|
||||
filename = "DaynaPort SCSI/Link";
|
||||
break;
|
||||
|
||||
default:
|
||||
filename = device.file().name();
|
||||
break;
|
||||
}
|
||||
|
||||
s << "| " << device.id() << " | " << device.unit() << " | " << PbDeviceType_Name(device.type()) << " | "
|
||||
<< (filename.empty() ? "NO MEDIA" : filename)
|
||||
<< (!device.removed() && (device.read_only() || device.protected_()) ? " (WRITEPROTECT)" : "") << endl;
|
||||
}
|
||||
|
||||
s << "+----+----+------+-------------------------------------" << endl;
|
||||
s << "+----+----+------+-------------------------------------";
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Map the device ID to the PbDeviceType and vice versa
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
PbDeviceType MapIdToType(const string& id, bool is_sasi)
|
||||
{
|
||||
if (id == "SCHD") {
|
||||
return is_sasi ? SASI_HD : SCSI_HD;
|
||||
}
|
||||
else if (id == "SCMO") {
|
||||
return MO;
|
||||
}
|
||||
else if (id == "SCCD") {
|
||||
return CD;
|
||||
}
|
||||
else if (id == "SCBR") {
|
||||
return BR;
|
||||
}
|
||||
else if (id == "SCDP") {
|
||||
return DAYNAPORT;
|
||||
}
|
||||
else if (id == "SCNL") {
|
||||
return NUVOLINK;
|
||||
}
|
||||
else {
|
||||
return UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
string MapTypeToId(const PbDeviceType type)
|
||||
{
|
||||
switch (type) {
|
||||
case SASI_HD:
|
||||
case SCSI_HD:
|
||||
return "SCHD";
|
||||
|
||||
case MO:
|
||||
return "SCMO";
|
||||
|
||||
case CD:
|
||||
return "SCCD";
|
||||
|
||||
case BR:
|
||||
return "SCBR";
|
||||
|
||||
case DAYNAPORT:
|
||||
return "SCDP";
|
||||
|
||||
case NUVOLINK:
|
||||
return "SCNL";
|
||||
|
||||
default:
|
||||
return "????";
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,17 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
// Copyright (C) 2016-2020 GIMONS
|
||||
// Copyright (C) 2020 akuker
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
// Helper methods used by rascsi and rasctl
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#if !defined(rasutil_h)
|
||||
#define rasutil_h
|
||||
#pragma once
|
||||
|
||||
#include "google/protobuf/message_lite.h"
|
||||
#include <string>
|
||||
#include "rascsi_interface.pb.h"
|
||||
|
||||
void SerializeMessage(int, const google::protobuf::MessageLite&);
|
||||
void DeserializeMessage(int, google::protobuf::MessageLite&);
|
||||
int ReadNBytes(int, uint8_t *, int);
|
||||
string ListDevices(const rascsi_interface::PbDevices&);
|
||||
rascsi_interface::PbDeviceType MapIdToType(const string&, bool);
|
||||
string MapTypeToId(const rascsi_interface::PbDeviceType);
|
||||
|
||||
#endif
|
||||
std::string ListDevices(const rascsi_interface::PbDevices&);
|
||||
|
@ -27,14 +27,14 @@
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 定数宣言
|
||||
// Constant declaration
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#define BUFSIZE 1024 * 64 // 64KBぐらいかなぁ
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 変数宣言
|
||||
// Variable declaration
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
GPIOBUS bus; // バス
|
||||
@ -43,35 +43,35 @@ int unitid; // ターゲットユニットID
|
||||
int bsiz; // ブロックサイズ
|
||||
int bnum; // ブロック数
|
||||
Filepath hdffile; // HDFファイル
|
||||
BOOL restore; // リストアフラグ
|
||||
bool restore; // リストアフラグ
|
||||
BYTE buffer[BUFSIZE]; // ワークバッファ
|
||||
int result; // 結果コード
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 関数宣言
|
||||
// Function declaration
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Cleanup();
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// シグナル処理
|
||||
// Signal processing
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void KillHandler(int sig)
|
||||
{
|
||||
// 停止指示
|
||||
// Stop instruction
|
||||
Cleanup();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// バナー出力
|
||||
// Banner output
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Banner(int argc, char* argv[])
|
||||
bool Banner(int argc, char* argv[])
|
||||
{
|
||||
printf("RaSCSI hard disk dump utility(SASI HDD) ");
|
||||
printf("version %s (%s, %s)\n",
|
||||
@ -87,33 +87,33 @@ BOOL Banner(int argc, char* argv[])
|
||||
printf(" COUNT is block count.\n");
|
||||
printf(" FILE is HDF file path.\n");
|
||||
printf(" -r is restore operation.\n");
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 初期化
|
||||
// Initialization
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Init()
|
||||
bool Init()
|
||||
{
|
||||
// 割り込みハンドラ設定
|
||||
if (signal(SIGINT, KillHandler) == SIG_ERR) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
if (signal(SIGHUP, KillHandler) == SIG_ERR) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
if (signal(SIGTERM, KillHandler) == SIG_ERR) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// GPIO初期化
|
||||
if (!bus.Init(BUS::INITIATOR)) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ワーク初期化
|
||||
@ -121,9 +121,9 @@ BOOL Init()
|
||||
unitid = 0;
|
||||
bsiz = 256;
|
||||
bnum = -1;
|
||||
restore = FALSE;
|
||||
restore = false;
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -139,21 +139,21 @@ void Cleanup()
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// リセット
|
||||
// Reset
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Reset()
|
||||
{
|
||||
// バス信号線をリセット
|
||||
// Reset bus signal line
|
||||
bus.Reset();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 引数処理
|
||||
// Argument processing
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL ParseArgument(int argc, char* argv[])
|
||||
bool ParseArgument(int argc, char* argv[])
|
||||
{
|
||||
int opt;
|
||||
char *file;
|
||||
@ -186,7 +186,7 @@ BOOL ParseArgument(int argc, char* argv[])
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
restore = TRUE;
|
||||
restore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -195,48 +195,48 @@ BOOL ParseArgument(int argc, char* argv[])
|
||||
if (targetid < 0 || targetid > 7) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid target id range\n");
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// UNIT IDチェック
|
||||
if (unitid < 0 || unitid > 1) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid unit id range\n");
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// BSIZチェック
|
||||
if (bsiz != 256 && bsiz != 512 && bsiz != 1024) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid block size\n");
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// BNUMチェック
|
||||
if (bnum < 0) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid block count\n");
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ファイルチェック
|
||||
if (!file) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid file path\n");
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
hdffile.SetPath(file);
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// フェーズ待ち
|
||||
// Waiting for phase
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL WaitPhase(BUS::phase_t phase)
|
||||
bool WaitPhase(BUS::phase_t phase)
|
||||
{
|
||||
DWORD now;
|
||||
|
||||
@ -245,11 +245,11 @@ BOOL WaitPhase(BUS::phase_t phase)
|
||||
while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) {
|
||||
bus.Aquire();
|
||||
if (bus.GetREQ() && bus.GetPhase() == phase) {
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -265,10 +265,10 @@ void BusFree()
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// セレクションフェーズ実行
|
||||
// Selection phase execution
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Selection(int id)
|
||||
bool Selection(int id)
|
||||
{
|
||||
BYTE data;
|
||||
int count;
|
||||
@ -298,16 +298,16 @@ BOOL Selection(int id)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// コマンドフェーズ実行
|
||||
// Command phase execution
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Command(BYTE *buf, int length)
|
||||
bool Command(BYTE *buf, int length)
|
||||
{
|
||||
int count;
|
||||
|
||||
// フェーズ待ち
|
||||
if (!WaitPhase(BUS::command)) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// コマンド送信
|
||||
@ -315,16 +315,16 @@ BOOL Command(BYTE *buf, int length)
|
||||
|
||||
// 送信結果が依頼数と同じなら成功
|
||||
if (count == length) {
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 送信エラー
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// データインフェーズ実行
|
||||
// Data in phase execution
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int DataIn(BYTE *buf, int length)
|
||||
@ -340,7 +340,7 @@ int DataIn(BYTE *buf, int length)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// データアウトフェーズ実行
|
||||
// Data out phase execution
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int DataOut(BYTE *buf, int length)
|
||||
@ -652,7 +652,7 @@ int main(int argc, char* argv[])
|
||||
DWORD dnum;
|
||||
Fileio fio;
|
||||
Fileio::OpenMode omode;
|
||||
off64_t size;
|
||||
off_t size;
|
||||
|
||||
// バナー出力
|
||||
if (!Banner(argc, argv)) {
|
||||
@ -732,9 +732,9 @@ int main(int argc, char* argv[])
|
||||
if (restore) {
|
||||
size = fio.GetFileSize();
|
||||
printf("Restore file size : %d bytes", (int)size);
|
||||
if (size > (off64_t)(bsiz * bnum)) {
|
||||
if (size > (off_t)(bsiz * bnum)) {
|
||||
printf("(WARNING : File size is larger than disk size)");
|
||||
} else if (size < (off64_t)(bsiz * bnum)) {
|
||||
} else if (size < (off_t)(bsiz * bnum)) {
|
||||
printf("(ERROR : File size is smaller than disk size)\n");
|
||||
goto cleanup_exit;
|
||||
}
|
||||
|
@ -42,7 +42,10 @@ public:
|
||||
enum asc : int {
|
||||
NO_ADDITIONAL_SENSE_INFORMATION = 0x00,
|
||||
INVALID_COMMAND_OPERATION_CODE = 0x20,
|
||||
LBA_OUT_OF_RANGE = 0x21,
|
||||
INVALID_FIELD_IN_CDB = 0x24,
|
||||
INVALID_LUN = 0x25,
|
||||
WRITE_PROTECTED = 0x27,
|
||||
MEDIUM_NOT_PRESENT = 0x3a
|
||||
};
|
||||
};
|
||||
@ -107,9 +110,9 @@ public:
|
||||
return ((raw_data >> pin_num) & 1);
|
||||
}
|
||||
|
||||
virtual BOOL GetBSY() = 0;
|
||||
virtual bool GetBSY() = 0;
|
||||
// BSYシグナル取得
|
||||
virtual void SetBSY(BOOL ast) = 0;
|
||||
virtual void SetBSY(bool ast) = 0;
|
||||
// BSYシグナル設定
|
||||
|
||||
virtual BOOL GetSEL() = 0;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "spdlog/spdlog.h"
|
||||
#include <sys/time.h>
|
||||
#include <climits>
|
||||
#include <sstream>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
@ -52,8 +53,7 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
static BYTE prev_value[32] = {0xFF};
|
||||
static volatile BOOL running; // Running flag
|
||||
static volatile BOOL active; // Processing flag
|
||||
static volatile bool running; // Running flag
|
||||
GPIOBUS *bus; // GPIO Bus
|
||||
typedef struct data_capture{
|
||||
DWORD data;
|
||||
@ -78,7 +78,7 @@ char log_file_name[_MAX_FNAME/2] = "log.vcd";
|
||||
void KillHandler(int sig)
|
||||
{
|
||||
// Stop instruction
|
||||
running = FALSE;
|
||||
running = false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -143,8 +143,7 @@ BOOL Init()
|
||||
bus->Reset();
|
||||
|
||||
// Other
|
||||
running = FALSE;
|
||||
active = FALSE;
|
||||
running = false;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -266,7 +265,9 @@ void create_value_change_dump()
|
||||
|
||||
while(i < data_idx)
|
||||
{
|
||||
fprintf(fp, "#%llu\n",(QWORD)(data_buffer[i].timestamp*ns_per_loop));
|
||||
ostringstream s;
|
||||
s << (QWORD)(data_buffer[i].timestamp*ns_per_loop);
|
||||
fprintf(fp, "#%s\n",s.str().c_str());
|
||||
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_BSY, SYMBOL_PIN_BSY);
|
||||
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_SEL, SYMBOL_PIN_SEL);
|
||||
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_CD, SYMBOL_PIN_CD);
|
||||
@ -350,7 +351,9 @@ static DWORD low_bits = 0xFFFFFFFF;
|
||||
//---------------------------------------------------------------------------
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
#ifdef DEBUG
|
||||
ostringstream s;
|
||||
|
||||
#ifdef DEBUG
|
||||
DWORD prev_high = high_bits;
|
||||
DWORD prev_low = low_bits;
|
||||
#endif
|
||||
@ -409,13 +412,14 @@ int main(int argc, char* argv[])
|
||||
sched_setscheduler(0, SCHED_FIFO, &schparam);
|
||||
|
||||
// Start execution
|
||||
running = TRUE;
|
||||
running = false;
|
||||
bus->SetACT(FALSE);
|
||||
|
||||
(void)gettimeofday(&start_time, NULL);
|
||||
|
||||
LOGDEBUG("ALL_SCSI_PINS %08X\n",ALL_SCSI_PINS);
|
||||
// Main Loop
|
||||
|
||||
// Main Loop
|
||||
while (running) {
|
||||
// Work initialization
|
||||
this_sample = (bus->Aquire() & ALL_SCSI_PINS);
|
||||
@ -441,12 +445,14 @@ int main(int argc, char* argv[])
|
||||
low_bits &= this_sample;
|
||||
if ((high_bits != prev_high) || (low_bits != prev_low))
|
||||
{
|
||||
LOGDEBUG(" %08lX %08lX\n",high_bits, low_bits);
|
||||
LOGDEBUG(" %08X %08X\n",high_bits, low_bits);
|
||||
}
|
||||
prev_high = high_bits;
|
||||
prev_low = low_bits;
|
||||
if((data_idx % 1000) == 0){
|
||||
LOGDEBUG("Collected %lu samples...", data_idx);
|
||||
s.str("");
|
||||
s << "Collected " << data_idx << " samples...";
|
||||
LOGDEBUG("%s", s.str().c_str());
|
||||
}
|
||||
#endif
|
||||
data_buffer[data_idx].data = this_sample;
|
||||
@ -471,12 +477,18 @@ int main(int argc, char* argv[])
|
||||
timersub(&stop_time, &start_time, &time_diff);
|
||||
|
||||
elapsed_us = ((time_diff.tv_sec*1000000) + time_diff.tv_usec);
|
||||
LOGINFO("Elapsed time: %llu microseconds (%lf seconds)",elapsed_us, ((double)elapsed_us)/1000000);
|
||||
LOGINFO("Collected %lu changes", data_idx);
|
||||
s.str("");
|
||||
s << "Elapsed time: " << elapsed_us << " microseconds (" << elapsed_us / 1000000 << " seconds)";
|
||||
LOGINFO("%s", s.str().c_str());
|
||||
s.str("");
|
||||
s << "Collected %lu changes" << data_idx;
|
||||
LOGINFO("%s", s.str().c_str());
|
||||
|
||||
// Note: ns_per_loop is a global variable that is used by Cleanup() to printout the timestamps.
|
||||
ns_per_loop = (elapsed_us * 1000) / (double)loop_count;
|
||||
LOGINFO("Read the SCSI bus %llu times with an average of %lu ns for each read", loop_count, (DWORD)ns_per_loop);
|
||||
s.str("");
|
||||
s << "Read the SCSI bus " << loop_count << " times with an average of " << ns_per_loop << " ns for each read";
|
||||
LOGINFO("%s", s.str().c_str());
|
||||
|
||||
// Cleanup
|
||||
Cleanup();
|
||||
|
@ -13,13 +13,6 @@
|
||||
#if !defined(xm6_h)
|
||||
#define xm6_h
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// RaSCSI
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#define RASCSI 1
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Various Operation Settings
|
||||
|
@ -6,7 +6,7 @@ import time
|
||||
from ractl_cmds import attach_image
|
||||
from settings import *
|
||||
|
||||
valid_file_suffix = ["*.hda", "*.hdn", "*.hdi", "*.nhd", "*.hdf", "*.hds", "*.iso", "*.cdr", "*.zip"]
|
||||
valid_file_suffix = ["*.hda", "*.hdn", "*.hdi", "*.nhd", "*.hdf", "*.hds", "*.hdr", "*.iso", "*.cdr", "*.zip"]
|
||||
valid_file_types = r"|".join([fnmatch.translate(x) for x in valid_file_suffix])
|
||||
|
||||
|
||||
|
@ -5,7 +5,7 @@ import re
|
||||
from settings import *
|
||||
|
||||
|
||||
valid_file_suffix = ["*.hda", "*.hdn", "*.hdi", "*.nhd", "*.hdf", "*.hds", "*.iso", "*.cdr", "*.zip"]
|
||||
valid_file_suffix = ["*.hda", "*.hdn", "*.hdi", "*.nhd", "*.hdf", "*.hds", "*.hdr", "*.iso", "*.cdr", "*.zip"]
|
||||
valid_file_types = r"|".join([fnmatch.translate(x) for x in valid_file_suffix])
|
||||
# List of SCSI ID's you'd like to exclude - eg if you are on a Mac, the System is usually 7
|
||||
EXCLUDE_SCSI_IDS = [7]
|
||||
@ -70,8 +70,6 @@ def attach_image(scsi_id, image, device_type):
|
||||
elif device_type == "SCDP":
|
||||
attach_daynaport(scsi_id)
|
||||
else:
|
||||
if device_type == "SCCD":
|
||||
device_type = "cd"
|
||||
return subprocess.run(
|
||||
["rasctl", "-c", "attach", "-t", device_type, "-i", scsi_id, "-f", image],
|
||||
capture_output=True,
|
||||
@ -105,7 +103,7 @@ def insert(scsi_id, image):
|
||||
|
||||
def attach_daynaport(scsi_id):
|
||||
return subprocess.run(
|
||||
["rasctl", "-i", scsi_id, "-c", "attach", "-t", "daynaport"],
|
||||
["rasctl", "-i", scsi_id, "-c", "attach", "-t", "scdp"],
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
|
@ -75,6 +75,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
<h2>Image File Management</h2>
|
||||
<table cellpadding="3" border="black">
|
||||
<tbody>
|
||||
@ -220,8 +221,9 @@
|
||||
<option value="hdn">SCSI Hard Disk image (NEC GENUINE)</option>
|
||||
<option value="hdi">SCSI Hard Disk image (Anex86 HD image)</option>
|
||||
<option value="nhd">SCSI Hard Disk image (T98Next HD image)</option>
|
||||
<option value="hds">SCSI Hard Disk image (Generic - recommended for Atari computers)</option>
|
||||
<option value="hdr">SCSI Removable Media Disk image (Generic)</option>
|
||||
<option value="hdf">SASI Hard Disk image (XM6 SASI HD image - typically only used with X68000)</option>
|
||||
<option value="hds">SCSI Hard Disk image (XM6 SCSI HD image - typically only used with X68000)</option>
|
||||
</select>
|
||||
<label for="size">Size(MB):</label>
|
||||
<input type="number" placeholder="Size(MB)" name="size"/>
|
||||
|
Loading…
Reference in New Issue
Block a user