Merge branch 'develop' of https://github.com/akuker/RASCSI into bug_335_cache_fix_selectable

This commit is contained in:
RaSCSI User 2022-02-22 02:37:57 +00:00
commit 9de6804cc5
62 changed files with 1719 additions and 1086 deletions

View File

@ -71,9 +71,9 @@ Set the vendor, product and revision for the device, to be returned with the INQ
The rascsi server port, default is 6868.
.TP
.BR \-r\fI " " \fIRESERVED_IDS
Comma-separated list of IDs to reserve.
Comma-separated list of IDs to reserve. Pass an empty list in order to not reserve anything.
.BR \-p\fI " " \fITYPE
The optional case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP, SCHS). If no type is specified for devices that support an image file, rascsi tries to derive the type from the file extension.
The optional case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP, SCLP, SCHS). If no type is specified for devices that support an image file, rascsi tries to derive the type from the file extension.
.TP
.BR \-v\fI " " \fI
Display the rascsi version.
@ -84,7 +84,7 @@ Overrides the default locale for client-faces error messages. The client can ove
.BR \-ID\fIn[:u] " " \fIFILE
n is the SCSI ID number (0-7). u (0-31) is the optional LUN (logical unit). The default LUN is 0.
.IP
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, SCHS) 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, SCLP, SCHS) the filename may have a special meaning or a dummy name can be provided. For SCBR and SCDP it is an optioinal prioritized list of network interfaces, an optional IP address and netmask, e.g. "interfaces=eth0,eth1,wlan0:inet=10.10.20.1/24". For SCLP it is the print command to be used and a reservation timeout in seconds, e.g. "cmd=lp -oraw %f:timeout=60".
.TP
.BR \-HD\fIn[:u] " " \fIFILE
n is the SASI ID number (0-15). The effective SASI ID is calculated as n/2, the effective SASI LUN is calculated is the remainder of n/2. Alternatively the n:u syntax can be used, where ns is the SASI ID (0-7) and u the LUN (0-1).

View File

@ -91,25 +91,31 @@ OPTIONS
The rascsi server port, default is 6868.
-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, SCHS). If no type is specified for devices that sup
port an image file, rascsi tries to derive the type from the
file extension.
Comma-separated list of IDs to reserve. Pass an empty list in
order to not reserve anything. -p TYPE The optional case-insen
sitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP,
SCLP, SCHS). If no type is specified for devices that support an
image file, rascsi tries to derive the type from the file exten
sion.
-v Display the rascsi version.
-z LOCALE
Overrides the default locale for client-faces error messages.
Overrides the default locale for client-faces error messages.
The client can override the locale.
-IDn[:u] FILE
n is the SCSI ID number (0-7). u (0-31) is the optional LUN
n is the SCSI ID number (0-7). u (0-31) is the optional LUN
(logical unit). The default LUN is 0.
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, SCHS)
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, SCLP,
SCHS) the filename may have a special meaning or a dummy name
can be provided. For SCBR and SCDP it is an optioinal priori
tized list of network interfaces, an optional IP address and
netmask, e.g. "interfaces=eth0,eth1,wlan0:inet=10.10.20.1/24".
For SCLP it is the print command to be used and a reservation
timeout in seconds, e.g. "cmd=lp -oraw %f:timeout=60".
-HDn[:u] FILE
n is the SASI ID number (0-15). The effective SASI ID is calcu

View File

@ -94,7 +94,7 @@ Rename an image file in the default image folder.
The rascsi port to connect to, default is 6868.
.TP
.BR \-r\fI " " \fIRESERVED_IDS
Comma-separated list of IDs to reserve.
Comma-separated list of IDs to reserve. Pass an empty list in order to not reserve anything.
.TP
.BR \-s\fI
Display server-side settings like available images or supported device types.
@ -147,6 +147,7 @@ Specifies the device type. This type overrides the type derived from the file ex
rm: SCSI removable media drive
cd: CD-ROM
mo: Magneto-Optical disk
lp: SCSI printer
bridge: Bridge device (Only applicable to the Sharp X68000)
daynaport: DaynaPort network adapter
services: Host services device

View File

@ -71,9 +71,10 @@ OPTIONS
The rascsi port to connect to, default is 6868.
-r RESERVED_IDS
Comma-separated list of IDs to reserve.
Comma-separated list of IDs to reserve. Pass an empty list in
order to not reserve anything.
-s Display server-side settings like available images or supported
-s Display server-side settings like available images or supported
device types.
-T Display all device types and their properties.
@ -100,7 +101,7 @@ OPTIONS
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
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)
@ -110,38 +111,39 @@ 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 size is 512 bytes. For SASI drives 256 or 1024
bytes, default is 256 bytes.
-f FILE|PARAM
Device-specific: Either a path to a disk image file, or a param
eter for a non-disk device. See the rascsi(1) man page for per
eter for a non-disk device. See the rascsi(1) man page for per
mitted file types.
-t TYPE
Specifies the device type. This type overrides the type derived
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
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
mo: Magneto-Optical disk
lp: SCSI printer
bridge: Bridge device (Only applicable to the Sharp X68000)
daynaport: DaynaPort network adapter
services: Host services device
-n VENDOR:PRODUCT:REVISION
The vendor, product and revision for the device, to be returned
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
tomatically applied. Once set the name of a device cannot be
changed.
-u UNIT
Unit number (0-31). This will default to 0. This option is only
used when there are multiple SCSI devices on a shared SCSI con
Unit number (0-31). This will default to 0. This option is only
used when there are multiple SCSI devices on a shared SCSI con
troller. (This is not common)
EXAMPLES

View File

