mirror of
https://github.com/akuker/RASCSI.git
synced 2025-04-09 12:38:27 +00:00
Configurable block size, controller/device cleanup, dispatchers per device, bridge setup (#203)
* Use foreach * Renaming * Revert "Renaming" This reverts commit b0554b9c0a052e282625a4565d429313af2b3cc7. * Manpage updates * Removed obsolete assertions * Replaced QWORD by uint64_t and removed respective typedef * Removed LPCSTR typedef * Removed LPCTSTR typedef * Removed LPTSTR typedef * Renamed SCSI command interface classes * Renamed xm6.h to rascsi.h * Moved interface classes to new interfaces subfolder * Added include * Fixed compilation issues of 64 bit Ubuntu * Renaming * Sort block sizes * protobuf interface description update * Fixed handling for sector size for non-disk devices * Fixed typo * Fixed comment * Translate code commends into English, removing redundant ones (#214) * Comment update * For other bridge interfaces than eth0 IP address and netmask can be provided * Added special rule for testing on x86 PCs * Translated code comments into English, removing some redundant ones in the process, plus fixing typos (#215) * Translate code commends into English, removing redundant ones * - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h - Removed some redundant comments where the context is obvious from the code - Fixed a few typos and mistakes * Comment update * Removed unused typedefs * Added special rule for testing on x86 PCs * Comment update * Comment update * Updated capacity calculation * Updated protobuf interface to signal parameter support * Simplified protobuf interface * Updated rasctl server info output * Updated logging * protobuf interface has to return block only if it is configurable * Updated block size handling * Improved error message if ID is already in use * Removed typedef * Renamed protobuf interface method * Renaming * Use protobuf::Messsge instead of protobuf::MessageLite * default_image_folder cannot be an empty string, removed obsolete check * Logging update * Made some error messages more concise * Removed magic constant * Updated error message * Comment update * Names of removable media drives must be constant and not contain the capacity * Improved DeviceFactory error handling * More error handing improvements * Interface comment update * Pass interface list to ctapdriver when creating bridge * Moved initialization code * Updated rasctl server information output * Improved handling of MO capacities * Renaming * Comment update * Reject inserting a medium when there is already a medium present (eject first) * Save list of files in use before dry-run * Updated rasctl server info message * Comment update * Fixed typo * Cleaned list handling * Sort devices list by ID *and unit* * Improved block size check * Fixed issue with missing method in old Raspberry Pi OS protobuf implementation * Updated error message * Improve and fix bugs with saving&loading configuration files for rascsi-web (#218) * Translate code commends into English, removing redundant ones * - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h - Removed some redundant comments where the context is obvious from the code - Fixed a few typos and mistakes * - Store only file path and name to configuration csv - Strip known non-file path strings when reading configuration csv (backwards compatibility) - Validate SCSI ID before attempting to attach a device * Add comment and TODO * Partial translation of cfilesystem.h * Move csv read/write logic into file_cmd.py * Load default.csv on rascsi-web startup * Add rudimentary error handling to config loading/saving * Implement a delete configuration csv file feature. Also rename the delete_image method to delete_file and made it take the full file patch as argument to be consistent with other file operation methods. * Catch the exception when attempting to exclude SCSI id that is already in use from a list of valid SCSI ids * Fix error handling when failing to open a csv file for read or write * Removed unused structures, code and type cleanup * Use unscoped enums for commands * SASI Format opcode is 0x06, not 0x04 (see comment in code) * Removed duplicate command * Code review, data type updates * Data type updated, use #pragma once * Logging update * Renaming * Renaming * Removed duplicate code * Renaming * Refactoring * Removed TODO * Updated logging * Comment update * Comment update * Updated GetEventStatusNotification * Removed goto * Options -h and -v do not require to be the root user (fixes issue #166) * Updated error messages and exception handling * Added number of supported LUNs to protobuf interface * Updated list handling of protobuf interface * Comment update * Improved error handling * Added missing return statement * Allow empty device list * Fixed unnecessary detach_all() when config file isn't read (#221) * Translate code commends into English, removing redundant ones * - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h - Removed some redundant comments where the context is obvious from the code - Fixed a few typos and mistakes * - Store only file path and name to configuration csv - Strip known non-file path strings when reading configuration csv (backwards compatibility) - Validate SCSI ID before attempting to attach a device * Add comment and TODO * Partial translation of cfilesystem.h * Move csv read/write logic into file_cmd.py * Load default.csv on rascsi-web startup * Add rudimentary error handling to config loading/saving * Implement a delete configuration csv file feature. Also rename the delete_image method to delete_file and made it take the full file patch as argument to be consistent with other file operation methods. * Catch the exception when attempting to exclude SCSI id that is already in use from a list of valid SCSI ids * Fix error handling when failing to open a csv file for read or write * Run detach_all() only after succeeding to open a file for reading * Protecting/unprotecing a non-ready medium is considered not possible * Updated error message * Extract detaching all devices, add parameter list support * Comment update * Fixed typos * Restore files in use if dry-run fails * Feature configurable reserved id for rascsi-web (#223) * Translate code commends into English, removing redundant ones * - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h - Removed some redundant comments where the context is obvious from the code - Fixed a few typos and mistakes * - Store only file path and name to configuration csv - Strip known non-file path strings when reading configuration csv (backwards compatibility) - Validate SCSI ID before attempting to attach a device * Add comment and TODO * Partial translation of cfilesystem.h * Move csv read/write logic into file_cmd.py * Load default.csv on rascsi-web startup * Add rudimentary error handling to config loading/saving * Implement a delete configuration csv file feature. Also rename the delete_image method to delete_file and made it take the full file patch as argument to be consistent with other file operation methods. * Catch the exception when attempting to exclude SCSI id that is already in use from a list of valid SCSI ids * Fix error handling when failing to open a csv file for read or write * Run detach_all() only after succeeding to open a file for reading * Make the reserved SCSI id configurable through an argument to start.sh; make the rascsi-web service reserve 7 by default to maintain current behavior. * Make it possible to reserve multiple scsi ids in the web ui * Added support for reserved IDs * rasctl output update * Re-ordered logging * Logging update * Make use of the newly introduced 'rasctl -r' to have the webui reserve ids on the backend side upon startup (#224) * Translate code commends into English, removing redundant ones * - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h - Removed some redundant comments where the context is obvious from the code - Fixed a few typos and mistakes * - Store only file path and name to configuration csv - Strip known non-file path strings when reading configuration csv (backwards compatibility) - Validate SCSI ID before attempting to attach a device * Add comment and TODO * Partial translation of cfilesystem.h * Move csv read/write logic into file_cmd.py * Load default.csv on rascsi-web startup * Add rudimentary error handling to config loading/saving * Implement a delete configuration csv file feature. Also rename the delete_image method to delete_file and made it take the full file patch as argument to be consistent with other file operation methods. * Catch the exception when attempting to exclude SCSI id that is already in use from a list of valid SCSI ids * Fix error handling when failing to open a csv file for read or write * Run detach_all() only after succeeding to open a file for reading * Make use of the new 'rasctl -r' command to reserve IDs on the backend side as well. * Updated string to integer conversions * Improved string to integer conversion * Move string to integer conversion to rasutil * Removed unused variable * Fixed detach, which did not remove the filename from the filenames set * Re-added folder to gitignore * Set/Display patch version * Fix issue where reserved ids were not reserved again when restarting rascsi-service from within the web ui (#226) * Translate code commends into English, removing redundant ones * - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h - Removed some redundant comments where the context is obvious from the code - Fixed a few typos and mistakes * - Store only file path and name to configuration csv - Strip known non-file path strings when reading configuration csv (backwards compatibility) - Validate SCSI ID before attempting to attach a device * Add comment and TODO * Partial translation of cfilesystem.h * Move csv read/write logic into file_cmd.py * Load default.csv on rascsi-web startup * Add rudimentary error handling to config loading/saving * Implement a delete configuration csv file feature. Also rename the delete_image method to delete_file and made it take the full file patch as argument to be consistent with other file operation methods. * Catch the exception when attempting to exclude SCSI id that is already in use from a list of valid SCSI ids * Fix error handling when failing to open a csv file for read or write * Run detach_all() only after succeeding to open a file for reading * Make use of the new 'rasctl -r' command to reserve IDs on the backend side as well. * Make sure reserved SCSI IDs gets reserved again when restarting rascsi-service from within the web ui * Updated interface comment * Accept daynaport as legacy type * Fixed typo * Remove file from the list of files in use when ejected with a SCSI command * Check for attached device for INSERT, EJECT, PROTECT, UNPROTECT * Fixed error handling * Fixed filepath handling * Added more device shortcuts to rasctl * Fixed function declaration * Extraced ATTACH and DETACH * Extracted INSERT * Simplified ProcessCmd * Comment update * Fixed memory leak * Log information on whether a new device is protected or read-only * Updated errro message * Updated error message * Initialize private fields * Updated rasctl help message * Added DEVICE_INFO to protobuf interface * Improved error handling * DEVICE_INFO supports device list * Updated server info handling * Unified result handling with oneof, all commands now return PbResult * A result can always return a message string * Fixed typo * Simplified sending of commands * Improved error handling * Removed unused code * Updated error handling * Code cleanup * Comment update * Updated logging * Updated error handling * Updated handling of removed status for devices without image file support * Comment update * Fixed typo * Updated logging * Updated parameter handling * Updated setting default interfaces * Revert "Updated setting default interfaces" This reverts commit 210abc775d9a79dd0c631cf3877966a2923f4d5b. * Revert "Updated parameter handling" This reverts commit 35302addd59f5f5e1cc032888ba32dcbb426a846. * rascsi supports reserving IDs * Updated help message * Replaced BOOL by bool * Logging update * Logging update * Added default parameters to device properties * Return parameters a device was set up with * Improved device initialization * Updated default parameter handling * Updated default parameter handling * Fixed typo * Comment updates * Comment update * Manage default parameters in the respective device * Do not pass empty parameter string * Added supports_params flag * Made comparisons more consistent * Updated error handling * Updated exception handling * Renaming * Comment update * NEC sectors size must be 512 bytes * Updated logging * Updated vendor name handling * Updated handling of media loading/unloading * Added stoppable property and stopped status * Made MO stoppable * Removed duplicate code * Removed duplicate code * Copy read-only property * Renaming * Removed duplicate code, added START/STOP * Improved default parameter handling * Updated load/eject handling * Logging update * Fixed typo * Verified START/STOP UNIT * Updated logging * Updated status handling * Updated status handling * More status handling updates * Logging update * Made instance fields local variables * Made disk_t private * Made some data structures private * Fixed ARM compile issue * Fixed ctapdriver initialization issue * Reset read-only status when opening an image file * Made logging more consistent * Updated log level * Log load/eject on error level for testing * Revert "Log load/eject on error level for testing" This reverts commit d35a15ea8e520517d25e1e1054ad1aeda9f85f2e. * Assume drive is not ready after having been stopped * Updated status handling * Fixed typo * Rebuild manpage * Fixed issue #234 (MODE SENSE (10) returns wrong mode parameter header) * Removed unused code * Enum data type update * Removed duplicate range check * Removed duplicate code * Removed more duplicate code * Logging update * SCCD sector size was not meant to be configurable * Updated configurable sector size properties * Removed assertion * Improved error handling * Updated error handling * Re-added special error handling only relevant for SASI * Added TODOs * Comment update * Added override modifier * Removed obsolete debug flag (related code was not called) * Comment and logging updates * Removed obsolete try/catch * Revert "Removed obsolete try/catch" This reverts commit 39ca12d8b153c706316ce79f4fec65c9abc60024. * Comment update * Removed duplicate code * Updated error messages, use more foreach loops * Updated logging * Logging update * README update * Added block_count * Evaluate block size when inserting a media * rasctl display capacity if available * Info message update * Added missing product name to NEC vital product data * MO block size depends on capacity only * Extended property/status display * Property display update * Updated error handling * (Doc only changes) Fix typos and add clarification that SASI is used on Unix workstations Co-authored-by: Daniel Markstedt <markstedt@gmail.com> Co-authored-by: Tony Kuker <akuker@gmail.com>
This commit is contained in:
parent
96108d9cf1
commit
3e7f317c49
@ -5,7 +5,7 @@
|
||||
</a>
|
||||
|
||||
# What is RaSCSI?
|
||||
RaSCSI is a virtual SCSI device emulator that runs on a Raspberry Pi. It runs in userspace, and can emulate several SCSI devices at one time. There is a control interface to attach / detach drives during runtime, as well as insert and eject removable media. This project is aimed at users of vintage Macintosh computers from the 1980's and 1990's.
|
||||
RaSCSI is a virtual SCSI device emulator that runs on a Raspberry Pi. It runs in userspace, and can emulate several SCSI devices at one time. There is a control interface to attach / detach drives during runtime, as well as insert and eject removable media. This project is aimed at users of vintage Macintosh and Atari computers from the 1980's and 1990's.
|
||||
|
||||
Please check out the full story with much more detail on the [wiki](https://github.com/akuker/RASCSI/wiki)!
|
||||
|
||||
|
@ -8,6 +8,7 @@ rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins
|
||||
[\fB\-h\fR]
|
||||
[\fB\-n\fR \fIVENDOR:PRODUCT:REVISION\fR]
|
||||
[\fB\-p\f® \fIPORT\fR]
|
||||
[\fB\-r\fR \fIRESERVED_IDS\fR]
|
||||
[\fB\-n\fR \fITYPE\fR]
|
||||
[\fB\-v\fR]
|
||||
[\fB\-IDn\fR \fIFILE\fR]
|
||||
@ -42,6 +43,9 @@ To quit RaSCSI, press Control + C. If it is running in the background, you can k
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-b\fI " " \fIBLOCK_SIZE
|
||||
The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
|
||||
.TP
|
||||
.BR \-f\fI " " \fIFOLDER
|
||||
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
|
||||
@ -57,6 +61,8 @@ Set the vendor, product and revision for the device, to be returned with the INQ
|
||||
.BR \-p\fI " " \fIPORT
|
||||
The rascsi server port, default is 6868.
|
||||
.TP
|
||||
.BR \-r\fI " " \fIRESERVED_IDS
|
||||
Comma-separated list of IDs to reserve.
|
||||
.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
|
||||
@ -73,7 +79,7 @@ n is the SASI ID number (0-15)
|
||||
.IP
|
||||
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.
|
||||
Note: SASI usage is rare, and is typically limited to early Unix workstations and Sharp X68000 systems.
|
||||
|
||||
.SH EXAMPLES
|
||||
Launch RaSCSI with no emulated drives attached:
|
||||
|
@ -1,123 +1,97 @@
|
||||
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
|
||||
!! ------ The native file is rascsi.1. Re-run 'make docs' after updating\n\n
|
||||
rascsi(1) General Commands Manual rascsi(1)
|
||||
!! ------ The native file is rascsi.1. Re-run 'make docs' after updating
|
||||
|
||||
|
||||
rascsi(1) General Commands Manual rascsi(1)
|
||||
|
||||
NAME
|
||||
rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins
|
||||
|
||||
SYNOPSIS
|
||||
rascsi [-f[u00AE] FOLDER] [-g[u00AE] LOG_LEVEL] [-h] [-n VENDOR:PROD‐
|
||||
UCT:REVISION] [-p[u00AE] PORT] [-n TYPE] [-v] [-IDn FILE] [-HDn
|
||||
FILE]...
|
||||
rascsi [-f[u00AE] FOLDER] [-g[u00AE] LOG_LEVEL] [-h] [-n VENDOR:PRODUCT:REVISION] [-p[u00AE] PORT] [-r RESERVED_IDS] [-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"
|
||||
(the host computer). Note that SASI is considered rare and only used on
|
||||
very early Sharp X68000 computers.
|
||||
In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) devices 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
|
||||
of the FILE argument.
|
||||
hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used
|
||||
with X68000)
|
||||
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, 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
|
||||
SCSI emulation)
|
||||
mos: SCSI Magneto-optical image (XM6 SCSI MO image - typically only
|
||||
used with X68000)
|
||||
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
|
||||
0, you can use the following command:
|
||||
For example, if you want to specify an Apple-compatible HD image on ID 0, you can use the following command:
|
||||
sudo rascsi -ID0 /path/to/drive/hdimage.hda
|
||||
|
||||
Once RaSCSI starts, it will open a socket (default port is 6868) to al‐
|
||||
low external management commands. If another process is using the
|
||||
rascsi port, RaSCSI will terminate, since it is likely another instance
|
||||
of RaSCSI. Once RaSCSI has initialized, the rasctl utility can be used
|
||||
to send commands.
|
||||
Once RaSCSI starts, it will open a socket (default port is 6868) to allow external management commands. If another process is using the rascsi port, RaSCSI will terminate, since
|
||||
it is likely another instance of RaSCSI. Once RaSCSI has initialized, the rasctl utility can be used to send commands.
|
||||
|
||||
To quit RaSCSI, press Control + C. If it is running in the background,
|
||||
you can kill it using an INT signal.
|
||||
To quit RaSCSI, press Control + C. If it is running in the background, you can kill it using an INT signal.
|
||||
|
||||
OPTIONS
|
||||
-b BLOCK_SIZE
|
||||
The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
|
||||
|
||||
-f FOLDER
|
||||
The default folder for image files. For files in this folder no
|
||||
absolute path needs to be specified. The initial default folder
|
||||
is '~/images'.
|
||||
The default folder for image files. For files in this folder no absolute path needs to be specified. The initial default folder is '~/images'.
|
||||
|
||||
-g LOG_LEVEL
|
||||
The rascsi log level (trace, debug, info, warn, err, critical,
|
||||
off). The default log level is 'info'.
|
||||
The rascsi log level (trace, debug, info, warn, err, 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.
|
||||
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.
|
||||
|
||||
-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.
|
||||
-r RESERVED_IDS
|
||||
Comma-separated list of IDs to reserve. -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
|
||||
n is the SCSI ID number (0-7)
|
||||
|
||||
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.
|
||||
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 use for the SASI device.
|
||||
|
||||
Note: SASI usage is rare, and is typically limited to early
|
||||
Sharp X68000 systems.
|
||||
Note: SASI usage is rare, and is typically limited to early Unix workstations and Sharp X68000 systems.
|
||||
|
||||
EXAMPLES
|
||||
Launch RaSCSI with no emulated drives attached:
|
||||
rascsi
|
||||
|
||||
Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID
|
||||
2
|
||||
Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID 2
|
||||
rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso
|
||||
|
||||
Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw de‐
|
||||
vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter
|
||||
as ID 6:
|
||||
Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw 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
|
||||
|
||||
In case the fallocate command is available a much faster alternative to
|
||||
the dd command is:
|
||||
In case the fallocate command is available a much faster alternative to the dd command is:
|
||||
fallocate -l 104857600 /path/to/newimage.hda
|
||||
|
||||
SEE ALSO
|
||||
rasctl(1), scsimon(1)
|
||||
|
||||
Full documentation is available at:
|
||||
<https://www.github.com/akuker/RASCSI/wiki/>
|
||||
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
|
||||
|
||||
rascsi(1)
|
||||
rascsi(1)
|
||||
|
36
doc/rasctl.1
36
doc/rasctl.1
@ -8,6 +8,7 @@ rasctl \- Sends management commands to the rascsi process
|
||||
[\fB\-g\fR \fILOG_LEVEL\fR]
|
||||
[\fB\-h\fR \fIHOST\fR]
|
||||
[\fB\-p\fR \fIPORT\fR]
|
||||
[\fB\-r\fR \fIRESERVED_IDS\fR]
|
||||
[\fB\-v\fR]
|
||||
\fB\-i\fR \fIID\fR
|
||||
[\fB\-c\fR \fICMD\fR]
|
||||
@ -39,8 +40,11 @@ List all of the devices that are currently being emulated by RaSCSI, as well as
|
||||
.BR \-p\fI " " \fIPORT
|
||||
The rascsi port to connect to, default is 6868.
|
||||
.TP
|
||||
.BR \-r\fI " " \fIRESERVED_IDS
|
||||
Comma-separated list of IDs to reserve.
|
||||
.TP
|
||||
.BR \-s\fI
|
||||
Display the server-side configuration settings.
|
||||
Display server-side settings like available images or supported device types.
|
||||
.TP
|
||||
.BR \-v\fI " " \fI
|
||||
Display the rascsi version.
|
||||
@ -49,27 +53,33 @@ Display the rascsi version.
|
||||
ID is the SCSI ID that you want to control. (0-7)
|
||||
.TP
|
||||
.BR \-c\fI " " \fICMD
|
||||
Command is the operation being requested. options are:
|
||||
attach: attach disk
|
||||
detach: detach disk
|
||||
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)
|
||||
Command is the operation being requested. Options are:
|
||||
a(ttach): Attach disk
|
||||
d(etach): Detach disk
|
||||
i(nsert): Insert media (removable media devices only)
|
||||
e(ject): Eject media (removable media devices only)
|
||||
p(rotect): Write protect the medium (not for CD-ROMs, which are always read-only)
|
||||
u(nprotect): Remove write protection from the medium (not for CD-ROMs, which are always read-only)
|
||||
s(how): Display device information
|
||||
.IP
|
||||
eject, protect and unprotect are idempotent.
|
||||
.TP
|
||||
.BR \-b\fI " " \fIBLOCK_SIZE
|
||||
The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
|
||||
.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 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
|
||||
Specifies the device type. This type overrides the type derived from the file extension of the specified image. See the rascsi(1) man page for the available device types. For some types there are shortcuts (only the first letter is required):
|
||||
hd: SCSI hard disk drive
|
||||
rm: SCSI removable media drive
|
||||
cd: CD-ROM
|
||||
bridge: Bridge device (This is only applicable to the Sharp X68000)
|
||||
mo: Magneto-Optical disk
|
||||
bridge: Bridge device (Only applicable to the Sharp X68000)
|
||||
daynaport: DaynaPORT network adapter
|
||||
.TP
|
||||
.BR \-u\fI " " \fIVENDOR:PRODUCT:REVISION
|
||||
.BR \-n\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
|
||||
|
@ -1,84 +1,78 @@
|
||||
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
|
||||
!! ------ The native file is rasctl.1. Re-run 'make docs' after updating\n\n
|
||||
rascsi(1) General Commands Manual rascsi(1)
|
||||
!! ------ The native file is rasctl.1. Re-run 'make docs' after updating
|
||||
|
||||
|
||||
rascsi(1) General Commands Manual rascsi(1)
|
||||
|
||||
NAME
|
||||
rasctl - Sends management commands to the rascsi process
|
||||
|
||||
SYNOPSIS
|
||||
rasctl -l | -s | [-g LOG_LEVEL] [-h HOST] [-p PORT] [-v] -i ID [-c CMD]
|
||||
[-f FILE] [-n NAME] [-t TYPE] [-u UNIT]
|
||||
rasctl -l | -s | [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] [-v] -i ID [-c CMD] [-f FILE] [-n NAME] [-t TYPE] [-u UNIT]
|
||||
|
||||
DESCRIPTION
|
||||
rasctl Sends commands to the rascsi process to make configuration ad‐
|
||||
justments at runtime or to check the status of the devices.
|
||||
rasctl Sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the devices.
|
||||
|
||||
Either the -i or -l option should be specified at one time. Not both.
|
||||
|
||||
You do NOT need root privileges to use rasctl.
|
||||
|
||||
Note: The command and type arguments are case insensitive. Only the
|
||||
first letter of the command/type is evaluated by the tool.
|
||||
Note: The command and type arguments are case insensitive. Only the first letter of the command/type is evaluated by the tool.
|
||||
|
||||
OPTIONS
|
||||
-g LOG_LEVEL
|
||||
The rascsi log level to set (trace, debug, info, warn, err,
|
||||
critical, off)
|
||||
The rascsi log level to set (trace, debug, info, warn, err, critical, off)
|
||||
|
||||
-h HOST
|
||||
The rascsi host to connect to, default is 'localhost'.
|
||||
|
||||
-l List all of the devices that are currently being emulated by
|
||||
RaSCSI, as well as their current status.
|
||||
-l List all of the devices that are currently being emulated by RaSCSI, as well as their current status.
|
||||
|
||||
-p PORT
|
||||
The rascsi port to connect to, default is 6868.
|
||||
|
||||
-s Display the server-side configuration settings.
|
||||
-r RESERVED_IDS
|
||||
Comma-separated list of IDs to reserve.
|
||||
|
||||
-s Display server-side settings like available images or supported device types.
|
||||
|
||||
-v Display the rascsi version.
|
||||
|
||||
-i ID ID is the SCSI ID that you want to control. (0-7)
|
||||
|
||||
-c CMD Command is the operation being requested. options are:
|
||||
attach: attach disk
|
||||
detach: detach disk
|
||||
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)
|
||||
-c CMD Command is the operation being requested. Options are:
|
||||
a(ttach): Attach disk
|
||||
d(etach): Detach disk
|
||||
i(nsert): Insert media (removable media devices only)
|
||||
e(ject): Eject media (removable media devices only)
|
||||
p(rotect): Write protect the medium (not for CD-ROMs, which are always read-only)
|
||||
u(nprotect): Remove write protection from the medium (not for CD-ROMs, which are always read-only)
|
||||
s(how): Display device information
|
||||
|
||||
eject, protect and unprotect are idempotent.
|
||||
|
||||
-b BLOCK_SIZE
|
||||
The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
|
||||
|
||||
-f FILE
|
||||
Path to the disk image file. See the rascsi(1) man page for al‐
|
||||
lowable file types.
|
||||
Path to the disk image file. See the rascsi(1) man page for allowable file types.
|
||||
|
||||
-t TYPE
|
||||
Specifies the device type. This type overrides the type derived
|
||||
from the file extension of the specified image. See the
|
||||
rascsi(1) man page for the available device types. Legacy drive
|
||||
types are:
|
||||
hd: Hard disk (SCSI or SASI)
|
||||
mo: Magneto-Optical disk
|
||||
Specifies the device type. This type overrides the type derived from the file extension of the specified image. See the rascsi(1) man page for the available device types.
|
||||
For some types there are shortcuts (only the first letter is required):
|
||||
hd: SCSI hard disk drive
|
||||
rm: SCSI removable media drive
|
||||
cd: CD-ROM
|
||||
bridge: Bridge device (This is only applicable to the Sharp
|
||||
X68000)
|
||||
mo: Magneto-Optical disk
|
||||
bridge: Bridge device (Only applicable to the Sharp X68000)
|
||||
daynaport: DaynaPORT network adapter
|
||||
|
||||
-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.
|
||||
-n 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 automatically 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
|
||||
controller. (This is not common)
|
||||
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)
|
||||
|
||||
EXAMPLES
|
||||
Show a listing of all of the SCSI devices and their current status.
|
||||
@ -91,14 +85,12 @@ EXAMPLES
|
||||
| 0 | 1 | SCHD | /home/pi/harddisk.hda
|
||||
+----+----+------+-------------------------------------
|
||||
|
||||
Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with
|
||||
the contents of the file system image "HDIIMAGE0.HDS".
|
||||
Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the contents of the file system image "HDIIMAGE0.HDS".
|
||||
rasctl -i 0 -f HDIIMAGE0.HDS
|
||||
|
||||
SEE ALSO
|
||||
rascsi(1) scsimon(1)
|
||||
|
||||
Full documentation is available at:
|
||||
<https://www.github.com/akuker/RASCSI/wiki/>
|
||||
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
|
||||
|
||||
rascsi(1)
|
||||
rascsi(1)
|
||||
|
@ -8,7 +8,7 @@
|
||||
## ARM=x86_64 CROSS_COMPILE=x86_64-linux-gnu-cpp
|
||||
CROSS_COMPILE ?= arm-linux-gnueabihf-
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
CXX = $(CROSS_COMPILE)g++
|
||||
|
||||
## DEBUG=1 : A Debug build includes the debugger symbols
|
||||
@ -17,8 +17,8 @@ CXX = $(CROSS_COMPILE)g++
|
||||
DEBUG ?= 0
|
||||
ifeq ($(DEBUG), 1)
|
||||
# Debug CFLAGS
|
||||
CFLAGS += -DDISK_LOG -O0 -g -Wall -DDEBUG
|
||||
CXXFLAGS += -DDISK_LOG -O0 -g -Wall -DDEBUG
|
||||
CFLAGS += -O0 -g -Wall -DDEBUG
|
||||
CXXFLAGS += -O0 -g -Wall -DDEBUG
|
||||
BUILD_TYPE = Debug
|
||||
else
|
||||
# Release CFLAGS
|
||||
@ -27,7 +27,7 @@ else
|
||||
BUILD_TYPE = Release
|
||||
endif
|
||||
CFLAGS += -iquote . -MD -MP
|
||||
CXXFLAGS += -std=c++17 -iquote . -MD -MP
|
||||
CXXFLAGS += -std=c++17 -Wno-psabi -iquote . -MD -MP
|
||||
|
||||
## EXTRA_FLAGS : Can be used to pass special purpose flags
|
||||
CFLAGS += $(EXTRA_FLAGS)
|
||||
@ -42,7 +42,7 @@ CXXFLAGS += $(EXTRA_FLAGS)
|
||||
CONNECT_TYPE ?= FULLSPEC
|
||||
|
||||
ifdef CONNECT_TYPE
|
||||
CFLAGS += -DCONNECT_TYPE_$(CONNECT_TYPE)
|
||||
CFLAGS += -DCONNECT_TYPE_$(CONNECT_TYPE)
|
||||
CXXFLAGS += -DCONNECT_TYPE_$(CONNECT_TYPE)
|
||||
endif
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "gpiobus.h"
|
||||
#include "devices/scsi_host_bridge.h"
|
||||
#include "devices/scsi_daynaport.h"
|
||||
#include "exceptions.h"
|
||||
#include <sstream>
|
||||
|
||||
//===========================================================================
|
||||
@ -110,18 +109,6 @@ void SASIDEV::Connect(int id, BUS *bus)
|
||||
ctrl.bus = bus;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Get the logical unit
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Disk* SASIDEV::GetUnit(int no)
|
||||
{
|
||||
ASSERT(no < UnitMax);
|
||||
|
||||
return ctrl.unit[no];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Set the logical unit
|
||||
@ -136,43 +123,18 @@ void SASIDEV::SetUnit(int no, Disk *dev)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Check to see if this has a valid logical unit
|
||||
// Check to see if this has a valid LUN
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SASIDEV::HasUnit()
|
||||
bool SASIDEV::HasUnit()
|
||||
{
|
||||
for (int i = 0; i < UnitMax; i++) {
|
||||
if (ctrl.unit[i]) {
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Get internal data
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::GetCTRL(ctrl_t *buffer)
|
||||
{
|
||||
ASSERT(buffer);
|
||||
|
||||
// reference the internal structure
|
||||
*buffer = ctrl;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Get a busy unit
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Disk* SASIDEV::GetBusyUnit()
|
||||
{
|
||||
// Logical Unit
|
||||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||||
return ctrl.unit[lun];
|
||||
return false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -258,7 +220,7 @@ void SASIDEV::BusFree()
|
||||
{
|
||||
// Phase change
|
||||
if (ctrl.phase != BUS::busfree) {
|
||||
LOGINFO("Bus free phase");
|
||||
LOGTRACE("%s Bus free phase", __PRETTY_FUNCTION__);
|
||||
|
||||
// Phase Setting
|
||||
ctrl.phase = BUS::busfree;
|
||||
@ -297,7 +259,7 @@ void SASIDEV::Selection()
|
||||
return;
|
||||
}
|
||||
|
||||
// Return if there is no unit
|
||||
// Return if there is no valid LUN
|
||||
if (!HasUnit()) {
|
||||
return;
|
||||
}
|
||||
@ -320,7 +282,7 @@ void SASIDEV::Selection()
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Command phase
|
||||
// Command phase (used by SASI and SCSI)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::Command()
|
||||
@ -369,14 +331,7 @@ void SASIDEV::Command()
|
||||
ctrl.blocks = 0;
|
||||
|
||||
// Execution Phase
|
||||
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);
|
||||
}
|
||||
Execute();
|
||||
}
|
||||
}
|
||||
|
||||
@ -397,14 +352,9 @@ void SASIDEV::Execute()
|
||||
ctrl.blocks = 1;
|
||||
ctrl.execstart = SysTimer::GetTimerLow();
|
||||
|
||||
// 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.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]) {
|
||||
switch ((SASIDEV::sasi_command)ctrl.cmd[0]) {
|
||||
// TEST UNIT READY
|
||||
case SASIDEV::eCmdTestUnitReady:
|
||||
CmdTestUnitReady();
|
||||
@ -427,7 +377,7 @@ void SASIDEV::Execute()
|
||||
|
||||
// REASSIGN BLOCKS
|
||||
case SASIDEV::eCmdReassign:
|
||||
CmdReassign();
|
||||
CmdReassignBlocks();
|
||||
return;
|
||||
|
||||
// READ(6)
|
||||
@ -468,12 +418,23 @@ void SASIDEV::Execute()
|
||||
case SASIDEV::eCmdInvalid:
|
||||
CmdSpecify();
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Unsupported command
|
||||
CmdInvalid();
|
||||
LOGWARN("%s ID %d received unsupported command: $%02X", __PRETTY_FUNCTION__, GetSCSIID(), (BYTE)ctrl.cmd[0]);
|
||||
|
||||
// Logical Unit
|
||||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||||
if (ctrl.unit[lun]) {
|
||||
// Command processing on drive
|
||||
ctrl.unit[lun]->SetStatusCode(STATUS_INVALIDCMD);
|
||||
}
|
||||
|
||||
// Failure (Error)
|
||||
Error();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -497,6 +458,8 @@ void SASIDEV::Status()
|
||||
SysTimer::SleepUsec(5);
|
||||
}
|
||||
|
||||
LOGTRACE("%s Status phase", __PRETTY_FUNCTION__);
|
||||
|
||||
// Phase Setting
|
||||
ctrl.phase = BUS::status;
|
||||
|
||||
@ -522,7 +485,7 @@ void SASIDEV::Status()
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Message in phase
|
||||
// Message in phase (used by SASI and SCSI)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::MsgIn()
|
||||
@ -553,7 +516,7 @@ void SASIDEV::MsgIn()
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Data-in Phase
|
||||
// Data-in Phase (used by SASI and SCSI)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::DataIn()
|
||||
@ -601,7 +564,7 @@ void SASIDEV::DataIn()
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Data out phase
|
||||
// Data out phase (used by SASI and SCSI)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::DataOut()
|
||||
@ -675,24 +638,10 @@ void SASIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::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;
|
||||
|
||||
#if defined(DISK_LOG)
|
||||
LOGWARN("Error occured (going to status phase)");
|
||||
#endif // DISK_LOG
|
||||
|
||||
// status phase
|
||||
Status();
|
||||
}
|
||||
@ -706,18 +655,8 @@ void SASIDEV::CmdTestUnitReady()
|
||||
{
|
||||
LOGTRACE("%s TEST UNIT READY Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->TestUnitReady(ctrl.cmd);
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// status phase
|
||||
Status();
|
||||
ctrl.device->TestUnitReady(this);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -729,18 +668,8 @@ void SASIDEV::CmdRezero()
|
||||
{
|
||||
LOGTRACE( "%s REZERO UNIT Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->Rezero(ctrl.cmd);
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// status phase
|
||||
Status();
|
||||
ctrl.device->Rezero(this);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -752,28 +681,8 @@ void SASIDEV::CmdRequestSense()
|
||||
{
|
||||
LOGTRACE( "%s REQUEST SENSE Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun;
|
||||
try {
|
||||
lun = 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.
|
||||
|
||||
// LUN 0 can be assumed to be present (required to call RequestSense() below)
|
||||
lun = 0;
|
||||
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_LUN);
|
||||
}
|
||||
|
||||
ctrl.length = ctrl.unit[lun]->RequestSense(ctrl.cmd, ctrl.buffer);
|
||||
ASSERT(ctrl.length > 0);
|
||||
|
||||
LOGTRACE("%s Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, ctrl.buffer[2], ctrl.buffer[12]);
|
||||
|
||||
// Read phase
|
||||
DataIn();
|
||||
// Command processing on drive
|
||||
ctrl.device->RequestSense(this);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -785,18 +694,8 @@ void SASIDEV::CmdFormat()
|
||||
{
|
||||
LOGTRACE( "%s FORMAT UNIT Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->Format(ctrl.cmd);
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// status phase
|
||||
Status();
|
||||
ctrl.device->FormatUnit(this);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -804,22 +703,12 @@ void SASIDEV::CmdFormat()
|
||||
// REASSIGN BLOCKS
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::CmdReassign()
|
||||
void SASIDEV::CmdReassignBlocks()
|
||||
{
|
||||
LOGTRACE("%s REASSIGN BLOCKS Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->Reassign(ctrl.cmd);
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// status phase
|
||||
Status();
|
||||
ctrl.device->ReassignBlocks(this);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -865,8 +754,6 @@ void SASIDEV::CmdReleaseUnit()
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::CmdRead6()
|
||||
{
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Get record number and block number
|
||||
DWORD record = ctrl.cmd[1] & 0x1f;
|
||||
record <<= 8;
|
||||
@ -878,33 +765,11 @@ 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=%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);
|
||||
LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, (int)ctrl.length);
|
||||
|
||||
// The DaynaPort will respond a status of 0x02 when a read of size 1 occurs.
|
||||
if (ctrl.length <= 0 && !ctrl.unit[lun]->IsDaynaPort()) {
|
||||
ctrl.length = ctrl.device->Read(ctrl.cmd, ctrl.buffer, record);
|
||||
if (ctrl.length <= 0) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
return;
|
||||
@ -917,59 +782,6 @@ void SASIDEV::CmdRead6()
|
||||
DataIn();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 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();
|
||||
|
||||
// Error if not a DaynaPort device
|
||||
if (!ctrl.unit[lun]->IsDaynaPort()) {
|
||||
LOGERROR("Received DaynaPortWrite for a non-DaynaPort device");
|
||||
Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reallocate buffer (because it is not transfer for each block)
|
||||
if (ctrl.bufsize < DAYNAPORT_BUFFER_SIZE) {
|
||||
free(ctrl.buffer);
|
||||
ctrl.bufsize = DAYNAPORT_BUFFER_SIZE;
|
||||
ctrl.buffer = (BYTE *)malloc(ctrl.bufsize);
|
||||
}
|
||||
|
||||
DWORD data_format = ctrl.cmd[5];
|
||||
|
||||
if(data_format == 0x00){
|
||||
ctrl.length = (WORD)ctrl.cmd[4] + ((WORD)ctrl.cmd[3] << 8);
|
||||
}
|
||||
else if (data_format == 0x80){
|
||||
ctrl.length = (WORD)ctrl.cmd[4] + ((WORD)ctrl.cmd[3] << 8) + 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)data_format);
|
||||
}
|
||||
LOGTRACE("%s length: %04X (%d) format: %02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.length, (int)ctrl.length, (unsigned int)data_format);
|
||||
|
||||
if (ctrl.length <= 0) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set next block
|
||||
ctrl.blocks = 1;
|
||||
ctrl.next = 1;
|
||||
|
||||
// Light phase
|
||||
DataOut();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// WRITE(6)
|
||||
@ -977,14 +789,6 @@ void SASIDEV::DaynaPortWrite()
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::CmdWrite6()
|
||||
{
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Special receive function for the DaynaPort
|
||||
if (ctrl.unit[lun]->IsDaynaPort()){
|
||||
DaynaPortWrite();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get record number and block number
|
||||
DWORD record = ctrl.cmd[1] & 0x1f;
|
||||
record <<= 8;
|
||||
@ -996,24 +800,13 @@ void SASIDEV::CmdWrite6()
|
||||
ctrl.blocks = 0x100;
|
||||
}
|
||||
|
||||
// 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);
|
||||
ctrl.length = ctrl.device->WriteCheck(record);
|
||||
if (ctrl.length <= 0) {
|
||||
// Failure (Error)
|
||||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::WRITE_PROTECTED);
|
||||
Error();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1033,18 +826,8 @@ void SASIDEV::CmdSeek6()
|
||||
{
|
||||
LOGTRACE("%s SEEK(6) Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->Seek(ctrl.cmd);
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// status phase
|
||||
Status();
|
||||
ctrl.device->Seek6(this);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -1056,10 +839,8 @@ void SASIDEV::CmdAssign()
|
||||
{
|
||||
LOGTRACE("%s ASSIGN Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->Assign(ctrl.cmd);
|
||||
bool status = ctrl.device->CheckReady();
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
@ -1082,10 +863,8 @@ void SASIDEV::CmdSpecify()
|
||||
{
|
||||
LOGTRACE("%s SPECIFY Command ", __PRETTY_FUNCTION__);
|
||||
|
||||
DWORD lun = GetLun();
|
||||
|
||||
// Command processing on drive
|
||||
BOOL status = ctrl.unit[lun]->Assign(ctrl.cmd);
|
||||
bool status = ctrl.device->CheckReady();
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
Error();
|
||||
@ -1099,18 +878,6 @@ void SASIDEV::CmdSpecify()
|
||||
DataOut();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Unsupported command
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::CmdInvalid()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Data transfer
|
||||
@ -1124,15 +891,12 @@ void SASIDEV::CmdInvalid()
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::Send()
|
||||
{
|
||||
int len;
|
||||
BOOL result;
|
||||
|
||||
ASSERT(!ctrl.bus->GetREQ());
|
||||
ASSERT(ctrl.bus->GetIO());
|
||||
|
||||
// Check that the length isn't 0
|
||||
if (ctrl.length != 0) {
|
||||
len = ctrl.bus->SendHandShake(
|
||||
int len = ctrl.bus->SendHandShake(
|
||||
&ctrl.buffer[ctrl.offset], ctrl.length, BUS::SEND_NO_DELAY);
|
||||
|
||||
// If you can not send it all, move on to the status phase
|
||||
@ -1153,7 +917,7 @@ void SASIDEV::Send()
|
||||
|
||||
// Remove block and initialize the result
|
||||
ctrl.blocks--;
|
||||
result = TRUE;
|
||||
bool result = true;
|
||||
|
||||
// Process after data collection (read/data-in only)
|
||||
if (ctrl.phase == BUS::datain) {
|
||||
@ -1216,9 +980,6 @@ void SASIDEV::Send()
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::Receive()
|
||||
{
|
||||
int len;
|
||||
BOOL result;
|
||||
|
||||
// REQ is low
|
||||
ASSERT(!ctrl.bus->GetREQ());
|
||||
ASSERT(!ctrl.bus->GetIO());
|
||||
@ -1226,7 +987,7 @@ void SASIDEV::Receive()
|
||||
// Length != 0 if received
|
||||
if (ctrl.length != 0) {
|
||||
// Receive
|
||||
len = ctrl.bus->ReceiveHandShake(
|
||||
int len = ctrl.bus->ReceiveHandShake(
|
||||
&ctrl.buffer[ctrl.offset], ctrl.length);
|
||||
LOGDEBUG("%s Received %d bytes", __PRETTY_FUNCTION__, len);
|
||||
|
||||
@ -1248,16 +1009,16 @@ void SASIDEV::Receive()
|
||||
|
||||
// Remove the control block and initialize the result
|
||||
ctrl.blocks--;
|
||||
result = TRUE;
|
||||
bool result = true;
|
||||
|
||||
// Process the data out phase
|
||||
if (ctrl.phase == BUS::dataout) {
|
||||
if (ctrl.blocks == 0) {
|
||||
// End with this buffer
|
||||
result = XferOut(FALSE);
|
||||
result = XferOut(false);
|
||||
} else {
|
||||
// Continue to next buffer (set offset, length)
|
||||
result = XferOut(TRUE);
|
||||
result = XferOut(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1299,7 +1060,7 @@ void SASIDEV::Receive()
|
||||
// *Reset offset and length
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SASIDEV::XferIn(BYTE *buf)
|
||||
bool SASIDEV::XferIn(BYTE *buf)
|
||||
{
|
||||
ASSERT(ctrl.phase == BUS::datain);
|
||||
LOGTRACE("%s ctrl.cmd[0]=%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]);
|
||||
@ -1307,7 +1068,7 @@ BOOL SASIDEV::XferIn(BYTE *buf)
|
||||
// Logical Unit
|
||||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||||
if (!ctrl.unit[lun]) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Limited to read commands
|
||||
@ -1322,7 +1083,7 @@ BOOL SASIDEV::XferIn(BYTE *buf)
|
||||
// If there is an error, go to the status phase
|
||||
if (ctrl.length <= 0) {
|
||||
// Cancel data-in
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If things are normal, work setting
|
||||
@ -1332,11 +1093,11 @@ BOOL SASIDEV::XferIn(BYTE *buf)
|
||||
// Other (impossible)
|
||||
default:
|
||||
ASSERT(FALSE);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Succeeded in setting the buffer
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -1345,23 +1106,23 @@ BOOL SASIDEV::XferIn(BYTE *buf)
|
||||
// *If cont=true, reset the offset and length
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SASIDEV::XferOut(BOOL cont)
|
||||
bool SASIDEV::XferOut(bool cont)
|
||||
{
|
||||
ASSERT(ctrl.phase == BUS::dataout);
|
||||
|
||||
// Logical Unit
|
||||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||||
if (!ctrl.unit[lun]) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
Disk *device = ctrl.unit[lun];
|
||||
|
||||
switch ((SASIDEV::scsi_command) ctrl.cmd[0]) {
|
||||
case SASIDEV::eCmdModeSelect:
|
||||
switch (ctrl.cmd[0]) {
|
||||
case SASIDEV::eCmdModeSelect6:
|
||||
case SASIDEV::eCmdModeSelect10:
|
||||
if (!ctrl.unit[lun]->ModeSelect(
|
||||
ctrl.cmd, ctrl.buffer, ctrl.offset)) {
|
||||
if (!device->ModeSelect(ctrl.cmd, ctrl.buffer, ctrl.offset)) {
|
||||
// MODE SELECT failed
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1372,10 +1133,10 @@ BOOL SASIDEV::XferOut(BOOL cont)
|
||||
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()) {
|
||||
if (!((SCSIBR*)ctrl.unit[lun])->SendMessage10(ctrl.cmd, ctrl.buffer)) {
|
||||
if (device->IsBridge()) {
|
||||
if (!((SCSIBR*)device)->SendMessage10(ctrl.cmd, ctrl.buffer)) {
|
||||
// write failed
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If normal, work setting
|
||||
@ -1384,14 +1145,12 @@ 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)) {
|
||||
// TODO This class must not know about DaynaPort
|
||||
if (device->IsDaynaPort()) {
|
||||
if (!device->Write(ctrl.cmd, ctrl.buffer, ctrl.length)) {
|
||||
// write failed
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
LOGTRACE("%s Done with DaynaPort Write", __PRETTY_FUNCTION__);
|
||||
|
||||
// If normal, work setting
|
||||
ctrl.offset = 0;
|
||||
@ -1399,10 +1158,9 @@ BOOL SASIDEV::XferOut(BOOL cont)
|
||||
break;
|
||||
}
|
||||
|
||||
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)) {
|
||||
if (!device->Write(ctrl.cmd, ctrl.buffer, ctrl.next - 1)) {
|
||||
// Write failed
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If you do not need the next block, end here
|
||||
@ -1412,10 +1170,10 @@ BOOL SASIDEV::XferOut(BOOL cont)
|
||||
}
|
||||
|
||||
// Check the next block
|
||||
ctrl.length = ctrl.unit[lun]->WriteCheck(ctrl.next - 1);
|
||||
ctrl.length = device->WriteCheck(ctrl.next - 1);
|
||||
if (ctrl.length <= 0) {
|
||||
// Cannot write
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If normal, work setting
|
||||
@ -1437,7 +1195,7 @@ BOOL SASIDEV::XferOut(BOOL cont)
|
||||
}
|
||||
|
||||
// Buffer saved successfully
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -1454,9 +1212,10 @@ void SASIDEV::FlushUnit()
|
||||
if (!ctrl.unit[lun]) {
|
||||
return;
|
||||
}
|
||||
Disk *device = ctrl.unit[lun];
|
||||
|
||||
// WRITE system only
|
||||
switch ((SASIDEV::scsi_command)ctrl.cmd[0]) {
|
||||
switch ((SASIDEV::sasi_command)ctrl.cmd[0]) {
|
||||
case SASIDEV::eCmdWrite6:
|
||||
case SASIDEV::eCmdWrite10:
|
||||
case SASIDEV::eCmdWrite16:
|
||||
@ -1464,7 +1223,7 @@ void SASIDEV::FlushUnit()
|
||||
case SASIDEV::eCmdVerify16:
|
||||
break;
|
||||
|
||||
case SASIDEV::eCmdModeSelect:
|
||||
case SASIDEV::eCmdModeSelect6:
|
||||
case SASIDEV::eCmdModeSelect10:
|
||||
// Debug code related to Issue #2 on github, where we get an unhandled Mode Select when
|
||||
// the mac is rebooted
|
||||
@ -1480,19 +1239,18 @@ void SASIDEV::FlushUnit()
|
||||
LOGWARN(" Reserved: %02X\n",(WORD)ctrl.cmd[5]);
|
||||
LOGWARN(" Ctrl Len: %08X\n",(WORD)ctrl.length);
|
||||
|
||||
if (!ctrl.unit[lun]->ModeSelect(
|
||||
if (!device->ModeSelect(
|
||||
ctrl.cmd, ctrl.buffer, ctrl.offset)) {
|
||||
// MODE SELECT failed
|
||||
LOGWARN("Error occured while processing Mode Select command %02X\n", (unsigned char)ctrl.cmd[0]);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SASIDEV::eCmdSetIfaceMode:
|
||||
LOGWARN("%s Trying to flush a command set interface mode. This should be a daynaport?", __PRETTY_FUNCTION__);
|
||||
break;
|
||||
|
||||
case SASIDEV::eCmdSetMcastAddr:
|
||||
// TODO: Eventually, we should store off the multicast address configuration data here...
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGWARN("Received an unexpected flush command %02X!!!!!\n",(WORD)ctrl.cmd[0]);
|
||||
// The following statement makes debugging a huge pain. You can un-comment it
|
||||
@ -1501,68 +1259,3 @@ void SASIDEV::FlushUnit()
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DISK_LOG
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Get the current phase as a string
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SASIDEV::GetPhaseStr(char *str)
|
||||
{
|
||||
switch(this->GetPhase())
|
||||
{
|
||||
case BUS::busfree:
|
||||
strcpy(str,"busfree ");
|
||||
break;
|
||||
case BUS::arbitration:
|
||||
strcpy(str,"arbitration");
|
||||
break;
|
||||
case BUS::selection:
|
||||
strcpy(str,"selection ");
|
||||
break;
|
||||
case BUS::reselection:
|
||||
strcpy(str,"reselection");
|
||||
break;
|
||||
case BUS::command:
|
||||
strcpy(str,"command ");
|
||||
break;
|
||||
case BUS::execute:
|
||||
strcpy(str,"execute ");
|
||||
break;
|
||||
case BUS::datain:
|
||||
strcpy(str,"datain ");
|
||||
break;
|
||||
case BUS::dataout:
|
||||
strcpy(str,"dataout ");
|
||||
break;
|
||||
case BUS::status:
|
||||
strcpy(str,"status ");
|
||||
break;
|
||||
case BUS::msgin:
|
||||
strcpy(str,"msgin ");
|
||||
break;
|
||||
case BUS::msgout:
|
||||
strcpy(str,"msgout ");
|
||||
break;
|
||||
case BUS::reserved:
|
||||
strcpy(str,"reserved ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Validate LUN
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DWORD SASIDEV::GetLun()
|
||||
{
|
||||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||||
if (!ctrl.unit[lun]) {
|
||||
throw lun_exception(lun);
|
||||
}
|
||||
|
||||
return lun;
|
||||
}
|
||||
|
@ -15,11 +15,11 @@
|
||||
//---------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "../rascsi.h"
|
||||
#include "os.h"
|
||||
#include "scsi.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
#include "xm6.h"
|
||||
|
||||
|
||||
//===========================================================================
|
||||
@ -31,76 +31,54 @@ class SASIDEV
|
||||
{
|
||||
protected:
|
||||
enum scsi_message_code : BYTE {
|
||||
eMsgCodeAbort = 0x06,
|
||||
eMsgCodeAbortTag = 0x0D,
|
||||
eMsgCodeBusDeviceReset = 0x0C,
|
||||
eMsgCodeClearQueue = 0x0E,
|
||||
eMsgCodeCommandComplete = 0x00,
|
||||
eMsgCodeDisconnect = 0x04,
|
||||
eMsgCodeIdentify = 0x80,
|
||||
eMsgCodeIgnoreWideResidue = 0x23, // (Two Bytes)
|
||||
eMsgCodeInitiateRecovery = 0x0F,
|
||||
eMsgCodeInitiatorDetectedError = 0x05,
|
||||
eMsgCodeLinkedCommandComplete = 0x0A,
|
||||
eMsgCodeAbort = 0x06,
|
||||
eMsgCodeAbortTag = 0x0D,
|
||||
eMsgCodeBusDeviceReset = 0x0C,
|
||||
eMsgCodeClearQueue = 0x0E,
|
||||
eMsgCodeCommandComplete = 0x00,
|
||||
eMsgCodeDisconnect = 0x04,
|
||||
eMsgCodeIdentify = 0x80,
|
||||
eMsgCodeIgnoreWideResidue = 0x23, // (Two Bytes)
|
||||
eMsgCodeInitiateRecovery = 0x0F,
|
||||
eMsgCodeInitiatorDetectedError = 0x05,
|
||||
eMsgCodeLinkedCommandComplete = 0x0A,
|
||||
eMsgCodeLinkedCommandCompleteWithFlag = 0x0B,
|
||||
eMsgCodeMessageParityError = 0x09,
|
||||
eMsgCodeMessageReject = 0x07,
|
||||
eMsgCodeNoOperation = 0x08,
|
||||
eMsgCodeHeadOfQueueTag = 0x21,
|
||||
eMsgCodeOrderedQueueTag = 0x22,
|
||||
eMsgCodeSimpleQueueTag = 0x20,
|
||||
eMsgCodeReleaseRecovery = 0x10,
|
||||
eMsgCodeRestorePointers = 0x03,
|
||||
eMsgCodeSaveDataPointer = 0x02,
|
||||
eMsgCodeTerminateIOProcess = 0x11,
|
||||
eMsgCodeMessageParityError = 0x09,
|
||||
eMsgCodeMessageReject = 0x07,
|
||||
eMsgCodeNoOperation = 0x08,
|
||||
eMsgCodeHeadOfQueueTag = 0x21,
|
||||
eMsgCodeOrderedQueueTag = 0x22,
|
||||
eMsgCodeSimpleQueueTag = 0x20,
|
||||
eMsgCodeReleaseRecovery = 0x10,
|
||||
eMsgCodeRestorePointers = 0x03,
|
||||
eMsgCodeSaveDataPointer = 0x02,
|
||||
eMsgCodeTerminateIOProcess = 0x11
|
||||
};
|
||||
|
||||
protected:
|
||||
enum scsi_command : int {
|
||||
eCmdTestUnitReady = 0x00,
|
||||
eCmdRezero = 0x01,
|
||||
eCmdRequestSense = 0x03,
|
||||
eCmdFormat = 0x04,
|
||||
eCmdReassign = 0x07,
|
||||
eCmdRead6 = 0x08,
|
||||
eCmdRetrieveStats = 0x09, // DaynaPort specific command
|
||||
eCmdWrite6 = 0x0A,
|
||||
eCmdSeek6 = 0x0B,
|
||||
eCmdSetIfaceMode = 0x0C, // DaynaPort specific command
|
||||
eCmdSetMcastAddr = 0x0D, // DaynaPort specific command
|
||||
eCmdEnableInterface = 0x0E, // DaynaPort specific command
|
||||
eCmdInquiry = 0x12,
|
||||
eCmdModeSelect = 0x15,
|
||||
eCmdReserve6 = 0x16,
|
||||
eCmdRelease6 = 0x17,
|
||||
eCmdModeSense = 0x1A,
|
||||
eCmdStartStop = 0x1B,
|
||||
eCmdSendDiag = 0x1D,
|
||||
eCmdRemoval = 0x1E,
|
||||
eCmdReadCapacity10 = 0x25,
|
||||
eCmdRead10 = 0x28,
|
||||
eCmdWrite10 = 0x2A,
|
||||
eCmdSeek10 = 0x2B,
|
||||
eCmdVerify10 = 0x2E,
|
||||
eCmdVerify = 0x2F,
|
||||
eCmdSynchronizeCache = 0x35,
|
||||
eCmdReadDefectData10 = 0x37,
|
||||
eCmdReadToc = 0x43,
|
||||
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.
|
||||
private:
|
||||
enum sasi_command : int {
|
||||
eCmdTestUnitReady = 0x00,
|
||||
eCmdRezero = 0x01,
|
||||
eCmdRequestSense = 0x03,
|
||||
eCmdFormat = 0x06,
|
||||
eCmdReassign = 0x07,
|
||||
eCmdRead6 = 0x08,
|
||||
eCmdWrite6 = 0x0A,
|
||||
eCmdSeek6 = 0x0B,
|
||||
eCmdSetMcastAddr = 0x0D, // DaynaPort specific command
|
||||
eCmdModeSelect6 = 0x15,
|
||||
eCmdReserve6 = 0x16,
|
||||
eCmdRelease6 = 0x17,
|
||||
eCmdRead10 = 0x28,
|
||||
eCmdWrite10 = 0x2A,
|
||||
eCmdVerify10 = 0x2E,
|
||||
eCmdVerify = 0x2F,
|
||||
eCmdModeSelect10 = 0x55,
|
||||
eCmdRead16 = 0x88,
|
||||
eCmdWrite16 = 0x8A,
|
||||
eCmdVerify16 = 0x8F,
|
||||
eCmdInvalid = 0xC2,
|
||||
eCmdSasiCmdAssign = 0x0E
|
||||
};
|
||||
|
||||
public:
|
||||
@ -110,6 +88,7 @@ public:
|
||||
|
||||
const int UNKNOWN_SCSI_ID = -1;
|
||||
const int DEFAULT_BUFFER_SIZE = 0x1000;
|
||||
// TODO Remove this duplicate
|
||||
const int DAYNAPORT_BUFFER_SIZE = 0x1000000;
|
||||
|
||||
// For timing adjustments
|
||||
@ -122,7 +101,7 @@ public:
|
||||
typedef struct {
|
||||
// General
|
||||
BUS::phase_t phase; // Transition phase
|
||||
int m_scsi_id; // Controller ID (0-7)
|
||||
int m_scsi_id; // Controller ID (0-7)
|
||||
BUS *bus; // Bus
|
||||
|
||||
// commands
|
||||
@ -136,13 +115,15 @@ public:
|
||||
// Transfer
|
||||
BYTE *buffer; // Transfer data buffer
|
||||
int bufsize; // Transfer data buffer size
|
||||
DWORD blocks; // Number of transfer block
|
||||
uint32_t blocks; // Number of transfer block
|
||||
DWORD next; // Next record
|
||||
DWORD offset; // Transfer offset
|
||||
DWORD length; // Transfer remaining length
|
||||
|
||||
// Logical unit
|
||||
Disk *unit[UnitMax];
|
||||
|
||||
Disk *device;
|
||||
} ctrl_t;
|
||||
|
||||
public:
|
||||
@ -158,22 +139,24 @@ public:
|
||||
void Connect(int id, BUS *sbus); // Controller connection
|
||||
Disk* GetUnit(int no); // Get logical unit
|
||||
void SetUnit(int no, Disk *dev); // Logical unit setting
|
||||
BOOL HasUnit(); // Has a valid logical unit
|
||||
bool HasUnit(); // Has a valid logical unit
|
||||
|
||||
// Other
|
||||
BUS::phase_t GetPhase() {return ctrl.phase;} // Get the phase
|
||||
#ifdef DISK_LOG
|
||||
|
||||
// Function to get the current phase as a String.
|
||||
void GetPhaseStr(char *str);
|
||||
#endif
|
||||
|
||||
int GetSCSIID() {return ctrl.m_scsi_id;} // Get the ID
|
||||
void GetCTRL(ctrl_t *buffer); // Get the internal information
|
||||
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
|
||||
ctrl_t* GetCtrl() { return &ctrl; } // Get the internal information address
|
||||
virtual bool IsSASI() const { return true; } // SASI Check
|
||||
virtual bool IsSCSI() const { return false; } // SCSI check
|
||||
|
||||
public:
|
||||
void DataIn(); // Data in phase
|
||||
void Status(); // Status phase
|
||||
void MsgIn(); // Message in phase
|
||||
void DataOut(); // Data out phase
|
||||
|
||||
virtual void Error(ERROR_CODES::sense_key sense_key = ERROR_CODES::sense_key::NO_SENSE,
|
||||
ERROR_CODES::asc = ERROR_CODES::asc::NO_ADDITIONAL_SENSE_INFORMATION); // Common error handling
|
||||
|
||||
protected:
|
||||
// Phase processing
|
||||
@ -181,19 +164,13 @@ protected:
|
||||
virtual void Selection(); // Selection phase
|
||||
virtual void Command(); // Command phase
|
||||
virtual void Execute(); // Execution phase
|
||||
void Status(); // Status phase
|
||||
void MsgIn(); // Message in phase
|
||||
void DataIn(); // Data in phase
|
||||
void DataOut(); // Data out phase
|
||||
virtual void Error(ERROR_CODES::sense_key sense_key = ERROR_CODES::sense_key::NO_SENSE,
|
||||
ERROR_CODES::asc = ERROR_CODES::asc::NO_ADDITIONAL_SENSE_INFORMATION); // Common error handling
|
||||
|
||||
// commands
|
||||
// Commands
|
||||
void CmdTestUnitReady(); // TEST UNIT READY command
|
||||
void CmdRezero(); // REZERO UNIT command
|
||||
void CmdRequestSense(); // REQUEST SENSE command
|
||||
void CmdFormat(); // FORMAT command
|
||||
void CmdReassign(); // REASSIGN BLOCKS command
|
||||
void CmdReassignBlocks(); // REASSIGN BLOCKS command
|
||||
void CmdReserveUnit(); // RESERVE UNIT command
|
||||
void CmdReleaseUnit(); // RELEASE UNIT command
|
||||
void CmdRead6(); // READ(6) command
|
||||
@ -201,21 +178,17 @@ protected:
|
||||
void CmdSeek6(); // SEEK(6) command
|
||||
void CmdAssign(); // ASSIGN command
|
||||
void CmdSpecify(); // SPECIFY command
|
||||
void CmdInvalid(); // Unsupported command
|
||||
void DaynaPortWrite(); // DaynaPort specific 'write' operation
|
||||
// データ転送
|
||||
virtual void Send(); // Send data
|
||||
|
||||
// Data transfer
|
||||
virtual void Send(); // Send data
|
||||
virtual void Receive(); // Receive data
|
||||
|
||||
BOOL XferIn(BYTE* buf); // Data transfer IN
|
||||
BOOL XferOut(BOOL cont); // Data transfer OUT
|
||||
bool XferIn(BYTE* buf); // Data transfer IN
|
||||
bool XferOut(bool cont); // Data transfer OUT
|
||||
|
||||
// Special operations
|
||||
void FlushUnit(); // Flush the logical unit
|
||||
|
||||
DWORD GetLun(); // Get the validated LUN
|
||||
|
||||
protected:
|
||||
ctrl_t ctrl; // Internal data
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,7 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "controllers/sasidev_ctrl.h"
|
||||
#include <map>
|
||||
|
||||
@ -24,7 +25,50 @@
|
||||
//===========================================================================
|
||||
class SCSIDEV : public SASIDEV
|
||||
{
|
||||
|
||||
public:
|
||||
enum scsi_command : int {
|
||||
eCmdTestUnitReady = 0x00,
|
||||
eCmdRezero = 0x01,
|
||||
eCmdRequestSense = 0x03,
|
||||
eCmdFormat = 0x04,
|
||||
eCmdReassign = 0x07,
|
||||
eCmdRead6 = 0x08,
|
||||
eCmdRetrieveStats = 0x09, // DaynaPort specific command
|
||||
eCmdWrite6 = 0x0A,
|
||||
eCmdSeek6 = 0x0B,
|
||||
eCmdSetIfaceMode = 0x0C, // DaynaPort specific command
|
||||
eCmdSetMcastAddr = 0x0D, // DaynaPort specific command
|
||||
eCmdEnableInterface = 0x0E, // DaynaPort specific command
|
||||
eCmdInquiry = 0x12,
|
||||
eCmdModeSelect6 = 0x15,
|
||||
eCmdReserve6 = 0x16,
|
||||
eCmdRelease6 = 0x17,
|
||||
eCmdModeSense6 = 0x1A,
|
||||
eCmdStartStop = 0x1B,
|
||||
eCmdSendDiag = 0x1D,
|
||||
eCmdRemoval = 0x1E,
|
||||
eCmdReadCapacity10 = 0x25,
|
||||
eCmdRead10 = 0x28,
|
||||
eCmdWrite10 = 0x2A,
|
||||
eCmdSeek10 = 0x2B,
|
||||
eCmdVerify10 = 0x2F,
|
||||
eCmdSynchronizeCache10 = 0x35,
|
||||
eCmdReadDefectData10 = 0x37,
|
||||
eCmdReadToc = 0x43,
|
||||
eCmdGetEventStatusNotification = 0x4a,
|
||||
eCmdModeSelect10 = 0x55,
|
||||
eCmdReserve10 = 0x56,
|
||||
eCmdRelease10 = 0x57,
|
||||
eCmdModeSense10 = 0x5A,
|
||||
eCmdRead16 = 0x88,
|
||||
eCmdWrite16 = 0x8A,
|
||||
eCmdVerify16 = 0x8F,
|
||||
eCmdSynchronizeCache16 = 0x91,
|
||||
eCmdReadCapacity16 = 0x9E,
|
||||
eCmdReportLuns = 0xA0
|
||||
};
|
||||
|
||||
// Internal data definition
|
||||
typedef struct {
|
||||
// Synchronous transfer
|
||||
@ -34,22 +78,11 @@ public:
|
||||
int syncack; // Number of synchronous transfer ACKs
|
||||
|
||||
// ATN message
|
||||
BOOL atnmsg;
|
||||
bool atnmsg;
|
||||
int msc;
|
||||
BYTE msb[256];
|
||||
} scsi_t;
|
||||
|
||||
// SCSI command name and pointer to implementation
|
||||
typedef struct _command_t {
|
||||
const char* name;
|
||||
void (SCSIDEV::*execute)(void);
|
||||
|
||||
_command_t(const char* _name, void (SCSIDEV::*_execute)(void)) : name(_name), execute(_execute) { };
|
||||
} command_t;
|
||||
|
||||
// Mapping of SCSI opcodes to command implementations
|
||||
std::map<scsi_command, command_t*> scsi_commands;
|
||||
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIDEV();
|
||||
@ -57,65 +90,33 @@ public:
|
||||
|
||||
void Reset(); // Device Reset
|
||||
|
||||
// 外部API
|
||||
// External API
|
||||
BUS::phase_t Process(); // Run
|
||||
|
||||
void SyncTransfer(BOOL enable) { scsi.syncenable = enable; } // Synchronouse transfer enable setting
|
||||
|
||||
// Other
|
||||
BOOL IsSASI() const {return FALSE;} // SASI Check
|
||||
BOOL IsSCSI() const {return TRUE;} // SCSI check
|
||||
bool IsSASI() const { return false; } // SASI Check
|
||||
bool IsSCSI() const { return true; } // SCSI check
|
||||
|
||||
void Error(ERROR_CODES::sense_key sense_key = ERROR_CODES::sense_key::NO_SENSE,
|
||||
ERROR_CODES::asc asc = ERROR_CODES::asc::NO_ADDITIONAL_SENSE_INFORMATION) override; // Common error handling
|
||||
|
||||
private:
|
||||
void SetUpCommand(scsi_command, const char*, void (SCSIDEV::*)(void));
|
||||
|
||||
// Phase
|
||||
void BusFree(); // Bus free phase
|
||||
void Selection(); // Selection phase
|
||||
void Execute(); // Execution phase
|
||||
void MsgOut(); // Message out phase
|
||||
void Error(ERROR_CODES::sense_key sense_key = ERROR_CODES::sense_key::NO_SENSE,
|
||||
ERROR_CODES::asc asc = ERROR_CODES::asc::NO_ADDITIONAL_SENSE_INFORMATION); // Common erorr handling
|
||||
|
||||
// commands
|
||||
void CmdInquiry(); // INQUIRY command
|
||||
void CmdModeSelect(); // MODE SELECT command
|
||||
void CmdReserve6(); // RESERVE(6) command
|
||||
void CmdReserve10(); // RESERVE(10) command
|
||||
void CmdRelease6(); // RELEASE(6) command
|
||||
void CmdRelease10(); // RELEASE(10) command
|
||||
void CmdModeSense(); // MODE SENSE command
|
||||
void CmdStartStop(); // START STOP UNIT command
|
||||
void CmdSendDiag(); // SEND DIAGNOSTIC command
|
||||
void CmdRemoval(); // PREVENT/ALLOW MEDIUM REMOVAL command
|
||||
void CmdReadCapacity10(); // READ CAPACITY(10) command
|
||||
void CmdRead10(); // READ(10) command
|
||||
void CmdWrite10(); // WRITE(10) command
|
||||
void CmdSeek10(); // SEEK(10) command
|
||||
void CmdVerify(); // VERIFY command
|
||||
void CmdSynchronizeCache(); // SYNCHRONIZE CACHE command
|
||||
void CmdReadDefectData10(); // READ DEFECT DATA(10) command
|
||||
void CmdReadToc(); // READ TOC command
|
||||
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
|
||||
void CmdSetIfaceMode(); // DaynaPort specific command
|
||||
void CmdSetMcastAddr(); // DaynaPort specific command
|
||||
void CmdEnableInterface(); // DaynaPort specific command
|
||||
// データ転送
|
||||
|
||||
// Data transfer
|
||||
void Send(); // Send data
|
||||
void Receive(); // Receive data
|
||||
BOOL XferMsg(DWORD msg); // Data transfer message
|
||||
bool XferMsg(DWORD msg); // Data transfer message
|
||||
|
||||
scsi_t scsi; // Internal data
|
||||
};
|
||||
|
@ -1,46 +0,0 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 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
|
||||
};
|
@ -19,10 +19,10 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "xm6.h"
|
||||
#include "log.h"
|
||||
#include "filepath.h"
|
||||
#include "cfilesystem.h"
|
||||
#include "../rascsi.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
@ -32,13 +32,13 @@
|
||||
#define IC_BUF_SIZE 1024
|
||||
static char convert_buf[IC_BUF_SIZE];
|
||||
#ifndef __NetBSD__
|
||||
// POSIX.1準拠iconv(3)を使用
|
||||
// Using POSIX.1 compliant iconv(3)
|
||||
#define CONVERT(src, dest, inbuf, outbuf, outsize) \
|
||||
convert(src, dest, (char *)inbuf, outbuf, outsize)
|
||||
static void convert(char const *src, char const *dest,
|
||||
char *inbuf, char *outbuf, size_t outsize)
|
||||
#else
|
||||
// NetBSD版iconv(3)を使用: 第2引数はconst char **
|
||||
// Using NetBSD version of iconv(3): The second argument is 'const char **'
|
||||
#define CONVERT(src, dest, inbuf, outbuf, outsize) \
|
||||
convert(src, dest, inbuf, outbuf, outsize)
|
||||
static void convert(char const *src, char const *dest,
|
||||
@ -67,7 +67,7 @@ static void convert(char const *src, char const *dest,
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SJIS->UTF8変換
|
||||
// SJIS->UTF8 conversion
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
static char* SJIS2UTF8(const char *sjis, char *utf8, size_t bufsize)
|
||||
@ -78,7 +78,7 @@ static char* SJIS2UTF8(const char *sjis, char *utf8, size_t bufsize)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// UTF8->SJIS変換
|
||||
// UTF8->SJIS conversion
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
static char* UTF82SJIS(const char *utf8, char *sjis, size_t bufsize)
|
||||
@ -89,7 +89,7 @@ static char* UTF82SJIS(const char *utf8, char *sjis, size_t bufsize)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SJIS->UTF8変換(簡易版)
|
||||
// SJIS->UTF8 conversion (simplified versoin)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
static char* S2U(const char *sjis)
|
||||
@ -100,7 +100,7 @@ static char* S2U(const char *sjis)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// UTF8->SJIS変換(簡易版)
|
||||
// UTF8->SJIS conversion (simplified version)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
static char* U2S(const char *utf8)
|
||||
@ -111,10 +111,10 @@ static char* U2S(const char *utf8)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// パス名取得
|
||||
/// Get path name
|
||||
///
|
||||
/// Human68k用namests構造体から、Human68kパス名を取得する。
|
||||
/// 書き込み先バッファは66バイト必要。
|
||||
/// From the structure used in Human68k namests, get the Human68k path name.
|
||||
/// A 66 byte buffer is required for writing.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Human68k::namests_t::GetCopyPath(BYTE* szPath) const
|
||||
@ -137,10 +137,10 @@ void Human68k::namests_t::GetCopyPath(BYTE* szPath) const
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
/// ファイル名取得
|
||||
/// Get file name
|
||||
///
|
||||
/// Human68k用namests構造体から、Human68kファイル名を取得する。
|
||||
/// 書き込み先バッファは23バイト必要。
|
||||
/// From the structure used in Human68k namests, get the Human68k file name.
|
||||
/// A 23 byte buffer is required for writing.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Human68k::namests_t::GetCopyFilename(BYTE* szFilename) const
|
||||
@ -150,7 +150,7 @@ void Human68k::namests_t::GetCopyFilename(BYTE* szFilename) const
|
||||
size_t i;
|
||||
BYTE* p = szFilename;
|
||||
|
||||
// ファイル名本体転送
|
||||
// Transfer the base file name
|
||||
for (i = 0; i < 8; i++) {
|
||||
BYTE c = name[i];
|
||||
if (c == ' ') {
|
||||
@ -1147,7 +1147,7 @@ void CHostFilename::SetEntryName()
|
||||
BOOL CHostFilename::isReduce() const
|
||||
{
|
||||
|
||||
return strcmp((LPTSTR)m_szHost, (const char*)m_szHuman) != 0; /// @warning Unicode時要修正 → 済
|
||||
return strcmp((char *)m_szHost, (const char*)m_szHuman) != 0; /// @warning Unicode時要修正 → 済
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -11,335 +11,325 @@
|
||||
// It is highly unlikely that this will work for other platforms.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#ifndef cfilesystem_h
|
||||
#define cfilesystem_h
|
||||
#pragma once
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ステータスコード定義
|
||||
// Status code definitions
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#define FS_INVALIDFUNC 0xFFFFFFFF ///< 無効なファンクションコードを実行した
|
||||
#define FS_FILENOTFND 0xFFFFFFFE ///< 指定したファイルが見つからない
|
||||
#define FS_DIRNOTFND 0xFFFFFFFD ///< 指定したディレクトリが見つからない
|
||||
#define FS_OVEROPENED 0xFFFFFFFC ///< オープンしているファイルが多すぎる
|
||||
#define FS_CANTACCESS 0xFFFFFFFB ///< ディレクトリやボリュームラベルはアクセス不可
|
||||
#define FS_NOTOPENED 0xFFFFFFFA ///< 指定したハンドルはオープンされていない
|
||||
#define FS_INVALIDMEM 0xFFFFFFF9 ///< メモリ管理領域が破壊された
|
||||
#define FS_OUTOFMEM 0xFFFFFFF8 ///< 実行に必要なメモリがない
|
||||
#define FS_INVALIDPTR 0xFFFFFFF7 ///< 無効なメモリ管理ポインタを指定した
|
||||
#define FS_INVALIDENV 0xFFFFFFF6 ///< 不正な環境を指定した
|
||||
#define FS_ILLEGALFMT 0xFFFFFFF5 ///< 実行ファイルのフォーマットが異常
|
||||
#define FS_ILLEGALMOD 0xFFFFFFF4 ///< オープンのアクセスモードが異常
|
||||
#define FS_INVALIDPATH 0xFFFFFFF3 ///< ファイル名の指定に誤りがある
|
||||
#define FS_INVALIDPRM 0xFFFFFFF2 ///< 無効なパラメータでコールした
|
||||
#define FS_INVALIDDRV 0xFFFFFFF1 ///< ドライブ指定に誤りがある
|
||||
#define FS_DELCURDIR 0xFFFFFFF0 ///< カレントディレクトリは削除できない
|
||||
#define FS_NOTIOCTRL 0xFFFFFFEF ///< IOCTRLできないデバイス
|
||||
#define FS_LASTFILE 0xFFFFFFEE ///< これ以上ファイルが見つからない
|
||||
#define FS_CANTWRITE 0xFFFFFFED ///< 指定のファイルは書き込みできない
|
||||
#define FS_DIRALREADY 0xFFFFFFEC ///< 指定のディレクトリは既に登録されている
|
||||
#define FS_CANTDELETE 0xFFFFFFEB ///< ファイルがあるので削除できない
|
||||
#define FS_CANTRENAME 0xFFFFFFEA ///< ファイルがあるのでリネームできない
|
||||
#define FS_DISKFULL 0xFFFFFFE9 ///< ディスクが一杯でファイルが作れない
|
||||
#define FS_DIRFULL 0xFFFFFFE8 ///< ディレクトリが一杯でファイルが作れない
|
||||
#define FS_CANTSEEK 0xFFFFFFE7 ///< 指定の位置にはシークできない
|
||||
#define FS_SUPERVISOR 0xFFFFFFE6 ///< スーパーバイザ状態でスーパバイザ指定した
|
||||
#define FS_THREADNAME 0xFFFFFFE5 ///< 同じスレッド名が存在する
|
||||
#define FS_BUFWRITE 0xFFFFFFE4 ///< プロセス間通信のバッファが書込み禁止
|
||||
#define FS_BACKGROUND 0xFFFFFFE3 ///< バックグラウンドプロセスを起動できない
|
||||
#define FS_OUTOFLOCK 0xFFFFFFE0 ///< ロック領域が足りない
|
||||
#define FS_LOCKED 0xFFFFFFDF ///< ロックされていてアクセスできない
|
||||
#define FS_DRIVEOPENED 0xFFFFFFDE ///< 指定のドライブはハンドラがオープンされている
|
||||
#define FS_LINKOVER 0xFFFFFFDD ///< シンボリックリンクネストが16回を超えた
|
||||
#define FS_FILEEXIST 0xFFFFFFB0 ///< ファイルが存在する
|
||||
#define FS_INVALIDFUNC 0xFFFFFFFF ///< Executed an invalid function
|
||||
#define FS_FILENOTFND 0xFFFFFFFE ///< The selected file can not be found
|
||||
#define FS_DIRNOTFND 0xFFFFFFFD ///< The selected directory can not be found
|
||||
#define FS_OVEROPENED 0xFFFFFFFC ///< There are too many files open
|
||||
#define FS_CANTACCESS 0xFFFFFFFB ///< Can not access the direcory or volume
|
||||
#define FS_NOTOPENED 0xFFFFFFFA ///< The selected handle is not opened
|
||||
#define FS_INVALIDMEM 0xFFFFFFF9 ///< Memory management has been destroyed
|
||||
#define FS_OUTOFMEM 0xFFFFFFF8 ///< Insufficient memory for execution
|
||||
#define FS_INVALIDPTR 0xFFFFFFF7 ///< Selected an invalid memory management pointer
|
||||
#define FS_INVALIDENV 0xFFFFFFF6 ///< Selected an invalid environment
|
||||
#define FS_ILLEGALFMT 0xFFFFFFF5 ///< The exeucted file is in an invalid format
|
||||
#define FS_ILLEGALMOD 0xFFFFFFF4 ///< Invalid open access mode
|
||||
#define FS_INVALIDPATH 0xFFFFFFF3 ///< Mistake in selected file name
|
||||
#define FS_INVALIDPRM 0xFFFFFFF2 ///< Called with an invalid parameter
|
||||
#define FS_INVALIDDRV 0xFFFFFFF1 ///< Mistake in selected drive
|
||||
#define FS_DELCURDIR 0xFFFFFFF0 ///< Unable to delete the current directory
|
||||
#define FS_NOTIOCTRL 0xFFFFFFEF ///< Unable to use IOCTRL with the device
|
||||
#define FS_LASTFILE 0xFFFFFFEE ///< Can not find any more files
|
||||
#define FS_CANTWRITE 0xFFFFFFED ///< Selected file can not be written
|
||||
#define FS_DIRALREADY 0xFFFFFFEC ///< Selected directory is already registered
|
||||
#define FS_CANTDELETE 0xFFFFFFEB ///< Can not delete because of a file
|
||||
#define FS_CANTRENAME 0xFFFFFFEA ///< Can not rename because of a file
|
||||
#define FS_DISKFULL 0xFFFFFFE9 ///< Can not create a file because the disk is full
|
||||
#define FS_DIRFULL 0xFFFFFFE8 ///< Can not create a file because the directory is full
|
||||
#define FS_CANTSEEK 0xFFFFFFE7 ///< Can not seek in the selected location
|
||||
#define FS_SUPERVISOR 0xFFFFFFE6 ///< Selected supervisor in supervisor mode
|
||||
#define FS_THREADNAME 0xFFFFFFE5 ///< A thread with this name already exists
|
||||
#define FS_BUFWRITE 0xFFFFFFE4 ///< Writing to inter-process communication buffers is disallowed
|
||||
#define FS_BACKGROUND 0xFFFFFFE3 ///< Unable to start a background process
|
||||
#define FS_OUTOFLOCK 0xFFFFFFE0 ///< Insufficient lock space
|
||||
#define FS_LOCKED 0xFFFFFFDF ///< Can not access because it is locked
|
||||
#define FS_DRIVEOPENED 0xFFFFFFDE ///< Selected drive has an open handler
|
||||
#define FS_LINKOVER 0xFFFFFFDD ///< The symbolic link is nested over 16 times
|
||||
#define FS_FILEEXIST 0xFFFFFFB0 ///< The file exists
|
||||
|
||||
#define FS_FATAL_MEDIAOFFLINE 0xFFFFFFA3 ///< メディアが入っていない
|
||||
#define FS_FATAL_WRITEPROTECT 0xFFFFFFA2 ///< 書き込み禁止違反
|
||||
#define FS_FATAL_INVALIDCOMMAND 0xFFFFFFA1 ///< 不正なコマンド番号
|
||||
#define FS_FATAL_INVALIDUNIT 0xFFFFFFA0 ///< 不正なユニット番号
|
||||
#define FS_FATAL_MEDIAOFFLINE 0xFFFFFFA3 ///< No media inserted
|
||||
#define FS_FATAL_WRITEPROTECT 0xFFFFFFA2 ///< Write protected
|
||||
#define FS_FATAL_INVALIDCOMMAND 0xFFFFFFA1 ///< Invalid command number
|
||||
#define FS_FATAL_INVALIDUNIT 0xFFFFFFA0 ///< Invalid unit number
|
||||
|
||||
#define HUMAN68K_PATH_MAX 96 ///< Human68kのパス最大長
|
||||
#define HUMAN68K_PATH_MAX 96 ///< Longest path allowed in Human68k
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
/// Human68k 名前空間
|
||||
/// Human68k name space
|
||||
//
|
||||
//===========================================================================
|
||||
namespace Human68k {
|
||||
/// ファイル属性ビット
|
||||
/// File attribute bit
|
||||
enum attribute_t {
|
||||
AT_READONLY = 0x01, ///< 読み込み専用属性
|
||||
AT_HIDDEN = 0x02, ///< 隠し属性
|
||||
AT_SYSTEM = 0x04, ///< システム属性
|
||||
AT_VOLUME = 0x08, ///< ボリュームラベル属性
|
||||
AT_DIRECTORY = 0x10, ///< ディレクトリ属性
|
||||
AT_ARCHIVE = 0x20, ///< アーカイブ属性
|
||||
AT_ALL = 0xFF, ///< 全ての属性ビットが1
|
||||
AT_READONLY = 0x01, ///< Read only attribute
|
||||
AT_HIDDEN = 0x02, ///< Hidden attribute
|
||||
AT_SYSTEM = 0x04, ///< System attribute
|
||||
AT_VOLUME = 0x08, ///< Volume label attribute
|
||||
AT_DIRECTORY = 0x10, ///< Directory attribute
|
||||
AT_ARCHIVE = 0x20, ///< Archive attribute
|
||||
AT_ALL = 0xFF, ///< All attribute bits are 1
|
||||
};
|
||||
|
||||
/// ファイルオープンモード
|
||||
/// File open modes
|
||||
enum open_t {
|
||||
OP_READ = 0, ///< 読み込み
|
||||
OP_WRITE = 1, ///< 書き込み
|
||||
OP_FULL = 2, ///< 読み書き
|
||||
OP_MASK = 0x0F, ///< 判定用マスク
|
||||
OP_SHARE_NONE = 0x10, ///< 共有禁止
|
||||
OP_SHARE_READ = 0x20, ///< 読み込み共有
|
||||
OP_SHARE_WRITE = 0x30, ///< 書き込み共有
|
||||
OP_SHARE_FULL = 0x40, ///< 読み書き共有
|
||||
OP_SHARE_MASK = 0x70, ///< 共有判定用マスク
|
||||
OP_SPECIAL = 0x100, ///< 辞書アクセス
|
||||
OP_READ = 0, ///< Read
|
||||
OP_WRITE = 1, ///< Write
|
||||
OP_FULL = 2, ///< Read/Write
|
||||
OP_MASK = 0x0F, ///< Decision mask
|
||||
OP_SHARE_NONE = 0x10, ///< Sharing forbidden
|
||||
OP_SHARE_READ = 0x20, ///< Read sharing
|
||||
OP_SHARE_WRITE = 0x30, ///< Write sharing
|
||||
OP_SHARE_FULL = 0x40, ///< Read/Write sharing
|
||||
OP_SHARE_MASK = 0x70, ///< Sharing decision mask
|
||||
OP_SPECIAL = 0x100, ///< Dictionary access
|
||||
};
|
||||
|
||||
/// シーク種類
|
||||
/// Seek types
|
||||
enum seek_t {
|
||||
SK_BEGIN = 0, ///< ファイル先頭から
|
||||
SK_CURRENT = 1, ///< 現在位置から
|
||||
SK_END = 2, ///< ファイル末尾から
|
||||
SK_BEGIN = 0, ///< From the beginning of a file
|
||||
SK_CURRENT = 1, ///< From the current location
|
||||
SK_END = 2, ///< From the end of the file
|
||||
};
|
||||
|
||||
/// メディアバイト
|
||||
/// Media byte
|
||||
enum media_t {
|
||||
MEDIA_2DD_10 = 0xE0, ///< 2DD/10セクタ
|
||||
MEDIA_1D_9 = 0xE5, ///< 1D/9セクタ
|
||||
MEDIA_2D_9 = 0xE6, ///< 2D/9セクタ
|
||||
MEDIA_1D_8 = 0xE7, ///< 1D/8セクタ
|
||||
MEDIA_2D_8 = 0xE8, ///< 2D/8セクタ
|
||||
MEDIA_2DD_10 = 0xE0, ///< 2DD/10 sector
|
||||
MEDIA_1D_9 = 0xE5, ///< 1D/9 sector
|
||||
MEDIA_2D_9 = 0xE6, ///< 2D/9 sector
|
||||
MEDIA_1D_8 = 0xE7, ///< 1D/8 sector
|
||||
MEDIA_2D_8 = 0xE8, ///< 2D/8 sector
|
||||
MEDIA_2HT = 0xEA, ///< 2HT
|
||||
MEDIA_2HS = 0xEB, ///< 2HS
|
||||
MEDIA_2HDE = 0xEC, ///< 2DDE
|
||||
MEDIA_1DD_9 = 0xEE, ///< 1DD/9セクタ
|
||||
MEDIA_1DD_8 = 0xEF, ///< 1DD/8セクタ
|
||||
MEDIA_MANUAL = 0xF1, ///< リモートドライブ (手動イジェクト)
|
||||
MEDIA_REMOVABLE = 0xF2, ///< リモートドライブ (リムーバブル)
|
||||
MEDIA_REMOTE = 0xF3, ///< リモートドライブ
|
||||
MEDIA_1DD_9 = 0xEE, ///< 1DD/9 sector
|
||||
MEDIA_1DD_8 = 0xEF, ///< 1DD/8 sector
|
||||
MEDIA_MANUAL = 0xF1, ///< Remote drive (manual eject)
|
||||
MEDIA_REMOVABLE = 0xF2, ///< Remote drive (removable)
|
||||
MEDIA_REMOTE = 0xF3, ///< Remote drive
|
||||
MEDIA_DAT = 0xF4, ///< SCSI-DAT
|
||||
MEDIA_CDROM = 0xF5, ///< SCSI-CDROM
|
||||
MEDIA_MO = 0xF6, ///< SCSI-MO
|
||||
MEDIA_SCSI_HD = 0xF7, ///< SCSI-HD
|
||||
MEDIA_SASI_HD = 0xF8, ///< SASI-HD
|
||||
MEDIA_RAMDISK = 0xF9, ///< RAMディスク
|
||||
MEDIA_RAMDISK = 0xF9, ///< RAM disk
|
||||
MEDIA_2HQ = 0xFA, ///< 2HQ
|
||||
MEDIA_2DD_8 = 0xFB, ///< 2DD/8セクタ
|
||||
MEDIA_2DD_9 = 0xFC, ///< 2DD/9セクタ
|
||||
MEDIA_2DD_8 = 0xFB, ///< 2DD/8 sector
|
||||
MEDIA_2DD_9 = 0xFC, ///< 2DD/9 sector
|
||||
MEDIA_2HC = 0xFD, ///< 2HC
|
||||
MEDIA_2HD = 0xFE, ///< 2HD
|
||||
};
|
||||
|
||||
/// namests構造体
|
||||
struct namests_t {
|
||||
BYTE wildcard; ///< ワイルドカード文字数
|
||||
BYTE drive; ///< ドライブ番号
|
||||
BYTE path[65]; ///< パス(サブディレクトリ+/)
|
||||
BYTE name[8]; ///< ファイル名 (PADDING 0x20)
|
||||
BYTE ext[3]; ///< 拡張子 (PADDING 0x20)
|
||||
BYTE add[10]; ///< ファイル名追加 (PADDING 0x00)
|
||||
BYTE wildcard; ///< Wildcard array
|
||||
BYTE drive; ///< Drive number
|
||||
BYTE path[65]; ///< Path (subdirectory +/)
|
||||
BYTE name[8]; ///< File name (PADDING 0x20)
|
||||
BYTE ext[3]; ///< Extension (PADDING 0x20)
|
||||
BYTE add[10]; ///< File name addition (PADDING 0x00)
|
||||
|
||||
// 文字列取得
|
||||
void GetCopyPath(BYTE* szPath) const;
|
||||
///< パス名取得
|
||||
void GetCopyFilename(BYTE* szFilename) const;
|
||||
///< ファイル名取得
|
||||
};
|
||||
|
||||
/// files構造体
|
||||
struct files_t {
|
||||
BYTE fatr; ///< + 0 検索する属性 読込専用
|
||||
// BYTE drive; ///< + 1 ドライブ番号 読込専用
|
||||
DWORD sector; ///< + 2 ディレクトリのセクタ DOS _FILES先頭アドレスで代用
|
||||
// WORD cluster; ///< + 6 ディレクトリのクラスタ 詳細不明 (未使用)
|
||||
WORD offset; ///< + 8 ディレクトリエントリ 書込専用
|
||||
// BYTE name[8]; ///< +10 作業用ファイル名 読込専用 (未使用)
|
||||
// BYTE ext[3]; ///< +18 作業用拡張子 読込専用 (未使用)
|
||||
BYTE attr; ///< +21 ファイル属性 書込専用
|
||||
WORD time; ///< +22 最終変更時刻 書込専用
|
||||
WORD date; ///< +24 最終変更月日 書込専用
|
||||
DWORD size; ///< +26 ファイルサイズ 書込専用
|
||||
BYTE full[23]; ///< +30 フルファイル名 書込専用
|
||||
BYTE fatr; ///< + 0 search attribute; read-only
|
||||
// BYTE drive; ///< + 1 drive number; read-only
|
||||
DWORD sector; ///< + 2 directory sector; DOS _FILES first address substitute
|
||||
// WORD cluster; ///< + 6 directory cluster; details unknown (unused)
|
||||
WORD offset; ///< + 8 directory entry; write-only
|
||||
// BYTE name[8]; ///< +10 working file name; write-only (unused)
|
||||
// BYTE ext[3]; ///< +18 working extension; write-only (unused)
|
||||
BYTE attr; ///< +21 file attribute; write-only
|
||||
WORD time; ///< +22 last change time of day; write-only
|
||||
WORD date; ///< +24 last change date; write-only
|
||||
DWORD size; ///< +26 file size; write-only
|
||||
BYTE full[23]; ///< +30 full name; write-only
|
||||
};
|
||||
|
||||
/// FCB構造体
|
||||
struct fcb_t {
|
||||
// BYTE pad00[6]; ///< + 0~+ 5 (未使用)
|
||||
DWORD fileptr; ///< + 6~+ 9 ファイルポインタ
|
||||
// BYTE pad01[4]; ///< +10~+13 (未使用)
|
||||
WORD mode; ///< +14~+15 オープンモード
|
||||
// BYTE pad02[16]; ///< +16~+31 (未使用)
|
||||
// DWORD zero; ///< +32~+35 オープンのとき0が書き込まれている (未使用)
|
||||
// BYTE name[8]; ///< +36~+43 ファイル名 (PADDING 0x20) (未使用)
|
||||
// BYTE ext[3]; ///< +44~+46 拡張子 (PADDING 0x20) (未使用)
|
||||
BYTE attr; ///< +47 ファイル属性
|
||||
// BYTE add[10]; ///< +48~+57 ファイル名追加 (PADDING 0x00) (未使用)
|
||||
WORD time; ///< +58~+59 最終変更時刻
|
||||
WORD date; ///< +60~+61 最終変更月日
|
||||
// WORD cluster; ///< +62~+63 クラスタ番号 (未使用)
|
||||
DWORD size; ///< +64~+67 ファイルサイズ
|
||||
// BYTE pad03[28]; ///< +68~+95 FATキャッシュ (未使用)
|
||||
// BYTE pad00[6]; ///< + 0~+ 5 (unused)
|
||||
DWORD fileptr; ///< + 6~+ 9 file pointer
|
||||
// BYTE pad01[4]; ///< +10~+13 (unused)
|
||||
WORD mode; ///< +14~+15 open mode
|
||||
// BYTE pad02[16]; ///< +16~+31 (unused)
|
||||
// DWORD zero; ///< +32~+35 zeros are written when opened (unused)
|
||||
// BYTE name[8]; ///< +36~+43 file name (PADDING 0x20) (unused)
|
||||
// BYTE ext[3]; ///< +44~+46 extension (PADDING 0x20) (unused)
|
||||
BYTE attr; ///< +47 file attribute
|
||||
// BYTE add[10]; ///< +48~+57 file name addition (PADDING 0x00) (unused)
|
||||
WORD time; ///< +58~+59 last change time of day
|
||||
WORD date; ///< +60~+61 last change date
|
||||
// WORD cluster; ///< +62~+63 cluster number (unused)
|
||||
DWORD size; ///< +64~+67 file size
|
||||
// BYTE pad03[28]; ///< +68~+95 FAT cache (unused)
|
||||
};
|
||||
|
||||
/// capacity構造体
|
||||
struct capacity_t {
|
||||
WORD freearea; ///< + 0 使用可能なクラスタ数
|
||||
WORD clusters; ///< + 2 総クラスタ数
|
||||
WORD sectors; ///< + 4 クラスタあたりのセクタ数
|
||||
WORD bytes; ///< + 6 セクタ当たりのバイト数
|
||||
WORD freearea; ///< + 0 Number of available clusters
|
||||
WORD clusters; ///< + 2 Total number of clusters
|
||||
WORD sectors; ///< + 4 Number of sectors per cluster
|
||||
WORD bytes; ///< + 6 Number of bytes per sector
|
||||
};
|
||||
|
||||
/// ctrldrive構造体
|
||||
struct ctrldrive_t {
|
||||
BYTE status; ///< +13 状態
|
||||
BYTE status; ///< +13 status
|
||||
BYTE pad[3]; ///< Padding
|
||||
};
|
||||
|
||||
/// DPB構造体
|
||||
struct dpb_t {
|
||||
WORD sector_size; ///< + 0 1セクタ当りのバイト数
|
||||
BYTE cluster_size; ///< + 2 1クラスタ当りのセクタ数-1
|
||||
BYTE shift; ///< + 3 クラスタ→セクタのシフト数
|
||||
WORD fat_sector; ///< + 4 FATの先頭セクタ番号
|
||||
BYTE fat_max; ///< + 6 FAT領域の個数
|
||||
BYTE fat_size; ///< + 7 FATの占めるセクタ数(複写分を除く)
|
||||
WORD file_max; ///< + 8 ルートディレクトリに入るファイルの個数
|
||||
WORD data_sector; ///< +10 データ領域の先頭セクタ番号
|
||||
WORD cluster_max; ///< +12 総クラスタ数+1
|
||||
WORD root_sector; ///< +14 ルートディレクトリの先頭セクタ番号
|
||||
// DWORD driverentry; ///< +16 デバイスドライバへのポインタ (未使用)
|
||||
BYTE media; ///< +20 メディア識別子
|
||||
// BYTE flag; ///< +21 DPB使用フラグ (未使用)
|
||||
WORD sector_size; ///< + 0 Number of bytes in one sector
|
||||
BYTE cluster_size; ///< + 2 Number sectors in one cluster -1
|
||||
BYTE shift; ///< + 3 Number of cluster→sector shifts
|
||||
WORD fat_sector; ///< + 4 FAT first sector number
|
||||
BYTE fat_max; ///< + 6 FAT storage quantity
|
||||
BYTE fat_size; ///< + 7 FAT controlled sector number (excluding duplicates)
|
||||
WORD file_max; ///< + 8 Number of files in the root directory
|
||||
WORD data_sector; ///< +10 First sector number of data storage
|
||||
WORD cluster_max; ///< +12 Total number of clusters +1
|
||||
WORD root_sector; ///< +14 First sector number of root directory
|
||||
// DWORD driverentry; ///< +16 Device driver pointer (unused)
|
||||
BYTE media; ///< +20 Media identifier
|
||||
// BYTE flag; ///< +21 Flag used by DPB (unused)
|
||||
};
|
||||
|
||||
/// ディレクトリエントリ構造体
|
||||
/// Directory entry struct
|
||||
struct dirent_t {
|
||||
BYTE name[8]; ///< + 0 ファイル名 (PADDING 0x20)
|
||||
BYTE ext[3]; ///< + 8 拡張子 (PADDING 0x20)
|
||||
BYTE attr; ///< +11 ファイル属性
|
||||
BYTE add[10]; ///< +12 ファイル名追加 (PADDING 0x00)
|
||||
WORD time; ///< +22 最終変更時刻
|
||||
WORD date; ///< +24 最終変更月日
|
||||
WORD cluster; ///< +26 クラスタ番号
|
||||
DWORD size; ///< +28 ファイルサイズ
|
||||
BYTE name[8]; ///< + 0 File name (PADDING 0x20)
|
||||
BYTE ext[3]; ///< + 8 Extension (PADDING 0x20)
|
||||
BYTE attr; ///< +11 File attribute
|
||||
BYTE add[10]; ///< +12 File name addition (PADDING 0x00)
|
||||
WORD time; ///< +22 Last change time of day
|
||||
WORD date; ///< +24 Last change date
|
||||
WORD cluster; ///< +26 Cluster number
|
||||
DWORD size; ///< +28 File size
|
||||
};
|
||||
|
||||
/// IOCTRLパラメータ共用体
|
||||
/// IOCTRL parameter union
|
||||
union ioctrl_t {
|
||||
BYTE buffer[8]; ///< バイト単位でのアクセス
|
||||
DWORD param; ///< パラメータ(先頭4バイト)
|
||||
WORD media; ///< メディアバイト(先頭2バイト)
|
||||
BYTE buffer[8]; ///< Access in byte units
|
||||
DWORD param; ///< Parameter (First 4 bytes)
|
||||
WORD media; ///< Media byte (First 2 bytes)
|
||||
};
|
||||
|
||||
/// コマンドライン引数構造体
|
||||
/// Command line parameter struct
|
||||
/**
|
||||
先頭にドライバ自身のパスが含まれるためHUMAN68K_PATH_MAX以上のサイズにする。
|
||||
The driver itself is included in the beginning of the argument,
|
||||
so setting to a length longer than HUMAN68K_PATH_MAX
|
||||
*/
|
||||
struct argument_t {
|
||||
BYTE buf[256]; ///< コマンドライン引数
|
||||
BYTE buf[256]; ///< Command line argument
|
||||
};
|
||||
}
|
||||
|
||||
/// FILES用バッファ個数
|
||||
/// Number of FILES buffers
|
||||
/**
|
||||
通常は数個で十分だが、Human68kの複数のプロセスがマルチタスクで同時に
|
||||
深い階層に渡って作業する時などはこの値を増やす必要がある。
|
||||
Under normal circumstances it's enough with just a few buffers,
|
||||
but Human68k multitasking may lead to multiple threads working
|
||||
deeply in the system, which is why this value is set this high.
|
||||
|
||||
デフォルトは20個。
|
||||
Default is 20 buffers.
|
||||
*/
|
||||
#define XM6_HOST_FILES_MAX 20
|
||||
|
||||
/// FCB用バッファ個数
|
||||
/// Number of FCB buffers
|
||||
/**
|
||||
同時にオープンできるファイル数はこれで決まる。
|
||||
This decides how many files can be opened at the same time.
|
||||
|
||||
デフォルトは100ファイル。
|
||||
Default is 100 files.
|
||||
*/
|
||||
#define XM6_HOST_FCB_MAX 100
|
||||
|
||||
/// 仮想セクタ/クラスタ 最大個数
|
||||
/// Max number of virtual clusters and sectors
|
||||
/**
|
||||
ファイル実体の先頭セクタへのアクセスに対応するための仮想セクタの個数。
|
||||
lzdsysによるアクセスを行なうスレッドの数より多めに確保する。
|
||||
Number of virtual sectors used for accessing the first sector of a file entity.
|
||||
Allocating a generous amount to exceed the number of threads lzdsys uses for access.
|
||||
|
||||
デフォルトは10セクタ。
|
||||
Default is 10 sectors.
|
||||
*/
|
||||
#define XM6_HOST_PSEUDO_CLUSTER_MAX 10
|
||||
|
||||
/// ディレクトリエントリ キャッシュ個数
|
||||
/// Number of caches for directory entries
|
||||
/**
|
||||
Human68kは、サブディレクトリ内で処理を行なう際にディレクトリエントリ
|
||||
のチェックを大量に発行する。この応答を高速化するための簡易キャッシュ
|
||||
の個数を指定する。キャッシュは各ドライブ毎に確保される。
|
||||
多いほど高速になるが、増やしすぎるとホストOS側に負担がかかるので注意。
|
||||
Human68k carries out a large number of checks of directory entries when doing an operation
|
||||
inside a subdirectory. This specifies the number of caches used to speed up this operation.
|
||||
Cache is allocated per drive. The more you add the faster it gets, but use too many
|
||||
and the host OS gets under a heavy load, so be careful.
|
||||
|
||||
デフォルトは16個。
|
||||
Default is 16.
|
||||
*/
|
||||
#define XM6_HOST_DIRENTRY_CACHE_MAX 16
|
||||
|
||||
/// 1ディレクトリに収納できるエントリの最大数
|
||||
/// Max number of entries that can be stored per directory
|
||||
/**
|
||||
ディレクトリ内にファイルが大量に存在すると、当時のアプリケーションが
|
||||
想定していない大量のデータを返してしまうことになる。アプリによっては
|
||||
一部しか認識されなかったり、速度が大幅に低下したり、メモリ不足で停止
|
||||
するなどの危険性が存在する。このため上限を設定することで対処する。
|
||||
例えばとあるファイラの場合、2560ファイルが上限となっている。この数を
|
||||
一つの目安とするのが良い。
|
||||
When a large number of files are stored in a directory, a larger amount of data than
|
||||
contemporanous applications can handle will be returned. This may lead to errors such as
|
||||
partial data being recognized, performance dropping significantly, or OOM crashes.
|
||||
To guard against this, an upper limit is defined here. In the case of a particular
|
||||
file manager, the upper limit is 2560 files. This is one good example to use as reference.
|
||||
|
||||
デフォルトは約6万エントリ。(FATのルートディレクトリでの上限値)
|
||||
Default is around 60000 entries. (Upper limit of the FAT root directory)
|
||||
*/
|
||||
#define XM6_HOST_DIRENTRY_FILE_MAX 65535
|
||||
|
||||
/// ファイル名の重複除外パターンの最大数
|
||||
/// Max number of patterns for file name deduplication
|
||||
/**
|
||||
Human68k側のファイル名は、ホスト側のファイルシステムの名称をもとに自
|
||||
動生成されるが、Human68k側のファイル名よりもホスト側のファイル名の名
|
||||
称のほうが長いため、同名のファイル名が生成されてしまう可能性がある。
|
||||
その時、Human68k側からファイル名を区別できるようにするため、WindrvXM
|
||||
独自の命名規則に従って別名を生成して解決している。
|
||||
理論上は約6千万(36の5乗)通りの別名を生成できる方式を取っているが、実
|
||||
際には数百パターン以上の重複判定が発生すると処理に時間がかかってしま
|
||||
うため、重複の上限を設定することで速度を維持する。常識的な運用であれ
|
||||
ば、代替名は数パターンもあれば十分運用できるはずであり、この値を可能
|
||||
な限り小さい値にすることでパフォーマンスの改善が期待できる。
|
||||
この個数を超えるファイル名が重複してしまった場合は、同名のエントリが
|
||||
複数生成される。この場合、ファイル一覧では見えるがファイル名で指定す
|
||||
ると最初のエントリのみ扱える状態となる。
|
||||
The file names on the Human68k side are automatically created based on the file system on
|
||||
the host side. However, Human68k have stricter file name length restrictions than the host has.
|
||||
Because of this, there is a risk that file name duplication will occur. When this happens,
|
||||
WindrvXM will use a certain renaming heuristic to generate alternate file names to resolve
|
||||
the duplication. Theoretically, there are over 60 million (36^5) unique file names that
|
||||
can be generated by this method. However, in reality any more than a few hundred
|
||||
deduplications will take excessive processing time. So here an upper limit to deduplication
|
||||
is set in order to maintain system performance. If a system is operated with common sense,
|
||||
you should only need a few dozen deduplication patterns, so this value can be kept low
|
||||
to further improve performance. In the case deduplication is not carried out, multiple files
|
||||
with the same name will be created. When trying to access such files,
|
||||
only the first entry will ever be accessed.
|
||||
|
||||
デフォルトは36パターン。
|
||||
Default is 36 patterns.
|
||||
*/
|
||||
#define XM6_HOST_FILENAME_PATTERN_MAX 36
|
||||
|
||||
/// ファイル名重複防止マーク
|
||||
/// Duplicate file identification mark
|
||||
/**
|
||||
ホスト側のファイル名とHuman68k側ファイル名の名称の区別をつけるときに
|
||||
使う文字。コマンドシェル等のエスケープ文字と重ならないものを選ぶと吉。
|
||||
A symbol used to distinguish between host and Human68k files.
|
||||
Do not use a command shell escape character, or similar protected symbol.
|
||||
|
||||
デフォルトは「@」。
|
||||
Default is '@'.
|
||||
*/
|
||||
#define XM6_HOST_FILENAME_MARK '@'
|
||||
|
||||
/// WINDRV動作フラグ
|
||||
/// WINDRV operational flags
|
||||
/**
|
||||
通常は0にする。ファイル削除にOSのごみ箱機能を利用する場合は1にする。
|
||||
それ以外の値は将来のための予約とする。
|
||||
内部動作フラグとメディアバイト偽装などを見越した将来の拡張用。
|
||||
Normally set to 0. When put in the OS trash can for deletion, it is set to 1.
|
||||
Other values are reserved for future use.
|
||||
Can be used for future extentions such as internal operational flags or mock media byte.
|
||||
*/
|
||||
enum {
|
||||
WINDRV_OPT_REMOVE = 0x00000001, ///< Bit 0: ファイル削除処理 0:直接 1:ごみ箱
|
||||
WINDRV_OPT_ALPHABET = 0x00000020, ///< Bit 5: ファイル名比較 Alphabet区別 0:なし 1:あり 0:-C 1:+C
|
||||
WINDRV_OPT_COMPARE_LENGTH = 0x00000040, ///< Bit 6: ファイル名比較 文字数(未実装) 0:18+3 1:8+3 0:+T 1:-T
|
||||
WINDRV_OPT_CONVERT_LENGTH = 0x00000080, ///< Bit 7: ファイル名変換 文字数 0:18+3 1:8+3 0:-A 1:+A
|
||||
WINDRV_OPT_CONVERT_SPACE = 0x00000100, ///< Bit 8: ファイル名変換 スペース 0:なし 1:'_'
|
||||
WINDRV_OPT_CONVERT_BADCHAR = 0x00000200, ///< Bit 9: ファイル名変換 無効な文字 0:なし 1:'_'
|
||||
WINDRV_OPT_CONVERT_HYPHENS = 0x00000400, ///< Bit10: ファイル名変換 中間のハイフン 0:なし 1:'_'
|
||||
WINDRV_OPT_CONVERT_HYPHEN = 0x00000800, ///< Bit11: ファイル名変換 先頭のハイフン 0:なし 1:'_'
|
||||
WINDRV_OPT_CONVERT_PERIODS = 0x00001000, ///< Bit12: ファイル名変換 中間のピリオド 0:なし 1:'_'
|
||||
WINDRV_OPT_CONVERT_PERIOD = 0x00002000, ///< Bit13: ファイル名変換 先頭のピリオド 0:なし 1:'_'
|
||||
WINDRV_OPT_REDUCED_SPACE = 0x00010000, ///< Bit16: ファイル名短縮 スペース 0:なし 1:短縮
|
||||
WINDRV_OPT_REDUCED_BADCHAR = 0x00020000, ///< Bit17: ファイル名短縮 無効な文字 0:なし 1:短縮
|
||||
WINDRV_OPT_REDUCED_HYPHENS = 0x00040000, ///< Bit18: ファイル名短縮 中間のハイフン 0:なし 1:短縮
|
||||
WINDRV_OPT_REDUCED_HYPHEN = 0x00080000, ///< Bit19: ファイル名短縮 先頭のハイフン 0:なし 1:短縮
|
||||
WINDRV_OPT_REDUCED_PERIODS = 0x00100000, ///< Bit20: ファイル名短縮 中間のピリオド 0:なし 1:短縮
|
||||
WINDRV_OPT_REDUCED_PERIOD = 0x00200000, ///< Bit21: ファイル名短縮 先頭のピリオド 0:なし 1:短縮
|
||||
// Bit24~30 ファイル重複防止マーク 0:自動 1~127:文字
|
||||
WINDRV_OPT_REMOVE = 0x00000001, ///< Bit 0: File delete process 0:Directly 1:Trash can
|
||||
WINDRV_OPT_ALPHABET = 0x00000020, ///< Bit 5: File name comparison; Alphabet distinction 0:No 1:Yes 0:-C 1:+C
|
||||
WINDRV_OPT_COMPARE_LENGTH = 0x00000040, ///< Bit 6: File name comparison; String length (unimplemented) 0:18+3 1:8+3 0:+T 1:-T
|
||||
WINDRV_OPT_CONVERT_LENGTH = 0x00000080, ///< Bit 7: File name conversion; String length 0:18+3 1:8+3 0:-A 1:+A
|
||||
WINDRV_OPT_CONVERT_SPACE = 0x00000100, ///< Bit 8: File name conversion; Space 0:No 1:'_'
|
||||
WINDRV_OPT_CONVERT_BADCHAR = 0x00000200, ///< Bit 9: File name conversion; Invalid char 0:No 1:'_'
|
||||
WINDRV_OPT_CONVERT_HYPHENS = 0x00000400, ///< Bit10: File name conversion; Middle hyphen 0:No 1:'_'
|
||||
WINDRV_OPT_CONVERT_HYPHEN = 0x00000800, ///< Bit11: File name conversion; Initial hyphen 0:No 1:'_'
|
||||
WINDRV_OPT_CONVERT_PERIODS = 0x00001000, ///< Bit12: File name conversion; Middle period 0:No 1:'_'
|
||||
WINDRV_OPT_CONVERT_PERIOD = 0x00002000, ///< Bit13: File name conversion; Initial period 0:No 1:'_'
|
||||
WINDRV_OPT_REDUCED_SPACE = 0x00010000, ///< Bit16: File name reduction; Space 0:No 1:Reduced
|
||||
WINDRV_OPT_REDUCED_BADCHAR = 0x00020000, ///< Bit17: File name reduction; Invalid char 0:No 1:Reduced
|
||||
WINDRV_OPT_REDUCED_HYPHENS = 0x00040000, ///< Bit18: File name reduction Middle hyphen 0:No 1:Reduced
|
||||
WINDRV_OPT_REDUCED_HYPHEN = 0x00080000, ///< Bit19: File name reduction Initial hyphen 0:No 1:Reduced
|
||||
WINDRV_OPT_REDUCED_PERIODS = 0x00100000, ///< Bit20: File name reduction Middle period 0:No 1:Reduced
|
||||
WINDRV_OPT_REDUCED_PERIOD = 0x00200000, ///< Bit21: File name reduction Initial period 0:No 1:Reduced
|
||||
// Bit24~30 Duplicate file identification mark 0:Automatic 1~127:Chars
|
||||
};
|
||||
|
||||
/// ファイルシステム動作フラグ
|
||||
@ -970,5 +960,3 @@ private:
|
||||
TCHAR m_szBase[DriveMax][FILEPATH_MAX]; ///< ベースパス状態復元用の候補
|
||||
static DWORD g_nOption; ///< ファイル名変換フラグ
|
||||
};
|
||||
|
||||
#endif // cfilesystem_h
|
||||
|
@ -14,6 +14,7 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#ifdef __linux__
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
@ -21,19 +22,22 @@
|
||||
#endif
|
||||
#include <zlib.h> // For crc32()
|
||||
#include "os.h"
|
||||
#include "xm6.h"
|
||||
#include "../rascsi.h"
|
||||
#include "ctapdriver.h"
|
||||
#include "log.h"
|
||||
#include "exceptions.h"
|
||||
#include <sstream>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
CTapDriver::CTapDriver()
|
||||
using namespace std;
|
||||
|
||||
CTapDriver::CTapDriver(const string& interfaces)
|
||||
{
|
||||
LOGTRACE("%s",__PRETTY_FUNCTION__);
|
||||
stringstream s(interfaces);
|
||||
string interface;
|
||||
while (getline(s, interface, ',')) {
|
||||
this->interfaces.push_back(interface);
|
||||
}
|
||||
|
||||
// Initialization
|
||||
m_bTxValid = FALSE;
|
||||
m_hTAP = -1;
|
||||
@ -84,119 +88,250 @@ static BOOL ip_link(int fd, const char* ifname, BOOL up) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CTapDriver::Init()
|
||||
static bool is_interface_up(const string& interface) {
|
||||
string file = "/sys/class/net/";
|
||||
file += interface;
|
||||
file += "/carrier";
|
||||
|
||||
bool status = true;
|
||||
FILE *fp = fopen(file.c_str(), "r");
|
||||
if (!fp || fgetc(fp) != '1') {
|
||||
status = false;
|
||||
}
|
||||
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool CTapDriver::Init()
|
||||
{
|
||||
LOGTRACE("%s",__PRETTY_FUNCTION__);
|
||||
|
||||
char dev[IFNAMSIZ] = "ras0";
|
||||
struct ifreq ifr;
|
||||
int ret;
|
||||
|
||||
LOGTRACE("Opening Tap device");
|
||||
// TAP device initilization
|
||||
if ((m_hTAP = open("/dev/net/tun", O_RDWR)) < 0) {
|
||||
LOGERROR("Error: can't open tun. Errno: %d %s", errno, strerror(errno));
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
LOGTRACE("Opened tap device %d",m_hTAP);
|
||||
|
||||
LOGTRACE("Opened tap device %d",m_hTAP);
|
||||
|
||||
// IFF_NO_PI for no extra packet information
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
||||
char dev[IFNAMSIZ] = "ras0";
|
||||
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
|
||||
|
||||
LOGTRACE("Going to open %s", ifr.ifr_name);
|
||||
if ((ret = ioctl(m_hTAP, TUNSETIFF, (void *)&ifr)) < 0) {
|
||||
|
||||
int ret = ioctl(m_hTAP, TUNSETIFF, (void *)&ifr);
|
||||
if (ret < 0) {
|
||||
LOGERROR("Error: can't ioctl TUNSETIFF. Errno: %d %s", errno, strerror(errno));
|
||||
|
||||
close(m_hTAP);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGTRACE("return code from ioctl was %d", ret);
|
||||
|
||||
int ip_fd;
|
||||
if ((ip_fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
int ip_fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (ip_fd < 0) {
|
||||
LOGERROR("Error: can't open ip socket. Errno: %d %s", errno, strerror(errno));
|
||||
|
||||
close(m_hTAP);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
int br_socket_fd = -1;
|
||||
if ((br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
|
||||
int br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||
if (br_socket_fd < 0) {
|
||||
LOGERROR("Error: can't open bridge socket. Errno: %d %s", errno, strerror(errno));
|
||||
|
||||
close(m_hTAP);
|
||||
close(ip_fd);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGTRACE("Going to see if the bridge is created");
|
||||
// Check if the bridge has already been created
|
||||
if (access("/sys/class/net/rascsi_bridge", F_OK)) {
|
||||
LOGINFO("rascsi_bridge is not yet available");
|
||||
|
||||
// Check if the bridge is already created
|
||||
if (access("/sys/class/net/rascsi_bridge", F_OK) != 0) {
|
||||
LOGINFO("Creating the rascsi_bridge...");
|
||||
LOGDEBUG("brctl addbr rascsi_bridge");
|
||||
if ((ret = ioctl(br_socket_fd, SIOCBRADDBR, "rascsi_bridge")) < 0) {
|
||||
LOGERROR("Error: can't ioctl SIOCBRADDBR. Errno: %d %s", errno, strerror(errno));
|
||||
close(m_hTAP);
|
||||
close(ip_fd);
|
||||
close(br_socket_fd);
|
||||
return FALSE;
|
||||
LOGTRACE("Checking which interface is available for creating the bridge");
|
||||
|
||||
string bridge_interface;
|
||||
string bridge_ip;
|
||||
for (const string& iface : interfaces) {
|
||||
string interface;
|
||||
size_t separatorPos = iface.find(':');
|
||||
if (separatorPos != string::npos) {
|
||||
interface = iface.substr(0, separatorPos);
|
||||
bridge_ip = iface.substr(separatorPos + 1);
|
||||
}
|
||||
else {
|
||||
interface = iface;
|
||||
bridge_ip = "10.10.20.1/24";
|
||||
}
|
||||
|
||||
ostringstream msg;
|
||||
if (is_interface_up(interface)) {
|
||||
msg << "Interface " << interface << " is up";
|
||||
LOGTRACE("%s", msg.str().c_str());
|
||||
|
||||
bridge_interface = interface;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
msg << "Interface " << interface << " is not available or is not up";
|
||||
LOGTRACE("%s", msg.str().c_str());
|
||||
}
|
||||
}
|
||||
LOGDEBUG("brctl addif rascsi_bridge eth0");
|
||||
if (!br_setif(br_socket_fd, "rascsi_bridge", "eth0", TRUE)) {
|
||||
close(m_hTAP);
|
||||
close(ip_fd);
|
||||
close(br_socket_fd);
|
||||
return FALSE;
|
||||
|
||||
if (bridge_interface.empty()) {
|
||||
LOGERROR("No interface is up, not creating bridge");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGINFO("Creating rascsi_bridge for interface %s", bridge_interface.c_str());
|
||||
|
||||
if (bridge_interface == "eth0") {
|
||||
LOGDEBUG("brctl addbr rascsi_bridge");
|
||||
|
||||
if ((ret = ioctl(br_socket_fd, SIOCBRADDBR, "rascsi_bridge")) < 0) {
|
||||
LOGERROR("Error: can't ioctl SIOCBRADDBR. Errno: %d %s", errno, strerror(errno));
|
||||
|
||||
close(m_hTAP);
|
||||
close(ip_fd);
|
||||
close(br_socket_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGDEBUG("brctl addif rascsi_bridge %s", bridge_interface.c_str());
|
||||
|
||||
if (!br_setif(br_socket_fd, "rascsi_bridge", bridge_interface.c_str(), TRUE)) {
|
||||
close(m_hTAP);
|
||||
close(ip_fd);
|
||||
close(br_socket_fd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGDEBUG("ip address add %s dev rascsi_bridge", bridge_ip.c_str());
|
||||
|
||||
string address = bridge_ip;
|
||||
string netmask = "255.255.255.0";
|
||||
size_t separatorPos = bridge_ip.find('/');
|
||||
if (separatorPos != string::npos) {
|
||||
address = bridge_ip.substr(0, separatorPos);
|
||||
|
||||
string mask = bridge_ip.substr(separatorPos + 1);
|
||||
if (mask == "8") {
|
||||
netmask = "255.0.0.0";
|
||||
}
|
||||
else if (mask == "16") {
|
||||
netmask = "255.255.0.0";
|
||||
}
|
||||
else if (mask == "24") {
|
||||
netmask = "255.255.255.0";
|
||||
}
|
||||
else {
|
||||
LOGERROR("Error: Invalid netmask in %s", bridge_ip.c_str());
|
||||
|
||||
close(m_hTAP);
|
||||
close(ip_fd);
|
||||
close(br_socket_fd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LOGDEBUG("brctl addbr rascsi_bridge");
|
||||
|
||||
if ((ret = ioctl(br_socket_fd, SIOCBRADDBR, "rascsi_bridge")) < 0) {
|
||||
LOGERROR("Error: can't ioctl SIOCBRADDBR. Errno: %d %s", errno, strerror(errno));
|
||||
|
||||
close(m_hTAP);
|
||||
close(ip_fd);
|
||||
close(br_socket_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGDEBUG("ip address add %s dev rascsi_bridge", bridge_ip.c_str());
|
||||
|
||||
struct ifreq ifr_a;
|
||||
ifr_a.ifr_addr.sa_family = AF_INET;
|
||||
strncpy(ifr_a.ifr_name, "rascsi_bridge", IFNAMSIZ);
|
||||
struct sockaddr_in* addr = (struct sockaddr_in*)&ifr_a.ifr_addr;
|
||||
inet_pton(AF_INET, address.c_str(), &addr->sin_addr);
|
||||
struct ifreq ifr_n;
|
||||
ifr_n.ifr_addr.sa_family = AF_INET;
|
||||
strncpy(ifr_n.ifr_name, "rascsi_bridge", IFNAMSIZ);
|
||||
struct sockaddr_in* mask = (struct sockaddr_in*)&ifr_n.ifr_addr;
|
||||
inet_pton(AF_INET, netmask.c_str(), &mask->sin_addr);
|
||||
if (ioctl(ip_fd, SIOCSIFADDR, &ifr_a) < 0 || ioctl(ip_fd, SIOCSIFNETMASK, &ifr_n) < 0) {
|
||||
LOGERROR("Error: can't ioctl SIOCSIFADDR or SIOCSIFNETMASK. Errno: %d %s", errno, strerror(errno));
|
||||
|
||||
close(m_hTAP);
|
||||
close(ip_fd);
|
||||
close(br_socket_fd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LOGDEBUG("ip link set dev rascsi_bridge up");
|
||||
|
||||
if (!ip_link(ip_fd, "rascsi_bridge", TRUE)) {
|
||||
close(m_hTAP);
|
||||
close(ip_fd);
|
||||
close(br_socket_fd);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGINFO("Note: rascsi_bridge already created");
|
||||
LOGINFO("rascsi_bridge is already available");
|
||||
}
|
||||
|
||||
LOGDEBUG("ip link set ras0 up");
|
||||
|
||||
if (!ip_link(ip_fd, "ras0", TRUE)) {
|
||||
close(m_hTAP);
|
||||
close(ip_fd);
|
||||
close(br_socket_fd);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGDEBUG("brctl addif rascsi_bridge ras0");
|
||||
|
||||
if (!br_setif(br_socket_fd, "rascsi_bridge", "ras0", TRUE)) {
|
||||
close(m_hTAP);
|
||||
close(ip_fd);
|
||||
close(br_socket_fd);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get MAC address
|
||||
LOGTRACE("Getting the MAC address");
|
||||
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
if ((ret = ioctl(m_hTAP, SIOCGIFHWADDR, &ifr)) < 0) {
|
||||
LOGERROR("Error: can't ioctl SIOCGIFHWADDR. Errno: %d %s", errno, strerror(errno));
|
||||
|
||||
close(m_hTAP);
|
||||
close(ip_fd);
|
||||
close(br_socket_fd);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
LOGTRACE("got the mac");
|
||||
LOGTRACE("Got the MAC");
|
||||
|
||||
// Save MAC address
|
||||
memcpy(m_MacAddr, ifr.ifr_hwaddr.sa_data, sizeof(m_MacAddr));
|
||||
LOGINFO("Tap device %s created", ifr.ifr_name);
|
||||
|
||||
close(ip_fd);
|
||||
close(br_socket_fd);
|
||||
|
||||
return TRUE;
|
||||
LOGINFO("Tap device %s created", ifr.ifr_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
@ -209,21 +344,21 @@ BOOL CTapDriver::Init()
|
||||
// TAP Device Initialization
|
||||
if ((m_hTAP = open("/dev/tap", O_RDWR)) < 0) {
|
||||
LOGERROR("Error: can't open tap. Errno: %d %s", errno, strerror(errno));
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get device name
|
||||
if (ioctl(m_hTAP, TAPGIFNAME, (void *)&ifr) < 0) {
|
||||
LOGERROR("Error: can't ioctl TAPGIFNAME. Errno: %d %s", errno, strerror(errno));
|
||||
close(m_hTAP);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get MAC address
|
||||
if (getifaddrs(&ifa) == -1) {
|
||||
LOGERROR("Error: can't getifaddrs. Errno: %d %s", errno, strerror(errno));
|
||||
close(m_hTAP);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
for (a = ifa; a != NULL; a = a->ifa_next)
|
||||
if (strcmp(ifr.ifr_name, a->ifa_name) == 0 &&
|
||||
@ -232,7 +367,7 @@ BOOL CTapDriver::Init()
|
||||
if (a == NULL) {
|
||||
LOGERROR("Error: can't get MAC addressErrno: %d %s", errno, strerror(errno));
|
||||
close(m_hTAP);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save MAC address
|
||||
@ -242,7 +377,7 @@ BOOL CTapDriver::Init()
|
||||
|
||||
LOGINFO("Tap device : %s\n", ifr.ifr_name);
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
#endif // __NetBSD__
|
||||
|
||||
@ -291,8 +426,10 @@ void CTapDriver::Cleanup()
|
||||
pcap_dump_close(m_pcap_dumper);
|
||||
m_pcap_dumper = NULL;
|
||||
}
|
||||
|
||||
if (m_pcap != NULL) {
|
||||
pcap_close(m_pcap);
|
||||
m_pcap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,11 +13,12 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#if !defined(ctapdriver_h)
|
||||
#define ctapdriver_h
|
||||
#pragma once
|
||||
|
||||
#include <pcap/pcap.h>
|
||||
#include "filepath.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#ifndef ETH_FRAME_LEN
|
||||
#define ETH_FRAME_LEN 1514
|
||||
@ -31,9 +32,10 @@
|
||||
class CTapDriver
|
||||
{
|
||||
public:
|
||||
// Basic Functionality
|
||||
CTapDriver(); // Constructor
|
||||
BOOL Init(); // Initilization
|
||||
CTapDriver(const std::string&);
|
||||
~CTapDriver() {};
|
||||
|
||||
bool Init();
|
||||
void OpenDump(const Filepath& path);
|
||||
// Capture packets
|
||||
void Cleanup(); // Cleanup
|
||||
@ -55,6 +57,7 @@ private:
|
||||
pcap_t *m_pcap;
|
||||
pcap_dumper_t *m_pcap_dumper;
|
||||
|
||||
// Prioritized comma-separated list of interfaces to create the bridge for
|
||||
std::vector<std::string> interfaces;
|
||||
};
|
||||
|
||||
#endif // ctapdriver_h
|
||||
|
@ -29,13 +29,18 @@ Device::Device(const string& type)
|
||||
ready = false;
|
||||
reset = false;
|
||||
attn = false;
|
||||
supported_luns = 1;
|
||||
protectable = false;
|
||||
write_protected = false;
|
||||
read_only = false;
|
||||
stoppable = false;
|
||||
stopped = false;
|
||||
removable = false;
|
||||
removed = false;
|
||||
lockable = false;
|
||||
locked = false;
|
||||
block_size_configurable = false;
|
||||
supports_params = false;
|
||||
|
||||
id = 0;
|
||||
lun = 0;
|
||||
@ -60,9 +65,7 @@ void Device::SetProtected(bool 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());
|
||||
throw illegal_argument_exception("Vendor '" + vendor + "' must be between 1 and 8 characters");
|
||||
}
|
||||
|
||||
this->vendor = vendor;
|
||||
@ -76,9 +79,7 @@ void Device::SetProduct(const string& product, bool force)
|
||||
}
|
||||
|
||||
if (product.empty() || product.length() > 16) {
|
||||
ostringstream error;
|
||||
error << "Product '" << product << "' must be between 1 and 16 characters";
|
||||
throw illegal_argument_exception(error.str());
|
||||
throw illegal_argument_exception("Product '" + product + "' must be between 1 and 16 characters");
|
||||
}
|
||||
|
||||
this->product = product;
|
||||
@ -87,9 +88,7 @@ void Device::SetProduct(const string& product, bool force)
|
||||
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());
|
||||
throw illegal_argument_exception("Revision '" + revision + "' must be between 1 and 4 characters");
|
||||
}
|
||||
|
||||
this->revision = revision;
|
||||
@ -111,14 +110,31 @@ const string Device::GetPaddedName() const
|
||||
|
||||
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);
|
||||
if (status_code) {
|
||||
LOGDEBUG("Error 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::Start()
|
||||
{
|
||||
if (!ready) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stopped = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Device::Stop()
|
||||
{
|
||||
ready = false;
|
||||
attn = false;
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
bool Device::Eject(bool force)
|
||||
{
|
||||
if (!ready || !removable) {
|
||||
@ -135,6 +151,7 @@ bool Device::Eject(bool force)
|
||||
removed = true;
|
||||
write_protected = false;
|
||||
locked = false;
|
||||
stopped = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
@ -46,6 +47,8 @@ using namespace std;
|
||||
#define STATUS_PARAMSAVE 0x00053900 // SAVING PARAMETERS NOT SUPPORTED
|
||||
#define STATUS_NODEFECT 0x00010000 // DEFECT LIST NOT FOUND
|
||||
|
||||
class SCSIDEV;
|
||||
|
||||
class Device
|
||||
{
|
||||
private:
|
||||
@ -56,12 +59,19 @@ private:
|
||||
bool reset;
|
||||
bool attn;
|
||||
|
||||
// Number of supported luns
|
||||
int supported_luns;
|
||||
|
||||
// Device is protectable/write-protected
|
||||
bool protectable;
|
||||
bool write_protected;
|
||||
// Device is permanently read-only
|
||||
bool read_only;
|
||||
|
||||
// Device can be stopped (parked)/is stopped (parked)
|
||||
bool stoppable;
|
||||
bool stopped;
|
||||
|
||||
// Device is removable/removed
|
||||
bool removable;
|
||||
bool removed;
|
||||
@ -70,14 +80,27 @@ private:
|
||||
bool lockable;
|
||||
bool locked;
|
||||
|
||||
// Device ID and LUN
|
||||
unsigned int id;
|
||||
unsigned int lun;
|
||||
// The block size is configurable
|
||||
bool block_size_configurable;
|
||||
|
||||
// Device can be created with parameters
|
||||
bool supports_params;
|
||||
|
||||
// Device ID and LUN
|
||||
int32_t id;
|
||||
int32_t lun;
|
||||
|
||||
// Device identifier (for INQUIRY)
|
||||
string vendor;
|
||||
string product;
|
||||
string revision;
|
||||
|
||||
// The parameters the device was created with
|
||||
list<string> params;
|
||||
|
||||
// The default parameters
|
||||
list<string> default_params;
|
||||
|
||||
// Sense Key, ASC and ASCQ
|
||||
int status_code;
|
||||
|
||||
@ -86,6 +109,11 @@ public:
|
||||
Device(const string&);
|
||||
virtual ~Device() {};
|
||||
|
||||
// Override for device specific initializations, to be called after all device properties have been set
|
||||
virtual bool Init(const list<string>&) { return true; };
|
||||
|
||||
virtual bool Dispatch(SCSIDEV *) = 0;
|
||||
|
||||
const string& GetType() const { return type; }
|
||||
|
||||
bool IsReady() const { return ready; }
|
||||
@ -96,6 +124,9 @@ public:
|
||||
bool IsAttn() const { return attn; }
|
||||
void SetAttn(bool attn) { this->attn = attn; }
|
||||
|
||||
int GetSupportedLuns() const { return supported_luns; }
|
||||
void SetSupportedLuns(int supported_luns) { this->supported_luns = supported_luns; }
|
||||
|
||||
bool IsProtectable() const { return protectable; }
|
||||
void SetProtectable(bool protectable) { this->protectable = protectable; }
|
||||
bool IsProtected() const { return write_protected; }
|
||||
@ -103,6 +134,10 @@ public:
|
||||
bool IsReadOnly() const { return read_only; }
|
||||
void SetReadOnly(bool read_only) { this->read_only = read_only; }
|
||||
|
||||
bool IsStoppable() const { return stoppable; }
|
||||
void SetStoppable(bool stoppable) { this->stoppable = stoppable; }
|
||||
bool IsStopped() const { return stopped; }
|
||||
void SetStopped(bool stopped) { this->stopped = stopped; }
|
||||
bool IsRemovable() const { return removable; }
|
||||
void SetRemovable(bool removable) { this->removable = removable; }
|
||||
bool IsRemoved() const { return removed; }
|
||||
@ -113,23 +148,35 @@ public:
|
||||
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; }
|
||||
int32_t GetId() const { return id; }
|
||||
void SetId(int32_t id) { this->id = id; }
|
||||
int32_t GetLun() const { return lun; }
|
||||
void SetLun(int32_t lun) { this->lun = lun; }
|
||||
|
||||
const string GetVendor() const { return vendor; }
|
||||
void SetVendor(const string&);
|
||||
const string GetProduct() const { return product; }
|
||||
void SetProduct(const string&, bool = true);
|
||||
const string GetRevision() const { return revision; }
|
||||
void SetRevision(const string&);
|
||||
const string GetPaddedName() const;
|
||||
|
||||
bool SupportsParams() const { return supports_params; }
|
||||
void SupportsParams(bool supports_paams) { this->supports_params = supports_paams; }
|
||||
const list<string> GetParams() const { return params; }
|
||||
void SetParams(const list<string>& params) { this->params = params; }
|
||||
const list<string> GetDefaultParams() const { return default_params; }
|
||||
void SetDefaultParams(const list<string>& default_params) { this->default_params = default_params; }
|
||||
|
||||
int GetStatusCode() const { return status_code; }
|
||||
void SetStatusCode(int status_code);
|
||||
|
||||
bool Start();
|
||||
void Stop();
|
||||
virtual bool Eject(bool);
|
||||
|
||||
bool IsSASI() const { return type == "SAHD"; }
|
||||
bool IsSCSI() const { return type == "SCHD" || type == "SCRM"; }
|
||||
bool IsSASIHD() const { return type == "SAHD"; }
|
||||
bool IsSCSIHD() const { return type == "SCHD" || type == "SCRM"; }
|
||||
bool IsCdRom() const { return type == "SCCD"; }
|
||||
bool IsMo() const { return type == "SCMO"; }
|
||||
bool IsBridge() const { return type == "SCBR"; }
|
||||
|
@ -14,12 +14,56 @@
|
||||
#include "scsicd.h"
|
||||
#include "scsi_host_bridge.h"
|
||||
#include "scsi_daynaport.h"
|
||||
#include "exceptions.h"
|
||||
#include "device_factory.h"
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
using namespace rascsi_interface;
|
||||
|
||||
Device *DeviceFactory::CreateDevice(PbDeviceType& type, const string& filename, const string& ext)
|
||||
DeviceFactory::DeviceFactory()
|
||||
{
|
||||
sector_sizes[SAHD] = { 256, 1024 };
|
||||
sector_sizes[SCHD] = { 512, 1024, 2048, 4096 };
|
||||
sector_sizes[SCRM] = { 512, 1024, 2048, 4096 };
|
||||
sector_sizes[SCMO] = {};
|
||||
// Some old Sun CD-ROM drives support 512 bytes per sector
|
||||
sector_sizes[SCCD] = { 512, 2048};
|
||||
sector_sizes[SCBR] = {};
|
||||
sector_sizes[SCDP] = {};
|
||||
|
||||
// 128 MB, 512 bytes per sector, 248826 sectors
|
||||
geometries[SCMO][0x797f400] = make_pair(512, 248826);
|
||||
// 230 MB, 512 bytes per block, 446325 sectors
|
||||
geometries[SCMO][0xd9eea00] = make_pair(512, 446325);
|
||||
// 540 MB, 512 bytes per sector, 1041500 sectors
|
||||
geometries[SCMO][0x1fc8b800] = make_pair(512, 1041500);
|
||||
// 640 MB, 20248 bytes per sector, 310352 sectors
|
||||
geometries[SCMO][0x25e28000] = make_pair(2048, 310352);
|
||||
geometries[SAHD] = {};
|
||||
geometries[SCHD] = {};
|
||||
geometries[SCRM] = {};
|
||||
geometries[SCCD] = {};
|
||||
geometries[SCBR] = {};
|
||||
geometries[SCDP] = {};
|
||||
|
||||
default_params[SAHD] = {};
|
||||
default_params[SCHD] = {};
|
||||
default_params[SCRM] = {};
|
||||
default_params[SCMO] = {};
|
||||
default_params[SCCD] = {};
|
||||
default_params[SCBR] = { "eth0,wlan0" };
|
||||
default_params[SCDP] = { "eth0,wlan0" };
|
||||
}
|
||||
|
||||
DeviceFactory& DeviceFactory::instance()
|
||||
{
|
||||
static DeviceFactory instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -42,35 +86,111 @@ Device *DeviceFactory::CreateDevice(PbDeviceType& type, const string& filename,
|
||||
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:
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Device *device = NULL;
|
||||
try {
|
||||
switch (type) {
|
||||
case SAHD:
|
||||
device = new SASIHD();
|
||||
device->SetSupportedLuns(2);
|
||||
((Disk *)device)->SetSectorSizes(sector_sizes[SAHD]);
|
||||
break;
|
||||
|
||||
case SCHD:
|
||||
if (ext == "hdn" || ext == "hdi" || ext == "nhd") {
|
||||
device = new SCSIHD_NEC();
|
||||
((Disk *)device)->SetSectorSizes({ 512 });
|
||||
} else {
|
||||
device = new SCSIHD(false);
|
||||
((Disk *)device)->SetSectorSizes(sector_sizes[SCHD]);
|
||||
}
|
||||
device->SetSupportedLuns(1);
|
||||
device->SetProtectable(true);
|
||||
device->SetStoppable(true);
|
||||
break;
|
||||
|
||||
case SCRM:
|
||||
device = new SCSIHD(true);
|
||||
device->SetSupportedLuns(1);
|
||||
device->SetProtectable(true);
|
||||
device->SetStoppable(true);
|
||||
device->SetRemovable(true);
|
||||
device->SetLockable(true);
|
||||
device->SetProduct("SCSI HD (REM.)");
|
||||
((Disk *)device)->SetSectorSizes(sector_sizes[SCRM]);
|
||||
break;
|
||||
|
||||
case SCMO:
|
||||
device = new SCSIMO();
|
||||
device->SetSupportedLuns(1);
|
||||
device->SetProtectable(true);
|
||||
device->SetStoppable(true);
|
||||
device->SetRemovable(true);
|
||||
device->SetLockable(true);
|
||||
device->SetProduct("SCSI MO");
|
||||
((Disk *)device)->SetGeometries(geometries[SCMO]);
|
||||
break;
|
||||
|
||||
case SCCD:
|
||||
device = new SCSICD();
|
||||
device->SetSupportedLuns(1);
|
||||
device->SetReadOnly(true);
|
||||
device->SetStoppable(true);
|
||||
device->SetRemovable(true);
|
||||
device->SetLockable(true);
|
||||
device->SetProduct("SCSI CD-ROM");
|
||||
((Disk *)device)->SetSectorSizes(sector_sizes[SCCD]);
|
||||
break;
|
||||
|
||||
case SCBR:
|
||||
device = new SCSIBR();
|
||||
device->SetSupportedLuns(1);
|
||||
device->SetProduct("SCSI HOST BRIDGE");
|
||||
device->SupportsParams(true);
|
||||
device->SetDefaultParams(default_params[SCBR]);
|
||||
break;
|
||||
|
||||
case SCDP:
|
||||
device = new SCSIDaynaPort();
|
||||
device->SetSupportedLuns(1);
|
||||
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
|
||||
device->SetVendor("Dayna");
|
||||
device->SetProduct("SCSI/Link");
|
||||
device->SetRevision("1.4a");
|
||||
device->SupportsParams(true);
|
||||
device->SetDefaultParams(default_params[SCDP]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch(const illegal_argument_exception& e) {
|
||||
// There was an internal problem with setting up the device data for INQUIRY
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
const set<uint32_t>& DeviceFactory::GetSectorSizes(const string& type)
|
||||
{
|
||||
PbDeviceType t = UNDEFINED;
|
||||
PbDeviceType_Parse(type, &t);
|
||||
return sector_sizes[t];
|
||||
}
|
||||
|
||||
const set<uint64_t> DeviceFactory::GetCapacities(PbDeviceType type)
|
||||
{
|
||||
set<uint64_t> keys;
|
||||
|
||||
for (const auto& geometry : geometries[type]) {
|
||||
keys.insert(geometry.first);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
@ -5,23 +5,46 @@
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
// A DeviceFactory creates devices based on their type and the extension of their image file
|
||||
// The DeviceFactory singleton creates devices based on their type and the extension of their image file
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "rascsi_interface.pb.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace rascsi_interface;
|
||||
|
||||
typedef pair<uint32_t, uint32_t> Geometry;
|
||||
|
||||
class Device;
|
||||
|
||||
class DeviceFactory
|
||||
{
|
||||
public:
|
||||
|
||||
DeviceFactory() { };
|
||||
~DeviceFactory() { };
|
||||
DeviceFactory();
|
||||
~DeviceFactory() {};
|
||||
|
||||
static Device *CreateDevice(rascsi_interface::PbDeviceType& type, const std::string& filename, const std::string& ext);
|
||||
static DeviceFactory& instance();
|
||||
|
||||
const set<uint32_t>& GetSectorSizes(PbDeviceType type) { return sector_sizes[type]; }
|
||||
const set<uint32_t>& GetSectorSizes(const string&);
|
||||
const set<uint64_t> GetCapacities(PbDeviceType);
|
||||
const list<string>& GetDefaultParams(PbDeviceType type) { return default_params[type]; }
|
||||
|
||||
Device *CreateDevice(PbDeviceType type, const string& filename, const string& ext);
|
||||
|
||||
private:
|
||||
|
||||
map<PbDeviceType, set<uint32_t>> sector_sizes;
|
||||
|
||||
map<PbDeviceType, map<uint64_t, Geometry>> geometries;
|
||||
|
||||
map<PbDeviceType, list<string>> default_params;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,182 +17,155 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xm6.h"
|
||||
#include "log.h"
|
||||
#include "scsi.h"
|
||||
#include "block_device.h"
|
||||
#include "controllers/scsidev_ctrl.h"
|
||||
#include "device.h"
|
||||
#include "device_factory.h"
|
||||
#include "disk_track_cache.h"
|
||||
#include "file_support.h"
|
||||
#include "filepath.h"
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Disk Track
|
||||
//
|
||||
//===========================================================================
|
||||
class DiskTrack
|
||||
#include "../rascsi.h"
|
||||
#include "interfaces/scsi_block_commands.h"
|
||||
#include "interfaces/scsi_primary_commands.h"
|
||||
|
||||
class Disk : public Device, ScsiPrimaryCommands, ScsiBlockCommands
|
||||
{
|
||||
public:
|
||||
// Internal data definition
|
||||
typedef struct {
|
||||
int track; // Track Number
|
||||
int size; // Sector Size(8 or 9)
|
||||
int sectors; // Number of sectors(<=0x100)
|
||||
DWORD length; // Data buffer length
|
||||
BYTE *buffer; // Data buffer
|
||||
BOOL init; // Is it initilized?
|
||||
BOOL changed; // Changed flag
|
||||
DWORD maplen; // Changed map length
|
||||
BOOL *changemap; // Changed map
|
||||
BOOL raw; // RAW mode flag
|
||||
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, off_t imgoff = 0);// Initialization
|
||||
BOOL Load(const Filepath& path); // Load
|
||||
BOOL Save(const Filepath& path); // Save
|
||||
|
||||
// Read / Write
|
||||
BOOL Read(BYTE *buf, int sec) const; // Sector Read
|
||||
BOOL Write(const BYTE *buf, int sec); // Sector Write
|
||||
|
||||
// Other
|
||||
int GetTrack() const { return dt.track; } // Get track
|
||||
BOOL IsChanged() const { return dt.changed; } // Changed flag check
|
||||
|
||||
private:
|
||||
// Internal data
|
||||
disktrk_t dt; // Internal data
|
||||
};
|
||||
enum access_mode { RW6, RW10, RW16 };
|
||||
|
||||
// The supported configurable block sizes, empty if not configurable
|
||||
set<uint32_t> sector_sizes;
|
||||
uint32_t configured_sector_size;
|
||||
|
||||
// The mapping of supported capacities to block sizes and block counts, empty if there is no capacity restriction
|
||||
map<uint64_t, Geometry> geometries;
|
||||
|
||||
SASIDEV::ctrl_t *ctrl;
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Disk Cache
|
||||
//
|
||||
//===========================================================================
|
||||
class DiskCache
|
||||
{
|
||||
public:
|
||||
// Internal data definition
|
||||
typedef struct {
|
||||
DiskTrack *disktrk; // Disk Track
|
||||
DWORD serial; // Serial
|
||||
} cache_t;
|
||||
|
||||
// Number of caches
|
||||
enum {
|
||||
CacheMax = 16 // Number of tracks to cache
|
||||
};
|
||||
|
||||
public:
|
||||
// Basic Functions
|
||||
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
|
||||
|
||||
// Access
|
||||
BOOL Save(); // Save and release all
|
||||
BOOL Read(BYTE *buf, int block); // Sector Read
|
||||
BOOL Write(const BYTE *buf, int block); // Sector Write
|
||||
BOOL GetCache(int index, int& track, DWORD& serial) const; // Get cache information
|
||||
|
||||
private:
|
||||
// Internal Management
|
||||
void Clear(); // Clear all tracks
|
||||
DiskTrack* Assign(int track); // Load track
|
||||
BOOL Load(int index, int track, DiskTrack *disktrk = NULL); // Load track
|
||||
void Update(); // Update serial number
|
||||
|
||||
// Internal data
|
||||
cache_t cache[CacheMax]; // Cache management
|
||||
DWORD serial; // Last serial number
|
||||
Filepath sec_path; // Path
|
||||
int sec_size; // Sector size (8 or 9 or 11)
|
||||
int sec_blocks; // Blocks per sector
|
||||
BOOL cd_raw; // CD-ROM RAW mode
|
||||
off_t imgoffset; // Offset to actual data
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Disk
|
||||
//
|
||||
//===========================================================================
|
||||
class Disk : public BlockDevice
|
||||
{
|
||||
protected:
|
||||
// Internal data structure
|
||||
typedef struct {
|
||||
int size; // Sector Size
|
||||
DWORD blocks; // Total number of sectors
|
||||
uint32_t size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096)
|
||||
// TODO blocks should be a 64 bit value in order to support higher capacities
|
||||
uint32_t blocks; // Total number of sectors
|
||||
DiskCache *dcache; // Disk cache
|
||||
off_t imgoffset; // Offset to actual data
|
||||
off_t image_offset; // Offset to actual data
|
||||
} disk_t;
|
||||
|
||||
typedef struct _command_t {
|
||||
const char* name;
|
||||
void (Disk::*execute)(SASIDEV *);
|
||||
|
||||
_command_t(const char* _name, void (Disk::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { };
|
||||
} command_t;
|
||||
std::map<SCSIDEV::scsi_command, command_t*> commands;
|
||||
|
||||
void AddCommand(SCSIDEV::scsi_command, const char*, void (Disk::*)(SASIDEV *));
|
||||
|
||||
public:
|
||||
// Basic Functions
|
||||
Disk(std::string); // Constructor
|
||||
virtual ~Disk(); // Destructor
|
||||
Disk(std::string);
|
||||
virtual ~Disk();
|
||||
|
||||
virtual bool Dispatch(SCSIDEV *) override;
|
||||
|
||||
void ReserveFile(const string&);
|
||||
|
||||
// Media Operations
|
||||
virtual void Open(const Filepath& path); // Open
|
||||
void GetPath(Filepath& path) const; // Get the path
|
||||
bool Eject(bool) override; // Eject
|
||||
bool Flush(); // Flush the cache
|
||||
virtual void Open(const Filepath& path);
|
||||
void GetPath(Filepath& path) const;
|
||||
bool Eject(bool) override;
|
||||
|
||||
// commands
|
||||
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) 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
|
||||
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
|
||||
// Commands covered by the SCSI specification (see https://www.t10.org/drafts.htm)
|
||||
virtual void TestUnitReady(SASIDEV *) override;
|
||||
void Inquiry(SASIDEV *) override;
|
||||
void RequestSense(SASIDEV *) override;
|
||||
void ModeSelect6(SASIDEV *);
|
||||
void ModeSelect10(SASIDEV *);
|
||||
void ModeSense6(SASIDEV *);
|
||||
void ModeSense10(SASIDEV *);
|
||||
void Rezero(SASIDEV *);
|
||||
void FormatUnit(SASIDEV *) override;
|
||||
void ReassignBlocks(SASIDEV *);
|
||||
void StartStopUnit(SASIDEV *);
|
||||
void SendDiagnostic(SASIDEV *);
|
||||
void PreventAllowMediumRemoval(SASIDEV *);
|
||||
void SynchronizeCache10(SASIDEV *);
|
||||
void SynchronizeCache16(SASIDEV *);
|
||||
void ReadDefectData10(SASIDEV *);
|
||||
virtual void Read6(SASIDEV *);
|
||||
void Read10(SASIDEV *) override;
|
||||
void Read16(SASIDEV *) override;
|
||||
virtual void Write6(SASIDEV *);
|
||||
void Write10(SASIDEV *) override;
|
||||
void Write16(SASIDEV *) override;
|
||||
void Verify10(SASIDEV *) override;
|
||||
void Verify16(SASIDEV *) override;
|
||||
void Seek(SASIDEV *);
|
||||
void Seek6(SASIDEV *);
|
||||
void Seek10(SASIDEV *);
|
||||
void ReadCapacity10(SASIDEV *) override;
|
||||
void ReadCapacity16(SASIDEV *) override;
|
||||
void ReportLuns(SASIDEV *) override;
|
||||
void Reserve6(SASIDEV *);
|
||||
void Reserve10(SASIDEV *);
|
||||
void Release6(SASIDEV *);
|
||||
void Release10(SASIDEV *);
|
||||
|
||||
// Command helpers
|
||||
virtual int Inquiry(const DWORD *cdb, BYTE *buf) = 0; // INQUIRY command
|
||||
virtual int WriteCheck(DWORD block); // WRITE check
|
||||
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
|
||||
virtual bool Write(const DWORD *cdb, const BYTE *buf, DWORD block); // WRITE 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
|
||||
|
||||
virtual int Read(const DWORD *cdb, BYTE *buf, uint64_t block);
|
||||
int ReadDefectData10(const DWORD *cdb, BYTE *buf);
|
||||
|
||||
void SetSize(uint32_t);
|
||||
uint32_t GetSectorSizeInBytes() const;
|
||||
void SetSectorSizeInBytes(uint32_t, bool);
|
||||
uint32_t GetSectorSize() const;
|
||||
bool IsSectorSizeConfigurable() const;
|
||||
set<uint32_t> GetSectorSizes() const;
|
||||
void SetSectorSizes(const set<uint32_t>&);
|
||||
uint32_t GetConfiguredSectorSize() const;
|
||||
bool SetConfiguredSectorSize(uint32_t);
|
||||
void SetGeometries(const map<uint64_t, Geometry>&);
|
||||
void SetGeometryForCapacity(uint64_t);
|
||||
uint64_t GetBlockCount() const;
|
||||
void SetBlockCount(uint32_t);
|
||||
bool GetStartAndCount(SASIDEV *, uint64_t&, uint32_t&, access_mode);
|
||||
bool CheckReady();
|
||||
|
||||
// TODO This method should not be called by SASIDEV
|
||||
virtual bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length);
|
||||
|
||||
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, BYTE *buf); // Add CD_DA
|
||||
virtual int AddVendor(int page, bool change, BYTE *buf); // Add vendor special info
|
||||
BOOL CheckReady(); // Check if ready
|
||||
virtual int AddErrorPage(bool change, BYTE *buf);
|
||||
virtual int AddFormatPage(bool change, BYTE *buf);
|
||||
virtual int AddDrivePage(bool change, BYTE *buf);
|
||||
virtual int AddVendorPage(int page, bool change, BYTE *buf);
|
||||
int AddOptionPage(bool change, BYTE *buf);
|
||||
int AddCachePage(bool change, BYTE *buf);
|
||||
int AddCDROMPage(bool change, BYTE *buf);
|
||||
int AddCDDAPage(bool, BYTE *buf);
|
||||
|
||||
// Internal data
|
||||
disk_t disk; // Internal disk data
|
||||
BOOL cache_wb; // Cache mode
|
||||
virtual int RequestSense(const DWORD *cdb, BYTE *buf);
|
||||
|
||||
// Internal disk data
|
||||
disk_t disk;
|
||||
|
||||
private:
|
||||
void Read(SASIDEV *, uint64_t);
|
||||
void Write(SASIDEV *, uint64_t);
|
||||
void Verify(SASIDEV *, uint64_t);
|
||||
bool Format(const DWORD *cdb);
|
||||
int ModeSense6(const DWORD *cdb, BYTE *buf);
|
||||
int ModeSense10(const DWORD *cdb, BYTE *buf);
|
||||
int ModeSelectCheck(const DWORD *cdb, int length);
|
||||
int ModeSelectCheck6(const DWORD *cdb);
|
||||
int ModeSelectCheck10(const DWORD *cdb);
|
||||
};
|
||||
|
656
src/raspberrypi/devices/disk_track_cache.cpp
Normal file
656
src/raspberrypi/devices/disk_track_cache.cpp
Normal file
@ -0,0 +1,656 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// X68000 EMULATOR "XM6"
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
//
|
||||
// XM6i
|
||||
// Copyright (C) 2010-2015 isaki@NetBSD.org
|
||||
// Copyright (C) 2010 Y.Sugahara
|
||||
//
|
||||
// Imported sava's Anex86/T98Next image and MO format support patch.
|
||||
// Imported NetBSD support and some optimisation patch by Rin Okuyama.
|
||||
// Comments translated to english by akuker.
|
||||
//
|
||||
// [ DiskTrack and DiskCache ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "log.h"
|
||||
#include "fileio.h"
|
||||
#include "disk_track_cache.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Disk Track
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
DiskTrack::DiskTrack()
|
||||
{
|
||||
// Initialization of internal information
|
||||
dt.track = 0;
|
||||
dt.size = 0;
|
||||
dt.sectors = 0;
|
||||
dt.raw = FALSE;
|
||||
dt.init = FALSE;
|
||||
dt.changed = FALSE;
|
||||
dt.length = 0;
|
||||
dt.buffer = NULL;
|
||||
dt.maplen = 0;
|
||||
dt.changemap = NULL;
|
||||
dt.imgoffset = 0;
|
||||
}
|
||||
|
||||
DiskTrack::~DiskTrack()
|
||||
{
|
||||
// Release memory, but do not save automatically
|
||||
if (dt.buffer) {
|
||||
free(dt.buffer);
|
||||
dt.buffer = NULL;
|
||||
}
|
||||
if (dt.changemap) {
|
||||
free(dt.changemap);
|
||||
dt.changemap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Initialization
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void DiskTrack::Init(int track, int size, int sectors, BOOL raw, off_t imgoff)
|
||||
{
|
||||
ASSERT(track >= 0);
|
||||
ASSERT((sectors > 0) && (sectors <= 0x100));
|
||||
ASSERT(imgoff >= 0);
|
||||
|
||||
// Set Parameters
|
||||
dt.track = track;
|
||||
dt.size = size;
|
||||
dt.sectors = sectors;
|
||||
dt.raw = raw;
|
||||
|
||||
// Not initialized (needs to be loaded)
|
||||
dt.init = FALSE;
|
||||
|
||||
// Not Changed
|
||||
dt.changed = FALSE;
|
||||
|
||||
// Offset to actual data
|
||||
dt.imgoffset = imgoff;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Load
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskTrack::Load(const Filepath& path)
|
||||
{
|
||||
Fileio fio;
|
||||
|
||||
// Not needed if already loaded
|
||||
if (dt.init) {
|
||||
ASSERT(dt.buffer);
|
||||
ASSERT(dt.changemap);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculate offset (previous tracks are considered to hold 256 sectors)
|
||||
off_t offset = ((off_t)dt.track << 8);
|
||||
if (dt.raw) {
|
||||
ASSERT(dt.size == 11);
|
||||
offset *= 0x930;
|
||||
offset += 0x10;
|
||||
} else {
|
||||
offset <<= dt.size;
|
||||
}
|
||||
|
||||
// Add offset to real image
|
||||
offset += dt.imgoffset;
|
||||
|
||||
// Calculate length (data size of this track)
|
||||
int length = dt.sectors << dt.size;
|
||||
|
||||
// Allocate buffer memory
|
||||
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||||
|
||||
if (dt.buffer == NULL) {
|
||||
if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) {
|
||||
LOGWARN("%s posix_memalign failed", __PRETTY_FUNCTION__);
|
||||
}
|
||||
dt.length = length;
|
||||
}
|
||||
|
||||
if (!dt.buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reallocate if the buffer length is different
|
||||
if (dt.length != (DWORD)length) {
|
||||
free(dt.buffer);
|
||||
if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) {
|
||||
LOGWARN("%s posix_memalign failed", __PRETTY_FUNCTION__);
|
||||
}
|
||||
dt.length = length;
|
||||
}
|
||||
|
||||
// Reserve change map memory
|
||||
if (dt.changemap == NULL) {
|
||||
dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL));
|
||||
dt.maplen = dt.sectors;
|
||||
}
|
||||
|
||||
if (!dt.changemap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reallocate if the buffer length is different
|
||||
if (dt.maplen != (DWORD)dt.sectors) {
|
||||
free(dt.changemap);
|
||||
dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL));
|
||||
dt.maplen = dt.sectors;
|
||||
}
|
||||
|
||||
// Clear changemap
|
||||
memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL));
|
||||
|
||||
// Read from File
|
||||
if (!fio.OpenDIO(path, Fileio::ReadOnly)) {
|
||||
return false;
|
||||
}
|
||||
if (dt.raw) {
|
||||
// Split Reading
|
||||
for (int i = 0; i < dt.sectors; i++) {
|
||||
// Seek
|
||||
if (!fio.Seek(offset)) {
|
||||
fio.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read
|
||||
if (!fio.Read(&dt.buffer[i << dt.size], 1 << dt.size)) {
|
||||
fio.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Next offset
|
||||
offset += 0x930;
|
||||
}
|
||||
} else {
|
||||
// Continuous reading
|
||||
if (!fio.Seek(offset)) {
|
||||
fio.Close();
|
||||
return false;
|
||||
}
|
||||
if (!fio.Read(dt.buffer, length)) {
|
||||
fio.Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
fio.Close();
|
||||
|
||||
// Set a flag and end normally
|
||||
dt.init = TRUE;
|
||||
dt.changed = FALSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Save
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskTrack::Save(const Filepath& path)
|
||||
{
|
||||
// Not needed if not initialized
|
||||
if (!dt.init) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not needed unless changed
|
||||
if (!dt.changed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Need to write
|
||||
ASSERT(dt.buffer);
|
||||
ASSERT(dt.changemap);
|
||||
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||||
|
||||
// Writing in RAW mode is not allowed
|
||||
ASSERT(!dt.raw);
|
||||
|
||||
// Calculate offset (previous tracks are considered to hold 256 sectors)
|
||||
off_t offset = ((off_t)dt.track << 8);
|
||||
offset <<= dt.size;
|
||||
|
||||
// Add offset to real image
|
||||
offset += dt.imgoffset;
|
||||
|
||||
// Calculate length per sector
|
||||
int length = 1 << dt.size;
|
||||
|
||||
// Open file
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadWrite)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Partial write loop
|
||||
int total;
|
||||
for (int i = 0; i < dt.sectors;) {
|
||||
// If changed
|
||||
if (dt.changemap[i]) {
|
||||
// Initialize write size
|
||||
total = 0;
|
||||
|
||||
// Seek
|
||||
if (!fio.Seek(offset + ((off_t)i << dt.size))) {
|
||||
fio.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Consectutive sector length
|
||||
int j;
|
||||
for (j = i; j < dt.sectors; j++) {
|
||||
// end when interrupted
|
||||
if (!dt.changemap[j]) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Add one sector
|
||||
total += length;
|
||||
}
|
||||
|
||||
// Write
|
||||
if (!fio.Write(&dt.buffer[i << dt.size], total)) {
|
||||
fio.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// To unmodified sector
|
||||
i = j;
|
||||
} else {
|
||||
// Next Sector
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Close
|
||||
fio.Close();
|
||||
|
||||
// Drop the change flag and exit
|
||||
memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL));
|
||||
dt.changed = FALSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Read Sector
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskTrack::Read(BYTE *buf, int sec) const
|
||||
{
|
||||
ASSERT(buf);
|
||||
ASSERT((sec >= 0) & (sec < 0x100));
|
||||
|
||||
LOGTRACE("%s reading sector: %d", __PRETTY_FUNCTION__,sec);
|
||||
// Error if not initialized
|
||||
if (!dt.init) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// // Error if the number of sectors exceeds the valid number
|
||||
if (sec >= dt.sectors) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy
|
||||
ASSERT(dt.buffer);
|
||||
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||||
memcpy(buf, &dt.buffer[(off_t)sec << dt.size], (off_t)1 << dt.size);
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Write Sector
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskTrack::Write(const BYTE *buf, int sec)
|
||||
{
|
||||
ASSERT(buf);
|
||||
ASSERT((sec >= 0) & (sec < 0x100));
|
||||
ASSERT(!dt.raw);
|
||||
|
||||
// Error if not initialized
|
||||
if (!dt.init) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// // Error if the number of sectors exceeds the valid number
|
||||
if (sec >= dt.sectors) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate offset and length
|
||||
int offset = sec << dt.size;
|
||||
int length = 1 << dt.size;
|
||||
|
||||
// Compare
|
||||
ASSERT(dt.buffer);
|
||||
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||||
if (memcmp(buf, &dt.buffer[offset], length) == 0) {
|
||||
// Exit normally since it's attempting to write the same thing
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copy, change
|
||||
memcpy(&dt.buffer[offset], buf, length);
|
||||
dt.changemap[sec] = TRUE;
|
||||
dt.changed = TRUE;
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Disk Cache
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DiskCache::DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff)
|
||||
{
|
||||
ASSERT(blocks > 0);
|
||||
ASSERT(imgoff >= 0);
|
||||
|
||||
// Cache work
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
cache[i].disktrk = NULL;
|
||||
cache[i].serial = 0;
|
||||
}
|
||||
|
||||
// Other
|
||||
serial = 0;
|
||||
sec_path = path;
|
||||
sec_size = size;
|
||||
sec_blocks = blocks;
|
||||
cd_raw = FALSE;
|
||||
imgoffset = imgoff;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DiskCache::~DiskCache()
|
||||
{
|
||||
// Clear the track
|
||||
Clear();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// RAW Mode Setting
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void DiskCache::SetRawMode(BOOL raw)
|
||||
{
|
||||
// Configuration
|
||||
cd_raw = raw;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Save
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskCache::Save()
|
||||
{
|
||||
// Save track
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
// Is it a valid track?
|
||||
if (cache[i].disktrk) {
|
||||
// Save
|
||||
if (!cache[i].disktrk->Save(sec_path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Get disk cache information
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskCache::GetCache(int index, int& track, DWORD& aserial) const
|
||||
{
|
||||
ASSERT((index >= 0) && (index < CacheMax));
|
||||
|
||||
// false if unused
|
||||
if (!cache[index].disktrk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set track and serial
|
||||
track = cache[index].disktrk->GetTrack();
|
||||
aserial = cache[index].serial;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Clear
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void DiskCache::Clear()
|
||||
{
|
||||
// Free the cache
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
if (cache[i].disktrk) {
|
||||
delete cache[i].disktrk;
|
||||
cache[i].disktrk = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Sector Read
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskCache::Read(BYTE *buf, int block)
|
||||
{
|
||||
ASSERT(sec_size != 0);
|
||||
|
||||
// Update first
|
||||
Update();
|
||||
|
||||
// Calculate track (fixed to 256 sectors/track)
|
||||
int track = block >> 8;
|
||||
|
||||
// Get the track data
|
||||
DiskTrack *disktrk = Assign(track);
|
||||
if (!disktrk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the track data to the cache
|
||||
return disktrk->Read(buf, (BYTE)block);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Sector write
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskCache::Write(const BYTE *buf, int block)
|
||||
{
|
||||
ASSERT(sec_size != 0);
|
||||
|
||||
// Update first
|
||||
Update();
|
||||
|
||||
// Calculate track (fixed to 256 sectors/track)
|
||||
int track = block >> 8;
|
||||
|
||||
// Get that track data
|
||||
DiskTrack *disktrk = Assign(track);
|
||||
if (!disktrk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the data to the cache
|
||||
return disktrk->Write(buf, (BYTE)block);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Track Assignment
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DiskTrack* DiskCache::Assign(int track)
|
||||
{
|
||||
ASSERT(sec_size != 0);
|
||||
ASSERT(track >= 0);
|
||||
|
||||
// First, check if it is already assigned
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
if (cache[i].disktrk) {
|
||||
if (cache[i].disktrk->GetTrack() == track) {
|
||||
// Track match
|
||||
cache[i].serial = serial;
|
||||
return cache[i].disktrk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next, check for empty
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
if (!cache[i].disktrk) {
|
||||
// Try loading
|
||||
if (Load(i, track)) {
|
||||
// Success loading
|
||||
cache[i].serial = serial;
|
||||
return cache[i].disktrk;
|
||||
}
|
||||
|
||||
// Load failed
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, find the youngest serial number and delete it
|
||||
|
||||
// Set index 0 as candidate c
|
||||
DWORD s = cache[0].serial;
|
||||
int c = 0;
|
||||
|
||||
// Compare candidate with serial and update to smaller one
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
ASSERT(cache[i].disktrk);
|
||||
|
||||
// Compare and update the existing serial
|
||||
if (cache[i].serial < s) {
|
||||
s = cache[i].serial;
|
||||
c = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Save this track
|
||||
if (!cache[c].disktrk->Save(sec_path)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Delete this track
|
||||
DiskTrack *disktrk = cache[c].disktrk;
|
||||
cache[c].disktrk = NULL;
|
||||
|
||||
// Load
|
||||
if (Load(c, track, disktrk)) {
|
||||
// Successful loading
|
||||
cache[c].serial = serial;
|
||||
return cache[c].disktrk;
|
||||
}
|
||||
|
||||
// Load failed
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Load cache
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool DiskCache::Load(int index, int track, DiskTrack *disktrk)
|
||||
{
|
||||
ASSERT((index >= 0) && (index < CacheMax));
|
||||
ASSERT(track >= 0);
|
||||
ASSERT(!cache[index].disktrk);
|
||||
|
||||
// Get the number of sectors on this track
|
||||
int sectors = sec_blocks - (track << 8);
|
||||
ASSERT(sectors > 0);
|
||||
if (sectors > 0x100) {
|
||||
sectors = 0x100;
|
||||
}
|
||||
|
||||
// Create a disk track
|
||||
if (disktrk == NULL) {
|
||||
disktrk = new DiskTrack();
|
||||
}
|
||||
|
||||
// Initialize disk track
|
||||
disktrk->Init(track, sec_size, sectors, cd_raw, imgoffset);
|
||||
|
||||
// Try loading
|
||||
if (!disktrk->Load(sec_path)) {
|
||||
// Failure
|
||||
delete disktrk;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocation successful, work set
|
||||
cache[index].disktrk = disktrk;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Update serial number
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void DiskCache::Update()
|
||||
{
|
||||
// Update and do nothing except 0
|
||||
serial++;
|
||||
if (serial != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear serial of all caches (loop in 32bit)
|
||||
for (int i = 0; i < CacheMax; i++) {
|
||||
cache[i].serial = 0;
|
||||
}
|
||||
}
|
||||
|
95
src/raspberrypi/devices/disk_track_cache.h
Normal file
95
src/raspberrypi/devices/disk_track_cache.h
Normal file
@ -0,0 +1,95 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// X68000 EMULATOR "XM6"
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
//
|
||||
// XM6i
|
||||
// Copyright (C) 2010-2015 isaki@NetBSD.org
|
||||
//
|
||||
// Imported sava's Anex86/T98Next image and MO format support patch.
|
||||
// Comments translated to english by akuker.
|
||||
//
|
||||
// [ DiskTrack and DiskCache ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../rascsi.h"
|
||||
#include "filepath.h"
|
||||
|
||||
// Number of tracks to cache
|
||||
#define CacheMax 16
|
||||
|
||||
class DiskTrack
|
||||
{
|
||||
private:
|
||||
struct {
|
||||
int track; // Track Number
|
||||
int size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096)
|
||||
int sectors; // Number of sectors(<0x100)
|
||||
DWORD length; // Data buffer length
|
||||
BYTE *buffer; // Data buffer
|
||||
BOOL init; // Is it initilized?
|
||||
BOOL changed; // Changed flag
|
||||
DWORD maplen; // Changed map length
|
||||
BOOL *changemap; // Changed map
|
||||
BOOL raw; // RAW mode flag
|
||||
off_t imgoffset; // Offset to actual data
|
||||
} dt;
|
||||
|
||||
public:
|
||||
DiskTrack();
|
||||
~DiskTrack();
|
||||
|
||||
void Init(int track, int size, int sectors, BOOL raw = FALSE, off_t imgoff = 0);
|
||||
bool Load(const Filepath& path);
|
||||
bool Save(const Filepath& path);
|
||||
|
||||
// Read / Write
|
||||
bool Read(BYTE *buf, int sec) const; // Sector Read
|
||||
bool Write(const BYTE *buf, int sec); // Sector Write
|
||||
|
||||
int GetTrack() const { return dt.track; } // Get track
|
||||
};
|
||||
|
||||
class DiskCache
|
||||
{
|
||||
public:
|
||||
// Internal data definition
|
||||
typedef struct {
|
||||
DiskTrack *disktrk; // Disk Track
|
||||
DWORD serial; // Serial
|
||||
} cache_t;
|
||||
|
||||
public:
|
||||
DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff = 0);
|
||||
~DiskCache();
|
||||
|
||||
void SetRawMode(BOOL raw); // CD-ROM raw mode setting
|
||||
|
||||
// Access
|
||||
bool Save(); // Save and release all
|
||||
bool Read(BYTE *buf, int block); // Sector Read
|
||||
bool Write(const BYTE *buf, int block); // Sector Write
|
||||
bool GetCache(int index, int& track, DWORD& serial) const; // Get cache information
|
||||
|
||||
private:
|
||||
// Internal Management
|
||||
void Clear(); // Clear all tracks
|
||||
DiskTrack* Assign(int track); // Load track
|
||||
bool Load(int index, int track, DiskTrack *disktrk = NULL); // Load track
|
||||
void Update(); // Update serial number
|
||||
|
||||
// Internal data
|
||||
cache_t cache[CacheMax]; // Cache management
|
||||
DWORD serial; // Last serial number
|
||||
Filepath sec_path; // Path
|
||||
int sec_size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096)
|
||||
int sec_blocks; // Blocks per sector
|
||||
BOOL cd_raw; // CD-ROM RAW mode
|
||||
off_t imgoffset; // Offset to actual data
|
||||
};
|
||||
|
43
src/raspberrypi/devices/file_support.cpp
Normal file
43
src/raspberrypi/devices/file_support.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "file_support.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
map<string, id_set> FileSupport::reserved_files;
|
||||
|
||||
void FileSupport::ReserveFile(const Filepath& path, int id, int lun)
|
||||
{
|
||||
reserved_files[path.GetPath()] = make_pair(id, lun);
|
||||
}
|
||||
|
||||
void FileSupport::UnreserveFile()
|
||||
{
|
||||
reserved_files.erase(diskpath.GetPath());
|
||||
}
|
||||
|
||||
bool FileSupport::GetIdsForReservedFile(const Filepath& path, int& id, int& unit) const
|
||||
{
|
||||
if (reserved_files.find(path.GetPath()) != reserved_files.end()) {
|
||||
const id_set ids = reserved_files[path.GetPath()];
|
||||
id = ids.first;
|
||||
unit = ids.second;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FileSupport::UnreserveAll()
|
||||
{
|
||||
reserved_files.clear();
|
||||
}
|
@ -5,19 +5,28 @@
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
// Devices inheriting from FileSupport support device image files
|
||||
// Devices inheriting from FileSupport support image files
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "filepath.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef pair<int, int> id_set;
|
||||
|
||||
class FileSupport
|
||||
{
|
||||
private:
|
||||
Filepath diskpath;
|
||||
|
||||
// The list of image files in use and the IDs and LUNs using these files
|
||||
static map<string, id_set> reserved_files;
|
||||
|
||||
public:
|
||||
|
||||
FileSupport() {};
|
||||
@ -25,6 +34,13 @@ public:
|
||||
|
||||
void GetPath(Filepath& path) const { path = diskpath; }
|
||||
void SetPath(const Filepath& path) { diskpath = path; }
|
||||
static const map<string, id_set> GetReservedFiles(){ return reserved_files; }
|
||||
static void SetReservedFiles(const map<string, id_set>& files_in_use) { FileSupport::reserved_files = files_in_use; }
|
||||
void ReserveFile(const Filepath&, int, int);
|
||||
void UnreserveFile();
|
||||
bool GetIdsForReservedFile(const Filepath&, int&, int&) const;
|
||||
|
||||
static void UnreserveAll();
|
||||
|
||||
virtual void Open(const Filepath&) = 0;
|
||||
};
|
||||
|
48
src/raspberrypi/devices/interfaces/scsi_block_commands.h
Normal file
48
src/raspberrypi/devices/interfaces/scsi_block_commands.h
Normal file
@ -0,0 +1,48 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
// Interface for SCSI block commands (see https://www.t10.org/drafts.htm, SBC-5)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
class SASIDEV;
|
||||
|
||||
class ScsiBlockCommands
|
||||
{
|
||||
public:
|
||||
|
||||
ScsiBlockCommands() {};
|
||||
virtual ~ScsiBlockCommands() {};
|
||||
|
||||
// Mandatory commands
|
||||
virtual void TestUnitReady(SASIDEV *) = 0;
|
||||
virtual void Inquiry(SASIDEV *) = 0;
|
||||
virtual void ReportLuns(SASIDEV *) = 0;
|
||||
virtual void FormatUnit(SASIDEV *) = 0;
|
||||
virtual void ReadCapacity10(SASIDEV *) = 0;
|
||||
virtual void ReadCapacity16(SASIDEV *) = 0;
|
||||
virtual void Read10(SASIDEV *) = 0;
|
||||
virtual void Read16(SASIDEV *) = 0;
|
||||
virtual void Write10(SASIDEV *) = 0;
|
||||
virtual void Write16(SASIDEV *) = 0;
|
||||
virtual void RequestSense(SASIDEV *) = 0;
|
||||
|
||||
// Implemented optional commands
|
||||
virtual void Verify10(SASIDEV *) = 0;
|
||||
virtual void Verify16(SASIDEV *) = 0;
|
||||
virtual void ModeSense6(SASIDEV *) = 0;
|
||||
virtual void ModeSense10(SASIDEV *) = 0;
|
||||
virtual void ModeSelect6(SASIDEV *) = 0;
|
||||
virtual void ModeSelect10(SASIDEV *) = 0;
|
||||
virtual void ReassignBlocks(SASIDEV *) = 0;
|
||||
virtual void SendDiagnostic(SASIDEV *) = 0;
|
||||
virtual void StartStopUnit(SASIDEV *) = 0;
|
||||
virtual void SynchronizeCache10(SASIDEV *) = 0;
|
||||
virtual void SynchronizeCache16(SASIDEV *) = 0;
|
||||
};
|
25
src/raspberrypi/devices/interfaces/scsi_mmc_commands.h
Normal file
25
src/raspberrypi/devices/interfaces/scsi_mmc_commands.h
Normal file
@ -0,0 +1,25 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
// Interface for SCSI Multi-Media commands (see https://www.t10.org/drafts.htm, MMC-6)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
class SASIDEV;
|
||||
|
||||
class ScsiMmcCommands
|
||||
{
|
||||
public:
|
||||
|
||||
ScsiMmcCommands() {};
|
||||
virtual ~ScsiMmcCommands() {};
|
||||
|
||||
virtual void ReadToc(SASIDEV *) = 0;
|
||||
virtual void GetEventStatusNotification(SASIDEV *) = 0;
|
||||
};
|
34
src/raspberrypi/devices/interfaces/scsi_primary_commands.h
Normal file
34
src/raspberrypi/devices/interfaces/scsi_primary_commands.h
Normal file
@ -0,0 +1,34 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
// Interface for SCSI primary commands (see https://www.t10.org/drafts.htm, SPC-6)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
class SASIDEV;
|
||||
|
||||
class ScsiPrimaryCommands
|
||||
{
|
||||
public:
|
||||
|
||||
ScsiPrimaryCommands() {};
|
||||
virtual ~ScsiPrimaryCommands() {};
|
||||
|
||||
// Mandatory commands
|
||||
virtual void TestUnitReady(SASIDEV *) = 0;
|
||||
virtual void Inquiry(SASIDEV *) = 0;
|
||||
virtual void ReportLuns(SASIDEV *) = 0;
|
||||
|
||||
// Implemented optional commands
|
||||
virtual void RequestSense(SASIDEV *) = 0;
|
||||
virtual void ModeSense6(SASIDEV *) = 0;
|
||||
virtual void ModeSense10(SASIDEV *) = 0;
|
||||
virtual void ModeSelect6(SASIDEV *) = 0;
|
||||
virtual void ModeSelect10(SASIDEV *) = 0;
|
||||
};
|
@ -1,33 +0,0 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 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;
|
||||
};
|
@ -14,9 +14,10 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#include "sasihd.h"
|
||||
#include "xm6.h"
|
||||
#include "fileio.h"
|
||||
#include "exceptions.h"
|
||||
#include <sstream>
|
||||
#include "../rascsi.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
@ -31,7 +32,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
SASIHD::SASIHD() : Disk("SAHD")
|
||||
{
|
||||
SetProtectable(true);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -62,30 +62,22 @@ void SASIHD::Open(const Filepath& path)
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw io_exception("Can't open hard disk file read-only");
|
||||
throw file_not_found_exception("Can't open SASI hard disk file");
|
||||
}
|
||||
|
||||
// Get file size
|
||||
off_t size = fio.GetFileSize();
|
||||
fio.Close();
|
||||
|
||||
#if defined(USE_MZ1F23_1024_SUPPORT)
|
||||
// For MZ-2500 / MZ-2800 MZ-1F23 (SASI 20M / sector size 1024) only
|
||||
// 20M(22437888 BS=1024 C=21912)
|
||||
if (size == 0x1566000) {
|
||||
// Sector size and number of blocks
|
||||
SetSectorSize(10);
|
||||
SetBlockCount((DWORD)(size >> 10));
|
||||
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
}
|
||||
#endif // USE_MZ1F23_1024_SUPPORT
|
||||
// Sector size (default 256 bytes) and number of blocks
|
||||
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 256, true);
|
||||
SetBlockCount((DWORD)(size >> GetSectorSize()));
|
||||
|
||||
#if defined(REMOVE_FIXED_SASIHD_SIZE)
|
||||
// Must be in 256-byte units
|
||||
if (size & 0xff) {
|
||||
throw io_exception("File size must be a multiple of 256 bytes");
|
||||
if (size % GetSectorSizeInBytes()) {
|
||||
stringstream error;
|
||||
error << "File size must be a multiple of " << GetSectorSizeInBytes() << " bytes but is " << size << " bytes";
|
||||
throw io_exception(error.str());
|
||||
}
|
||||
|
||||
// 10MB or more
|
||||
@ -118,12 +110,19 @@ void SASIHD::Open(const Filepath& path)
|
||||
}
|
||||
#endif // REMOVE_FIXED_SASIHD_SIZE
|
||||
|
||||
// Sector size 256 bytes and number of blocks
|
||||
SetSectorSize(8);
|
||||
SetBlockCount((DWORD)(size >> 8));
|
||||
|
||||
// Call the base class
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// INQUIRY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SASIHD::Inquiry(const DWORD* /*cdb*/, BYTE* /*buf*/)
|
||||
{
|
||||
SetStatusCode(STATUS_INVALIDCMD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -27,11 +27,13 @@
|
||||
class SASIHD : public Disk, public FileSupport
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
SASIHD(); // Constructor
|
||||
void Reset(); // Reset
|
||||
void Open(const Filepath& path); // Open
|
||||
SASIHD();
|
||||
~SASIHD() {};
|
||||
|
||||
// commands
|
||||
int RequestSense(const DWORD *cdb, BYTE *buf); // REQUEST SENSE command
|
||||
void Reset();
|
||||
void Open(const Filepath& path);
|
||||
|
||||
// Commands
|
||||
int RequestSense(const DWORD *cdb, BYTE *buf) override;
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf) override;
|
||||
};
|
||||
|
@ -24,48 +24,86 @@
|
||||
// This does NOT include the file system functionality that is present
|
||||
// in the Sharp X68000 host bridge.
|
||||
//
|
||||
// Note: This requires the DaynaPort SCSI Link driver.
|
||||
// Note: This requires a DaynaPort SCSI Link driver.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "scsi_daynaport.h"
|
||||
#include <sstream>
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// DaynaPort SCSI Link Ethernet Adapter
|
||||
//
|
||||
//===========================================================================
|
||||
const char* SCSIDaynaPort::m_vendor_name = "DAYNA ";
|
||||
const char* SCSIDaynaPort::m_device_name = "SCSI/Link ";
|
||||
const char* SCSIDaynaPort::m_revision = "1.4a";
|
||||
const char* SCSIDaynaPort::m_firmware_version = "01.00.00";
|
||||
|
||||
const BYTE SCSIDaynaPort::m_bcast_addr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
const BYTE SCSIDaynaPort::m_apple_talk_addr[6] = { 0x09, 0x00, 0x07, 0xff, 0xff, 0xff };
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP")
|
||||
{
|
||||
SetRemovable(false);
|
||||
m_tap = NULL;
|
||||
m_bTapEnable = false;
|
||||
|
||||
SetVendor("Dayna");
|
||||
SetProduct("SCSI/Link");
|
||||
AddCommand(SCSIDEV::eCmdTestUnitReady, "TestUnitReady", &SCSIDaynaPort::TestUnitReady);
|
||||
AddCommand(SCSIDEV::eCmdRead6, "Read6", &SCSIDaynaPort::Read6);
|
||||
AddCommand(SCSIDEV::eCmdWrite6, "Write6", &SCSIDaynaPort::Write6);
|
||||
AddCommand(SCSIDEV::eCmdRetrieveStats, "RetrieveStats", &SCSIDaynaPort::RetrieveStatistics);
|
||||
AddCommand(SCSIDEV::eCmdSetIfaceMode, "SetIfaceMode", &SCSIDaynaPort::SetInterfaceMode);
|
||||
AddCommand(SCSIDEV::eCmdSetMcastAddr, "SetMcastAddr", &SCSIDaynaPort::SetMcastAddr);
|
||||
AddCommand(SCSIDEV::eCmdEnableInterface, "EnableInterface", &SCSIDaynaPort::EnableInterface);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
SCSIDaynaPort::~SCSIDaynaPort()
|
||||
{
|
||||
// TAP driver release
|
||||
if (m_tap) {
|
||||
m_tap->Cleanup();
|
||||
delete m_tap;
|
||||
}
|
||||
|
||||
for (auto const& command : commands) {
|
||||
delete command.second;
|
||||
}
|
||||
}
|
||||
|
||||
void SCSIDaynaPort::AddCommand(SCSIDEV::scsi_command opcode, const char* name, void (SCSIDaynaPort::*execute)(SASIDEV *))
|
||||
{
|
||||
commands[opcode] = new command_t(name, execute);
|
||||
}
|
||||
|
||||
bool SCSIDaynaPort::Dispatch(SCSIDEV *controller)
|
||||
{
|
||||
ctrl = controller->GetCtrl();
|
||||
|
||||
if (commands.count(static_cast<SCSIDEV::scsi_command>(ctrl->cmd[0]))) {
|
||||
command_t *command = commands[static_cast<SCSIDEV::scsi_command>(ctrl->cmd[0])];
|
||||
|
||||
LOGDEBUG("%s Executing %s ($%02X)", __PRETTY_FUNCTION__, command->name, (unsigned int)ctrl->cmd[0]);
|
||||
|
||||
(this->*command->execute)(controller);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LOGTRACE("%s Calling base class for dispatching $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->cmd[0]);
|
||||
|
||||
// The base class handles the less specific commands
|
||||
return Disk::Dispatch(controller);
|
||||
}
|
||||
|
||||
bool SCSIDaynaPort::Init(const list<string>& params)
|
||||
{
|
||||
SetParams(params.empty() ? GetDefaultParams() : params);
|
||||
|
||||
#ifdef __linux__
|
||||
// TAP Driver Generation
|
||||
m_tap = new CTapDriver();
|
||||
m_tap = new CTapDriver(GetParams().front());
|
||||
m_bTapEnable = m_tap->Init();
|
||||
if(!m_bTapEnable){
|
||||
LOGERROR("Unable to open the TAP interface");
|
||||
}else {
|
||||
|
||||
// Not terminating on regular Linux PCs is helpful for testing
|
||||
#if !defined(__x86_64__) && !defined(__X86__)
|
||||
return false;
|
||||
#endif
|
||||
} else {
|
||||
LOGDEBUG("Tap interface created");
|
||||
}
|
||||
|
||||
LOGTRACE("%s this->reset()", __PRETTY_FUNCTION__);
|
||||
this->Reset();
|
||||
SetReady(true);
|
||||
SetReset(false);
|
||||
@ -87,30 +125,13 @@ SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP")
|
||||
m_mac_addr[3]=0x10;
|
||||
m_mac_addr[4]=0x98;
|
||||
m_mac_addr[5]=0xE3;
|
||||
|
||||
#endif // linux
|
||||
LOGTRACE("SCSIDaynaPort Constructor End");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIDaynaPort::~SCSIDaynaPort()
|
||||
void SCSIDaynaPort::Open(const Filepath& path)
|
||||
{
|
||||
LOGTRACE("SCSIDaynaPort Destructor");
|
||||
// TAP driver release
|
||||
if (m_tap) {
|
||||
m_tap->Cleanup();
|
||||
delete m_tap;
|
||||
}
|
||||
}
|
||||
|
||||
void SCSIDaynaPort::Open(const Filepath& path, BOOL attn)
|
||||
{
|
||||
LOGTRACE("SCSIDaynaPort Open");
|
||||
m_tap->OpenDump(path);
|
||||
}
|
||||
|
||||
@ -119,42 +140,34 @@ SCSIDaynaPort::~SCSIDaynaPort()
|
||||
// INQUIRY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIDaynaPort::Inquiry(const DWORD *cdb, BYTE *buffer)
|
||||
int SCSIDaynaPort::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
{
|
||||
// scsi_cdb_6_byte_t command;
|
||||
// memcpy(&command,cdb,sizeof(command));
|
||||
int allocation_length = cdb[4] + (((DWORD)cdb[3]) << 8);
|
||||
|
||||
ASSERT(cdb);
|
||||
ASSERT(buffer);
|
||||
LOGTRACE("%s Inquiry, allocation length: %d",__PRETTY_FUNCTION__, allocation_length);
|
||||
|
||||
//allocation_length = command->length;
|
||||
DWORD allocation_length = cdb[4] + (((DWORD)cdb[3]) << 8);
|
||||
// if(allocation_length != command.length){
|
||||
// LOGDEBUG("%s CDB: %02X %02X %02X %02X %02X %02X", __PRETTY_FUNCTION__, (unsigned int)cdb[0], (unsigned int)cdb[1], (unsigned int)cdb[2], (unsigned int)cdb[3], (unsigned int)cdb[4], (unsigned int)cdb[5] );
|
||||
// LOGWARN(":::::::::: Expected allocation length %04X but found %04X", (unsigned int)allocation_length, (unsigned int)command.length);
|
||||
// LOGWARN(":::::::::: Doing runtime pointer conversion: %04X", ((scsi_cdb_6_byte_t*)cdb)->length);
|
||||
// }
|
||||
|
||||
LOGTRACE("%s Inquiry, allocation length: %d",__PRETTY_FUNCTION__, (int)allocation_length);
|
||||
|
||||
if (allocation_length > 4){
|
||||
if (allocation_length > sizeof(m_daynaport_inquiry_response)) {
|
||||
allocation_length = sizeof(m_daynaport_inquiry_response);
|
||||
if (allocation_length > 4) {
|
||||
if (allocation_length > 44) {
|
||||
allocation_length = 44;
|
||||
}
|
||||
|
||||
// Copy the pre-canned response
|
||||
memcpy(buffer, m_daynaport_inquiry_response, allocation_length);
|
||||
// Basic data
|
||||
// buf[0] ... Processor Device
|
||||
// buf[1] ... Not removable
|
||||
// buf[2] ... SCSI-2 compliant command system
|
||||
// buf[3] ... SCSI-2 compliant Inquiry response
|
||||
// buf[4] ... Inquiry additional data
|
||||
// http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/pocket_scsiLINK/pocketscsilink_inq.png
|
||||
memset(buf, 0, allocation_length);
|
||||
buf[0] = 0x03;
|
||||
buf[2] = 0x01;
|
||||
buf[4] = 0x1F;
|
||||
|
||||
// Padded vendor, product, revision
|
||||
memcpy(&buffer[8], GetPaddedName().c_str(), 28);
|
||||
memcpy(&buf[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);
|
||||
LOGTRACE("response size is %d", allocation_length);
|
||||
|
||||
return allocation_length;
|
||||
}
|
||||
@ -163,40 +176,62 @@ int SCSIDaynaPort::Inquiry(const DWORD *cdb, BYTE *buffer)
|
||||
//
|
||||
// READ
|
||||
//
|
||||
// Command: 08 00 00 LL LL XX (LLLL is data length, XX = c0 or 80)
|
||||
// Function: Read a packet at a time from the device (standard SCSI Read)
|
||||
// Type: Input; the following data is returned:
|
||||
// LL LL NN NN NN NN XX XX XX ... CC CC CC CC
|
||||
// where:
|
||||
// LLLL is normally the length of the packet (a 2-byte
|
||||
// big-endian hex value), including 4 trailing bytes
|
||||
// of CRC, but excluding itself and the flag field.
|
||||
// See below for special values
|
||||
// NNNNNNNN is a 4-byte flag field with the following meanings:
|
||||
// FFFFFFFF a packet has been dropped (?); in this case
|
||||
// the length field appears to be always 4000
|
||||
// 00000010 there are more packets currently available
|
||||
// in SCSI/Link memory
|
||||
// 00000000 this is the last packet
|
||||
// XX XX ... is the actual packet
|
||||
// CCCCCCCC is the CRC
|
||||
//
|
||||
// Notes:
|
||||
// - When all packets have been retrieved successfully, a length field
|
||||
// of 0000 is returned; however, if a packet has been dropped, the
|
||||
// SCSI/Link will instead return a non-zero length field with a flag
|
||||
// of FFFFFFFF when there are no more packets available. This behaviour
|
||||
// seems to continue until a disable/enable sequence has been issued.
|
||||
// - The SCSI/Link apparently has about 6KB buffer space for packets.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, uint64_t block)
|
||||
{
|
||||
WORD requested_length = 0;
|
||||
int rx_packet_size = 0;
|
||||
BOOL send_message_to_host;
|
||||
scsi_resp_read_t *response = (scsi_resp_read_t*)buf;
|
||||
scsi_cmd_read_6_t *command = (scsi_cmd_read_6_t*)cdb;
|
||||
int read_count = 0;
|
||||
|
||||
ASSERT(buf);
|
||||
|
||||
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);
|
||||
if (command->operation_code != 0x08) {
|
||||
LOGERROR("Received unexpected cdb command: %02X. Expected 0x08", command->operation_code);
|
||||
}
|
||||
|
||||
requested_length = (WORD)command->transfer_length;
|
||||
LOGTRACE("%s Read maximum length %d, (%04X)", __PRETTY_FUNCTION__, (unsigned int)requested_length, (unsigned int)requested_length);
|
||||
int requested_length = command->transfer_length;
|
||||
LOGTRACE("%s Read maximum length %d, (%04X)", __PRETTY_FUNCTION__, requested_length, requested_length);
|
||||
|
||||
|
||||
// At host startup, it will send a READ(6) command with a length of 1. We should
|
||||
// respond by going into the status mode with a code of 0x02
|
||||
if(requested_length == 1){
|
||||
if (requested_length == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Some of the packets we receive will not be for us. So, we'll keep pulling messages
|
||||
// until the buffer is empty, or we've read X times. (X is just a made up number)
|
||||
while(read_count < MAX_READ_RETRIES)
|
||||
{
|
||||
bool send_message_to_host;
|
||||
int read_count = 0;
|
||||
while (read_count < MAX_READ_RETRIES) {
|
||||
read_count++;
|
||||
|
||||
// The first 2 bytes are reserved for the length of the packet
|
||||
@ -205,7 +240,7 @@ int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
rx_packet_size = m_tap->Rx(&buf[DAYNAPORT_READ_HEADER_SZ]);
|
||||
|
||||
// If we didn't receive anything, return size of 0
|
||||
if(rx_packet_size <= 0){
|
||||
if (rx_packet_size <= 0) {
|
||||
LOGTRACE("%s No packet received", __PRETTY_FUNCTION__);
|
||||
response->length = 0;
|
||||
response->flags = e_no_more_data;
|
||||
@ -216,7 +251,7 @@ int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
|
||||
// This is a very basic filter to prevent unnecessary packets from
|
||||
// being sent to the SCSI initiator.
|
||||
send_message_to_host = FALSE;
|
||||
send_message_to_host = false;
|
||||
|
||||
// The following doesn't seem to work with unicast messages. Temporarily removing the filtering
|
||||
// functionality.
|
||||
@ -224,24 +259,24 @@ int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
/////// // DaynaPort MAC. For IP packets, the mac_address will be the first 6 bytes
|
||||
/////// // of the data.
|
||||
/////// if (memcmp(response->data, m_mac_addr, 6) == 0) {
|
||||
/////// send_message_to_host = TRUE;
|
||||
/////// send_message_to_host = true;
|
||||
/////// }
|
||||
|
||||
/////// // Check to see if this is a broadcast message
|
||||
/////// if (memcmp(response->data, m_bcast_addr, 6) == 0) {
|
||||
/////// send_message_to_host = TRUE;
|
||||
/////// send_message_to_host = true;
|
||||
/////// }
|
||||
|
||||
/////// // Check to see if this is an AppleTalk Message
|
||||
/////// if (memcmp(response->data, m_apple_talk_addr, 6) == 0) {
|
||||
/////// send_message_to_host = TRUE;
|
||||
/////// send_message_to_host = true;
|
||||
/////// }
|
||||
send_message_to_host = TRUE;
|
||||
send_message_to_host = true;
|
||||
|
||||
// TODO: We should check to see if this message is in the multicast
|
||||
// configuration from SCSI command 0x0D
|
||||
|
||||
if(!send_message_to_host){
|
||||
if (!send_message_to_host) {
|
||||
LOGDEBUG("%s Received a packet that's not for me: %02X %02X %02X %02X %02X %02X", \
|
||||
__PRETTY_FUNCTION__,
|
||||
(int)response->data[0],
|
||||
@ -253,19 +288,16 @@ int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
|
||||
// If there are pending packets to be processed, we'll tell the host that the read
|
||||
// length was 0.
|
||||
if(!m_tap->PendingPackets())
|
||||
if (!m_tap->PendingPackets())
|
||||
{
|
||||
response->length = 0;
|
||||
response->flags = e_no_more_data;
|
||||
return DAYNAPORT_READ_HEADER_SZ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
else {
|
||||
// TODO: Need to do some sort of size checking. The buffer can easily overflow, probably.
|
||||
|
||||
|
||||
// response->length = rx_packet_size;
|
||||
// if(m_tap->PendingPackets()){
|
||||
// response->flags = e_more_data_available;
|
||||
@ -304,21 +336,17 @@ int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIDaynaPort::WriteCheck(DWORD block)
|
||||
{
|
||||
ostringstream s;
|
||||
s << __PRETTY_FUNCTION__ << " block: " << block;
|
||||
LOGTRACE("%s", s.str().c_str());
|
||||
|
||||
// Status check
|
||||
if (!CheckReady()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!m_bTapEnable){
|
||||
if (!m_bTapEnable){
|
||||
SetStatusCode(STATUS_NOTREADY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Success
|
||||
// Success
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -326,32 +354,32 @@ int SCSIDaynaPort::WriteCheck(DWORD block)
|
||||
//
|
||||
// Write
|
||||
//
|
||||
// Command: 0a 00 00 LL LL XX (LLLL is data length, XX = 80 or 00)
|
||||
// Function: Write a packet at a time to the device (standard SCSI Write)
|
||||
// Type: Output; the format of the data to be sent depends on the value
|
||||
// of XX, as follows:
|
||||
// - if XX = 00, LLLL is the packet length, and the data to be sent
|
||||
// must be an image of the data packet
|
||||
// - if XX = 80, LLLL is the packet length + 8, and the data to be
|
||||
// sent is:
|
||||
// PP PP 00 00 XX XX XX ... 00 00 00 00
|
||||
// where:
|
||||
// PPPP is the actual (2-byte big-endian) packet length
|
||||
// XX XX ... is the actual packet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool SCSIDaynaPort::Write(const DWORD *cdb, const BYTE *buf, DWORD block)
|
||||
{
|
||||
BYTE data_format;
|
||||
WORD data_length;
|
||||
// const scsi_cmd_daynaport_write_t* command = (const scsi_cmd_daynaport_write_t*)cdb;
|
||||
BYTE data_format = cdb[5];
|
||||
WORD data_length = (WORD)cdb[4] + ((WORD)cdb[3] << 8);
|
||||
|
||||
data_format = cdb[5];
|
||||
data_length = (WORD)cdb[4] + ((WORD)cdb[3] << 8);
|
||||
|
||||
// if(data_format != command->format){
|
||||
// LOGDEBUG("%s CDB: %02X %02X %02X %02X %02X %02X", __PRETTY_FUNCTION__, (unsigned int)cdb[0], (unsigned int)cdb[1], (unsigned int)cdb[2], (unsigned int)cdb[3], (unsigned int)cdb[4], (unsigned int)cdb[5] );
|
||||
// LOGWARN("Expected data_format: %02X, but found %02X", (unsigned int)cdb[5], (unsigned int)command->format);
|
||||
// }
|
||||
// if(data_length != command->length){
|
||||
// LOGDEBUG("%s CDB: %02X %02X %02X %02X %02X %02X", __PRETTY_FUNCTION__, (unsigned int)cdb[0], (unsigned int)cdb[1], (unsigned int)cdb[2], (unsigned int)cdb[3], (unsigned int)cdb[4], (unsigned int)cdb[5] );
|
||||
// LOGWARN("Expected data_length: %04X, but found %04X", data_length, (unsigned int)command->length);
|
||||
// }
|
||||
|
||||
if(data_format == 0x00){
|
||||
if (data_format == 0x00){
|
||||
m_tap->Tx(buf, data_length);
|
||||
LOGTRACE("%s Transmitted %u bytes (00 format)", __PRETTY_FUNCTION__, data_length);
|
||||
return true;
|
||||
}
|
||||
else if (data_format == 0x80){
|
||||
// The data length is actuall specified in the first 2 bytes of the payload
|
||||
// The data length is 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);
|
||||
@ -365,43 +393,36 @@ bool SCSIDaynaPort::Write(const DWORD *cdb, const BYTE *buf, DWORD block)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// RetrieveStats
|
||||
//
|
||||
// Command: 09 00 00 00 12 00
|
||||
// Function: Retrieve MAC address and device statistics
|
||||
// Type: Input; returns 18 (decimal) bytes of data as follows:
|
||||
// - bytes 0-5: the current hardware ethernet (MAC) address
|
||||
// - bytes 6-17: three long word (4-byte) counters (little-endian).
|
||||
// Notes: The contents of the three longs are typically zero, and their
|
||||
// usage is unclear; they are suspected to be:
|
||||
// - long #1: frame alignment errors
|
||||
// - long #2: CRC errors
|
||||
// - long #3: frames lost
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIDaynaPort::RetrieveStats(const DWORD *cdb, BYTE *buffer)
|
||||
{
|
||||
DWORD response_size;
|
||||
DWORD allocation_length;
|
||||
int allocation_length = cdb[4] + (((DWORD)cdb[3]) << 8);
|
||||
LOGTRACE("%s Retrieve Stats buffer size is %d", __PRETTY_FUNCTION__, (int)allocation_length);
|
||||
|
||||
// DWORD frame_alignment_errors;
|
||||
// DWORD crc_errors;
|
||||
// DWORD frames_lost;
|
||||
|
||||
LOGTRACE("%s RetrieveStats ", __PRETTY_FUNCTION__);
|
||||
|
||||
ASSERT(cdb);
|
||||
ASSERT(buffer);
|
||||
|
||||
allocation_length = cdb[4] + (((DWORD)cdb[3]) << 8);
|
||||
LOGTRACE("%s Retrieve Stats buffer size was %d", __PRETTY_FUNCTION__, (int)allocation_length);
|
||||
|
||||
// // ASSERT(cdb[0] == 0x09);
|
||||
|
||||
// if(cdb[0] != 0x09)
|
||||
// {
|
||||
// LOGWARN("%s cdb[0] was not 0x09, as I expected. It was %02X.", __PRETTY_FUNCTION__, (unsigned int)cdb[0]);
|
||||
// }
|
||||
// if(cdb[4] != 0x12)
|
||||
// {
|
||||
// LOGWARN("%s cdb[4] was not 0x12, as I expected. It was %02X.", __PRETTY_FUNCTION__, (unsigned int)cdb[4]);
|
||||
// }
|
||||
|
||||
// memset(buffer,0,18);
|
||||
|
||||
// memcpy(&buffer[0],m_mac_addr,sizeof(m_mac_addr));
|
||||
// uint32_t frame_alignment_errors;
|
||||
// uint32_t crc_errors;
|
||||
// uint32_t frames_lost;
|
||||
// // frame alignment errors
|
||||
// frame_alignment_errors = htonl(0);
|
||||
// memcpy(&(buffer[6]),&frame_alignment_errors,sizeof(frame_alignment_errors));
|
||||
@ -412,47 +433,35 @@ int SCSIDaynaPort::RetrieveStats(const DWORD *cdb, BYTE *buffer)
|
||||
// frames_lost = htonl(0);
|
||||
// memcpy(&(buffer[14]),&frames_lost,sizeof(frames_lost));
|
||||
|
||||
for(int i=0; i< 6; i++)
|
||||
{
|
||||
for (int i = 0; i < 6; i++) {
|
||||
LOGTRACE("%s CDB byte %d: %02X",__PRETTY_FUNCTION__, i, (unsigned int)cdb[i]);
|
||||
}
|
||||
|
||||
response_size = 18;
|
||||
|
||||
response_size = sizeof(m_scsi_link_stats);
|
||||
int response_size = sizeof(m_scsi_link_stats);
|
||||
memcpy(buffer, &m_scsi_link_stats, sizeof(m_scsi_link_stats));
|
||||
|
||||
LOGTRACE("%s response size is %d", __PRETTY_FUNCTION__, (int)response_size);
|
||||
|
||||
if(response_size > allocation_length)
|
||||
{
|
||||
if (response_size > allocation_length) {
|
||||
response_size = allocation_length;
|
||||
LOGINFO("%s Truncating the inquiry response", __PRETTY_FUNCTION__)
|
||||
}
|
||||
|
||||
// Success
|
||||
// Success
|
||||
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;
|
||||
|
||||
// LOGTRACE("%s Retrieve Stats buffer size was %d", __PRETTY_FUNCTION__, command->length);
|
||||
|
||||
// ASSERT(sizeof(scsi_resp_link_stats_t) == 18);
|
||||
|
||||
// memcpy(response->mac_address, m_mac_addr, sizeof(m_mac_addr));
|
||||
// response->crc_errors = 0;
|
||||
// response->frames_lost = 0;
|
||||
// response->frame_alignment_errors = 0;
|
||||
|
||||
// // Success
|
||||
// disk.code = DISK_NOERROR;
|
||||
// return sizeof(scsi_resp_link_stats_t);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Enable or Disable the interface
|
||||
//
|
||||
// Command: 0e 00 00 00 00 XX (XX = 80 or 00)
|
||||
// Function: Enable (80) / disable (00) Ethernet interface
|
||||
// Type: No data transferred
|
||||
// Notes: After issuing an Enable, the initiator should avoid sending
|
||||
// any subsequent commands to the device for approximately 0.5
|
||||
// seconds
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool SCSIDaynaPort::EnableInterface(const DWORD *cdb)
|
||||
{
|
||||
@ -480,17 +489,158 @@ bool SCSIDaynaPort::EnableInterface(const DWORD *cdb)
|
||||
return result;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// TEST UNIT READY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool SCSIDaynaPort::TestUnitReady(const DWORD* /*cdb*/)
|
||||
void SCSIDaynaPort::TestUnitReady(SASIDEV *controller)
|
||||
{
|
||||
LOGTRACE("%s", __PRETTY_FUNCTION__);
|
||||
|
||||
// TEST UNIT READY Success
|
||||
return true;
|
||||
|
||||
controller->Status();
|
||||
}
|
||||
|
||||
void SCSIDaynaPort::Read6(SASIDEV *controller)
|
||||
{
|
||||
// Get record number and block number
|
||||
uint32_t record = ctrl->cmd[1] & 0x1f;
|
||||
record <<= 8;
|
||||
record |= ctrl->cmd[2];
|
||||
record <<= 8;
|
||||
record |= ctrl->cmd[3];
|
||||
ctrl->blocks=1;
|
||||
|
||||
LOGTRACE("%s READ(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl->blocks);
|
||||
|
||||
ctrl->length = Read(ctrl->cmd, ctrl->buffer, record);
|
||||
LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, (int)ctrl->length);
|
||||
|
||||
// Set next block
|
||||
ctrl->next = record + 1;
|
||||
|
||||
controller->DataIn();
|
||||
}
|
||||
|
||||
void SCSIDaynaPort::Write6(SASIDEV *controller)
|
||||
{
|
||||
// Reallocate buffer (because it is not transfer for each block)
|
||||
if (ctrl->bufsize < DAYNAPORT_BUFFER_SIZE) {
|
||||
free(ctrl->buffer);
|
||||
ctrl->bufsize = DAYNAPORT_BUFFER_SIZE;
|
||||
ctrl->buffer = (BYTE *)malloc(ctrl->bufsize);
|
||||
}
|
||||
|
||||
DWORD data_format = ctrl->cmd[5];
|
||||
|
||||
if(data_format == 0x00) {
|
||||
ctrl->length = (WORD)ctrl->cmd[4] + ((WORD)ctrl->cmd[3] << 8);
|
||||
}
|
||||
else if (data_format == 0x80) {
|
||||
ctrl->length = (WORD)ctrl->cmd[4] + ((WORD)ctrl->cmd[3] << 8) + 8;
|
||||
}
|
||||
else {
|
||||
LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)data_format);
|
||||
}
|
||||
LOGTRACE("%s length: %04X (%d) format: %02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->length, (int)ctrl->length, (unsigned int)data_format);
|
||||
|
||||
if (ctrl->length <= 0) {
|
||||
// Failure (Error)
|
||||
controller->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set next block
|
||||
ctrl->blocks = 1;
|
||||
ctrl->next = 1;
|
||||
|
||||
controller->DataOut();
|
||||
}
|
||||
|
||||
void SCSIDaynaPort::RetrieveStatistics(SASIDEV *controller)
|
||||
{
|
||||
ctrl->length = RetrieveStats(ctrl->cmd, ctrl->buffer);
|
||||
if (ctrl->length <= 0) {
|
||||
// Failure (Error)
|
||||
controller->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set next block
|
||||
ctrl->blocks = 1;
|
||||
ctrl->next = 1;
|
||||
|
||||
controller->DataIn();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Set interface mode/Set MAC address
|
||||
//
|
||||
// Set Interface Mode (0c)
|
||||
// -----------------------
|
||||
// Command: 0c 00 00 00 FF 80 (FF = 08 or 04)
|
||||
// Function: Allow interface to receive broadcast messages (FF = 04); the
|
||||
// function of (FF = 08) is currently unknown.
|
||||
// Type: No data transferred
|
||||
// Notes: This command is accepted by firmware 1.4a & 2.0f, but has no
|
||||
// effect on 2.0f, which is always capable of receiving broadcast
|
||||
// messages. In 1.4a, once broadcast mode is set, it remains set
|
||||
// until the interface is disabled.
|
||||
//
|
||||
// Set MAC Address (0c)
|
||||
// --------------------
|
||||
// Command: 0c 00 00 00 FF 40 (FF = 08 or 04)
|
||||
// Function: Set MAC address
|
||||
// Type: Output; overrides built-in MAC address with user-specified
|
||||
// 6-byte value
|
||||
// Notes: This command is intended primarily for debugging/test purposes.
|
||||
// Disabling the interface resets the MAC address to the built-in
|
||||
// value.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIDaynaPort::SetInterfaceMode(SASIDEV *controller)
|
||||
{
|
||||
// Check whether this command is telling us to "Set Interface Mode" or "Set MAC Address"
|
||||
|
||||
ctrl->length = RetrieveStats(ctrl->cmd, ctrl->buffer);
|
||||
switch(ctrl->cmd[5]){
|
||||
case SCSIDaynaPort::CMD_SCSILINK_SETMODE:
|
||||
SetMode(ctrl->cmd, ctrl->buffer);
|
||||
controller->Status();
|
||||
break;
|
||||
break;
|
||||
|
||||
case SCSIDaynaPort::CMD_SCSILINK_SETMAC:
|
||||
ctrl->length = 6;
|
||||
controller->DataOut();
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGWARN("%s Unknown SetInterface command received: %02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->cmd[5]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SCSIDaynaPort::SetMcastAddr(SASIDEV *controller)
|
||||
{
|
||||
ctrl->length = (DWORD)ctrl->cmd[4];
|
||||
if (ctrl->length == 0) {
|
||||
LOGWARN("%s Not supported SetMcastAddr Command %02X", __PRETTY_FUNCTION__, (WORD)ctrl->cmd[2]);
|
||||
|
||||
// Failure (Error)
|
||||
controller->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
controller->DataOut();
|
||||
}
|
||||
|
||||
void SCSIDaynaPort::EnableInterface(SASIDEV *controller)
|
||||
{
|
||||
bool status = EnableInterface(ctrl->cmd);
|
||||
if (!status) {
|
||||
// Failure (Error)
|
||||
controller->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
controller->Status();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -502,9 +652,8 @@ void SCSIDaynaPort::SetMode(const DWORD *cdb, BYTE *buffer)
|
||||
{
|
||||
LOGTRACE("%s Setting mode", __PRETTY_FUNCTION__);
|
||||
|
||||
for(size_t i=0; i<sizeof(6); i++)
|
||||
{
|
||||
LOGTRACE("%s %d: %02X",__PRETTY_FUNCTION__, (unsigned int)i,(WORD)cdb[i]);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
LOGTRACE("%s %d: %02X",__PRETTY_FUNCTION__, (unsigned int)i, (int)cdb[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,10 +28,12 @@
|
||||
//---------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "xm6.h"
|
||||
#include "os.h"
|
||||
#include "disk.h"
|
||||
#include "ctapdriver.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "../rascsi.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
@ -40,36 +42,51 @@
|
||||
//===========================================================================
|
||||
class SCSIDaynaPort: public Disk
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIDaynaPort();
|
||||
// Constructor
|
||||
~SCSIDaynaPort();
|
||||
// Destructor
|
||||
void Open(const Filepath& path, BOOL attn = TRUE);
|
||||
// Capture packets
|
||||
|
||||
// commands
|
||||
private:
|
||||
typedef struct _command_t {
|
||||
const char* name;
|
||||
void (SCSIDaynaPort::*execute)(SASIDEV *);
|
||||
|
||||
_command_t(const char* _name, void (SCSIDaynaPort::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { };
|
||||
} command_t;
|
||||
std::map<SCSIDEV::scsi_command, command_t*> commands;
|
||||
|
||||
SASIDEV::ctrl_t *ctrl;
|
||||
|
||||
void AddCommand(SCSIDEV::scsi_command, const char*, void (SCSIDaynaPort::*)(SASIDEV *));
|
||||
|
||||
public:
|
||||
SCSIDaynaPort();
|
||||
~SCSIDaynaPort();
|
||||
|
||||
bool Init(const list<string>&) override;
|
||||
void Open(const Filepath& path) override;
|
||||
|
||||
// Commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buffer) override;
|
||||
// INQUIRY command
|
||||
bool TestUnitReady(const DWORD *cdb) override;
|
||||
// TEST UNIT READY command
|
||||
int Read(const DWORD *cdb, BYTE *buf, DWORD block) override;
|
||||
// READ command
|
||||
int Read(const DWORD *cdb, BYTE *buf, uint64_t block) override;
|
||||
bool Write(const DWORD *cdb, const BYTE *buf, DWORD block) override;
|
||||
// WRITE command
|
||||
int WriteCheck(DWORD block);
|
||||
// WRITE check
|
||||
int WriteCheck(DWORD block) override; // WRITE check
|
||||
|
||||
int RetrieveStats(const DWORD *cdb, BYTE *buffer);
|
||||
// Retrieve DaynaPort statistics
|
||||
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
|
||||
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
|
||||
|
||||
void TestUnitReady(SASIDEV *) override;
|
||||
void Read6(SASIDEV *) override;
|
||||
void Write6(SASIDEV *) override;
|
||||
void RetrieveStatistics(SASIDEV *);
|
||||
void SetInterfaceMode(SASIDEV *);
|
||||
void SetMcastAddr(SASIDEV *);
|
||||
void EnableInterface(SASIDEV *);
|
||||
|
||||
bool Dispatch(SCSIDEV *);
|
||||
|
||||
const int DAYNAPORT_BUFFER_SIZE = 0x1000000;
|
||||
|
||||
static const BYTE CMD_SCSILINK_STATS = 0x09;
|
||||
static const BYTE CMD_SCSILINK_ENABLE = 0x0E;
|
||||
static const BYTE CMD_SCSILINK_SET = 0x0C;
|
||||
@ -88,58 +105,34 @@ public:
|
||||
static const DWORD DAYNAPORT_READ_HEADER_SZ = 2 + 4;
|
||||
|
||||
private:
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE reserved;
|
||||
WORD pad;
|
||||
BYTE transfer_length;
|
||||
BYTE control;
|
||||
} scsi_cmd_config_multicast_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE reserved;
|
||||
BYTE pad2;
|
||||
BYTE pad3;
|
||||
BYTE pad4;
|
||||
BYTE control;
|
||||
} scsi_cmd_enable_disable_iface_t;
|
||||
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE misc_cdb_information;
|
||||
BYTE logical_block_address;
|
||||
WORD length;
|
||||
uint16_t length;
|
||||
BYTE format;
|
||||
} scsi_cmd_daynaport_write_t;
|
||||
|
||||
|
||||
enum read_data_flags_t : DWORD {
|
||||
enum read_data_flags_t : uint32_t {
|
||||
e_no_more_data = 0x00000000,
|
||||
e_more_data_available = 0x00000001,
|
||||
e_dropped_packets = 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
WORD length;
|
||||
uint32_t length;
|
||||
read_data_flags_t flags;
|
||||
BYTE pad;
|
||||
BYTE data[ETH_FRAME_LEN + sizeof(DWORD)]; // Frame length + 4 byte CRC
|
||||
BYTE data[ETH_FRAME_LEN + sizeof(uint32_t)]; // Frame length + 4 byte CRC
|
||||
} scsi_resp_read_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE mac_address[6];
|
||||
DWORD frame_alignment_errors;
|
||||
DWORD crc_errors;
|
||||
DWORD frames_lost;
|
||||
uint32_t frame_alignment_errors;
|
||||
uint32_t crc_errors;
|
||||
uint32_t frames_lost;
|
||||
} scsi_resp_link_stats_t;
|
||||
|
||||
static const char* m_vendor_name;
|
||||
static const char* m_device_name;
|
||||
static const char* m_revision;
|
||||
static const char* m_firmware_version;
|
||||
|
||||
scsi_resp_link_stats_t m_scsi_link_stats = {
|
||||
.mac_address = { 0x00, 0x80, 0x19, 0x10, 0x98, 0xE3 },//MAC address of @PotatoFi's DayanPort
|
||||
.frame_alignment_errors = 0,
|
||||
@ -147,36 +140,22 @@ private:
|
||||
.frames_lost = 0,
|
||||
};
|
||||
|
||||
const BYTE m_daynacom_mac_prefix[3] = {0x00,0x80,0x19};
|
||||
|
||||
|
||||
// Basic data
|
||||
// buf[0] ... Processor Device
|
||||
// buf[1] ... Not removable
|
||||
// buf[2] ... SCSI-2 compliant command system
|
||||
// buf[3] ... SCSI-2 compliant Inquiry response
|
||||
// buf[4] ... Inquiry additional data
|
||||
//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
|
||||
0x1F, 0x00, 0x00, 0x00, // 4 bytes
|
||||
// Vendor ID (8 Bytes)
|
||||
'D','a','y','n','a',' ',' ',' ',
|
||||
// Product ID (16 Bytes)
|
||||
'S','C','S','I','/','L','i','n',
|
||||
'k',' ',' ',' ',' ',' ',' ',' ',
|
||||
// Revision Number (4 Bytes)
|
||||
'1','.','4','a',
|
||||
// Firmware Version (8 Bytes)
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
};
|
||||
const BYTE m_daynacom_mac_prefix[3] = { 0x00, 0x80, 0x19 };
|
||||
|
||||
CTapDriver *m_tap;
|
||||
// TAP driver
|
||||
BOOL m_bTapEnable;
|
||||
bool m_bTapEnable;
|
||||
// TAP valid flag
|
||||
BYTE m_mac_addr[6];
|
||||
// MAC Address
|
||||
static const BYTE m_bcast_addr[6];
|
||||
static const BYTE m_apple_talk_addr[6];
|
||||
};
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE lba_msb_bits_4_0;
|
||||
uint16_t logical_block_address;
|
||||
BYTE transfer_length;
|
||||
BYTE control;
|
||||
} scsi_cmd_read_6_t;
|
||||
|
@ -17,58 +17,33 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "scsi_host_bridge.h"
|
||||
#include "xm6.h"
|
||||
|
||||
#include "../rascsi.h"
|
||||
#include "ctapdriver.h"
|
||||
#include "cfilesystem.h"
|
||||
#include <sstream>
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SCSI Host Bridge
|
||||
//
|
||||
//===========================================================================
|
||||
using namespace std;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIBR::SCSIBR() : Disk("SCBR")
|
||||
{
|
||||
SetRemovable(false);
|
||||
|
||||
SetProduct("RASCSI BRIDGE");
|
||||
tap = NULL;
|
||||
m_bTapEnable = false;
|
||||
|
||||
fsoptlen = 0;
|
||||
fsoutlen = 0;
|
||||
fsresult = 0;
|
||||
packet_len = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
// TAP Driver Generation
|
||||
tap = new CTapDriver();
|
||||
m_bTapEnable = tap->Init();
|
||||
|
||||
// Generate MAC Address
|
||||
memset(mac_addr, 0x00, 6);
|
||||
if (m_bTapEnable) {
|
||||
tap->GetMacAddr(mac_addr);
|
||||
mac_addr[5]++;
|
||||
}
|
||||
|
||||
// Packet reception flag OFF
|
||||
packet_enable = FALSE;
|
||||
#endif
|
||||
|
||||
// Create host file system
|
||||
fs = new CFileSys();
|
||||
fs->Reset();
|
||||
|
||||
AddCommand(SCSIDEV::eCmdTestUnitReady, "TestUnitReady", &SCSIBR::TestUnitReady);
|
||||
AddCommand(SCSIDEV::eCmdRead6, "GetMessage10", &SCSIBR::GetMessage10);
|
||||
AddCommand(SCSIDEV::eCmdWrite6, "SendMessage10", &SCSIBR::SendMessage10);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIBR::~SCSIBR()
|
||||
{
|
||||
// TAP driver release
|
||||
@ -82,6 +57,66 @@ SCSIBR::~SCSIBR()
|
||||
fs->Reset();
|
||||
delete fs;
|
||||
}
|
||||
|
||||
for (auto const& command : commands) {
|
||||
delete command.second;
|
||||
}
|
||||
}
|
||||
|
||||
bool SCSIBR::Init(const list<string>& params)
|
||||
{
|
||||
// Use default parameters if no parameters were provided
|
||||
SetParams(params.empty() ? GetDefaultParams() : params);
|
||||
|
||||
#ifdef __linux__
|
||||
// TAP Driver Generation
|
||||
tap = new CTapDriver(GetParams().front());
|
||||
m_bTapEnable = tap->Init();
|
||||
|
||||
// Generate MAC Address
|
||||
memset(mac_addr, 0x00, 6);
|
||||
if (m_bTapEnable) {
|
||||
tap->GetMacAddr(mac_addr);
|
||||
mac_addr[5]++;
|
||||
}
|
||||
|
||||
// Packet reception flag OFF
|
||||
packet_enable = false;
|
||||
#endif
|
||||
|
||||
SetReady(m_bTapEnable);
|
||||
|
||||
// Not terminating on regular Linux PCs is helpful for testing
|
||||
#if defined(__x86_64__) || defined(__X86__)
|
||||
return true;
|
||||
#else
|
||||
return m_bTapEnable;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SCSIBR::AddCommand(SCSIDEV::scsi_command opcode, const char* name, void (SCSIBR::*execute)(SASIDEV *))
|
||||
{
|
||||
commands[opcode] = new command_t(name, execute);
|
||||
}
|
||||
|
||||
bool SCSIBR::Dispatch(SCSIDEV *controller)
|
||||
{
|
||||
ctrl = controller->GetCtrl();
|
||||
|
||||
if (commands.count(static_cast<SCSIDEV::scsi_command>(ctrl->cmd[0]))) {
|
||||
command_t *command = commands[static_cast<SCSIDEV::scsi_command>(ctrl->cmd[0])];
|
||||
|
||||
LOGDEBUG("%s Executing %s ($%02X)", __PRETTY_FUNCTION__, command->name, (unsigned int)ctrl->cmd[0]);
|
||||
|
||||
(this->*command->execute)(controller);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LOGTRACE("%s Calling base class for dispatching $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->cmd[0]);
|
||||
|
||||
// The base class handles the less specific commands
|
||||
return Disk::Dispatch(controller);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -108,12 +143,6 @@ int SCSIBR::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
// buf[4] ... Inquiry additional data
|
||||
memset(buf, 0, 8);
|
||||
buf[0] = 0x09;
|
||||
|
||||
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
|
||||
if (((cdb[1] >> 5) & 0x07) != GetLun()) {
|
||||
buf[0] = 0x7f;
|
||||
}
|
||||
|
||||
buf[2] = 0x02;
|
||||
buf[3] = 0x02;
|
||||
buf[4] = 36 - 5 + 8; // required + 8 byte extension
|
||||
@ -141,7 +170,6 @@ int SCSIBR::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
}
|
||||
|
||||
// Success
|
||||
SetStatusCode(STATUS_NOERROR);
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -150,12 +178,11 @@ int SCSIBR::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
// TEST UNIT READY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool SCSIBR::TestUnitReady(const DWORD* /*cdb*/)
|
||||
void SCSIBR::TestUnitReady(SASIDEV *controller)
|
||||
{
|
||||
// TEST UNIT READY Success
|
||||
SetStatusCode(STATUS_NOERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
controller->Status();}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
@ -164,15 +191,11 @@ bool SCSIBR::TestUnitReady(const DWORD* /*cdb*/)
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf)
|
||||
{
|
||||
int func;
|
||||
int total_len;
|
||||
int i;
|
||||
|
||||
// Type
|
||||
int type = cdb[2];
|
||||
|
||||
// Function number
|
||||
func = cdb[3];
|
||||
int func = cdb[3];
|
||||
|
||||
// Phase
|
||||
int phase = cdb[9];
|
||||
@ -211,8 +234,8 @@ int SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf)
|
||||
case 3: // Simultaneous acquisition of multiple packets (size + buffer simultaneously)
|
||||
// Currently the maximum number of packets is 10
|
||||
// Isn't it too fast if I increase more?
|
||||
total_len = 0;
|
||||
for (i = 0; i < 10; i++) {
|
||||
int total_len = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ReceivePacket();
|
||||
*buf++ = (BYTE)(packet_len >> 8);
|
||||
*buf++ = (BYTE)packet_len;
|
||||
@ -242,7 +265,7 @@ int SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf)
|
||||
}
|
||||
|
||||
// Error
|
||||
ASSERT(FALSE);
|
||||
ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -251,7 +274,7 @@ int SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf)
|
||||
// SEND MESSAGE(10)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSIBR::SendMessage10(const DWORD *cdb, BYTE *buf)
|
||||
bool SCSIBR::SendMessage10(const DWORD *cdb, BYTE *buf)
|
||||
{
|
||||
ASSERT(cdb);
|
||||
ASSERT(buf);
|
||||
@ -276,17 +299,17 @@ BOOL SCSIBR::SendMessage10(const DWORD *cdb, BYTE *buf)
|
||||
case 1: // Ethernet
|
||||
// Do not process if TAP is invalid
|
||||
if (!m_bTapEnable) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (func) {
|
||||
case 0: // MAC address setting
|
||||
SetMacAddr(buf);
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
case 1: // Send packet
|
||||
SendPacket(buf, len);
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -294,18 +317,82 @@ BOOL SCSIBR::SendMessage10(const DWORD *cdb, BYTE *buf)
|
||||
switch (phase) {
|
||||
case 0: // issue command
|
||||
WriteFs(func, buf);
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
case 1: // additional data writing
|
||||
WriteFsOpt(buf, len);
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Error
|
||||
ASSERT(FALSE);
|
||||
return FALSE;
|
||||
ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// GET MESSAGE(10)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIBR::GetMessage10(SASIDEV *controller)
|
||||
{
|
||||
// Reallocate buffer (because it is not transfer for each block)
|
||||
if (ctrl->bufsize < 0x1000000) {
|
||||
free(ctrl->buffer);
|
||||
ctrl->bufsize = 0x1000000;
|
||||
ctrl->buffer = (BYTE *)malloc(ctrl->bufsize);
|
||||
}
|
||||
|
||||
ctrl->length = GetMessage10(ctrl->cmd, ctrl->buffer);
|
||||
if (ctrl->length <= 0) {
|
||||
// Failure (Error)
|
||||
controller->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set next block
|
||||
ctrl->blocks = 1;
|
||||
ctrl->next = 1;
|
||||
|
||||
controller->DataIn();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SEND MESSAGE(10)
|
||||
//
|
||||
// This Send Message command is used by the X68000 host driver
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIBR::SendMessage10(SASIDEV *controller)
|
||||
{
|
||||
// Reallocate buffer (because it is not transfer for each block)
|
||||
if (ctrl->bufsize < 0x1000000) {
|
||||
free(ctrl->buffer);
|
||||
ctrl->bufsize = 0x1000000;
|
||||
ctrl->buffer = (BYTE *)malloc(ctrl->bufsize);
|
||||
}
|
||||
|
||||
// Set transfer amount
|
||||
ctrl->length = ctrl->cmd[6];
|
||||
ctrl->length <<= 8;
|
||||
ctrl->length |= ctrl->cmd[7];
|
||||
ctrl->length <<= 8;
|
||||
ctrl->length |= ctrl->cmd[8];
|
||||
|
||||
if (ctrl->length <= 0) {
|
||||
// Failure (Error)
|
||||
controller->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set next block
|
||||
ctrl->blocks = 1;
|
||||
ctrl->next = 1;
|
||||
|
||||
controller->DataOut();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -368,7 +455,7 @@ void SCSIBR::ReceivePacket()
|
||||
|
||||
// Store in receive buffer
|
||||
if (packet_len > 0) {
|
||||
packet_enable = TRUE;
|
||||
packet_enable = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,7 +479,7 @@ void SCSIBR::GetPacketBuf(BYTE *buf)
|
||||
memcpy(buf, packet_buf, len);
|
||||
|
||||
// Received
|
||||
packet_enable = FALSE;
|
||||
packet_enable = false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -1248,7 +1335,6 @@ void SCSIBR::WriteFs(int func, BYTE *buf)
|
||||
fsoutlen = 0;
|
||||
fsoptlen = 0;
|
||||
|
||||
// コマンド分岐
|
||||
func &= 0x1f;
|
||||
switch (func) {
|
||||
case 0x00: return FS_InitDevice(buf); // $40 - start device
|
||||
|
@ -17,9 +17,10 @@
|
||||
//---------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "xm6.h"
|
||||
#include "os.h"
|
||||
#include "disk.h"
|
||||
#include <string>
|
||||
#include "../rascsi.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
@ -28,18 +29,37 @@
|
||||
//===========================================================================
|
||||
class CTapDriver;
|
||||
class CFileSys;
|
||||
|
||||
class SCSIBR : public Disk
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIBR(); // Constructor
|
||||
~SCSIBR(); // Destructor
|
||||
|
||||
// commands
|
||||
private:
|
||||
typedef struct _command_t {
|
||||
const char* name;
|
||||
void (SCSIBR::*execute)(SASIDEV *);
|
||||
|
||||
_command_t(const char* _name, void (SCSIBR::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { };
|
||||
} command_t;
|
||||
std::map<SCSIDEV::scsi_command, command_t*> commands;
|
||||
|
||||
SASIDEV::ctrl_t *ctrl;
|
||||
|
||||
void AddCommand(SCSIDEV::scsi_command, const char*, void (SCSIBR::*)(SASIDEV *));
|
||||
|
||||
public:
|
||||
SCSIBR();
|
||||
~SCSIBR();
|
||||
|
||||
bool Init(const list<string>&) override;
|
||||
bool Dispatch(SCSIDEV *) override;
|
||||
|
||||
// Commands
|
||||
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
|
||||
bool SendMessage10(const DWORD *cdb, BYTE *buf); // SEND MESSAGE10 command
|
||||
void TestUnitReady(SASIDEV *) override;
|
||||
void GetMessage10(SASIDEV *);
|
||||
void SendMessage10(SASIDEV *);
|
||||
|
||||
private:
|
||||
int GetMacAddr(BYTE *buf); // Get MAC address
|
||||
@ -49,11 +69,11 @@ private:
|
||||
void SendPacket(BYTE *buf, int len); // Send a packet
|
||||
|
||||
CTapDriver *tap; // TAP driver
|
||||
BOOL m_bTapEnable; // TAP valid flag
|
||||
bool m_bTapEnable; // TAP valid flag
|
||||
BYTE mac_addr[6]; // MAC Addres
|
||||
int packet_len; // Receive packet size
|
||||
BYTE packet_buf[0x1000]; // Receive packet buffer
|
||||
BOOL packet_enable; // Received packet valid
|
||||
bool packet_enable; // Received packet valid
|
||||
|
||||
int ReadFsResult(BYTE *buf); // Read filesystem (result code)
|
||||
int ReadFsOut(BYTE *buf); // Read filesystem (return data)
|
||||
|
@ -10,14 +10,15 @@
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ SCSI Hard Disk for Apple Macintosh ]
|
||||
// [ SCSI CD-ROM for Apple Macintosh ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "xm6.h"
|
||||
#include "scsicd.h"
|
||||
#include "fileio.h"
|
||||
#include "exceptions.h"
|
||||
#include <sstream>
|
||||
#include "../rascsi.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
@ -38,14 +39,14 @@ CDTrack::CDTrack(SCSICD *scsicd)
|
||||
cdrom = scsicd;
|
||||
|
||||
// Track defaults to disabled
|
||||
valid = FALSE;
|
||||
valid = false;
|
||||
|
||||
// Initialize other data
|
||||
track_no = -1;
|
||||
first_lba = 0;
|
||||
last_lba = 0;
|
||||
audio = FALSE;
|
||||
raw = FALSE;
|
||||
audio = false;
|
||||
raw = false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -82,7 +83,7 @@ void CDTrack::Init(int track, DWORD first, DWORD last)
|
||||
// Set Path
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CDTrack::SetPath(BOOL cdda, const Filepath& path)
|
||||
void CDTrack::SetPath(bool cdda, const Filepath& path)
|
||||
{
|
||||
ASSERT(valid);
|
||||
|
||||
@ -180,25 +181,25 @@ int CDTrack::GetTrackNo() const
|
||||
// Is valid block
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL CDTrack::IsValid(DWORD lba) const
|
||||
bool CDTrack::IsValid(DWORD lba) const
|
||||
{
|
||||
// FALSE if the track itself is invalid
|
||||
if (!valid) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the block is BEFORE the first block
|
||||
if (lba < first_lba) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the block is AFTER the last block
|
||||
if (last_lba < lba) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// This track is valid
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -206,7 +207,7 @@ BOOL CDTrack::IsValid(DWORD lba) const
|
||||
// Is audio track
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL CDTrack::IsAudio() const
|
||||
bool CDTrack::IsAudio() const
|
||||
{
|
||||
ASSERT(valid);
|
||||
|
||||
@ -224,15 +225,10 @@ BOOL CDTrack::IsAudio() const
|
||||
// Constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
SCSICD::SCSICD() : Disk("SCCD")
|
||||
SCSICD::SCSICD() : Disk("SCCD"), ScsiMmcCommands(), FileSupport()
|
||||
{
|
||||
SetRemovable(true);
|
||||
SetReadOnly(true);
|
||||
|
||||
SetProduct("CD-ROM CDU-55S");
|
||||
|
||||
// NOT in raw format
|
||||
rawfile = FALSE;
|
||||
rawfile = false;
|
||||
|
||||
// Frame initialization
|
||||
frame = 0;
|
||||
@ -244,6 +240,9 @@ SCSICD::SCSICD() : Disk("SCCD")
|
||||
tracks = 0;
|
||||
dataindex = -1;
|
||||
audioindex = -1;
|
||||
|
||||
AddCommand(SCSIDEV::eCmdReadToc, "ReadToc", &SCSICD::ReadToc);
|
||||
AddCommand(SCSIDEV::eCmdGetEventStatusNotification, "GetEventStatusNotification", &SCSICD::GetEventStatusNotification);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -255,6 +254,35 @@ SCSICD::~SCSICD()
|
||||
{
|
||||
// Clear track
|
||||
ClearTrack();
|
||||
|
||||
for (auto const& command : commands) {
|
||||
delete command.second;
|
||||
}
|
||||
}
|
||||
|
||||
void SCSICD::AddCommand(SCSIDEV::scsi_command opcode, const char* name, void (SCSICD::*execute)(SASIDEV *))
|
||||
{
|
||||
commands[opcode] = new command_t(name, execute);
|
||||
}
|
||||
|
||||
bool SCSICD::Dispatch(SCSIDEV *controller)
|
||||
{
|
||||
ctrl = controller->GetCtrl();
|
||||
|
||||
if (commands.count(static_cast<SCSIDEV::scsi_command>(ctrl->cmd[0]))) {
|
||||
command_t *command = commands[static_cast<SCSIDEV::scsi_command>(ctrl->cmd[0])];
|
||||
|
||||
LOGDEBUG("%s Executing %s ($%02X)", __PRETTY_FUNCTION__, command->name, (unsigned int)ctrl->cmd[0]);
|
||||
|
||||
(this->*command->execute)(controller);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LOGTRACE("%s Calling base class for dispatching $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl->cmd[0]);
|
||||
|
||||
// The base class handles the less specific commands
|
||||
return Disk::Dispatch(controller);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -264,20 +292,19 @@ SCSICD::~SCSICD()
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSICD::Open(const Filepath& path)
|
||||
{
|
||||
Fileio fio;
|
||||
off_t size;
|
||||
TCHAR file[5];
|
||||
|
||||
ASSERT(!IsReady());
|
||||
|
||||
// Initialization, track clear
|
||||
SetBlockCount(0);
|
||||
rawfile = FALSE;
|
||||
rawfile = false;
|
||||
ClearTrack();
|
||||
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw io_exception("Can't open CD-ROM file read-only");
|
||||
throw file_not_found_exception("Can't open CD-ROM file");
|
||||
}
|
||||
|
||||
// Close and transfer for physical CD access
|
||||
@ -296,12 +323,13 @@ void SCSICD::Open(const Filepath& path)
|
||||
}
|
||||
|
||||
// Judge whether it is a CUE sheet or an ISO file
|
||||
TCHAR file[5];
|
||||
fio.Read(file, 4);
|
||||
file[4] = '\0';
|
||||
fio.Close();
|
||||
|
||||
// If it starts with FILE, consider it as a CUE sheet
|
||||
if (strncasecmp(file, _T("FILE"), 4) == 0) {
|
||||
if (!strncasecmp(file, _T("FILE"), 4)) {
|
||||
// Open as CUE
|
||||
OpenCue(path);
|
||||
} else {
|
||||
@ -313,8 +341,8 @@ void SCSICD::Open(const Filepath& path)
|
||||
// Successful opening
|
||||
ASSERT(GetBlockCount() > 0);
|
||||
|
||||
// Sector size 2048 bytes
|
||||
SetSectorSize(11);
|
||||
// Default sector size is 2048 bytes
|
||||
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 2048, false);
|
||||
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
@ -346,13 +374,10 @@ void SCSICD::OpenCue(const Filepath& /*path*/)
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSICD::OpenIso(const Filepath& path)
|
||||
{
|
||||
BYTE header[12];
|
||||
BYTE sync[12];
|
||||
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw io_exception("Can't open ISO CD-ROM file read-only");
|
||||
throw io_exception("Can't open ISO CD-ROM file");
|
||||
}
|
||||
|
||||
// Get file size
|
||||
@ -363,16 +388,18 @@ void SCSICD::OpenIso(const Filepath& path)
|
||||
}
|
||||
|
||||
// Read the first 12 bytes and close
|
||||
BYTE header[12];
|
||||
if (!fio.Read(header, sizeof(header))) {
|
||||
fio.Close();
|
||||
throw io_exception("Can't read header of ISO CD-ROM file");
|
||||
}
|
||||
|
||||
// Check if it is RAW format
|
||||
BYTE sync[12];
|
||||
memset(sync, 0xff, sizeof(sync));
|
||||
sync[0] = 0x00;
|
||||
sync[11] = 0x00;
|
||||
rawfile = FALSE;
|
||||
rawfile = false;
|
||||
if (memcmp(header, sync, sizeof(sync)) == 0) {
|
||||
// 00,FFx10,00, so it is presumed to be RAW format
|
||||
if (!fio.Read(header, 4)) {
|
||||
@ -388,14 +415,16 @@ void SCSICD::OpenIso(const Filepath& path)
|
||||
}
|
||||
|
||||
// Set to RAW file
|
||||
rawfile = TRUE;
|
||||
rawfile = true;
|
||||
}
|
||||
fio.Close();
|
||||
|
||||
if (rawfile) {
|
||||
// Size must be a multiple of 2536 and less than 700MB
|
||||
if (size % 0x930) {
|
||||
throw io_exception("Raw ISO CD-ROM file size must be a multiple of 2536 bytes");
|
||||
if (size % 2536) {
|
||||
stringstream error;
|
||||
error << "Raw ISO CD-ROM file size must be a multiple of 2536 bytes but is " << size << " bytes";
|
||||
throw io_exception(error.str());
|
||||
}
|
||||
if (size > 912579600) {
|
||||
throw io_exception("Raw ISO CD-ROM file size must not exceed 700 MB");
|
||||
@ -405,7 +434,7 @@ void SCSICD::OpenIso(const Filepath& path)
|
||||
SetBlockCount((DWORD)(size / 0x930));
|
||||
} else {
|
||||
// Size must be a multiple of 2048 and less than 700MB
|
||||
if (size & 0x7ff) {
|
||||
if (size % 2048) {
|
||||
throw io_exception("ISO CD-ROM file size must be a multiple of 2048 bytes");
|
||||
}
|
||||
if (size > 0x2bed5000) {
|
||||
@ -416,13 +445,11 @@ void SCSICD::OpenIso(const Filepath& path)
|
||||
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, GetBlockCount() - 1);
|
||||
track[0]->SetPath(FALSE, path);
|
||||
track[0]->SetPath(false, path);
|
||||
tracks = 1;
|
||||
dataindex = 0;
|
||||
}
|
||||
@ -437,7 +464,7 @@ void SCSICD::OpenPhysical(const Filepath& path)
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw io_exception("Can't open CD-ROM file read-only");
|
||||
throw io_exception("Can't open CD-ROM file");
|
||||
}
|
||||
|
||||
// Get size
|
||||
@ -465,11 +492,23 @@ void SCSICD::OpenPhysical(const Filepath& path)
|
||||
ASSERT(!track[0]);
|
||||
track[0] = new CDTrack(this);
|
||||
track[0]->Init(1, 0, GetBlockCount() - 1);
|
||||
track[0]->SetPath(FALSE, path);
|
||||
track[0]->SetPath(false, path);
|
||||
tracks = 1;
|
||||
dataindex = 0;
|
||||
}
|
||||
|
||||
void SCSICD::ReadToc(SASIDEV *controller)
|
||||
{
|
||||
ctrl->length = ReadToc(ctrl->cmd, ctrl->buffer);
|
||||
if (ctrl->length <= 0) {
|
||||
// Failure (Error)
|
||||
controller->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
controller->DataIn();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// INQUIRY
|
||||
@ -494,12 +533,6 @@ int SCSICD::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
// buf[4] ... Inquiry additional data
|
||||
memset(buf, 0, 8);
|
||||
buf[0] = 0x05;
|
||||
|
||||
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
|
||||
if (((cdb[1] >> 5) & 0x07) != GetLun()) {
|
||||
buf[0] = 0x7f;
|
||||
}
|
||||
|
||||
buf[1] = 0x80;
|
||||
buf[2] = 0x02;
|
||||
buf[3] = 0x02;
|
||||
@ -550,7 +583,7 @@ int SCSICD::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
// READ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSICD::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||||
int SCSICD::Read(const DWORD *cdb, BYTE *buf, uint64_t block)
|
||||
{
|
||||
ASSERT(buf);
|
||||
|
||||
@ -709,6 +742,18 @@ int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf)
|
||||
return length;
|
||||
}
|
||||
|
||||
void SCSICD::GetEventStatusNotification(SASIDEV *controller)
|
||||
{
|
||||
if (!ctrl->cmd[1] & 0x01) {
|
||||
// Asynchronous notification is optional and not supported by rascsi
|
||||
controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_FIELD_IN_CDB);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGTRACE("Received request for event polling, which is currently not supported");
|
||||
controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// LBA→MSF Conversion
|
||||
@ -739,29 +784,6 @@ void SCSICD::LBAtoMSF(DWORD lba, BYTE *msf) const
|
||||
msf[3] = (BYTE)f;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// MSF→LBA Conversion
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DWORD SCSICD::MSFtoLBA(const BYTE *msf) const
|
||||
{
|
||||
ASSERT(msf[2] < 60);
|
||||
ASSERT(msf[3] < 75);
|
||||
|
||||
// 1, 75, add up in multiples of 75*60
|
||||
DWORD lba = msf[1];
|
||||
lba *= 60;
|
||||
lba += msf[2];
|
||||
lba *= 75;
|
||||
lba += msf[3];
|
||||
|
||||
// Since the base point is M=0, S=2, F=0, subtract 150
|
||||
lba -= 150;
|
||||
|
||||
return lba;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Clear Track
|
||||
@ -806,18 +828,3 @@ int SCSICD::SearchTrack(DWORD lba) const
|
||||
return -1;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Next Frame
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool SCSICD::NextFrame()
|
||||
{
|
||||
ASSERT((frame >= 0) && (frame < 75));
|
||||
|
||||
// set the frame in the range 0-74
|
||||
frame = (frame + 1) % 75;
|
||||
|
||||
// FALSE after one lap
|
||||
return frame != 0;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ SCSI Hard Disk for Apple Macintosh ]
|
||||
// [ SCSI CD-ROM for Apple Macintosh ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#pragma once
|
||||
@ -18,6 +18,8 @@
|
||||
#include "os.h"
|
||||
#include "disk.h"
|
||||
#include "filepath.h"
|
||||
#include "interfaces/scsi_mmc_commands.h"
|
||||
#include "interfaces/scsi_primary_commands.h"
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -35,30 +37,30 @@ class SCSICD;
|
||||
class CDTrack
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
CDTrack(SCSICD *scsicd); // Constructor
|
||||
virtual ~CDTrack(); // Destructor
|
||||
void Init(int track, DWORD first, DWORD last); // Initialization
|
||||
CDTrack(SCSICD *scsicd);
|
||||
virtual ~CDTrack();
|
||||
|
||||
void Init(int track, DWORD first, DWORD last);
|
||||
|
||||
// Properties
|
||||
void SetPath(BOOL cdda, const Filepath& path); // Set the path
|
||||
void SetPath(bool cdda, const Filepath& path); // Set the path
|
||||
void GetPath(Filepath& path) const; // Get the path
|
||||
void AddIndex(int index, DWORD lba); // Add index
|
||||
DWORD GetFirst() const; // Get the start LBA
|
||||
DWORD GetLast() const; // Get the last LBA
|
||||
DWORD GetBlocks() const; // Get the number of blocks
|
||||
int GetTrackNo() const; // Get the track number
|
||||
BOOL IsValid(DWORD lba) const; // Is this a valid LBA?
|
||||
BOOL IsAudio() const; // Is this an audio track?
|
||||
bool IsValid(DWORD lba) const; // Is this a valid LBA?
|
||||
bool IsAudio() const; // Is this an audio track?
|
||||
|
||||
private:
|
||||
SCSICD *cdrom; // Parent device
|
||||
BOOL valid; // Valid track
|
||||
bool valid; // Valid track
|
||||
int track_no; // Track number
|
||||
DWORD first_lba; // First LBA
|
||||
DWORD last_lba; // Last LBA
|
||||
BOOL audio; // Audio track flag
|
||||
BOOL raw; // RAW data flag
|
||||
bool audio; // Audio track flag
|
||||
bool raw; // RAW data flag
|
||||
Filepath imgpath; // Image file path
|
||||
};
|
||||
|
||||
@ -67,41 +69,51 @@ private:
|
||||
// SCSI CD-ROM
|
||||
//
|
||||
//===========================================================================
|
||||
class SCSICD : public Disk, public FileSupport
|
||||
class SCSICD : public Disk, public ScsiMmcCommands, public FileSupport
|
||||
{
|
||||
private:
|
||||
typedef struct _command_t {
|
||||
const char* name;
|
||||
void (SCSICD::*execute)(SASIDEV *);
|
||||
|
||||
_command_t(const char* _name, void (SCSICD::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { };
|
||||
} command_t;
|
||||
std::map<SCSIDEV::scsi_command, command_t*> commands;
|
||||
|
||||
SASIDEV::ctrl_t *ctrl;
|
||||
|
||||
void AddCommand(SCSIDEV::scsi_command, const char*, void (SCSICD::*)(SASIDEV *));
|
||||
|
||||
public:
|
||||
// Number of tracks
|
||||
enum {
|
||||
TrackMax = 96 // Maximum number of tracks
|
||||
};
|
||||
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSICD(); // Constructor
|
||||
~SCSICD(); // Destructor
|
||||
void Open(const Filepath& path); // Open
|
||||
SCSICD();
|
||||
~SCSICD();
|
||||
|
||||
// commands
|
||||
bool Dispatch(SCSIDEV *) override;
|
||||
|
||||
void Open(const Filepath& path) override;
|
||||
|
||||
// Commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf) override; // INQUIRY command
|
||||
int Read(const DWORD *cdb, BYTE *buf, DWORD block) override; // READ command
|
||||
int Read(const DWORD *cdb, BYTE *buf, uint64_t block) override; // READ command
|
||||
int ReadToc(const DWORD *cdb, BYTE *buf); // READ TOC command
|
||||
|
||||
// CD-DA
|
||||
// TODO Never called
|
||||
bool NextFrame(); // Frame notification
|
||||
// TODO Never called
|
||||
void GetBuf(DWORD *buffer, int samples, DWORD rate); // Get CD-DA buffer
|
||||
|
||||
// LBA-MSF変換
|
||||
void LBAtoMSF(DWORD lba, BYTE *msf) const; // LBA→MSF conversion
|
||||
DWORD MSFtoLBA(const BYTE *msf) const; // MSF→LBA conversion
|
||||
|
||||
private:
|
||||
// Open
|
||||
void OpenCue(const Filepath& path); // Open(CUE)
|
||||
void OpenIso(const Filepath& path); // Open(ISO)
|
||||
void OpenPhysical(const Filepath& path); // Open(Physical)
|
||||
BOOL rawfile; // RAW flag
|
||||
|
||||
void ReadToc(SASIDEV *);
|
||||
void GetEventStatusNotification(SASIDEV *);
|
||||
|
||||
void LBAtoMSF(DWORD lba, BYTE *msf) const; // LBA→MSF conversion
|
||||
|
||||
bool rawfile; // RAW flag
|
||||
|
||||
// Track management
|
||||
void ClearTrack(); // Clear the track
|
||||
|
@ -14,10 +14,12 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#include "scsihd.h"
|
||||
#include "xm6.h"
|
||||
#include "fileio.h"
|
||||
#include "exceptions.h"
|
||||
#include <sstream>
|
||||
#include "../rascsi.h"
|
||||
|
||||
#define DEFAULT_PRODUCT "SCSI HD"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
@ -32,8 +34,38 @@
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIHD::SCSIHD(bool removable) : Disk(removable ? "SCRM" : "SCHD")
|
||||
{
|
||||
SetRemovable(removable);
|
||||
}
|
||||
|
||||
void SCSIHD::FinalizeSetup(const Filepath &path, off_t size)
|
||||
{
|
||||
// 2TB is the current maximum
|
||||
if (size > 2LL * 1024 * 1024 * 1024 * 1024) {
|
||||
throw io_exception("File size must not exceed 2 TB");
|
||||
}
|
||||
|
||||
// For non-removable media drives set the default product name based on the drive capacity
|
||||
if (!IsRemovable()) {
|
||||
int capacity;
|
||||
string unit;
|
||||
if (GetBlockCount() >> 11 >= 1) {
|
||||
capacity = GetBlockCount() >> 11;
|
||||
unit = "MB";
|
||||
}
|
||||
else {
|
||||
capacity = GetBlockCount() >> 1;
|
||||
unit = "KB";
|
||||
}
|
||||
stringstream product;
|
||||
product << DEFAULT_PRODUCT << " " << capacity << " " << unit;
|
||||
SetProduct(product.str(), false);
|
||||
}
|
||||
|
||||
SetReadOnly(false);
|
||||
SetProtectable(true);
|
||||
SetProtected(false);
|
||||
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -64,36 +96,25 @@ void SCSIHD::Open(const Filepath& path)
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw io_exception("Can't open hard disk file read-only");
|
||||
throw file_not_found_exception("Can't open SCSI hard disk file");
|
||||
}
|
||||
|
||||
// Get file size
|
||||
off_t size = fio.GetFileSize();
|
||||
fio.Close();
|
||||
|
||||
// Must be a multiple of 512 bytes
|
||||
if (size & 0x1ff) {
|
||||
throw io_exception("File size must be a multiple of 512 bytes");
|
||||
// Sector size (default 512 bytes) and number of blocks
|
||||
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512, false);
|
||||
SetBlockCount((DWORD)(size >> GetSectorSize()));
|
||||
|
||||
// File size must be a multiple of the sector size
|
||||
if (size % GetSectorSizeInBytes()) {
|
||||
stringstream error;
|
||||
error << "File size must be a multiple of " << GetSectorSizeInBytes() << " bytes but is " << size << " bytes";
|
||||
throw io_exception(error.str());
|
||||
}
|
||||
|
||||
// 2TB is the current maximum
|
||||
if (size > 2LL * 1024 * 1024 * 1024 * 1024) {
|
||||
throw io_exception("File size must not exceed 2 TB");
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
FinalizeSetup(path, size);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -125,12 +146,6 @@ int SCSIHD::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
// 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) != GetLun()) {
|
||||
buf[0] = 0x7f;
|
||||
}
|
||||
|
||||
buf[1] = IsRemovable() ? 0x80 : 0x00;
|
||||
buf[2] = 0x02;
|
||||
buf[3] = 0x02;
|
||||
@ -153,12 +168,10 @@ int SCSIHD::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// MODE SELECT
|
||||
// *Not affected by disk.code
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
{
|
||||
BYTE page;
|
||||
int size;
|
||||
|
||||
ASSERT(buf);
|
||||
@ -184,7 +197,7 @@ bool SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
// Parsing the page
|
||||
while (length > 0) {
|
||||
// Get page
|
||||
page = buf[0];
|
||||
BYTE page = buf[0];
|
||||
|
||||
switch (page) {
|
||||
// format device
|
||||
@ -200,6 +213,7 @@ bool SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
break;
|
||||
|
||||
// CD-ROM Parameters
|
||||
// TODO Move to scsicd.cpp
|
||||
// According to the SONY CDU-541 manual, Page code 8 is supposed
|
||||
// to set the Logical Block Adress Format, as well as the
|
||||
// inactivity timer multiplier
|
||||
@ -235,7 +249,7 @@ bool SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
// Add Vendor special page to make drive Apple compatible
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD::AddVendor(int page, bool change, BYTE *buf)
|
||||
int SCSIHD::AddVendorPage(int page, bool change, BYTE *buf)
|
||||
{
|
||||
ASSERT(buf);
|
||||
|
||||
|
@ -19,25 +19,21 @@
|
||||
#include "disk.h"
|
||||
#include "filepath.h"
|
||||
|
||||
#define DEFAULT_PRODUCT "SCSI HD"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SCSI Hard Disk
|
||||
//
|
||||
//===========================================================================
|
||||
class SCSIHD : public Disk, public FileSupport
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIHD(bool = false); // Constructor
|
||||
void Reset(); // Reset
|
||||
void Open(const Filepath& path); // Open
|
||||
SCSIHD(bool);
|
||||
virtual ~SCSIHD() {};
|
||||
|
||||
// commands
|
||||
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
|
||||
void FinalizeSetup(const Filepath&, off_t);
|
||||
|
||||
// Internal processing
|
||||
int AddVendor(int page, bool change, BYTE *buf) override; // Add vendor special page
|
||||
void Reset();
|
||||
virtual void Open(const Filepath&);
|
||||
|
||||
// Commands
|
||||
virtual int Inquiry(const DWORD *cdb, BYTE *buf) override;
|
||||
bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override;
|
||||
|
||||
// Add vendor special page
|
||||
int AddVendorPage(int page, bool change, BYTE *buf) override;
|
||||
};
|
||||
|
@ -18,28 +18,12 @@
|
||||
#include "fileio.h"
|
||||
#include "exceptions.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SCSI hard disk (PC-9801-55 NEC genuine /Anex86/T98Next)
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIHD_NEC::SCSIHD_NEC() : SCSIHD()
|
||||
SCSIHD_NEC::SCSIHD_NEC() : SCSIHD(false)
|
||||
{
|
||||
SetVendor("NEC");
|
||||
|
||||
// Work initialization
|
||||
cylinders = 0;
|
||||
heads = 0;
|
||||
sectors = 0;
|
||||
sectorsize = 0;
|
||||
imgoffset = 0;
|
||||
imgsize = 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -59,34 +43,28 @@ static inline WORD getWordLE(const BYTE *b)
|
||||
//---------------------------------------------------------------------------
|
||||
static inline DWORD getDwordLE(const BYTE *b)
|
||||
{
|
||||
return ((DWORD)(b[3]) << 24) | ((DWORD)(b[2]) << 16) |
|
||||
((DWORD)(b[1]) << 8) | b[0];
|
||||
return ((DWORD)(b[3]) << 24) | ((DWORD)(b[2]) << 16) | ((DWORD)(b[1]) << 8) | b[0];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Open
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
|
||||
void SCSIHD_NEC::Open(const Filepath& path)
|
||||
{
|
||||
ASSERT(!IsReady());
|
||||
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw io_exception("Can't open hard disk file read-only");
|
||||
throw file_not_found_exception("Can't open hard disk file");
|
||||
}
|
||||
|
||||
// Get file size
|
||||
off_t size = fio.GetFileSize();
|
||||
|
||||
// Read header
|
||||
BYTE hdr[512];
|
||||
if (size >= (off_t)sizeof(hdr)) {
|
||||
if (!fio.Read(hdr, sizeof(hdr))) {
|
||||
// NEC root sector
|
||||
BYTE root_sector[512];
|
||||
if (size >= (off_t)sizeof(root_sector)) {
|
||||
if (!fio.Read(root_sector, sizeof(root_sector))) {
|
||||
fio.Close();
|
||||
throw io_exception("Can't read NEC hard disk file header");
|
||||
throw io_exception("Can't read NEC hard disk file root sector");
|
||||
}
|
||||
}
|
||||
fio.Close();
|
||||
@ -96,75 +74,69 @@ void SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
|
||||
throw io_exception("File size must be a multiple of 512 bytes");
|
||||
}
|
||||
|
||||
// 2TB is the current maximum
|
||||
if (size > 2LL * 1024 * 1024 * 1024 * 1024) {
|
||||
throw io_exception("File size must not exceed 2 TB");
|
||||
}
|
||||
int image_size = 0;
|
||||
int sector_size = 0;
|
||||
|
||||
// Determine parameters by extension
|
||||
LPCTSTR ext = path.GetFileExt();
|
||||
if (strcasecmp(ext, _T(".HDN")) == 0) {
|
||||
const char *ext = path.GetFileExt();
|
||||
|
||||
// PC-9801-55 NEC genuine?
|
||||
if (!strcasecmp(ext, ".hdn")) {
|
||||
// Assuming sector size 512, number of sectors 25, number of heads 8 as default settings
|
||||
imgoffset = 0;
|
||||
imgsize = size;
|
||||
sectorsize = 512;
|
||||
disk.image_offset = 0;
|
||||
image_size = size;
|
||||
sector_size = 512;
|
||||
sectors = 25;
|
||||
heads = 8;
|
||||
cylinders = (int)(size >> 9);
|
||||
cylinders >>= 3;
|
||||
cylinders /= 25;
|
||||
} else if (strcasecmp(ext, _T(".HDI")) == 0) { // Anex86 HD image?
|
||||
imgoffset = getDwordLE(&hdr[4 + 4]);
|
||||
imgsize = getDwordLE(&hdr[4 + 4 + 4]);
|
||||
sectorsize = getDwordLE(&hdr[4 + 4 + 4 + 4]);
|
||||
sectors = getDwordLE(&hdr[4 + 4 + 4 + 4 + 4]);
|
||||
heads = getDwordLE(&hdr[4 + 4 + 4 + 4 + 4 + 4]);
|
||||
cylinders = getDwordLE(&hdr[4 + 4 + 4 + 4 + 4 + 4 + 4]);
|
||||
} else if (strcasecmp(ext, _T(".NHD")) == 0 &&
|
||||
memcmp(hdr, "T98HDDIMAGE.R0\0", 15) == 0) { // T98Next HD image?
|
||||
imgoffset = getDwordLE(&hdr[0x10 + 0x100]);
|
||||
cylinders = getDwordLE(&hdr[0x10 + 0x100 + 4]);
|
||||
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 = (off_t)cylinders * heads * sectors * sectorsize;
|
||||
}
|
||||
|
||||
// Supports 256 or 512 sector sizes
|
||||
if (sectorsize != 256 && sectorsize != 512) {
|
||||
throw io_exception("Sector size must be 256 or 512 bytes");
|
||||
// Anex86 HD image?
|
||||
else if (!strcasecmp(ext, ".hdi")) {
|
||||
disk.image_offset = getDwordLE(&root_sector[8]);
|
||||
image_size = getDwordLE(&root_sector[12]);
|
||||
sector_size = getDwordLE(&root_sector[16]);
|
||||
sectors = getDwordLE(&root_sector[20]);
|
||||
heads = getDwordLE(&root_sector[24]);
|
||||
cylinders = getDwordLE(&root_sector[28]);
|
||||
}
|
||||
// T98Next HD image?
|
||||
else if (!strcasecmp(ext, ".nhd")) {
|
||||
if (!memcmp(root_sector, "T98HDDIMAGE.R0\0", 15)) {
|
||||
disk.image_offset = getDwordLE(&root_sector[0x110]);
|
||||
cylinders = getDwordLE(&root_sector[0x114]);
|
||||
heads = getWordLE(&root_sector[0x118]);
|
||||
sectors = getWordLE(&root_sector[0x11a]);
|
||||
sector_size = getWordLE(&root_sector[0x11c]);
|
||||
image_size = (off_t)cylinders * heads * sectors * sector_size;
|
||||
}
|
||||
else {
|
||||
throw io_exception("Invalid NEC image file format");
|
||||
}
|
||||
}
|
||||
|
||||
// Image size consistency check
|
||||
if (imgoffset + imgsize > size || (imgsize % sectorsize != 0)) {
|
||||
if (disk.image_offset + image_size > size || (image_size % sector_size != 0)) {
|
||||
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)
|
||||
// Calculate sector size
|
||||
for (size = 16; size > 0; --size) {
|
||||
if ((1 << size) == sector_size)
|
||||
break;
|
||||
}
|
||||
if (disk.size <= 0 || disk.size > 16) {
|
||||
throw io_exception("Invalid disk size");
|
||||
if (size <= 0 || size > 16) {
|
||||
throw io_exception("Invalid NEC disk size");
|
||||
}
|
||||
SetSize(size);
|
||||
|
||||
// Number of blocks
|
||||
SetBlockCount((DWORD)(imgsize >> disk.size));
|
||||
disk.imgoffset = imgoffset;
|
||||
SetBlockCount(image_size >> disk.size);
|
||||
|
||||
LOGINFO("Media capacity for image file '%s': %d blocks", path.GetPath(), GetBlockCount());
|
||||
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
FinalizeSetup(path, size);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// INQUIRY
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD_NEC::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
{
|
||||
int size = SCSIHD::Inquiry(cdb, buf);
|
||||
@ -176,12 +148,7 @@ int SCSIHD_NEC::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
return size;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Error page added
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD_NEC::AddError(bool change, BYTE *buf)
|
||||
int SCSIHD_NEC::AddErrorPage(bool change, BYTE *buf)
|
||||
{
|
||||
ASSERT(buf);
|
||||
|
||||
@ -193,12 +160,7 @@ int SCSIHD_NEC::AddError(bool change, BYTE *buf)
|
||||
return 8;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Format page added
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD_NEC::AddFormat(bool change, BYTE *buf)
|
||||
int SCSIHD_NEC::AddFormatPage(bool change, BYTE *buf)
|
||||
{
|
||||
ASSERT(buf);
|
||||
|
||||
@ -236,12 +198,7 @@ int SCSIHD_NEC::AddFormat(bool change, BYTE *buf)
|
||||
return 24;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Drive page added
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSIHD_NEC::AddDrive(bool change, BYTE *buf)
|
||||
int SCSIHD_NEC::AddDrivePage(bool change, BYTE *buf)
|
||||
{
|
||||
ASSERT(buf);
|
||||
|
||||
|
@ -19,29 +19,27 @@
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SCSI hard disk (PC-9801-55 NEC genuine /Anex86/T98Next)
|
||||
// SCSI hard disk (PC-9801-55 NEC genuine / Anex86 / T98Next)
|
||||
//
|
||||
//===========================================================================
|
||||
class SCSIHD_NEC : public SCSIHD
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIHD_NEC(); // Constructor
|
||||
void Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
SCSIHD_NEC();
|
||||
~SCSIHD_NEC() {};
|
||||
|
||||
// commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf) override; // INQUIRY command
|
||||
void Open(const Filepath& path) override;
|
||||
|
||||
// Internal processing
|
||||
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
|
||||
// Commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf) override;
|
||||
|
||||
int AddErrorPage(bool change, BYTE *buf) override;
|
||||
int AddFormatPage(bool change, BYTE *buf) override;
|
||||
int AddDrivePage(bool change, BYTE *buf) override;
|
||||
|
||||
private:
|
||||
int cylinders; // Number of cylinders
|
||||
int heads; // Number of heads
|
||||
int sectors; // Number of sectors
|
||||
int sectorsize; // Sector size
|
||||
off_t imgoffset; // Image offset
|
||||
off_t imgsize; // Image size
|
||||
// Geometry data
|
||||
int cylinders;
|
||||
int heads;
|
||||
int sectors;
|
||||
};
|
||||
|
@ -15,7 +15,8 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "scsimo.h"
|
||||
#include "xm6.h"
|
||||
|
||||
#include "../rascsi.h"
|
||||
#include "fileio.h"
|
||||
#include "exceptions.h"
|
||||
|
||||
@ -32,10 +33,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIMO::SCSIMO() : Disk("SCMO")
|
||||
{
|
||||
SetRemovable(true);
|
||||
SetProtectable(true);
|
||||
|
||||
SetProduct("M2513A");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -49,48 +46,20 @@ void SCSIMO::Open(const Filepath& path)
|
||||
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
throw io_exception("Can't open MO file read-only");
|
||||
throw file_not_found_exception("Can't open MO file");
|
||||
}
|
||||
|
||||
// Get file size
|
||||
off_t size = fio.GetFileSize();
|
||||
fio.Close();
|
||||
|
||||
switch (size) {
|
||||
// 128MB
|
||||
case 0x797f400:
|
||||
// 512 bytes per sector
|
||||
SetSectorSize(9);
|
||||
SetBlockCount(248826);
|
||||
break;
|
||||
SetGeometryForCapacity(size);
|
||||
|
||||
// 230MB
|
||||
case 0xd9eea00:
|
||||
// 512 bytes per sector
|
||||
SetSectorSize(9);
|
||||
SetBlockCount(446325);
|
||||
break;
|
||||
|
||||
// 540MB
|
||||
case 0x1fc8b800:
|
||||
// 512 bytes per sector
|
||||
SetSectorSize(9);
|
||||
SetBlockCount(1041500);
|
||||
break;
|
||||
|
||||
// 640MB
|
||||
case 0x25e28000:
|
||||
// 2048 bytes per sector
|
||||
SetSectorSize(11);
|
||||
SetBlockCount(310352);
|
||||
break;
|
||||
|
||||
// Other (this is an error)
|
||||
default:
|
||||
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)");
|
||||
}
|
||||
SetReadOnly(false);
|
||||
SetProtectable(true);
|
||||
SetProtected(false);
|
||||
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
@ -125,12 +94,6 @@ int SCSIMO::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
// buf[4] ... Inquiry additional data
|
||||
memset(buf, 0, 8);
|
||||
buf[0] = 0x07;
|
||||
|
||||
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
|
||||
if (((cdb[1] >> 5) & 0x07) != GetLun()) {
|
||||
buf[0] = 0x7f;
|
||||
}
|
||||
|
||||
buf[1] = 0x80;
|
||||
buf[2] = 0x02;
|
||||
buf[3] = 0x02;
|
||||
@ -153,12 +116,10 @@ int SCSIMO::Inquiry(const DWORD *cdb, BYTE *buf)
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// MODE SELECT
|
||||
// *Not affected by disk.code
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
{
|
||||
int page;
|
||||
int size;
|
||||
|
||||
ASSERT(buf);
|
||||
@ -183,7 +144,7 @@ bool SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
|
||||
// Parsing the page
|
||||
while (length > 0) {
|
||||
// Get the page
|
||||
page = buf[0];
|
||||
int page = buf[0];
|
||||
|
||||
switch (page) {
|
||||
// format device
|
||||
@ -267,40 +228,44 @@ int SCSIMO::AddVendor(int page, BOOL change, BYTE *buf)
|
||||
if (IsReady()) {
|
||||
unsigned spare = 0;
|
||||
unsigned bands = 0;
|
||||
DWORD blocks = GetBlockCount();
|
||||
uint64_t blocks = GetBlockCount();
|
||||
|
||||
if (GetSectorSize() == 9) switch (blocks) {
|
||||
// 128MB
|
||||
case 248826:
|
||||
spare = 1024;
|
||||
bands = 1;
|
||||
break;
|
||||
if (GetSectorSizeInBytes() == 512) {
|
||||
switch (blocks) {
|
||||
// 128MB
|
||||
case 248826:
|
||||
spare = 1024;
|
||||
bands = 1;
|
||||
break;
|
||||
|
||||
// 230MB
|
||||
case 446325:
|
||||
spare = 1025;
|
||||
bands = 10;
|
||||
break;
|
||||
// 230MB
|
||||
case 446325:
|
||||
spare = 1025;
|
||||
bands = 10;
|
||||
break;
|
||||
|
||||
// 540MB
|
||||
case 1041500:
|
||||
spare = 2250;
|
||||
bands = 18;
|
||||
break;
|
||||
// 540MB
|
||||
case 1041500:
|
||||
spare = 2250;
|
||||
bands = 18;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetSectorSize() == 11) switch (blocks) {
|
||||
// 640MB
|
||||
case 310352:
|
||||
spare = 2244;
|
||||
bands = 11;
|
||||
break;
|
||||
if (GetSectorSizeInBytes() == 2048) {
|
||||
switch (blocks) {
|
||||
// 640MB
|
||||
case 310352:
|
||||
spare = 2244;
|
||||
bands = 11;
|
||||
break;
|
||||
|
||||
// 1.3GB (lpproj: not tested with real device)
|
||||
case 605846:
|
||||
spare = 4437;
|
||||
bands = 18;
|
||||
break;
|
||||
// 1.3GB (lpproj: not tested with real device)
|
||||
case 605846:
|
||||
spare = 4437;
|
||||
bands = 18;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
buf[2] = 0; // format mode
|
||||
|
@ -19,21 +19,17 @@
|
||||
#include "disk.h"
|
||||
#include "filepath.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// SCSI magneto-optical disk
|
||||
//
|
||||
//===========================================================================
|
||||
class SCSIMO : public Disk, public FileSupport
|
||||
{
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIMO(); // Constructor
|
||||
void Open(const Filepath& path); // Open
|
||||
SCSIMO();
|
||||
~SCSIMO() {};
|
||||
|
||||
// commands
|
||||
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
|
||||
void Open(const Filepath& path);
|
||||
|
||||
// Commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf) override;
|
||||
bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override;
|
||||
|
||||
// Internal processing
|
||||
int AddVendor(int page, BOOL change, BYTE *buf); // Add vendor special page
|
||||
|
@ -29,28 +29,21 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
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() {}
|
||||
virtual ~io_exception() {}
|
||||
|
||||
const string& getmsg() const {
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
class file_not_found_exception : public io_exception {
|
||||
public:
|
||||
file_not_found_exception(const string& msg) : io_exception(msg) {}
|
||||
~file_not_found_exception() {}
|
||||
};
|
||||
|
@ -4,135 +4,100 @@
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2010-2020 GIMONS
|
||||
// [ ファイルI/O(RaSCSI用サブセット) ]
|
||||
// [ File I/O (Subset for RaSCSI) ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "xm6.h"
|
||||
#include "filepath.h"
|
||||
#include "fileio.h"
|
||||
#include "rascsi.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ファイルI/O
|
||||
// File I/O
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// コンストラクタ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Fileio::Fileio()
|
||||
{
|
||||
// ワーク初期化
|
||||
// Initialize work
|
||||
handle = -1;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// デストラクタ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Fileio::~Fileio()
|
||||
{
|
||||
ASSERT(handle == -1);
|
||||
|
||||
// Releaseでの安全策
|
||||
// Safety measure for Release
|
||||
Close();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ロード
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Fileio::Load(const Filepath& path, void *buffer, int size)
|
||||
{
|
||||
ASSERT(buffer);
|
||||
ASSERT(size > 0);
|
||||
ASSERT(handle < 0);
|
||||
|
||||
// オープン
|
||||
if (!Open(path, ReadOnly)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// 読み込み
|
||||
if (!Read(buffer, size)) {
|
||||
Close();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// クローズ
|
||||
Close();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// セーブ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Fileio::Save(const Filepath& path, void *buffer, int size)
|
||||
{
|
||||
ASSERT(buffer);
|
||||
ASSERT(size > 0);
|
||||
ASSERT(handle < 0);
|
||||
|
||||
// オープン
|
||||
if (!Open(path, WriteOnly)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// 書き込み
|
||||
if (!Write(buffer, size)) {
|
||||
Close();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// クローズ
|
||||
Close();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// オープン
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Fileio::Open(LPCTSTR fname, OpenMode mode, BOOL directIO)
|
||||
BOOL Fileio::Open(const char *fname, OpenMode mode, BOOL directIO)
|
||||
{
|
||||
mode_t omode;
|
||||
|
||||
ASSERT(fname);
|
||||
ASSERT(handle < 0);
|
||||
|
||||
// ヌル文字列からの読み込みは必ず失敗させる
|
||||
// Always fail a read from a null array
|
||||
if (fname[0] == _T('\0')) {
|
||||
handle = -1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// デフォルトモード
|
||||
// Default mode
|
||||
omode = directIO ? O_DIRECT : 0;
|
||||
|
||||
// モード別
|
||||
switch (mode) {
|
||||
// 読み込みのみ
|
||||
case ReadOnly:
|
||||
handle = open(fname, O_RDONLY | omode);
|
||||
break;
|
||||
|
||||
// 書き込みのみ
|
||||
case WriteOnly:
|
||||
handle = open(fname, O_CREAT | O_WRONLY | O_TRUNC | omode, 0666);
|
||||
break;
|
||||
|
||||
// 読み書き両方
|
||||
case ReadWrite:
|
||||
// CD-ROMからの読み込みはRWが成功してしまう
|
||||
if (access(fname, 0x06) != 0) {
|
||||
@ -141,13 +106,12 @@ BOOL Fileio::Open(LPCTSTR fname, OpenMode mode, BOOL directIO)
|
||||
handle = open(fname, O_RDWR | omode);
|
||||
break;
|
||||
|
||||
// それ以外
|
||||
default:
|
||||
ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
|
||||
// 結果評価
|
||||
// Evaluate results
|
||||
if (handle == -1) {
|
||||
return FALSE;
|
||||
}
|
||||
@ -156,61 +120,36 @@ BOOL Fileio::Open(LPCTSTR fname, OpenMode mode, BOOL directIO)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// オープン
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Fileio::Open(LPCTSTR fname, OpenMode mode)
|
||||
BOOL Fileio::Open(const char *fname, OpenMode mode)
|
||||
{
|
||||
|
||||
return Open(fname, mode, FALSE);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// オープン
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Fileio::Open(const Filepath& path, OpenMode mode)
|
||||
{
|
||||
|
||||
return Open(path.GetPath(), mode);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// オープン
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Fileio::OpenDIO(LPCTSTR fname, OpenMode mode)
|
||||
BOOL Fileio::OpenDIO(const char *fname, OpenMode mode)
|
||||
{
|
||||
|
||||
// O_DIRECT付きでオープン
|
||||
// Open with included O_DIRECT
|
||||
if (!Open(fname, mode, TRUE)) {
|
||||
// 通常モードリトライ(tmpfs等)
|
||||
// Normal mode retry (tmpfs etc.)
|
||||
return Open(fname, mode, FALSE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// オープン
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Fileio::OpenDIO(const Filepath& path, OpenMode mode)
|
||||
{
|
||||
|
||||
return OpenDIO(path.GetPath(), mode);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 読み込み
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Fileio::Read(void *buffer, int size)
|
||||
{
|
||||
int count;
|
||||
@ -219,7 +158,6 @@ BOOL Fileio::Read(void *buffer, int size)
|
||||
ASSERT(size > 0);
|
||||
ASSERT(handle >= 0);
|
||||
|
||||
// 読み込み
|
||||
count = read(handle, buffer, size);
|
||||
if (count != size) {
|
||||
return FALSE;
|
||||
@ -228,11 +166,6 @@ BOOL Fileio::Read(void *buffer, int size)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 書き込み
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Fileio::Write(const void *buffer, int size)
|
||||
{
|
||||
int count;
|
||||
@ -241,7 +174,6 @@ BOOL Fileio::Write(const void *buffer, int size)
|
||||
ASSERT(size > 0);
|
||||
ASSERT(handle >= 0);
|
||||
|
||||
// 書き込み
|
||||
count = write(handle, buffer, size);
|
||||
if (count != size) {
|
||||
return FALSE;
|
||||
@ -250,17 +182,12 @@ BOOL Fileio::Write(const void *buffer, int size)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// シーク
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Fileio::Seek(off_t offset, BOOL relative)
|
||||
{
|
||||
ASSERT(handle >= 0);
|
||||
ASSERT(offset >= 0);
|
||||
|
||||
// 相対シークならオフセットに現在値を追加
|
||||
// Add current position in case of relative seek
|
||||
if (relative) {
|
||||
offset += GetFilePos();
|
||||
}
|
||||
@ -272,11 +199,6 @@ BOOL Fileio::Seek(off_t offset, BOOL relative)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ファイルサイズ取得
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
off_t Fileio::GetFileSize()
|
||||
{
|
||||
off_t cur;
|
||||
@ -284,40 +206,30 @@ off_t Fileio::GetFileSize()
|
||||
|
||||
ASSERT(handle >= 0);
|
||||
|
||||
// ファイル位置を64bitで取得
|
||||
// Get file position in 64bit
|
||||
cur = GetFilePos();
|
||||
|
||||
// ファイルサイズを64bitで取得
|
||||
// Get file size in64bitで
|
||||
end = lseek(handle, 0, SEEK_END);
|
||||
|
||||
// 位置を元に戻す
|
||||
// Return to start position
|
||||
Seek(cur);
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ファイル位置取得
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
off_t Fileio::GetFilePos() const
|
||||
{
|
||||
off_t pos;
|
||||
|
||||
ASSERT(handle >= 0);
|
||||
|
||||
// ファイル位置を64bitで取得
|
||||
// Get file position in 64bit
|
||||
pos = lseek(handle, 0, SEEK_CUR);
|
||||
return pos;
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// クローズ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Fileio::Close()
|
||||
{
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
//
|
||||
// Copyright (C) 2001-2005 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2013-2020 GIMONS
|
||||
// [ ファイルI/O(RaSCSI用サブセット) ]
|
||||
// [ File I/O (Subset for RaSCSI) ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// マクロ(Load,Save用)
|
||||
// Macros (for Load, Save)
|
||||
//
|
||||
//===========================================================================
|
||||
#define PROP_IMPORT(f, p) \
|
||||
@ -30,56 +30,40 @@
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ファイルI/O
|
||||
// File I/O
|
||||
//
|
||||
//===========================================================================
|
||||
class Fileio
|
||||
{
|
||||
public:
|
||||
enum OpenMode {
|
||||
ReadOnly, // 読み込みのみ
|
||||
WriteOnly, // 書き込みのみ
|
||||
ReadWrite // 読み書き両方
|
||||
ReadOnly,
|
||||
WriteOnly,
|
||||
ReadWrite
|
||||
};
|
||||
|
||||
public:
|
||||
Fileio();
|
||||
// コンストラクタ
|
||||
virtual ~Fileio();
|
||||
// デストラクタ
|
||||
BOOL Load(const Filepath& path, void *buffer, int size);
|
||||
// ROM,RAMロード
|
||||
BOOL Save(const Filepath& path, void *buffer, int size);
|
||||
// RAMセーブ
|
||||
BOOL Load(const Filepath& path, void *buffer, int size); // Load ROM, RAM
|
||||
BOOL Save(const Filepath& path, void *buffer, int size); // Save RAM
|
||||
|
||||
BOOL Open(LPCTSTR fname, OpenMode mode);
|
||||
// オープン
|
||||
BOOL Open(const char *fname, OpenMode mode);
|
||||
BOOL Open(const Filepath& path, OpenMode mode);
|
||||
// オープン
|
||||
BOOL OpenDIO(LPCTSTR fname, OpenMode mode);
|
||||
// オープン
|
||||
BOOL OpenDIO(const char *fname, OpenMode mode);
|
||||
BOOL OpenDIO(const Filepath& path, OpenMode mode);
|
||||
// オープン
|
||||
BOOL Seek(off_t offset, BOOL relative = FALSE);
|
||||
// シーク
|
||||
BOOL Read(void *buffer, int size);
|
||||
// 読み込み
|
||||
BOOL Write(const void *buffer, int size);
|
||||
// 書き込み
|
||||
off_t GetFileSize();
|
||||
// ファイルサイズ取得
|
||||
off_t GetFilePos() const;
|
||||
// ファイル位置取得
|
||||
void Close();
|
||||
// クローズ
|
||||
BOOL IsValid() const { return (BOOL)(handle != -1); }
|
||||
// 有効チェック
|
||||
|
||||
private:
|
||||
BOOL Open(LPCTSTR fname, OpenMode mode, BOOL directIO);
|
||||
// オープン
|
||||
BOOL Open(const char *fname, OpenMode mode, BOOL directIO);
|
||||
|
||||
int handle; // ファイルハンドル
|
||||
int handle; // File handle
|
||||
};
|
||||
|
||||
#endif // fileio_h
|
||||
|
@ -4,14 +4,14 @@
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2012-2020 GIMONS
|
||||
// [ ファイルパス(サブセット) ]
|
||||
// [ File path (subset) ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "xm6.h"
|
||||
#include "filepath.h"
|
||||
#include "fileio.h"
|
||||
#include "rascsi.h"
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
@ -19,31 +19,16 @@
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Filepath::Filepath()
|
||||
{
|
||||
// Clear
|
||||
Clear();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Filepath::~Filepath()
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Assignment operator
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
Filepath& Filepath::operator=(const Filepath& path)
|
||||
{
|
||||
// Set path (split internally)
|
||||
@ -52,11 +37,6 @@ Filepath& Filepath::operator=(const Filepath& path)
|
||||
return *this;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Clear
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Filepath::Clear()
|
||||
{
|
||||
|
||||
@ -69,16 +49,16 @@ void Filepath::Clear()
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ファイル設定(ユーザ) MBCS用
|
||||
// File settings (user) for MBCS
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Filepath::SetPath(LPCSTR path)
|
||||
void Filepath::SetPath(const char *path)
|
||||
{
|
||||
ASSERT(path);
|
||||
ASSERT(strlen(path) < _MAX_PATH);
|
||||
|
||||
// Copy pathname
|
||||
strcpy(m_szPath, (LPTSTR)path);
|
||||
strcpy(m_szPath, (char *)path);
|
||||
|
||||
// Split
|
||||
Split();
|
||||
@ -86,31 +66,24 @@ void Filepath::SetPath(LPCSTR path)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// パス分離
|
||||
// Split paths
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Filepath::Split()
|
||||
{
|
||||
LPTSTR pDir;
|
||||
LPTSTR pDirName;
|
||||
LPTSTR pBase;
|
||||
LPTSTR pBaseName;
|
||||
LPTSTR pExtName;
|
||||
|
||||
|
||||
// パーツを初期化
|
||||
// Initialize the parts
|
||||
m_szDir[0] = _T('\0');
|
||||
m_szFile[0] = _T('\0');
|
||||
m_szExt[0] = _T('\0');
|
||||
|
||||
// 分離
|
||||
pDir = strdup(m_szPath);
|
||||
pDirName = dirname(pDir);
|
||||
pBase = strdup(m_szPath);
|
||||
pBaseName = basename(pBase);
|
||||
pExtName = strrchr(pBaseName, '.');
|
||||
// Split
|
||||
char *pDir = strdup(m_szPath);
|
||||
char *pDirName = dirname(pDir);
|
||||
char *pBase = strdup(m_szPath);
|
||||
char *pBaseName = basename(pBase);
|
||||
char *pExtName = strrchr(pBaseName, '.');
|
||||
|
||||
// 転送
|
||||
// Transmit
|
||||
if (pDirName) {
|
||||
strcpy(m_szDir, pDirName);
|
||||
strcat(m_szDir, "/");
|
||||
@ -124,7 +97,7 @@ void Filepath::Split()
|
||||
strcpy(m_szFile, pBaseName);
|
||||
}
|
||||
|
||||
// 解放
|
||||
// Release
|
||||
free(pDir);
|
||||
free(pBase);
|
||||
}
|
||||
@ -135,21 +108,16 @@ void Filepath::Split()
|
||||
// The returned pointer is temporary. Copy immediately.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
LPCTSTR Filepath::GetFileExt() const
|
||||
const char *Filepath::GetFileExt() const
|
||||
{
|
||||
|
||||
// 固定バッファへ合成
|
||||
// Merge into static buffer
|
||||
strcpy(FileExt, m_szExt);
|
||||
|
||||
// LPCTSTRとして返す
|
||||
return (LPCTSTR)FileExt;
|
||||
// Return as LPCTSTR
|
||||
return (const char *)FileExt;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Save
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Filepath::Save(Fileio *fio, int /*ver*/)
|
||||
{
|
||||
ASSERT(fio);
|
||||
@ -157,11 +125,6 @@ BOOL Filepath::Save(Fileio *fio, int /*ver*/)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Load
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Filepath::Load(Fileio *fio, int /*ver*/)
|
||||
{
|
||||
ASSERT(fio);
|
||||
|
@ -4,12 +4,13 @@
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2012-2020 GIMONS
|
||||
// [ ファイルパス(サブセット) ]
|
||||
// [ File path (subset) ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#if !defined(filepath_h)
|
||||
#define filepath_h
|
||||
#pragma once
|
||||
|
||||
#include "os.h"
|
||||
|
||||
class Fileio;
|
||||
|
||||
@ -22,47 +23,30 @@ class Fileio;
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ファイルパス
|
||||
// ※代入演算子を用意すること
|
||||
// File path
|
||||
// Assignment operators are prepared here
|
||||
//
|
||||
//===========================================================================
|
||||
class Filepath
|
||||
{
|
||||
public:
|
||||
Filepath();
|
||||
// コンストラクタ
|
||||
virtual ~Filepath();
|
||||
// デストラクタ
|
||||
Filepath& operator=(const Filepath& path);
|
||||
// 代入
|
||||
|
||||
void Clear();
|
||||
// クリア
|
||||
void SetPath(LPCSTR path);
|
||||
// ファイル設定(ユーザ) MBCS用
|
||||
LPCTSTR GetPath() const { return m_szPath; }
|
||||
// パス名取得
|
||||
LPCTSTR GetFileExt() const;
|
||||
// ショート名取得(LPCTSTR)
|
||||
void SetPath(const char *path); // File settings (user) for MBCS
|
||||
const char *GetPath() const { return m_szPath; } // Get path name
|
||||
const char *GetFileExt() const; // Get short name (LPCTSTR)
|
||||
BOOL Save(Fileio *fio, int ver);
|
||||
// セーブ
|
||||
BOOL Load(Fileio *fio, int ver);
|
||||
// ロード
|
||||
|
||||
private:
|
||||
void Split();
|
||||
// パス分割
|
||||
TCHAR m_szPath[_MAX_PATH];
|
||||
// ファイルパス
|
||||
TCHAR m_szDir[_MAX_DIR];
|
||||
// ディレクトリ
|
||||
TCHAR m_szFile[_MAX_FNAME];
|
||||
// ファイル
|
||||
TCHAR m_szExt[_MAX_EXT];
|
||||
// 拡張子
|
||||
void Split(); // Split the path
|
||||
TCHAR m_szPath[_MAX_PATH]; // File path
|
||||
TCHAR m_szDir[_MAX_DIR]; // Directory
|
||||
TCHAR m_szFile[_MAX_FNAME]; // File
|
||||
TCHAR m_szExt[_MAX_EXT]; // Extension
|
||||
|
||||
static TCHAR FileExt[_MAX_FNAME + _MAX_DIR];
|
||||
// ショート名(TCHAR)
|
||||
static TCHAR FileExt[_MAX_FNAME + _MAX_DIR]; // Short name (TCHAR)
|
||||
};
|
||||
|
||||
#endif // filepath_h
|
||||
|
@ -15,9 +15,9 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "os.h"
|
||||
#include "xm6.h"
|
||||
#include "gpiobus.h"
|
||||
#include "log.h"
|
||||
#include "rascsi.h"
|
||||
|
||||
#ifdef __linux__
|
||||
//---------------------------------------------------------------------------
|
||||
@ -82,11 +82,6 @@ DWORD bcm_host_get_peripheral_address(void)
|
||||
}
|
||||
#endif // __NetBSD__
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
GPIOBUS::GPIOBUS()
|
||||
{
|
||||
actmode = TARGET;
|
||||
@ -102,20 +97,10 @@ GPIOBUS::GPIOBUS()
|
||||
rpitype = 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
GPIOBUS::~GPIOBUS()
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 初期化
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL GPIOBUS::Init(mode_e mode)
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__X86__)
|
||||
@ -339,11 +324,6 @@ BOOL GPIOBUS::Init(mode_e mode)
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Cleanup
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void GPIOBUS::Cleanup()
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__X86__)
|
||||
@ -381,11 +361,6 @@ void GPIOBUS::Cleanup()
|
||||
#endif // ifdef __x86_64__ || __X86__
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Reset
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void GPIOBUS::Reset()
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__X86__)
|
||||
@ -1612,7 +1587,7 @@ BUS::phase_t GPIOBUS::GetPhaseRaw(DWORD raw_data)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int GPIOBUS::GetCommandByteCount(BYTE opcode) {
|
||||
if (opcode == 0x88 || opcode == 0x8A || opcode == 0x8F || opcode == 0x9E) {
|
||||
if (opcode == 0x88 || opcode == 0x8A || opcode == 0x8F || opcode == 0x91 || opcode == 0x9E) {
|
||||
return 16;
|
||||
}
|
||||
else if (opcode == 0xA0) {
|
||||
|
@ -12,6 +12,7 @@
|
||||
#if !defined(gpiobus_h)
|
||||
#define gpiobus_h
|
||||
|
||||
#include "rascsi.h"
|
||||
#include "scsi.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -188,7 +189,7 @@
|
||||
|
||||
#ifdef CONNECT_TYPE_AIBOM
|
||||
//
|
||||
// RaSCSI Adapter Aibomu version
|
||||
// RaSCSI Adapter Aibom version
|
||||
//
|
||||
|
||||
#define CONNECT_DESC "AIBOM PRODUCTS version" // Startup message
|
||||
@ -233,7 +234,7 @@
|
||||
|
||||
#ifdef CONNECT_TYPE_GAMERNIUM
|
||||
//
|
||||
// RaSCSI Adapter GAMERnium.com版
|
||||
// RaSCSI Adapter GAMERnium.com version
|
||||
//
|
||||
|
||||
#define CONNECT_DESC "GAMERnium.com version"// Startup message
|
||||
@ -298,7 +299,7 @@
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constant declarations(GPIO)
|
||||
// Constant declarations (GPIO)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#define SYST_OFFSET 0x00003000
|
||||
@ -375,7 +376,7 @@
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constant declarations(GIC)
|
||||
// Constant declarations (GIC)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#define ARM_GICD_BASE 0xFF841000
|
||||
@ -400,7 +401,7 @@
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constant declarations(GIC IRQ)
|
||||
// Constant declarations (GIC IRQ)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#define GIC_IRQLOCAL0 (16 + 14)
|
||||
@ -419,7 +420,7 @@
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constant declarations(SCSI)
|
||||
// Constant declarations (SCSI)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#define IN GPIO_INPUT
|
||||
|
@ -105,12 +105,8 @@
|
||||
typedef unsigned char BYTE;
|
||||
typedef uint16_t WORD;
|
||||
typedef uint32_t DWORD;
|
||||
typedef uint64_t QWORD;
|
||||
typedef int BOOL;
|
||||
typedef char TCHAR;
|
||||
typedef char *LPTSTR;
|
||||
typedef const char *LPCTSTR;
|
||||
typedef const char *LPCSTR;
|
||||
|
||||
#if !defined(FALSE)
|
||||
#define FALSE 0
|
||||
|
@ -23,7 +23,7 @@ using namespace rascsi_interface;
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void SerializeMessage(int fd, const google::protobuf::MessageLite& message)
|
||||
void SerializeMessage(int fd, const google::protobuf::Message& message)
|
||||
{
|
||||
string data;
|
||||
message.SerializeToString(&data);
|
||||
@ -35,14 +35,12 @@ void SerializeMessage(int fd, const google::protobuf::MessageLite& message)
|
||||
}
|
||||
|
||||
// Write the actual protobuf data
|
||||
uint8_t buf[size];
|
||||
memcpy(buf, data.data(), size);
|
||||
if (write(fd, buf, size) != size) {
|
||||
if (write(fd, data.data(), size) != size) {
|
||||
throw io_exception("Can't write protobuf data");
|
||||
}
|
||||
}
|
||||
|
||||
void DeserializeMessage(int fd, google::protobuf::MessageLite& message)
|
||||
void DeserializeMessage(int fd, google::protobuf::Message& message)
|
||||
{
|
||||
// Read the header with the size of the protobuf data
|
||||
uint8_t header_buf[4];
|
||||
|
@ -11,9 +11,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "google/protobuf/message_lite.h"
|
||||
#include "google/protobuf/message.h"
|
||||
#include "rascsi_interface.pb.h"
|
||||
|
||||
void SerializeMessage(int, const google::protobuf::MessageLite&);
|
||||
void DeserializeMessage(int, google::protobuf::MessageLite&);
|
||||
void SerializeMessage(int, const google::protobuf::Message&);
|
||||
void DeserializeMessage(int, google::protobuf::Message&);
|
||||
int ReadNBytes(int, uint8_t *, int);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,12 +6,11 @@
|
||||
// Powered by XM6 TypeG Technology.
|
||||
// Copyright (C) 2016-2020 GIMONS
|
||||
//
|
||||
// [ Common Definition ]
|
||||
// [ Common Definitions ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#if !defined(xm6_h)
|
||||
#define xm6_h
|
||||
#pragma once
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
@ -20,7 +19,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
#define USE_SEL_EVENT_ENABLE // Check SEL signal by event
|
||||
#define REMOVE_FIXED_SASIHD_SIZE // remove the size limitation of SASIHD
|
||||
#define USE_MZ1F23_1024_SUPPORT // MZ-1F23 (SASI 20M/sector size 1024)
|
||||
// This avoids an indefinite loop with warnings if there is no RaSCSI hardware
|
||||
// and thus helps with running certain tests on X86 hardware.
|
||||
#if defined(__x86_64__) || defined(__X86__)
|
||||
@ -29,7 +27,7 @@
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Class Declaration
|
||||
// Class Declarations
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
class Fileio;
|
||||
@ -37,6 +35,3 @@ class Fileio;
|
||||
class Disk;
|
||||
// SASI/SCSI Disk
|
||||
class Filepath;
|
||||
// File Path
|
||||
|
||||
#endif // xm6_h
|
@ -1,6 +1,7 @@
|
||||
//
|
||||
// Each rascsi remote interface message is preceded by a little endian 32 bit header,
|
||||
// which contains the protobuf message size.
|
||||
// Unless explicitly specified the order of repeated data returned is undefined.
|
||||
//
|
||||
|
||||
syntax = "proto3";
|
||||
@ -26,27 +27,82 @@ enum PbDeviceType {
|
||||
SCDP = 7;
|
||||
}
|
||||
|
||||
// rascsi remote operations
|
||||
// rascsi remote operations, returns PbResult
|
||||
enum PbOperation {
|
||||
NONE = 0;
|
||||
// Returns the server information
|
||||
// Gets the server information
|
||||
SERVER_INFO = 1;
|
||||
// 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;
|
||||
// Gets information for a list of attached devices. Returns data for all attached devices if empty.
|
||||
DEVICE_INFO = 2;
|
||||
// Set the default folder for image files. PbCommand.params contains the folder name.
|
||||
DEFAULT_FOLDER = 3;
|
||||
// Set server log level. PbCommand.params contains the log level.
|
||||
LOG_LEVEL = 4;
|
||||
// Attach devices
|
||||
ATTACH = 5;
|
||||
// Detach devices
|
||||
DETACH = 6;
|
||||
// Detach all devices, does not require a device list
|
||||
DETACH_ALL = 7;
|
||||
// Start device
|
||||
START = 8;
|
||||
// Stop device, e.g. park drive
|
||||
STOP = 9;
|
||||
// Insert medium
|
||||
INSERT = 10;
|
||||
// Eject medium
|
||||
EJECT = 11;
|
||||
// Write-protect medium (not possible for read-only media)
|
||||
PROTECT = 12;
|
||||
// Make medium writable (not possible for read-only media)
|
||||
UNPROTECT = 13;
|
||||
// IDs blocked from being used, usually the IDs of the initiators (computers) in the SCSI chain.
|
||||
// PbCommand.params contains the list of IDs to reserve, or is empty in order not to reserve any ID.
|
||||
RESERVE = 14;
|
||||
}
|
||||
|
||||
// The properties supported by a device, helping clients to offer a good user experience
|
||||
message PbDeviceProperties {
|
||||
// Read-only media (e.g. CD-ROMs) are not protectable but permanently read-only
|
||||
bool read_only = 1;
|
||||
// Medium can be write-protected
|
||||
bool protectable = 2;
|
||||
// Device can be stopped, e.g. parked
|
||||
bool stoppable = 3;
|
||||
// Medium can be removed
|
||||
bool removable = 4;
|
||||
// Medium can be locked
|
||||
bool lockable = 5;
|
||||
// Device supports image file as a parameter
|
||||
bool supports_file = 6;
|
||||
// Device supports parameters other than a filename
|
||||
bool supports_params = 7;
|
||||
// Ordered list of default parameters, if any (requires supports_params to be true)
|
||||
repeated string default_params = 8;
|
||||
// Number of supported LUNs, at least 1 (for LUN 0)
|
||||
uint32 luns = 9;
|
||||
// Unordered list of permitted block sizes in bytes, empty if the block size is not configurable
|
||||
repeated uint32 block_sizes = 10;
|
||||
// Unordered list of permitted media capacities in bytes, empty if there is no capacity restriction
|
||||
repeated uint64 capacities = 11;
|
||||
}
|
||||
|
||||
// The status of a device, only meaningful if the respective feature is supported
|
||||
message PbDeviceStatus {
|
||||
// Medium is write-protected
|
||||
bool protected = 1;
|
||||
// Device is stopped, e.g. parked
|
||||
bool stopped = 2;
|
||||
// Medium is removed
|
||||
bool removed = 3;
|
||||
// Medium is locked
|
||||
bool locked = 4;
|
||||
}
|
||||
|
||||
// Device properties by device type. (Note that a protobuf map cannot be used because enums cannot be map keys.)
|
||||
message PbDeviceTypeProperties {
|
||||
PbDeviceType type = 1;
|
||||
PbDeviceProperties properties = 2;
|
||||
}
|
||||
|
||||
// The image file data
|
||||
@ -61,14 +117,16 @@ message PbDeviceDefinition {
|
||||
int32 id = 1;
|
||||
int32 unit = 2;
|
||||
PbDeviceType type = 3;
|
||||
string file = 4;
|
||||
// The device name, format is VENDOR:PRODUCT:REVISION
|
||||
string name = 5;
|
||||
bool protected = 6;
|
||||
}
|
||||
|
||||
message PbDeviceDefinitions {
|
||||
repeated PbDeviceDefinition devices = 1;
|
||||
// Optional device specific parameters, e.g. the name of an image file
|
||||
repeated string params = 4;
|
||||
// The optional block size in bytes per sector, must be one of the supported block sizes for SASI/SCSI
|
||||
int32 block_size = 5;
|
||||
// The device name components
|
||||
string vendor = 6;
|
||||
string product = 7;
|
||||
string revision = 8;
|
||||
// Create a write-protected device
|
||||
bool protected = 9;
|
||||
}
|
||||
|
||||
// The device data, sent from the server to the client
|
||||
@ -76,26 +134,19 @@ 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;
|
||||
PbDeviceProperties properties = 4;
|
||||
PbDeviceStatus status = 5;
|
||||
// Image file information, if the device supports image files
|
||||
PbImageFile file = 6;
|
||||
// Ordered list of effective parameters the device was created with
|
||||
repeated string params = 7;
|
||||
string vendor = 8;
|
||||
string product = 9;
|
||||
string revision = 10;
|
||||
// Block size in bytes
|
||||
int32 block_size = 11;
|
||||
// Number of blocks
|
||||
int64 block_count = 12;
|
||||
}
|
||||
|
||||
message PbDevices {
|
||||
@ -104,30 +155,45 @@ message PbDevices {
|
||||
|
||||
// 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;
|
||||
PbOperation operation = 1;
|
||||
// The non-empty list of devices for this command
|
||||
repeated PbDeviceDefinition devices = 2;
|
||||
// The optional parameters depending on the operation, e.g. a filename, or a network interface list
|
||||
repeated string params = 3;
|
||||
}
|
||||
|
||||
// The result of a command
|
||||
message PbResult {
|
||||
// false means that an error occurred
|
||||
bool status = 1;
|
||||
// The (error) message
|
||||
// An optional error or information message, depending on the status. A string without trailing CR/LF.
|
||||
string msg = 2;
|
||||
// Optional additional result data
|
||||
oneof result {
|
||||
// The result of a SERVER_INFO command
|
||||
PbServerInfo server_info = 3;
|
||||
// The result of a DEVICE_INFO command
|
||||
PbDevices device_info = 4;
|
||||
}
|
||||
}
|
||||
|
||||
// The rascsi server information
|
||||
message PbServerInfo {
|
||||
string rascsi_version = 1;
|
||||
// Sorted by severity
|
||||
repeated string available_log_levels = 2;
|
||||
string current_log_level = 3;
|
||||
string default_image_folder = 4;
|
||||
// Files in the default folder
|
||||
repeated PbImageFile available_image_files = 5;
|
||||
// The rascsi server version
|
||||
uint32 major_version = 1;
|
||||
uint32 minor_version = 2;
|
||||
// < 0 for a development version, = 0 if there is no patch yet
|
||||
int32 patch_version = 3;
|
||||
// List of available log levels, ordered by increasing by severity
|
||||
repeated string log_levels = 4;
|
||||
string current_log_level = 5;
|
||||
string default_image_folder = 6;
|
||||
// Supported device types and their properties
|
||||
repeated PbDeviceTypeProperties types_properties = 7;
|
||||
// Unordered list of files in the default image folder
|
||||
repeated PbImageFile image_files = 8;
|
||||
// The attached devices
|
||||
PbDevices devices = 6;
|
||||
}
|
||||
repeated PbDevice devices = 9;
|
||||
// The unsorted list of reserved IDs
|
||||
repeated uint32 reserved_ids = 10;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2020 akuker
|
||||
// [ Define the version string]
|
||||
// [ Define the version string ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2020 akuker
|
||||
// [ Define the version string]
|
||||
// [ Define the version string ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
@ -20,18 +20,16 @@
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
|
||||
// Separator for the INQUIRY name components
|
||||
#define COMPONENT_SEPARATOR ':'
|
||||
|
||||
using namespace std;
|
||||
using namespace rascsi_interface;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Send Command
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SendCommand(const string& hostname, int port, const PbCommand& command)
|
||||
void SendCommand(const string& hostname, int port, const PbCommand& command, PbResult& result)
|
||||
{
|
||||
// Send command
|
||||
int fd = -1;
|
||||
|
||||
try {
|
||||
struct hostent *host = gethostbyname(hostname.c_str());
|
||||
if (!host) {
|
||||
@ -68,36 +66,89 @@ int SendCommand(const string& hostname, int port, const PbCommand& command)
|
||||
exit(fd < 0 ? ENOTCONN : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Receive command result
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool ReceiveResult(int fd)
|
||||
{
|
||||
// Receive result
|
||||
try {
|
||||
PbResult result;
|
||||
DeserializeMessage(fd, result);
|
||||
close(fd);
|
||||
|
||||
if (!result.status()) {
|
||||
throw io_exception(result.msg());
|
||||
}
|
||||
|
||||
if (!result.msg().empty()) {
|
||||
cout << result.msg() << endl;
|
||||
}
|
||||
}
|
||||
catch(const io_exception& e) {
|
||||
close(fd);
|
||||
|
||||
cerr << "Error: " << e.getmsg() << endl;
|
||||
|
||||
return false;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return true;
|
||||
close(fd);
|
||||
|
||||
if (!result.msg().empty()) {
|
||||
cout << result.msg() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayDeviceInfo(const PbDevice& pb_device)
|
||||
{
|
||||
cout << " " << pb_device.id() << ":" << pb_device.unit() << " " << PbDeviceType_Name(pb_device.type())
|
||||
<< " " << pb_device.vendor() << ":" << pb_device.product() << ":" << pb_device.revision();
|
||||
|
||||
if (pb_device.block_size()) {
|
||||
cout << " " << pb_device.block_size() << " bytes per sector";
|
||||
if (pb_device.block_count()) {
|
||||
cout << " " << pb_device.block_size() * pb_device.block_count() << " bytes capacity";
|
||||
}
|
||||
}
|
||||
|
||||
if (pb_device.properties().supports_file() && !pb_device.file().name().empty()) {
|
||||
cout << " " << pb_device.file().name();
|
||||
}
|
||||
|
||||
cout << " ";
|
||||
bool hasProperty = false;
|
||||
if (pb_device.properties().read_only()) {
|
||||
cout << "read-only";
|
||||
hasProperty = true;
|
||||
}
|
||||
if (pb_device.properties().protectable() && pb_device.status().protected_()) {
|
||||
if (hasProperty) {
|
||||
cout << ", ";
|
||||
}
|
||||
cout << "protected";
|
||||
hasProperty = true;
|
||||
}
|
||||
if (pb_device.properties().stoppable() && pb_device.status().stopped()) {
|
||||
if (hasProperty) {
|
||||
cout << ", ";
|
||||
}
|
||||
cout << "stopped";
|
||||
hasProperty = true;
|
||||
}
|
||||
if (pb_device.properties().removable() && pb_device.status().removed()) {
|
||||
if (hasProperty) {
|
||||
cout << ", ";
|
||||
}
|
||||
cout << "removed";
|
||||
hasProperty = true;
|
||||
}
|
||||
if (pb_device.properties().lockable() && pb_device.status().locked()) {
|
||||
if (hasProperty) {
|
||||
cout << ", ";
|
||||
}
|
||||
cout << "locked";
|
||||
}
|
||||
if (hasProperty) {
|
||||
cout << " ";
|
||||
}
|
||||
|
||||
if (pb_device.params_size()) {
|
||||
for (const string param : pb_device.params()) {
|
||||
cout << param << " ";
|
||||
}
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -106,101 +157,239 @@ bool ReceiveResult(int fd)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
const PbServerInfo GetServerInfo(const string& hostname, int port)
|
||||
{
|
||||
PbCommand command;
|
||||
command.set_operation(SERVER_INFO);
|
||||
|
||||
PbResult result;
|
||||
SendCommand(hostname.c_str(), port, command, result);
|
||||
|
||||
return result.server_info();
|
||||
}
|
||||
|
||||
void CommandList(const string& hostname, int port)
|
||||
{
|
||||
PbCommand command;
|
||||
command.set_cmd(SERVER_INFO);
|
||||
command.set_operation(DEVICE_INFO);
|
||||
|
||||
int fd = SendCommand(hostname.c_str(), port, command);
|
||||
PbResult result;
|
||||
SendCommand(hostname.c_str(), port, command, result);
|
||||
|
||||
PbServerInfo serverInfo;
|
||||
try {
|
||||
DeserializeMessage(fd, serverInfo);
|
||||
}
|
||||
catch(const io_exception& e) {
|
||||
cerr << "Error: " << e.getmsg() << endl;
|
||||
|
||||
close(fd);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
cout << ListDevices(serverInfo.devices()) << endl;
|
||||
const list<PbDevice>& devices = { result.device_info().devices().begin(), result.device_info().devices().end() };
|
||||
cout << ListDevices(devices) << endl;
|
||||
}
|
||||
|
||||
void CommandLogLevel(const string& hostname, int port, const string& log_level)
|
||||
{
|
||||
PbCommand command;
|
||||
command.set_cmd(LOG_LEVEL);
|
||||
command.set_params(log_level);
|
||||
command.set_operation(LOG_LEVEL);
|
||||
command.add_params(log_level);
|
||||
|
||||
int fd = SendCommand(hostname.c_str(), port, command);
|
||||
ReceiveResult(fd);
|
||||
close(fd);
|
||||
PbResult result;
|
||||
SendCommand(hostname.c_str(), port, command, result);
|
||||
}
|
||||
|
||||
void CommandReserve(const string&hostname, int port, const string& reserved_ids)
|
||||
{
|
||||
PbCommand command;
|
||||
command.set_operation(RESERVE);
|
||||
|
||||
stringstream ss(reserved_ids);
|
||||
string reserved_id;
|
||||
|
||||
while (getline(ss, reserved_id, ',')) {
|
||||
command.add_params(reserved_id);
|
||||
}
|
||||
|
||||
PbResult result;
|
||||
SendCommand(hostname.c_str(), port, command, result);
|
||||
}
|
||||
|
||||
void CommandDefaultImageFolder(const string& hostname, int port, const string& folder)
|
||||
{
|
||||
PbCommand command;
|
||||
command.set_cmd(DEFAULT_FOLDER);
|
||||
command.set_params(folder);
|
||||
command.set_operation(DEFAULT_FOLDER);
|
||||
command.add_params(folder);
|
||||
|
||||
int fd = SendCommand(hostname.c_str(), port, command);
|
||||
ReceiveResult(fd);
|
||||
close(fd);
|
||||
PbResult result;
|
||||
SendCommand(hostname.c_str(), port, command, result);
|
||||
}
|
||||
|
||||
void CommandDeviceInfo(const string& hostname, int port, const PbCommand& command)
|
||||
{
|
||||
PbResult result;
|
||||
SendCommand(hostname.c_str(), port, command, result);
|
||||
|
||||
for (const auto& pb_device : result.device_info().devices()) {
|
||||
DisplayDeviceInfo(pb_device);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandServerInfo(const string& hostname, int port)
|
||||
{
|
||||
PbCommand command;
|
||||
command.set_cmd(SERVER_INFO);
|
||||
command.set_operation(SERVER_INFO);
|
||||
|
||||
int fd = SendCommand(hostname.c_str(), port, command);
|
||||
PbResult result;
|
||||
SendCommand(hostname.c_str(), port, command, result);
|
||||
|
||||
PbServerInfo serverInfo;
|
||||
try {
|
||||
DeserializeMessage(fd, serverInfo);
|
||||
PbServerInfo server_info = result.server_info();
|
||||
|
||||
cout << "rascsi server version: " << server_info.major_version() << "." << server_info.minor_version();
|
||||
if (server_info.patch_version() > 0) {
|
||||
cout << "." << server_info.patch_version();
|
||||
}
|
||||
catch(const io_exception& e) {
|
||||
cerr << "Error: " << e.getmsg() << endl;
|
||||
|
||||
close(fd);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
else if (server_info.patch_version() < 0) {
|
||||
cout << " (development version)";
|
||||
}
|
||||
cout << endl;
|
||||
|
||||
close(fd);
|
||||
|
||||
cout << "rascsi server version: " << serverInfo.rascsi_version() << endl;
|
||||
|
||||
if (!serverInfo.available_log_levels_size()) {
|
||||
if (!server_info.log_levels_size()) {
|
||||
cout << " No log level settings available" << endl;
|
||||
}
|
||||
else {
|
||||
cout << "Available rascsi log levels, sorted by severity:" << endl;
|
||||
for (int i = 0; i < serverInfo.available_log_levels_size(); i++) {
|
||||
cout << " " << serverInfo.available_log_levels(i) << endl;
|
||||
cout << "rascsi log levels, sorted by severity:" << endl;
|
||||
for (int i = 0; i < server_info.log_levels_size(); i++) {
|
||||
cout << " " << server_info.log_levels(i) << endl;
|
||||
}
|
||||
|
||||
cout << "Current rascsi log level: " << serverInfo.current_log_level() << endl;
|
||||
cout << "Current rascsi log level: " << server_info.current_log_level() << endl;
|
||||
}
|
||||
|
||||
cout << "Default image file folder: " << serverInfo.default_image_folder() << endl;
|
||||
if (!serverInfo.available_image_files_size()) {
|
||||
cout << " No image files available in the default folder" << endl;
|
||||
cout << "Default image file folder: " << server_info.default_image_folder() << endl;
|
||||
if (!server_info.image_files_size()) {
|
||||
cout << " No image files available" << endl;
|
||||
}
|
||||
else {
|
||||
list<string> sorted_files;
|
||||
for (int i = 0; i < serverInfo.available_image_files_size(); i++) {
|
||||
sorted_files.push_back(serverInfo.available_image_files(i).name());
|
||||
}
|
||||
sorted_files.sort();
|
||||
list<PbImageFile> files = { server_info.image_files().begin(), server_info.image_files().end() };
|
||||
files.sort([](const auto& a, const auto& b) { return a.name() < b.name(); });
|
||||
|
||||
cout << "Image files available in the default folder:" << endl;
|
||||
for (auto it = sorted_files.begin(); it != sorted_files.end(); ++it) {
|
||||
cout << " " << *it << endl;
|
||||
cout << "Available image files:" << endl;
|
||||
for (const auto& file : files) {
|
||||
cout << " " << file.name() << " (" << file.size() << " bytes)";
|
||||
if (file.read_only()) {
|
||||
cout << ", read-only";
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
|
||||
cout << "Supported device types and their properties:" << endl;
|
||||
for (auto it = server_info.types_properties().begin(); it != server_info.types_properties().end(); ++it) {
|
||||
cout << " " << PbDeviceType_Name(it->type());
|
||||
|
||||
const PbDeviceProperties& properties = it->properties();
|
||||
|
||||
cout << " Supported LUNs: " << properties.luns() << endl;
|
||||
|
||||
if (properties.read_only() || properties.protectable() || properties.stoppable() || properties.read_only()
|
||||
|| properties.lockable()) {
|
||||
cout << " Properties: ";
|
||||
bool has_property = false;
|
||||
if (properties.read_only()) {
|
||||
cout << "read-only";
|
||||
has_property = true;
|
||||
}
|
||||
if (properties.protectable()) {
|
||||
cout << (has_property ? ", " : "") << "protectable";
|
||||
has_property = true;
|
||||
}
|
||||
if (properties.stoppable()) {
|
||||
cout << (has_property ? ", " : "") << "stoppable";
|
||||
has_property = true;
|
||||
}
|
||||
if (properties.removable()) {
|
||||
cout << (has_property ? ", " : "") << "removable";
|
||||
has_property = true;
|
||||
}
|
||||
if (properties.lockable()) {
|
||||
cout << (has_property ? ", " : "") << "lockable";
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
if (properties.supports_file()) {
|
||||
cout << " Image file support" << endl;
|
||||
}
|
||||
else if (properties.supports_params()) {
|
||||
cout << " Parameter support" << endl;
|
||||
}
|
||||
|
||||
if (properties.supports_params() && properties.default_params_size()) {
|
||||
list<string> params = { properties.default_params().begin(), properties.default_params().end() };
|
||||
params.sort([](const auto& a, const auto& b) { return a < b; });
|
||||
|
||||
cout << " Default parameters: ";
|
||||
|
||||
bool isFirst = true;
|
||||
for (const auto& param : params) {
|
||||
if (!isFirst) {
|
||||
cout << ", ";
|
||||
}
|
||||
cout << param;
|
||||
|
||||
isFirst = false;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
if (properties.block_sizes_size()) {
|
||||
list<uint32_t> block_sizes = { properties.block_sizes().begin(), properties.block_sizes().end() };
|
||||
block_sizes.sort([](const auto& a, const auto& b) { return a < b; });
|
||||
|
||||
cout << " Configurable block sizes in bytes: ";
|
||||
|
||||
bool isFirst = true;
|
||||
for (const auto& block_size : block_sizes) {
|
||||
if (!isFirst) {
|
||||
cout << ", ";
|
||||
}
|
||||
cout << block_size;
|
||||
|
||||
isFirst = false;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
if (properties.capacities_size()) {
|
||||
list<uint64_t> capacities = { properties.capacities().begin(), properties.capacities().end() };
|
||||
capacities.sort([](const auto& a, const auto& b) { return a < b; });
|
||||
|
||||
cout << " Media capacities in bytes: ";
|
||||
|
||||
bool isFirst = true;
|
||||
for (const auto& capacity : capacities) {
|
||||
if (!isFirst) {
|
||||
cout << ", ";
|
||||
}
|
||||
cout << capacity;
|
||||
|
||||
isFirst = false;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (server_info.reserved_ids_size()) {
|
||||
cout << "Reserved device IDs: ";
|
||||
for (int i = 0; i < server_info.reserved_ids_size(); i++) {
|
||||
if(i) {
|
||||
cout << ", ";
|
||||
}
|
||||
cout << server_info.reserved_ids(i);
|
||||
}
|
||||
cout <<endl;
|
||||
}
|
||||
|
||||
if (server_info.devices_size()) {
|
||||
list<PbDevice> sorted_devices = { server_info.devices().begin(), server_info.devices().end() };
|
||||
sorted_devices.sort([](const auto& a, const auto& b) { return a.id() < b.id(); });
|
||||
|
||||
cout << "Attached devices:" << endl;
|
||||
|
||||
for (const auto& device : sorted_devices) {
|
||||
DisplayDeviceInfo(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,6 +415,9 @@ PbOperation ParseOperation(const char *optarg)
|
||||
case 'u':
|
||||
return UNPROTECT;
|
||||
|
||||
case 's':
|
||||
return DEVICE_INFO;
|
||||
|
||||
default:
|
||||
return NONE;
|
||||
}
|
||||
@ -241,16 +433,25 @@ PbDeviceType ParseType(const char *optarg)
|
||||
return type;
|
||||
}
|
||||
else {
|
||||
// Parse legacy types
|
||||
// Parse convenience types (shortcuts)
|
||||
switch (tolower(optarg[0])) {
|
||||
case 'm':
|
||||
return SCMO;
|
||||
|
||||
case 'c':
|
||||
return SCCD;
|
||||
|
||||
case 'b':
|
||||
return SCBR;
|
||||
|
||||
case 'd':
|
||||
return SCDP;
|
||||
|
||||
case 'h':
|
||||
return SCHD;
|
||||
|
||||
case 'm':
|
||||
return SCMO;
|
||||
|
||||
case 'r':
|
||||
return SCRM;
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,16 +471,19 @@ 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] [-n NAME] [-f FILE] [-d DEFAULT_IMAGE_FOLDER] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-v]" << endl;
|
||||
cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-b BLOCK_SIZE] [-n NAME] [-f FILE] ";
|
||||
cerr << "[-d DEFAULT_IMAGE_FOLDER] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] [-l] [-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 := {sahd|schd|scrm|sccd|scmo|scbr|scdp} or legacy types {hd|mo|cd|bridge}" << endl;
|
||||
cerr << " UNIT := {0|1}, default setting is 0." << endl;
|
||||
cerr << " CMD := {attach|detach|insert|eject|protect|unprotect|show}" << endl;
|
||||
cerr << " TYPE := {sahd|schd|scrm|sccd|scmo|scbr|scdp} or convenience type {hd|rm|mo|cd|bridge|daynaport}" << endl;
|
||||
cerr << " BLOCK_SIZE := {256|512|1024|2048|4096) bytes per hard disk drive block" << 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 << " RESERVED_IDS := comma-separated list of IDs to reserve" << endl;
|
||||
cerr << " LOG_LEVEL := log level {trace|debug|info|warn|err|critical|off}, default is 'trace'" << endl;
|
||||
cerr << " If CMD is 'attach' or 'insert' the FILE parameter is required." << endl;
|
||||
cerr << "Usage: " << argv[0] << " -l" << endl;
|
||||
@ -290,19 +494,19 @@ int main(int argc, char* argv[])
|
||||
|
||||
// Parse the arguments
|
||||
PbCommand command;
|
||||
PbDeviceDefinitions devices;
|
||||
command.set_allocated_devices(&devices);
|
||||
PbDeviceDefinition *device = devices.add_devices();
|
||||
list<PbDeviceDefinition> devices;
|
||||
PbDeviceDefinition* device = command.add_devices();
|
||||
device->set_id(-1);
|
||||
const char *hostname = "localhost";
|
||||
int port = 6868;
|
||||
string log_level;
|
||||
string default_folder;
|
||||
string reserved_ids;
|
||||
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) {
|
||||
while ((opt = getopt(argc, argv, "b:c:d:f:g:h:i:n:p:r:t:u:lsv")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
device->set_id(optarg[0] - '0');
|
||||
@ -312,21 +516,43 @@ int main(int argc, char* argv[])
|
||||
device->set_unit(optarg[0] - '0');
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
int block_size;
|
||||
if (!GetAsInt(optarg, block_size)) {
|
||||
cerr << "Error: Invalid block size " << optarg << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
device->set_block_size(block_size);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
command.set_cmd(ParseOperation(optarg));
|
||||
command.set_operation(ParseOperation(optarg));
|
||||
if (command.operation() == NONE) {
|
||||
cerr << "Error: Unknown operation '" << optarg << "'" << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
command.set_operation(DEFAULT_FOLDER);
|
||||
default_folder = optarg;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
device->add_params(optarg);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
device->set_type(ParseType(optarg));
|
||||
if (device->type() == UNDEFINED) {
|
||||
cerr << "Error: Unknown device type '" << optarg << "'" << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
device->set_file(optarg);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
command.set_cmd(DEFAULT_FOLDER);
|
||||
default_folder = optarg;
|
||||
case 'g':
|
||||
command.set_operation(LOG_LEVEL);
|
||||
log_level = optarg;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
@ -337,25 +563,49 @@ int main(int argc, char* argv[])
|
||||
list = true;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
device->set_name(optarg);
|
||||
case 'n': {
|
||||
string vendor;
|
||||
string product;
|
||||
string revision;
|
||||
|
||||
string s = optarg;
|
||||
size_t separatorPos = s.find(COMPONENT_SEPARATOR);
|
||||
if (separatorPos != string::npos) {
|
||||
vendor = s.substr(0, separatorPos);
|
||||
s = s.substr(separatorPos + 1);
|
||||
separatorPos = s.find(COMPONENT_SEPARATOR);
|
||||
if (separatorPos != string::npos) {
|
||||
product = s.substr(0, separatorPos);
|
||||
revision = s.substr(separatorPos + 1);
|
||||
}
|
||||
else {
|
||||
product = s;
|
||||
}
|
||||
}
|
||||
else {
|
||||
vendor = s;
|
||||
}
|
||||
|
||||
device->set_vendor(vendor);
|
||||
device->set_product(product);
|
||||
device->set_revision(revision);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
if (port <= 0 || port > 65535) {
|
||||
cerr << "Invalid port " << optarg << ", port must be between 1 and 65535" << endl;
|
||||
if (!GetAsInt(optarg, port) || port <= 0 || port > 65535) {
|
||||
cerr << "Error: Invalid port " << optarg << ", port must be between 1 and 65535" << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
command.set_cmd(LOG_LEVEL);
|
||||
log_level = optarg;
|
||||
case 'r':
|
||||
command.set_operation(RESERVE);
|
||||
reserved_ids = optarg;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
command.set_cmd(SERVER_INFO);
|
||||
command.set_operation(SERVER_INFO);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
@ -369,19 +619,29 @@ int main(int argc, char* argv[])
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (command.cmd() == LOG_LEVEL) {
|
||||
CommandLogLevel(hostname, port, log_level);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
switch(command.operation()) {
|
||||
case LOG_LEVEL:
|
||||
CommandLogLevel(hostname, port, log_level);
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
if (command.cmd() == DEFAULT_FOLDER) {
|
||||
CommandDefaultImageFolder(hostname, port, default_folder);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
case DEFAULT_FOLDER:
|
||||
CommandDefaultImageFolder(hostname, port, default_folder);
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
if (command.cmd() == SERVER_INFO) {
|
||||
CommandServerInfo(hostname, port);
|
||||
exit(EXIT_SUCCESS);
|
||||
case RESERVE:
|
||||
CommandReserve(hostname, port, reserved_ids);
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
case DEVICE_INFO:
|
||||
CommandDeviceInfo(hostname, port, command);
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
case SERVER_INFO:
|
||||
CommandServerInfo(hostname, port);
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (list) {
|
||||
@ -389,11 +649,8 @@ int main(int argc, char* argv[])
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// Send the command
|
||||
int fd = SendCommand(hostname, port, command);
|
||||
bool status = ReceiveResult(fd);
|
||||
close(fd);
|
||||
PbResult result;
|
||||
SendCommand(hostname, port, command, result);
|
||||
|
||||
// All done!
|
||||
exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
@ -10,10 +10,10 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "xm6.h"
|
||||
#include "fileio.h"
|
||||
#include "filepath.h"
|
||||
#include "gpiobus.h"
|
||||
#include "rascsi.h"
|
||||
#include "rascsi_version.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
#include "rascsi_interface.pb.h"
|
||||
#include "rasutil.h"
|
||||
@ -14,27 +15,40 @@
|
||||
using namespace std;
|
||||
using namespace rascsi_interface;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// List devices
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
string ListDevices(const PbDevices& devices)
|
||||
bool GetAsInt(const string& value, int& result)
|
||||
{
|
||||
ostringstream s;
|
||||
|
||||
if (devices.devices_size()) {
|
||||
s << "+----+----+------+-------------------------------------" << endl
|
||||
<< "| ID | UN | TYPE | DEVICE STATUS" << endl
|
||||
<< "+----+----+------+-------------------------------------" << endl;
|
||||
if (value.find_first_not_of("0123456789") != string::npos) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
|
||||
try {
|
||||
result = std::stoul(value);
|
||||
}
|
||||
catch(const invalid_argument& e) {
|
||||
return false;
|
||||
}
|
||||
catch(const out_of_range& e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string ListDevices(const list<PbDevice>& pb_devices)
|
||||
{
|
||||
if (pb_devices.empty()) {
|
||||
return "No images currently attached.";
|
||||
}
|
||||
|
||||
for (int i = 0; i < devices.devices_size() ; i++) {
|
||||
PbDevice device = devices.devices(i);
|
||||
ostringstream s;
|
||||
s << "+----+----+------+-------------------------------------" << endl
|
||||
<< "| ID | UN | TYPE | DEVICE STATUS" << endl
|
||||
<< "+----+----+------+-------------------------------------" << endl;
|
||||
|
||||
list<PbDevice> devices = pb_devices;
|
||||
devices.sort([](const auto& a, const auto& b) { return a.id() < b.id() && a.unit() < b.unit(); });
|
||||
|
||||
for (const auto& device : devices) {
|
||||
string filename;
|
||||
switch (device.type()) {
|
||||
case SCBR:
|
||||
@ -52,7 +66,8 @@ string ListDevices(const PbDevices& devices)
|
||||
|
||||
s << "| " << device.id() << " | " << device.unit() << " | " << PbDeviceType_Name(device.type()) << " | "
|
||||
<< (filename.empty() ? "NO MEDIA" : filename)
|
||||
<< (!device.removed() && (device.read_only() || device.protected_()) ? " (WRITEPROTECT)" : "") << endl;
|
||||
<< (!device.status().removed() && (device.properties().read_only() || device.status().protected_()) ? " (WRITEPROTECT)" : "")
|
||||
<< endl;
|
||||
}
|
||||
|
||||
s << "+----+----+------+-------------------------------------";
|
||||
|
@ -11,7 +11,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include "rascsi_interface.pb.h"
|
||||
|
||||
std::string ListDevices(const rascsi_interface::PbDevices&);
|
||||
bool GetAsInt(const std::string&, int&);
|
||||
std::string ListDevices(const std::list<rascsi_interface::PbDevice>&);
|
||||
|
@ -5,7 +5,7 @@
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
// Copyright (C) 2016-2020 GIMONS
|
||||
// [ HDDダンプユーティリティ(イニシーエタモード/SASI Version) ]
|
||||
// [ HDD dump utility (Initiator mode/SASI Version) ]
|
||||
//
|
||||
// SASI IMAGE EXAMPLE
|
||||
// X68000
|
||||
@ -19,39 +19,24 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "xm6.h"
|
||||
#include "fileio.h"
|
||||
#include "filepath.h"
|
||||
#include "gpiobus.h"
|
||||
#include "rascsi.h"
|
||||
#include "rascsi_version.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Constant declaration
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
#define BUFSIZE 1024 * 64 // 64KBぐらいかなぁ
|
||||
#define BUFSIZE 1024 * 64 // Maybe around 64KB?
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Variable declaration
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
GPIOBUS bus; // バス
|
||||
int targetid; // ターゲットデバイスID
|
||||
int unitid; // ターゲットユニットID
|
||||
int bsiz; // ブロックサイズ
|
||||
int bnum; // ブロック数
|
||||
Filepath hdffile; // HDFファイル
|
||||
bool restore; // リストアフラグ
|
||||
BYTE buffer[BUFSIZE]; // ワークバッファ
|
||||
int result; // 結果コード
|
||||
GPIOBUS bus;
|
||||
int targetid;
|
||||
int unitid;
|
||||
int bsiz; // Block size
|
||||
int bnum; // Number of blocks
|
||||
Filepath hdffile; // HDF file
|
||||
bool restore; // Restore flag
|
||||
BYTE buffer[BUFSIZE]; // Work buffer
|
||||
int result; // Result code
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Function declaration
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Cleanup();
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -73,7 +58,7 @@ void KillHandler(int sig)
|
||||
//---------------------------------------------------------------------------
|
||||
bool Banner(int argc, char* argv[])
|
||||
{
|
||||
printf("RaSCSI hard disk dump utility(SASI HDD) ");
|
||||
printf("RaSCSI hard disk dump utility (SASI HDD) ");
|
||||
printf("version %s (%s, %s)\n",
|
||||
rascsi_get_version_string(),
|
||||
__DATE__,
|
||||
@ -93,14 +78,9 @@ bool Banner(int argc, char* argv[])
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Initialization
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool Init()
|
||||
{
|
||||
// 割り込みハンドラ設定
|
||||
// Interrupt handler settings
|
||||
if (signal(SIGINT, KillHandler) == SIG_ERR) {
|
||||
return false;
|
||||
}
|
||||
@ -111,12 +91,12 @@ bool Init()
|
||||
return false;
|
||||
}
|
||||
|
||||
// GPIO初期化
|
||||
// GPIO initialization
|
||||
if (!bus.Init(BUS::INITIATOR)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ワーク初期化
|
||||
// Work initialization
|
||||
targetid = -1;
|
||||
unitid = 0;
|
||||
bsiz = 256;
|
||||
@ -126,42 +106,26 @@ bool Init()
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// クリーンアップ
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Cleanup()
|
||||
{
|
||||
// バスをクリーンアップ
|
||||
bus.Cleanup();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Reset
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Reset()
|
||||
{
|
||||
// Reset bus signal line
|
||||
bus.Reset();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Argument processing
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool ParseArgument(int argc, char* argv[])
|
||||
{
|
||||
int opt;
|
||||
char *file;
|
||||
|
||||
// 初期化
|
||||
// Initialization
|
||||
file = NULL;
|
||||
|
||||
// 引数解析
|
||||
// Argument parsing
|
||||
opterr = 0;
|
||||
while ((opt = getopt(argc, argv, "i:u:b:c:f:r")) != -1) {
|
||||
switch (opt) {
|
||||
@ -191,35 +155,35 @@ bool ParseArgument(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// TARGET IDチェック
|
||||
// TARGET ID check
|
||||
if (targetid < 0 || targetid > 7) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid target id range\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// UNIT IDチェック
|
||||
// UNIT ID check
|
||||
if (unitid < 0 || unitid > 1) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid unit id range\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// BSIZチェック
|
||||
// BSIZ check
|
||||
if (bsiz != 256 && bsiz != 512 && bsiz != 1024) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid block size\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// BNUMチェック
|
||||
// BNUM check
|
||||
if (bnum < 0) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid block count\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ファイルチェック
|
||||
// File check
|
||||
if (!file) {
|
||||
fprintf(stderr,
|
||||
"Error : Invalid file path\n");
|
||||
@ -231,16 +195,11 @@ bool ParseArgument(int argc, char* argv[])
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Waiting for phase
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool WaitPhase(BUS::phase_t phase)
|
||||
{
|
||||
DWORD now;
|
||||
|
||||
// タイムアウト(3000ms)
|
||||
// Timeout (3000ms)
|
||||
now = SysTimer::GetTimerLow();
|
||||
while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) {
|
||||
bus.Aquire();
|
||||
@ -254,12 +213,11 @@ bool WaitPhase(BUS::phase_t phase)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// バスフリーフェーズ実行
|
||||
// Bus free phase execution
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void BusFree()
|
||||
{
|
||||
// バスリセット
|
||||
bus.Reset();
|
||||
}
|
||||
|
||||
@ -273,13 +231,13 @@ bool Selection(int id)
|
||||
BYTE data;
|
||||
int count;
|
||||
|
||||
// ID設定とSELアサート
|
||||
// ID setting and SEL assertion
|
||||
data = 0;
|
||||
data |= (1 << id);
|
||||
bus.SetDAT(data);
|
||||
bus.SetSEL(TRUE);
|
||||
|
||||
// BSYを待つ
|
||||
// Wait for BSY
|
||||
count = 10000;
|
||||
do {
|
||||
usleep(20);
|
||||
@ -289,10 +247,10 @@ bool Selection(int id)
|
||||
}
|
||||
} while (count--);
|
||||
|
||||
// SELネゲート
|
||||
// SEL negate
|
||||
bus.SetSEL(FALSE);
|
||||
|
||||
// ターゲットがビジー状態なら成功
|
||||
// Return true if target is busy
|
||||
return bus.GetBSY();
|
||||
}
|
||||
|
||||
@ -305,20 +263,20 @@ bool Command(BYTE *buf, int length)
|
||||
{
|
||||
int count;
|
||||
|
||||
// フェーズ待ち
|
||||
// Wait for phase
|
||||
if (!WaitPhase(BUS::command)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// コマンド送信
|
||||
// Send command
|
||||
count = bus.SendHandShake(buf, length);
|
||||
|
||||
// 送信結果が依頼数と同じなら成功
|
||||
// Return true is send results match number of requests
|
||||
if (count == length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 送信エラー
|
||||
// Send error
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -329,12 +287,12 @@ bool Command(BYTE *buf, int length)
|
||||
//---------------------------------------------------------------------------
|
||||
int DataIn(BYTE *buf, int length)
|
||||
{
|
||||
// フェーズ待ち
|
||||
// Wait for phase
|
||||
if (!WaitPhase(BUS::datain)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// データ受信
|
||||
// Receive data
|
||||
return bus.ReceiveHandShake(buf, length);
|
||||
}
|
||||
|
||||
@ -345,80 +303,78 @@ int DataIn(BYTE *buf, int length)
|
||||
//---------------------------------------------------------------------------
|
||||
int DataOut(BYTE *buf, int length)
|
||||
{
|
||||
// フェーズ待ち
|
||||
// Wait for phase
|
||||
if (!WaitPhase(BUS::dataout)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// データ受信
|
||||
// Receive data
|
||||
return bus.SendHandShake(buf, length);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// ステータスフェーズ実行
|
||||
// Status phase execution
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int Status()
|
||||
{
|
||||
BYTE buf[256];
|
||||
|
||||
// フェーズ待ち
|
||||
// Wait for phase
|
||||
if (!WaitPhase(BUS::status)) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
// データ受信
|
||||
// Receive data
|
||||
if (bus.ReceiveHandShake(buf, 1) == 1) {
|
||||
return (int)buf[0];
|
||||
}
|
||||
|
||||
// 受信エラー
|
||||
// Receive error
|
||||
return -1;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// メッセージインフェーズ実行
|
||||
// Message in phase execution
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int MessageIn()
|
||||
{
|
||||
BYTE buf[256];
|
||||
|
||||
// フェーズ待ち
|
||||
// Wait for phase
|
||||
if (!WaitPhase(BUS::msgin)) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
// データ受信
|
||||
// Receive data
|
||||
if (bus.ReceiveHandShake(buf, 1) == 1) {
|
||||
return (int)buf[0];
|
||||
}
|
||||
|
||||
// 受信エラー
|
||||
// Receive error
|
||||
return -1;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// TEST UNIT READY実行
|
||||
// TEST UNIT READY execution
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int TestUnitReady(int id)
|
||||
{
|
||||
BYTE cmd[256];
|
||||
|
||||
// 結果コード初期化
|
||||
// Initialize result code
|
||||
result = 0;
|
||||
|
||||
// SELECTION
|
||||
if (!Selection(id)) {
|
||||
result = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// COMMAND
|
||||
memset(cmd, 0x00, 6);
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = unitid << 5;
|
||||
@ -427,20 +383,17 @@ int TestUnitReady(int id)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// STATUS
|
||||
if (Status() < 0) {
|
||||
result = -4;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// MESSAGE IN
|
||||
if (MessageIn() < 0) {
|
||||
result = -5;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
// バスフリー
|
||||
BusFree();
|
||||
|
||||
return result;
|
||||
@ -448,7 +401,7 @@ exit:
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// REQUEST SENSE実行
|
||||
// REQUEST SENSE execution
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int RequestSense(int id, BYTE *buf)
|
||||
@ -456,17 +409,15 @@ int RequestSense(int id, BYTE *buf)
|
||||
BYTE cmd[256];
|
||||
int count;
|
||||
|
||||
// 結果コード初期化
|
||||
// Initialize result codes
|
||||
result = 0;
|
||||
count = 0;
|
||||
|
||||
// SELECTION
|
||||
if (!Selection(id)) {
|
||||
result = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// COMMAND
|
||||
memset(cmd, 0x00, 6);
|
||||
cmd[0] = 0x03;
|
||||
cmd[1] = unitid << 5;
|
||||
@ -476,7 +427,6 @@ int RequestSense(int id, BYTE *buf)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// DATAIN
|
||||
memset(buf, 0x00, 256);
|
||||
count = DataIn(buf, 256);
|
||||
if (count <= 0) {
|
||||
@ -484,23 +434,20 @@ int RequestSense(int id, BYTE *buf)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// STATUS
|
||||
if (Status() < 0) {
|
||||
result = -4;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// MESSAGE IN
|
||||
if (MessageIn() < 0) {
|
||||
result = -5;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
// バスフリー
|
||||
BusFree();
|
||||
|
||||
// 成功であれば転送数を返す
|
||||
// If successful, return number of transfers
|
||||
if (result == 0) {
|
||||
return count;
|
||||
}
|
||||
@ -510,7 +457,7 @@ exit:
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// READ6実行
|
||||
// READ6 execution
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
|
||||
@ -518,17 +465,15 @@ int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
|
||||
BYTE cmd[256];
|
||||
int count;
|
||||
|
||||
// 結果コード初期化
|
||||
// Initialize result codes
|
||||
result = 0;
|
||||
count = 0;
|
||||
|
||||
// SELECTION
|
||||
if (!Selection(id)) {
|
||||
result = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// COMMAND
|
||||
memset(cmd, 0x00, 10);
|
||||
cmd[0] = 0x8;
|
||||
cmd[1] = (BYTE)(bstart >> 16);
|
||||
@ -542,30 +487,26 @@ int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// DATAIN
|
||||
count = DataIn(buf, length);
|
||||
if (count <= 0) {
|
||||
result = -3;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// STATUS
|
||||
if (Status() < 0) {
|
||||
result = -4;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// MESSAGE IN
|
||||
if (MessageIn() < 0) {
|
||||
result = -5;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
// バスフリー
|
||||
BusFree();
|
||||
|
||||
// 成功であれば転送数を返す
|
||||
// If successful, return number of transfers
|
||||
if (result == 0) {
|
||||
return count;
|
||||
}
|
||||
@ -575,7 +516,7 @@ exit:
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// WRITE6実行
|
||||
// WRITE6 execution
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
|
||||
@ -583,17 +524,15 @@ int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
|
||||
BYTE cmd[256];
|
||||
int count;
|
||||
|
||||
// 結果コード初期化
|
||||
// Initialize result codes
|
||||
result = 0;
|
||||
count = 0;
|
||||
|
||||
// SELECTION
|
||||
if (!Selection(id)) {
|
||||
result = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// COMMAND
|
||||
memset(cmd, 0x00, 10);
|
||||
cmd[0] = 0xa;
|
||||
cmd[1] = (BYTE)(bstart >> 16);
|
||||
@ -607,30 +546,26 @@ int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// DATAOUT
|
||||
count = DataOut(buf, length);
|
||||
if (count <= 0) {
|
||||
result = -3;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// STATUS
|
||||
if (Status() < 0) {
|
||||
result = -4;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// MESSAGE IN
|
||||
if (MessageIn() < 0) {
|
||||
result = -5;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
// バスフリー
|
||||
BusFree();
|
||||
|
||||
// 成功であれば転送数を返す
|
||||
// If successful, return number of transfers
|
||||
if (result == 0) {
|
||||
return count;
|
||||
}
|
||||
@ -640,7 +575,7 @@ exit:
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 主処理
|
||||
// Main processing
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int main(int argc, char* argv[])
|
||||
@ -654,32 +589,28 @@ int main(int argc, char* argv[])
|
||||
Fileio::OpenMode omode;
|
||||
off_t size;
|
||||
|
||||
// バナー出力
|
||||
// Output banner
|
||||
if (!Banner(argc, argv)) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// 初期化
|
||||
if (!Init()) {
|
||||
fprintf(stderr, "Error : Initializing\n");
|
||||
|
||||
// 恐らくrootでは無い?
|
||||
// Probably not executing as root?
|
||||
exit(EPERM);
|
||||
}
|
||||
|
||||
// 構築
|
||||
if (!ParseArgument(argc, argv)) {
|
||||
// クリーンアップ
|
||||
Cleanup();
|
||||
|
||||
// 引数エラーで終了
|
||||
// Exit with argument error
|
||||
exit(EINVAL);
|
||||
}
|
||||
|
||||
// リセット
|
||||
Reset();
|
||||
|
||||
// ファイルオープン
|
||||
// Open file
|
||||
if (restore) {
|
||||
omode = Fileio::ReadOnly;
|
||||
} else {
|
||||
@ -688,20 +619,18 @@ int main(int argc, char* argv[])
|
||||
if (!fio.Open(hdffile.GetPath(), omode)) {
|
||||
fprintf(stderr, "Error : Can't open hdf file\n");
|
||||
|
||||
// クリーンアップ
|
||||
Cleanup();
|
||||
exit(EPERM);
|
||||
}
|
||||
|
||||
// バスフリー
|
||||
BusFree();
|
||||
|
||||
// RESETシグナル発行
|
||||
// Execute RESET signal
|
||||
bus.SetRST(TRUE);
|
||||
usleep(1000);
|
||||
bus.SetRST(FALSE);
|
||||
|
||||
// ダンプ開始
|
||||
// Start dump
|
||||
printf("TARGET ID : %d\n", targetid);
|
||||
printf("UNIT ID : %d\n", unitid);
|
||||
|
||||
@ -712,23 +641,22 @@ int main(int argc, char* argv[])
|
||||
goto cleanup_exit;
|
||||
}
|
||||
|
||||
// REQUEST SENSE(for CHECK CONDITION)
|
||||
// REQUEST SENSE (for CHECK CONDITION)
|
||||
count = RequestSense(targetid, buffer);
|
||||
if (count < 0) {
|
||||
fprintf(stderr, "REQUEST SENSE ERROR %d\n", count);
|
||||
goto cleanup_exit;
|
||||
}
|
||||
|
||||
// ブロックサイズとブロック数の表示
|
||||
printf("Number of blocks : %d Blocks\n", bnum);
|
||||
printf("Block length : %d Bytes\n", bsiz);
|
||||
|
||||
// データサイズの表示
|
||||
// Display data size
|
||||
printf("Total length : %d MBytes %d Bytes\n",
|
||||
(bsiz * bnum / 1024 / 1024),
|
||||
(bsiz * bnum));
|
||||
|
||||
// リストアファイルサイズの取得
|
||||
// Get restore file size
|
||||
if (restore) {
|
||||
size = fio.GetFileSize();
|
||||
printf("Restore file size : %d bytes", (int)size);
|
||||
@ -741,7 +669,7 @@ int main(int argc, char* argv[])
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// バッファサイズ毎にダンプする
|
||||
// Dump by buffer size
|
||||
duni = BUFSIZE;
|
||||
duni /= bsiz;
|
||||
dsiz = BUFSIZE;
|
||||
@ -789,7 +717,7 @@ int main(int argc, char* argv[])
|
||||
printf("\033[0K");
|
||||
}
|
||||
|
||||
// 容量上の端数処理
|
||||
// Rounding of capacity
|
||||
dnum = bnum % duni;
|
||||
dsiz = dnum * bsiz;
|
||||
|
||||
@ -805,16 +733,13 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 完了メッセージ
|
||||
// Completion message
|
||||
printf("%3d%%(%7d/%7d)\n", 100, bnum, bnum);
|
||||
|
||||
cleanup_exit:
|
||||
// ファイルクローズ
|
||||
fio.Close();
|
||||
|
||||
// クリーンアップ
|
||||
Cleanup();
|
||||
|
||||
// 終了
|
||||
exit(0);
|
||||
}
|
||||
|
@ -10,8 +10,8 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "xm6.h"
|
||||
#include "scsi.h"
|
||||
#include "rascsi.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
@ -9,8 +9,7 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#if !defined(scsi_h)
|
||||
#define scsi_h
|
||||
#pragma once
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
@ -65,41 +64,35 @@ public:
|
||||
MONITOR = 2,
|
||||
};
|
||||
|
||||
// Phase definition
|
||||
// Phase definitions
|
||||
enum phase_t : BYTE {
|
||||
busfree, // バスフリーフェーズ
|
||||
arbitration, // アービトレーションフェーズ
|
||||
selection, // セレクションフェーズ
|
||||
reselection, // リセレクションフェーズ
|
||||
command, // コマンドフェーズ
|
||||
execute, // 実行フェーズ Execute is an extension of the command phase
|
||||
datain, // データイン
|
||||
dataout, // データアウト
|
||||
status, // ステータスフェーズ
|
||||
msgin, // メッセージフェーズ
|
||||
msgout, // メッセージアウトフェーズ
|
||||
reserved // 未使用/リザーブ
|
||||
busfree,
|
||||
arbitration,
|
||||
selection,
|
||||
reselection,
|
||||
command,
|
||||
execute, // Execute phase is an extension of the command phase
|
||||
datain,
|
||||
dataout,
|
||||
status,
|
||||
msgin,
|
||||
msgout,
|
||||
reserved // Unused
|
||||
};
|
||||
|
||||
BUS() { };
|
||||
virtual ~BUS() { };
|
||||
|
||||
// Basic Functions
|
||||
// 基本ファンクション
|
||||
virtual BOOL Init(mode_e mode) = 0;
|
||||
// 初期化
|
||||
virtual void Reset() = 0;
|
||||
// リセット
|
||||
virtual void Cleanup() = 0;
|
||||
// クリーンアップ
|
||||
phase_t GetPhase();
|
||||
// フェーズ取得
|
||||
|
||||
static phase_t GetPhase(DWORD mci)
|
||||
{
|
||||
return phase_table[mci];
|
||||
}
|
||||
// フェーズ取得
|
||||
|
||||
static const char* GetPhaseStrRaw(phase_t current_phase);
|
||||
// Get the string phase name, based upon the raw data
|
||||
@ -111,64 +104,39 @@ public:
|
||||
}
|
||||
|
||||
virtual bool GetBSY() = 0;
|
||||
// BSYシグナル取得
|
||||
virtual void SetBSY(bool ast) = 0;
|
||||
// BSYシグナル設定
|
||||
|
||||
virtual BOOL GetSEL() = 0;
|
||||
// SELシグナル取得
|
||||
virtual void SetSEL(BOOL ast) = 0;
|
||||
// SELシグナル設定
|
||||
|
||||
virtual BOOL GetATN() = 0;
|
||||
// ATNシグナル取得
|
||||
virtual void SetATN(BOOL ast) = 0;
|
||||
// ATNシグナル設定
|
||||
|
||||
virtual BOOL GetACK() = 0;
|
||||
// ACKシグナル取得
|
||||
virtual void SetACK(BOOL ast) = 0;
|
||||
// ACKシグナル設定
|
||||
|
||||
virtual BOOL GetRST() = 0;
|
||||
// RSTシグナル取得
|
||||
virtual void SetRST(BOOL ast) = 0;
|
||||
// RSTシグナル設定
|
||||
|
||||
virtual BOOL GetMSG() = 0;
|
||||
// MSGシグナル取得
|
||||
virtual void SetMSG(BOOL ast) = 0;
|
||||
// MSGシグナル設定
|
||||
|
||||
virtual BOOL GetCD() = 0;
|
||||
// CDシグナル取得
|
||||
virtual void SetCD(BOOL ast) = 0;
|
||||
// CDシグナル設定
|
||||
|
||||
virtual BOOL GetIO() = 0;
|
||||
// IOシグナル取得
|
||||
virtual void SetIO(BOOL ast) = 0;
|
||||
// IOシグナル設定
|
||||
|
||||
virtual BOOL GetREQ() = 0;
|
||||
// REQシグナル取得
|
||||
virtual void SetREQ(BOOL ast) = 0;
|
||||
// REQシグナル設定
|
||||
|
||||
virtual BYTE GetDAT() = 0;
|
||||
// データシグナル取得
|
||||
virtual void SetDAT(BYTE dat) = 0;
|
||||
// データシグナル設定
|
||||
virtual BOOL GetDP() = 0;
|
||||
// パリティシグナル取得
|
||||
virtual BOOL GetDP() = 0; // Get parity signal
|
||||
|
||||
virtual int CommandHandShake(BYTE *buf) = 0;
|
||||
// コマンド受信ハンドシェイク
|
||||
virtual int ReceiveHandShake(BYTE *buf, int count) = 0;
|
||||
// データ受信ハンドシェイク
|
||||
virtual int SendHandShake(BYTE *buf, int count, int delay_after_bytes) = 0;
|
||||
// データ送信ハンドシェイク
|
||||
|
||||
|
||||
virtual BOOL GetSignal(int pin) = 0;
|
||||
// Get SCSI input signal value
|
||||
@ -181,89 +149,6 @@ protected:
|
||||
|
||||
private:
|
||||
static const phase_t phase_table[8];
|
||||
// フェーズテーブル
|
||||
|
||||
static const char* phase_str_table[];
|
||||
};
|
||||
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE misc_cdb_information;
|
||||
BYTE page_code;
|
||||
WORD length;
|
||||
BYTE control;
|
||||
} scsi_cdb_6_byte_t;
|
||||
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE service_action;
|
||||
DWORD logical_block_address;
|
||||
BYTE misc_cdb_information;
|
||||
WORD length;
|
||||
BYTE control;
|
||||
} scsi_cdb_10_byte_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE service_action;
|
||||
DWORD logical_block_address;
|
||||
DWORD length;
|
||||
BYTE misc_cdb_information;
|
||||
BYTE control;
|
||||
} scsi_cdb_12_byte_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE service_action;
|
||||
DWORD logical_block_address;
|
||||
DWORD length;
|
||||
BYTE misc_cdb_information;
|
||||
BYTE control;
|
||||
} scsi_cdb_16_byte_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE reserved;
|
||||
BYTE page_code;
|
||||
WORD allocation_length;
|
||||
BYTE control;
|
||||
} scsi_cmd_inquiry_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE lba_msb_bits_4_0;
|
||||
WORD logical_block_address;
|
||||
BYTE transfer_length;
|
||||
BYTE control;
|
||||
} scsi_cmd_read_6_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE flags;
|
||||
DWORD logical_block_address;
|
||||
BYTE group_number;
|
||||
WORD transfer_length;
|
||||
BYTE control;
|
||||
} scsi_cmd_read_10_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE flags;
|
||||
DWORD logical_block_address;
|
||||
DWORD transfer_length;
|
||||
BYTE group_number;
|
||||
BYTE control;
|
||||
} scsi_cmd_read_12_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BYTE operation_code;
|
||||
BYTE descriptor_format;
|
||||
WORD reserved;
|
||||
BYTE allocation_length;
|
||||
BYTE control;
|
||||
} scsi_cmd_request_sense_t;
|
||||
|
||||
|
||||
#endif // scsi_h
|
||||
|
@ -10,7 +10,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "xm6.h"
|
||||
#include "filepath.h"
|
||||
#include "fileio.h"
|
||||
#include "devices/disk.h"
|
||||
@ -21,6 +20,7 @@
|
||||
#include <sys/time.h>
|
||||
#include <climits>
|
||||
#include <sstream>
|
||||
#include "rascsi.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
@ -57,7 +57,7 @@ static volatile bool running; // Running flag
|
||||
GPIOBUS *bus; // GPIO Bus
|
||||
typedef struct data_capture{
|
||||
DWORD data;
|
||||
QWORD timestamp;
|
||||
uint64_t timestamp;
|
||||
} data_capture_t;
|
||||
|
||||
data_capture data_buffer[MAX_BUFF_SIZE];
|
||||
@ -266,7 +266,7 @@ void create_value_change_dump()
|
||||
while(i < data_idx)
|
||||
{
|
||||
ostringstream s;
|
||||
s << (QWORD)(data_buffer[i].timestamp*ns_per_loop);
|
||||
s << (uint64_t)(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);
|
||||
@ -362,9 +362,9 @@ int main(int argc, char* argv[])
|
||||
int ret;
|
||||
struct sched_param schparam;
|
||||
timeval start_time, stop_time;
|
||||
QWORD loop_count = 0;
|
||||
uint64_t loop_count = 0;
|
||||
timeval time_diff;
|
||||
QWORD elapsed_us;
|
||||
uint64_t elapsed_us;
|
||||
int str_len;
|
||||
|
||||
// If there is an argument specified and it is NOT -h or --help
|
||||
|
@ -2,8 +2,15 @@ import fnmatch
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
import io
|
||||
import re
|
||||
import sys
|
||||
|
||||
from ractl_cmds import attach_image
|
||||
from ractl_cmds import (
|
||||
attach_image,
|
||||
detach_all,
|
||||
list_devices,
|
||||
)
|
||||
from settings import *
|
||||
|
||||
|
||||
@ -19,10 +26,9 @@ def create_new_image(file_name, type, size):
|
||||
)
|
||||
|
||||
|
||||
def delete_image(file_name):
|
||||
full_path = base_dir + file_name
|
||||
if os.path.exists(full_path):
|
||||
os.remove(full_path)
|
||||
def delete_file(file_name):
|
||||
if os.path.exists(file_name):
|
||||
os.remove(file_name)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@ -71,3 +77,49 @@ def download_image(url):
|
||||
full_path = base_dir + file_name
|
||||
|
||||
urllib.request.urlretrieve(url, full_path)
|
||||
|
||||
def write_config_csv(file_name):
|
||||
import csv
|
||||
|
||||
# This method takes the output of 'rasctl -l' and parses it into csv format:
|
||||
# 0: ID
|
||||
# 1: Unit Number (unused in rascsi-web)
|
||||
# 2: Device Type
|
||||
# 3: Device Status (includes the path to a loaded image file)
|
||||
# TODO: Remove the dependence on rasctl; e.g. when implementing protobuf for rascsi-web
|
||||
try:
|
||||
with open(file_name, "w") as csv_file:
|
||||
writer = csv.writer(csv_file)
|
||||
for device in list_devices():
|
||||
if device["type"] != "-":
|
||||
device_info = list (device.values())
|
||||
# Match a *nix file path inside column 3, cutting out the last chunk that starts with a space
|
||||
filesearch = re.search("(^(/[^/ ]*)+)(\s.*)*$", device_info[3])
|
||||
if filesearch is None:
|
||||
device_info[3] = ""
|
||||
else:
|
||||
device_info[3] = filesearch.group(1)
|
||||
writer.writerow(device_info)
|
||||
return True
|
||||
except:
|
||||
print ("Could not open file for writing: ", file_name)
|
||||
return False
|
||||
|
||||
def read_config_csv(file_name):
|
||||
import csv
|
||||
|
||||
try:
|
||||
with open(file_name) as csv_file:
|
||||
detach_all()
|
||||
config_reader = csv.reader(csv_file)
|
||||
#TODO: Remove hard-coded string sanitation (e.g. after implementing protobuf)
|
||||
exclude_list = ("X68000 HOST BRIDGE", "DaynaPort SCSI/Link", " (WRITEPROTECT)", "NO MEDIA")
|
||||
for row in config_reader:
|
||||
image_name = row[3]
|
||||
for e in exclude_list:
|
||||
image_name = image_name.replace(e, "")
|
||||
attach_image(row[0], image_name, row[2])
|
||||
return True
|
||||
except:
|
||||
print ("Could not access file: ", file_name)
|
||||
return False
|
||||
|
@ -1,14 +1,13 @@
|
||||
import fnmatch
|
||||
import subprocess
|
||||
import re
|
||||
import logging
|
||||
|
||||
from settings import *
|
||||
|
||||
|
||||
valid_file_suffix = ["*.hda", "*.hdn", "*.hdi", "*.nhd", "*.hdf", "*.hds", "*.hdr", "*.iso", "*.cdr", "*.toast", "*.img", "*.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]
|
||||
|
||||
|
||||
def is_active():
|
||||
@ -46,17 +45,18 @@ def list_config_files():
|
||||
return files_list
|
||||
|
||||
|
||||
def get_valid_scsi_ids(devices):
|
||||
invalid_list = EXCLUDE_SCSI_IDS.copy()
|
||||
def get_valid_scsi_ids(devices, invalid_list):
|
||||
for device in devices:
|
||||
if device["file"] != "NO MEDIA" and device["file"] != "-":
|
||||
invalid_list.append(int(device["id"]))
|
||||
|
||||
valid_list = list(range(8))
|
||||
for id in invalid_list:
|
||||
valid_list.remove(id)
|
||||
try:
|
||||
valid_list.remove(int(id))
|
||||
except:
|
||||
logging.warning("Invalid SCSI id " + str(id))
|
||||
valid_list.reverse()
|
||||
|
||||
return valid_list
|
||||
|
||||
|
||||
@ -79,7 +79,7 @@ def detach_by_id(scsi_id):
|
||||
|
||||
|
||||
def detach_all():
|
||||
for scsi_id in range(0, 7):
|
||||
for scsi_id in range(0, 8):
|
||||
subprocess.run(["rasctl", "-c" "detach", "-i", str(scsi_id)])
|
||||
|
||||
|
||||
@ -158,3 +158,7 @@ def list_devices():
|
||||
device_list[idx]["file"] = segments[4].strip()
|
||||
|
||||
return device_list
|
||||
|
||||
def reserve_scsi_ids(reserved_scsi_ids):
|
||||
scsi_ids = ",".join(list(reserved_scsi_ids))
|
||||
return subprocess.run(["rasctl", "-r", scsi_ids])
|
||||
|
@ -5,10 +5,12 @@ After=network.target
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
ExecStart=/home/pi/RASCSI/src/web/start.sh
|
||||
ExecStart=/home/pi/RASCSI/src/web/start.sh --reserved_ids=7
|
||||
# Use the --reserved_ids argument to define an id or range of ids that will be unavailable for attaching devices.
|
||||
# The default is 7 for Macintosh. Ex. to reserve IDs 0, 1, and 7 do: --reserved_ids=017
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
SyslogIdentifier=RASCSIWEB
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=multi-user.target
|
||||
|
@ -2,6 +2,22 @@
|
||||
set -e
|
||||
# set -x # Uncomment to Debug
|
||||
|
||||
# parse arguments
|
||||
while [ "$1" != "" ]; do
|
||||
PARAM=`echo $1 | awk -F= '{print $1}'`
|
||||
VALUE=`echo $1 | awk -F= '{print $2}'`
|
||||
case $PARAM in
|
||||
-r | --reserved_ids)
|
||||
RESERVED_IDS=$VALUE
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unknown parameter \"$PARAM\""
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
cd $(dirname $0)
|
||||
# verify packages installed
|
||||
ERROR=0
|
||||
@ -56,4 +72,4 @@ else
|
||||
fi
|
||||
|
||||
echo "Starting web server..."
|
||||
python3 web.py
|
||||
python3 web.py ${RESERVED_IDS}
|
||||
|
@ -21,14 +21,19 @@
|
||||
|
||||
{% block content %}
|
||||
<h2>Current RaSCSI Configuration</h2>
|
||||
<p>The <em>default</em> configuration will be loaded when the Web UI starts up.</p>
|
||||
<p>
|
||||
<form action="/config/load" method="post">
|
||||
<select name="name" >
|
||||
{% for config in config_files %}
|
||||
<option value="{{config}}">{{config.replace(".csv", '')}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="Load" />
|
||||
<input type="submit" name="load" value="Load" />
|
||||
<input type="submit" name="delete" value="Delete" />
|
||||
</form>
|
||||
</p>
|
||||
<p>
|
||||
<form action="/config/save" method="post">
|
||||
<input name="name" placeholder="default">
|
||||
<input type="submit" value="Save" />
|
||||
@ -36,6 +41,7 @@
|
||||
<form action="/scsi/detach_all" method="post" onsubmit="return confirm('Detach all SCSI Devices?')">
|
||||
<input type="submit" value="Detach All" />
|
||||
</form>
|
||||
</p>
|
||||
|
||||
<table cellpadding="3" border="black">
|
||||
<tbody>
|
||||
@ -47,7 +53,7 @@
|
||||
</tr>
|
||||
{% for device in devices %}
|
||||
<tr>
|
||||
{% if device.id != "7" %}
|
||||
{% if device.id not in reserved_scsi_ids %}
|
||||
<td style="text-align:center">{{device.id}}</td>
|
||||
<td style="text-align:center">{{device.type}}</td>
|
||||
<td>{{device.file}}</td>
|
||||
@ -67,7 +73,7 @@
|
||||
{% else %}
|
||||
<td style="text-align:center">{{device.id}}</td>
|
||||
<td style="text-align:center">-</td>
|
||||
<td>Host Machine</td>
|
||||
<td>Reserved ID</td>
|
||||
<td>-</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
|
@ -1,14 +1,17 @@
|
||||
import io
|
||||
import re
|
||||
import sys
|
||||
|
||||
from flask import Flask, render_template, request, flash, url_for, redirect, send_file, send_from_directory
|
||||
|
||||
from file_cmds import (
|
||||
create_new_image,
|
||||
download_file_to_iso,
|
||||
delete_image,
|
||||
delete_file,
|
||||
unzip_file,
|
||||
download_image,
|
||||
write_config_csv,
|
||||
read_config_csv,
|
||||
)
|
||||
from pi_cmds import shutdown_pi, reboot_pi, running_version, rascsi_service
|
||||
from ractl_cmds import (
|
||||
@ -25,7 +28,8 @@ from ractl_cmds import (
|
||||
list_config_files,
|
||||
detach_all,
|
||||
valid_file_suffix,
|
||||
valid_file_types
|
||||
valid_file_types,
|
||||
reserve_scsi_ids,
|
||||
)
|
||||
from settings import *
|
||||
|
||||
@ -35,7 +39,8 @@ app = Flask(__name__)
|
||||
@app.route("/")
|
||||
def index():
|
||||
devices = list_devices()
|
||||
scsi_ids = get_valid_scsi_ids(devices)
|
||||
reserved_scsi_ids = app.config.get("RESERVED_SCSI_IDS")
|
||||
scsi_ids = get_valid_scsi_ids(devices, list(reserved_scsi_ids))
|
||||
return render_template(
|
||||
"index.html",
|
||||
bridge_configured=is_bridge_setup("eth0"),
|
||||
@ -45,6 +50,7 @@ def index():
|
||||
config_files=list_config_files(),
|
||||
base_dir=base_dir,
|
||||
scsi_ids=scsi_ids,
|
||||
reserved_scsi_ids=reserved_scsi_ids,
|
||||
max_file_size=MAX_FILE_SIZE,
|
||||
version=running_version(),
|
||||
)
|
||||
@ -57,30 +63,28 @@ def send_pwa_files(path):
|
||||
def config_save():
|
||||
file_name = request.form.get("name") or "default"
|
||||
file_name = f"{base_dir}{file_name}.csv"
|
||||
import csv
|
||||
|
||||
with open(file_name, "w") as csv_file:
|
||||
writer = csv.writer(csv_file)
|
||||
for device in list_devices():
|
||||
if device["type"] != "-":
|
||||
writer.writerow(device.values())
|
||||
write_config_csv(file_name)
|
||||
flash(f"Saved config to {file_name}!")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route("/config/load", methods=["POST"])
|
||||
def config_load():
|
||||
file_name = request.form.get("name") or "default.csv"
|
||||
file_name = request.form.get("name")
|
||||
file_name = f"{base_dir}{file_name}"
|
||||
detach_all()
|
||||
import csv
|
||||
|
||||
with open(file_name) as csv_file:
|
||||
config_reader = csv.reader(csv_file)
|
||||
for row in config_reader:
|
||||
image_name = row[3].replace("(WRITEPROTECT)", "")
|
||||
attach_image(row[0], image_name, row[2])
|
||||
flash(f"Loaded config from {file_name}!")
|
||||
if "load" in request.form:
|
||||
if read_config_csv(file_name):
|
||||
flash(f"Loaded config from {file_name}!")
|
||||
else:
|
||||
flash(f"Failed to load {file_name}!", "error")
|
||||
elif "delete" in request.form:
|
||||
if delete_file(file_name):
|
||||
flash(f"Deleted config {file_name}!")
|
||||
else:
|
||||
flash(f"Failed to delete {file_name}!", "error")
|
||||
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@ -147,6 +151,11 @@ def attach():
|
||||
flash(f"Unknown file type. Valid files are: {', '.join(valid_file_suffix)}", "error")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
# Validate the SCSI ID
|
||||
if re.match("[0-7]", str(scsi_id)) == None:
|
||||
flash(f"Invalid SCSI ID. Should be a number between 0-7", "error")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
process = attach_image(scsi_id, file_name, image_type)
|
||||
if process.returncode == 0:
|
||||
flash(f"Attached {file_name} to SCSI id {scsi_id}!")
|
||||
@ -204,6 +213,9 @@ def restart():
|
||||
def rascsi_restart():
|
||||
rascsi_service("restart")
|
||||
flash("Restarting RaSCSI Service...")
|
||||
reserved_scsi_ids = app.config.get("RESERVED_SCSI_IDS")
|
||||
if reserved_scsi_ids != "":
|
||||
reserve_scsi_ids(reserved_scsi_ids)
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@ -286,7 +298,7 @@ def download():
|
||||
@app.route("/files/delete", methods=["POST"])
|
||||
def delete():
|
||||
image = request.form.get("image")
|
||||
if delete_image(image):
|
||||
if delete_file(base_dir + image):
|
||||
flash("File " + image + " deleted")
|
||||
return redirect(url_for("index"))
|
||||
else:
|
||||
@ -312,6 +324,15 @@ if __name__ == "__main__":
|
||||
app.config["UPLOAD_FOLDER"] = base_dir
|
||||
os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
|
||||
app.config["MAX_CONTENT_LENGTH"] = MAX_FILE_SIZE
|
||||
if len(sys.argv) >= 2:
|
||||
app.config["RESERVED_SCSI_IDS"] = str(sys.argv[1])
|
||||
# Reserve SCSI IDs on the backend side to prevent use
|
||||
reserve_scsi_ids(app.config.get("RESERVED_SCSI_IDS"))
|
||||
else:
|
||||
app.config["RESERVED_SCSI_IDS"] = ""
|
||||
|
||||
# Load the configuration in default.cvs, if it exists
|
||||
read_config_csv(f"{base_dir}default.csv")
|
||||
|
||||
import bjoern
|
||||
print("Serving rascsi-web...")
|
||||
|
Loading…
x
Reference in New Issue
Block a user