mirror of
https://github.com/akuker/RASCSI.git
synced 2025-05-24 04:38:15 +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) {
|
||||