@ -113,9 +113,17 @@ function installRaScsi() {
}
function preparePythonCommon() {
if [ -f "$PYTHON_COMMON_PATH/rascsi_interface_pb2.py" ]; then
sudo rm "$PYTHON_COMMON_PATH/rascsi_interface_pb2.py"
echo "Deleting old Python protobuf library rascsi_interface_pb2.py"
if [ -f "$WEB_INSTALL_PATH/src/rascsi_interface_pb2.py" ]; then
sudo rm "$WEB_INSTALL_PATH/src/rascsi_interface_pb2.py"
echo "Deleting old Python protobuf library $WEB_INSTALL_PATH/src/rascsi_interface_pb2.py"
fi
if [ -f "$OLED_INSTALL_PATH/src/rascsi_interface_pb2.py" ]; then
sudo rm "$OLED_INSTALL_PATH/src/rascsi_interface_pb2.py"
echo "Deleting old Python protobuf library $OLED_INSTALL_PATH/src/rascsi_interface_pb2.py"
fi
if [ -f "$PYTHON_COMMON_PATH/src/rascsi_interface_pb2.py" ]; then
sudo rm "$PYTHON_COMMON_PATH/src/rascsi_interface_pb2.py"
echo "Deleting old Python protobuf library $PYTHON_COMMON_PATH/src/rascsi_interface_pb2.py"
fi
echo "Compiling the Python protobuf library rascsi_interface_pb2.py..."
protoc -I="$BASE/src/raspberrypi/" --python_out="$PYTHON_COMMON_PATH/src" rascsi_interface.proto

View File

@ -4,13 +4,8 @@ Module for general settings used in the rascsi module
from os import getcwd
WORK_DIR = getcwd()
REMOVABLE_DEVICE_TYPES = ("SCCD", "SCRM", "SCMO")
NETWORK_DEVICE_TYPES = ("SCDP", "SCBR")
# There may be a more elegant way to get the HOME dir of the user that installed RaSCSI
HOME_DIR = "/".join(WORK_DIR.split("/")[0:3])
HOME_DIR = "/".join(getcwd().split("/")[0:3])
CFG_DIR = f"{HOME_DIR}/.config/rascsi"
CONFIG_FILE_SUFFIX = "json"

View File

@ -450,17 +450,16 @@ class FileCmds:
for row in config["devices"]:
kwargs = {
"device_type": row["device_type"],
"image": row["image"],
"unit": int(row["unit"]),
"vendor": row["vendor"],
"product": row["product"],
"revision": row["revision"],
"block_size": row["block_size"],
"params": dict(row["params"]),
}
params = dict(row["params"])
for param in params.keys():
kwargs[param] = params[param]
self.ractl.attach_image(row["id"], **kwargs)
if row["image"]:
kwargs["params"]["file"] = row["image"]
self.ractl.attach_device(row["id"], **kwargs)
# The config file format in RaSCSI 21.10 is using a list data type at the top level.
# If future config file formats return to the list data type,
# introduce more sophisticated format detection logic here.
@ -470,17 +469,17 @@ class FileCmds:
kwargs = {
"device_type": row["device_type"],
"image": row["image"],
# "un" for backwards compatibility
"unit": int(row["un"]),
"vendor": row["vendor"],
"product": row["product"],
"revision": row["revision"],
"block_size": row["block_size"],
"params": dict(row["params"]),
}
params = dict(row["params"])
for param in params.keys():
kwargs[param] = params[param]
self.ractl.attach_image(row["id"], **kwargs)
if row["image"]:
kwargs["params"]["file"] = row["image"]
self.ractl.attach_device(row["id"], **kwargs)
logging.warning("%s is in an obsolete config file format", file_name)
else:
return {"status": False,
"return_code": ReturnCodes.READCONFIG_INVALID_CONFIG_FILE_FORMAT}

View File

@ -3,7 +3,6 @@ Module for commands sent to the RaSCSI backend service.
"""
import rascsi_interface_pb2 as proto
from rascsi.common_settings import REMOVABLE_DEVICE_TYPES
from rascsi.return_codes import ReturnCodes
from rascsi.socket_cmds import SocketCmds
@ -125,7 +124,8 @@ class RaCtlCmds:
Sends a DEVICE_TYPES_INFO command to the server.
Returns a dict with:
- (bool) status
- (list) of (str) device_types (device types that RaSCSI supports, ex. SCHD, SCCD, etc)
- (dict) device_types, where keys are the four letter device type acronym,
and the value is a (dict) of supported parameters and their default values.
"""
command = proto.PbCommand()
command.operation = proto.PbOperation.DEVICE_TYPES_INFO
@ -135,11 +135,55 @@ class RaCtlCmds:
data = self.sock_cmd.send_pb_command(command.SerializeToString())
result = proto.PbResult()
result.ParseFromString(data)
device_types = []
for prop in result.device_types_info.properties:
device_types.append(proto.PbDeviceType.Name(prop.type))
device_types = {}
for device in result.device_types_info.properties:
params = {}
for key, value in device.properties.default_params.items():
params[key] = value
device_types[proto.PbDeviceType.Name(device.type)] = {
"removable": device.properties.removable,
"supports_file": device.properties.supports_file,
"params": params,
"block_sizes": device.properties.block_sizes,
}
return {"status": result.status, "device_types": device_types}
def get_removable_device_types(self):
"""
Returns a (list) of (str) of four letter device acronyms
that are of the removable type.
"""
device_types = self.get_device_types()
removable_device_types = []
for device, value in device_types["device_types"].items():
if value["removable"]:
removable_device_types.append(device)
return removable_device_types
def get_disk_device_types(self):
"""
Returns a (list) of (str) of four letter device acronyms
that take image files as arguments.
"""
device_types = self.get_device_types()
disk_device_types = []
for device, value in device_types["device_types"].items():
if value["supports_file"]:
disk_device_types.append(device)
return disk_device_types
def get_peripheral_device_types(self):
"""
Returns a (list) of (str) of four letter device acronyms
that don't take image files as arguments.
"""
device_types = self.get_device_types()
image_device_types = self.get_disk_device_types()
peripheral_device_types = [
x for x in device_types["device_types"] if x not in image_device_types
]
return peripheral_device_types
def get_image_files_info(self):
"""
Sends a DEFAULT_IMAGE_FILES_INFO command to the server.
@ -167,7 +211,7 @@ class RaCtlCmds:
"scan_depth": scan_depth,
}
def attach_image(self, scsi_id, **kwargs):
def attach_device(self, scsi_id, **kwargs):
"""
Takes (int) scsi_id and kwargs containing 0 or more device properties
@ -185,14 +229,15 @@ class RaCtlCmds:
devices.id = int(scsi_id)
if "device_type" in kwargs.keys():
if kwargs["device_type"] not in [None, ""]:
if kwargs["device_type"]:
devices.type = proto.PbDeviceType.Value(str(kwargs["device_type"]))
if "unit" in kwargs.keys():
if kwargs["unit"] not in [None, ""]:
if kwargs["unit"]:
devices.unit = kwargs["unit"]
if "image" in kwargs.keys():
if kwargs["image"] not in [None, ""]:
devices.params["file"] = kwargs["image"]
if "params" in kwargs.keys():
if isinstance(kwargs["params"], dict):
for param in kwargs["params"]:
devices.params[param] = kwargs["params"][param]
# Handling the inserting of media into an attached removable type device
device_type = kwargs.get("device_type", None)
@ -202,7 +247,8 @@ class RaCtlCmds:
else:
current_type = None
if device_type in REMOVABLE_DEVICE_TYPES and current_type in REMOVABLE_DEVICE_TYPES:
removable_device_types = self.get_removable_device_types()
if device_type in removable_device_types and current_type in removable_device_types:
if current_type != device_type:
parameters = {
"device_type": device_type,
@ -214,23 +260,21 @@ class RaCtlCmds:
"parameters": parameters,
}
command.operation = proto.PbOperation.INSERT
# Handling attaching a new device
else:
command.operation = proto.PbOperation.ATTACH
if "interfaces" in kwargs.keys():
if kwargs["interfaces"] not in [None, ""]:
devices.params["interfaces"] = kwargs["interfaces"]
if "vendor" in kwargs.keys():
if kwargs["vendor"] is not None:
if kwargs["vendor"]:
devices.vendor = kwargs["vendor"]
if "product" in kwargs.keys():
if kwargs["product"] is not None:
if kwargs["product"]:
devices.product = kwargs["product"]
if "revision" in kwargs.keys():
if kwargs["revision"] is not None:
if kwargs["revision"]:
devices.revision = kwargs["revision"]
if "block_size" in kwargs.keys():
if kwargs["block_size"] not in [None, ""]:
if kwargs["block_size"]:
devices.block_size = int(kwargs["block_size"])
command.devices.append(devices)

View File

@ -43,11 +43,6 @@ from pi_cmds import get_ip_and_host
from rascsi.ractl_cmds import RaCtlCmds
from rascsi.socket_cmds import SocketCmds
from rascsi.common_settings import (
REMOVABLE_DEVICE_TYPES,
NETWORK_DEVICE_TYPES,
)
parser = argparse.ArgumentParser(description="RaSCSI OLED Monitor script")
parser.add_argument(
"--rotation",
@ -165,7 +160,8 @@ LINE_SPACING = 8
FONT = ImageFont.truetype('resources/type_writer.ttf', FONT_SIZE)
IP_ADDR, HOSTNAME = get_ip_and_host()
REMOVABLE_DEVICE_TYPES = ractl_cmd.get_removable_device_types()
SUPPORT_DEVICE_TYPES = ractl_cmd.get_support_device_types()
def formatted_output():
"""
@ -186,10 +182,13 @@ def formatted_output():
f"{line['file']} {line['status']}")
else:
output.append(f"{line['id']} {line['device_type'][2:4]} {line['status']}")
# Special handling for network devices
elif line["device_type"] in NETWORK_DEVICE_TYPES:
output.append(f"{line['id']} {line['device_type'][2:4]} {line['vendor']} "
f"{line['product']}")
# Special handling of devices that don't use image files
elif line["device_type"] in (SUPPORT_DEVICE_TYPES):
if line["vendor"] == "RaSCSI":
output.append(f"{line['id']} {line['device_type'][2:4]} {line['product']}")
else:
output.append(f"{line['id']} {line['device_type'][2:4]} {line['vendor']} "
f"{line['product']}")
# Print only the Vendor/Product info if it's not generic RaSCSI
elif line["vendor"] not in "RaSCSI":
output.append(f"{line['id']} {line['device_type'][2:4]} {line['file']} "

View File

@ -52,29 +52,39 @@ def sort_and_format_devices(devices):
return formatted_devices
def extend_device_names(device_types):
def map_device_types_and_names(device_types):
"""
Takes a (list) of (str) device_types with the four letter device acronyms
Takes a (dict) corresponding to the data structure returned by RaCtlCmds.get_device_types()
Returns a (dict) of device_type:device_name mappings of localized device names
"""
mapped_device_types = {}
for device_type in device_types:
if device_type is "SAHD":
device_name = _("SASI Hard Drive")
elif device_type is "SCHD":
device_name = _("SCSI Hard Drive")
elif device_type is "SCRM":
device_name = _("Removable Drive")
elif device_type is "SCMO":
device_name = _("Magneto-Optical Drive")
elif device_type is "SCCD":
device_name = _("CD-ROM Drive")
elif device_type is "SCBR":
device_name = _("X68000 Host Bridge")
elif device_type is "SCDP":
device_name = _("DaynaPORT SCSI/Link")
else:
device_name = _("Unknown Device")
mapped_device_types[device_type] = device_name
for key, value in device_types.items():
device_types[key]["name"] = get_device_name(key)
return mapped_device_types
return device_types
def get_device_name(device_type):
"""
Takes a four letter device acronym (str) device_type.
Returns the human-readable name for the device type.
"""
if device_type == "SAHD":
return _("SASI Hard Disk")
elif device_type == "SCHD":
return _("SCSI Hard Disk")
elif device_type == "SCRM":
return _("Removable Disk")
elif device_type == "SCMO":
return _("Magneto-Optical")
elif device_type == "SCCD":
return _("CD / DVD")
elif device_type == "SCBR":
return _("X68000 Host Bridge")
elif device_type == "SCDP":
return _("DaynaPORT SCSI/Link")
elif device_type == "SCLP":
return _("Printer")
elif device_type == "SCHS":
return _("Host Services")
else:
return device_type

View File

@ -15,7 +15,7 @@
<td><b>{{ _("Ref.") }}</b></td>
<td><b>{{ _("Action") }}</b></td>
</tr>
{% for hd in hd_conf %}
{% for hd in hd_conf|sort(attribute='name') %}
<tr>
<td style="text-align:center">{{ hd.name }}</td>
<td style="text-align:center">{{ hd.size_mb }}</td>
@ -59,7 +59,7 @@
<td><b>{{ _("Ref.") }}</b></td>
<td><b>{{ _("Action") }}</b></td>
</tr>
{% for cd in cd_conf %}
{% for cd in cd_conf|sort(attribute='name') %}
<tr>
<td style="text-align:center">{{ cd.name }}</td>
<td style="text-align:center">{{ cd.size_mb }}</td>
@ -79,9 +79,9 @@
<input type="hidden" name="block_size" value="{{ cd.block_size }}">
<label for="file_name">{{ _("Create for:") }}</label>
<select type="select" name="file_name">
{% for f in files %}
{% if f["name"].lower().endswith(cdrom_file_suffix) %}
<option value="{{ f["name"] }}">{{ f["name"].replace(base_dir, '') }}</option>
{% for file in files|sort(attribute='name') %}
{% if file["name"].lower().endswith(cdrom_file_suffix) %}
<option value="{{ file["name"] }}">{{ file["name"].replace(base_dir, '') }}</option>
{% endif %}
{% endfor %}
</select>
@ -105,7 +105,7 @@
<td><b>{{ _("Ref.") }}</b></td>
<td><b>{{ _("Action") }}</b></td>
</tr>
{% for rm in rm_conf %}
{% for rm in rm_conf|sort(attribute='name') %}
<tr>
<td style="text-align:center">{{ rm.name }}</td>
<td style="text-align:center">{{ rm.size_mb }}</td>

View File

@ -15,7 +15,7 @@
<p><form action="/config/load" method="post">
<select name="name" required="" width="14">
{% if config_files %}
{% for config in config_files %}
{% for config in config_files|sort %}
<option value="{{ config }}">
{{ config.replace(".json", '') }}
</option>
@ -130,9 +130,9 @@
<td class="inactive">{{ RESERVATIONS[device.id] }}</td>
<td class="inactive"></td>
<td class="inactive">
<form action="/scsi/unreserve" method="post">
<form action="/scsi/release" method="post">
<input name="scsi_id" type="hidden" value="{{ device.id }}">
<input type="submit" value="{{ _("Unreserve") }}">
<input type="submit" value="{{ _("Release") }}">
</form>
</td>
{% endif %}
@ -155,18 +155,18 @@
<li>{{ _("Manage image files in the active RaSCSI image directory: <tt>%(directory)s</tt> with a scan depth of %(scan_depth)s.", directory=base_dir, scan_depth=scan_depth) }}</li>
<li>{{ _("Select a valid SCSI ID and <a href=\"%(url)s\">LUN</a> to attach to. Unless you know what you're doing, always use LUN 0.", url="https://en.wikipedia.org/wiki/Logical_unit_number") }}
</li>
<li>{{ _("If RaSCSI was unable to detect the device type associated with the image, you can choose the type from the dropdown.") }}</li>
<li>{{ _("If RaSCSI was unable to detect the media type associated with the image, you get to choose the type from the dropdown.") }}</li>
</ul>
</details>
<table border="black" cellpadding="3">
<tbody>
<tr>
<td><b>{{ _("File") }}</b></td>
<td><b>{{ _("Size") }}</b></td>
<td><b>{{ _("Actions") }}</b></td>
<tr style="font-weight: bold;">
<td>{{ _("File") }}</td>
<td>{{ _("Size") }}</td>
<td>{{ _("Parameters and Actions") }}</td>
</tr>
{% for file in files %}
{% for file in files|sort(attribute='name') %}
<tr>
{% if file["prop"] %}
<td>
@ -264,12 +264,12 @@
{% else %}
<select name="type">
<option selected disabled value="">
{{ _("Select device type") }}
{{ _("Select media type") }}
</option>
{% for key, value in device_types.items() %}
{% if key not in NETWORK_DEVICE_TYPES %}
{% if key in DISK_DEVICE_TYPES %}
<option value="{{ key }}">
{{ value }}
{{ value["name"] }}
</option>
{% endif %}
{% endfor %}
@ -296,49 +296,54 @@
<p><small>{{ _("%(disk_space)s MB disk space remaining on the Pi", disk_space=free_disk) }}</small></p>
<hr/>
<details>
<summary class="heading">
{{ _("Attach Network Adapter") }}
{{ _("Attach Peripheral Device") }}
</summary>
<ul>
<li>{{ _("Emulates either a <a href=\"%(url1)s\">DaynaPORT SCSI/Link Ethernet Adapter</a>, or an <a href=\"%(url2)s\">X68000 Host Bridge</a>.", url1="https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link", url2="https://github.com/akuker/RASCSI/wiki/X68000#Host_File_System_driver") }}
<li>{{ _("<a href=\"%(url1)s\">DaynaPORT SCSI/Link</a> and <a href=\"%(url2)s\">X68000 Host Bridge</a> are network devices.", url1="https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link", url2="https://github.com/akuker/RASCSI/wiki/X68000#Host_File_System_driver") }}
</li>
<li>{{ _("If you have a DHCP setup, choose only the interface you have configured the bridge with. You can ignore the Static IP fields when attaching.") }}</li>
<ul>
<li>{{ _("If you have a DHCP setup, choose only the interface you have configured the bridge with. You can ignore the inet field when attaching.") }}</li>
<li>{{ _("Configure the network bridge by running easyinstall.sh, or follow the <a href=\"%(url)s\">manual steps in the wiki</a>.", url="https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link#manual-setup") }}
{% if bridge_configured %}
<li>{{ _("The <tt>rascsi_bridge</tt> network bridge is active and ready to be used by an emulated network adapter!") }}</li>
{% endif %}
</li>
</ul>
<li>{{ _("The Printer and Host Services device are currently supported on compatible Atari systems, and require <a href=\"%(url)s\">driver software</a> to be installed on the host system.", url="https://www.hddriver.net/en/rascsi_tools.html") }}
</li>
<li style="list-style: none">{% if bridge_configured %}</li>
<li>{{ _("The <tt>rascsi_bridge</tt> network bridge is active and ready to be used by the emulated network adapter!") }}</li>
<li style="list-style: none">{% endif %}</li>
</ul>
</details>
<table style="border: none">
<tr style="border: none">
<td style="border: none; vertical-align:top;">
<form action="/scsi/attach_network" method="post">
<label for="if">{{ _("Interface:") }}</label>
<select name="if">
<table border="black" cellpadding="3">
<tr style="font-weight: bold;">
<td>{{ _("Peripheral") }}</td>
<td>{{ _("Parameters and Actions") }}</td>
</tr>
{% for type in PERIPHERAL_DEVICE_TYPES %}
<tr>
<td>
<div>{{ device_types[type]["name"] }}</div>
</td>
<td>
<form action="/scsi/attach_device" method="post">
<input name="type" type="hidden" value="{{ type }}">
{% for key, value in device_types[type]["params"].items() %}
<label for="{{ key }}">{{ key }}:</label>
{% if value.isnumeric() %}
<input name="{{ key }}" type="number" size="{{ value|length }}" value="{{ value }}">
{% elif key == "interface" %}
<select name="interface">
{% for if in netinfo["ifs"] %}
<option value="{{ if }}">
{{ if }}
</option>
{% endfor %}
</select>
<label for="ip">{{ _("Static IP (optional):") }}</label>
<input name="ip" type="text" size="15" placeholder="10.10.20.1" minlength="7" maxlength="15" pattern="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$">
<input name="mask" type="number" size="2" placeholder="24" min="16" max="30">
<label for="type">{{ _("Type:") }}</label>
<select name="type">
{% for type in NETWORK_DEVICE_TYPES %}
<option value="{{ type }}">
{% for key, value in device_types.items() %}
{% if key == type %}
{{ value }}
{% endif %}
{% endfor %}
</option>
{% endfor %}
</select>
{% else %}
<input name="{{ key }}" type="text" size="{{ value|length }}" placeholder="{{ value }}">
{% endif %}
{% endfor %}
<label for="scsi_id">{{ _("SCSI ID:") }}</label>
<select name="scsi_id">
{% for id in scsi_ids %}
@ -353,6 +358,7 @@
</form>
</td>
</tr>
{% endfor %}
</table>
{% if macproxy_configured %}
<p><small>{{ _("Macproxy is running at %(ip_addr)s (default port 5000)", ip_addr=ip_addr) }}</small></p>

View File

@ -35,7 +35,8 @@ from pi_cmds import (
from device_utils import (
sort_and_format_devices,
get_valid_scsi_ids,
extend_device_names,
map_device_types_and_names,
get_device_name,
)
from return_code_mapper import ReturnCodeMapper
@ -53,8 +54,6 @@ from rascsi.common_settings import (
CFG_DIR,
CONFIG_FILE_SUFFIX,
PROPERTIES_SUFFIX,
REMOVABLE_DEVICE_TYPES,
NETWORK_DEVICE_TYPES,
RESERVATIONS,
)
from rascsi.ractl_cmds import RaCtlCmds
@ -108,25 +107,18 @@ def index():
),
)
locales = get_supported_locales()
server_info = ractl.get_server_info()
disk = disk_space()
devices = ractl.list_devices()
device_types = ractl.get_device_types()
device_types = map_device_types_and_names(ractl.get_device_types()["device_types"])
image_files = file_cmds.list_images()
config_files = file_cmds.list_config_files()
mapped_device_types = extend_device_names(device_types["device_types"])
extended_image_files = []
for image in image_files["files"]:
if image["detected_type"] is not "UNDEFINED":
image["detected_type_name"] = mapped_device_types[image["detected_type"]]
if image["detected_type"] != "UNDEFINED":
image["detected_type_name"] = device_types[image["detected_type"]]["name"]
extended_image_files.append(image)
sorted_image_files = sorted(extended_image_files, key=lambda x: x["name"].lower())
sorted_config_files = sorted(config_files, key=lambda x: x.lower())
attached_images = []
units = 0
# If there are more than 0 logical unit numbers, display in the Web UI
@ -154,14 +146,14 @@ def index():
return render_template(
"index.html",
locales=locales,
locales=get_supported_locales(),
bridge_configured=is_bridge_setup(),
netatalk_configured=running_proc("afpd"),
macproxy_configured=running_proc("macproxy"),
ip_addr=get_ip_address(),
devices=formatted_devices,
files=sorted_image_files,
config_files=sorted_config_files,
files=extended_image_files,
config_files=config_files,
base_dir=server_info["image_dir"],
scan_depth=server_info["scan_depth"],
CFG_DIR=CFG_DIR,
@ -178,8 +170,8 @@ def index():
log_levels=server_info["log_levels"],
current_log_level=server_info["current_log_level"],
netinfo=ractl.get_network_info(),
device_types=mapped_device_types,
free_disk=int(disk["free"] / 1024 / 1024),
device_types=device_types,
free_disk=int(disk_space()["free"] / 1024 / 1024),
valid_file_suffix=valid_file_suffix,
cdrom_file_suffix=tuple(server_info["sccd"]),
removable_file_suffix=tuple(server_info["scrm"]),
@ -188,8 +180,9 @@ def index():
auth_active=auth_active()["status"],
ARCHIVE_FILE_SUFFIX=ARCHIVE_FILE_SUFFIX,
PROPERTIES_SUFFIX=PROPERTIES_SUFFIX,
REMOVABLE_DEVICE_TYPES=REMOVABLE_DEVICE_TYPES,
NETWORK_DEVICE_TYPES=NETWORK_DEVICE_TYPES,
REMOVABLE_DEVICE_TYPES=ractl.get_removable_device_types(),
DISK_DEVICE_TYPES=ractl.get_disk_device_types(),
PERIPHERAL_DEVICE_TYPES=ractl.get_peripheral_device_types(),
)
@ -198,9 +191,6 @@ def drive_list():
"""
Sets up the data structures and kicks off the rendering of the drive list page
"""
server_info = ractl.get_server_info()
disk = disk_space()
# Reads the canonical drive properties into a dict
# The file resides in the current dir of the web ui process
drive_properties = Path(DRIVE_PROPERTIES_FILE)
@ -239,27 +229,23 @@ def drive_list():
device["size_mb"] = "{:,.2f}".format(device["size"] / 1024 / 1024)
rm_conf.append(device)
files = file_cmds.list_images()
sorted_image_files = sorted(files["files"], key=lambda x: x["name"].lower())
hd_conf = sorted(hd_conf, key=lambda x: x["name"].lower())
cd_conf = sorted(cd_conf, key=lambda x: x["name"].lower())
rm_conf = sorted(rm_conf, key=lambda x: x["name"].lower())
if "username" in session:
username = session["username"]
else:
username = None
server_info = ractl.get_server_info()
return render_template(
"drives.html",
files=sorted_image_files,
files=file_cmds.list_images()["files"],
base_dir=server_info["image_dir"],
hd_conf=hd_conf,
cd_conf=cd_conf,
rm_conf=rm_conf,
running_env=running_env(),
version=server_info["version"],
free_disk=int(disk["free"] / 1024 / 1024),
free_disk=int(disk_space()["free"] / 1024 / 1024),
cdrom_file_suffix=tuple(server_info["sccd"]),
username=username,
auth_active=auth_active()["status"],
@ -493,57 +479,59 @@ def log_level():
return redirect(url_for("index"))
@APP.route("/scsi/attach_network", methods=["POST"])
@APP.route("/scsi/attach_device", methods=["POST"])
@login_required
def attach_network_adapter():
def attach_device():
"""
Attaches a network adapter device
Attaches a peripheral device that doesn't take an image file as argument
"""
scsi_id = request.form.get("scsi_id")
unit = request.form.get("unit")
device_type = request.form.get("type")
interface = request.form.get("if")
ip_addr = request.form.get("ip")
mask = request.form.get("mask")
params = {}
for item in request.form:
if item == "scsi_id":
scsi_id = request.form.get(item)
elif item == "unit":
unit = request.form.get(item)
elif item == "type":
device_type = request.form.get(item)
else:
param = request.form.get(item)
if param:
params.update({item: param})
error_url = "https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link"
error_msg = _("Please follow the instructions at %(url)s", url=error_url)
if interface.startswith("wlan"):
if not introspect_file("/etc/sysctl.conf", r"^net\.ipv4\.ip_forward=1$"):
flash(_("Configure IPv4 forwarding before using a wireless network device."), "error")
flash(error_msg, "error")
return redirect(url_for("index"))
if not Path("/etc/iptables/rules.v4").is_file():
flash(_("Configure NAT before using a wireless network device."), "error")
flash(error_msg, "error")
return redirect(url_for("index"))
else:
if not introspect_file("/etc/dhcpcd.conf", r"^denyinterfaces " + interface + r"$"):
flash(_("Configure the network bridge before using a wired network device."), "error")
flash(error_msg, "error")
return redirect(url_for("index"))
if not Path("/etc/network/interfaces.d/rascsi_bridge").is_file():
flash(_("Configure the network bridge before using a wired network device."), "error")
flash(error_msg, "error")
return redirect(url_for("index"))
if "interface" in params.keys():
if params["interface"].startswith("wlan"):
if not introspect_file("/etc/sysctl.conf", r"^net\.ipv4\.ip_forward=1$"):
flash(_("Configure IPv4 forwarding before using a wireless network device."), "error")
flash(error_msg, "error")
return redirect(url_for("index"))
if not Path("/etc/iptables/rules.v4").is_file():
flash(_("Configure NAT before using a wireless network device."), "error")
flash(error_msg, "error")
return redirect(url_for("index"))
else:
if not introspect_file("/etc/dhcpcd.conf", r"^denyinterfaces " + params["interface"] + r"$"):
flash(_("Configure the network bridge before using a wired network device."), "error")
flash(error_msg, "error")
return redirect(url_for("index"))
if not Path("/etc/network/interfaces.d/rascsi_bridge").is_file():
flash(_("Configure the network bridge before using a wired network device."), "error")
flash(error_msg, "error")
return redirect(url_for("index"))
kwargs = {"unit": int(unit), "device_type": device_type}
if interface != "":
arg = interface
if "" not in (ip_addr, mask):
arg += (":" + ip_addr + "/" + mask)
kwargs["interfaces"] = arg
process = ractl.attach_image(scsi_id, **kwargs)
kwargs = {
"unit": int(unit),
"device_type": device_type,
"params": params,
}
process = ractl.attach_device(scsi_id, **kwargs)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(_(
(
"Attached network device of type %(device_type)s "
"to SCSI ID %(id_number)s LUN %(unit_number)s"
),
device_type=device_type,
"Attached %(device_type)s to SCSI ID %(id_number)s LUN %(unit_number)s",
device_type=get_device_name(device_type),
id_number=scsi_id,
unit_number=unit,
))
@ -555,7 +543,7 @@ def attach_network_adapter():
@APP.route("/scsi/attach", methods=["POST"])
@login_required
def attach():
def attach_image():
"""
Attaches a file image as a device
"""
@ -565,17 +553,12 @@ def attach():
unit = request.form.get("unit")
device_type = request.form.get("type")
kwargs = {"unit": int(unit), "image": file_name}
kwargs = {"unit": int(unit), "params": {"file": file_name}}
# The most common block size is 512 bytes
expected_block_size = 512
if device_type != "":
if device_type:
kwargs["device_type"] = device_type
if device_type == "SCCD":
expected_block_size = 2048
elif device_type == "SAHD":
expected_block_size = 256
device_types = ractl.get_device_types()
expected_block_size = min(device_types["device_types"][device_type]["block_sizes"])
# Attempt to load the device properties file:
# same file name with PROPERTIES_SUFFIX appended
@ -593,11 +576,18 @@ def attach():
kwargs["block_size"] = conf["block_size"]
expected_block_size = conf["block_size"]
process = ractl.attach_image(scsi_id, **kwargs)
process = ractl.attach_device(scsi_id, **kwargs)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(_("Attached %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s",
file_name=file_name, id_number=scsi_id, unit_number=unit))
flash(_((
"Attached %(file_name)s as %(device_type)s to "
"SCSI ID %(id_number)s LUN %(unit_number)s"
),
file_name=file_name,
device_type=get_device_name(device_type),
id_number=scsi_id,
unit_number=unit,
))
if int(file_size) % int(expected_block_size):
flash(_("The image file size %(file_size)s bytes is not a multiple of "
u"%(block_size)s. RaSCSI will ignore the trailing data. "
@ -722,11 +712,11 @@ def reserve_id():
flash(process["msg"], "error")
return redirect(url_for("index"))
@APP.route("/scsi/unreserve", methods=["POST"])
@APP.route("/scsi/release", methods=["POST"])
@login_required
def unreserve_id():
def release_id():
"""
Removes the reservation of a SCSI ID as well as the memo for the reservation
Releases the reservation of a SCSI ID as well as the memo for the reservation
"""
scsi_id = request.form.get("scsi_id")
reserved_ids = ractl.get_reserved_ids()["ids"]
@ -782,7 +772,11 @@ def download_to_iso():
flash(process["msg"], "error")
return redirect(url_for("index"))
process_attach = ractl.attach_image(scsi_id, device_type="SCCD", image=process["file_name"])
process_attach = ractl.attach_device(
scsi_id,
device_type="SCCD",
params={"file": process["file_name"]},
)
process_attach = ReturnCodeMapper.add_msg(process_attach)
if process_attach["status"]:
flash(_("Attached to SCSI ID %(id_number)s", id_number=scsi_id))

View File

@ -143,7 +143,7 @@ bool SASIDEV::HasUnit()
// Run
//
//---------------------------------------------------------------------------
BUS::phase_t SASIDEV::Process()
BUS::phase_t SASIDEV::Process(int)
{
// Do nothing if not connected
if (ctrl.m_scsi_id < 0 || ctrl.bus == NULL) {
@ -608,9 +608,8 @@ void SASIDEV::DataOut()
ctrl.bus->SetCD(FALSE);
ctrl.bus->SetIO(FALSE);
// length, blocks are already calculated
// Length has already been calculated
ASSERT(ctrl.length > 0);
ASSERT(ctrl.blocks > 0);
ctrl.offset = 0;
return;
}
@ -624,7 +623,7 @@ void SASIDEV::DataOut()
// Error
//
//---------------------------------------------------------------------------
void SASIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc)
void SASIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc, ERROR_CODES::status status)
{
// Get bus information
((GPIOBUS*)ctrl.bus)->Aquire();
@ -648,8 +647,8 @@ void SASIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc)
// Logical Unit
DWORD lun = GetEffectiveLun();
// Set status and message(CHECK CONDITION)
ctrl.status = (lun << 5) | 0x02;
// Set status and message
ctrl.status = (lun << 5) | status;
// status phase
Status();
@ -1067,6 +1066,8 @@ void SASIDEV::Receive()
//
// Data transfer IN
// *Reset offset and length
// TODO XferIn probably needs a dispatcher, in order to avoid subclassing Disk, i.e.
// just like the actual SCSI commands XferIn should be executed by the respective device
//
//---------------------------------------------------------------------------
bool SASIDEV::XferIn(BYTE *buf)
@ -1113,6 +1114,8 @@ bool SASIDEV::XferIn(BYTE *buf)
//
// Data transfer OUT
// *If cont=true, reset the offset and length
// TODO XferOut probably needs a dispatcher, in order to avoid subclassing Disk, i.e.
// just like the actual SCSI commands XferOut should be executed by the respective device
//
//---------------------------------------------------------------------------
bool SASIDEV::XferOut(bool cont)
@ -1156,7 +1159,7 @@ bool SASIDEV::XferOut(bool cont)
// Special case Write function for DaynaPort
// TODO This class must not know about DaynaPort
if (device->IsDaynaPort()) {
if (!device->Write(ctrl.cmd, ctrl.buffer, ctrl.length)) {
if (!((SCSIDaynaPort*)device)->Write(ctrl.cmd, ctrl.buffer, ctrl.length)) {
// write failed
return false;
}

View File

@ -15,11 +15,10 @@
//---------------------------------------------------------------------------
#pragma once
#include "../rascsi.h"
#include "../config.h"
#include "os.h"
#include "scsi.h"
#include "fileio.h"
#include "log.h"
class Device;
@ -140,7 +139,7 @@ public:
virtual void Reset(); // Device Reset
// External API
virtual BUS::phase_t Process(); // Run
virtual BUS::phase_t Process(int); // Run
// Connect
void Connect(int id, BUS *sbus); // Controller connection
@ -162,10 +161,12 @@ public:
void MsgIn(); // Message in phase
void DataOut(); // Data out phase
// Get LUN based on IDENTIFY message, with LUN from the CDB as fallback
int GetEffectiveLun() const;
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
ERROR_CODES::asc = ERROR_CODES::asc::NO_ADDITIONAL_SENSE_INFORMATION,
ERROR_CODES::status = ERROR_CODES::status::CHECK_CONDITION); // Common error handling
protected:
// Phase processing
@ -193,7 +194,7 @@ protected:
virtual void Receive(); // Receive data
bool XferIn(BYTE* buf); // Data transfer IN
bool XferOut(bool cont); // Data transfer OUT
virtual bool XferOut(bool cont); // Data transfer OUT
// Special operations
void FlushUnit(); // Flush the logical unit

View File

@ -17,7 +17,7 @@
#include "controllers/scsidev_ctrl.h"
#include "gpiobus.h"
#include "devices/scsi_daynaport.h"
#include <sstream>
#include "devices/scsi_printer.h"
//===========================================================================
//
@ -27,6 +27,7 @@
SCSIDEV::SCSIDEV() : SASIDEV()
{
scsi.bytes_to_transfer = 0;
shutdown_mode = NONE;
// Synchronous transfer work initialization
@ -44,16 +45,17 @@ SCSIDEV::~SCSIDEV()
void SCSIDEV::Reset()
{
scsi.bytes_to_transfer = 0;
// Work initialization
scsi.atnmsg = false;
scsi.msc = 0;
memset(scsi.msb, 0x00, sizeof(scsi.msb));
// Base class
SASIDEV::Reset();
super::Reset();
}
BUS::phase_t SCSIDEV::Process()
BUS::phase_t SCSIDEV::Process(int initiator_id)
{
// Do nothing if not connected
if (ctrl.m_scsi_id < 0 || ctrl.bus == NULL) {
@ -75,6 +77,8 @@ BUS::phase_t SCSIDEV::Process()
return ctrl.phase;
}
scsi.initiator_id = initiator_id;
// Phase processing
switch (ctrl.phase) {
// Bus free phase
@ -155,6 +159,9 @@ void SCSIDEV::BusFree()
ctrl.lun = -1;
scsi.is_byte_transfer = false;
scsi.bytes_to_transfer = 0;
// When the bus is free RaSCSI or the Pi may be shut down
switch(shutdown_mode) {
case RASCSI:
@ -164,7 +171,9 @@ void SCSIDEV::BusFree()
case PI:
LOGINFO("Raspberry Pi shutdown requested");
system("init 0");
if (system("init 0") == -1) {
LOGERROR("Raspberry Pi shutdown failed: %s", strerror(errno));
}
break;
default:
@ -190,7 +199,7 @@ void SCSIDEV::Selection()
// Phase change
if (ctrl.phase != BUS::selection) {
// invalid if IDs do not match
DWORD id = 1 << ctrl.m_scsi_id;
int id = 1 << ctrl.m_scsi_id;
if ((ctrl.bus->GetDAT() & id) == 0) {
return;
}
@ -202,6 +211,13 @@ void SCSIDEV::Selection()
LOGTRACE("%s Selection Phase ID=%d (with device)", __PRETTY_FUNCTION__, (int)ctrl.m_scsi_id);
if (scsi.initiator_id != UNKNOWN_SCSI_ID) {
LOGTRACE("%s Initiator ID is %d", __PRETTY_FUNCTION__, scsi.initiator_id);
}
else {
LOGTRACE("%s Initiator ID is unknown", __PRETTY_FUNCTION__);
}
// Phase setting
ctrl.phase = BUS::selection;
@ -239,7 +255,7 @@ void SCSIDEV::Execute()
ctrl.execstart = SysTimer::GetTimerLow();
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE
if ((ScsiDefs::scsi_command)ctrl.cmd[0] != ScsiDefs::eCmdRequestSense) {
if ((scsi_defs::scsi_command)ctrl.cmd[0] != scsi_defs::eCmdRequestSense) {
ctrl.status = 0;
}
@ -247,8 +263,8 @@ void SCSIDEV::Execute()
int lun = GetEffectiveLun();
if (!ctrl.unit[lun]) {
if ((ScsiDefs::scsi_command)ctrl.cmd[0] != ScsiDefs::eCmdInquiry &&
(ScsiDefs::scsi_command)ctrl.cmd[0] != ScsiDefs::eCmdRequestSense) {
if ((scsi_defs::scsi_command)ctrl.cmd[0] != scsi_defs::eCmdInquiry &&
(scsi_defs::scsi_command)ctrl.cmd[0] != scsi_defs::eCmdRequestSense) {
LOGDEBUG("Invalid LUN %d for ID %d", lun, GetSCSIID());
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_LUN);
@ -266,7 +282,7 @@ void SCSIDEV::Execute()
ctrl.device = ctrl.unit[lun];
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE
if ((ScsiDefs::scsi_command)ctrl.cmd[0] != ScsiDefs::eCmdRequestSense) {
if ((scsi_defs::scsi_command)ctrl.cmd[0] != scsi_defs::eCmdRequestSense) {
ctrl.device->SetStatusCode(0);
}
@ -277,7 +293,7 @@ void SCSIDEV::Execute()
}
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
if ((ScsiDefs::scsi_command)ctrl.cmd[0] == ScsiDefs::eCmdInquiry && !ctrl.unit[lun]) {
if ((scsi_defs::scsi_command)ctrl.cmd[0] == scsi_defs::eCmdInquiry && !ctrl.unit[lun]) {
lun = GetEffectiveLun();
LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, ctrl.device->GetId());
@ -330,7 +346,7 @@ void SCSIDEV::MsgOut()
// Common Error Handling
//
//---------------------------------------------------------------------------
void SCSIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc)
void SCSIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc, ERROR_CODES::status status)
{
// Get bus information
((GPIOBUS*)ctrl.bus)->Aquire();
@ -351,7 +367,7 @@ void SCSIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc)
return;
}
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
int lun = GetEffectiveLun();
if (!ctrl.unit[lun] || asc == ERROR_CODES::INVALID_LUN) {
lun = 0;
}
@ -361,7 +377,7 @@ void SCSIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc)
ctrl.unit[lun]->SetStatusCode((sense_key << 16) | (asc << 8));
}
ctrl.status = 0x02;
ctrl.status = status;
ctrl.message = 0x00;
LOGTRACE("%s Error (to status phase)", __PRETTY_FUNCTION__);
@ -381,9 +397,8 @@ void SCSIDEV::Send()
//if Length! = 0, send
if (ctrl.length != 0) {
ostringstream s;
s << __PRETTY_FUNCTION__ << " sending handhake with offset " << ctrl.offset << ", length " << ctrl.length;
LOGTRACE("%s", s.str().c_str());
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Sending handhake with offset " + to_string(ctrl.offset) + ", length "
+ to_string(ctrl.length)).c_str());
// TODO Get rid of Daynaport specific code in this class
// The Daynaport needs to have a delay after the size/flags field
@ -420,9 +435,7 @@ void SCSIDEV::Send()
if (ctrl.blocks != 0) {
// set next buffer (set offset, length)
result = XferIn(ctrl.buffer);
ostringstream s;
s << __PRETTY_FUNCTION__ << " Processing after data collection. Blocks: " << ctrl.blocks;
LOGTRACE("%s", s.str().c_str());
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Processing after data collection. Blocks: " + to_string(ctrl.blocks)).c_str());
}
}
@ -434,9 +447,7 @@ void SCSIDEV::Send()
// Continue sending if block !=0
if (ctrl.blocks != 0){
ostringstream s;
s << __PRETTY_FUNCTION__ << " Continuing to send. Blocks: " << ctrl.blocks;
LOGTRACE("%s", s.str().c_str());
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Continuing to send. Blocks: " + to_string(ctrl.blocks)).c_str());
ASSERT(ctrl.length > 0);
ASSERT(ctrl.offset == 0);
return;
@ -488,6 +499,11 @@ void SCSIDEV::Send()
//---------------------------------------------------------------------------
void SCSIDEV::Receive()
{
if (scsi.is_byte_transfer) {
ReceiveBytes();
return;
}
int len;
BYTE data;
@ -572,7 +588,7 @@ void SCSIDEV::Receive()
len = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]);
for (int i = 0; i < len; i++) {
ctrl.cmd[i] = (DWORD)ctrl.buffer[i];
ctrl.cmd[i] = ctrl.buffer[i];
LOGTRACE("%s Command Byte %d: $%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i]);
}
@ -687,7 +703,7 @@ void SCSIDEV::Receive()
// Transfer MSG
//
//---------------------------------------------------------------------------
bool SCSIDEV::XferMsg(DWORD msg)
bool SCSIDEV::XferMsg(int msg)
{
ASSERT(ctrl.phase == BUS::msgout);
@ -700,3 +716,194 @@ bool SCSIDEV::XferMsg(DWORD msg)
return true;
}
void SCSIDEV::ReceiveBytes()
{
uint32_t len;
BYTE data;
LOGTRACE("%s",__PRETTY_FUNCTION__);
// REQ is low
ASSERT(!ctrl.bus->GetREQ());
ASSERT(!ctrl.bus->GetIO());
if (ctrl.length) {
LOGTRACE("%s length is %d", __PRETTY_FUNCTION__, ctrl.length);
len = ctrl.bus->ReceiveHandShake(&ctrl.buffer[ctrl.offset], ctrl.length);
// If not able to receive all, move to status phase
if (len != ctrl.length) {
LOGERROR("%s Not able to receive %d data, only received %d. Going to error",
__PRETTY_FUNCTION__, ctrl.length, len);
Error();
return;
}
ctrl.offset += ctrl.length;
scsi.bytes_to_transfer = ctrl.length;
ctrl.length = 0;
return;
}
// Result initialization
bool result = true;
// Processing after receiving data (by phase)
LOGTRACE("%s ctrl.phase: %d (%s)",__PRETTY_FUNCTION__, (int)ctrl.phase, BUS::GetPhaseStrRaw(ctrl.phase));
switch (ctrl.phase) {
case BUS::dataout:
result = XferOut(false);
break;
case BUS::msgout:
ctrl.message = ctrl.buffer[0];
if (!XferMsg(ctrl.message)) {
// Immediately free the bus if message output fails
BusFree();
return;
}
// Clear message data in preparation for message-in
ctrl.message = 0x00;
break;
default:
break;
}
// If result FALSE, move to status phase
if (!result) {
Error();
return;
}
// Move to next phase
switch (ctrl.phase) {
case BUS::command:
len = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]);
for (uint32_t i = 0; i < len; i++) {
ctrl.cmd[i] = ctrl.buffer[i];
LOGTRACE("%s Command Byte %d: $%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i]);
}
Execute();
break;
case BUS::msgout:
// Continue message out phase as long as ATN keeps asserting
if (ctrl.bus->GetATN()) {
// Data transfer is 1 byte x 1 block
ctrl.offset = 0;
ctrl.length = 1;
ctrl.blocks = 1;
return;
}
// Parsing messages sent by ATN
if (scsi.atnmsg) {
int i = 0;
while (i < scsi.msc) {
// Message type
data = scsi.msb[i];
// ABORT
if (data == 0x06) {
LOGTRACE("Message code ABORT $%02X", data);
BusFree();
return;
}
// BUS DEVICE RESET
if (data == 0x0C) {
LOGTRACE("Message code BUS DEVICE RESET $%02X", data);
scsi.syncoffset = 0;
BusFree();
return;
}
// IDENTIFY
if (data >= 0x80) {
ctrl.lun = data & 0x1F;
LOGTRACE("Message code IDENTIFY $%02X, LUN %d selected", data, ctrl.lun);
}
// Extended Message
if (data == 0x01) {
LOGTRACE("Message code EXTENDED MESSAGE $%02X", data);
// Check only when synchronous transfer is possible
if (!scsi.syncenable || scsi.msb[i + 2] != 0x01) {
ctrl.length = 1;
ctrl.blocks = 1;
ctrl.buffer[0] = 0x07;
MsgIn();
return;
}
// Transfer period factor (limited to 50 x 4 = 200ns)
scsi.syncperiod = scsi.msb[i + 3];
if (scsi.syncperiod > 50) {
scsi.syncoffset = 50;
}
// REQ/ACK offset(limited to 16)
scsi.syncoffset = scsi.msb[i + 4];
if (scsi.syncoffset > 16) {
scsi.syncoffset = 16;
}
// STDR response message generation
ctrl.length = 5;
ctrl.blocks = 1;
ctrl.buffer[0] = 0x01;
ctrl.buffer[1] = 0x03;
ctrl.buffer[2] = 0x01;
ctrl.buffer[3] = (BYTE)scsi.syncperiod;
ctrl.buffer[4] = (BYTE)scsi.syncoffset;
MsgIn();
return;
}
// next
i++;
}
}
// Initialize ATN message reception status
scsi.atnmsg = false;
Command();
break;
case BUS::dataout:
Status();
break;
default:
assert(false);
break;
}
}
bool SCSIDEV::XferOut(bool cont)
{
if (!scsi.is_byte_transfer) {
return super::XferOut(cont);
}
ASSERT(ctrl.phase == BUS::dataout);
PrimaryDevice *device = dynamic_cast<PrimaryDevice *>(ctrl.unit[GetEffectiveLun()]);
if (device && ctrl.cmd[0] == scsi_defs::eCmdWrite6) {
return device->WriteBytes(ctrl.buffer, scsi.bytes_to_transfer);
}
LOGWARN("Received an unexpected command ($%02X) in %s", (WORD)ctrl.cmd[0] , __PRETTY_FUNCTION__)
return false;
}

View File

@ -45,45 +45,54 @@ public:
bool atnmsg;
int msc;
BYTE msb[256];
// -1 means that the initiator ID is unknown, e.g. with Atari ACSI and old host adapters
int initiator_id;
bool is_byte_transfer;
uint32_t bytes_to_transfer;
} scsi_t;
// Basic Functions
SCSIDEV();
~SCSIDEV();
void Reset() override;
// External API
BUS::phase_t Process() override;
BUS::phase_t Process(int) override;
void Receive() override;
// Other
bool IsSASI() const override { return false; }
bool IsSCSI() const override { return true; }
// Common error handling
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
ERROR_CODES::asc asc = ERROR_CODES::asc::NO_ADDITIONAL_SENSE_INFORMATION,
ERROR_CODES::status status = ERROR_CODES::status::CHECK_CONDITION) override;
void ShutDown(rascsi_shutdown_mode shutdown_mode) { this->shutdown_mode = shutdown_mode; }
void ScheduleShutDown(rascsi_shutdown_mode shutdown_mode) { this->shutdown_mode = shutdown_mode; }
int GetInitiatorId() const { return scsi.initiator_id; }
bool IsByteTransfer() const { return scsi.is_byte_transfer; }
void SetByteTransfer(bool is_byte_transfer) { scsi.is_byte_transfer = is_byte_transfer; }
private:
typedef SASIDEV super;
// Phase
void BusFree() override; // Bus free phase
void Selection() override; // Selection phase
void Execute() override; // Execution phase
void MsgOut(); // Message out phase
// commands
void CmdGetEventStatusNotification();
void CmdModeSelect10();
void CmdModeSense10();
// Phases
void BusFree() override;
void Selection() override;
void Execute() override;
void MsgOut();
// Data transfer
void Send() override;
void Receive() override;
bool XferMsg(DWORD msg);
bool XferMsg(int);
bool XferOut(bool);
void ReceiveBytes();
scsi_t scsi; // Internal data
// Internal data
scsi_t scsi;
rascsi_shutdown_mode shutdown_mode;
};

View File

@ -20,25 +20,22 @@
#include <sys/ioctl.h>
#include <linux/sockios.h>
#endif
// TODO Try to get rid of zlib, there is only one operation using it
#include <zlib.h> // For crc32()
#include "os.h"
#include "ctapdriver.h"
#include "log.h"
#include "rasutil.h"
#include "exceptions.h"
#include <sstream>
#define BRIDGE_NAME "rascsi_bridge"
using namespace std;
using namespace ras_util;
CTapDriver::CTapDriver(const string& interfaces)
CTapDriver::CTapDriver()
{
stringstream s(interfaces);
string interface;
while (getline(s, interface, ',')) {
this->interfaces.push_back(interface);
}
// Initialization
m_bTxValid = FALSE;
m_hTAP = -1;
memset(&m_MacAddr, 0, sizeof(m_MacAddr));
m_pcap = NULL;
@ -52,28 +49,28 @@ CTapDriver::CTapDriver(const string& interfaces)
//---------------------------------------------------------------------------
#ifdef __linux__
static BOOL br_setif(int br_socket_fd, const char* bridgename, const char* ifname, BOOL add) {
static bool br_setif(int br_socket_fd, const char* bridgename, const char* ifname, bool add) {
struct ifreq ifr;
ifr.ifr_ifindex = if_nametoindex(ifname);
if (ifr.ifr_ifindex == 0) {
LOGERROR("Error: can't if_nametoindex. Errno: %d %s", errno, strerror(errno));
return FALSE;
LOGERROR("Can't if_nametoindex: %s", strerror(errno));
return false;
}
strncpy(ifr.ifr_name, bridgename, IFNAMSIZ);
if (ioctl(br_socket_fd, add ? SIOCBRADDIF : SIOCBRDELIF, &ifr) < 0) {
LOGERROR("Error: can't ioctl %s. Errno: %d %s", add ? "SIOCBRADDIF" : "SIOCBRDELIF", errno, strerror(errno));
return FALSE;
LOGERROR("Can't ioctl %s: %s", add ? "SIOCBRADDIF" : "SIOCBRDELIF", strerror(errno));
return false;
}
return TRUE;
return true;
}
static BOOL ip_link(int fd, const char* ifname, BOOL up) {
static bool ip_link(int fd, const char* ifname, bool up) {
struct ifreq ifr;
strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1); // Need to save room for null terminator
int err = ioctl(fd, SIOCGIFFLAGS, &ifr);
if (err) {
LOGERROR("Error: can't ioctl SIOCGIFFLAGS. Errno: %d %s", errno, strerror(errno));
return FALSE;
LOGERROR("Can't ioctl SIOCGIFFLAGS: %s", strerror(errno));
return false;
}
ifr.ifr_flags &= ~IFF_UP;
if (up) {
@ -81,10 +78,10 @@ static BOOL ip_link(int fd, const char* ifname, BOOL up) {
}
err = ioctl(fd, SIOCSIFFLAGS, &ifr);
if (err) {
LOGERROR("Error: can't ioctl SIOCSIFFLAGS. Errno: %d %s", errno, strerror(errno));
return FALSE;
LOGERROR("Can't ioctl SIOCSIFFLAGS: %s", strerror(errno));
return false;
}
return TRUE;
return true;
}
static bool is_interface_up(const string& interface) {
@ -105,12 +102,33 @@ static bool is_interface_up(const string& interface) {
return status;
}
bool CTapDriver::Init()
bool CTapDriver::Init(const map<string, string>& const_params)
{
map<string, string> params = const_params;
if (params.count("interfaces")) {
LOGWARN("You are using the deprecated 'interfaces' parameter. "
"Provide the interface list and the IP address/netmask with the 'interface' and 'inet' parameters");
// TODO Remove the deprecated syntax in a future version
const string& interfaces = params["interfaces"];
size_t separatorPos = interfaces.find(':');
if (separatorPos != string::npos) {
params["interface"] = interfaces.substr(0, separatorPos);
params["inet"] = interfaces.substr(separatorPos + 1);
}
}
stringstream s(params["interface"]);
string interface;
while (getline(s, interface, ',')) {
this->interfaces.push_back(interface);
}
this->inet = params["inet"];
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));
LOGERROR("Can't open tun: %s", strerror(errno));
return false;
}
@ -127,17 +145,17 @@ bool CTapDriver::Init()
int ret = ioctl(m_hTAP, TUNSETIFF, (void *)&ifr);
if (ret < 0) {
LOGERROR("Error: can't ioctl TUNSETIFF. Errno: %d %s", errno, strerror(errno));
LOGERROR("Can't ioctl TUNSETIFF: %s", strerror(errno));
close(m_hTAP);
return false;
}
LOGTRACE("return code from ioctl was %d", ret);
LOGTRACE("Return code from ioctl was %d", ret);
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));
LOGERROR("Can't open ip socket: %s", strerror(errno));
close(m_hTAP);
return false;
@ -145,7 +163,7 @@ bool CTapDriver::Init()
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));
LOGERROR("Can't open bridge socket: %s", strerror(errno));
close(m_hTAP);
close(ip_fd);
@ -153,36 +171,23 @@ bool CTapDriver::Init()
}
// Check if the bridge has already been created
if (access("/sys/class/net/rascsi_bridge", F_OK)) {
LOGINFO("rascsi_bridge is not yet available");
string sys_file = "/sys/class/net/";
sys_file += BRIDGE_NAME;
if (access(sys_file.c_str(), F_OK)) {
LOGINFO("%s is not yet available", BRIDGE_NAME);
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;
for (const string& interface : interfaces) {
if (is_interface_up(interface)) {
msg << "Interface " << interface << " is up";
LOGTRACE("%s", msg.str().c_str());
LOGTRACE("%s", string("Interface " + interface + " is up").c_str());
bridge_interface = interface;
break;
}
else {
msg << "Interface " << interface << " is not available or is not up";
LOGTRACE("%s", msg.str().c_str());
LOGTRACE("%s", string("Interface " + interface + " is not available or is not up").c_str());
}
}
@ -191,13 +196,13 @@ bool CTapDriver::Init()
return false;
}
LOGINFO("Creating rascsi_bridge for interface %s", bridge_interface.c_str());
LOGINFO("Creating %s for interface %s", BRIDGE_NAME, bridge_interface.c_str());
if (bridge_interface == "eth0") {
LOGDEBUG("brctl addbr rascsi_bridge");
LOGTRACE("brctl addbr %s", BRIDGE_NAME);
if ((ret = ioctl(br_socket_fd, SIOCBRADDBR, "rascsi_bridge")) < 0) {
LOGERROR("Error: can't ioctl SIOCBRADDBR. Errno: %d %s", errno, strerror(errno));
if ((ret = ioctl(br_socket_fd, SIOCBRADDBR, BRIDGE_NAME)) < 0) {
LOGERROR("Can't ioctl SIOCBRADDBR: %s", strerror(errno));
close(m_hTAP);
close(ip_fd);
@ -205,9 +210,9 @@ bool CTapDriver::Init()
return false;
}
LOGDEBUG("brctl addif rascsi_bridge %s", bridge_interface.c_str());
LOGTRACE("brctl addif %s %s", BRIDGE_NAME, bridge_interface.c_str());
if (!br_setif(br_socket_fd, "rascsi_bridge", bridge_interface.c_str(), TRUE)) {
if (!br_setif(br_socket_fd, BRIDGE_NAME, bridge_interface.c_str(), true)) {
close(m_hTAP);
close(ip_fd);
close(br_socket_fd);
@ -215,38 +220,34 @@ bool CTapDriver::Init()
}
}
else {
LOGDEBUG("ip address add %s dev rascsi_bridge", bridge_ip.c_str());
string address = bridge_ip;
string address = inet;
string netmask = "255.255.255.0";
size_t separatorPos = bridge_ip.find('/');
size_t separatorPos = inet.find('/');
if (separatorPos != string::npos) {
address = bridge_ip.substr(0, separatorPos);
address = inet.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());
int m;
if (!GetAsInt(inet.substr(separatorPos + 1), m) || m < 8 || m > 32) {
LOGERROR("Invalid CIDR netmask notation '%s'", inet.substr(separatorPos + 1).c_str());
close(m_hTAP);
close(ip_fd);
close(br_socket_fd);
return false;
}
// long long is required for compatibility with 32 bit platforms
long long mask = pow(2, 32) - (1 << (32 - m));
char buf[16];
sprintf(buf, "%lld.%lld.%lld.%lld", (mask >> 24) & 0xff, (mask >> 16) & 0xff, (mask >> 8) & 0xff,
mask & 0xff);
netmask = buf;
}
LOGDEBUG("brctl addbr rascsi_bridge");
LOGTRACE("brctl addbr %s", BRIDGE_NAME);
if ((ret = ioctl(br_socket_fd, SIOCBRADDBR, "rascsi_bridge")) < 0) {
LOGERROR("Error: can't ioctl SIOCBRADDBR. Errno: %d %s", errno, strerror(errno));
if ((ret = ioctl(br_socket_fd, SIOCBRADDBR, BRIDGE_NAME)) < 0) {
LOGERROR("Can't ioctl SIOCBRADDBR: %s", strerror(errno));
close(m_hTAP);
close(ip_fd);
@ -254,20 +255,36 @@ bool CTapDriver::Init()
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);
strncpy(ifr_a.ifr_name, BRIDGE_NAME, IFNAMSIZ);
struct sockaddr_in* addr = (struct sockaddr_in*)&ifr_a.ifr_addr;
inet_pton(AF_INET, address.c_str(), &addr->sin_addr);
if (inet_pton(AF_INET, address.c_str(), &addr->sin_addr) != 1) {
LOGERROR("Can't convert '%s' into a network address: %s", address.c_str(), strerror(errno));
close(m_hTAP);
close(ip_fd);
close(br_socket_fd);
return false;
}
struct ifreq ifr_n;
ifr_n.ifr_addr.sa_family = AF_INET;
strncpy(ifr_n.ifr_name, "rascsi_bridge", IFNAMSIZ);
strncpy(ifr_n.ifr_name, BRIDGE_NAME, IFNAMSIZ);
struct sockaddr_in* mask = (struct sockaddr_in*)&ifr_n.ifr_addr;
inet_pton(AF_INET, netmask.c_str(), &mask->sin_addr);
if (inet_pton(AF_INET, netmask.c_str(), &mask->sin_addr) != 1) {
LOGERROR("Can't convert '%s' into a netmask: %s", netmask.c_str(), strerror(errno));
close(m_hTAP);
close(ip_fd);
close(br_socket_fd);
return false;
}
LOGTRACE("ip address add %s dev %s", inet.c_str(), BRIDGE_NAME);
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));
LOGERROR("Can't ioctl SIOCSIFADDR or SIOCSIFNETMASK: %s", strerror(errno));
close(m_hTAP);
close(ip_fd);
@ -276,9 +293,9 @@ bool CTapDriver::Init()
}
}
LOGDEBUG("ip link set dev rascsi_bridge up");
LOGTRACE("ip link set dev %s up", BRIDGE_NAME);
if (!ip_link(ip_fd, "rascsi_bridge", TRUE)) {
if (!ip_link(ip_fd, BRIDGE_NAME, true)) {
close(m_hTAP);
close(ip_fd);
close(br_socket_fd);
@ -287,21 +304,21 @@ bool CTapDriver::Init()
}
else
{
LOGINFO("rascsi_bridge is already available");
LOGINFO("%s is already available", BRIDGE_NAME);
}
LOGDEBUG("ip link set ras0 up");
LOGTRACE("ip link set ras0 up");
if (!ip_link(ip_fd, "ras0", TRUE)) {
if (!ip_link(ip_fd, "ras0", true)) {
close(m_hTAP);
close(ip_fd);
close(br_socket_fd);
return false;
}
LOGDEBUG("brctl addif rascsi_bridge ras0");
LOGTRACE("brctl addif %s ras0", BRIDGE_NAME);
if (!br_setif(br_socket_fd, "rascsi_bridge", "ras0", TRUE)) {
if (!br_setif(br_socket_fd, BRIDGE_NAME, "ras0", true)) {
close(m_hTAP);
close(ip_fd);
close(br_socket_fd);
@ -313,7 +330,7 @@ bool CTapDriver::Init()
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));
LOGERROR("Can't ioctl SIOCGIFHWADDR: %s", strerror(errno));
close(m_hTAP);
close(ip_fd);
@ -335,27 +352,27 @@ bool CTapDriver::Init()
#endif // __linux__
#ifdef __NetBSD__
BOOL CTapDriver::Init()
bool CTapDriver::Init(const map<string, string>&)
{
struct ifreq ifr;
struct ifaddrs *ifa, *a;
// TAP Device Initialization
if ((m_hTAP = open("/dev/tap", O_RDWR)) < 0) {
LOGERROR("Error: can't open tap. Errno: %d %s", errno, strerror(errno));
LOGERROR("Can't open tap: %s", strerror(errno));
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));
LOGERROR("Can't ioctl TAPGIFNAME: %s", strerror(errno));
close(m_hTAP);
return false;
}
// Get MAC address
if (getifaddrs(&ifa) == -1) {
LOGERROR("Error: can't getifaddrs. Errno: %d %s", errno, strerror(errno));
LOGERROR("Can't getifaddrs: %s", strerror(errno));
close(m_hTAP);
return false;
}
@ -364,7 +381,7 @@ BOOL CTapDriver::Init()
a->ifa_addr->sa_family == AF_LINK)
break;
if (a == NULL) {
LOGERROR("Error: can't get MAC addressErrno: %d %s", errno, strerror(errno));
LOGERROR("Can't get MAC address: %s", strerror(errno));
close(m_hTAP);
return false;
}
@ -374,7 +391,7 @@ BOOL CTapDriver::Init()
sizeof(m_MacAddr));
freeifaddrs(ifa);
LOGINFO("Tap device : %s\n", ifr.ifr_name);
LOGINFO("Tap device: %s\n", ifr.ifr_name);
return true;
}
@ -389,26 +406,21 @@ void CTapDriver::OpenDump(const Filepath& path) {
}
m_pcap_dumper = pcap_dump_open(m_pcap, path.GetPath());
if (m_pcap_dumper == NULL) {
LOGERROR("Error: can't open pcap file: %s", pcap_geterr(m_pcap));
LOGERROR("Can't open pcap file: %s", pcap_geterr(m_pcap));
throw io_exception("Can't open pcap file");
}
LOGTRACE("%s Opened %s for dumping", __PRETTY_FUNCTION__, path.GetPath());
}
//---------------------------------------------------------------------------
//
// Cleanup
//
//---------------------------------------------------------------------------
void CTapDriver::Cleanup()
{
int br_socket_fd = -1;
if ((br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
LOGERROR("Error: can't open bridge socket. Errno: %d %s", errno, strerror(errno));
LOGERROR("Can't open bridge socket: %s", strerror(errno));
} else {
LOGDEBUG("brctl delif rascsi_bridge ras0");
if (!br_setif(br_socket_fd, "rascsi_bridge", "ras0", FALSE)) {
LOGDEBUG("brctl delif %s ras0", BRIDGE_NAME);
if (!br_setif(br_socket_fd, BRIDGE_NAME, "ras0", false)) {
LOGWARN("Warning: Removing ras0 from the bridge failed.");
LOGWARN("You may need to manually remove the ras0 tap device from the bridge");
}
@ -432,50 +444,29 @@ void CTapDriver::Cleanup()
}
}
//---------------------------------------------------------------------------
//
// Enable
//
//---------------------------------------------------------------------------
bool CTapDriver::Enable(){
int fd = socket(PF_INET, SOCK_DGRAM, 0);
LOGDEBUG("%s: ip link set ras0 up", __PRETTY_FUNCTION__);
bool result = ip_link(fd, "ras0", TRUE);
bool result = ip_link(fd, "ras0", true);
close(fd);
return result;
}
//---------------------------------------------------------------------------
//
// Disable
//
//---------------------------------------------------------------------------
bool CTapDriver::Disable(){
int fd = socket(PF_INET, SOCK_DGRAM, 0);
LOGDEBUG("%s: ip link set ras0 down", __PRETTY_FUNCTION__);
bool result = ip_link(fd, "ras0", FALSE);
bool result = ip_link(fd, "ras0", false);
close(fd);
return result;
}
//---------------------------------------------------------------------------
//
// Flush
//
//---------------------------------------------------------------------------
BOOL CTapDriver::Flush(){
void CTapDriver::Flush(){
LOGTRACE("%s", __PRETTY_FUNCTION__);
while(PendingPackets()){
(void)Rx(m_garbage_buffer);
}
return TRUE;
}
//---------------------------------------------------------------------------
//
// MGet MAC Address
//
//---------------------------------------------------------------------------
void CTapDriver::GetMacAddr(BYTE *mac)
{
ASSERT(mac);
@ -488,7 +479,7 @@ void CTapDriver::GetMacAddr(BYTE *mac)
// Receive
//
//---------------------------------------------------------------------------
BOOL CTapDriver::PendingPackets()
bool CTapDriver::PendingPackets()
{
struct pollfd fds;
@ -501,9 +492,9 @@ BOOL CTapDriver::PendingPackets()
poll(&fds, 1, 0);
LOGTRACE("%s %u revents", __PRETTY_FUNCTION__, fds.revents);
if (!(fds.revents & POLLIN)) {
return FALSE;
return false;
}else {
return TRUE;
return true;
}
}

View File

@ -17,6 +17,7 @@
#include <pcap/pcap.h>
#include "filepath.h"
#include <map>
#include <vector>
#include <string>
@ -24,6 +25,8 @@
#define ETH_FRAME_LEN 1514
#endif
using namespace std;
//===========================================================================
//
// Linux Tap Driver
@ -31,25 +34,31 @@
//===========================================================================
class CTapDriver
{
public:
CTapDriver(const std::string&);
~CTapDriver() {};
private:
friend class SCSIDaynaPort;
friend class SCSIBR;
CTapDriver();
~CTapDriver() {}
bool Init(const map<string, string>&);
public:
bool Init();
void OpenDump(const Filepath& path);
// Capture packets
void Cleanup(); // Cleanup
void GetMacAddr(BYTE *mac); // Get Mac Address
int Rx(BYTE *buf); // Receive
int Tx(const BYTE *buf, int len); // Send
BOOL PendingPackets(); // Check if there are IP packets available
bool PendingPackets(); // Check if there are IP packets available
bool Enable(); // Enable the ras0 interface
bool Disable(); // Disable the ras0 interface
BOOL Flush(); // Purge all of the packets that are waiting to be processed
void Flush(); // Purge all of the packets that are waiting to be processed
private:
BYTE m_MacAddr[6]; // MAC Address
BOOL m_bTxValid; // Send Valid Flag
int m_hTAP; // File handle
BYTE m_garbage_buffer[ETH_FRAME_LEN];
@ -58,6 +67,8 @@ private:
pcap_dumper_t *m_pcap_dumper;
// Prioritized comma-separated list of interfaces to create the bridge for
std::vector<std::string> interfaces;
vector<string> interfaces;
string inet;
};

View File

@ -8,17 +8,20 @@
//---------------------------------------------------------------------------
#include <cassert>
#include <sstream>
#include "rascsi_version.h"
#include "os.h"
#include "log.h"
#include "exceptions.h"
#include "device.h"
set<Device *> Device::devices;
Device::Device(const string& type)
{
assert(type.length() == 4);
devices.insert(this);
this->type = type;
vendor = DEFAULT_VENDOR;
@ -48,6 +51,11 @@ Device::Device(const string& type)
status_code = STATUS_NOERROR;
}
Device::~Device()
{
devices.erase(this);
}
void Device::Reset()
{
locked = false;
@ -113,6 +121,15 @@ const string Device::GetParam(const string& key)
return params.find(key) != params.end() ? params[key] : "";
}
void Device::SetParams(const map<string, string>& params)
{
this->params = GetDefaultParams();
for (const auto& param : params) {
this->params[param.first] = param.second;
}
}
void Device::SetStatusCode(int status_code)
{
if (status_code) {

View File

@ -9,6 +9,7 @@
#pragma once
#include <set>
#include <map>
#include <string>
@ -104,10 +105,14 @@ private:
// Sense Key, ASC and ASCQ
int status_code;
protected:
static set<Device *> devices;
public:
Device(const string&);
virtual ~Device() {};
virtual ~Device();
// Override for device specific initializations, to be called after all device properties have been set
virtual bool Init(const map<string, string>&) { return true; };
@ -166,7 +171,7 @@ public:
void SupportsParams(bool supports_paams) { this->supports_params = supports_paams; }
const map<string, string> GetParams() const { return params; }
const string GetParam(const string&);
void SetParams(const map<string, string>& params) { this->params = params; }
void SetParams(const map<string, string>&);
const map<string, string> GetDefaultParams() const { return default_params; }
void SetDefaultParams(const map<string, string>& default_params) { this->default_params = default_params; }

View File

@ -12,6 +12,7 @@
#include "scsihd_nec.h"
#include "scsimo.h"
#include "scsicd.h"
#include "scsi_printer.h"
#include "scsi_host_bridge.h"
#include "scsi_daynaport.h"
#include "exceptions.h"
@ -50,8 +51,12 @@ DeviceFactory::DeviceFactory()
network_interfaces += network_interface;
}
default_params[SCBR]["interfaces"] = network_interfaces;
default_params[SCDP]["interfaces"] = network_interfaces;
default_params[SCBR]["interface"] = network_interfaces;
default_params[SCBR]["inet"] = "10.10.20.1/24";
default_params[SCDP]["interface"] = network_interfaces;
default_params[SCDP]["inet"] = "10.10.20.1/24";
default_params[SCLP]["cmd"] = "lp -oraw %f";
default_params[SCLP]["timeout"] = "30";
extension_mapping["hdf"] = SAHD;
extension_mapping["hds"] = SCHD;
@ -95,6 +100,9 @@ PbDeviceType DeviceFactory::GetTypeForFile(const string& filename)
else if (filename == "daynaport") {
return SCDP;
}
else if (filename == "printer") {
return SCLP;
}
else if (filename == "services") {
return SCHS;
}
@ -197,6 +205,13 @@ Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename)
device->SetProduct("Host Services");
break;
case SCLP:
device = new SCSIPrinter();
device->SetProduct("SCSI PRINTER");
device->SupportsParams(true);
device->SetDefaultParams(default_params[SCLP]);
break;
default:
break;
}

View File

@ -20,49 +20,48 @@
#include "exceptions.h"
#include "disk.h"
#include "mode_page_device.h"
#include <sstream>
#include "../rascsi.h"
#include "file_access/file_access_factory.h"
Disk::Disk(const std::string id) : ModePageDevice(id), ScsiBlockCommands()
{
disks.insert(this);
using namespace scsi_defs;
Disk::Disk(const string& id) : ModePageDevice(id), ScsiBlockCommands()
{
// Work initialization
configured_sector_size = 0;
disk.size = 0;
disk.blocks = 0;
disk.dcache = NULL;
disk.image_offset = 0;
disk.is_medium_changed = false;
AddCommand(ScsiDefs::eCmdRezero, "Rezero", &Disk::Rezero);
AddCommand(ScsiDefs::eCmdFormat, "FormatUnit", &Disk::FormatUnit);
AddCommand(ScsiDefs::eCmdReassign, "ReassignBlocks", &Disk::ReassignBlocks);
AddCommand(ScsiDefs::eCmdRead6, "Read6", &Disk::Read6);
AddCommand(ScsiDefs::eCmdWrite6, "Write6", &Disk::Write6);
AddCommand(ScsiDefs::eCmdSeek6, "Seek6", &Disk::Seek6);
AddCommand(ScsiDefs::eCmdReserve6, "Reserve6", &Disk::Reserve6);
AddCommand(ScsiDefs::eCmdRelease6, "Release6", &Disk::Release6);
AddCommand(ScsiDefs::eCmdStartStop, "StartStopUnit", &Disk::StartStopUnit);
AddCommand(ScsiDefs::eCmdSendDiag, "SendDiagnostic", &Disk::SendDiagnostic);
AddCommand(ScsiDefs::eCmdRemoval, "PreventAllowMediumRemoval", &Disk::PreventAllowMediumRemoval);
AddCommand(ScsiDefs::eCmdReadCapacity10, "ReadCapacity10", &Disk::ReadCapacity10);
AddCommand(ScsiDefs::eCmdRead10, "Read10", &Disk::Read10);
AddCommand(ScsiDefs::eCmdWrite10, "Write10", &Disk::Write10);
AddCommand(ScsiDefs::eCmdReadLong10, "ReadLong10", &Disk::ReadLong10);
AddCommand(ScsiDefs::eCmdWriteLong10, "WriteLong10", &Disk::WriteLong10);
AddCommand(ScsiDefs::eCmdWriteLong16, "WriteLong16", &Disk::WriteLong16);
AddCommand(ScsiDefs::eCmdSeek10, "Seek10", &Disk::Seek10);
AddCommand(ScsiDefs::eCmdVerify10, "Verify10", &Disk::Verify10);
AddCommand(ScsiDefs::eCmdSynchronizeCache10, "SynchronizeCache10", &Disk::SynchronizeCache10);
AddCommand(ScsiDefs::eCmdSynchronizeCache16, "SynchronizeCache16", &Disk::SynchronizeCache16);
AddCommand(ScsiDefs::eCmdReadDefectData10, "ReadDefectData10", &Disk::ReadDefectData10);
AddCommand(ScsiDefs::eCmdReserve10, "Reserve10", &Disk::Reserve10);
AddCommand(ScsiDefs::eCmdRelease10, "Release10", &Disk::Release10);
AddCommand(ScsiDefs::eCmdRead16, "Read16", &Disk::Read16);
AddCommand(ScsiDefs::eCmdWrite16, "Write16", &Disk::Write16);
AddCommand(ScsiDefs::eCmdVerify16, "Verify16", &Disk::Verify16);
AddCommand(ScsiDefs::eCmdReadCapacity16_ReadLong16, "ReadCapacity16/ReadLong16", &Disk::ReadCapacity16_ReadLong16);
dispatcher.AddCommand(eCmdRezero, "Rezero", &Disk::Rezero);
dispatcher.AddCommand(eCmdFormat, "FormatUnit", &Disk::FormatUnit);
dispatcher.AddCommand(eCmdReassign, "ReassignBlocks", &Disk::ReassignBlocks);
dispatcher.AddCommand(eCmdRead6, "Read6", &Disk::Read6);
dispatcher.AddCommand(eCmdWrite6, "Write6", &Disk::Write6);
dispatcher.AddCommand(eCmdSeek6, "Seek6", &Disk::Seek6);
dispatcher.AddCommand(eCmdReserve6, "Reserve6", &Disk::Reserve6);
dispatcher.AddCommand(eCmdRelease6, "Release6", &Disk::Release6);
dispatcher.AddCommand(eCmdStartStop, "StartStopUnit", &Disk::StartStopUnit);
dispatcher.AddCommand(eCmdSendDiag, "SendDiagnostic", &Disk::SendDiagnostic);
dispatcher.AddCommand(eCmdRemoval, "PreventAllowMediumRemoval", &Disk::PreventAllowMediumRemoval);
dispatcher.AddCommand(eCmdReadCapacity10, "ReadCapacity10", &Disk::ReadCapacity10);
dispatcher.AddCommand(eCmdRead10, "Read10", &Disk::Read10);
dispatcher.AddCommand(eCmdWrite10, "Write10", &Disk::Write10);
dispatcher.AddCommand(eCmdReadLong10, "ReadLong10", &Disk::ReadLong10);
dispatcher.AddCommand(eCmdWriteLong10, "WriteLong10", &Disk::WriteLong10);
dispatcher.AddCommand(eCmdWriteLong16, "WriteLong16", &Disk::WriteLong16);
dispatcher.AddCommand(eCmdSeek10, "Seek10", &Disk::Seek10);
dispatcher.AddCommand(eCmdVerify10, "Verify10", &Disk::Verify10);
dispatcher.AddCommand(eCmdSynchronizeCache10, "SynchronizeCache10", &Disk::SynchronizeCache10);
dispatcher.AddCommand(eCmdSynchronizeCache16, "SynchronizeCache16", &Disk::SynchronizeCache16);
dispatcher.AddCommand(eCmdReadDefectData10, "ReadDefectData10", &Disk::ReadDefectData10);
dispatcher.AddCommand(eCmdReserve10, "Reserve10", &Disk::Reserve10);
dispatcher.AddCommand(eCmdRelease10, "Release10", &Disk::Release10);
dispatcher.AddCommand(eCmdRead16, "Read16", &Disk::Read16);
dispatcher.AddCommand(eCmdWrite16, "Write16", &Disk::Write16);
dispatcher.AddCommand(eCmdVerify16, "Verify16", &Disk::Verify16);
dispatcher.AddCommand(eCmdReadCapacity16_ReadLong16, "ReadCapacity16/ReadLong16", &Disk::ReadCapacity16_ReadLong16);
}
Disk::~Disk()
@ -70,9 +69,7 @@ Disk::~Disk()
// Save disk cache
if (IsReady()) {
// Only if ready...
if (disk.dcache) {
disk.dcache->Save();
}
FlushCache();
}
// Clear disk cache
@ -80,37 +77,22 @@ Disk::~Disk()
delete disk.dcache;
disk.dcache = NULL;
}
for (auto const& command : commands) {
delete command.second;
}
disks.erase(this);
}
void Disk::AddCommand(ScsiDefs::scsi_command opcode, const char* name, void (Disk::*execute)(SASIDEV *))
{
commands[opcode] = new command_t(name, execute);
}
bool Disk::Dispatch(SCSIDEV *controller)
{
ctrl = controller->GetCtrl();
// Media changes must be reported on the next access, i.e. not only for TEST UNIT READY
if (disk.is_medium_changed) {
assert(IsRemovable());
if (commands.count(static_cast<ScsiDefs::scsi_command>(ctrl->cmd[0]))) {
command_t *command = commands[static_cast<ScsiDefs::scsi_command>(ctrl->cmd[0])];
LOGDEBUG("%s Executing %s ($%02X)", __PRETTY_FUNCTION__, command->name, (unsigned int)ctrl->cmd[0]);
(this->*command->execute)(controller);
disk.is_medium_changed = false;
controller->Error(ERROR_CODES::sense_key::UNIT_ATTENTION, ERROR_CODES::asc::NOT_READY_TO_READY_CHANGE);
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 ModePageDevice::Dispatch(controller);
// The superclass handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
}
//---------------------------------------------------------------------------
@ -146,6 +128,13 @@ void Disk::Open(const Filepath& path)
SetLocked(false);
}
void Disk::FlushCache()
{
if (disk.dcache) {
disk.dcache->Save();
}
}
void Disk::Rezero(SASIDEV *controller)
{
if (!CheckReady()) {
@ -397,8 +386,7 @@ void Disk::PreventAllowMediumRemoval(SASIDEV *controller)
void Disk::SynchronizeCache10(SASIDEV *controller)
{
//Flush the RaSCSI cache
disk.dcache->Save();
FlushCache();
controller->Status();
}
@ -419,12 +407,20 @@ void Disk::ReadDefectData10(SASIDEV *controller)
controller->DataIn();
}
void Disk::MediumChanged()
{
assert(IsRemovable());
if (IsRemovable()) {
disk.is_medium_changed = true;
}
}
bool Disk::Eject(bool force)
{
bool status = Device::Eject(force);
if (status) {
// Remove disk cache
disk.dcache->Save();
FlushCache();
delete disk.dcache;
disk.dcache = NULL;
@ -1058,8 +1054,7 @@ bool Disk::StartStop(const DWORD *cdb)
}
if (!start) {
// Flush the cache when stopping
disk.dcache->Save();
FlushCache();
// Look at the eject bit and eject if necessary
if (load) {
@ -1278,9 +1273,8 @@ bool Disk::CheckBlockAddress(SASIDEV *controller, access_mode mode)
uint64_t capacity = GetBlockCount();
if (block > capacity) {
ostringstream s;
s << "Capacity of " << capacity << " blocks exceeded: " << "Trying to access block " << block;
LOGTRACE("%s", s.str().c_str());
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " blocks exceeded: Trying to access block "
+ to_string(block)).c_str());
controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::LBA_OUT_OF_RANGE);
return false;
}
@ -1341,10 +1335,8 @@ bool Disk::GetStartAndCount(SASIDEV *controller, uint64_t& start, uint32_t& coun
// Check capacity
uint64_t capacity = GetBlockCount();
if (start > capacity || start + count > capacity) {
ostringstream s;
s << "Capacity of " << capacity << " blocks exceeded: "
<< "Trying to read block " << start << ", block count " << ctrl->blocks;
LOGTRACE("%s", s.str().c_str());
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " blocks exceeded: Trying to access block "
+ to_string(start) + ", block count " + to_string(ctrl->blocks)).c_str());
controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::LBA_OUT_OF_RANGE);
return false;
}
@ -1368,9 +1360,7 @@ void Disk::SetSectorSizeInBytes(uint32_t size, bool sasi)
{
set<uint32_t> sector_sizes = DeviceFactory::instance().GetSectorSizes(GetType());
if (!sector_sizes.empty() && sector_sizes.find(size) == sector_sizes.end()) {
stringstream error;
error << "Invalid block size of " << size << " bytes";
throw io_exception(error.str());
throw io_exception("Invalid block size of " + to_string(size) + " bytes");
}
switch (size) {

View File

@ -26,12 +26,13 @@
#include "file_support.h"
#include "filepath.h"
#include "interfaces/scsi_block_commands.h"
#include "interfaces/scsi_primary_commands.h"
#include "mode_page_device.h"
#include <string>
#include <set>
#include <map>
using namespace std;
class Disk : public ModePageDevice, ScsiBlockCommands
{
private:
@ -50,24 +51,18 @@ private:
uint32_t blocks; // Total number of sectors
FileAccess *dcache; // Disk cache
off_t image_offset; // Offset to actual data
bool is_medium_changed;
} 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<ScsiDefs::scsi_command, command_t*> commands;
void AddCommand(ScsiDefs::scsi_command, const char*, void (Disk::*)(SASIDEV *));
Dispatcher<Disk, SASIDEV> dispatcher;
public:
Disk(std::string);
Disk(const string&);
virtual ~Disk();
virtual bool Dispatch(SCSIDEV *) override;
void MediumChanged();
void ReserveFile(const string&);
// Media Operations
@ -76,13 +71,14 @@ public:
bool Eject(bool) override;
private:
typedef ModePageDevice super;
// Commands covered by the SCSI specification (see https://www.t10.org/drafts.htm)
void StartStopUnit(SASIDEV *) override;
void SendDiagnostic(SASIDEV *) override;
void StartStopUnit(SASIDEV *);
void SendDiagnostic(SASIDEV *);
void PreventAllowMediumRemoval(SASIDEV *);
void SynchronizeCache10(SASIDEV *) override;
void SynchronizeCache16(SASIDEV *) override;
void SynchronizeCache10(SASIDEV *);
void SynchronizeCache16(SASIDEV *);
void ReadDefectData10(SASIDEV *);
virtual void Read6(SASIDEV *);
void Read10(SASIDEV *) override;
@ -90,12 +86,12 @@ private:
virtual void Write6(SASIDEV *);
void Write10(SASIDEV *) override;
void Write16(SASIDEV *) override;
void ReadLong10(SASIDEV *) override;
void ReadLong16(SASIDEV *) override;
void WriteLong10(SASIDEV *) override;
void WriteLong16(SASIDEV *) override;
void Verify10(SASIDEV *) override;
void Verify16(SASIDEV *) override;
void ReadLong10(SASIDEV *);
void ReadLong16(SASIDEV *);
void WriteLong10(SASIDEV *);
void WriteLong16(SASIDEV *);
void Verify10(SASIDEV *);
void Verify16(SASIDEV *);
void Seek(SASIDEV *);
void Seek10(SASIDEV *);
void ReadCapacity10(SASIDEV *) override;
@ -110,7 +106,7 @@ public:
// Commands covered by the SCSI specification (see https://www.t10.org/drafts.htm)
void Rezero(SASIDEV *);
void FormatUnit(SASIDEV *) override;
void ReassignBlocks(SASIDEV *) override;
void ReassignBlocks(SASIDEV *);
void Seek6(SASIDEV *);
// Command helpers
@ -138,6 +134,7 @@ public:
void SetBlockCount(uint32_t);
bool CheckBlockAddress(SASIDEV *, access_mode);
bool GetStartAndCount(SASIDEV *, uint64_t&, uint32_t&, access_mode);
void FlushCache();
protected:
int ModeSense6(const DWORD *cdb, BYTE *buf);
@ -154,8 +151,6 @@ protected:
// Internal disk data
disk_t disk;
set<Disk *> disks;
private:
void Read(SASIDEV *, uint64_t);
void Write(SASIDEV *, uint64_t);

View File

@ -0,0 +1,67 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
// A template for dispatching SCSI commands
//
//---------------------------------------------------------------------------
#pragma once
#include "log.h"
#include "scsi.h"
#include <map>
class SASIDEV;
class SCSIDEV;
using namespace std;
using namespace scsi_defs;
template<class T, class U>
class Dispatcher
{
public:
Dispatcher() {}
~Dispatcher()
{
for (auto const& command : commands) {
delete command.second;
}
}
typedef struct _command_t {
const char* name;
void (T::*execute)(U *);
_command_t(const char* _name, void (T::*_execute)(U *)) : name(_name), execute(_execute) { };
} command_t;
map<scsi_command, command_t*> commands;
void AddCommand(scsi_command opcode, const char* name, void (T::*execute)(U *))
{
commands[opcode] = new command_t(name, execute);
}
bool Dispatch(T *instance, U *controller)
{
SASIDEV::ctrl_t *ctrl = controller->GetCtrl();
instance->SetCtrl(ctrl);
const auto& it = commands.find(static_cast<scsi_command>(ctrl->cmd[0]));
if (it != commands.end()) {
LOGDEBUG("%s Executing %s ($%02X)", __PRETTY_FUNCTION__, it->second->name, (unsigned int)ctrl->cmd[0]);
(instance->*it->second->execute)(controller);
return true;
}
// Unknown command
return false;
}
};

View File

@ -5,6 +5,8 @@
//
// Copyright (C) 2022 Uwe Seimet
//
// Host Services with realtime clock and shutdown support
//
//---------------------------------------------------------------------------
//
@ -30,27 +32,25 @@
// b) !start && load (EJECT): Shut down the Raspberry Pi
//
#include "controllers/scsidev_ctrl.h"
#include "disk.h"
#include "host_services.h"
using namespace scsi_defs;
HostServices::HostServices() : ModePageDevice("SCHS")
{
dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &HostServices::TestUnitReady);
dispatcher.AddCommand(eCmdStartStop, "StartStopUnit", &HostServices::StartStopUnit);
}
bool HostServices::Dispatch(SCSIDEV *controller)
{
unsigned int cmd = controller->GetCtrl()->cmd[0];
// Only certain commands are supported
// TODO Do not inherit from Disk, mode page handling should be moved to ModePageDevice
if (cmd == ScsiDefs::eCmdTestUnitReady || cmd == ScsiDefs::eCmdRequestSense
|| cmd == ScsiDefs::eCmdInquiry || cmd == ScsiDefs::eCmdReportLuns
|| cmd == ScsiDefs::eCmdModeSense6 || cmd == ScsiDefs::eCmdModeSense10
|| cmd == ScsiDefs::eCmdStartStop) {
LOGTRACE("%s Calling base class for dispatching $%02X", __PRETTY_FUNCTION__, cmd);
return Disk::Dispatch(controller);
}
return false;
// The superclass class handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
}
void HostServices::TestUnitReady(SASIDEV *controller)
void HostServices::TestUnitReady(SCSIDEV *controller)
{
// Always successful
controller->Status();
@ -58,48 +58,29 @@ void HostServices::TestUnitReady(SASIDEV *controller)
int HostServices::Inquiry(const DWORD *cdb, BYTE *buf)
{
int allocation_length = cdb[4] + (((DWORD)cdb[3]) << 8);
if (allocation_length > 4) {
if (allocation_length > 44) {
allocation_length = 44;
}
// 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
memset(buf, 0, allocation_length);
buf[0] = 0x03;
buf[2] = 0x01;
buf[4] = 0x1F;
// Padded vendor, product, revision
memcpy(&buf[8], GetPaddedName().c_str(), 28);
}
return allocation_length;
// Processor device, SPC-5, not removable
return PrimaryDevice::Inquiry(3, 7, false, cdb, buf);
}
void HostServices::StartStopUnit(SASIDEV *controller)
void HostServices::StartStopUnit(SCSIDEV *controller)
{
bool start = ctrl->cmd[4] & 0x01;
bool load = ctrl->cmd[4] & 0x02;
if (!start) {
// Delete all regular disk devices. This also flushes their caches.
for (auto disk : disks) {
if (disk && disk != this) {
delete disk;
// Flush any caches
for (Device *device : devices) {
Disk *disk = dynamic_cast<Disk *>(device);
if (disk) {
disk->FlushCache();
}
}
if (load) {
((SCSIDEV *)controller)->ShutDown(SCSIDEV::rascsi_shutdown_mode::PI);
controller->ScheduleShutDown(SCSIDEV::rascsi_shutdown_mode::PI);
}
else {
((SCSIDEV *)controller)->ShutDown(SCSIDEV::rascsi_shutdown_mode::RASCSI);
controller->ScheduleShutDown(SCSIDEV::rascsi_shutdown_mode::RASCSI);
}
controller->Status();
@ -109,7 +90,91 @@ void HostServices::StartStopUnit(SASIDEV *controller)
controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_FIELD_IN_CDB);
}
int HostServices::AddVendorPage(int page, bool, BYTE *buf)
int HostServices::ModeSense6(const DWORD *cdb, BYTE *buf)
{
// Get length, clear buffer
int length = (int)cdb[4];
memset(buf, 0, length);
// Get page code (0x00 is valid from the beginning)
int page = cdb[2] & 0x3f;
bool valid = page == 0x00;
LOGTRACE("%s Requesting mode page $%02X", __PRETTY_FUNCTION__, page);
// Basic information
int size = 4;
int ret = AddRealtimeClockPage(page, &buf[size]);
if (ret > 0) {
size += ret;
valid = true;
}
if (!valid) {
LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, page);
SetStatusCode(STATUS_INVALIDCDB);
return 0;
}
// Do not return more than ALLOCATION LENGTH bytes
if (size > length) {
LOGTRACE("%s %d bytes available, %d bytes requested", __PRETTY_FUNCTION__, size, length);
size = length;
}
// Final setting of mode data length
buf[0] = size;
return size;
}
int HostServices::ModeSense10(const DWORD *cdb, BYTE *buf)
{
// Get length, clear buffer
int length = cdb[7];
length <<= 8;
length |= cdb[8];
if (length > 0x800) {
length = 0x800;
}
memset(buf, 0, length);
// Get page code (0x00 is valid from the beginning)
int page = cdb[2] & 0x3f;
bool valid = page == 0x00;
LOGTRACE("%s Requesting mode page $%02X", __PRETTY_FUNCTION__, page);
// Basic Information
int size = 8;
int ret = AddRealtimeClockPage(page, &buf[size]);
if (ret > 0) {
size += ret;
valid = true;
}
if (!valid) {
LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, page);
SetStatusCode(STATUS_INVALIDCDB);
return 0;
}
// Do not return more than ALLOCATION LENGTH bytes
if (size > length) {
LOGTRACE("%s %d bytes available, %d bytes requested", __PRETTY_FUNCTION__, size, length);
size = length;
}
// Final setting of mode data length
buf[0] = size >> 8;
buf[1] = size;
return size;
}
int HostServices::AddRealtimeClockPage(int page, BYTE *buf)
{
if (page == 0x20) {
// Data structure version 1.0
@ -131,4 +196,3 @@ int HostServices::AddVendorPage(int page, bool, BYTE *buf)
return 0;
}

View File

@ -5,21 +5,37 @@
//
// Copyright (C) 2022 Uwe Seimet
//
// Host Services with realtime clock and shutdown support
//
//---------------------------------------------------------------------------
#pragma once
#include "disk.h"
#include "mode_page_device.h"
class HostServices: public Disk
using namespace std;
class HostServices: public ModePageDevice
{
public:
HostServices() : Disk("SCHS") {};
~HostServices() {};
HostServices();
~HostServices() {}
virtual bool Dispatch(SCSIDEV *) override;
int Inquiry(const DWORD *, BYTE *) override;
void TestUnitReady(SASIDEV *) override;
void StartStopUnit(SASIDEV *) override;
int AddVendorPage(int, bool, BYTE *) override;
void TestUnitReady(SCSIDEV *);
void StartStopUnit(SCSIDEV *);
int ModeSense6(const DWORD *, BYTE *);
int ModeSense10(const DWORD *, BYTE *);
private:
typedef ModePageDevice super;
Dispatcher<HostServices, SCSIDEV> dispatcher;
int AddRealtimeClockPage(int, BYTE *);
};

View File

@ -30,17 +30,4 @@ public:
virtual void Read16(SASIDEV *) = 0;
virtual void Write10(SASIDEV *) = 0;
virtual void Write16(SASIDEV *) = 0;
// Implemented optional commands
virtual void ReadLong10(SASIDEV *) = 0;
virtual void WriteLong10(SASIDEV *) = 0;
virtual void Verify10(SASIDEV *) = 0;
virtual void ReadLong16(SASIDEV *) = 0;
virtual void WriteLong16(SASIDEV *) = 0;
virtual void Verify16(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

@ -25,10 +25,6 @@ public:
virtual void Inquiry(SASIDEV *) = 0;
virtual void ReportLuns(SASIDEV *) = 0;
// Optional commands
// Implemented for all RaSCSI device types
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

@ -0,0 +1,30 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
// Interface for SCSI printer commands (see SCSI-2 specification)
//
//---------------------------------------------------------------------------
#pragma once
#include "scsi_primary_commands.h"
class SCSIDEV;
class ScsiPrinterCommands : virtual public ScsiPrimaryCommands
{
public:
ScsiPrinterCommands() {}
virtual ~ScsiPrinterCommands() {}
// Mandatory commands
virtual void Print(SCSIDEV *) = 0;
virtual void ReleaseUnit(SCSIDEV *) = 0;
virtual void ReserveUnit(SCSIDEV *) = 0;
virtual void SendDiagnostic(SCSIDEV *) = 0;
};

View File

@ -14,36 +14,20 @@
#include "mode_page_device.h"
using namespace std;
using namespace scsi_defs;
ModePageDevice::ModePageDevice(const string id) : PrimaryDevice(id)
ModePageDevice::ModePageDevice(const string& id) : PrimaryDevice(id)
{
AddCommand(ScsiDefs::eCmdModeSense6, "ModeSense6", &ModePageDevice::ModeSense6);
AddCommand(ScsiDefs::eCmdModeSense10, "ModeSense10", &ModePageDevice::ModeSense10);
AddCommand(ScsiDefs::eCmdModeSelect6, "ModeSelect6", &ModePageDevice::ModeSelect6);
AddCommand(ScsiDefs::eCmdModeSelect10, "ModeSelect10", &ModePageDevice::ModeSelect10);
}
void ModePageDevice::AddCommand(ScsiDefs::scsi_command opcode, const char* name, void (ModePageDevice::*execute)(SASIDEV *))
{
commands[opcode] = new command_t(name, execute);
dispatcher.AddCommand(eCmdModeSense6, "ModeSense6", &ModePageDevice::ModeSense6);
dispatcher.AddCommand(eCmdModeSense10, "ModeSense10", &ModePageDevice::ModeSense10);
dispatcher.AddCommand(eCmdModeSelect6, "ModeSelect6", &ModePageDevice::ModeSelect6);
dispatcher.AddCommand(eCmdModeSelect10, "ModeSelect10", &ModePageDevice::ModeSelect10);
}
bool ModePageDevice::Dispatch(SCSIDEV *controller)
{
ctrl = controller->GetCtrl();
if (commands.count(static_cast<ScsiDefs::scsi_command>(ctrl->cmd[0]))) {
command_t *command = commands[static_cast<ScsiDefs::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;
}
// Unknown command
return false;
// The superclass class handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
}
void ModePageDevice::ModeSense6(SASIDEV *controller)
@ -108,6 +92,7 @@ void ModePageDevice::ModeSelect10(SASIDEV *controller)
int ModePageDevice::ModeSelectCheck(const DWORD *cdb, int length)
{
// Error if save parameters are set for other types than of SCHD or SCRM
// TODO This assumption is not correct, and this code should be located elsewhere
if (!IsSCSIHD() && (cdb[1] & 0x01)) {
SetStatusCode(STATUS_INVALIDCDB);
return 0;

View File

@ -18,7 +18,7 @@ class ModePageDevice: public PrimaryDevice
{
public:
ModePageDevice(const string);
ModePageDevice(const string&);
virtual ~ModePageDevice() {}
virtual bool Dispatch(SCSIDEV *) override;
@ -31,20 +31,14 @@ public:
private:
typedef struct _command_t {
const char* name;
void (ModePageDevice::*execute)(SASIDEV *);
typedef PrimaryDevice super;
_command_t(const char* _name, void (ModePageDevice::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { };
} command_t;
std::map<ScsiDefs::scsi_command, command_t*> commands;
Dispatcher<ModePageDevice, SASIDEV> dispatcher;
void AddCommand(ScsiDefs::scsi_command, const char*, void (ModePageDevice::*)(SASIDEV *));
void ModeSense6(SASIDEV *) override;
void ModeSense10(SASIDEV *) override;
void ModeSelect6(SASIDEV *) override;
void ModeSelect10(SASIDEV *) override;
void ModeSense6(SASIDEV *);
void ModeSense10(SASIDEV *);
void ModeSelect6(SASIDEV *);
void ModeSelect10(SASIDEV *);
int ModeSelectCheck(const DWORD *, int);
int ModeSelectCheck6(const DWORD *);

View File

@ -9,44 +9,28 @@
#include "log.h"
#include "controllers/scsidev_ctrl.h"
#include "dispatcher.h"
#include "primary_device.h"
using namespace std;
using namespace scsi_defs;
PrimaryDevice::PrimaryDevice(const string id) : ScsiPrimaryCommands(), Device(id)
PrimaryDevice::PrimaryDevice(const string& id) : ScsiPrimaryCommands(), Device(id)
{
ctrl = NULL;
// Mandatory SCSI primary commands
AddCommand(ScsiDefs::eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady);
AddCommand(ScsiDefs::eCmdInquiry, "Inquiry", &PrimaryDevice::Inquiry);
AddCommand(ScsiDefs::eCmdReportLuns, "ReportLuns", &PrimaryDevice::ReportLuns);
dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady);
dispatcher.AddCommand(eCmdInquiry, "Inquiry", &PrimaryDevice::Inquiry);
dispatcher.AddCommand(eCmdReportLuns, "ReportLuns", &PrimaryDevice::ReportLuns);
// Optional commands used by all RaSCSI devices
AddCommand(ScsiDefs::eCmdRequestSense, "RequestSense", &PrimaryDevice::RequestSense);
}
void PrimaryDevice::AddCommand(ScsiDefs::scsi_command opcode, const char* name, void (PrimaryDevice::*execute)(SASIDEV *))
{
commands[opcode] = new command_t(name, execute);
dispatcher.AddCommand(eCmdRequestSense, "RequestSense", &PrimaryDevice::RequestSense);
}
bool PrimaryDevice::Dispatch(SCSIDEV *controller)
{
ctrl = controller->GetCtrl();
if (commands.count(static_cast<ScsiDefs::scsi_command>(ctrl->cmd[0]))) {
command_t *command = commands[static_cast<ScsiDefs::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;
}
// Unknown command
return false;
return dispatcher.Dispatch(this, controller);
}
void PrimaryDevice::TestUnitReady(SASIDEV *controller)
@ -61,37 +45,19 @@ void PrimaryDevice::TestUnitReady(SASIDEV *controller)
void PrimaryDevice::Inquiry(SASIDEV *controller)
{
int lun = controller->GetEffectiveLun();
const Device *device = ctrl->unit[lun];
// Find a valid unit
// TODO The code below is probably wrong. It results in the same INQUIRY data being
// used for all LUNs, even though each LUN has its individual set of INQUIRY data.
// In addition, it supports gaps in the LUN list, which is not correct.
if (!device) {
for (int valid_lun = 0; valid_lun < SASIDEV::UnitMax; valid_lun++) {
if (ctrl->unit[valid_lun]) {
device = ctrl->unit[valid_lun];
break;
}
}
}
if (device) {
ctrl->length = Inquiry(ctrl->cmd, ctrl->buffer);
} else {
ctrl->length = 0;
}
ctrl->length = Inquiry(ctrl->cmd, ctrl->buffer);
if (ctrl->length <= 0) {
controller->Error();
return;
}
int lun = controller->GetEffectiveLun();
// Report if the device does not support the requested LUN
if (!ctrl->unit[lun]) {
LOGDEBUG("Reporting LUN %d for device ID %d as not supported", lun, ctrl->device->GetId());
LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, ctrl->device->GetId());
// Signal that the requested LUN does not exist
ctrl->buffer[0] |= 0x7f;
}
@ -183,6 +149,33 @@ bool PrimaryDevice::CheckReady()
return true;
}
int PrimaryDevice::Inquiry(int type, int scsi_level, bool is_removable, const DWORD *cdb, BYTE *buf)
{
int allocation_length = cdb[4] + (((DWORD)cdb[3]) << 8);
if (allocation_length > 4) {
if (allocation_length > 44) {
allocation_length = 44;
}
// Basic data
// buf[0] ... SCSI Device type
// buf[1] ... Bit 7: Removable/not removable
// buf[2] ... SCSI-2 compliant command system
// buf[3] ... SCSI-2 compliant Inquiry response
// buf[4] ... Inquiry additional data
memset(buf, 0, allocation_length);
buf[0] = type;
buf[1] = is_removable ? 0x80 : 0x00;
buf[2] = scsi_level;
buf[4] = 0x1F;
// Padded vendor, product, revision
memcpy(&buf[8], GetPaddedName().c_str(), 28);
}
return allocation_length;
}
int PrimaryDevice::RequestSense(const DWORD *cdb, BYTE *buf)
{
ASSERT(cdb);
@ -221,3 +214,9 @@ int PrimaryDevice::RequestSense(const DWORD *cdb, BYTE *buf)
return size;
}
bool PrimaryDevice::WriteBytes(BYTE *buf, uint32_t length)
{
LOGERROR("%s Writing bytes is not supported by this device", __PRETTY_FUNCTION__);
return false;
}

View File

@ -11,11 +11,11 @@
#pragma once
#include "controllers/sasidev_ctrl.h"
#include "controllers/scsidev_ctrl.h"
#include "interfaces/scsi_primary_commands.h"
#include "device.h"
#include "dispatcher.h"
#include <string>
#include <map>
using namespace std;
@ -23,33 +23,30 @@ class PrimaryDevice: public Device, virtual public ScsiPrimaryCommands
{
public:
PrimaryDevice(const string);
PrimaryDevice(const string&);
virtual ~PrimaryDevice() {}
virtual bool Dispatch(SCSIDEV *) override;
virtual bool Dispatch(SCSIDEV *);
void TestUnitReady(SASIDEV *);
void RequestSense(SASIDEV *);
void SetCtrl(SASIDEV::ctrl_t *ctrl) { this->ctrl = ctrl; }
bool CheckReady();
virtual int Inquiry(const DWORD *, BYTE *) = 0;
virtual int RequestSense(const DWORD *, BYTE *);
virtual bool WriteBytes(BYTE *, uint32_t);
protected:
int Inquiry(int, int, bool, const DWORD *, BYTE *);
SASIDEV::ctrl_t *ctrl;
private:
typedef struct _command_t {
const char* name;
void (PrimaryDevice::*execute)(SASIDEV *);
_command_t(const char* _name, void (PrimaryDevice::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { };
} command_t;
std::map<ScsiDefs::scsi_command, command_t*> commands;
void AddCommand(ScsiDefs::scsi_command, const char*, void (PrimaryDevice::*)(SASIDEV *));
Dispatcher<PrimaryDevice, SASIDEV> dispatcher;
void Inquiry(SASIDEV *);
void ReportLuns(SASIDEV *);

View File

@ -13,11 +13,11 @@
// [ SASI hard disk ]
//
//---------------------------------------------------------------------------
#include "sasihd.h"
#include "fileio.h"
#include "exceptions.h"
#include <sstream>
#include "../rascsi.h"
#include "../config.h"
//===========================================================================
//

View File

@ -28,23 +28,25 @@
//---------------------------------------------------------------------------
#include "scsi_daynaport.h"
#include <sstream>
using namespace scsi_defs;
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 };
// TODO Disk should not be the superclass
SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP")
{
m_tap = NULL;
m_bTapEnable = false;
AddCommand(ScsiDefs::eCmdTestUnitReady, "TestUnitReady", &SCSIDaynaPort::TestUnitReady);
AddCommand(ScsiDefs::eCmdRead6, "Read6", &SCSIDaynaPort::Read6);
AddCommand(ScsiDefs::eCmdWrite6, "Write6", &SCSIDaynaPort::Write6);
AddCommand(ScsiDefs::eCmdRetrieveStats, "RetrieveStats", &SCSIDaynaPort::RetrieveStatistics);
AddCommand(ScsiDefs::eCmdSetIfaceMode, "SetIfaceMode", &SCSIDaynaPort::SetInterfaceMode);
AddCommand(ScsiDefs::eCmdSetMcastAddr, "SetMcastAddr", &SCSIDaynaPort::SetMcastAddr);
AddCommand(ScsiDefs::eCmdEnableInterface, "EnableInterface", &SCSIDaynaPort::EnableInterface);
dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &SCSIDaynaPort::TestUnitReady);
dispatcher.AddCommand(eCmdRead6, "Read6", &SCSIDaynaPort::Read6);
dispatcher.AddCommand(eCmdWrite6, "Write6", &SCSIDaynaPort::Write6);
dispatcher.AddCommand(eCmdRetrieveStats, "RetrieveStats", &SCSIDaynaPort::RetrieveStatistics);
dispatcher.AddCommand(eCmdSetIfaceMode, "SetIfaceMode", &SCSIDaynaPort::SetInterfaceMode);
dispatcher.AddCommand(eCmdSetMcastAddr, "SetMcastAddr", &SCSIDaynaPort::SetMcastAddr);
dispatcher.AddCommand(eCmdEnableInterface, "EnableInterface", &SCSIDaynaPort::EnableInterface);
}
SCSIDaynaPort::~SCSIDaynaPort()
@ -54,45 +56,22 @@ SCSIDaynaPort::~SCSIDaynaPort()
m_tap->Cleanup();
delete m_tap;
}
for (auto const& command : commands) {
delete command.second;
}
}
void SCSIDaynaPort::AddCommand(ScsiDefs::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<ScsiDefs::scsi_command>(ctrl->cmd[0]))) {
command_t *command = commands[static_cast<ScsiDefs::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);
// The superclass class handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
}
bool SCSIDaynaPort::Init(const map<string, string>& params)
{
SetParams(params.empty() ? GetDefaultParams() : params);
SetParams(params);
#ifdef __linux__
// TAP Driver Generation
m_tap = new CTapDriver(GetParam("interfaces"));
m_bTapEnable = m_tap->Init();
m_tap = new CTapDriver();
m_bTapEnable = m_tap->Init(GetParams());
if(!m_bTapEnable){
LOGERROR("Unable to open the TAP interface");
@ -133,36 +112,10 @@ void SCSIDaynaPort::Open(const Filepath& path)
m_tap->OpenDump(path);
}
//---------------------------------------------------------------------------
//
// INQUIRY
//
//---------------------------------------------------------------------------
int SCSIDaynaPort::Inquiry(const DWORD *cdb, BYTE *buf)
{
int allocation_length = cdb[4] + (((DWORD)cdb[3]) << 8);
if (allocation_length > 4) {
if (allocation_length > 44) {
allocation_length = 44;
}
// 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(&buf[8], GetPaddedName().c_str(), 28);
}
return allocation_length;
// Processor device, SCSI-2, not removable
return PrimaryDevice::Inquiry(3, 2, false, cdb, buf);
}
//---------------------------------------------------------------------------
@ -201,10 +154,6 @@ int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, uint64_t block)
int rx_packet_size = 0;
scsi_resp_read_t *response = (scsi_resp_read_t*)buf;
ostringstream s;
s << __PRETTY_FUNCTION__ << " reading DaynaPort block " << block;
LOGTRACE("%s", s.str().c_str());
int requested_length = cdb[4];
LOGTRACE("%s Read maximum length %d, (%04X)", __PRETTY_FUNCTION__, requested_length, requested_length);
@ -292,8 +241,17 @@ int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, uint64_t block)
// } else {
// response->flags = e_no_more_data;
// }
buf[0] = (BYTE)((rx_packet_size >> 8) & 0xFF);
buf[1] = (BYTE)(rx_packet_size & 0xFF);
int size = rx_packet_size;
if (size < 64) {
// A frame must have at least 64 bytes (see https://github.com/akuker/RASCSI/issues/619)
// Note that this work-around breaks the checksum. As currently there are no known drivers
// that care for the checksum, and the Daynaport driver for the Atari expects frames of
// 64 bytes it was decided to accept the broken checksum. If a driver should pop up that
// breaks because of this, the work-around has to be re-evaluated.
size = 64;
}
buf[0] = size >> 8;
buf[1] = size;
buf[2] = 0;
buf[3] = 0;
@ -306,7 +264,7 @@ int SCSIDaynaPort::Read(const DWORD *cdb, BYTE *buf, uint64_t block)
// Return the packet size + 2 for the length + 4 for the flag field
// The CRC was already appended by the ctapdriver
return rx_packet_size + DAYNAPORT_READ_HEADER_SZ;
return size + DAYNAPORT_READ_HEADER_SZ;
}
// If we got to this point, there are still messages in the queue, so
// we should loop back and get the next one.

View File

@ -42,19 +42,6 @@
class SCSIDaynaPort: public Disk
{
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<ScsiDefs::scsi_command, command_t*> commands;
SASIDEV::ctrl_t *ctrl;
void AddCommand(ScsiDefs::scsi_command, const char*, void (SCSIDaynaPort::*)(SASIDEV *));
public:
SCSIDaynaPort();
~SCSIDaynaPort();
@ -103,6 +90,10 @@ public:
static const DWORD DAYNAPORT_READ_HEADER_SZ = 2 + 4;
private:
typedef Disk super;
Dispatcher<SCSIDaynaPort, SASIDEV> dispatcher;
typedef struct __attribute__((packed)) {
BYTE operation_code;
BYTE misc_cdb_information;

View File

@ -16,17 +16,19 @@
// work with the Sharp X68000 operating system.
//---------------------------------------------------------------------------
#include "controllers/scsidev_ctrl.h"
#include "scsi_host_bridge.h"
#include "ctapdriver.h"
#include "cfilesystem.h"
#include <sstream>
using namespace std;
using namespace scsi_defs;
SCSIBR::SCSIBR() : Disk("SCBR")
{
tap = NULL;
m_bTapEnable = false;
packet_enable = false;
fsoptlen = 0;
fsoutlen = 0;
@ -37,9 +39,9 @@ SCSIBR::SCSIBR() : Disk("SCBR")
fs = new CFileSys();
fs->Reset();
AddCommand(ScsiDefs::eCmdTestUnitReady, "TestUnitReady", &SCSIBR::TestUnitReady);
AddCommand(ScsiDefs::eCmdRead6, "GetMessage10", &SCSIBR::GetMessage10);
AddCommand(ScsiDefs::eCmdWrite6, "SendMessage10", &SCSIBR::SendMessage10);
dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &SCSIBR::TestUnitReady);
dispatcher.AddCommand(eCmdRead6, "GetMessage10", &SCSIBR::GetMessage10);
dispatcher.AddCommand(eCmdWrite6, "SendMessage10", &SCSIBR::SendMessage10);
}
SCSIBR::~SCSIBR()
@ -55,21 +57,16 @@ SCSIBR::~SCSIBR()
fs->Reset();
delete fs;
}
for (auto const& command : commands) {
delete command.second;
}
}
bool SCSIBR::Init(const map<string, string>& params)
{
// Use default parameters if no parameters were provided
SetParams(params.empty() ? GetDefaultParams() : params);
SetParams(params);
#ifdef __linux__
// TAP Driver Generation
tap = new CTapDriver(GetParam("interfaces"));
m_bTapEnable = tap->Init();
tap = new CTapDriver();
m_bTapEnable = tap->Init(GetParams());
if (!m_bTapEnable){
LOGERROR("Unable to open the TAP interface");
return false;
@ -96,29 +93,10 @@ bool SCSIBR::Init(const map<string, string>& params)
#endif
}
void SCSIBR::AddCommand(ScsiDefs::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<ScsiDefs::scsi_command>(ctrl->cmd[0]))) {
command_t *command = commands[static_cast<ScsiDefs::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);
// The superclass class handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
}
//---------------------------------------------------------------------------
@ -128,14 +106,10 @@ bool SCSIBR::Dispatch(SCSIDEV *controller)
//---------------------------------------------------------------------------
int SCSIBR::Inquiry(const DWORD *cdb, BYTE *buf)
{
ASSERT(cdb);
ASSERT(buf);
ASSERT(cdb[0] == 0x12);
// EVPD check
if (cdb[1] & 0x01) {
SetStatusCode(STATUS_INVALIDCDB);
return FALSE;
return 0;
}
// Basic data
@ -175,15 +149,9 @@ int SCSIBR::Inquiry(const DWORD *cdb, BYTE *buf)
return size;
}
//---------------------------------------------------------------------------
//
// TEST UNIT READY
//
//---------------------------------------------------------------------------
void SCSIBR::TestUnitReady(SASIDEV *controller)
{
// TEST UNIT READY Success
// Always successful
controller->Status();}
//---------------------------------------------------------------------------

View File

@ -32,19 +32,6 @@ class CFileSys;
class SCSIBR : public Disk
{
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<ScsiDefs::scsi_command, command_t*> commands;
SASIDEV::ctrl_t *ctrl;
void AddCommand(ScsiDefs::scsi_command, const char*, void (SCSIBR::*)(SASIDEV *));
public:
SCSIBR();
~SCSIBR();
@ -61,6 +48,10 @@ public:
void SendMessage10(SASIDEV *);
private:
typedef Disk super;
Dispatcher<SCSIBR, SASIDEV> dispatcher;
int GetMacAddr(BYTE *buf); // Get MAC address
void SetMacAddr(BYTE *buf); // Set MAC address
void ReceivePacket(); // Receive a packet

View File

@ -0,0 +1,302 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
// Implementation of a SCSI printer (see SCSI-2 specification for a command description)
//
//---------------------------------------------------------------------------
//
// How to print:
//
// 1. The client reserves the printer device with RESERVE UNIT (optional step, mandatory for
// a multi-initiator environment).
// 2. The client sends the data to be printed with one or several PRINT commands. Due to
// https://github.com/akuker/RASCSI/issues/669 the maximum transfer size per PRINT command is
// limited to 4096 bytes.
// 3. The client triggers printing with SYNCHRONIZE BUFFER.
// 4. The client releases the printer with RELEASE UNIT (optional step, mandatory for a
// multi-initiator environment).
//
// A client usually does not know whether it is running in a multi-initiator environment. This is why
// always using a reservation is recommended.
//
// The command to be used for printing can be set with the "cmd" property when attaching the device.
// By default the data to be printed are sent to the printer unmodified, using "lp -oraw %f". This
// requires that the client uses a printer driver compatible with the respective printer, or that the
// printing service on the Pi is configured to do any necessary conversions, or that the print command
// applies any conversions on the file to be printed (%f) before passing it to the printing service.
// 'enscript' is an example for a conversion tool.
// By attaching different devices/LUNs multiple printers (i.e. different print commands) are possible.
// Note that the print command is not executed by root but with the permissions of the lp user.
//
// With STOP PRINT printing can be cancelled before SYNCHRONIZE BUFFER was sent.
//
// SEND DIAGNOSTIC currently returns no data.
//
#include <sys/stat.h>
#include "controllers/scsidev_ctrl.h"
#include "../rasutil.h"
#include "scsi_printer.h"
#include <string>
#define NOT_RESERVED -2
using namespace std;
using namespace scsi_defs;
using namespace ras_util;
SCSIPrinter::SCSIPrinter() : PrimaryDevice("SCLP"), ScsiPrinterCommands()
{
fd = -1;
reserving_initiator = NOT_RESERVED;
reservation_time = 0;
timeout = 0;
dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &SCSIPrinter::TestUnitReady);
dispatcher.AddCommand(eCmdReserve6, "ReserveUnit", &SCSIPrinter::ReserveUnit);
dispatcher.AddCommand(eCmdRelease6, "ReleaseUnit", &SCSIPrinter::ReleaseUnit);
dispatcher.AddCommand(eCmdWrite6, "Print", &SCSIPrinter::Print);
dispatcher.AddCommand(eCmdSynchronizeBuffer, "SynchronizeBuffer", &SCSIPrinter::SynchronizeBuffer);
dispatcher.AddCommand(eCmdSendDiag, "SendDiagnostic", &SCSIPrinter::SendDiagnostic);
dispatcher.AddCommand(eCmdStartStop, "StopPrint", &SCSIPrinter::StopPrint);
}
SCSIPrinter::~SCSIPrinter()
{
DiscardReservation();
}
bool SCSIPrinter::Init(const map<string, string>& params)
{
SetParams(params);
if (GetParam("cmd").find("%f") == string::npos) {
LOGERROR("Missing filename specifier %s", "%f");
return false;
}
if (!GetAsInt(GetParam("timeout"), timeout) || timeout <= 0) {
LOGERROR("Reservation timeout value must be > 0");
return false;
}
return true;
}
bool SCSIPrinter::Dispatch(SCSIDEV *controller)
{
// The superclass class handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
}
void SCSIPrinter::TestUnitReady(SCSIDEV *controller)
{
if (!CheckReservation(controller)) {
return;
}
controller->Status();
}
int SCSIPrinter::Inquiry(const DWORD *cdb, BYTE *buf)
{
// Printer device, SCSI-2, not removable
return PrimaryDevice::Inquiry(2, 2, false, cdb, buf);
}
void SCSIPrinter::ReserveUnit(SCSIDEV *controller)
{
// The printer is released after a configurable time in order to prevent deadlocks caused by broken clients
if (reservation_time + timeout < time(0)) {
DiscardReservation();
}
if (!CheckReservation(controller)) {
return;
}
reserving_initiator = controller->GetInitiatorId();
if (reserving_initiator != -1) {
LOGTRACE("Reserved device ID %d, LUN %d for initiator ID %d", GetId(), GetLun(), reserving_initiator);
}
else {
LOGTRACE("Reserved device ID %d, LUN %d for unknown initiator", GetId(), GetLun());
}
Cleanup();
controller->Status();
}
void SCSIPrinter::ReleaseUnit(SCSIDEV *controller)
{
if (!CheckReservation(controller)) {
return;
}
if (reserving_initiator != -1) {
LOGTRACE("Released device ID %d, LUN %d reserved by initiator ID %d", GetId(), GetLun(), reserving_initiator);
}
else {
LOGTRACE("Released device ID %d, LUN %d reserved by unknown initiator", GetId(), GetLun());
}
DiscardReservation();
controller->Status();
}
void SCSIPrinter::Print(SCSIDEV *controller)
{
if (!CheckReservation(controller)) {
return;
}
uint32_t length = ctrl->cmd[2];
length <<= 8;
length |= ctrl->cmd[3];
length <<= 8;
length |= ctrl->cmd[4];
LOGTRACE("Receiving %d bytes to be printed", length);
// TODO This device suffers from the statically allocated buffer size,
// see https://github.com/akuker/RASCSI/issues/669
if (length > (uint32_t)controller->DEFAULT_BUFFER_SIZE) {
LOGERROR("Transfer buffer overflow");
controller->Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_FIELD_IN_CDB);
return;
}
ctrl->length = length;
controller->SetByteTransfer(true);
controller->DataOut();
}
void SCSIPrinter::SynchronizeBuffer(SCSIDEV *controller)
{
if (!CheckReservation(controller)) {
return;
}
if (fd == -1) {
controller->Error();
return;
}
// Make the file readable for the lp user
fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
struct stat st;
fstat(fd, &st);
close(fd);
fd = -1;
string cmd = GetParam("cmd");
size_t file_position = cmd.find("%f");
assert(file_position != string::npos);
cmd.replace(file_position, 2, filename);
cmd = "sudo -u lp " + cmd;
LOGTRACE("%s", string("Printing file with size of " + to_string(st.st_size) +" byte(s)").c_str());
LOGDEBUG("Executing '%s'", cmd.c_str());
if (system(cmd.c_str())) {
LOGERROR("Printing failed, the printing system might not be configured");
controller->Error();
}
else {
controller->Status();
}
unlink(filename);
}
void SCSIPrinter::SendDiagnostic(SCSIDEV *controller)
{
if (!CheckReservation(controller)) {
return;
}
controller->Status();
}
void SCSIPrinter::StopPrint(SCSIDEV *controller)
{
if (!CheckReservation(controller)) {
return;
}
// Nothing to do, printing has not yet been started
controller->Status();
}
bool SCSIPrinter::WriteBytes(BYTE *buf, uint32_t length)
{
if (fd == -1) {
strcpy(filename, TMP_FILE_PATTERN);
fd = mkstemp(filename);
if (fd == -1) {
LOGERROR("Can't create printer output file: %s", strerror(errno));
return false;
}
LOGTRACE("Created printer output file '%s'", filename);
}
LOGTRACE("Appending %d byte(s) to printer output file", length);
write(fd, buf, length);
return true;
}
bool SCSIPrinter::CheckReservation(SCSIDEV *controller)
{
if (reserving_initiator == NOT_RESERVED || reserving_initiator == controller->GetInitiatorId()) {
reservation_time = time(0);
return true;
}
if (controller->GetInitiatorId() != -1) {
LOGTRACE("Initiator ID %d tries to access reserved device ID %d, LUN %d", controller->GetInitiatorId(), GetId(), GetLun());
}
else {
LOGTRACE("Unknown initiator tries to access reserved device ID %d, LUN %d", GetId(), GetLun());
}
controller->Error(ERROR_CODES::sense_key::ABORTED_COMMAND, ERROR_CODES::asc::NO_ADDITIONAL_SENSE_INFORMATION,
ERROR_CODES::status::RESERVATION_CONFLICT);
return false;
}
void SCSIPrinter::DiscardReservation()
{
Cleanup();
reserving_initiator = NOT_RESERVED;
}
void SCSIPrinter::Cleanup()
{
if (fd != -1) {
close(fd);
fd = -1;
unlink(filename);
}
}

View File

@ -0,0 +1,61 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
// Implementation of a SCSI printer (see SCSI-2 specification for a command description)
//
//---------------------------------------------------------------------------
#pragma once
#include "interfaces/scsi_printer_commands.h"
#include "primary_device.h"
#include <string>
#include <map>
using namespace std;
#define TMP_FILE_PATTERN "/tmp/rascsi_sclp-XXXXXX"
class SCSIPrinter: public PrimaryDevice, ScsiPrinterCommands
{
public:
SCSIPrinter();
~SCSIPrinter();
virtual bool Dispatch(SCSIDEV *) override;
bool Init(const map<string, string>&);
int Inquiry(const DWORD *, BYTE *) override;
void TestUnitReady(SCSIDEV *);
void ReserveUnit(SCSIDEV *);
void ReleaseUnit(SCSIDEV *);
void Print(SCSIDEV *);
void SynchronizeBuffer(SCSIDEV *);
void SendDiagnostic(SCSIDEV *);
void StopPrint(SCSIDEV *);
bool WriteBytes(BYTE *, uint32_t) override;
bool CheckReservation(SCSIDEV *);
void DiscardReservation();
void Cleanup();
private:
typedef PrimaryDevice super;
Dispatcher<SCSIPrinter, SCSIDEV> dispatcher;
char filename[sizeof(TMP_FILE_PATTERN) + 1];
int fd;
int reserving_initiator;
time_t reservation_time;
int timeout;
};

View File

@ -10,28 +10,23 @@
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ SCSI CD-ROM for Apple Macintosh ]
// [ SCSI CD-ROM ]
//
//---------------------------------------------------------------------------
#include "scsicd.h"
#include "fileio.h"
#include "exceptions.h"
#include <sstream>
#include "../rascsi.h"
#include "file_access/file_access_factory.h"
using namespace scsi_defs;
//===========================================================================
//
// CD Track
// CD Track
//
//===========================================================================
//---------------------------------------------------------------------------
//
// Constructor
//
//---------------------------------------------------------------------------
CDTrack::CDTrack(SCSICD *scsicd)
{
ASSERT(scsicd);
@ -50,20 +45,6 @@ CDTrack::CDTrack(SCSICD *scsicd)
raw = false;
}
//---------------------------------------------------------------------------
//
// Destructor
//
//---------------------------------------------------------------------------
CDTrack::~CDTrack()
{
}
//---------------------------------------------------------------------------
//
// Init
//
//---------------------------------------------------------------------------
void CDTrack::Init(int track, DWORD first, DWORD last)
{
ASSERT(!valid);
@ -79,11 +60,6 @@ void CDTrack::Init(int track, DWORD first, DWORD last)
last_lba = last;
}
//---------------------------------------------------------------------------
//
// Set Path
//
//---------------------------------------------------------------------------
void CDTrack::SetPath(bool cdda, const Filepath& path)
{
ASSERT(valid);
@ -95,11 +71,6 @@ void CDTrack::SetPath(bool cdda, const Filepath& path)
imgpath = path;
}
//---------------------------------------------------------------------------
//
// Get Path
//
//---------------------------------------------------------------------------
void CDTrack::GetPath(Filepath& path) const
{
ASSERT(valid);
@ -108,11 +79,6 @@ void CDTrack::GetPath(Filepath& path) const
path = imgpath;
}
//---------------------------------------------------------------------------
//
// Add Index
//
//---------------------------------------------------------------------------
void CDTrack::AddIndex(int index, DWORD lba)
{
ASSERT(valid);
@ -150,11 +116,6 @@ DWORD CDTrack::GetLast() const
return last_lba;
}
//---------------------------------------------------------------------------
//
// Get the number of blocks
//
//---------------------------------------------------------------------------
DWORD CDTrack::GetBlocks() const
{
ASSERT(valid);
@ -164,11 +125,6 @@ DWORD CDTrack::GetBlocks() const
return (DWORD)(last_lba - first_lba + 1);
}
//---------------------------------------------------------------------------
//
// Get track number
//
//---------------------------------------------------------------------------
int CDTrack::GetTrackNo() const
{
ASSERT(valid);
@ -221,11 +177,6 @@ bool CDTrack::IsAudio() const
//
//===========================================================================
//---------------------------------------------------------------------------
//
// Constructor
//
//---------------------------------------------------------------------------
SCSICD::SCSICD() : Disk("SCCD"), ScsiMmcCommands(), FileSupport()
{
// NOT in raw format
@ -242,55 +193,22 @@ SCSICD::SCSICD() : Disk("SCCD"), ScsiMmcCommands(), FileSupport()
dataindex = -1;
audioindex = -1;
AddCommand(ScsiDefs::eCmdReadToc, "ReadToc", &SCSICD::ReadToc);
AddCommand(ScsiDefs::eCmdGetEventStatusNotification, "GetEventStatusNotification", &SCSICD::GetEventStatusNotification);
dispatcher.AddCommand(eCmdReadToc, "ReadToc", &SCSICD::ReadToc);
dispatcher.AddCommand(eCmdGetEventStatusNotification, "GetEventStatusNotification", &SCSICD::GetEventStatusNotification);
}
//---------------------------------------------------------------------------
//
// Destructor
//
//---------------------------------------------------------------------------
SCSICD::~SCSICD()
{
// Clear track
ClearTrack();
for (auto const& command : commands) {
delete command.second;
}
}
void SCSICD::AddCommand(ScsiDefs::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<ScsiDefs::scsi_command>(ctrl->cmd[0]))) {
command_t *command = commands[static_cast<ScsiDefs::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);
// The superclass class handles the less specific commands
return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller);
}
//---------------------------------------------------------------------------
//
// Open
//
//---------------------------------------------------------------------------
void SCSICD::Open(const Filepath& path)
{
off_t size;
@ -358,21 +276,11 @@ void SCSICD::Open(const Filepath& path)
}
}
//---------------------------------------------------------------------------
//
// Open (CUE)
//
//---------------------------------------------------------------------------
void SCSICD::OpenCue(const Filepath& /*path*/)
{
throw io_exception("Opening CUE CD-ROM files is not supported");
}
//---------------------------------------------------------------------------
//
// Open (ISO)
//
//---------------------------------------------------------------------------
void SCSICD::OpenIso(const Filepath& path)
{
// Open as read-only
@ -423,9 +331,8 @@ void SCSICD::OpenIso(const Filepath& path)
if (rawfile) {
// Size must be a multiple of 2536
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());
throw io_exception("Raw ISO CD-ROM file size must be a multiple of 2536 bytes but is "
+ to_string(size) + " bytes");
}
// Set the number of blocks
@ -444,11 +351,6 @@ void SCSICD::OpenIso(const Filepath& path)
dataindex = 0;
}
//---------------------------------------------------------------------------
//
// Open (Physical)
//
//---------------------------------------------------------------------------
void SCSICD::OpenPhysical(const Filepath& path)
{
// Open as read-only
@ -494,20 +396,12 @@ void SCSICD::ReadToc(SASIDEV *controller)
controller->DataIn();
}
//---------------------------------------------------------------------------
//
// INQUIRY
//
//---------------------------------------------------------------------------
int SCSICD::Inquiry(const DWORD *cdb, BYTE *buf)
{
ASSERT(cdb);
ASSERT(buf);
// EVPD check
if (cdb[1] & 0x01) {
SetStatusCode(STATUS_INVALIDCDB);
return FALSE;
return 0;
}
// Basic data
@ -563,11 +457,6 @@ int SCSICD::Inquiry(const DWORD *cdb, BYTE *buf)
return size;
}
//---------------------------------------------------------------------------
//
// READ
//
//---------------------------------------------------------------------------
int SCSICD::Read(const DWORD *cdb, BYTE *buf, uint64_t block)
{
ASSERT(buf);
@ -612,11 +501,6 @@ int SCSICD::Read(const DWORD *cdb, BYTE *buf, uint64_t block)
return Disk::Read(cdb, buf, block);
}
//---------------------------------------------------------------------------
//
// READ TOC
//
//---------------------------------------------------------------------------
int SCSICD::ReadToc(const DWORD *cdb, BYTE *buf)
{
ASSERT(cdb);
@ -769,11 +653,6 @@ void SCSICD::LBAtoMSF(DWORD lba, BYTE *msf) const
msf[3] = (BYTE)f;
}
//---------------------------------------------------------------------------
//
// Clear Track
//
//---------------------------------------------------------------------------
void SCSICD::ClearTrack()
{
// delete the track object

View File

@ -10,7 +10,7 @@
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ SCSI CD-ROM for Apple Macintosh ]
// [ SCSI CD-ROM ]
//
//---------------------------------------------------------------------------
#pragma once
@ -21,12 +21,6 @@
#include "interfaces/scsi_mmc_commands.h"
#include "interfaces/scsi_primary_commands.h"
//---------------------------------------------------------------------------
//
// Class precedence definition
//
//---------------------------------------------------------------------------
class SCSICD;
//===========================================================================
@ -36,9 +30,14 @@ class SCSICD;
//===========================================================================
class CDTrack
{
public:
private:
friend class SCSICD;
CDTrack(SCSICD *scsicd);
virtual ~CDTrack();
virtual ~CDTrack() {}
public:
void Init(int track, DWORD first, DWORD last);
@ -71,25 +70,12 @@ private:
//===========================================================================
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<ScsiDefs::scsi_command, command_t*> commands;
SASIDEV::ctrl_t *ctrl;
void AddCommand(ScsiDefs::scsi_command, const char*, void (SCSICD::*)(SASIDEV *));
public:
enum {
TrackMax = 96 // Maximum number of tracks
};
public:
SCSICD();
~SCSICD();
@ -103,6 +89,10 @@ public:
int ReadToc(const DWORD *cdb, BYTE *buf); // READ TOC command
private:
typedef Disk super;
Dispatcher<SCSICD, SASIDEV> dispatcher;
// Open
void OpenCue(const Filepath& path); // Open(CUE)
void OpenIso(const Filepath& path); // Open(ISO)

View File

@ -26,11 +26,6 @@
//
//===========================================================================
//---------------------------------------------------------------------------
//
// Constructor
//
//---------------------------------------------------------------------------
SCSIHD::SCSIHD(bool removable) : Disk(removable ? "SCRM" : "SCHD")
{
}
@ -67,11 +62,6 @@ void SCSIHD::FinalizeSetup(const Filepath &path, off_t size)
FileSupport::SetPath(path);
}
//---------------------------------------------------------------------------
//
// Reset
//
//---------------------------------------------------------------------------
void SCSIHD::Reset()
{
// Unlock and release attention
@ -83,11 +73,6 @@ void SCSIHD::Reset()
SetStatusCode(STATUS_NOERROR);
}
//---------------------------------------------------------------------------
//
// Open
//
//---------------------------------------------------------------------------
void SCSIHD::Open(const Filepath& path)
{
ASSERT(!IsReady());
@ -112,16 +97,8 @@ void SCSIHD::Open(const Filepath& path)
FinalizeSetup(path, size);
}
//---------------------------------------------------------------------------
//
// INQUIRY
//
//---------------------------------------------------------------------------
int SCSIHD::Inquiry(const DWORD *cdb, BYTE *buf)
{
ASSERT(cdb);
ASSERT(buf);
// EVPD check
if (cdb[1] & 0x01) {
SetStatusCode(STATUS_INVALIDCDB);
@ -160,11 +137,6 @@ int SCSIHD::Inquiry(const DWORD *cdb, BYTE *buf)
return size;
}
//---------------------------------------------------------------------------
//
// MODE SELECT
//
//---------------------------------------------------------------------------
bool SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length)
{
int size;

View File

@ -18,7 +18,6 @@
#include "fileio.h"
#include "exceptions.h"
#include <sstream>
//===========================================================================
//

View File

@ -44,6 +44,9 @@ public:
DiskTrack();
~DiskTrack();
private:
friend class DiskCache;
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);

View File

@ -11,7 +11,8 @@
#include "os.h"
#include "filepath.h"
#include "fileio.h"
#include "rascsi.h"
#include "config.h"
//===========================================================================
//

View File

@ -10,8 +10,8 @@
#include "os.h"
#include "filepath.h"
#include "config.h"
#include "fileio.h"
#include "rascsi.h"
//===========================================================================
//

View File

@ -16,8 +16,9 @@
#include "os.h"
#include "gpiobus.h"
#include "config.h"
#include "log.h"
#include "rascsi.h"
#ifdef __linux__
//---------------------------------------------------------------------------

View File

@ -12,7 +12,7 @@
#if !defined(gpiobus_h)
#define gpiobus_h
#include "rascsi.h"
#include "config.h"
#include "scsi.h"
//---------------------------------------------------------------------------

View File

@ -20,8 +20,37 @@ using namespace rascsi_interface;
#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
#define COMPONENT_SEPARATOR ':'
#define KEY_VALUE_SEPARATOR '='
Localizer localizer;
void protobuf_util::ParseParameters(PbDeviceDefinition& device, const string& params)
{
if (!params.empty()) {
if (params.find(KEY_VALUE_SEPARATOR) != string::npos) {
stringstream ss(params);
string p;
while (getline(ss, p, COMPONENT_SEPARATOR)) {
if (!p.empty()) {
size_t separator_pos = p.find(KEY_VALUE_SEPARATOR);
if (separator_pos != string::npos) {
AddParam(device, p.substr(0, separator_pos), p.substr(separator_pos + 1));
}
}
}
}
// Old style parameters, for backwards compatibility only.
// Only one of these parameters will be used by rascsi, depending on the device type.
else {
AddParam(device, "file", params);
if (params != "bridge" && params != "daynaport" && params != "printer" && params != "services") {
AddParam(device, "interfaces", params);
}
}
}
}
const string protobuf_util::GetParam(const PbCommand& command, const string& key)
{
auto map = command.params();

View File

@ -23,6 +23,7 @@ using namespace rascsi_interface;
namespace protobuf_util
{
void ParseParameters(PbDeviceDefinition&, const string&);
const string GetParam(const PbCommand&, const string&);
const string GetParam(const PbDeviceDefinition&, const string&);
void AddParam(PbCommand&, const string&, const string&);

View File

@ -10,7 +10,6 @@
//
//---------------------------------------------------------------------------
#include "rascsi.h"
#include "os.h"
#include "controllers/sasidev_ctrl.h"
#include "devices/device_factory.h"
@ -35,6 +34,7 @@
#include <list>
#include <vector>
#include <map>
#include "config.h"
using namespace std;
using namespace spdlog;
@ -51,6 +51,8 @@ using namespace protobuf_util;
#define UnitNum SASIDEV::UnitMax // Number of units around controller
#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
#define COMPONENT_SEPARATOR ':'
//---------------------------------------------------------------------------
//
// Variable declarations
@ -108,7 +110,7 @@ void Banner(int argc, char* argv[])
FPRT(stdout," FILE is disk image file.\n\n");
FPRT(stdout,"Usage: %s [-HDn FILE] ...\n\n", argv[0]);
FPRT(stdout," n is X68000 SASI HD number(0-15).\n");
FPRT(stdout," FILE is disk image file, \"daynaport\", \"bridge\" or \"services\".\n\n");
FPRT(stdout," FILE is disk image file, \"daynaport\", \"bridge\", \"printer\" or \"services\".\n\n");
FPRT(stdout," Image type is detected based on file extension.\n");
FPRT(stdout," hdf : SASI HD image (XM6 SASI HD image)\n");
FPRT(stdout," hds : SCSI HD image (Non-removable generic SCSI HD image)\n");
@ -521,17 +523,17 @@ string SetReservedIds(const string& ids)
reserved_ids = reserved;
if (!reserved_ids.empty()) {
ostringstream s;
string s;
bool isFirst = true;
for (auto const& reserved_id : reserved_ids) {
if (!isFirst) {
s << ", ";
s += ", ";
}
isFirst = false;
s << reserved_id;
s += to_string(reserved_id);
}
LOGINFO("Reserved ID(s) set to %s", s.str().c_str());
LOGINFO("Reserved ID(s) set to %s", s.c_str());
}
else {
LOGINFO("Cleared reserved IDs");
@ -581,15 +583,14 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
if (unit >= supported_luns) {
delete device;
ostringstream error;
error << "Invalid unit " << unit << " for device type " << PbDeviceType_Name(type);
string error = "Invalid unit " + to_string(unit) + " for device type " + PbDeviceType_Name(type);
if (supported_luns == 1) {
error << " (0)";
error += " (0)";
}
else {
error << " (0-" << (supported_luns -1) << ")";
error += " (0-" + to_string(supported_luns -1) + ")";
}
return ReturnStatus(context, false, error.str());
return ReturnStatus(context, false, error);
}
// If no filename was provided the medium is considered removed
@ -710,16 +711,15 @@ bool Attach(const CommandContext& context, const PbDeviceDefinition& pb_device,
// Re-map the controller
if (MapController(map)) {
ostringstream msg;
msg << "Attached ";
string msg = "Attached ";
if (device->IsReadOnly()) {
msg << "read-only ";
msg += "read-only ";
}
else if (device->IsProtectable() && device->IsProtected()) {
msg << "protected ";
msg += "protected ";
}
msg << device->GetType() << " device, ID " << id << ", unit " << unit;
LOGINFO("%s", msg.str().c_str());
msg += device->GetType() + " device, ID " + to_string(id) + ", unit " + to_string(unit);
LOGINFO("%s", msg.c_str());
return true;
}
@ -823,6 +823,11 @@ bool Insert(const CommandContext& context, const PbDeviceDefinition& pb_device,
device->SetProtected(pb_device.protected_());
}
Disk *disk = dynamic_cast<Disk *>(device);
if (disk) {
disk->MediumChanged();
}
return true;
}
@ -830,6 +835,8 @@ void TerminationHandler(int signum)
{
DetachAll();
Cleanup();
exit(signum);
}
@ -841,8 +848,6 @@ void TerminationHandler(int signum)
bool ProcessCmd(const CommandContext& context, const PbDeviceDefinition& pb_device, const PbCommand& command, bool dryRun)
{
ostringstream error;
const int id = pb_device.id();
const int unit = pb_device.unit();
const PbDeviceType type = pb_device.type();
@ -873,7 +878,7 @@ bool ProcessCmd(const CommandContext& context, const PbDeviceDefinition& pb_devi
bool isFirst = true;
for (const auto& param: pb_device.params()) {
if (!isFirst) {
s << ", ";
s << ":";
}
isFirst = false;
s << "'" << param.first << "=" << param.second << "'";
@ -1089,7 +1094,7 @@ bool ProcessCmd(const CommandContext& context, const PbCommand& command)
bool ProcessId(const string id_spec, PbDeviceType type, int& id, int& unit)
{
size_t separator_pos = id_spec.find(':');
size_t separator_pos = id_spec.find(COMPONENT_SEPARATOR);
if (separator_pos == string::npos) {
int max_id = type == SAHD ? 16 : 8;
@ -1324,19 +1329,13 @@ bool ParseArgument(int argc, char* argv[], int& port)
device->set_type(type);
device->set_block_size(block_size);
// Either interface or file parameters are supported
if (device_factory.GetDefaultParams(type).count("interfaces")) {
AddParam(*device, "interfaces", optarg);
}
else {
AddParam(*device, "file", optarg);
}
ParseParameters(*device, optarg);
size_t separator_pos = name.find(':');
size_t separator_pos = name.find(COMPONENT_SEPARATOR);
if (separator_pos != string::npos) {
device->set_vendor(name.substr(0, separator_pos));
name = name.substr(separator_pos + 1);
separator_pos = name.find(':');
separator_pos = name.find(COMPONENT_SEPARATOR);
if (separator_pos != string::npos) {
device->set_product(name.substr(0, separator_pos));
device->set_revision(name.substr(separator_pos + 1));
@ -1621,9 +1620,13 @@ int main(int argc, char* argv[])
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
#ifndef NDEBUG
// Get temporary operation info, in order to trigger an assertion on startup if the operation list is incomplete
PbResult pb_operation_info_result;
rascsi_response.GetOperationInfo(pb_operation_info_result, 0);
const PbOperationInfo *operation_info = rascsi_response.GetOperationInfo(pb_operation_info_result, 0);
assert(operation_info->operations_size() == PbOperation_ARRAYSIZE - 1);
delete operation_info;
#endif
int actid;
BUS::phase_t phase;
@ -1731,16 +1734,34 @@ int main(int argc, char* argv[])
pthread_mutex_lock(&ctrl_mutex);
// Notify all controllers
BYTE data = bus->GetDAT();
int initiator_id = -1;
// Notify all controllers
int i = 0;
for (auto it = controllers.begin(); it != controllers.end(); ++i, ++it) {
if (!*it || (data & (1 << i)) == 0) {
continue;
}
// Extract the SCSI initiator ID
int tmp = data - (1 << i);
if (tmp) {
initiator_id = 0;
for (int j = 0; j < 8; j++) {
tmp >>= 1;
if (tmp) {
initiator_id++;
}
else {
break;
}
}
}
// Find the target that has moved to the selection phase
if ((*it)->Process() == BUS::selection) {
if ((*it)->Process(initiator_id) == BUS::selection) {
// Get the target ID
actid = i;
@ -1768,7 +1789,7 @@ int main(int argc, char* argv[])
// Loop until the bus is free
while (running) {
// Target drive
phase = controllers[actid]->Process();
phase = controllers[actid]->Process(initiator_id);
// End when the bus is free
if (phase == BUS::busfree) {

View File

@ -30,6 +30,8 @@ enum PbDeviceType {
SCDP = 7;
// Host services device
SCHS = 8;
// Printer device
SCLP = 9;
}
// rascsi remote operations, returning PbResult
@ -37,9 +39,12 @@ enum PbOperation {
NO_OPERATION = 0;
// Attach devices and return the new device list (PbDevicesInfo)
// Parameters (mutually exclusive):
// Parameters (depending on the device type):
// "file": The filename relative to the default image folder
// "interfaces": A prioritized comma-separated list of interfaces to create a network bridge for.
// "interface": A prioritized comma-separated list of interfaces to create a network bridge for
// "inet": The IP address and netmask for the network bridge
// "cmd": The command to be used for printing, with "%f" as file placeholder
// "timeout": The timeout for printer reservations in seconds
ATTACH = 1;
// Detach a device and return the new device list (PbDevicesInfo)

View File

@ -70,7 +70,7 @@ void RascsiResponse::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_
int ordinal = 1;
while (PbDeviceType_IsValid(ordinal)) {
PbDeviceType type = UNDEFINED;
PbDeviceType_Parse(PbDeviceType_Name(ordinal), &type);
PbDeviceType_Parse(PbDeviceType_Name((PbDeviceType)ordinal), &type);
GetDeviceTypeProperties(device_types_info, type);
ordinal++;
}
@ -369,7 +369,10 @@ PbOperationInfo *RascsiResponse::GetOperationInfo(PbResult& result, int depth)
PbOperationMetaData *meta_data = new PbOperationMetaData();
AddOperationParameter(meta_data, "name", "Image file name in case of a mass storage device");
AddOperationParameter(meta_data, "interfaces", "Comma-separated prioritized network interface list");
AddOperationParameter(meta_data, "interface", "Comma-separated prioritized network interface list");
AddOperationParameter(meta_data, "inet", "IP address and netmask of the network bridge");
AddOperationParameter(meta_data, "cmd", "Print command for the printer device");
AddOperationParameter(meta_data, "timeout", "Reservation timeout for the printer device in seconds");
CreateOperation(operation_info, meta_data, ATTACH, "Attach device, device-specific parameters are required");
meta_data = new PbOperationMetaData();
@ -498,9 +501,6 @@ PbOperationInfo *RascsiResponse::GetOperationInfo(PbResult& result, int depth)
meta_data = new PbOperationMetaData();
CreateOperation(operation_info, meta_data, OPERATION_INFO, "Get operation meta data");
// Ensure that the complete set of operations is covered
assert(operation_info->operations_size() == PbOperation_ARRAYSIZE - 1);
result.set_status(true);
return operation_info;
@ -513,6 +513,7 @@ void RascsiResponse::CreateOperation(PbOperationInfo *operation_info, PbOperatio
meta_data->set_description(description);
int ordinal = PbOperation_descriptor()->FindValueByName(PbOperation_Name(operation))->index();
(*operation_info->mutable_operations())[ordinal] = *meta_data;
delete meta_data;
}
PbOperationParameter *RascsiResponse::AddOperationParameter(PbOperationMetaData *meta_data, const string& name,

View File

@ -20,7 +20,7 @@
#include <iostream>
#include <list>
// Separator for the INQUIRY name components
// Separator for the INQUIRY name components and for compound parameters
#define COMPONENT_SEPARATOR ':'
using namespace std;
@ -87,6 +87,9 @@ PbDeviceType ParseType(const char *optarg)
case 'r':
return SCRM;
case 'l':
return SCLP;
case 's':
return SCHS;
@ -380,11 +383,7 @@ int main(int argc, char* argv[])
exit(EXIT_SUCCESS);
}
if (!param.empty()) {
// Only one of these parameters will be used, depending on the device type
AddParam(*device, "interfaces", param);
AddParam(*device, "file", param);
}
ParseParameters(*device, param);
RasctlCommands rasctl_commands(command, hostname, port, token, locale);

View File

@ -14,7 +14,6 @@
#include "rasutil.h"
#include "rasctl_commands.h"
#include "rascsi_interface.pb.h"
#include <sstream>
#include <iostream>
#include <list>
@ -66,9 +65,8 @@ void RasctlCommands::SendCommand()
memcpy(&server.sin_addr.s_addr, host->h_addr, host->h_length);
if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
ostringstream error;
error << "Can't connect to rascsi process on host '" << hostname << "', port " << port;
throw io_exception(error.str());
throw io_exception("Can't connect to rascsi process on host '" + hostname + "', port "
+ to_string(port));
}
if (write(fd, "RASCSI", 6) != 6) {

View File

@ -76,10 +76,12 @@ void RasctlDisplay::DisplayDeviceInfo(const PbDevice& pb_device)
cout << " ";
}
// Creates a sorted map
map<string, string> params = { pb_device.params().begin(), pb_device.params().end() };
bool isFirst = true;
for (const auto& param : pb_device.params()) {
for (const auto& param : params) {
if (!isFirst) {
cout << " ";
cout << ":";
}
isFirst = false;
cout << param.first << "=" << param.second;
@ -171,13 +173,12 @@ void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types
bool isFirst = true;
for (const auto& param : params) {
if (!isFirst) {
cout << ", ";
cout << " ";
}
cout << param.first << "=" << param.second;
cout << param.first << "=" << param.second << endl;
isFirst = false;
}
cout << endl;
}
if (properties.block_sizes_size()) {
@ -302,7 +303,10 @@ void RasctlDisplay::DisplayOperationInfo(const PbOperationInfo& operation_info)
}
cout << endl;
for (const auto& parameter : operation.second.parameters()) {
list<PbOperationParameter> sorted_parameters = { operation.second.parameters().begin(), operation.second.parameters().end() };
sorted_parameters.sort([](const auto& a, const auto& b) { return a.name() < b.name(); });
for (const auto& parameter : sorted_parameters) {
cout << " " << parameter.name() << ": "
<< (parameter.is_mandatory() ? "mandatory" : "optional");
if (!parameter.description().empty()) {

View File

@ -63,6 +63,10 @@ string ras_util::ListDevices(const list<PbDevice>& pb_devices)
filename = "Host Services";
break;
case SCLP:
filename = "SCSI Printer";
break;
default:
filename = device.file().name();
break;

View File

@ -23,7 +23,6 @@
#include "fileio.h"
#include "filepath.h"
#include "gpiobus.h"
#include "rascsi.h"
#include "rascsi_version.h"
#define BUFSIZE 1024 * 64 // Maybe around 64KB?

View File

@ -14,13 +14,25 @@
//===========================================================================
//
// Sense Keys and Additional Sense Codes
// Status byte codes, Sense Keys and Additional Sense Codes
// (See https://www.t10.org/lists/1spc-lst.htm)
//
//===========================================================================
class ERROR_CODES
{
public:
enum status : int {
GOOD = 0x00,
CHECK_CONDITION = 0x02,
CONDITION_MET = 0x04,
BUSY = 0x08,
INTERMEDIATE = 0x10,
INTERMEDIATE_CONDITION_MET = 0x14,
RESERVATION_CONFLICT = 0x18,
COMMAND_TERMINATED = 0x22,
QUEUE_FULL = 0x28
};
enum sense_key : int {
NO_SENSE = 0x00,
RECOVERED_ERROR = 0x01,
@ -46,6 +58,7 @@ public:
INVALID_FIELD_IN_CDB = 0x24,
INVALID_LUN = 0x25,
WRITE_PROTECTED = 0x27,
NOT_READY_TO_READY_CHANGE = 0x28,
MEDIUM_NOT_PRESENT = 0x3a
};
};
@ -154,8 +167,7 @@ private:
static const char* phase_str_table[];
};
class ScsiDefs {
public:
namespace scsi_defs {
enum scsi_command : int {
eCmdTestUnitReady = 0x00,
eCmdRezero = 0x01,
@ -173,6 +185,7 @@ public:
eCmdSetMcastAddr = 0x0D,
// DaynaPort specific command
eCmdEnableInterface = 0x0E,
eCmdSynchronizeBuffer = 0x10,
eCmdInquiry = 0x12,
eCmdModeSelect6 = 0x15,
eCmdReserve6 = 0x16,