diff --git a/doc/rascsi.1 b/doc/rascsi.1 index 5f0ac645..1ce9523d 100644 --- a/doc/rascsi.1 +++ b/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 diff --git a/doc/rascsi_man_page.txt b/doc/rascsi_man_page.txt index 722ab5e6..2ad09f86 100644 --- a/doc/rascsi_man_page.txt +++ b/doc/rascsi_man_page.txt @@ -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 diff --git a/doc/rasctl.1 b/doc/rasctl.1 index caa5c1c3..3d91d38a 100644 --- a/doc/rasctl.1 +++ b/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) diff --git a/doc/rasctl_man_page.txt b/doc/rasctl_man_page.txt index fb5f7504..1771b605 100644 --- a/doc/rasctl_man_page.txt +++ b/doc/rasctl_man_page.txt @@ -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 diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile index ccb0a26a..d18122a9 100644 --- a/src/raspberrypi/Makefile +++ b/src/raspberrypi/Makefile @@ -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 = \ diff --git a/src/raspberrypi/controllers/sasidev_ctrl.cpp b/src/raspberrypi/controllers/sasidev_ctrl.cpp index f1e73f3b..d9590a53 100644 --- a/src/raspberrypi/controllers/sasidev_ctrl.cpp +++ b/src/raspberrypi/controllers/sasidev_ctrl.cpp @@ -19,6 +19,7 @@ #include "devices/scsi_host_bridge.h" #include "devices/scsi_daynaport.h" #include "exceptions.h" +#include //=========================================================================== // @@ -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; } diff --git a/src/raspberrypi/controllers/sasidev_ctrl.h b/src/raspberrypi/controllers/sasidev_ctrl.h index 8f176d86..c78d1b0e 100644 --- a/src/raspberrypi/controllers/sasidev_ctrl.h +++ b/src/raspberrypi/controllers/sasidev_ctrl.h @@ -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 diff --git a/src/raspberrypi/controllers/scsidev_ctrl.cpp b/src/raspberrypi/controllers/scsidev_ctrl.cpp index 1feed5db..cf6362f3 100644 --- a/src/raspberrypi/controllers/scsidev_ctrl.cpp +++ b/src/raspberrypi/controllers/scsidev_ctrl.cpp @@ -19,6 +19,7 @@ #include "devices/scsi_host_bridge.h" #include "devices/scsi_daynaport.h" #include "exceptions.h" +#include //=========================================================================== // @@ -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(ctrl.cmd[0]))) { - LOGWARN("%s Received unsupported command: $%02X", __PRETTY_FUNCTION__, (BYTE)ctrl.cmd[0]); CmdInvalid(); return; } command_t* command = scsi_commands[static_cast(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]; diff --git a/src/raspberrypi/controllers/scsidev_ctrl.h b/src/raspberrypi/controllers/scsidev_ctrl.h index ec3e02d4..b2179425 100644 --- a/src/raspberrypi/controllers/scsidev_ctrl.h +++ b/src/raspberrypi/controllers/scsidev_ctrl.h @@ -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 diff --git a/src/raspberrypi/devices/block_device.h b/src/raspberrypi/devices/block_device.h new file mode 100644 index 00000000..d6dd4813 --- /dev/null +++ b/src/raspberrypi/devices/block_device.h @@ -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 +}; diff --git a/src/raspberrypi/devices/cfilesystem.cpp b/src/raspberrypi/devices/cfilesystem.cpp index 050f0e73..0765a516 100644 --- a/src/raspberrypi/devices/cfilesystem.cpp +++ b/src/raspberrypi/devices/cfilesystem.cpp @@ -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: diff --git a/src/raspberrypi/devices/ctapdriver.cpp b/src/raspberrypi/devices/ctapdriver.cpp index dfffc43c..05457628 100644 --- a/src/raspberrypi/devices/ctapdriver.cpp +++ b/src/raspberrypi/devices/ctapdriver.cpp @@ -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; diff --git a/src/raspberrypi/devices/ctapdriver.h b/src/raspberrypi/devices/ctapdriver.h index 19d8d6ef..61cfeb9c 100644 --- a/src/raspberrypi/devices/ctapdriver.h +++ b/src/raspberrypi/devices/ctapdriver.h @@ -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: diff --git a/src/raspberrypi/devices/device.cpp b/src/raspberrypi/devices/device.cpp new file mode 100644 index 00000000..48b83963 --- /dev/null +++ b/src/raspberrypi/devices/device.cpp @@ -0,0 +1,140 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include +#include +#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; +} diff --git a/src/raspberrypi/devices/device.h b/src/raspberrypi/devices/device.h new file mode 100644 index 00000000..87a85c89 --- /dev/null +++ b/src/raspberrypi/devices/device.h @@ -0,0 +1,137 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#pragma once + +#include + +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"; } +}; diff --git a/src/raspberrypi/devices/device_factory.cpp b/src/raspberrypi/devices/device_factory.cpp new file mode 100644 index 00000000..266ff03f --- /dev/null +++ b/src/raspberrypi/devices/device_factory.cpp @@ -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; + } +} diff --git a/src/raspberrypi/devices/device_factory.h b/src/raspberrypi/devices/device_factory.h new file mode 100644 index 00000000..9d4265ef --- /dev/null +++ b/src/raspberrypi/devices/device_factory.h @@ -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 +#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); +}; diff --git a/src/raspberrypi/devices/disk.cpp b/src/raspberrypi/devices/disk.cpp index 946286a2..b0e5f8fc 100644 --- a/src/raspberrypi/devices/disk.cpp +++ b/src/raspberrypi/devices/disk.cpp @@ -83,8 +83,7 @@ DiskTrack::~DiskTrack() // Initialization // //--------------------------------------------------------------------------- -void DiskTrack::Init( - int track, int size, int sectors, BOOL raw, off64_t imgoff) +void DiskTrack::Init(int track, int size, int sectors, BOOL raw, off_t imgoff) { ASSERT(track >= 0); ASSERT((size >= 8) && (size <= 11)); @@ -126,7 +125,7 @@ BOOL DiskTrack::Load(const Filepath& path) // Calculate offset (previous tracks are considered to // hold 256 sectors) - off64_t offset = ((off64_t)dt.track << 8); + off_t offset = ((off_t)dt.track << 8); if (dt.raw) { ASSERT(dt.size == 11); offset *= 0x930; @@ -257,7 +256,7 @@ BOOL DiskTrack::Save(const Filepath& path) ASSERT(!dt.raw); // Calculate offset (previous tracks are considered to hold 256 - off64_t offset = ((off64_t)dt.track << 8); + off_t offset = ((off_t)dt.track << 8); offset <<= dt.size; // Add offset to real image @@ -280,7 +279,7 @@ BOOL DiskTrack::Save(const Filepath& path) total = 0; // Seek - if (!fio.Seek(offset + ((off64_t)i << dt.size))) { + if (!fio.Seek(offset + ((off_t)i << dt.size))) { fio.Close(); return FALSE; } @@ -344,7 +343,7 @@ BOOL DiskTrack::Read(BYTE *buf, int sec) const ASSERT(dt.buffer); ASSERT((dt.size >= 8) && (dt.size <= 11)); ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); - memcpy(buf, &dt.buffer[(off64_t)sec << dt.size], (off64_t)1 << dt.size); + memcpy(buf, &dt.buffer[(off_t)sec << dt.size], (off_t)1 << dt.size); // Success return TRUE; @@ -404,17 +403,14 @@ BOOL DiskTrack::Write(const BYTE *buf, int sec) // Constructor // //--------------------------------------------------------------------------- -DiskCache::DiskCache( - const Filepath& path, int size, int blocks, off64_t imgoff) +DiskCache::DiskCache(const Filepath& path, int size, int blocks, off_t imgoff) { - int i; - ASSERT((size >= 8) && (size <= 11)); ASSERT(blocks > 0); ASSERT(imgoff >= 0); // Cache work - for (i = 0; i < CacheMax; i++) { + for (int i = 0; i < CacheMax; i++) { cache[i].disktrk = NULL; cache[i].serial = 0; } @@ -703,28 +699,11 @@ void DiskCache::Update() // Constructor // //--------------------------------------------------------------------------- -Disk::Disk(std::string id) +Disk::Disk(const std::string id) : BlockDevice(id) { - assert(id.length() == 4); - - disk.id = id; - // Work initialization - disk.ready = FALSE; - disk.protectable = false; - disk.writep = false; - disk.readonly = false; - disk.removable = false; - disk.removed = false; - disk.lockable = false; - disk.locked = false; - disk.supports_file = true; - disk.attn = false; - disk.reset = false; disk.size = 0; disk.blocks = 0; - disk.lun = 0; - disk.code = 0; disk.dcache = NULL; disk.imgoffset = 0; @@ -740,7 +719,7 @@ Disk::Disk(std::string id) Disk::~Disk() { // Save disk cache - if (disk.ready) { + if (IsReady()) { // Only if ready... if (disk.dcache) { disk.dcache->Save(); @@ -754,104 +733,18 @@ Disk::~Disk() } } -//--------------------------------------------------------------------------- -// -// Reset -// -//--------------------------------------------------------------------------- -void Disk::Reset() -{ - // no lock, no attention, reset - disk.locked = false; - disk.attn = false; - disk.reset = false; -} - -//--------------------------------------------------------------------------- -// -// Retrieve the disk's ID -// -//--------------------------------------------------------------------------- -const std::string& Disk::GetID() const -{ - return disk.id; -} - - -//--------------------------------------------------------------------------- -// -// Get cache writeback mode -// -//--------------------------------------------------------------------------- -bool Disk::IsCacheWB() -{ - return cache_wb; -} - -//--------------------------------------------------------------------------- -// -// Set cache writeback mode -// -//--------------------------------------------------------------------------- -void Disk::SetCacheWB(BOOL enable) -{ - cache_wb = enable; -} - -//--------------------------------------------------------------------------- -// -// Device type checks -// -//--------------------------------------------------------------------------- - -bool Disk::IsSASI() const -{ - return disk.id == "SAHD"; -} - -bool Disk::IsSCSI() const -{ - return disk.id == "SCHD"; -} - -bool Disk::IsCdRom() const -{ - return disk.id == "SCCD"; -} - -bool Disk::IsMo() const -{ - return disk.id == "SCMO"; -} - -bool Disk::IsBridge() const -{ - return disk.id == "SCBR"; -} - -bool Disk::IsDaynaPort() const -{ - return disk.id == "SCDP"; -} - -bool Disk::IsNuvolink() const -{ - return disk.id == "SCNL"; -} - //--------------------------------------------------------------------------- // // Open // * Call as a post-process after successful opening in a derived class // //--------------------------------------------------------------------------- -void Disk::Open(const Filepath& path, BOOL /*attn*/) +void Disk::Open(const Filepath& path) { ASSERT((disk.size >= 8) && (disk.size <= 11)); ASSERT(disk.blocks > 0); - // Ready - disk.ready = TRUE; + SetReady(true); // Cache initialization ASSERT(!disk.dcache); @@ -860,95 +753,35 @@ void Disk::Open(const Filepath& path, BOOL /*attn*/) // Can read/write open Fileio fio; if (fio.Open(path, Fileio::ReadWrite)) { - // Write permission, not read only - disk.writep = false; - disk.readonly = false; + // Write permission fio.Close(); } else { - // Write protected, read only - disk.writep = true; - disk.readonly = true; + // Permanently write-protected + SetReadOnly(true); + SetProtectable(false); + SetProtected(false); } - // Not locked, not removed - disk.locked = false; - disk.removed = false; - - // Save path - diskpath = path; + SetLocked(false); + SetRemoved(false); } //--------------------------------------------------------------------------- // // Eject -// TODO This implemenation 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 Disk::Eject(bool force) { - // Can only be ejected if it is removable - if (!disk.removable) { - return false; + bool status = BlockDevice::Eject(force); + if (status) { + // Remove disk cache + disk.dcache->Save(); + delete disk.dcache; + disk.dcache = NULL; } - // If you're not ready, you don't need to eject - if (!disk.ready) { - return false; - } - - // Must be unlocked if there is no force flag - if (!force && disk.locked) { - return false; - } - - // Remove disk cache - disk.dcache->Save(); - delete disk.dcache; - disk.dcache = NULL; - - // Not ready, no attention - disk.ready = FALSE; - disk.writep = false; - disk.readonly = false; - disk.removed = true; - disk.attn = false; - - return true; -} - -//--------------------------------------------------------------------------- -// -// Write Protected -// -//--------------------------------------------------------------------------- -bool Disk::WriteP(bool writep) -{ - // be ready - if (!disk.ready) { - return false; - } - - // Read Only, protect only - if (disk.readonly && !writep) { - return false; - } - - // Write protect flag setting - disk.writep = writep; - - return true; -} - -//--------------------------------------------------------------------------- -// -// Get Path -// -//--------------------------------------------------------------------------- -void Disk::GetPath(Filepath& path) const -{ - path = diskpath; + return status; } //--------------------------------------------------------------------------- @@ -960,7 +793,7 @@ bool Disk::Flush() { // Do nothing if there's nothing cached if (!disk.dcache) { - return TRUE; + return true; } // Save cache @@ -975,30 +808,30 @@ bool Disk::Flush() BOOL Disk::CheckReady() { // Not ready if reset - if (disk.reset) { - disk.code = DISK_DEVRESET; - disk.reset = false; + if (IsReset()) { + SetStatusCode(STATUS_DEVRESET); + SetReset(false); LOGTRACE("%s Disk in reset", __PRETTY_FUNCTION__); return FALSE; } // Not ready if it needs attention - if (disk.attn) { - disk.code = DISK_ATTENTION; - disk.attn = false; + if (IsAttn()) { + SetStatusCode(STATUS_ATTENTION); + SetAttn(false); LOGTRACE("%s Disk in needs attention", __PRETTY_FUNCTION__); return FALSE; } // Return status if not ready - if (!disk.ready) { - disk.code = DISK_NOTREADY; + if (!IsReady()) { + SetStatusCode(STATUS_NOTREADY); LOGTRACE("%s Disk not ready", __PRETTY_FUNCTION__); return FALSE; } // Initialization with no error - disk.code = DISK_NOERROR; + SetStatusCode(STATUS_NOERROR); LOGTRACE("%s Disk is ready!", __PRETTY_FUNCTION__); return TRUE; @@ -1010,11 +843,10 @@ BOOL Disk::CheckReady() // *You need to be successful at all times // //--------------------------------------------------------------------------- -int Disk::Inquiry( - const DWORD* /*cdb*/, BYTE* /*buf*/, DWORD /*major*/, DWORD /*minor*/) +int Disk::Inquiry(const DWORD* /*cdb*/, BYTE* /*buf*/) { // default is INQUIRY failure - disk.code = DISK_INVALIDCMD; + SetStatusCode(STATUS_INVALIDCMD); return 0; } @@ -1030,9 +862,9 @@ int Disk::RequestSense(const DWORD *cdb, BYTE *buf) ASSERT(buf); // Return not ready only if there are no errors - if (disk.code == DISK_NOERROR) { - if (!disk.ready) { - disk.code = DISK_NOTREADY; + if (GetStatusCode() == STATUS_NOERROR) { + if (!IsReady()) { + SetStatusCode(STATUS_NOTREADY); } } @@ -1055,13 +887,13 @@ int Disk::RequestSense(const DWORD *cdb, BYTE *buf) // Current error buf[0] = 0x70; - buf[2] = (BYTE)(disk.code >> 16); + buf[2] = (BYTE)(GetStatusCode() >> 16); buf[7] = 10; - buf[12] = (BYTE)(disk.code >> 8); - buf[13] = (BYTE)disk.code; + buf[12] = (BYTE)(GetStatusCode() >> 8); + buf[13] = (BYTE)GetStatusCode(); // Clear the code - disk.code = 0x00; + SetStatusCode(STATUS_NOERROR); return size; } @@ -1076,11 +908,11 @@ int Disk::SelectCheck(const DWORD *cdb) { ASSERT(cdb); - // Error if save parameters are set instead of SCSIHD + // Error if save parameters are set instead of SCSIHD or SCSIRM if (!IsSCSI()) { // Error if save parameters are set if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; + SetStatusCode(STATUS_INVALIDCDB); return 0; } } @@ -1101,10 +933,10 @@ int Disk::SelectCheck10(const DWORD *cdb) { ASSERT(cdb); - // Error if save parameters are set instead of SCSIHD + // Error if save parameters are set instead of SCSIHD or SCSIRM if (!IsSCSI()) { if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; + SetStatusCode(STATUS_INVALIDCDB); return 0; } } @@ -1126,16 +958,15 @@ int Disk::SelectCheck10(const DWORD *cdb) // * Not affected by disk.code // //--------------------------------------------------------------------------- -BOOL Disk::ModeSelect( - const DWORD* /*cdb*/, const BYTE *buf, int length) +bool Disk::ModeSelect(const DWORD* /*cdb*/, const BYTE *buf, int length) { ASSERT(buf); ASSERT(length >= 0); // cannot be set - disk.code = DISK_INVALIDPRM; + SetStatusCode(STATUS_INVALIDPRM); - return FALSE; + return false; } //--------------------------------------------------------------------------- @@ -1146,10 +977,6 @@ BOOL Disk::ModeSelect( //--------------------------------------------------------------------------- int Disk::ModeSense(const DWORD *cdb, BYTE *buf) { - BOOL valid; - BOOL change; - int ret; - ASSERT(cdb); ASSERT(buf); ASSERT(cdb[0] == 0x1a); @@ -1160,19 +987,11 @@ int Disk::ModeSense(const DWORD *cdb, BYTE *buf) memset(buf, 0, length); // Get changeable flag - if ((cdb[2] & 0xc0) == 0x40) { - change = TRUE; - } else { - change = FALSE; - } + bool change = (cdb[2] & 0xc0) == 0x40; // Get page code (0x00 is valid from the beginning) int page = cdb[2] & 0x3f; - if (page == 0x00) { - valid = TRUE; - } else { - valid = FALSE; - } + bool valid = page == 0x00; // Basic information int size = 4; @@ -1183,7 +1002,7 @@ int Disk::ModeSense(const DWORD *cdb, BYTE *buf) } // DEVICE SPECIFIC PARAMETER - if (disk.writep) { + if (IsProtected()) { buf[2] = 0x80; } @@ -1193,7 +1012,7 @@ int Disk::ModeSense(const DWORD *cdb, BYTE *buf) buf[3] = 0x08; // Only if ready - if (disk.ready) { + if (IsReady()) { // Block descriptor (number of blocks) buf[5] = (BYTE)(disk.blocks >> 16); buf[6] = (BYTE)(disk.blocks >> 8); @@ -1213,40 +1032,40 @@ int Disk::ModeSense(const DWORD *cdb, BYTE *buf) // Page code 1(read-write error recovery) if ((page == 0x01) || (page == 0x3f)) { size += AddError(change, &buf[size]); - valid = TRUE; + valid = true; } // Page code 3(format device) if ((page == 0x03) || (page == 0x3f)) { size += AddFormat(change, &buf[size]); - valid = TRUE; + valid = true; } // Page code 4(drive parameter) if ((page == 0x04) || (page == 0x3f)) { size += AddDrive(change, &buf[size]); - valid = TRUE; + valid = true; } // Page code 6(optical) if (IsMo()) { if ((page == 0x06) || (page == 0x3f)) { size += AddOpt(change, &buf[size]); - valid = TRUE; + valid = true; } } // Page code 8(caching) if ((page == 0x08) || (page == 0x3f)) { size += AddCache(change, &buf[size]); - valid = TRUE; + valid = true; } // Page code 13(CD-ROM) if (IsCdRom()) { if ((page == 0x0d) || (page == 0x3f)) { size += AddCDROM(change, &buf[size]); - valid = TRUE; + valid = true; } } @@ -1254,15 +1073,15 @@ int Disk::ModeSense(const DWORD *cdb, BYTE *buf) if (IsCdRom()) { if ((page == 0x0e) || (page == 0x3f)) { size += AddCDDA(change, &buf[size]); - valid = TRUE; + valid = true; } } // Page (vendor special) - ret = AddVendor(page, change, &buf[size]); + int ret = AddVendor(page, change, &buf[size]); if (ret > 0) { size += ret; - valid = TRUE; + valid = true; } // final setting of mode data length @@ -1270,12 +1089,12 @@ int Disk::ModeSense(const DWORD *cdb, BYTE *buf) // Unsupported page if (!valid) { - disk.code = DISK_INVALIDCDB; + SetStatusCode(STATUS_INVALIDCDB); return 0; } // MODE SENSE success - disk.code = DISK_NOERROR; + SetStatusCode(STATUS_NOERROR); return length; } @@ -1287,8 +1106,6 @@ int Disk::ModeSense(const DWORD *cdb, BYTE *buf) //--------------------------------------------------------------------------- int Disk::ModeSense10(const DWORD *cdb, BYTE *buf) { - BOOL valid; - BOOL change; int ret; ASSERT(cdb); @@ -1306,23 +1123,15 @@ int Disk::ModeSense10(const DWORD *cdb, BYTE *buf) memset(buf, 0, length); // Get changeable flag - if ((cdb[2] & 0xc0) == 0x40) { - change = TRUE; - } else { - change = FALSE; - } + bool change = (cdb[2] & 0xc0) == 0x40; // Get page code (0x00 is valid from the beginning) int page = cdb[2] & 0x3f; - if (page == 0x00) { - valid = TRUE; - } else { - valid = FALSE; - } + bool valid = page == 0x00; // Basic Information int size = 4; - if (disk.writep) { + if (IsProtected()) { buf[2] = 0x80; } @@ -1332,7 +1141,7 @@ int Disk::ModeSense10(const DWORD *cdb, BYTE *buf) buf[3] = 0x08; // Only if ready - if (disk.ready) { + if (IsReady()) { // Block descriptor (number of blocks) buf[5] = (BYTE)(disk.blocks >> 16); buf[6] = (BYTE)(disk.blocks >> 8); @@ -1352,40 +1161,40 @@ int Disk::ModeSense10(const DWORD *cdb, BYTE *buf) // Page code 1(read-write error recovery) if ((page == 0x01) || (page == 0x3f)) { size += AddError(change, &buf[size]); - valid = TRUE; + valid = true; } // Page code 3(format device) if ((page == 0x03) || (page == 0x3f)) { size += AddFormat(change, &buf[size]); - valid = TRUE; + valid = true; } // Page code 4(drive parameter) if ((page == 0x04) || (page == 0x3f)) { size += AddDrive(change, &buf[size]); - valid = TRUE; + valid = true; } // ペPage code 6(optical) if (IsMo()) { if ((page == 0x06) || (page == 0x3f)) { size += AddOpt(change, &buf[size]); - valid = TRUE; + valid = true; } } // Page code 8(caching) if ((page == 0x08) || (page == 0x3f)) { size += AddCache(change, &buf[size]); - valid = TRUE; + valid = true; } // Page code 13(CD-ROM) if (IsCdRom()) { if ((page == 0x0d) || (page == 0x3f)) { size += AddCDROM(change, &buf[size]); - valid = TRUE; + valid = true; } } @@ -1393,7 +1202,7 @@ int Disk::ModeSense10(const DWORD *cdb, BYTE *buf) if (IsCdRom()) { if ((page == 0x0e) || (page == 0x3f)) { size += AddCDDA(change, &buf[size]); - valid = TRUE; + valid = true; } } @@ -1401,7 +1210,7 @@ int Disk::ModeSense10(const DWORD *cdb, BYTE *buf) ret = AddVendor(page, change, &buf[size]); if (ret > 0) { size += ret; - valid = TRUE; + valid = true; } // final setting of mode data length @@ -1409,12 +1218,12 @@ int Disk::ModeSense10(const DWORD *cdb, BYTE *buf) // Unsupported page if (!valid) { - disk.code = DISK_INVALIDCDB; + SetStatusCode(STATUS_INVALIDCDB); return 0; } // MODE SENSE success - disk.code = DISK_NOERROR; + SetStatusCode(STATUS_NOERROR); return length; } @@ -1423,7 +1232,7 @@ int Disk::ModeSense10(const DWORD *cdb, BYTE *buf) // Add error page // //--------------------------------------------------------------------------- -int Disk::AddError(BOOL change, BYTE *buf) +int Disk::AddError(bool change, BYTE *buf) { ASSERT(buf); @@ -1445,7 +1254,7 @@ int Disk::AddError(BOOL change, BYTE *buf) // Add format page // //--------------------------------------------------------------------------- -int Disk::AddFormat(BOOL change, BYTE *buf) +int Disk::AddFormat(bool change, BYTE *buf) { int size; @@ -1463,7 +1272,7 @@ int Disk::AddFormat(BOOL change, BYTE *buf) return 24; } - if (disk.ready) { + if (IsReady()) { // Set the number of tracks in one zone to 8 (TODO) buf[0x3] = 0x08; @@ -1478,7 +1287,7 @@ int Disk::AddFormat(BOOL change, BYTE *buf) } // Set removable attribute - if (disk.removable) { + if (IsRemovable()) { buf[20] = 0x20; } @@ -1490,7 +1299,7 @@ int Disk::AddFormat(BOOL change, BYTE *buf) // Add drive page // //--------------------------------------------------------------------------- -int Disk::AddDrive(BOOL change, BYTE *buf) +int Disk::AddDrive(bool change, BYTE *buf) { ASSERT(buf); @@ -1503,7 +1312,7 @@ int Disk::AddDrive(BOOL change, BYTE *buf) return 24; } - if (disk.ready) { + if (IsReady()) { // Set the number of cylinders (total number of blocks // divided by 25 sectors/track and 8 heads) DWORD cylinder = disk.blocks; @@ -1525,7 +1334,7 @@ int Disk::AddDrive(BOOL change, BYTE *buf) // Add option // //--------------------------------------------------------------------------- -int Disk::AddOpt(BOOL change, BYTE *buf) +int Disk::AddOpt(bool change, BYTE *buf) { ASSERT(buf); @@ -1547,7 +1356,7 @@ int Disk::AddOpt(BOOL change, BYTE *buf) // Add Cache Page // //--------------------------------------------------------------------------- -int Disk::AddCache(BOOL change, BYTE *buf) +int Disk::AddCache(bool change, BYTE *buf) { ASSERT(buf); @@ -1569,7 +1378,7 @@ int Disk::AddCache(BOOL change, BYTE *buf) // Add CDROM Page // //--------------------------------------------------------------------------- -int Disk::AddCDROM(BOOL change, BYTE *buf) +int Disk::AddCDROM(bool change, BYTE *buf) { ASSERT(buf); @@ -1597,7 +1406,7 @@ int Disk::AddCDROM(BOOL change, BYTE *buf) // CD-DAページ追加 // //--------------------------------------------------------------------------- -int Disk::AddCDDA(BOOL change, BYTE *buf) +int Disk::AddCDDA(bool change, BYTE *buf) { ASSERT(buf); @@ -1620,7 +1429,7 @@ int Disk::AddCDDA(BOOL change, BYTE *buf) // Add special vendor page // //--------------------------------------------------------------------------- -int Disk::AddVendor(int /*page*/, BOOL /*change*/, BYTE *buf) +int Disk::AddVendor(int /*page*/, bool /*change*/, BYTE *buf) { ASSERT(buf); @@ -1664,7 +1473,7 @@ int Disk::ReadDefectData10(const DWORD *cdb, BYTE *buf) buf[11] = 0xff; // no list - disk.code = DISK_NODEFECT; + SetStatusCode(STATUS_NODEFECT); return 4; } @@ -1679,15 +1488,10 @@ int Disk::ReadDefectData10(const DWORD *cdb, BYTE *buf) // TEST UNIT READY // //--------------------------------------------------------------------------- -BOOL Disk::TestUnitReady(const DWORD* /*cdb*/) +bool Disk::TestUnitReady(const DWORD* /*cdb*/) { // Status check - if (!CheckReady()) { - return FALSE; - } - - // TEST UNIT READY Success - return TRUE; + return CheckReady(); } //--------------------------------------------------------------------------- @@ -1695,15 +1499,10 @@ BOOL Disk::TestUnitReady(const DWORD* /*cdb*/) // REZERO UNIT // //--------------------------------------------------------------------------- -BOOL Disk::Rezero(const DWORD* /*cdb*/) +bool Disk::Rezero(const DWORD* /*cdb*/) { // Status check - if (!CheckReady()) { - return FALSE; - } - - // REZERO Success - return TRUE; + return CheckReady(); } //--------------------------------------------------------------------------- @@ -1712,21 +1511,21 @@ BOOL Disk::Rezero(const DWORD* /*cdb*/) // *Opcode $06 for SASI, Opcode $04 for SCSI // //--------------------------------------------------------------------------- -BOOL Disk::Format(const DWORD *cdb) +bool Disk::Format(const DWORD *cdb) { // Status check if (!CheckReady()) { - return FALSE; + return false; } // FMTDATA=1 is not supported (but OK if there is no DEFECT LIST) if ((cdb[1] & 0x10) != 0 && cdb[4] != 0) { - disk.code = DISK_INVALIDCDB; - return FALSE; + SetStatusCode(STATUS_INVALIDCDB); + return false; } // FORMAT Success - return TRUE; + return true; } //--------------------------------------------------------------------------- @@ -1734,15 +1533,10 @@ BOOL Disk::Format(const DWORD *cdb) // REASSIGN BLOCKS // //--------------------------------------------------------------------------- -BOOL Disk::Reassign(const DWORD* /*cdb*/) +bool Disk::Reassign(const DWORD* /*cdb*/) { // Status check - if (!CheckReady()) { - return FALSE; - } - - // REASSIGN BLOCKS Success - return TRUE; + return CheckReady(); } //--------------------------------------------------------------------------- @@ -1754,6 +1548,8 @@ int Disk::Read(const DWORD *cdb, BYTE *buf, DWORD block) { ASSERT(buf); + LOGTRACE("%s", __PRETTY_FUNCTION__); + // Status check if (!CheckReady()) { return 0; @@ -1761,13 +1557,13 @@ int Disk::Read(const DWORD *cdb, BYTE *buf, DWORD block) // Error if the total number of blocks is exceeded if (block >= disk.blocks) { - disk.code = DISK_INVALIDLBA; + SetStatusCode(STATUS_INVALIDLBA); return 0; } // leave it to the cache if (!disk.dcache->Read(buf, block)) { - disk.code = DISK_READFAULT; + SetStatusCode(STATUS_READFAULT); return 0; } @@ -1784,17 +1580,20 @@ int Disk::WriteCheck(DWORD block) { // Status check if (!CheckReady()) { + LOGDEBUG("WriteCheck failed (not ready)"); return 0; } // Error if the total number of blocks is exceeded if (block >= disk.blocks) { + LOGDEBUG("WriteCheck failed (capacity exceeded)"); return 0; } // Error if write protected - if (disk.writep) { - disk.code = DISK_WRITEPROTECT; + if (IsProtected()) { + LOGDEBUG("WriteCheck failed (protected)"); + SetStatusCode(STATUS_WRITEPROTECT); return 0; } @@ -1807,38 +1606,39 @@ int Disk::WriteCheck(DWORD block) // WRITE // //--------------------------------------------------------------------------- -BOOL Disk::Write(const DWORD *cdb, const BYTE *buf, DWORD block) +bool Disk::Write(const DWORD *cdb, const BYTE *buf, DWORD block) { ASSERT(buf); LOGTRACE("%s", __PRETTY_FUNCTION__); + // Error if not ready - if (!disk.ready) { - disk.code = DISK_NOTREADY; - return FALSE; + if (!IsReady()) { + SetStatusCode(STATUS_NOTREADY); + return false; } // Error if the total number of blocks is exceeded if (block >= disk.blocks) { - disk.code = DISK_INVALIDLBA; - return FALSE; + SetStatusCode(STATUS_INVALIDLBA); + return false; } // Error if write protected - if (disk.writep) { - disk.code = DISK_WRITEPROTECT; - return FALSE; + if (IsProtected()) { + SetStatusCode(STATUS_WRITEPROTECT); + return false; } // Leave it to the cache if (!disk.dcache->Write(buf, block)) { - disk.code = DISK_WRITEFAULT; - return FALSE; + SetStatusCode(STATUS_WRITEFAULT); + return false; } // Success - disk.code = DISK_NOERROR; - return TRUE; + SetStatusCode(STATUS_NOERROR); + return true; } //--------------------------------------------------------------------------- @@ -1847,15 +1647,10 @@ BOOL Disk::Write(const DWORD *cdb, const BYTE *buf, DWORD block) // *Does not check LBA (SASI IOCS) // //--------------------------------------------------------------------------- -BOOL Disk::Seek(const DWORD* /*cdb*/) +bool Disk::Seek(const DWORD* /*cdb*/) { // Status check - if (!CheckReady()) { - return FALSE; - } - - // SEEK Success - return TRUE; + return CheckReady(); } //--------------------------------------------------------------------------- @@ -1863,15 +1658,10 @@ BOOL Disk::Seek(const DWORD* /*cdb*/) // ASSIGN // //--------------------------------------------------------------------------- -BOOL Disk::Assign(const DWORD* /*cdb*/) +bool Disk::Assign(const DWORD* /*cdb*/) { // Status check - if (!CheckReady()) { - return FALSE; - } - - // Success - return TRUE; + return CheckReady(); } //--------------------------------------------------------------------------- @@ -1879,15 +1669,10 @@ BOOL Disk::Assign(const DWORD* /*cdb*/) // SPECIFY // //--------------------------------------------------------------------------- -BOOL Disk::Specify(const DWORD* /*cdb*/) +bool Disk::Specify(const DWORD* /*cdb*/) { // Status check - if (!CheckReady()) { - return FALSE; - } - - // Success - return TRUE; + return CheckReady(); } //--------------------------------------------------------------------------- @@ -1895,17 +1680,17 @@ BOOL Disk::Specify(const DWORD* /*cdb*/) // START STOP UNIT // //--------------------------------------------------------------------------- -BOOL Disk::StartStop(const DWORD *cdb) +bool Disk::StartStop(const DWORD *cdb) { ASSERT(cdb); ASSERT(cdb[0] == 0x1b); // Look at the eject bit and eject if necessary if (cdb[4] & 0x02) { - if (disk.locked) { + if (IsLocked()) { // Cannot be ejected because it is locked - disk.code = DISK_PREVENT; - return FALSE; + SetStatusCode(STATUS_PREVENT); + return false; } // Eject @@ -1913,8 +1698,8 @@ BOOL Disk::StartStop(const DWORD *cdb) } // OK - disk.code = DISK_NOERROR; - return TRUE; + SetStatusCode(STATUS_NOERROR); + return true; } //--------------------------------------------------------------------------- @@ -1922,26 +1707,26 @@ BOOL Disk::StartStop(const DWORD *cdb) // SEND DIAGNOSTIC // //--------------------------------------------------------------------------- -BOOL Disk::SendDiag(const DWORD *cdb) +bool Disk::SendDiag(const DWORD *cdb) { ASSERT(cdb); ASSERT(cdb[0] == 0x1d); // Do not support PF bit if (cdb[1] & 0x10) { - disk.code = DISK_INVALIDCDB; - return FALSE; + SetStatusCode(STATUS_INVALIDCDB); + return false; } // Do not support parameter list if ((cdb[3] != 0) || (cdb[4] != 0)) { - disk.code = DISK_INVALIDCDB; - return FALSE; + SetStatusCode(STATUS_INVALIDCDB); + return false; } // Always successful - disk.code = DISK_NOERROR; - return TRUE; + SetStatusCode(STATUS_NOERROR); + return true; } //--------------------------------------------------------------------------- @@ -1949,25 +1734,21 @@ BOOL Disk::SendDiag(const DWORD *cdb) // PREVENT/ALLOW MEDIUM REMOVAL // //--------------------------------------------------------------------------- -BOOL Disk::Removal(const DWORD *cdb) +bool Disk::Removal(const DWORD *cdb) { ASSERT(cdb); ASSERT(cdb[0] == 0x1e); // Status check if (!CheckReady()) { - return FALSE; + return false; } // Set Lock flag - if (cdb[4] & 0x01) { - disk.locked = true; - } else { - disk.locked = false; - } + SetLocked(cdb[4] & 0x01); // REMOVAL Success - return TRUE; + return true; } //--------------------------------------------------------------------------- @@ -1975,11 +1756,8 @@ BOOL Disk::Removal(const DWORD *cdb) // READ CAPACITY // //--------------------------------------------------------------------------- -int Disk::ReadCapacity(const DWORD* /*cdb*/, BYTE *buf) +int Disk::ReadCapacity10(const DWORD* /*cdb*/, BYTE *buf) { - DWORD blocks; - DWORD length; - ASSERT(buf); // Buffer clear @@ -1997,14 +1775,14 @@ int Disk::ReadCapacity(const DWORD* /*cdb*/, BYTE *buf) } // Create end of logical block address (disk.blocks-1) - blocks = disk.blocks - 1; + DWORD blocks = disk.blocks - 1; buf[0] = (BYTE)(blocks >> 24); buf[1] = (BYTE)(blocks >> 16); buf[2] = (BYTE)(blocks >> 8); buf[3] = (BYTE)blocks; // Create block length (1 << disk.size) - length = 1 << disk.size; + DWORD length = 1 << disk.size; buf[4] = (BYTE)(length >> 24); buf[5] = (BYTE)(length >> 16); buf[6] = (BYTE)(length >> 8); @@ -2014,12 +1792,77 @@ int Disk::ReadCapacity(const DWORD* /*cdb*/, BYTE *buf) return 8; } +int Disk::ReadCapacity16(const DWORD* /*cdb*/, BYTE *buf) +{ + ASSERT(buf); + + // Buffer clear + memset(buf, 0, 14); + + // Status check + if (!CheckReady()) { + return 0; + } + + if (disk.blocks <= 0) { + LOGWARN("%s Capacity not available, medium may not be present", __PRETTY_FUNCTION__); + + return -1; + } + + // Create end of logical block address (disk.blocks-1) + // TODO blocks should be a 64 bit value in order to support higher capacities + DWORD blocks = disk.blocks - 1; + buf[4] = (BYTE)(blocks >> 24); + buf[5] = (BYTE)(blocks >> 16); + buf[6] = (BYTE)(blocks >> 8); + buf[7] = (BYTE)blocks; + + // Create block length (1 << disk.size) + DWORD length = 1 << disk.size; + buf[8] = (BYTE)(length >> 24); + buf[9] = (BYTE)(length >> 16); + buf[10] = (BYTE)(length >> 8); + buf[11] = (BYTE)length; + + // Logical blocks per physical block: not reported (1 or more) + buf[13] = 0; + + // return the size + return 14; +} + +//--------------------------------------------------------------------------- +// +// REPORT LUNS +// +//--------------------------------------------------------------------------- +int Disk::ReportLuns(const DWORD* /*cdb*/, BYTE *buf) +{ + ASSERT(buf); + + // Buffer clear + memset(buf, 0, 16); + + // Status check + if (!CheckReady()) { + return 0; + } + + // LUN list length + buf[3] = 8; + + // As long as there is no proper support for more than one SCSI LUN no other fields must be set => 1 LUN + + return 16; +} + //--------------------------------------------------------------------------- // // VERIFY // //--------------------------------------------------------------------------- -BOOL Disk::Verify(const DWORD *cdb) +bool Disk::Verify(const DWORD *cdb) { ASSERT(cdb); ASSERT(cdb[0] == 0x2f); @@ -2038,17 +1881,17 @@ BOOL Disk::Verify(const DWORD *cdb) // Status check if (!CheckReady()) { - return 0; + return false; } // Parameter check if (disk.blocks < (record + blocks)) { - disk.code = DISK_INVALIDLBA; - return FALSE; + SetStatusCode(STATUS_INVALIDLBA); + return false; } // Success - return TRUE; + return true; } //--------------------------------------------------------------------------- @@ -2063,8 +1906,8 @@ int Disk::ReadToc(const DWORD *cdb, BYTE *buf) ASSERT(buf); // This command is not supported - disk.code = DISK_INVALIDCMD; - return FALSE; + SetStatusCode(STATUS_INVALIDCMD); + return 0; } //--------------------------------------------------------------------------- @@ -2072,14 +1915,14 @@ int Disk::ReadToc(const DWORD *cdb, BYTE *buf) // PLAY AUDIO // //--------------------------------------------------------------------------- -BOOL Disk::PlayAudio(const DWORD *cdb) +bool Disk::PlayAudio(const DWORD *cdb) { ASSERT(cdb); ASSERT(cdb[0] == 0x45); // This command is not supported - disk.code = DISK_INVALIDCMD; - return FALSE; + SetStatusCode(STATUS_INVALIDCMD); + return false; } //--------------------------------------------------------------------------- @@ -2087,14 +1930,14 @@ BOOL Disk::PlayAudio(const DWORD *cdb) // PLAY AUDIO MSF // //--------------------------------------------------------------------------- -BOOL Disk::PlayAudioMSF(const DWORD *cdb) +bool Disk::PlayAudioMSF(const DWORD *cdb) { ASSERT(cdb); ASSERT(cdb[0] == 0x47); // This command is not supported - disk.code = DISK_INVALIDCMD; - return FALSE; + SetStatusCode(STATUS_INVALIDCMD); + return false; } //--------------------------------------------------------------------------- @@ -2102,13 +1945,32 @@ BOOL Disk::PlayAudioMSF(const DWORD *cdb) // PLAY AUDIO TRACK // //--------------------------------------------------------------------------- -BOOL Disk::PlayAudioTrack(const DWORD *cdb) +bool Disk::PlayAudioTrack(const DWORD *cdb) { ASSERT(cdb); ASSERT(cdb[0] == 0x48); // This command is not supported - disk.code = DISK_INVALIDCMD; - return FALSE; + SetStatusCode(STATUS_INVALIDCMD); + return false; } +int Disk::GetSectorSize() const +{ + return disk.size; +} + +void Disk::SetSectorSize(int size) +{ + disk.size = size; +} + +DWORD Disk::GetBlockCount() const +{ + return disk.blocks; +} + +void Disk::SetBlockCount(DWORD blocks) +{ + disk.blocks = blocks; +} diff --git a/src/raspberrypi/devices/disk.h b/src/raspberrypi/devices/disk.h index 64ed1ef3..8cb755e2 100644 --- a/src/raspberrypi/devices/disk.h +++ b/src/raspberrypi/devices/disk.h @@ -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 -//--------------------------------------------------------------------------- -// -// 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 }; diff --git a/src/raspberrypi/devices/file_support.h b/src/raspberrypi/devices/file_support.h new file mode 100644 index 00000000..c7d30ffa --- /dev/null +++ b/src/raspberrypi/devices/file_support.h @@ -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; +}; diff --git a/src/raspberrypi/devices/primary_device.h b/src/raspberrypi/devices/primary_device.h new file mode 100644 index 00000000..66383113 --- /dev/null +++ b/src/raspberrypi/devices/primary_device.h @@ -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; +}; diff --git a/src/raspberrypi/devices/sasihd.cpp b/src/raspberrypi/devices/sasihd.cpp index c0ce22ef..a2ca9929 100644 --- a/src/raspberrypi/devices/sasihd.cpp +++ b/src/raspberrypi/devices/sasihd.cpp @@ -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; } diff --git a/src/raspberrypi/devices/sasihd.h b/src/raspberrypi/devices/sasihd.h index 4697b1c1..03e12ea0 100644 --- a/src/raspberrypi/devices/sasihd.h +++ b/src/raspberrypi/devices/sasihd.h @@ -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 diff --git a/src/raspberrypi/devices/scsi_daynaport.cpp b/src/raspberrypi/devices/scsi_daynaport.cpp index 31f9cf87..44de1a4d 100644 --- a/src/raspberrypi/devices/scsi_daynaport.cpp +++ b/src/raspberrypi/devices/scsi_daynaport.cpp @@ -28,6 +28,7 @@ //--------------------------------------------------------------------------- #include "scsi_daynaport.h" +#include //=========================================================================== // @@ -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; } //--------------------------------------------------------------------------- diff --git a/src/raspberrypi/devices/scsi_daynaport.h b/src/raspberrypi/devices/scsi_daynaport.h index 1a75a8ee..11c913e1 100644 --- a/src/raspberrypi/devices/scsi_daynaport.h +++ b/src/raspberrypi/devices/scsi_daynaport.h @@ -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) diff --git a/src/raspberrypi/devices/scsi_host_bridge.cpp b/src/raspberrypi/devices/scsi_host_bridge.cpp index d606255d..81443c81 100644 --- a/src/raspberrypi/devices/scsi_host_bridge.cpp +++ b/src/raspberrypi/devices/scsi_host_bridge.cpp @@ -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; } //--------------------------------------------------------------------------- diff --git a/src/raspberrypi/devices/scsi_host_bridge.h b/src/raspberrypi/devices/scsi_host_bridge.h index b722c896..88382038 100644 --- a/src/raspberrypi/devices/scsi_host_bridge.h +++ b/src/raspberrypi/devices/scsi_host_bridge.h @@ -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 diff --git a/src/raspberrypi/devices/scsicd.cpp b/src/raspberrypi/devices/scsicd.cpp index 007107a8..d76436b2 100644 --- a/src/raspberrypi/devices/scsicd.cpp +++ b/src/raspberrypi/devices/scsicd.cpp @@ -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; } diff --git a/src/raspberrypi/devices/scsicd.h b/src/raspberrypi/devices/scsicd.h index 9c3ef065..ee8c8bd7 100644 --- a/src/raspberrypi/devices/scsicd.h +++ b/src/raspberrypi/devices/scsicd.h @@ -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 }; diff --git a/src/raspberrypi/devices/scsihd.cpp b/src/raspberrypi/devices/scsihd.cpp index 15bd225e..5e1380bf 100644 --- a/src/raspberrypi/devices/scsihd.cpp +++ b/src/raspberrypi/devices/scsihd.cpp @@ -17,6 +17,7 @@ #include "xm6.h" #include "fileio.h" #include "exceptions.h" +#include //=========================================================================== // @@ -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; } diff --git a/src/raspberrypi/devices/scsihd.h b/src/raspberrypi/devices/scsihd.h index d4e19983..7c003325 100644 --- a/src/raspberrypi/devices/scsihd.h +++ b/src/raspberrypi/devices/scsihd.h @@ -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 }; diff --git a/src/raspberrypi/devices/scsihd_apple.cpp b/src/raspberrypi/devices/scsihd_apple.cpp deleted file mode 100644 index 5471cd04..00000000 --- a/src/raspberrypi/devices/scsihd_apple.cpp +++ /dev/null @@ -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; -} diff --git a/src/raspberrypi/devices/scsihd_apple.h b/src/raspberrypi/devices/scsihd_apple.h deleted file mode 100644 index c35e4b03..00000000 --- a/src/raspberrypi/devices/scsihd_apple.h +++ /dev/null @@ -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 -}; \ No newline at end of file diff --git a/src/raspberrypi/devices/scsihd_nec.cpp b/src/raspberrypi/devices/scsihd_nec.cpp index 14fb06e9..497a2cbf 100644 --- a/src/raspberrypi/devices/scsihd_nec.cpp +++ b/src/raspberrypi/devices/scsihd_nec.cpp @@ -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); diff --git a/src/raspberrypi/devices/scsihd_nec.h b/src/raspberrypi/devices/scsihd_nec.h index f6070c5d..58671ea7 100644 --- a/src/raspberrypi/devices/scsihd_nec.h +++ b/src/raspberrypi/devices/scsihd_nec.h @@ -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 }; diff --git a/src/raspberrypi/devices/scsimo.cpp b/src/raspberrypi/devices/scsimo.cpp index df2efa9d..0cc60917 100644 --- a/src/raspberrypi/devices/scsimo.cpp +++ b/src/raspberrypi/devices/scsimo.cpp @@ -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); diff --git a/src/raspberrypi/devices/scsimo.h b/src/raspberrypi/devices/scsimo.h index 9dbf20c3..a0b87af0 100644 --- a/src/raspberrypi/devices/scsimo.h +++ b/src/raspberrypi/devices/scsimo.h @@ -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 diff --git a/src/raspberrypi/exceptions.h b/src/raspberrypi/exceptions.h index 783f7ff2..efeaa78c 100644 --- a/src/raspberrypi/exceptions.h +++ b/src/raspberrypi/exceptions.h @@ -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 #include 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; + } +}; diff --git a/src/raspberrypi/fileio.cpp b/src/raspberrypi/fileio.cpp index 8e281c9b..fc2a304a 100644 --- a/src/raspberrypi/fileio.cpp +++ b/src/raspberrypi/fileio.cpp @@ -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); diff --git a/src/raspberrypi/fileio.h b/src/raspberrypi/fileio.h index 6b83103c..90a9d19d 100644 --- a/src/raspberrypi/fileio.h +++ b/src/raspberrypi/fileio.h @@ -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(); // クローズ diff --git a/src/raspberrypi/gpiobus.cpp b/src/raspberrypi/gpiobus.cpp index d17e24d5..37adf653 100644 --- a/src/raspberrypi/gpiobus.cpp +++ b/src/raspberrypi/gpiobus.cpp @@ -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; + } +} //--------------------------------------------------------------------------- // diff --git a/src/raspberrypi/gpiobus.h b/src/raspberrypi/gpiobus.h index dc6c56fc..bc56a6eb 100644 --- a/src/raspberrypi/gpiobus.h +++ b/src/raspberrypi/gpiobus.h @@ -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 diff --git a/src/raspberrypi/log.h b/src/raspberrypi/log.h index 9288d55c..7347376d 100644 --- a/src/raspberrypi/log.h +++ b/src/raspberrypi/log.h @@ -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 diff --git a/src/raspberrypi/os.h b/src/raspberrypi/os.h index b4f694a6..52615e5d 100644 --- a/src/raspberrypi/os.h +++ b/src/raspberrypi/os.h @@ -55,6 +55,7 @@ #include #include +#include #include #include #include @@ -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 diff --git a/src/raspberrypi/protobuf_util.cpp b/src/raspberrypi/protobuf_util.cpp new file mode 100644 index 00000000..97a7d575 --- /dev/null +++ b/src/raspberrypi/protobuf_util.cpp @@ -0,0 +1,80 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include +#include +#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; +} diff --git a/src/raspberrypi/protobuf_util.h b/src/raspberrypi/protobuf_util.h new file mode 100644 index 00000000..8854614f --- /dev/null +++ b/src/raspberrypi/protobuf_util.h @@ -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); diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index fc713aae..a3c4c5aa 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -13,19 +13,15 @@ #include "xm6.h" #include "filepath.h" #include "fileio.h" -#include "devices/disk.h" -#include "devices/sasihd.h" -#include "devices/scsihd.h" -#include "devices/scsihd_apple.h" -#include "devices/scsihd_nec.h" -#include "devices/scsicd.h" -#include "devices/scsimo.h" -#include "devices/scsi_host_bridge.h" -#include "devices/scsi_daynaport.h" #include "controllers/scsidev_ctrl.h" #include "controllers/sasidev_ctrl.h" +#include "devices/device_factory.h" +#include "devices/device.h" +#include "devices/disk.h" +#include "devices/file_support.h" #include "gpiobus.h" #include "exceptions.h" +#include "protobuf_util.h" #include "rascsi_version.h" #include "rasutil.h" #include "rascsi_interface.pb.h" @@ -35,7 +31,7 @@ #include #include #include -#include +#include #include using namespace std; @@ -56,18 +52,18 @@ using namespace rascsi_interface; // Variable declarations // //--------------------------------------------------------------------------- -static volatile BOOL running; // Running flag -static volatile BOOL active; // Processing flag -SASIDEV *ctrl[CtrlMax]; // Controller -Disk *disk[CtrlMax * UnitNum]; // Disk +static volatile bool running; // Running flag +static volatile bool active; // Processing flag +vector controllers(CtrlMax); // Controllers +vector devices(CtrlMax * UnitNum); // Disks GPIOBUS *bus; // GPIO Bus int monsocket; // Monitor Socket pthread_t monthread; // Monitor Thread pthread_mutex_t ctrl_mutex; // Semaphore for the ctrl array static void *MonThread(void *param); -list available_log_levels; +vector available_log_levels; string current_log_level; // Some versions of spdlog do not support get_log_level() -string default_image_folder = "/home/pi/images"; +string default_image_folder; set files_in_use; //--------------------------------------------------------------------------- @@ -78,7 +74,7 @@ set files_in_use; void KillHandler(int sig) { // Stop instruction - running = FALSE; + running = false; } //--------------------------------------------------------------------------- @@ -107,16 +103,17 @@ void Banner(int argc, char* argv[]) FPRT(stdout," n is X68000 SASI HD number(0-15).\n"); FPRT(stdout," FILE is disk image file, \"daynaport\", or \"bridge\".\n\n"); FPRT(stdout," Image type is detected based on file extension.\n"); - FPRT(stdout," hdf : SASI HD image(XM6 SASI HD image)\n"); - FPRT(stdout," hds : SCSI HD image(XM6 SCSI HD image)\n"); - FPRT(stdout," hdn : SCSI HD image(NEC GENUINE)\n"); - FPRT(stdout," hdi : SCSI HD image(Anex86 HD image)\n"); - FPRT(stdout," nhd : SCSI HD image(T98Next HD image)\n"); - FPRT(stdout," hda : SCSI HD image(APPLE GENUINE)\n"); - FPRT(stdout," mos : SCSI MO image(XM6 SCSI MO image)\n"); - FPRT(stdout," iso : SCSI CD image(ISO 9660 image)\n"); + FPRT(stdout," hdf : SASI HD image (XM6 SASI HD image)\n"); + FPRT(stdout," hds : SCSI HD image (Non-removable generic SCSI HD image)\n"); + FPRT(stdout," hdr : SCSI HD image (Removable generic SCSI HD image)\n"); + FPRT(stdout," hdn : SCSI HD image (NEC GENUINE)\n"); + FPRT(stdout," hdi : SCSI HD image (Anex86 HD image)\n"); + FPRT(stdout," nhd : SCSI HD image (T98Next HD image)\n"); + FPRT(stdout," hda : SCSI HD image (APPLE GENUINE)\n"); + FPRT(stdout," mos : SCSI MO image (XM6 SCSI MO image)\n"); + FPRT(stdout," iso : SCSI CD image (ISO 9660 image)\n"); - exit(0); + exit(EXIT_SUCCESS); } } @@ -126,10 +123,10 @@ void Banner(int argc, char* argv[]) // //--------------------------------------------------------------------------- -BOOL InitService(int port) +bool InitService(int port) { int result = pthread_mutex_init(&ctrl_mutex,NULL); - if(result != EXIT_SUCCESS){ + if (result != EXIT_SUCCESS){ LOGERROR("Unable to create a mutex. Err code: %d", result); return FALSE; } @@ -144,11 +141,12 @@ BOOL InitService(int port) // allow address reuse int yes = 1; - if (setsockopt( - monsocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0){ + if (setsockopt(monsocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { return FALSE; } + signal(SIGPIPE, SIG_IGN); + // Bind if (bind(monsocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) { @@ -170,8 +168,8 @@ BOOL InitService(int port) return FALSE; } - running = FALSE; - active = FALSE; + running = false; + active = false; return true; } @@ -192,19 +190,6 @@ bool InitBus() return true; } -void InitDisks() -{ - // Controller initialization - for (int i = 0; i < CtrlMax; i++) { - ctrl[i] = NULL; - } - - // Disk Initialization - for (int i = 0; i < CtrlMax; i++) { - disk[i] = NULL; - } -} - //--------------------------------------------------------------------------- // // Cleanup @@ -213,18 +198,18 @@ void InitDisks() void Cleanup() { // Delete the disks - for (int i = 0; i < CtrlMax * UnitNum; i++) { - if (disk[i]) { - delete disk[i]; - disk[i] = NULL; + for (auto it = devices.begin(); it != devices.end(); ++it) { + if (*it) { + delete *it; + *it = NULL; } } // Delete the Controllers - for (int i = 0; i < CtrlMax; i++) { - if (ctrl[i]) { - delete ctrl[i]; - ctrl[i] = NULL; + for (auto it = controllers.begin(); it != controllers.end(); ++it) { + if (*it) { + delete *it; + *it = NULL; } } @@ -252,9 +237,9 @@ void Cleanup() void Reset() { // Reset all of the controllers - for (int i = 0; i < CtrlMax; i++) { - if (ctrl[i]) { - ctrl[i]->Reset(); + for (auto it = controllers.begin(); it != controllers.end(); ++it) { + if (*it) { + (*it)->Reset(); } } @@ -267,51 +252,61 @@ void Reset() // Get the list of attached devices // //--------------------------------------------------------------------------- -const PbDevices GetDevices() { - PbDevices devices; - for (int i = 0; i < CtrlMax * UnitNum; i++) { +void GetImageFile(PbImageFile *image_file, const string& filename) +{ + image_file->set_name(filename); + if (!filename.empty()) { + image_file->set_read_only(access(filename.c_str(), W_OK)); + + struct stat st; + stat(image_file->name().c_str(), &st); + image_file->set_size(st.st_size); + } +} + +const PbDevices GetDevices() +{ + PbDevices pbDevices; + + for (size_t i = 0; i < devices.size(); i++) { // skip if unit does not exist or null disk - Disk *pUnit = disk[i]; - if (!pUnit) { + Device *device = devices[i]; + if (!device) { continue; } - PbDevice *device = devices.add_devices(); + PbDevice *pbDevice = pbDevices.add_devices(); // Initialize ID and unit number - device->set_id(i / UnitNum); - device->set_un(i % UnitNum); + pbDevice->set_id(i / UnitNum); + pbDevice->set_unit(i % UnitNum); // ID,UNIT,Type,Device Status - device->set_type(MapIdToType(pUnit->GetID(), pUnit->IsSASI())); + PbDeviceType type = UNDEFINED; + PbDeviceType_Parse(device->GetType(), &type); + pbDevice->set_type(type); - // mount status output - if (pUnit->IsBridge()) { - device->set_file("X68000 HOST BRIDGE"); - } else if (pUnit->IsDaynaPort()) { - device->set_file("DaynaPort SCSI/Link"); - } else { + pbDevice->set_read_only(device->IsReadOnly()); + pbDevice->set_protectable(device->IsProtectable()); + pbDevice->set_protected_(device->IsProtectable() && device->IsProtected()); + pbDevice->set_removable(device->IsRemovable()); + pbDevice->set_removed(device->IsRemoved()); + pbDevice->set_lockable(device->IsLockable()); + pbDevice->set_locked(device->IsLocked()); + + const FileSupport *fileSupport = dynamic_cast(device); + if (fileSupport) { Filepath filepath; - pUnit->GetPath(filepath); - device->set_file(pUnit->IsRemovable() && !pUnit->IsReady() ? "" : filepath.GetPath()); - } - - device->set_protectable(pUnit->IsProtectable()); - device->set_protected_(pUnit->IsProtectable() && pUnit->IsWriteP()); - device->set_removable(pUnit->IsRemovable()); - device->set_removed(pUnit->IsRemoved()); - device->set_lockable(pUnit->IsLockable()); - device->set_locked(pUnit->IsLocked()); - device->set_supports_file(pUnit->SupportsFile()); - - // Write protection status - if (pUnit->IsRemovable() && pUnit->IsReady() && pUnit->IsWriteP()) { - device->set_read_only(true); + fileSupport->GetPath(filepath); + PbImageFile *image_file = new PbImageFile(); + GetImageFile(image_file, device->IsRemovable() && !device->IsReady() ? "" : filepath.GetPath()); + pbDevice->set_allocated_file(image_file); + pbDevice->set_supports_file(true); } } - return devices; + return pbDevices; } //--------------------------------------------------------------------------- @@ -319,45 +314,48 @@ const PbDevices GetDevices() { // Controller Mapping // //--------------------------------------------------------------------------- -bool MapController(Disk **map) +bool MapController(Device **map) { + assert(bus); + bool status = true; // Take ownership of the ctrl data structure pthread_mutex_lock(&ctrl_mutex); // Replace the changed unit - for (int i = 0; i < CtrlMax; i++) { + for (size_t i = 0; i < controllers.size(); i++) { for (int j = 0; j < UnitNum; j++) { int unitno = i * UnitNum + j; - if (disk[unitno] != map[unitno]) { + if (devices[unitno] != map[unitno]) { // Check if the original unit exists - if (disk[unitno]) { + if (devices[unitno]) { // Disconnect it from the controller - if (ctrl[i]) { - ctrl[i]->SetUnit(j, NULL); + if (controllers[i]) { + controllers[i]->SetUnit(j, NULL); } // Free the Unit - delete disk[unitno]; + delete devices[unitno]; } // Setup a new unit - disk[unitno] = map[unitno]; + devices[unitno] = map[unitno]; } } } // Reconfigure all of the controllers - for (int i = 0; i < CtrlMax; i++) { + int i = 0; + for (auto it = controllers.begin(); it != controllers.end(); ++i, ++it) { // Examine the unit configuration int sasi_num = 0; int scsi_num = 0; for (int j = 0; j < UnitNum; j++) { int unitno = i * UnitNum + j; // branch by unit type - if (disk[unitno]) { - if (disk[unitno]->IsSASI()) { + if (devices[unitno]) { + if (devices[unitno]->IsSASI()) { // Drive is SASI, so increment SASI count sasi_num++; } else { @@ -367,16 +365,16 @@ bool MapController(Disk **map) } // Remove the unit - if (ctrl[i]) { - ctrl[i]->SetUnit(j, NULL); + if (*it) { + (*it)->SetUnit(j, NULL); } } // If there are no units connected if (sasi_num == 0 && scsi_num == 0) { - if (ctrl[i]) { - delete ctrl[i]; - ctrl[i] = NULL; + if (*it) { + delete *it; + *it = NULL; continue; } } @@ -391,38 +389,38 @@ bool MapController(Disk **map) // Only SASI Unit(s) // Release the controller if it is not SASI - if (ctrl[i] && !ctrl[i]->IsSASI()) { - delete ctrl[i]; - ctrl[i] = NULL; + if (*it && !(*it)->IsSASI()) { + delete *it; + *it = NULL; } // Create a new SASI controller - if (!ctrl[i]) { - ctrl[i] = new SASIDEV(); - ctrl[i]->Connect(i, bus); + if (!*it) { + *it = new SASIDEV(); + (*it)->Connect(i, bus); } } else { // Only SCSI Unit(s) // Release the controller if it is not SCSI - if (ctrl[i] && !ctrl[i]->IsSCSI()) { - delete ctrl[i]; - ctrl[i] = NULL; + if (*it && !(*it)->IsSCSI()) { + delete *it; + *it = NULL; } // Create a new SCSI controller - if (!ctrl[i]) { - ctrl[i] = new SCSIDEV(); - ctrl[i]->Connect(i, bus); + if (!*it) { + *it = new SCSIDEV(); + (*it)->Connect(i, bus); } } // connect all units for (int j = 0; j < UnitNum; j++) { int unitno = i * UnitNum + j; - if (disk[unitno]) { + if (devices[unitno]) { // Add the unit connection - ctrl[i]->SetUnit(j, disk[unitno]); + (*it)->SetUnit(j, (static_cast(devices[unitno]))); } } } @@ -439,9 +437,15 @@ bool ReturnStatus(int fd, bool status = true, const string msg = "") if (fd == -1) { if (!msg.empty()) { - FPRT(stderr, "Error: "); - FPRT(stderr, msg.c_str()); - FPRT(stderr, "\n"); + if (status) { + FPRT(stderr, "Error: "); + FPRT(stderr, "%s", msg.c_str()); + FPRT(stderr, "\n"); + } + else { + FPRT(stdout, "%s", msg.c_str()); + FPRT(stderr, "\n"); + } } } else { @@ -491,9 +495,9 @@ bool SetLogLevel(const string& log_level) return true; } -void LogDevices(const string& device_list) +void LogDevices(const string& devices) { - stringstream ss(device_list); + stringstream ss(devices); string line; while (getline(ss, line, '\n')) { @@ -513,31 +517,80 @@ void GetAvailableImages(PbServerInfo& serverInfo) if (access(default_image_folder.c_str(), F_OK) != -1) { for (const auto& entry : filesystem::directory_iterator(default_image_folder)) { if (entry.is_regular_file()) { - serverInfo.add_available_image_files(entry.path().filename()); + PbImageFile *image_file = serverInfo.add_available_image_files(); + GetImageFile(image_file, entry.path().filename()); } } } } +bool SetDefaultImageFolder(const string& f) +{ + string folder = f; + + // If a relative path is specified the path is assumed to be relative to the user's home directory + if (folder[0] != '/') { + const passwd *passwd = getpwuid(getuid()); + if (passwd) { + folder = passwd->pw_dir; + folder += "/"; + folder += f; + } + } + + struct stat info; + stat(folder.c_str(), &info); + if (!S_ISDIR(info.st_mode) || access(folder.c_str(), F_OK) == -1) { + return false; + } + + default_image_folder = folder; + + LOGINFO("Set default image folder to '%s'", default_image_folder.c_str()); + + return true; +} + +void SetDeviceName(Device *device, const string& name) +{ + size_t productSeparatorPos = name.find(':'); + if (productSeparatorPos != string::npos) { + device->SetVendor(name.substr(0, productSeparatorPos)); + + const string remaining = name.substr(productSeparatorPos + 1); + size_t revisionSeparatorPos = remaining.find(':'); + if (revisionSeparatorPos != string::npos) { + device->SetProduct(remaining.substr(0, revisionSeparatorPos)); + device->SetRevision(remaining.substr(revisionSeparatorPos + 1)); + return; + } + } + + throw illegal_argument_exception("Invalid device name '" + name + "', format must be VENDOR:PRODUCT:REVISION"); +} + //--------------------------------------------------------------------------- // // Command Processing // //--------------------------------------------------------------------------- -bool ProcessCmd(int fd, const PbCommand &command) + +bool ProcessCmd(int fd, const PbDeviceDefinition& pbDevice, const PbOperation cmd, const string& params, bool dryRun) { Filepath filepath; - Disk *pUnit; + Device *device = NULL; ostringstream error; - int id = command.id(); - int un = command.un(); - PbOperation cmd = command.cmd(); - PbDeviceType type = command.type(); - string params = command.params().c_str(); + const int id = pbDevice.id(); + const int unit = pbDevice.unit(); + const string filename = pbDevice.file(); + PbDeviceType type = pbDevice.type(); + bool all = params == "all"; ostringstream s; - s << "Processing: cmd=" << PbOperation_Name(cmd) << ", id=" << id << ", un=" << un << ", type=" << PbDeviceType_Name(type) << ", params=" << params; + s << (dryRun ? "Validating: " : "Executing: "); + s << "cmd=" << PbOperation_Name(cmd) << ", id=" << id << ", unit=" << unit << ", type=" << PbDeviceType_Name(type) + << ", filename='" << filename << "', device name='" << pbDevice.name() << "', params='" << params << "'"; LOGINFO("%s", s.str().c_str()); // Check the Controller Number @@ -547,240 +600,280 @@ bool ProcessCmd(int fd, const PbCommand &command) } // Check the Unit Number - if (un < 0 || un >= UnitNum) { - error << "Invalid unit " << un << " (0-" << UnitNum - 1 << ")"; + if (unit < 0 || unit >= UnitNum) { + error << "Invalid unit " << unit << " (0-" << UnitNum - 1 << ")"; return ReturnStatus(fd, false, error); } - // Copy the Unit List - Disk *map[CtrlMax * UnitNum]; - memcpy(map, disk, sizeof(disk)); + // Copy the devices + Device *map[devices.size()]; + for (size_t i = 0; i < devices.size(); i++) { + map[i] = devices[i]; + } - // Connect Command if (cmd == ATTACH) { - if (map[id * UnitNum + un]) { + if (map[id * UnitNum + unit]) { error << "Duplicate ID " << id; return ReturnStatus(fd, false, error); } - string file = params; - - // Try to extract the file type from the filename. Convention: "filename:type". - size_t separatorPos = file.find(':'); - if (separatorPos != string::npos) { - string t = file.substr(separatorPos + 1); - transform(t.begin(), t.end(), t.begin(), ::toupper); - - if (!PbDeviceType_Parse(t, &type)) { - error << "Invalid device type " << t; - return ReturnStatus(fd, false, error); - } - - file = file.substr(0, separatorPos); + if (dryRun) { + return true; } string ext; - int len = file.size(); - if (len > 4 && file[len - 4] == '.') { - ext = file.substr(len - 3); + int len = filename.length(); + if (len > 4 && filename[len - 4] == '.') { + ext = filename.substr(len - 3); } - // If no type was specified try to derive the file type from the extension - if (type == UNDEFINED) { - if (ext == "hdf") { - type = SASI_HD; + // Create a new device, based upon type or file extension + device = DeviceFactory::CreateDevice(type, filename, ext); + if (!device) { + return ReturnStatus(fd, false, "Invalid device type " + PbDeviceType_Name(type)); + } + + if (!pbDevice.name().empty()) { + try { + SetDeviceName(device, pbDevice.name()); } - else if (ext == "hds" || ext == "hdn" || ext == "hdi" || ext == "nhd" || ext == "hda") { - type = SCSI_HD; - } else if (ext == "mos") { - type = MO; - } else if (ext == "iso") { - type = CD; + catch(const illegal_argument_exception& e) { + return ReturnStatus(fd, false, e.getmsg()); } } - // File check (type is HD, for CD and MO the medium (=file) may be inserted later) - if ((type == SASI_HD || type == SCSI_HD) && file.empty()) { - return ReturnStatus(fd, false, "Missing filename"); + device->SetId(id); + device->SetLun(unit); + if (!device->IsReadOnly()) { + device->SetProtected(pbDevice.protected_()); } - // Create a new drive, based upon type - pUnit = NULL; - switch (type) { - case SASI_HD: // HDF - pUnit = new SASIHD(); - break; - case SCSI_HD: // HDS/HDN/HDI/NHD/HDA - if (ext == "hdn" || ext == "hdi" || ext == "nhd") { - pUnit = new SCSIHD_NEC(); - } else if (ext == "hda") { - pUnit = new SCSIHD_APPLE(); - } else { - pUnit = new SCSIHD(); - } - break; - case MO: - pUnit = new SCSIMO(); - break; - case CD: - pUnit = new SCSICD(); - break; - case BR: - pUnit = new SCSIBR(); - break; - // case NUVOLINK: - // pUnit = new SCSINuvolink(); - // break; - case DAYNAPORT: - pUnit = new SCSIDaynaPort(); - break; - default: - error << "Received a command for an invalid drive type: " << PbDeviceType_Name(type); - return ReturnStatus(fd, false, error); + FileSupport *fileSupport = dynamic_cast(device); + + // File check (type is HD, for removable media drives, CD and MO the medium (=file) may be inserted later) + if (fileSupport && !device->IsRemovable() && filename.empty()) { + delete device; + + return ReturnStatus(fd, false, "Device type " + PbDeviceType_Name(type) + " requires a filename"); } // drive checks files - if (type != BR && type != DAYNAPORT && !command.params().empty()) { + if (fileSupport && !filename.empty()) { // Set the Path - filepath.SetPath(file.c_str()); + filepath.SetPath(filename.c_str()); // Open the file path try { - pUnit->Open(filepath); + fileSupport->Open(filepath); } - catch(const ioexception& e) { + catch(const io_exception& e) { // If the file does not exist search for it in the default image folder - string default_file = default_image_folder + "/" + file; - filepath.SetPath(default_file.c_str()); + string file = default_image_folder + "/" + filename; + filepath.SetPath(file.c_str()); try { - pUnit->Open(filepath); + fileSupport->Open(filepath); } - catch(const ioexception&) { - delete pUnit; + catch(const io_exception&) { + delete device; - error << "Tried to open an invalid file '" << file << "': " << e.getmsg(); - return ReturnStatus(fd, false, error); + return ReturnStatus(fd, false, "Tried to open an invalid file '" + filename + "': " + e.getmsg()); } } - if (files_in_use.find(filepath.GetPath()) != files_in_use.end()) { - error << "Image file '" << file << "' is already in use"; - return ReturnStatus(fd, false, error); + if (!dryRun) { + if (files_in_use.find(filepath.GetPath()) != files_in_use.end()) { + delete device; + + return ReturnStatus(fd, false, "Image file '" + filename + "' is already in use"); + } + + files_in_use.insert(filepath.GetPath()); + } + } + + if (!dryRun) { + // Replace with the newly created unit + map[id * UnitNum + unit] = device; + + // Re-map the controller + bool status = MapController(map); + if (status) { + LOGINFO("Added new %s device, ID: %d unit: %d", device->GetType().c_str(), id, unit); + return true; } - files_in_use.insert(filepath.GetPath()); + return ReturnStatus(fd, false, "SASI and SCSI can't be mixed"); + } + } + + // When detaching all devices no controller/unit tests are required + if (cmd != DETACH || !all) { + // Does the controller exist? + if (!dryRun && !controllers[id]) { + error << "Received a command for non-existing ID " << id; + return ReturnStatus(fd, false, error); } - // Set the cache to write-through - pUnit->SetCacheWB(FALSE); - - // Replace with the newly created unit - map[id * UnitNum + un] = pUnit; - - // Re-map the controller - bool status = MapController(map); - if (status) { - LOGINFO("Added new %s device. ID: %d UN: %d", pUnit->GetID().c_str(), id, un); + // Does the unit exist? + device = devices[id * UnitNum + unit]; + if (!dryRun && !device) { + error << "Received a command for ID " << id << ", non-existing unit " << unit; + return ReturnStatus(fd, false, error); } - - return ReturnStatus(fd, status, status ? "" : "SASI and SCSI can't be mixed"); } - // Does the controller exist? - if (ctrl[id] == NULL) { - error << "Received a command for invalid ID " << id; - return ReturnStatus(fd, false, error); - } + FileSupport *fileSupport = dynamic_cast(device); - // Does the unit exist? - pUnit = disk[id * UnitNum + un]; - if (pUnit == NULL) { - error << "Received a command for invalid ID " << id << ", unit " << un; - return ReturnStatus(fd, false, error); - } - - // Disconnect Command if (cmd == DETACH) { - LOGINFO("Disconnect %s at ID: %d UN: %d", pUnit->GetID().c_str(), id, un); + if (!all && !params.empty()) { + return ReturnStatus(fd, false, "Invalid command parameter '" + params + "' for " + PbOperation_Name(DETACH)); + } - // Free the existing unit - map[id * UnitNum + un] = NULL; + if (!dryRun) { + // Free the existing unit(s) + if (all) { + for (size_t i = 0; i < devices.size(); i++) { + map[i] = NULL; + } - Filepath filepath; - pUnit->GetPath(filepath); - files_in_use.erase(filepath.GetPath()); + files_in_use.clear(); + } + else { + map[id * UnitNum + unit] = NULL; - // Re-map the controller - bool status = MapController(map); + if (fileSupport) { + Filepath filepath; + fileSupport->GetPath(filepath); + files_in_use.erase(filepath.GetPath()); + } + } - return ReturnStatus(fd, status, status ? "" : "SASI and SCSI can't be mixed"); + // Re-map the controller, remember the device type because the device gets lost when re-mapping + const string device_type = device ? device->GetType() : PbDeviceType_Name(UNDEFINED); + bool status = MapController(map); + if (status) { + if (all) { + LOGINFO("Disconnected all devices"); + } + else { + LOGINFO("Disconnected %s device with ID %d, unit %d", device_type.c_str(), id, unit); + } + return true; + } + + return ReturnStatus(fd, false, "SASI and SCSI can't be mixed"); + } } - // Only MOs or CDs may be inserted/ejected, only MOs, CDs or hard disks may be protected - if ((cmd == INSERT || cmd == EJECT) && !pUnit->IsRemovable()) { - error << PbOperation_Name(cmd) << " operation denied (Device type " << pUnit->GetID() << " isn't removable)"; - return ReturnStatus(fd, false, error); + if ((cmd == INSERT || cmd == EJECT) && !device->IsRemovable()) { + return ReturnStatus(fd, false, PbOperation_Name(cmd) + " operation denied (" + device->GetType() + " isn't removable)"); } - if ((cmd == PROTECT || cmd == UNPROTECT) && (!pUnit->IsProtectable() || pUnit->IsReadOnly())) { - error << PbOperation_Name(cmd) << " operation denied (Device type " << pUnit->GetID() << " isn't protectable)"; - return ReturnStatus(fd, false, error); + if ((cmd == PROTECT || cmd == UNPROTECT) && !device->IsProtectable()) { + return ReturnStatus(fd, false, PbOperation_Name(cmd) + " operation denied (" + device->GetType() + " isn't protectable)"); } switch (cmd) { case INSERT: - if (params.empty()) { + if (!pbDevice.name().empty()) { + return ReturnStatus(fd, false, "Device name cannot be changed"); + } + + if (filename.empty()) { return ReturnStatus(fd, false, "Missing filename"); } - filepath.SetPath(params.c_str()); - LOGINFO("Insert file '%s' requested into %s ID: %d UN: %d", params.c_str(), pUnit->GetID().c_str(), id, un); + if (dryRun) { + return true; + } + + filepath.SetPath(filename.c_str()); + LOGINFO("Insert file '%s' requested into %s ID: %d unit: %d", filename.c_str(), device->GetType().c_str(), id, unit); try { - pUnit->Open(filepath); + fileSupport->Open(filepath); } - catch(const ioexception& e) { - // If the file does not exist search for it in the default image folder - string default_file = default_image_folder + "/" + params; - filepath.SetPath(default_file.c_str()); - try { - pUnit->Open(filepath); + catch(const io_exception& e) { + if (!default_image_folder.empty()) { + // If the file does not exist search for it in the default image folder + string default_file = default_image_folder + "/" + filename; + filepath.SetPath(default_file.c_str()); + try { + fileSupport->Open(filepath); + } + catch(const io_exception&) { + return ReturnStatus(fd, false, "Tried to open an invalid file '" + filename + "': " + e.getmsg()); + } } - catch(const ioexception&) { - error << "Tried to open an invalid file '" << params << "': " << e.getmsg(); - LOGWARN("%s", error.str().c_str()); - return ReturnStatus(fd, false, error); + else { + return ReturnStatus(fd, false, "No default image folder"); } } break; case EJECT: - LOGINFO("Eject requested for %s ID: %d UN: %d", pUnit->GetID().c_str(), id, un); - pUnit->Eject(true); + if (dryRun) { + return true; + } + + LOGINFO("Eject requested for %s ID %d, unit %d", device->GetType().c_str(), id, unit); + // EJECT is idempotent + device->Eject(true); break; case PROTECT: - LOGINFO("Write protection requested for %s ID: %d UN: %d", pUnit->GetID().c_str(), id, un); - pUnit->WriteP(true); + if (dryRun) { + return true; + } + + LOGINFO("Write protection requested for %s ID %d, unit %d", device->GetType().c_str(), id, unit); + // PROTECT is idempotent + device->SetProtected(true); break; case UNPROTECT: - LOGINFO("Write unprotection requested for %s ID: %d UN: %d", pUnit->GetID().c_str(), id, un); - pUnit->WriteP(false); + if (dryRun) { + return true; + } + + LOGINFO("Write unprotection requested for %s ID: %d unit: %d", device->GetType().c_str(), id, unit); + // UNPROTECT is idempotent + device->SetProtected(false); break; + case ATTACH: + case DETACH: + assert(dryRun); + + // The non dry-run case was handled above + return true; + default: - error << "Received unknown command: " << PbOperation_Name(cmd); - return ReturnStatus(fd, false, error); + return ReturnStatus(fd, false, "Received unknown command: " + PbOperation_Name(cmd)); + } + + return true; +} + +bool ProcessCmd(const int fd, const PbCommand& command) +{ + // Dry run first + for (int i = 0; i < command.devices().devices_size(); i++) { + if (!ProcessCmd(fd, command.devices().devices(i), command.cmd(), command.params(), true)) { + return false; + } + } + + // Execute + for (int i = 0; i < command.devices().devices_size(); i++) { + if (!ProcessCmd(fd, command.devices().devices(i), command.cmd(), command.params(), false)) { + return false; + } } return ReturnStatus(fd); } -bool has_suffix(const string& filename, const string& suffix) { - return filename.size() >= suffix.size() && !filename.compare(filename.size() - suffix.size(), suffix.size(), suffix); -} - //--------------------------------------------------------------------------- // // Argument Parsing @@ -788,13 +881,17 @@ bool has_suffix(const string& filename, const string& suffix) { //--------------------------------------------------------------------------- bool ParseArgument(int argc, char* argv[], int& port) { + PbDeviceDefinitions devices; int id = -1; bool is_sasi = false; int max_id = 7; - string log_level = "trace"; + PbDeviceType type = UNDEFINED; + string name; + string log_level; + opterr = 1; int opt; - while ((opt = getopt(argc, argv, "-IiHhG:g:D:d:P:p:f:Vv")) != -1) { + while ((opt = getopt(argc, argv, "-IiHhG:g:D:d:N:n:T:t:P:p:f:Vv")) != -1) { switch (tolower(opt)) { case 'i': is_sasi = false; @@ -831,54 +928,78 @@ bool ParseArgument(int argc, char* argv[], int& port) continue; case 'f': - struct stat folder_stat; - stat(optarg, &folder_stat); - if (!S_ISDIR(folder_stat.st_mode) || access(optarg, F_OK) == -1) { - cerr << "Default image folder '" << optarg << "' is not accessible" << endl; + if (!SetDefaultImageFolder(optarg)) { + cerr << "Folder '" << optarg << "' does not exist or is not accessible"; return false; } + continue; - default_image_folder = optarg; + case 'n': + name = optarg; + continue; + + case 't': { + string t = optarg; + transform(t.begin(), t.end(), t.begin(), ::toupper); + if (!PbDeviceType_Parse(t, &type)) { + cerr << "Illegal device type '" << optarg << "'" << endl; + return false; + } + } continue; case 'v': cout << rascsi_get_version_string() << endl; - exit(0); + exit(EXIT_SUCCESS); break; default: return false; case 1: + // Encountered filename break; } - int un = 0; + if (optopt) { + return false; + } + + int unit = 0; if (is_sasi) { - un = id % UnitNum; + unit = id % UnitNum; id /= UnitNum; } - // Execute the command - PbCommand command; - command.set_id(id); - command.set_un(un); - command.set_cmd(ATTACH); - command.set_params(optarg); - if (!ProcessCmd(-1, command)) { - return false; - } + // Set up the device data + PbDeviceDefinition *device = devices.add_devices(); + device->set_id(id); + device->set_unit(unit); + device->set_type(type); + device->set_name(name); + device->set_file(optarg); + id = -1; + type = UNDEFINED; } - if (!SetLogLevel(log_level)) { + if (!log_level.empty() && !SetLogLevel(log_level)) { LOGWARN("Invalid log level '%s'", log_level.c_str()); } + // Attach all specified devices + PbCommand command; + command.set_cmd(ATTACH); + command.set_allocated_devices(new PbDeviceDefinitions(devices)); + + if (!ProcessCmd(-1, command)) { + return false; + } + // Display and log the device list - const string devices = ListDevices(GetDevices()); - cout << devices << endl; - LogDevices(devices); + const string deviceList = ListDevices(GetDevices()); + cout << deviceList << endl; + LogDevices(deviceList); return true; } @@ -911,8 +1032,6 @@ void FixCpu(int cpu) //--------------------------------------------------------------------------- static void *MonThread(void *param) { - int fd; - // Scheduler Settings struct sched_param schedparam; schedparam.sched_priority = 0; @@ -929,15 +1048,17 @@ static void *MonThread(void *param) // Set up the monitor socket to receive commands listen(monsocket, 1); - try { - while (true) { + while (true) { + int fd = -1; + + try { // Wait for connection struct sockaddr_in client; socklen_t socklen = sizeof(client); memset(&client, 0, socklen); fd = accept(monsocket, (struct sockaddr*)&client, &socklen); if (fd < 0) { - throw ioexception("accept() failed"); + throw io_exception("accept() failed"); } // Fetch the command @@ -945,19 +1066,10 @@ static void *MonThread(void *param) DeserializeMessage(fd, command); switch(command.cmd()) { - case LIST: { - const PbDevices devices = GetDevices(); - SerializeMessage(fd, devices); - LogDevices(ListDevices(devices)); - break; - } - case LOG_LEVEL: { bool status = SetLogLevel(command.params()); if (!status) { - ostringstream error; - error << "Invalid log level: " << command.params(); - ReturnStatus(fd, false, error); + ReturnStatus(fd, false, "Invalid log level: " + command.params()); } else { ReturnStatus(fd); @@ -965,6 +1077,19 @@ static void *MonThread(void *param) break; } + case DEFAULT_FOLDER: + if (command.params().empty()) { + ReturnStatus(fd, false, "Can't set default image folder: Missing folder name"); + } + + if (!SetDefaultImageFolder(command.params())) { + ReturnStatus(fd, false, "Folder '" + command.params() + "' does not exist or is not accessible"); + } + else { + ReturnStatus(fd); + } + break; + case SERVER_INFO: { PbServerInfo serverInfo; serverInfo.set_rascsi_version(rascsi_get_version_string()); @@ -974,6 +1099,7 @@ static void *MonThread(void *param) GetAvailableImages(serverInfo); serverInfo.set_allocated_devices(new PbDevices(GetDevices())); SerializeMessage(fd, serverInfo); + LogDevices(ListDevices(serverInfo.devices())); break; } @@ -987,19 +1113,16 @@ static void *MonThread(void *param) break; } } - - close(fd); - fd = -1; } - } - catch(const ioexception& e) { - LOGERROR("%s", e.getmsg().c_str()); + catch(const io_exception& e) { + LOGWARN("%s", e.getmsg().c_str()); - // Fall through - } + // Fall through + } - if (fd >= 0) { - close(fd); + if (fd >= 0) { + close(fd); + } } return NULL; @@ -1014,7 +1137,6 @@ int main(int argc, char* argv[]) { GOOGLE_PROTOBUF_VERIFY_VERSION; - int i; int actid; DWORD now; BUS::phase_t phase; @@ -1030,29 +1152,39 @@ int main(int argc, char* argv[]) available_log_levels.push_back("err"); available_log_levels.push_back("critical"); available_log_levels.push_back("off"); - SetLogLevel("trace"); + SetLogLevel("info"); // Create a thread-safe stdout logger to process the log messages auto logger = stdout_color_mt("rascsi stdout logger"); + // ~/images is the default folder for device image file. For the root user /home/pi/images is the default. + const int uid = getuid(); + const passwd *passwd = getpwuid(uid); + if (uid && passwd) { + string folder = passwd->pw_dir; + folder += "/images"; + default_image_folder = folder; + } + else { + default_image_folder = "/home/pi/images"; + } + // Output the Banner Banner(argc, argv); int ret = 0; int port = 6868; - InitDisks(); + if (!InitBus()) { + ret = EPERM; + goto init_exit; + } if (!ParseArgument(argc, argv, port)) { ret = EINVAL; goto err_exit; } - if (!InitBus()) { - ret = EPERM; - goto init_exit; - } - if (!InitService(port)) { ret = EPERM; goto init_exit; @@ -1071,7 +1203,7 @@ int main(int argc, char* argv[]) #endif // USE_SEL_EVENT_ENABLE // Start execution - running = TRUE; + running = true; // Main Loop while (running) { @@ -1120,13 +1252,14 @@ int main(int argc, char* argv[]) // Notify all controllers data = bus->GetDAT(); - for (i = 0; i < CtrlMax; i++) { - if (!ctrl[i] || (data & (1 << i)) == 0) { + int i = 0; + for (auto it = controllers.begin(); it != controllers.end(); ++i, ++it) { + if (!*it || (data & (1 << i)) == 0) { continue; } // Find the target that has moved to the selection phase - if (ctrl[i]->Process() == BUS::selection) { + if ((*it)->Process() == BUS::selection) { // Get the target ID actid = i; @@ -1143,7 +1276,7 @@ int main(int argc, char* argv[]) } // Start target device - active = TRUE; + active = true; #ifndef USE_SEL_EVENT_ENABLE // Scheduling policy setting (highest priority) @@ -1154,7 +1287,7 @@ int main(int argc, char* argv[]) // Loop until the bus is free while (running) { // Target drive - phase = ctrl[actid]->Process(); + phase = controllers[actid]->Process(); // End when the bus is free if (phase == BUS::busfree) { @@ -1171,7 +1304,7 @@ int main(int argc, char* argv[]) #endif // USE_SEL_EVENT_ENABLE // End the target travel - active = FALSE; + active = false; } err_exit: diff --git a/src/raspberrypi/rascsi_interface.proto b/src/raspberrypi/rascsi_interface.proto index 98059dd7..45842937 100644 --- a/src/raspberrypi/rascsi_interface.proto +++ b/src/raspberrypi/rascsi_interface.proto @@ -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; } \ No newline at end of file diff --git a/src/raspberrypi/rascsi_version.cpp b/src/raspberrypi/rascsi_version.cpp index 08ec2f48..db2d9e39 100644 --- a/src/raspberrypi/rascsi_version.cpp +++ b/src/raspberrypi/rascsi_version.cpp @@ -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; } + + diff --git a/src/raspberrypi/rascsi_version.h b/src/raspberrypi/rascsi_version.h index 17a2d345..36eba2b6 100644 --- a/src/raspberrypi/rascsi_version.h +++ b/src/raspberrypi/rascsi_version.h @@ -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(); diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp index 7711e459..6a206861 100644 --- a/src/raspberrypi/rasctl.cpp +++ b/src/raspberrypi/rasctl.cpp @@ -10,10 +10,10 @@ //--------------------------------------------------------------------------- #include -#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 @@ -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 sorted_image_files; + list 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); } diff --git a/src/raspberrypi/rasdump.cpp b/src/raspberrypi/rasdump.cpp index 4d6d60e0..8fbf0bd1 100644 --- a/src/raspberrypi/rasdump.cpp +++ b/src/raspberrypi/rasdump.cpp @@ -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; } diff --git a/src/raspberrypi/rasutil.cpp b/src/raspberrypi/rasutil.cpp index 9600093d..63b681b0 100644 --- a/src/raspberrypi/rasutil.cpp +++ b/src/raspberrypi/rasutil.cpp @@ -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 #include #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 "????"; - } -} diff --git a/src/raspberrypi/rasutil.h b/src/raspberrypi/rasutil.h index cfb3ea80..2a6160a4 100644 --- a/src/raspberrypi/rasutil.h +++ b/src/raspberrypi/rasutil.h @@ -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 #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&); diff --git a/src/raspberrypi/sasidump.cpp b/src/raspberrypi/sasidump.cpp index 3e9dc960..a0a8ee9a 100644 --- a/src/raspberrypi/sasidump.cpp +++ b/src/raspberrypi/sasidump.cpp @@ -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; } diff --git a/src/raspberrypi/scsi.h b/src/raspberrypi/scsi.h index 2e85e65f..02c862f1 100644 --- a/src/raspberrypi/scsi.h +++ b/src/raspberrypi/scsi.h @@ -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; diff --git a/src/raspberrypi/scsimon.cpp b/src/raspberrypi/scsimon.cpp index a9f1a55a..20ce9b14 100644 --- a/src/raspberrypi/scsimon.cpp +++ b/src/raspberrypi/scsimon.cpp @@ -20,6 +20,7 @@ #include "spdlog/spdlog.h" #include #include +#include //--------------------------------------------------------------------------- // @@ -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(); diff --git a/src/raspberrypi/xm6.h b/src/raspberrypi/xm6.h index fbeeb13b..50c5524d 100644 --- a/src/raspberrypi/xm6.h +++ b/src/raspberrypi/xm6.h @@ -13,13 +13,6 @@ #if !defined(xm6_h) #define xm6_h -//--------------------------------------------------------------------------- -// -// RaSCSI -// -//--------------------------------------------------------------------------- -#define RASCSI 1 - //--------------------------------------------------------------------------- // // Various Operation Settings diff --git a/src/web/file_cmds.py b/src/web/file_cmds.py index 9ff3c8f1..55434a56 100644 --- a/src/web/file_cmds.py +++ b/src/web/file_cmds.py @@ -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]) diff --git a/src/web/ractl_cmds.py b/src/web/ractl_cmds.py index de288520..ef10d5cc 100644 --- a/src/web/ractl_cmds.py +++ b/src/web/ractl_cmds.py @@ -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, ) diff --git a/src/web/templates/index.html b/src/web/templates/index.html index 33130f01..58dfacdf 100644 --- a/src/web/templates/index.html +++ b/src/web/templates/index.html @@ -75,6 +75,7 @@ +

Image File Management

@@ -220,8 +221,9 @@ + + -