Configurable block size, controller/device cleanup, dispatchers per device, bridge setup (#203)

* Use foreach

* Renaming

* Revert "Renaming"

This reverts commit b0554b9c0a.

* 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 210abc775d.

* Revert "Updated parameter handling"

This reverts commit 35302addd5.

* 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 d35a15ea8e.

* 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 39ca12d8b1.

* 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:
Uwe Seimet 2021-09-15 03:23:04 +02:00 committed by GitHub
parent 96108d9cf1
commit 3e7f317c49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 5477 additions and 5481 deletions

View File

@ -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)!

View File

@ -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:

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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
};

View File

@ -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
};

View File

@ -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時要修正 → 済
}
//---------------------------------------------------------------------------

View File

@ -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(365)
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
/**
0OSのごみ箱機能を利用する場合は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:短縮
// Bit2430 ファイル重複防止マーク 0:自動 1127:文字
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
// Bit2430 Duplicate file identification mark 0:Automatic 1127:Chars
};
/// ファイルシステム動作フラグ
@ -970,5 +960,3 @@ private:
TCHAR m_szBase[DriveMax][FILEPATH_MAX]; ///< ベースパス状態復元用の候補
static DWORD g_nOption; ///< ファイル名変換フラグ
};
#endif // cfilesystem_h

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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"; }

View File

@ -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;
}

View File

@ -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

View File

@ -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);
};

View File

@ -0,0 +1,656 @@
//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 (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;
}
}

View File

@ -0,0 +1,95 @@
//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 (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
};

View 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();
}

View File

@ -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;
};

View 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;
};

View 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;
};

View 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;
};

View File

@ -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;
};

View File

@ -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;
}
//---------------------------------------------------------------------------

View File

@ -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;
};

View File

@ -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]);
}
}

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;
};

View File

@ -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);

View File

@ -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;
};

View File

@ -15,7 +15,8 @@
//---------------------------------------------------------------------------
#include "scsimo.h"
#include "xm6.h"
#include "../rascsi.h"
#include "fileio.h"
#include "exceptions.h"
@ -32,10 +33,6 @@
//---------------------------------------------------------------------------
SCSIMO::SCSIMO() : Disk("SCMO")
{
SetRemovable(true);
SetProtectable(true);
SetProduct("M2513A");
}
//---------------------------------------------------------------------------
@ -49,48 +46,20 @@ void SCSIMO::Open(const Filepath& path)
// Open as read-only
Fileio fio;
if (!fio.Open(path, Fileio::ReadOnly)) {
throw io_exception("Can't open MO file read-only");
throw file_not_found_exception("Can't open MO file");
}
// Get file size
off_t size = fio.GetFileSize();
fio.Close();
switch (size) {
// 128MB
case 0x797f400:
// 512 bytes per sector
SetSectorSize(9);
SetBlockCount(248826);
break;
SetGeometryForCapacity(size);
// 230MB
case 0xd9eea00:
// 512 bytes per sector
SetSectorSize(9);
SetBlockCount(446325);
break;
// 540MB
case 0x1fc8b800:
// 512 bytes per sector
SetSectorSize(9);
SetBlockCount(1041500);
break;
// 640MB
case 0x25e28000:
// 2048 bytes per sector
SetSectorSize(11);
SetBlockCount(310352);
break;
// Other (this is an error)
default:
throw io_exception("Invalid MO file size, supported sizes are 127398912 bytes (128 MB), "
"228518400 bytes (230 MB), 533248000 bytes (540 MB), 635600896 bytes (640 MB)");
}
SetReadOnly(false);
SetProtectable(true);
SetProtected(false);
Disk::Open(path);
FileSupport::SetPath(path);
@ -125,12 +94,6 @@ int SCSIMO::Inquiry(const DWORD *cdb, BYTE *buf)
// buf[4] ... Inquiry additional data
memset(buf, 0, 8);
buf[0] = 0x07;
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
if (((cdb[1] >> 5) & 0x07) != GetLun()) {
buf[0] = 0x7f;
}
buf[1] = 0x80;
buf[2] = 0x02;
buf[3] = 0x02;
@ -153,12 +116,10 @@ int SCSIMO::Inquiry(const DWORD *cdb, BYTE *buf)
//---------------------------------------------------------------------------
//
// MODE SELECT
// *Not affected by disk.code
//
//---------------------------------------------------------------------------
bool SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
{
int page;
int size;
ASSERT(buf);
@ -183,7 +144,7 @@ bool SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
// Parsing the page
while (length > 0) {
// Get the page
page = buf[0];
int page = buf[0];
switch (page) {
// format device
@ -267,40 +228,44 @@ int SCSIMO::AddVendor(int page, BOOL change, BYTE *buf)
if (IsReady()) {
unsigned spare = 0;
unsigned bands = 0;
DWORD blocks = GetBlockCount();
uint64_t blocks = GetBlockCount();
if (GetSectorSize() == 9) switch (blocks) {
// 128MB
case 248826:
spare = 1024;
bands = 1;
break;
if (GetSectorSizeInBytes() == 512) {
switch (blocks) {
// 128MB
case 248826:
spare = 1024;
bands = 1;
break;
// 230MB
case 446325:
spare = 1025;
bands = 10;
break;
// 230MB
case 446325:
spare = 1025;
bands = 10;
break;
// 540MB
case 1041500:
spare = 2250;
bands = 18;
break;
// 540MB
case 1041500:
spare = 2250;
bands = 18;
break;
}
}
if (GetSectorSize() == 11) switch (blocks) {
// 640MB
case 310352:
spare = 2244;
bands = 11;
break;
if (GetSectorSizeInBytes() == 2048) {
switch (blocks) {
// 640MB
case 310352:
spare = 2244;
bands = 11;
break;
// 1.3GB (lpproj: not tested with real device)
case 605846:
spare = 4437;
bands = 18;
break;
// 1.3GB (lpproj: not tested with real device)
case 605846:
spare = 4437;
bands = 18;
break;
}
}
buf[2] = 0; // format mode

View File

@ -19,21 +19,17 @@
#include "disk.h"
#include "filepath.h"
//===========================================================================
//
// SCSI magneto-optical disk
//
//===========================================================================
class SCSIMO : public Disk, public FileSupport
{
public:
// Basic Functions
SCSIMO(); // Constructor
void Open(const Filepath& path); // Open
SCSIMO();
~SCSIMO() {};
// commands
int Inquiry(const DWORD *cdb, BYTE *buf) override; // INQUIRY command
bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override; // MODE SELECT(6) command
void Open(const Filepath& path);
// Commands
int Inquiry(const DWORD *cdb, BYTE *buf) override;
bool ModeSelect(const DWORD *cdb, const BYTE *buf, int length) override;
// Internal processing
int AddVendor(int page, BOOL change, BYTE *buf); // Add vendor special page

View File

@ -29,28 +29,21 @@ public:
}
};
class lun_exception final : public exception {
private:
int lun;
public:
lun_exception(int _lun) : lun(_lun) {}
~lun_exception() {}
int getlun() const {
return lun;
}
};
class io_exception : public exception {
private:
string msg;
public:
io_exception(const string& _msg) : msg(_msg) {}
~io_exception() {}
virtual ~io_exception() {}
const string& getmsg() const {
return msg;
}
};
class file_not_found_exception : public io_exception {
public:
file_not_found_exception(const string& msg) : io_exception(msg) {}
~file_not_found_exception() {}
};

View File

@ -4,135 +4,100 @@
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2010-2020 GIMONS
// [ ファイルI/O(RaSCSI用サブセット) ]
// [ File I/O (Subset for RaSCSI) ]
//
//---------------------------------------------------------------------------
#include "os.h"
#include "xm6.h"
#include "filepath.h"
#include "fileio.h"
#include "rascsi.h"
//===========================================================================
//
// ファイルI/O
// File I/O
//
//===========================================================================
//---------------------------------------------------------------------------
//
// コンストラクタ
//
//---------------------------------------------------------------------------
Fileio::Fileio()
{
// ワーク初期化
// Initialize work
handle = -1;
}
//---------------------------------------------------------------------------
//
// デストラクタ
//
//---------------------------------------------------------------------------
Fileio::~Fileio()
{
ASSERT(handle == -1);
// Releaseでの安全策
// Safety measure for Release
Close();
}
//---------------------------------------------------------------------------
//
// ロード
//
//---------------------------------------------------------------------------
BOOL Fileio::Load(const Filepath& path, void *buffer, int size)
{
ASSERT(buffer);
ASSERT(size > 0);
ASSERT(handle < 0);
// オープン
if (!Open(path, ReadOnly)) {
return FALSE;
}
// 読み込み
if (!Read(buffer, size)) {
Close();
return FALSE;
}
// クローズ
Close();
return TRUE;
}
//---------------------------------------------------------------------------
//
// セーブ
//
//---------------------------------------------------------------------------
BOOL Fileio::Save(const Filepath& path, void *buffer, int size)
{
ASSERT(buffer);
ASSERT(size > 0);
ASSERT(handle < 0);
// オープン
if (!Open(path, WriteOnly)) {
return FALSE;
}
// 書き込み
if (!Write(buffer, size)) {
Close();
return FALSE;
}
// クローズ
Close();
return TRUE;
}
//---------------------------------------------------------------------------
//
// オープン
//
//---------------------------------------------------------------------------
BOOL Fileio::Open(LPCTSTR fname, OpenMode mode, BOOL directIO)
BOOL Fileio::Open(const char *fname, OpenMode mode, BOOL directIO)
{
mode_t omode;
ASSERT(fname);
ASSERT(handle < 0);
// ヌル文字列からの読み込みは必ず失敗させる
// Always fail a read from a null array
if (fname[0] == _T('\0')) {
handle = -1;
return FALSE;
}
// デフォルトモード
// Default mode
omode = directIO ? O_DIRECT : 0;
// モード別
switch (mode) {
// 読み込みのみ
case ReadOnly:
handle = open(fname, O_RDONLY | omode);
break;
// 書き込みのみ
case WriteOnly:
handle = open(fname, O_CREAT | O_WRONLY | O_TRUNC | omode, 0666);
break;
// 読み書き両方
case ReadWrite:
// CD-ROMからの読み込みはRWが成功してしまう
if (access(fname, 0x06) != 0) {
@ -141,13 +106,12 @@ BOOL Fileio::Open(LPCTSTR fname, OpenMode mode, BOOL directIO)
handle = open(fname, O_RDWR | omode);
break;
// それ以外
default:
ASSERT(FALSE);
break;
}
// 結果評価
// Evaluate results
if (handle == -1) {
return FALSE;
}
@ -156,61 +120,36 @@ BOOL Fileio::Open(LPCTSTR fname, OpenMode mode, BOOL directIO)
return TRUE;
}
//---------------------------------------------------------------------------
//
// オープン
//
//---------------------------------------------------------------------------
BOOL Fileio::Open(LPCTSTR fname, OpenMode mode)
BOOL Fileio::Open(const char *fname, OpenMode mode)
{
return Open(fname, mode, FALSE);
}
//---------------------------------------------------------------------------
//
// オープン
//
//---------------------------------------------------------------------------
BOOL Fileio::Open(const Filepath& path, OpenMode mode)
{
return Open(path.GetPath(), mode);
}
//---------------------------------------------------------------------------
//
// オープン
//
//---------------------------------------------------------------------------
BOOL Fileio::OpenDIO(LPCTSTR fname, OpenMode mode)
BOOL Fileio::OpenDIO(const char *fname, OpenMode mode)
{
// O_DIRECT付きでオープン
// Open with included O_DIRECT
if (!Open(fname, mode, TRUE)) {
// 通常モードリトライ(tmpfs等)
// Normal mode retry (tmpfs etc.)
return Open(fname, mode, FALSE);
}
return TRUE;
}
//---------------------------------------------------------------------------
//
// オープン
//
//---------------------------------------------------------------------------
BOOL Fileio::OpenDIO(const Filepath& path, OpenMode mode)
{
return OpenDIO(path.GetPath(), mode);
}
//---------------------------------------------------------------------------
//
// 読み込み
//
//---------------------------------------------------------------------------
BOOL Fileio::Read(void *buffer, int size)
{
int count;
@ -219,7 +158,6 @@ BOOL Fileio::Read(void *buffer, int size)
ASSERT(size > 0);
ASSERT(handle >= 0);
// 読み込み
count = read(handle, buffer, size);
if (count != size) {
return FALSE;
@ -228,11 +166,6 @@ BOOL Fileio::Read(void *buffer, int size)
return TRUE;
}
//---------------------------------------------------------------------------
//
// 書き込み
//
//---------------------------------------------------------------------------
BOOL Fileio::Write(const void *buffer, int size)
{
int count;
@ -241,7 +174,6 @@ BOOL Fileio::Write(const void *buffer, int size)
ASSERT(size > 0);
ASSERT(handle >= 0);
// 書き込み
count = write(handle, buffer, size);
if (count != size) {
return FALSE;
@ -250,17 +182,12 @@ BOOL Fileio::Write(const void *buffer, int size)
return TRUE;
}
//---------------------------------------------------------------------------
//
// シーク
//
//---------------------------------------------------------------------------
BOOL Fileio::Seek(off_t offset, BOOL relative)
{
ASSERT(handle >= 0);
ASSERT(offset >= 0);
// 相対シークならオフセットに現在値を追加
// Add current position in case of relative seek
if (relative) {
offset += GetFilePos();
}
@ -272,11 +199,6 @@ BOOL Fileio::Seek(off_t offset, BOOL relative)
return TRUE;
}
//---------------------------------------------------------------------------
//
// ファイルサイズ取得
//
//---------------------------------------------------------------------------
off_t Fileio::GetFileSize()
{
off_t cur;
@ -284,40 +206,30 @@ off_t Fileio::GetFileSize()
ASSERT(handle >= 0);
// ファイル位置を64bitで取得
// Get file position in 64bit
cur = GetFilePos();
// ファイルサイズを64bitで取得
// Get file size in64bitで
end = lseek(handle, 0, SEEK_END);
// 位置を元に戻す
// Return to start position
Seek(cur);
return end;
}
//---------------------------------------------------------------------------
//
// ファイル位置取得
//
//---------------------------------------------------------------------------
off_t Fileio::GetFilePos() const
{
off_t pos;
ASSERT(handle >= 0);
// ファイル位置を64bitで取得
// Get file position in 64bit
pos = lseek(handle, 0, SEEK_CUR);
return pos;
}
//---------------------------------------------------------------------------
//
// クローズ
//
//---------------------------------------------------------------------------
void Fileio::Close()
{

View File

@ -4,7 +4,7 @@
//
// Copyright (C) 2001-2005 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2013-2020 GIMONS
// [ ファイルI/O(RaSCSI用サブセット) ]
// [ File I/O (Subset for RaSCSI) ]
//
//---------------------------------------------------------------------------
@ -15,7 +15,7 @@
//===========================================================================
//
// マクロ(Load,Save用)
// Macros (for Load, Save)
//
//===========================================================================
#define PROP_IMPORT(f, p) \
@ -30,56 +30,40 @@
//===========================================================================
//
// ファイルI/O
// File I/O
//
//===========================================================================
class Fileio
{
public:
enum OpenMode {
ReadOnly, // 読み込みのみ
WriteOnly, // 書き込みのみ
ReadWrite // 読み書き両方
ReadOnly,
WriteOnly,
ReadWrite
};
public:
Fileio();
// コンストラクタ
virtual ~Fileio();
// デストラクタ
BOOL Load(const Filepath& path, void *buffer, int size);
// ROM,RAMロード
BOOL Save(const Filepath& path, void *buffer, int size);
// RAMセーブ
BOOL Load(const Filepath& path, void *buffer, int size); // Load ROM, RAM
BOOL Save(const Filepath& path, void *buffer, int size); // Save RAM
BOOL Open(LPCTSTR fname, OpenMode mode);
// オープン
BOOL Open(const char *fname, OpenMode mode);
BOOL Open(const Filepath& path, OpenMode mode);
// オープン
BOOL OpenDIO(LPCTSTR fname, OpenMode mode);
// オープン
BOOL OpenDIO(const char *fname, OpenMode mode);
BOOL OpenDIO(const Filepath& path, OpenMode mode);
// オープン
BOOL Seek(off_t offset, BOOL relative = FALSE);
// シーク
BOOL Read(void *buffer, int size);
// 読み込み
BOOL Write(const void *buffer, int size);
// 書き込み
off_t GetFileSize();
// ファイルサイズ取得
off_t GetFilePos() const;
// ファイル位置取得
void Close();
// クローズ
BOOL IsValid() const { return (BOOL)(handle != -1); }
// 有効チェック
private:
BOOL Open(LPCTSTR fname, OpenMode mode, BOOL directIO);
// オープン
BOOL Open(const char *fname, OpenMode mode, BOOL directIO);
int handle; // ファイルハンドル
int handle; // File handle
};
#endif // fileio_h

View File

@ -4,14 +4,14 @@
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2012-2020 GIMONS
// [ ファイルパス(サブセット) ]
// [ File path (subset) ]
//
//---------------------------------------------------------------------------
#include "os.h"
#include "xm6.h"
#include "filepath.h"
#include "fileio.h"
#include "rascsi.h"
//===========================================================================
//
@ -19,31 +19,16 @@
//
//===========================================================================
//---------------------------------------------------------------------------
//
// Constructor
//
//---------------------------------------------------------------------------
Filepath::Filepath()
{
// Clear
Clear();
}
//---------------------------------------------------------------------------
//
// Destructor
//
//---------------------------------------------------------------------------
Filepath::~Filepath()
{
}
//---------------------------------------------------------------------------
//
// Assignment operator
//
//---------------------------------------------------------------------------
Filepath& Filepath::operator=(const Filepath& path)
{
// Set path (split internally)
@ -52,11 +37,6 @@ Filepath& Filepath::operator=(const Filepath& path)
return *this;
}
//---------------------------------------------------------------------------
//
// Clear
//
//---------------------------------------------------------------------------
void Filepath::Clear()
{
@ -69,16 +49,16 @@ void Filepath::Clear()
//---------------------------------------------------------------------------
//
// ファイル設定(ユーザ) MBCS用
// File settings (user) for MBCS
//
//---------------------------------------------------------------------------
void Filepath::SetPath(LPCSTR path)
void Filepath::SetPath(const char *path)
{
ASSERT(path);
ASSERT(strlen(path) < _MAX_PATH);
// Copy pathname
strcpy(m_szPath, (LPTSTR)path);
strcpy(m_szPath, (char *)path);
// Split
Split();
@ -86,31 +66,24 @@ void Filepath::SetPath(LPCSTR path)
//---------------------------------------------------------------------------
//
// パス分離
// Split paths
//
//---------------------------------------------------------------------------
void Filepath::Split()
{
LPTSTR pDir;
LPTSTR pDirName;
LPTSTR pBase;
LPTSTR pBaseName;
LPTSTR pExtName;
// パーツを初期化
// Initialize the parts
m_szDir[0] = _T('\0');
m_szFile[0] = _T('\0');
m_szExt[0] = _T('\0');
// 分離
pDir = strdup(m_szPath);
pDirName = dirname(pDir);
pBase = strdup(m_szPath);
pBaseName = basename(pBase);
pExtName = strrchr(pBaseName, '.');
// Split
char *pDir = strdup(m_szPath);
char *pDirName = dirname(pDir);
char *pBase = strdup(m_szPath);
char *pBaseName = basename(pBase);
char *pExtName = strrchr(pBaseName, '.');
// 転送
// Transmit
if (pDirName) {
strcpy(m_szDir, pDirName);
strcat(m_szDir, "/");
@ -124,7 +97,7 @@ void Filepath::Split()
strcpy(m_szFile, pBaseName);
}
// 解放
// Release
free(pDir);
free(pBase);
}
@ -135,21 +108,16 @@ void Filepath::Split()
// The returned pointer is temporary. Copy immediately.
//
//---------------------------------------------------------------------------
LPCTSTR Filepath::GetFileExt() const
const char *Filepath::GetFileExt() const
{
// 固定バッファへ合成
// Merge into static buffer
strcpy(FileExt, m_szExt);
// LPCTSTRとして返す
return (LPCTSTR)FileExt;
// Return as LPCTSTR
return (const char *)FileExt;
}
//---------------------------------------------------------------------------
//
// Save
//
//---------------------------------------------------------------------------
BOOL Filepath::Save(Fileio *fio, int /*ver*/)
{
ASSERT(fio);
@ -157,11 +125,6 @@ BOOL Filepath::Save(Fileio *fio, int /*ver*/)
return TRUE;
}
//---------------------------------------------------------------------------
//
// Load
//
//---------------------------------------------------------------------------
BOOL Filepath::Load(Fileio *fio, int /*ver*/)
{
ASSERT(fio);

View File

@ -4,12 +4,13 @@
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2012-2020 GIMONS
// [ ファイルパス(サブセット) ]
// [ File path (subset) ]
//
//---------------------------------------------------------------------------
#if !defined(filepath_h)
#define filepath_h
#pragma once
#include "os.h"
class Fileio;
@ -22,47 +23,30 @@ class Fileio;
//===========================================================================
//
// ファイルパス
// ※代入演算子を用意すること
// File path
// Assignment operators are prepared here
//
//===========================================================================
class Filepath
{
public:
Filepath();
// コンストラクタ
virtual ~Filepath();
// デストラクタ
Filepath& operator=(const Filepath& path);
// 代入
void Clear();
// クリア
void SetPath(LPCSTR path);
// ファイル設定(ユーザ) MBCS用
LPCTSTR GetPath() const { return m_szPath; }
// パス名取得
LPCTSTR GetFileExt() const;
// ショート名取得(LPCTSTR)
void SetPath(const char *path); // File settings (user) for MBCS
const char *GetPath() const { return m_szPath; } // Get path name
const char *GetFileExt() const; // Get short name (LPCTSTR)
BOOL Save(Fileio *fio, int ver);
// セーブ
BOOL Load(Fileio *fio, int ver);
// ロード
private:
void Split();
// パス分割
TCHAR m_szPath[_MAX_PATH];
// ファイルパス
TCHAR m_szDir[_MAX_DIR];
// ディレクトリ
TCHAR m_szFile[_MAX_FNAME];
// ファイル
TCHAR m_szExt[_MAX_EXT];
// 拡張子
void Split(); // Split the path
TCHAR m_szPath[_MAX_PATH]; // File path
TCHAR m_szDir[_MAX_DIR]; // Directory
TCHAR m_szFile[_MAX_FNAME]; // File
TCHAR m_szExt[_MAX_EXT]; // Extension
static TCHAR FileExt[_MAX_FNAME + _MAX_DIR];
// ショート名(TCHAR)
static TCHAR FileExt[_MAX_FNAME + _MAX_DIR]; // Short name (TCHAR)
};
#endif // filepath_h

View File

@ -15,9 +15,9 @@
#include <sys/mman.h>
#include "os.h"
#include "xm6.h"
#include "gpiobus.h"
#include "log.h"
#include "rascsi.h"
#ifdef __linux__
//---------------------------------------------------------------------------
@ -82,11 +82,6 @@ DWORD bcm_host_get_peripheral_address(void)
}
#endif // __NetBSD__
//---------------------------------------------------------------------------
//
// Constructor
//
//---------------------------------------------------------------------------
GPIOBUS::GPIOBUS()
{
actmode = TARGET;
@ -102,20 +97,10 @@ GPIOBUS::GPIOBUS()
rpitype = 0;
}
//---------------------------------------------------------------------------
//
// Destructor
//
//---------------------------------------------------------------------------
GPIOBUS::~GPIOBUS()
{
}
//---------------------------------------------------------------------------
//
// 初期化
//
//---------------------------------------------------------------------------
BOOL GPIOBUS::Init(mode_e mode)
{
#if defined(__x86_64__) || defined(__X86__)
@ -339,11 +324,6 @@ BOOL GPIOBUS::Init(mode_e mode)
}
//---------------------------------------------------------------------------
//
// Cleanup
//
//---------------------------------------------------------------------------
void GPIOBUS::Cleanup()
{
#if defined(__x86_64__) || defined(__X86__)
@ -381,11 +361,6 @@ void GPIOBUS::Cleanup()
#endif // ifdef __x86_64__ || __X86__
}
//---------------------------------------------------------------------------
//
// Reset
//
//---------------------------------------------------------------------------
void GPIOBUS::Reset()
{
#if defined(__x86_64__) || defined(__X86__)
@ -1612,7 +1587,7 @@ BUS::phase_t GPIOBUS::GetPhaseRaw(DWORD raw_data)
//
//---------------------------------------------------------------------------
int GPIOBUS::GetCommandByteCount(BYTE opcode) {
if (opcode == 0x88 || opcode == 0x8A || opcode == 0x8F || opcode == 0x9E) {
if (opcode == 0x88 || opcode == 0x8A || opcode == 0x8F || opcode == 0x91 || opcode == 0x9E) {
return 16;
}
else if (opcode == 0xA0) {

View File

@ -12,6 +12,7 @@
#if !defined(gpiobus_h)
#define gpiobus_h
#include "rascsi.h"
#include "scsi.h"
//---------------------------------------------------------------------------
@ -188,7 +189,7 @@
#ifdef CONNECT_TYPE_AIBOM
//
// RaSCSI Adapter Aibomu version
// RaSCSI Adapter Aibom version
//
#define CONNECT_DESC "AIBOM PRODUCTS version" // Startup message
@ -233,7 +234,7 @@
#ifdef CONNECT_TYPE_GAMERNIUM
//
// RaSCSI Adapter GAMERnium.com
// RaSCSI Adapter GAMERnium.com version
//
#define CONNECT_DESC "GAMERnium.com version"// Startup message
@ -298,7 +299,7 @@
//---------------------------------------------------------------------------
//
// Constant declarations(GPIO)
// Constant declarations (GPIO)
//
//---------------------------------------------------------------------------
#define SYST_OFFSET 0x00003000
@ -375,7 +376,7 @@
//---------------------------------------------------------------------------
//
// Constant declarations(GIC)
// Constant declarations (GIC)
//
//---------------------------------------------------------------------------
#define ARM_GICD_BASE 0xFF841000
@ -400,7 +401,7 @@
//---------------------------------------------------------------------------
//
// Constant declarations(GIC IRQ)
// Constant declarations (GIC IRQ)
//
//---------------------------------------------------------------------------
#define GIC_IRQLOCAL0 (16 + 14)
@ -419,7 +420,7 @@
//---------------------------------------------------------------------------
//
// Constant declarations(SCSI)
// Constant declarations (SCSI)
//
//---------------------------------------------------------------------------
#define IN GPIO_INPUT

View File

@ -105,12 +105,8 @@
typedef unsigned char BYTE;
typedef uint16_t WORD;
typedef uint32_t DWORD;
typedef uint64_t QWORD;
typedef int BOOL;
typedef char TCHAR;
typedef char *LPTSTR;
typedef const char *LPCTSTR;
typedef const char *LPCSTR;
#if !defined(FALSE)
#define FALSE 0

View File

@ -23,7 +23,7 @@ using namespace rascsi_interface;
//
//---------------------------------------------------------------------------
void SerializeMessage(int fd, const google::protobuf::MessageLite& message)
void SerializeMessage(int fd, const google::protobuf::Message& message)
{
string data;
message.SerializeToString(&data);
@ -35,14 +35,12 @@ void SerializeMessage(int fd, const google::protobuf::MessageLite& message)
}
// Write the actual protobuf data
uint8_t buf[size];
memcpy(buf, data.data(), size);
if (write(fd, buf, size) != size) {
if (write(fd, data.data(), size) != size) {
throw io_exception("Can't write protobuf data");
}
}
void DeserializeMessage(int fd, google::protobuf::MessageLite& message)
void DeserializeMessage(int fd, google::protobuf::Message& message)
{
// Read the header with the size of the protobuf data
uint8_t header_buf[4];

View File

@ -11,9 +11,9 @@
#pragma once
#include "google/protobuf/message_lite.h"
#include "google/protobuf/message.h"
#include "rascsi_interface.pb.h"
void SerializeMessage(int, const google::protobuf::MessageLite&);
void DeserializeMessage(int, google::protobuf::MessageLite&);
void SerializeMessage(int, const google::protobuf::Message&);
void DeserializeMessage(int, google::protobuf::Message&);
int ReadNBytes(int, uint8_t *, int);

File diff suppressed because it is too large Load Diff

View File

@ -6,12 +6,11 @@
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
//
// [ Common Definition ]
// [ Common Definitions ]
//
//---------------------------------------------------------------------------
#if !defined(xm6_h)
#define xm6_h
#pragma once
//---------------------------------------------------------------------------
//
@ -20,7 +19,6 @@
//---------------------------------------------------------------------------
#define USE_SEL_EVENT_ENABLE // Check SEL signal by event
#define REMOVE_FIXED_SASIHD_SIZE // remove the size limitation of SASIHD
#define USE_MZ1F23_1024_SUPPORT // MZ-1F23 (SASI 20M/sector size 1024)
// This avoids an indefinite loop with warnings if there is no RaSCSI hardware
// and thus helps with running certain tests on X86 hardware.
#if defined(__x86_64__) || defined(__X86__)
@ -29,7 +27,7 @@
//---------------------------------------------------------------------------
//
// Class Declaration
// Class Declarations
//
//---------------------------------------------------------------------------
class Fileio;
@ -37,6 +35,3 @@ class Fileio;
class Disk;
// SASI/SCSI Disk
class Filepath;
// File Path
#endif // xm6_h

View File

@ -1,6 +1,7 @@
//
// Each rascsi remote interface message is preceded by a little endian 32 bit header,
// which contains the protobuf message size.
// Unless explicitly specified the order of repeated data returned is undefined.
//
syntax = "proto3";
@ -26,27 +27,82 @@ enum PbDeviceType {
SCDP = 7;
}
// rascsi remote operations
// rascsi remote operations, returns PbResult
enum PbOperation {
NONE = 0;
// Returns the server information
// Gets the server information
SERVER_INFO = 1;
// Set the default folder for image files, PbCommand.params contains the folder name
DEFAULT_FOLDER = 2;
// Set server log level, PbCommand.params contains the log level
LOG_LEVEL = 3;
// Attach new device
ATTACH = 4;
// Detach device. Detach all devices if PbCommand.params == "all". In this case ID and unit are ignored.
DETACH = 5;
// Insert media
INSERT = 6;
// Eject media
EJECT = 7;
// Write-protect media (not possible for read-only media)
PROTECT = 8;
// Make media writable (not possible for read-only media)
UNPROTECT = 9;
// Gets information for a list of attached devices. Returns data for all attached devices if empty.
DEVICE_INFO = 2;
// Set the default folder for image files. PbCommand.params contains the folder name.
DEFAULT_FOLDER = 3;
// Set server log level. PbCommand.params contains the log level.
LOG_LEVEL = 4;
// Attach devices
ATTACH = 5;
// Detach devices
DETACH = 6;
// Detach all devices, does not require a device list
DETACH_ALL = 7;
// Start device
START = 8;
// Stop device, e.g. park drive
STOP = 9;
// Insert medium
INSERT = 10;
// Eject medium
EJECT = 11;
// Write-protect medium (not possible for read-only media)
PROTECT = 12;
// Make medium writable (not possible for read-only media)
UNPROTECT = 13;
// IDs blocked from being used, usually the IDs of the initiators (computers) in the SCSI chain.
// PbCommand.params contains the list of IDs to reserve, or is empty in order not to reserve any ID.
RESERVE = 14;
}
// The properties supported by a device, helping clients to offer a good user experience
message PbDeviceProperties {
// Read-only media (e.g. CD-ROMs) are not protectable but permanently read-only
bool read_only = 1;
// Medium can be write-protected
bool protectable = 2;
// Device can be stopped, e.g. parked
bool stoppable = 3;
// Medium can be removed
bool removable = 4;
// Medium can be locked
bool lockable = 5;
// Device supports image file as a parameter
bool supports_file = 6;
// Device supports parameters other than a filename
bool supports_params = 7;
// Ordered list of default parameters, if any (requires supports_params to be true)
repeated string default_params = 8;
// Number of supported LUNs, at least 1 (for LUN 0)
uint32 luns = 9;
// Unordered list of permitted block sizes in bytes, empty if the block size is not configurable
repeated uint32 block_sizes = 10;
// Unordered list of permitted media capacities in bytes, empty if there is no capacity restriction
repeated uint64 capacities = 11;
}
// The status of a device, only meaningful if the respective feature is supported
message PbDeviceStatus {
// Medium is write-protected
bool protected = 1;
// Device is stopped, e.g. parked
bool stopped = 2;
// Medium is removed
bool removed = 3;
// Medium is locked
bool locked = 4;
}
// Device properties by device type. (Note that a protobuf map cannot be used because enums cannot be map keys.)
message PbDeviceTypeProperties {
PbDeviceType type = 1;
PbDeviceProperties properties = 2;
}
// The image file data
@ -61,14 +117,16 @@ message PbDeviceDefinition {
int32 id = 1;
int32 unit = 2;
PbDeviceType type = 3;
string file = 4;
// The device name, format is VENDOR:PRODUCT:REVISION
string name = 5;
bool protected = 6;
}
message PbDeviceDefinitions {
repeated PbDeviceDefinition devices = 1;
// Optional device specific parameters, e.g. the name of an image file
repeated string params = 4;
// The optional block size in bytes per sector, must be one of the supported block sizes for SASI/SCSI
int32 block_size = 5;
// The device name components
string vendor = 6;
string product = 7;
string revision = 8;
// Create a write-protected device
bool protected = 9;
}
// The device data, sent from the server to the client
@ -76,26 +134,19 @@ message PbDevice {
int32 id = 1;
int32 unit = 2;
PbDeviceType type = 3;
PbImageFile file = 4;
string vendor = 5;
string product = 6;
string revision = 7;
// Read-only media (e.g. CD-ROMs) are not protectable but read-only all the time
bool read_only = 8;
// Media can be write-protected
bool protectable = 9;
// Media is write-protected
bool protected = 10;
// Media can be removed
bool removable = 11;
// Media is removed
bool removed = 12;
// Media can be locked
bool lockable = 13;
// Media is locked
bool locked = 14;
// Device supports image file
bool supports_file = 15;
PbDeviceProperties properties = 4;
PbDeviceStatus status = 5;
// Image file information, if the device supports image files
PbImageFile file = 6;
// Ordered list of effective parameters the device was created with
repeated string params = 7;
string vendor = 8;
string product = 9;
string revision = 10;
// Block size in bytes
int32 block_size = 11;
// Number of blocks
int64 block_count = 12;
}
message PbDevices {
@ -104,30 +155,45 @@ message PbDevices {
// Commands rascsi can execute and their parameters
message PbCommand {
PbOperation cmd = 1;
// The optional device list
PbDeviceDefinitions devices = 2;
// The optional parameters, depending on the operation
string params = 3;
PbOperation operation = 1;
// The non-empty list of devices for this command
repeated PbDeviceDefinition devices = 2;
// The optional parameters depending on the operation, e.g. a filename, or a network interface list
repeated string params = 3;
}
// The result of a command
message PbResult {
// false means that an error occurred
bool status = 1;
// The (error) message
// An optional error or information message, depending on the status. A string without trailing CR/LF.
string msg = 2;
// Optional additional result data
oneof result {
// The result of a SERVER_INFO command
PbServerInfo server_info = 3;
// The result of a DEVICE_INFO command
PbDevices device_info = 4;
}
}
// The rascsi server information
message PbServerInfo {
string rascsi_version = 1;
// Sorted by severity
repeated string available_log_levels = 2;
string current_log_level = 3;
string default_image_folder = 4;
// Files in the default folder
repeated PbImageFile available_image_files = 5;
// The rascsi server version
uint32 major_version = 1;
uint32 minor_version = 2;
// < 0 for a development version, = 0 if there is no patch yet
int32 patch_version = 3;
// List of available log levels, ordered by increasing by severity
repeated string log_levels = 4;
string current_log_level = 5;
string default_image_folder = 6;
// Supported device types and their properties
repeated PbDeviceTypeProperties types_properties = 7;
// Unordered list of files in the default image folder
repeated PbImageFile image_files = 8;
// The attached devices
PbDevices devices = 6;
}
repeated PbDevice devices = 9;
// The unsorted list of reserved IDs
repeated uint32 reserved_ids = 10;
}

View File

@ -4,7 +4,7 @@
// for Raspberry Pi
//
// Copyright (C) 2020 akuker
// [ Define the version string]
// [ Define the version string ]
//
//---------------------------------------------------------------------------

View File

@ -4,7 +4,7 @@
// for Raspberry Pi
//
// Copyright (C) 2020 akuker
// [ Define the version string]
// [ Define the version string ]
//
//---------------------------------------------------------------------------
#pragma once

View File

@ -20,18 +20,16 @@
#include <iostream>
#include <list>
// Separator for the INQUIRY name components
#define COMPONENT_SEPARATOR ':'
using namespace std;
using namespace rascsi_interface;
//---------------------------------------------------------------------------
//
// Send Command
//
//---------------------------------------------------------------------------
int SendCommand(const string& hostname, int port, const PbCommand& command)
void SendCommand(const string& hostname, int port, const PbCommand& command, PbResult& result)
{
// Send command
int fd = -1;
try {
struct hostent *host = gethostbyname(hostname.c_str());
if (!host) {
@ -68,36 +66,89 @@ int SendCommand(const string& hostname, int port, const PbCommand& command)
exit(fd < 0 ? ENOTCONN : EXIT_FAILURE);
}
return fd;
}
//---------------------------------------------------------------------------
//
// Receive command result
//
//---------------------------------------------------------------------------
bool ReceiveResult(int fd)
{
// Receive result
try {
PbResult result;
DeserializeMessage(fd, result);
close(fd);
if (!result.status()) {
throw io_exception(result.msg());
}
if (!result.msg().empty()) {
cout << result.msg() << endl;
}
}
catch(const io_exception& e) {
close(fd);
cerr << "Error: " << e.getmsg() << endl;
return false;
exit(EXIT_FAILURE);
}
return true;
close(fd);
if (!result.msg().empty()) {
cout << result.msg() << endl;
}
}
void DisplayDeviceInfo(const PbDevice& pb_device)
{
cout << " " << pb_device.id() << ":" << pb_device.unit() << " " << PbDeviceType_Name(pb_device.type())
<< " " << pb_device.vendor() << ":" << pb_device.product() << ":" << pb_device.revision();
if (pb_device.block_size()) {
cout << " " << pb_device.block_size() << " bytes per sector";
if (pb_device.block_count()) {
cout << " " << pb_device.block_size() * pb_device.block_count() << " bytes capacity";
}
}
if (pb_device.properties().supports_file() && !pb_device.file().name().empty()) {
cout << " " << pb_device.file().name();
}
cout << " ";
bool hasProperty = false;
if (pb_device.properties().read_only()) {
cout << "read-only";
hasProperty = true;
}
if (pb_device.properties().protectable() && pb_device.status().protected_()) {
if (hasProperty) {
cout << ", ";
}
cout << "protected";
hasProperty = true;
}
if (pb_device.properties().stoppable() && pb_device.status().stopped()) {
if (hasProperty) {
cout << ", ";
}
cout << "stopped";
hasProperty = true;
}
if (pb_device.properties().removable() && pb_device.status().removed()) {
if (hasProperty) {
cout << ", ";
}
cout << "removed";
hasProperty = true;
}
if (pb_device.properties().lockable() && pb_device.status().locked()) {
if (hasProperty) {
cout << ", ";
}
cout << "locked";
}
if (hasProperty) {
cout << " ";
}
if (pb_device.params_size()) {
for (const string param : pb_device.params()) {
cout << param << " ";
}
}
cout << endl;
}
//---------------------------------------------------------------------------
@ -106,101 +157,239 @@ bool ReceiveResult(int fd)
//
//---------------------------------------------------------------------------
const PbServerInfo GetServerInfo(const string& hostname, int port)
{
PbCommand command;
command.set_operation(SERVER_INFO);
PbResult result;
SendCommand(hostname.c_str(), port, command, result);
return result.server_info();
}
void CommandList(const string& hostname, int port)
{
PbCommand command;
command.set_cmd(SERVER_INFO);
command.set_operation(DEVICE_INFO);
int fd = SendCommand(hostname.c_str(), port, command);
PbResult result;
SendCommand(hostname.c_str(), port, command, result);
PbServerInfo serverInfo;
try {
DeserializeMessage(fd, serverInfo);
}
catch(const io_exception& e) {
cerr << "Error: " << e.getmsg() << endl;
close(fd);
exit(EXIT_FAILURE);
}
close(fd);
cout << ListDevices(serverInfo.devices()) << endl;
const list<PbDevice>& devices = { result.device_info().devices().begin(), result.device_info().devices().end() };
cout << ListDevices(devices) << endl;
}
void CommandLogLevel(const string& hostname, int port, const string& log_level)
{
PbCommand command;
command.set_cmd(LOG_LEVEL);
command.set_params(log_level);
command.set_operation(LOG_LEVEL);
command.add_params(log_level);
int fd = SendCommand(hostname.c_str(), port, command);
ReceiveResult(fd);
close(fd);
PbResult result;
SendCommand(hostname.c_str(), port, command, result);
}
void CommandReserve(const string&hostname, int port, const string& reserved_ids)
{
PbCommand command;
command.set_operation(RESERVE);
stringstream ss(reserved_ids);
string reserved_id;
while (getline(ss, reserved_id, ',')) {
command.add_params(reserved_id);
}
PbResult result;
SendCommand(hostname.c_str(), port, command, result);
}
void CommandDefaultImageFolder(const string& hostname, int port, const string& folder)
{
PbCommand command;
command.set_cmd(DEFAULT_FOLDER);
command.set_params(folder);
command.set_operation(DEFAULT_FOLDER);
command.add_params(folder);
int fd = SendCommand(hostname.c_str(), port, command);
ReceiveResult(fd);
close(fd);
PbResult result;
SendCommand(hostname.c_str(), port, command, result);
}
void CommandDeviceInfo(const string& hostname, int port, const PbCommand& command)
{
PbResult result;
SendCommand(hostname.c_str(), port, command, result);
for (const auto& pb_device : result.device_info().devices()) {
DisplayDeviceInfo(pb_device);
}
}
void CommandServerInfo(const string& hostname, int port)
{
PbCommand command;
command.set_cmd(SERVER_INFO);
command.set_operation(SERVER_INFO);
int fd = SendCommand(hostname.c_str(), port, command);
PbResult result;
SendCommand(hostname.c_str(), port, command, result);
PbServerInfo serverInfo;
try {
DeserializeMessage(fd, serverInfo);
PbServerInfo server_info = result.server_info();
cout << "rascsi server version: " << server_info.major_version() << "." << server_info.minor_version();
if (server_info.patch_version() > 0) {
cout << "." << server_info.patch_version();
}
catch(const io_exception& e) {
cerr << "Error: " << e.getmsg() << endl;
close(fd);
exit(EXIT_FAILURE);
else if (server_info.patch_version() < 0) {
cout << " (development version)";
}
cout << endl;
close(fd);
cout << "rascsi server version: " << serverInfo.rascsi_version() << endl;
if (!serverInfo.available_log_levels_size()) {
if (!server_info.log_levels_size()) {
cout << " No log level settings available" << endl;
}
else {
cout << "Available rascsi log levels, sorted by severity:" << endl;
for (int i = 0; i < serverInfo.available_log_levels_size(); i++) {
cout << " " << serverInfo.available_log_levels(i) << endl;
cout << "rascsi log levels, sorted by severity:" << endl;
for (int i = 0; i < server_info.log_levels_size(); i++) {
cout << " " << server_info.log_levels(i) << endl;
}
cout << "Current rascsi log level: " << serverInfo.current_log_level() << endl;
cout << "Current rascsi log level: " << server_info.current_log_level() << endl;
}
cout << "Default image file folder: " << serverInfo.default_image_folder() << endl;
if (!serverInfo.available_image_files_size()) {
cout << " No image files available in the default folder" << endl;
cout << "Default image file folder: " << server_info.default_image_folder() << endl;
if (!server_info.image_files_size()) {
cout << " No image files available" << endl;
}
else {
list<string> sorted_files;
for (int i = 0; i < serverInfo.available_image_files_size(); i++) {
sorted_files.push_back(serverInfo.available_image_files(i).name());
}
sorted_files.sort();
list<PbImageFile> files = { server_info.image_files().begin(), server_info.image_files().end() };
files.sort([](const auto& a, const auto& b) { return a.name() < b.name(); });
cout << "Image files available in the default folder:" << endl;
for (auto it = sorted_files.begin(); it != sorted_files.end(); ++it) {
cout << " " << *it << endl;
cout << "Available image files:" << endl;
for (const auto& file : files) {
cout << " " << file.name() << " (" << file.size() << " bytes)";
if (file.read_only()) {
cout << ", read-only";
}
cout << endl;
}
}
cout << "Supported device types and their properties:" << endl;
for (auto it = server_info.types_properties().begin(); it != server_info.types_properties().end(); ++it) {
cout << " " << PbDeviceType_Name(it->type());
const PbDeviceProperties& properties = it->properties();
cout << " Supported LUNs: " << properties.luns() << endl;
if (properties.read_only() || properties.protectable() || properties.stoppable() || properties.read_only()
|| properties.lockable()) {
cout << " Properties: ";
bool has_property = false;
if (properties.read_only()) {
cout << "read-only";
has_property = true;
}
if (properties.protectable()) {
cout << (has_property ? ", " : "") << "protectable";
has_property = true;
}
if (properties.stoppable()) {
cout << (has_property ? ", " : "") << "stoppable";
has_property = true;
}
if (properties.removable()) {
cout << (has_property ? ", " : "") << "removable";
has_property = true;
}
if (properties.lockable()) {
cout << (has_property ? ", " : "") << "lockable";
}
cout << endl;
}
if (properties.supports_file()) {
cout << " Image file support" << endl;
}
else if (properties.supports_params()) {
cout << " Parameter support" << endl;
}
if (properties.supports_params() && properties.default_params_size()) {
list<string> params = { properties.default_params().begin(), properties.default_params().end() };
params.sort([](const auto& a, const auto& b) { return a < b; });
cout << " Default parameters: ";
bool isFirst = true;
for (const auto& param : params) {
if (!isFirst) {
cout << ", ";
}
cout << param;
isFirst = false;
}
cout << endl;
}
if (properties.block_sizes_size()) {
list<uint32_t> block_sizes = { properties.block_sizes().begin(), properties.block_sizes().end() };
block_sizes.sort([](const auto& a, const auto& b) { return a < b; });
cout << " Configurable block sizes in bytes: ";
bool isFirst = true;
for (const auto& block_size : block_sizes) {
if (!isFirst) {
cout << ", ";
}
cout << block_size;
isFirst = false;
}
cout << endl;
}
if (properties.capacities_size()) {
list<uint64_t> capacities = { properties.capacities().begin(), properties.capacities().end() };
capacities.sort([](const auto& a, const auto& b) { return a < b; });
cout << " Media capacities in bytes: ";
bool isFirst = true;
for (const auto& capacity : capacities) {
if (!isFirst) {
cout << ", ";
}
cout << capacity;
isFirst = false;
}
cout << endl;
}
}
if (server_info.reserved_ids_size()) {
cout << "Reserved device IDs: ";
for (int i = 0; i < server_info.reserved_ids_size(); i++) {
if(i) {
cout << ", ";
}
cout << server_info.reserved_ids(i);
}
cout <<endl;
}
if (server_info.devices_size()) {
list<PbDevice> sorted_devices = { server_info.devices().begin(), server_info.devices().end() };
sorted_devices.sort([](const auto& a, const auto& b) { return a.id() < b.id(); });
cout << "Attached devices:" << endl;
for (const auto& device : sorted_devices) {
DisplayDeviceInfo(device);
}
}
}
@ -226,6 +415,9 @@ PbOperation ParseOperation(const char *optarg)
case 'u':
return UNPROTECT;
case 's':
return DEVICE_INFO;
default:
return NONE;
}
@ -241,16 +433,25 @@ PbDeviceType ParseType(const char *optarg)
return type;
}
else {
// Parse legacy types
// Parse convenience types (shortcuts)
switch (tolower(optarg[0])) {
case 'm':
return SCMO;
case 'c':
return SCCD;
case 'b':
return SCBR;
case 'd':
return SCDP;
case 'h':
return SCHD;
case 'm':
return SCMO;
case 'r':
return SCRM;
}
}
@ -270,16 +471,19 @@ int main(int argc, char* argv[])
if (argc < 2) {
cerr << "SCSI Target Emulator RaSCSI Controller" << endl;
cerr << "version " << rascsi_get_version_string() << " (" << __DATE__ << ", " << __TIME__ << ")" << endl;
cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-n NAME] [-f FILE] [-d DEFAULT_IMAGE_FOLDER] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-v]" << endl;
cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-b BLOCK_SIZE] [-n NAME] [-f FILE] ";
cerr << "[-d DEFAULT_IMAGE_FOLDER] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] [-l] [-v]" << endl;
cerr << " where ID := {0|1|2|3|4|5|6|7}" << endl;
cerr << " UNIT := {0|1} default setting is 0." << endl;
cerr << " CMD := {attach|detach|insert|eject|protect|unprotect}" << endl;
cerr << " TYPE := {sahd|schd|scrm|sccd|scmo|scbr|scdp} or legacy types {hd|mo|cd|bridge}" << endl;
cerr << " UNIT := {0|1}, default setting is 0." << endl;
cerr << " CMD := {attach|detach|insert|eject|protect|unprotect|show}" << endl;
cerr << " TYPE := {sahd|schd|scrm|sccd|scmo|scbr|scdp} or convenience type {hd|rm|mo|cd|bridge|daynaport}" << endl;
cerr << " BLOCK_SIZE := {256|512|1024|2048|4096) bytes per hard disk drive block" << endl;
cerr << " NAME := name of device to attach (VENDOR:PRODUCT:REVISION)" << endl;
cerr << " FILE := image file path" << endl;
cerr << " DEFAULT_IMAGE_FOLDER := default location for image files, default is '~/images'" << endl;
cerr << " HOST := rascsi host to connect to, default is 'localhost'" << endl;
cerr << " PORT := rascsi port to connect to, default is 6868" << endl;
cerr << " RESERVED_IDS := comma-separated list of IDs to reserve" << endl;
cerr << " LOG_LEVEL := log level {trace|debug|info|warn|err|critical|off}, default is 'trace'" << endl;
cerr << " If CMD is 'attach' or 'insert' the FILE parameter is required." << endl;
cerr << "Usage: " << argv[0] << " -l" << endl;
@ -290,19 +494,19 @@ int main(int argc, char* argv[])
// Parse the arguments
PbCommand command;
PbDeviceDefinitions devices;
command.set_allocated_devices(&devices);
PbDeviceDefinition *device = devices.add_devices();
list<PbDeviceDefinition> devices;
PbDeviceDefinition* device = command.add_devices();
device->set_id(-1);
const char *hostname = "localhost";
int port = 6868;
string log_level;
string default_folder;
string reserved_ids;
bool list = false;
opterr = 1;
int opt;
while ((opt = getopt(argc, argv, "i:u:c:t:f:d:h:n:p:u:g:lsv")) != -1) {
while ((opt = getopt(argc, argv, "b:c:d:f:g:h:i:n:p:r:t:u:lsv")) != -1) {
switch (opt) {
case 'i':
device->set_id(optarg[0] - '0');
@ -312,21 +516,43 @@ int main(int argc, char* argv[])
device->set_unit(optarg[0] - '0');
break;
case 'b':
int block_size;
if (!GetAsInt(optarg, block_size)) {
cerr << "Error: Invalid block size " << optarg << endl;
exit(EXIT_FAILURE);
}
device->set_block_size(block_size);
break;
case 'c':
command.set_cmd(ParseOperation(optarg));
command.set_operation(ParseOperation(optarg));
if (command.operation() == NONE) {
cerr << "Error: Unknown operation '" << optarg << "'" << endl;
exit(EXIT_FAILURE);
}
break;
case 'd':
command.set_operation(DEFAULT_FOLDER);
default_folder = optarg;
break;
case 'f':
device->add_params(optarg);
break;
case 't':
device->set_type(ParseType(optarg));
if (device->type() == UNDEFINED) {
cerr << "Error: Unknown device type '" << optarg << "'" << endl;
exit(EXIT_FAILURE);
}
break;
case 'f':
device->set_file(optarg);
break;
case 'd':
command.set_cmd(DEFAULT_FOLDER);
default_folder = optarg;
case 'g':
command.set_operation(LOG_LEVEL);
log_level = optarg;
break;
case 'h':
@ -337,25 +563,49 @@ int main(int argc, char* argv[])
list = true;
break;
case 'n':
device->set_name(optarg);
case 'n': {
string vendor;
string product;
string revision;
string s = optarg;
size_t separatorPos = s.find(COMPONENT_SEPARATOR);
if (separatorPos != string::npos) {
vendor = s.substr(0, separatorPos);
s = s.substr(separatorPos + 1);
separatorPos = s.find(COMPONENT_SEPARATOR);
if (separatorPos != string::npos) {
product = s.substr(0, separatorPos);
revision = s.substr(separatorPos + 1);
}
else {
product = s;
}
}
else {
vendor = s;
}
device->set_vendor(vendor);
device->set_product(product);
device->set_revision(revision);
}
break;
case 'p':
port = atoi(optarg);
if (port <= 0 || port > 65535) {
cerr << "Invalid port " << optarg << ", port must be between 1 and 65535" << endl;
if (!GetAsInt(optarg, port) || port <= 0 || port > 65535) {
cerr << "Error: Invalid port " << optarg << ", port must be between 1 and 65535" << endl;
exit(EXIT_FAILURE);
}
break;
case 'g':
command.set_cmd(LOG_LEVEL);
log_level = optarg;
case 'r':
command.set_operation(RESERVE);
reserved_ids = optarg;
break;
case 's':
command.set_cmd(SERVER_INFO);
command.set_operation(SERVER_INFO);
break;
case 'v':
@ -369,19 +619,29 @@ int main(int argc, char* argv[])
exit(EXIT_FAILURE);
}
if (command.cmd() == LOG_LEVEL) {
CommandLogLevel(hostname, port, log_level);
exit(EXIT_SUCCESS);
}
switch(command.operation()) {
case LOG_LEVEL:
CommandLogLevel(hostname, port, log_level);
exit(EXIT_SUCCESS);
if (command.cmd() == DEFAULT_FOLDER) {
CommandDefaultImageFolder(hostname, port, default_folder);
exit(EXIT_SUCCESS);
}
case DEFAULT_FOLDER:
CommandDefaultImageFolder(hostname, port, default_folder);
exit(EXIT_SUCCESS);
if (command.cmd() == SERVER_INFO) {
CommandServerInfo(hostname, port);
exit(EXIT_SUCCESS);
case RESERVE:
CommandReserve(hostname, port, reserved_ids);
exit(EXIT_SUCCESS);
case DEVICE_INFO:
CommandDeviceInfo(hostname, port, command);
exit(EXIT_SUCCESS);
case SERVER_INFO:
CommandServerInfo(hostname, port);
exit(EXIT_SUCCESS);
default:
break;
}
if (list) {
@ -389,11 +649,8 @@ int main(int argc, char* argv[])
exit(EXIT_SUCCESS);
}
// Send the command
int fd = SendCommand(hostname, port, command);
bool status = ReceiveResult(fd);
close(fd);
PbResult result;
SendCommand(hostname, port, command, result);
// All done!
exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
exit(EXIT_SUCCESS);
}

View File

@ -10,10 +10,10 @@
//---------------------------------------------------------------------------
#include "os.h"
#include "xm6.h"
#include "fileio.h"
#include "filepath.h"
#include "gpiobus.h"
#include "rascsi.h"
#include "rascsi_version.h"
//---------------------------------------------------------------------------

View File

@ -7,6 +7,7 @@
//
//---------------------------------------------------------------------------
#include <list>
#include <sstream>
#include "rascsi_interface.pb.h"
#include "rasutil.h"
@ -14,27 +15,40 @@
using namespace std;
using namespace rascsi_interface;
//---------------------------------------------------------------------------
//
// List devices
//
//---------------------------------------------------------------------------
string ListDevices(const PbDevices& devices)
bool GetAsInt(const string& value, int& result)
{
ostringstream s;
if (devices.devices_size()) {
s << "+----+----+------+-------------------------------------" << endl
<< "| ID | UN | TYPE | DEVICE STATUS" << endl
<< "+----+----+------+-------------------------------------" << endl;
if (value.find_first_not_of("0123456789") != string::npos) {
return false;
}
else {
try {
result = std::stoul(value);
}
catch(const invalid_argument& e) {
return false;
}
catch(const out_of_range& e) {
return false;
}
return true;
}
string ListDevices(const list<PbDevice>& pb_devices)
{
if (pb_devices.empty()) {
return "No images currently attached.";
}
for (int i = 0; i < devices.devices_size() ; i++) {
PbDevice device = devices.devices(i);
ostringstream s;
s << "+----+----+------+-------------------------------------" << endl
<< "| ID | UN | TYPE | DEVICE STATUS" << endl
<< "+----+----+------+-------------------------------------" << endl;
list<PbDevice> devices = pb_devices;
devices.sort([](const auto& a, const auto& b) { return a.id() < b.id() && a.unit() < b.unit(); });
for (const auto& device : devices) {
string filename;
switch (device.type()) {
case SCBR:
@ -52,7 +66,8 @@ string ListDevices(const PbDevices& devices)
s << "| " << device.id() << " | " << device.unit() << " | " << PbDeviceType_Name(device.type()) << " | "
<< (filename.empty() ? "NO MEDIA" : filename)
<< (!device.removed() && (device.read_only() || device.protected_()) ? " (WRITEPROTECT)" : "") << endl;
<< (!device.status().removed() && (device.properties().read_only() || device.status().protected_()) ? " (WRITEPROTECT)" : "")
<< endl;
}
s << "+----+----+------+-------------------------------------";

View File

@ -11,7 +11,9 @@
#pragma once
#include <list>
#include <string>
#include "rascsi_interface.pb.h"
std::string ListDevices(const rascsi_interface::PbDevices&);
bool GetAsInt(const std::string&, int&);
std::string ListDevices(const std::list<rascsi_interface::PbDevice>&);

View File

@ -5,7 +5,7 @@
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ HDDダンプユーティリティ(イニシーエタモード/SASI Version) ]
// [ HDD dump utility (Initiator mode/SASI Version) ]
//
// SASI IMAGE EXAMPLE
// X68000
@ -19,39 +19,24 @@
//---------------------------------------------------------------------------
#include "os.h"
#include "xm6.h"
#include "fileio.h"
#include "filepath.h"
#include "gpiobus.h"
#include "rascsi.h"
#include "rascsi_version.h"
//---------------------------------------------------------------------------
//
// Constant declaration
//
//---------------------------------------------------------------------------
#define BUFSIZE 1024 * 64 // 64KBぐらいかなぁ
#define BUFSIZE 1024 * 64 // Maybe around 64KB?
//---------------------------------------------------------------------------
//
// Variable declaration
//
//---------------------------------------------------------------------------
GPIOBUS bus; // バス
int targetid; // ターゲットデバイスID
int unitid; // ターゲットユニットID
int bsiz; // ブロックサイズ
int bnum; // ブロック数
Filepath hdffile; // HDFファイル
bool restore; // リストアフラグ
BYTE buffer[BUFSIZE]; // ワークバッファ
int result; // 結果コード
GPIOBUS bus;
int targetid;
int unitid;
int bsiz; // Block size
int bnum; // Number of blocks
Filepath hdffile; // HDF file
bool restore; // Restore flag
BYTE buffer[BUFSIZE]; // Work buffer
int result; // Result code
//---------------------------------------------------------------------------
//
// Function declaration
//
//---------------------------------------------------------------------------
void Cleanup();
//---------------------------------------------------------------------------
@ -73,7 +58,7 @@ void KillHandler(int sig)
//---------------------------------------------------------------------------
bool Banner(int argc, char* argv[])
{
printf("RaSCSI hard disk dump utility(SASI HDD) ");
printf("RaSCSI hard disk dump utility (SASI HDD) ");
printf("version %s (%s, %s)\n",
rascsi_get_version_string(),
__DATE__,
@ -93,14 +78,9 @@ bool Banner(int argc, char* argv[])
return true;
}
//---------------------------------------------------------------------------
//
// Initialization
//
//---------------------------------------------------------------------------
bool Init()
{
// 割り込みハンドラ設定
// Interrupt handler settings
if (signal(SIGINT, KillHandler) == SIG_ERR) {
return false;
}
@ -111,12 +91,12 @@ bool Init()
return false;
}
// GPIO初期化
// GPIO initialization
if (!bus.Init(BUS::INITIATOR)) {
return false;
}
// ワーク初期化
// Work initialization
targetid = -1;
unitid = 0;
bsiz = 256;
@ -126,42 +106,26 @@ bool Init()
return true;
}
//---------------------------------------------------------------------------
//
// クリーンアップ
//
//---------------------------------------------------------------------------
void Cleanup()
{
// バスをクリーンアップ
bus.Cleanup();
}
//---------------------------------------------------------------------------
//
// Reset
//
//---------------------------------------------------------------------------
void Reset()
{
// Reset bus signal line
bus.Reset();
}
//---------------------------------------------------------------------------
//
// Argument processing
//
//---------------------------------------------------------------------------
bool ParseArgument(int argc, char* argv[])
{
int opt;
char *file;
// 初期化
// Initialization
file = NULL;
// 引数解析
// Argument parsing
opterr = 0;
while ((opt = getopt(argc, argv, "i:u:b:c:f:r")) != -1) {
switch (opt) {
@ -191,35 +155,35 @@ bool ParseArgument(int argc, char* argv[])
}
}
// TARGET IDチェック
// TARGET ID check
if (targetid < 0 || targetid > 7) {
fprintf(stderr,
"Error : Invalid target id range\n");
return false;
}
// UNIT IDチェック
// UNIT ID check
if (unitid < 0 || unitid > 1) {
fprintf(stderr,
"Error : Invalid unit id range\n");
return false;
}
// BSIZチェック
// BSIZ check
if (bsiz != 256 && bsiz != 512 && bsiz != 1024) {
fprintf(stderr,
"Error : Invalid block size\n");
return false;
}
// BNUMチェック
// BNUM check
if (bnum < 0) {
fprintf(stderr,
"Error : Invalid block count\n");
return false;
}
// ファイルチェック
// File check
if (!file) {
fprintf(stderr,
"Error : Invalid file path\n");
@ -231,16 +195,11 @@ bool ParseArgument(int argc, char* argv[])
return true;
}
//---------------------------------------------------------------------------
//
// Waiting for phase
//
//---------------------------------------------------------------------------
bool WaitPhase(BUS::phase_t phase)
{
DWORD now;
// タイムアウト(3000ms)
// Timeout (3000ms)
now = SysTimer::GetTimerLow();
while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) {
bus.Aquire();
@ -254,12 +213,11 @@ bool WaitPhase(BUS::phase_t phase)
//---------------------------------------------------------------------------
//
// バスフリーフェーズ実行
// Bus free phase execution
//
//---------------------------------------------------------------------------
void BusFree()
{
// バスリセット
bus.Reset();
}
@ -273,13 +231,13 @@ bool Selection(int id)
BYTE data;
int count;
// ID設定とSELアサート
// ID setting and SEL assertion
data = 0;
data |= (1 << id);
bus.SetDAT(data);
bus.SetSEL(TRUE);
// BSYを待つ
// Wait for BSY
count = 10000;
do {
usleep(20);
@ -289,10 +247,10 @@ bool Selection(int id)
}
} while (count--);
// SELネゲート
// SEL negate
bus.SetSEL(FALSE);
// ターゲットがビジー状態なら成功
// Return true if target is busy
return bus.GetBSY();
}
@ -305,20 +263,20 @@ bool Command(BYTE *buf, int length)
{
int count;
// フェーズ待ち
// Wait for phase
if (!WaitPhase(BUS::command)) {
return false;
}
// コマンド送信
// Send command
count = bus.SendHandShake(buf, length);
// 送信結果が依頼数と同じなら成功
// Return true is send results match number of requests
if (count == length) {
return true;
}
// 送信エラー
// Send error
return false;
}
@ -329,12 +287,12 @@ bool Command(BYTE *buf, int length)
//---------------------------------------------------------------------------
int DataIn(BYTE *buf, int length)
{
// フェーズ待ち
// Wait for phase
if (!WaitPhase(BUS::datain)) {
return -1;
}
// データ受信
// Receive data
return bus.ReceiveHandShake(buf, length);
}
@ -345,80 +303,78 @@ int DataIn(BYTE *buf, int length)
//---------------------------------------------------------------------------
int DataOut(BYTE *buf, int length)
{
// フェーズ待ち
// Wait for phase
if (!WaitPhase(BUS::dataout)) {
return -1;
}
// データ受信
// Receive data
return bus.SendHandShake(buf, length);
}
//---------------------------------------------------------------------------
//
// ステータスフェーズ実行
// Status phase execution
//
//---------------------------------------------------------------------------
int Status()
{
BYTE buf[256];
// フェーズ待ち
// Wait for phase
if (!WaitPhase(BUS::status)) {
return -2;
}
// データ受信
// Receive data
if (bus.ReceiveHandShake(buf, 1) == 1) {
return (int)buf[0];
}
// 受信エラー
// Receive error
return -1;
}
//---------------------------------------------------------------------------
//
// メッセージインフェーズ実行
// Message in phase execution
//
//---------------------------------------------------------------------------
int MessageIn()
{
BYTE buf[256];
// フェーズ待ち
// Wait for phase
if (!WaitPhase(BUS::msgin)) {
return -2;
}
// データ受信
// Receive data
if (bus.ReceiveHandShake(buf, 1) == 1) {
return (int)buf[0];
}
// 受信エラー
// Receive error
return -1;
}
//---------------------------------------------------------------------------
//
// TEST UNIT READY実行
// TEST UNIT READY execution
//
//---------------------------------------------------------------------------
int TestUnitReady(int id)
{
BYTE cmd[256];
// 結果コード初期化
// Initialize result code
result = 0;
// SELECTION
if (!Selection(id)) {
result = -1;
goto exit;
}
// COMMAND
memset(cmd, 0x00, 6);
cmd[0] = 0x00;
cmd[1] = unitid << 5;
@ -427,20 +383,17 @@ int TestUnitReady(int id)
goto exit;
}
// STATUS
if (Status() < 0) {
result = -4;
goto exit;
}
// MESSAGE IN
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
// バスフリー
BusFree();
return result;
@ -448,7 +401,7 @@ exit:
//---------------------------------------------------------------------------
//
// REQUEST SENSE実行
// REQUEST SENSE execution
//
//---------------------------------------------------------------------------
int RequestSense(int id, BYTE *buf)
@ -456,17 +409,15 @@ int RequestSense(int id, BYTE *buf)
BYTE cmd[256];
int count;
// 結果コード初期化
// Initialize result codes
result = 0;
count = 0;
// SELECTION
if (!Selection(id)) {
result = -1;
goto exit;
}
// COMMAND
memset(cmd, 0x00, 6);
cmd[0] = 0x03;
cmd[1] = unitid << 5;
@ -476,7 +427,6 @@ int RequestSense(int id, BYTE *buf)
goto exit;
}
// DATAIN
memset(buf, 0x00, 256);
count = DataIn(buf, 256);
if (count <= 0) {
@ -484,23 +434,20 @@ int RequestSense(int id, BYTE *buf)
goto exit;
}
// STATUS
if (Status() < 0) {
result = -4;
goto exit;
}
// MESSAGE IN
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
// バスフリー
BusFree();
// 成功であれば転送数を返す
// If successful, return number of transfers
if (result == 0) {
return count;
}
@ -510,7 +457,7 @@ exit:
//---------------------------------------------------------------------------
//
// READ6実行
// READ6 execution
//
//---------------------------------------------------------------------------
int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
@ -518,17 +465,15 @@ int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
BYTE cmd[256];
int count;
// 結果コード初期化
// Initialize result codes
result = 0;
count = 0;
// SELECTION
if (!Selection(id)) {
result = -1;
goto exit;
}
// COMMAND
memset(cmd, 0x00, 10);
cmd[0] = 0x8;
cmd[1] = (BYTE)(bstart >> 16);
@ -542,30 +487,26 @@ int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
goto exit;
}
// DATAIN
count = DataIn(buf, length);
if (count <= 0) {
result = -3;
goto exit;
}
// STATUS
if (Status() < 0) {
result = -4;
goto exit;
}
// MESSAGE IN
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
// バスフリー
BusFree();
// 成功であれば転送数を返す
// If successful, return number of transfers
if (result == 0) {
return count;
}
@ -575,7 +516,7 @@ exit:
//---------------------------------------------------------------------------
//
// WRITE6実行
// WRITE6 execution
//
//---------------------------------------------------------------------------
int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
@ -583,17 +524,15 @@ int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
BYTE cmd[256];
int count;
// 結果コード初期化
// Initialize result codes
result = 0;
count = 0;
// SELECTION
if (!Selection(id)) {
result = -1;
goto exit;
}
// COMMAND
memset(cmd, 0x00, 10);
cmd[0] = 0xa;
cmd[1] = (BYTE)(bstart >> 16);
@ -607,30 +546,26 @@ int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
goto exit;
}
// DATAOUT
count = DataOut(buf, length);
if (count <= 0) {
result = -3;
goto exit;
}
// STATUS
if (Status() < 0) {
result = -4;
goto exit;
}
// MESSAGE IN
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
// バスフリー
BusFree();
// 成功であれば転送数を返す
// If successful, return number of transfers
if (result == 0) {
return count;
}
@ -640,7 +575,7 @@ exit:
//---------------------------------------------------------------------------
//
// 主処理
// Main processing
//
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
@ -654,32 +589,28 @@ int main(int argc, char* argv[])
Fileio::OpenMode omode;
off_t size;
// バナー出力
// Output banner
if (!Banner(argc, argv)) {
exit(0);
}
// 初期化
if (!Init()) {
fprintf(stderr, "Error : Initializing\n");
// 恐らくrootでは無い
// Probably not executing as root?
exit(EPERM);
}
// 構築
if (!ParseArgument(argc, argv)) {
// クリーンアップ
Cleanup();
// 引数エラーで終了
// Exit with argument error
exit(EINVAL);
}
// リセット
Reset();
// ファイルオープン
// Open file
if (restore) {
omode = Fileio::ReadOnly;
} else {
@ -688,20 +619,18 @@ int main(int argc, char* argv[])
if (!fio.Open(hdffile.GetPath(), omode)) {
fprintf(stderr, "Error : Can't open hdf file\n");
// クリーンアップ
Cleanup();
exit(EPERM);
}
// バスフリー
BusFree();
// RESETシグナル発行
// Execute RESET signal
bus.SetRST(TRUE);
usleep(1000);
bus.SetRST(FALSE);
// ダンプ開始
// Start dump
printf("TARGET ID : %d\n", targetid);
printf("UNIT ID : %d\n", unitid);
@ -712,23 +641,22 @@ int main(int argc, char* argv[])
goto cleanup_exit;
}
// REQUEST SENSE(for CHECK CONDITION)
// REQUEST SENSE (for CHECK CONDITION)
count = RequestSense(targetid, buffer);
if (count < 0) {
fprintf(stderr, "REQUEST SENSE ERROR %d\n", count);
goto cleanup_exit;
}
// ブロックサイズとブロック数の表示
printf("Number of blocks : %d Blocks\n", bnum);
printf("Block length : %d Bytes\n", bsiz);
// データサイズの表示
// Display data size
printf("Total length : %d MBytes %d Bytes\n",
(bsiz * bnum / 1024 / 1024),
(bsiz * bnum));
// リストアファイルサイズの取得
// Get restore file size
if (restore) {
size = fio.GetFileSize();
printf("Restore file size : %d bytes", (int)size);
@ -741,7 +669,7 @@ int main(int argc, char* argv[])
printf("\n");
}
// バッファサイズ毎にダンプする
// Dump by buffer size
duni = BUFSIZE;
duni /= bsiz;
dsiz = BUFSIZE;
@ -789,7 +717,7 @@ int main(int argc, char* argv[])
printf("\033[0K");
}
// 容量上の端数処理
// Rounding of capacity
dnum = bnum % duni;
dsiz = dnum * bsiz;
@ -805,16 +733,13 @@ int main(int argc, char* argv[])
}
}
// 完了メッセージ
// Completion message
printf("%3d%%(%7d/%7d)\n", 100, bnum, bnum);
cleanup_exit:
// ファイルクローズ
fio.Close();
// クリーンアップ
Cleanup();
// 終了
exit(0);
}

View File

@ -10,8 +10,8 @@
//---------------------------------------------------------------------------
#include "os.h"
#include "xm6.h"
#include "scsi.h"
#include "rascsi.h"
//---------------------------------------------------------------------------
//

View File

@ -9,8 +9,7 @@
//
//---------------------------------------------------------------------------
#if !defined(scsi_h)
#define scsi_h
#pragma once
//===========================================================================
//
@ -65,41 +64,35 @@ public:
MONITOR = 2,
};
// Phase definition
// Phase definitions
enum phase_t : BYTE {
busfree, // バスフリーフェーズ
arbitration, // アービトレーションフェーズ
selection, // セレクションフェーズ
reselection, // リセレクションフェーズ
command, // コマンドフェーズ
execute, // 実行フェーズ Execute is an extension of the command phase
datain, // データイン
dataout, // データアウト
status, // ステータスフェーズ
msgin, // メッセージフェーズ
msgout, // メッセージアウトフェーズ
reserved // 未使用/リザーブ
busfree,
arbitration,
selection,
reselection,
command,
execute, // Execute phase is an extension of the command phase
datain,
dataout,
status,
msgin,
msgout,
reserved // Unused
};
BUS() { };
virtual ~BUS() { };
// Basic Functions
// 基本ファンクション
virtual BOOL Init(mode_e mode) = 0;
// 初期化
virtual void Reset() = 0;
// リセット
virtual void Cleanup() = 0;
// クリーンアップ
phase_t GetPhase();
// フェーズ取得
static phase_t GetPhase(DWORD mci)
{
return phase_table[mci];
}
// フェーズ取得
static const char* GetPhaseStrRaw(phase_t current_phase);
// Get the string phase name, based upon the raw data
@ -111,64 +104,39 @@ public:
}
virtual bool GetBSY() = 0;
// BSYシグナル取得
virtual void SetBSY(bool ast) = 0;
// BSYシグナル設定
virtual BOOL GetSEL() = 0;
// SELシグナル取得
virtual void SetSEL(BOOL ast) = 0;
// SELシグナル設定
virtual BOOL GetATN() = 0;
// ATNシグナル取得
virtual void SetATN(BOOL ast) = 0;
// ATNシグナル設定
virtual BOOL GetACK() = 0;
// ACKシグナル取得
virtual void SetACK(BOOL ast) = 0;
// ACKシグナル設定
virtual BOOL GetRST() = 0;
// RSTシグナル取得
virtual void SetRST(BOOL ast) = 0;
// RSTシグナル設定
virtual BOOL GetMSG() = 0;
// MSGシグナル取得
virtual void SetMSG(BOOL ast) = 0;
// MSGシグナル設定
virtual BOOL GetCD() = 0;
// CDシグナル取得
virtual void SetCD(BOOL ast) = 0;
// CDシグナル設定
virtual BOOL GetIO() = 0;
// IOシグナル取得
virtual void SetIO(BOOL ast) = 0;
// IOシグナル設定
virtual BOOL GetREQ() = 0;
// REQシグナル取得
virtual void SetREQ(BOOL ast) = 0;
// REQシグナル設定
virtual BYTE GetDAT() = 0;
// データシグナル取得
virtual void SetDAT(BYTE dat) = 0;
// データシグナル設定
virtual BOOL GetDP() = 0;
// パリティシグナル取得
virtual BOOL GetDP() = 0; // Get parity signal
virtual int CommandHandShake(BYTE *buf) = 0;
// コマンド受信ハンドシェイク
virtual int ReceiveHandShake(BYTE *buf, int count) = 0;
// データ受信ハンドシェイク
virtual int SendHandShake(BYTE *buf, int count, int delay_after_bytes) = 0;
// データ送信ハンドシェイク
virtual BOOL GetSignal(int pin) = 0;
// Get SCSI input signal value
@ -181,89 +149,6 @@ protected:
private:
static const phase_t phase_table[8];
// フェーズテーブル
static const char* phase_str_table[];
};
typedef struct __attribute__((packed)) {
BYTE operation_code;
BYTE misc_cdb_information;
BYTE page_code;
WORD length;
BYTE control;
} scsi_cdb_6_byte_t;
typedef struct __attribute__((packed)) {
BYTE operation_code;
BYTE service_action;
DWORD logical_block_address;
BYTE misc_cdb_information;
WORD length;
BYTE control;
} scsi_cdb_10_byte_t;
typedef struct __attribute__((packed)) {
BYTE operation_code;
BYTE service_action;
DWORD logical_block_address;
DWORD length;
BYTE misc_cdb_information;
BYTE control;
} scsi_cdb_12_byte_t;
typedef struct __attribute__((packed)) {
BYTE operation_code;
BYTE service_action;
DWORD logical_block_address;
DWORD length;
BYTE misc_cdb_information;
BYTE control;
} scsi_cdb_16_byte_t;
typedef struct __attribute__((packed)) {
BYTE operation_code;
BYTE reserved;
BYTE page_code;
WORD allocation_length;
BYTE control;
} scsi_cmd_inquiry_t;
typedef struct __attribute__((packed)) {
BYTE operation_code;
BYTE lba_msb_bits_4_0;
WORD logical_block_address;
BYTE transfer_length;
BYTE control;
} scsi_cmd_read_6_t;
typedef struct __attribute__((packed)) {
BYTE operation_code;
BYTE flags;
DWORD logical_block_address;
BYTE group_number;
WORD transfer_length;
BYTE control;
} scsi_cmd_read_10_t;
typedef struct __attribute__((packed)) {
BYTE operation_code;
BYTE flags;
DWORD logical_block_address;
DWORD transfer_length;
BYTE group_number;
BYTE control;
} scsi_cmd_read_12_t;
typedef struct __attribute__((packed)) {
BYTE operation_code;
BYTE descriptor_format;
WORD reserved;
BYTE allocation_length;
BYTE control;
} scsi_cmd_request_sense_t;
#endif // scsi_h

View File

@ -10,7 +10,6 @@
//---------------------------------------------------------------------------
#include "os.h"
#include "xm6.h"
#include "filepath.h"
#include "fileio.h"
#include "devices/disk.h"
@ -21,6 +20,7 @@
#include <sys/time.h>
#include <climits>
#include <sstream>
#include "rascsi.h"
//---------------------------------------------------------------------------
//
@ -57,7 +57,7 @@ static volatile bool running; // Running flag
GPIOBUS *bus; // GPIO Bus
typedef struct data_capture{
DWORD data;
QWORD timestamp;
uint64_t timestamp;
} data_capture_t;
data_capture data_buffer[MAX_BUFF_SIZE];
@ -266,7 +266,7 @@ void create_value_change_dump()
while(i < data_idx)
{
ostringstream s;
s << (QWORD)(data_buffer[i].timestamp*ns_per_loop);
s << (uint64_t)(data_buffer[i].timestamp*ns_per_loop);
fprintf(fp, "#%s\n",s.str().c_str());
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_BSY, SYMBOL_PIN_BSY);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_SEL, SYMBOL_PIN_SEL);
@ -362,9 +362,9 @@ int main(int argc, char* argv[])
int ret;
struct sched_param schparam;
timeval start_time, stop_time;
QWORD loop_count = 0;
uint64_t loop_count = 0;
timeval time_diff;
QWORD elapsed_us;
uint64_t elapsed_us;
int str_len;
// If there is an argument specified and it is NOT -h or --help

View File

@ -2,8 +2,15 @@ import fnmatch
import os
import subprocess
import time
import io
import re
import sys
from ractl_cmds import attach_image
from ractl_cmds import (
attach_image,
detach_all,
list_devices,
)
from settings import *
@ -19,10 +26,9 @@ def create_new_image(file_name, type, size):
)
def delete_image(file_name):
full_path = base_dir + file_name
if os.path.exists(full_path):
os.remove(full_path)
def delete_file(file_name):
if os.path.exists(file_name):
os.remove(file_name)
return True
else:
return False
@ -71,3 +77,49 @@ def download_image(url):
full_path = base_dir + file_name
urllib.request.urlretrieve(url, full_path)
def write_config_csv(file_name):
import csv
# This method takes the output of 'rasctl -l' and parses it into csv format:
# 0: ID
# 1: Unit Number (unused in rascsi-web)
# 2: Device Type
# 3: Device Status (includes the path to a loaded image file)
# TODO: Remove the dependence on rasctl; e.g. when implementing protobuf for rascsi-web
try:
with open(file_name, "w") as csv_file:
writer = csv.writer(csv_file)
for device in list_devices():
if device["type"] != "-":
device_info = list (device.values())
# Match a *nix file path inside column 3, cutting out the last chunk that starts with a space
filesearch = re.search("(^(/[^/ ]*)+)(\s.*)*$", device_info[3])
if filesearch is None:
device_info[3] = ""
else:
device_info[3] = filesearch.group(1)
writer.writerow(device_info)
return True
except:
print ("Could not open file for writing: ", file_name)
return False
def read_config_csv(file_name):
import csv
try:
with open(file_name) as csv_file:
detach_all()
config_reader = csv.reader(csv_file)
#TODO: Remove hard-coded string sanitation (e.g. after implementing protobuf)
exclude_list = ("X68000 HOST BRIDGE", "DaynaPort SCSI/Link", " (WRITEPROTECT)", "NO MEDIA")
for row in config_reader:
image_name = row[3]
for e in exclude_list:
image_name = image_name.replace(e, "")
attach_image(row[0], image_name, row[2])
return True
except:
print ("Could not access file: ", file_name)
return False

View File

@ -1,14 +1,13 @@
import fnmatch
import subprocess
import re
import logging
from settings import *
valid_file_suffix = ["*.hda", "*.hdn", "*.hdi", "*.nhd", "*.hdf", "*.hds", "*.hdr", "*.iso", "*.cdr", "*.toast", "*.img", "*.zip"]
valid_file_types = r"|".join([fnmatch.translate(x) for x in valid_file_suffix])
# List of SCSI ID's you'd like to exclude - eg if you are on a Mac, the System is usually 7
EXCLUDE_SCSI_IDS = [7]
def is_active():
@ -46,17 +45,18 @@ def list_config_files():
return files_list
def get_valid_scsi_ids(devices):
invalid_list = EXCLUDE_SCSI_IDS.copy()
def get_valid_scsi_ids(devices, invalid_list):
for device in devices:
if device["file"] != "NO MEDIA" and device["file"] != "-":
invalid_list.append(int(device["id"]))
valid_list = list(range(8))
for id in invalid_list:
valid_list.remove(id)
try:
valid_list.remove(int(id))
except:
logging.warning("Invalid SCSI id " + str(id))
valid_list.reverse()
return valid_list
@ -79,7 +79,7 @@ def detach_by_id(scsi_id):
def detach_all():
for scsi_id in range(0, 7):
for scsi_id in range(0, 8):
subprocess.run(["rasctl", "-c" "detach", "-i", str(scsi_id)])
@ -158,3 +158,7 @@ def list_devices():
device_list[idx]["file"] = segments[4].strip()
return device_list
def reserve_scsi_ids(reserved_scsi_ids):
scsi_ids = ",".join(list(reserved_scsi_ids))
return subprocess.run(["rasctl", "-r", scsi_ids])

View File

@ -5,10 +5,12 @@ After=network.target
[Service]
Type=simple
Restart=always
ExecStart=/home/pi/RASCSI/src/web/start.sh
ExecStart=/home/pi/RASCSI/src/web/start.sh --reserved_ids=7
# Use the --reserved_ids argument to define an id or range of ids that will be unavailable for attaching devices.
# The default is 7 for Macintosh. Ex. to reserve IDs 0, 1, and 7 do: --reserved_ids=017
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=RASCSIWEB
[Install]
WantedBy=multi-user.target
WantedBy=multi-user.target

View File

@ -2,6 +2,22 @@
set -e
# set -x # Uncomment to Debug
# parse arguments
while [ "$1" != "" ]; do
PARAM=`echo $1 | awk -F= '{print $1}'`
VALUE=`echo $1 | awk -F= '{print $2}'`
case $PARAM in
-r | --reserved_ids)
RESERVED_IDS=$VALUE
;;
*)
echo "ERROR: unknown parameter \"$PARAM\""
exit 1
;;
esac
shift
done
cd $(dirname $0)
# verify packages installed
ERROR=0
@ -56,4 +72,4 @@ else
fi
echo "Starting web server..."
python3 web.py
python3 web.py ${RESERVED_IDS}

View File

@ -21,14 +21,19 @@
{% block content %}
<h2>Current RaSCSI Configuration</h2>
<p>The <em>default</em> configuration will be loaded when the Web UI starts up.</p>
<p>
<form action="/config/load" method="post">
<select name="name" >
{% for config in config_files %}
<option value="{{config}}">{{config.replace(".csv", '')}}</option>
{% endfor %}
</select>
<input type="submit" value="Load" />
<input type="submit" name="load" value="Load" />
<input type="submit" name="delete" value="Delete" />
</form>
</p>
<p>
<form action="/config/save" method="post">
<input name="name" placeholder="default">
<input type="submit" value="Save" />
@ -36,6 +41,7 @@
<form action="/scsi/detach_all" method="post" onsubmit="return confirm('Detach all SCSI Devices?')">
<input type="submit" value="Detach All" />
</form>
</p>
<table cellpadding="3" border="black">
<tbody>
@ -47,7 +53,7 @@
</tr>
{% for device in devices %}
<tr>
{% if device.id != "7" %}
{% if device.id not in reserved_scsi_ids %}
<td style="text-align:center">{{device.id}}</td>
<td style="text-align:center">{{device.type}}</td>
<td>{{device.file}}</td>
@ -67,7 +73,7 @@
{% else %}
<td style="text-align:center">{{device.id}}</td>
<td style="text-align:center">-</td>
<td>Host Machine</td>
<td>Reserved ID</td>
<td>-</td>
{% endif %}
</tr>

View File

@ -1,14 +1,17 @@
import io
import re
import sys
from flask import Flask, render_template, request, flash, url_for, redirect, send_file, send_from_directory
from file_cmds import (
create_new_image,
download_file_to_iso,
delete_image,
delete_file,
unzip_file,
download_image,
write_config_csv,
read_config_csv,
)
from pi_cmds import shutdown_pi, reboot_pi, running_version, rascsi_service
from ractl_cmds import (
@ -25,7 +28,8 @@ from ractl_cmds import (
list_config_files,
detach_all,
valid_file_suffix,
valid_file_types
valid_file_types,
reserve_scsi_ids,
)
from settings import *
@ -35,7 +39,8 @@ app = Flask(__name__)
@app.route("/")
def index():
devices = list_devices()
scsi_ids = get_valid_scsi_ids(devices)
reserved_scsi_ids = app.config.get("RESERVED_SCSI_IDS")
scsi_ids = get_valid_scsi_ids(devices, list(reserved_scsi_ids))
return render_template(
"index.html",
bridge_configured=is_bridge_setup("eth0"),
@ -45,6 +50,7 @@ def index():
config_files=list_config_files(),
base_dir=base_dir,
scsi_ids=scsi_ids,
reserved_scsi_ids=reserved_scsi_ids,
max_file_size=MAX_FILE_SIZE,
version=running_version(),
)
@ -57,30 +63,28 @@ def send_pwa_files(path):
def config_save():
file_name = request.form.get("name") or "default"
file_name = f"{base_dir}{file_name}.csv"
import csv
with open(file_name, "w") as csv_file:
writer = csv.writer(csv_file)
for device in list_devices():
if device["type"] != "-":
writer.writerow(device.values())
write_config_csv(file_name)
flash(f"Saved config to {file_name}!")
return redirect(url_for("index"))
@app.route("/config/load", methods=["POST"])
def config_load():
file_name = request.form.get("name") or "default.csv"
file_name = request.form.get("name")
file_name = f"{base_dir}{file_name}"
detach_all()
import csv
with open(file_name) as csv_file:
config_reader = csv.reader(csv_file)
for row in config_reader:
image_name = row[3].replace("(WRITEPROTECT)", "")
attach_image(row[0], image_name, row[2])
flash(f"Loaded config from {file_name}!")
if "load" in request.form:
if read_config_csv(file_name):
flash(f"Loaded config from {file_name}!")
else:
flash(f"Failed to load {file_name}!", "error")
elif "delete" in request.form:
if delete_file(file_name):
flash(f"Deleted config {file_name}!")
else:
flash(f"Failed to delete {file_name}!", "error")
return redirect(url_for("index"))
@ -147,6 +151,11 @@ def attach():
flash(f"Unknown file type. Valid files are: {', '.join(valid_file_suffix)}", "error")
return redirect(url_for("index"))
# Validate the SCSI ID
if re.match("[0-7]", str(scsi_id)) == None:
flash(f"Invalid SCSI ID. Should be a number between 0-7", "error")
return redirect(url_for("index"))
process = attach_image(scsi_id, file_name, image_type)
if process.returncode == 0:
flash(f"Attached {file_name} to SCSI id {scsi_id}!")
@ -204,6 +213,9 @@ def restart():
def rascsi_restart():
rascsi_service("restart")
flash("Restarting RaSCSI Service...")
reserved_scsi_ids = app.config.get("RESERVED_SCSI_IDS")
if reserved_scsi_ids != "":
reserve_scsi_ids(reserved_scsi_ids)
return redirect(url_for("index"))
@ -286,7 +298,7 @@ def download():
@app.route("/files/delete", methods=["POST"])
def delete():
image = request.form.get("image")
if delete_image(image):
if delete_file(base_dir + image):
flash("File " + image + " deleted")
return redirect(url_for("index"))
else:
@ -312,6 +324,15 @@ if __name__ == "__main__":
app.config["UPLOAD_FOLDER"] = base_dir
os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
app.config["MAX_CONTENT_LENGTH"] = MAX_FILE_SIZE
if len(sys.argv) >= 2:
app.config["RESERVED_SCSI_IDS"] = str(sys.argv[1])
# Reserve SCSI IDs on the backend side to prevent use
reserve_scsi_ids(app.config.get("RESERVED_SCSI_IDS"))
else:
app.config["RESERVED_SCSI_IDS"] = ""
# Load the configuration in default.cvs, if it exists
read_config_csv(f"{base_dir}default.csv")
import bjoern
print("Serving rascsi-web...")