diff --git a/doc/rascsi.1 b/doc/rascsi.1 index 32f60a29..8c63bc13 100644 --- a/doc/rascsi.1 +++ b/doc/rascsi.1 @@ -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). diff --git a/doc/rascsi_man_page.txt b/doc/rascsi_man_page.txt index 4ff5553e..1f6d8518 100644 --- a/doc/rascsi_man_page.txt +++ b/doc/rascsi_man_page.txt @@ -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‐ diff --git a/doc/rasctl.1 b/doc/rasctl.1 index 1159ee1d..61b7a153 100644 --- a/doc/rasctl.1 +++ b/doc/rasctl.1 @@ -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 diff --git a/doc/rasctl_man_page.txt b/doc/rasctl_man_page.txt index 03413ee6..ad1f4da7 100644 --- a/doc/rasctl_man_page.txt +++ b/doc/rasctl_man_page.txt @@ -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 diff --git a/easyinstall.sh b/easyinstall.sh index 95d398e8..6bfa87ca 100755 --- a/easyinstall.sh +++ b/easyinstall.sh @@ -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 diff --git a/python/common/src/rascsi/common_settings.py b/python/common/src/rascsi/common_settings.py index 0bd83a84..8e0d80e5 100644 --- a/python/common/src/rascsi/common_settings.py +++ b/python/common/src/rascsi/common_settings.py @@ -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" diff --git a/python/common/src/rascsi/file_cmds.py b/python/common/src/rascsi/file_cmds.py index 009edfff..b3e493ec 100644 --- a/python/common/src/rascsi/file_cmds.py +++ b/python/common/src/rascsi/file_cmds.py @@ -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} diff --git a/python/common/src/rascsi/ractl_cmds.py b/python/common/src/rascsi/ractl_cmds.py index 2bba4030..3e0576fe 100644 --- a/python/common/src/rascsi/ractl_cmds.py +++ b/python/common/src/rascsi/ractl_cmds.py @@ -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) diff --git a/python/oled/src/rascsi_oled_monitor.py b/python/oled/src/rascsi_oled_monitor.py index 90ba7d29..343bd396 100755 --- a/python/oled/src/rascsi_oled_monitor.py +++ b/python/oled/src/rascsi_oled_monitor.py @@ -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']} " diff --git a/python/web/src/device_utils.py b/python/web/src/device_utils.py index e5abdff7..0d1d6cf8 100644 --- a/python/web/src/device_utils.py +++ b/python/web/src/device_utils.py @@ -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 diff --git a/python/web/src/templates/drives.html b/python/web/src/templates/drives.html index 24656035..accaee99 100644 --- a/python/web/src/templates/drives.html +++ b/python/web/src/templates/drives.html @@ -15,7 +15,7 @@ {{ _("Ref.") }} {{ _("Action") }} -{% for hd in hd_conf %} +{% for hd in hd_conf|sort(attribute='name') %} {{ hd.name }} {{ hd.size_mb }} @@ -59,7 +59,7 @@ {{ _("Ref.") }} {{ _("Action") }} -{% for cd in cd_conf %} +{% for cd in cd_conf|sort(attribute='name') %} {{ cd.name }} {{ cd.size_mb }} @@ -79,9 +79,9 @@ @@ -105,7 +105,7 @@ {{ _("Ref.") }} {{ _("Action") }} -{% for rm in rm_conf %} +{% for rm in rm_conf|sort(attribute='name') %} {{ rm.name }} {{ rm.size_mb }} diff --git a/python/web/src/templates/index.html b/python/web/src/templates/index.html index 667f230a..43d0a328 100644 --- a/python/web/src/templates/index.html +++ b/python/web/src/templates/index.html @@ -15,7 +15,7 @@

- +
{% endif %} @@ -155,18 +155,18 @@
  • {{ _("Manage image files in the active RaSCSI image directory: %(directory)s with a scan depth of %(scan_depth)s.", directory=base_dir, scan_depth=scan_depth) }}
  • {{ _("Select a valid SCSI ID and LUN to attach to. Unless you know what you're doing, always use LUN 0.", url="https://en.wikipedia.org/wiki/Logical_unit_number") }}
  • -
  • {{ _("If RaSCSI was unable to detect the device type associated with the image, you can choose the type from the dropdown.") }}
  • +
  • {{ _("If RaSCSI was unable to detect the media type associated with the image, you get to choose the type from the dropdown.") }}
  • - - - - + + + + - {% for file in files %} + {% for file in files|sort(attribute='name') %} {% if file["prop"] %}
    {{ _("File") }}{{ _("Size") }}{{ _("Actions") }}
    {{ _("File") }}{{ _("Size") }}{{ _("Parameters and Actions") }}
    @@ -264,12 +264,12 @@ {% else %} - -
    -
    - - + + + + + {% for type in PERIPHERAL_DEVICE_TYPES %} + + + + {% endfor %}
    {{ _("Peripheral") }}{{ _("Parameters and Actions") }}
    +
    {{ device_types[type]["name"] }}
    +
    + + + {% for key, value in device_types[type]["params"].items() %} + + {% if value.isnumeric() %} + + {% elif key == "interface" %} + - - - - - + {% else %} + + {% endif %} + {% endfor %}
    {% if macproxy_configured %}

    {{ _("Macproxy is running at %(ip_addr)s (default port 5000)", ip_addr=ip_addr) }}

    diff --git a/python/web/src/web.py b/python/web/src/web.py index aa16c820..59a89862 100644 --- a/python/web/src/web.py +++ b/python/web/src/web.py @@ -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)) diff --git a/src/raspberrypi/rascsi.h b/src/raspberrypi/config.h similarity index 100% rename from src/raspberrypi/rascsi.h rename to src/raspberrypi/config.h diff --git a/src/raspberrypi/controllers/sasidev_ctrl.cpp b/src/raspberrypi/controllers/sasidev_ctrl.cpp index 6c40ae02..5fa72f51 100644 --- a/src/raspberrypi/controllers/sasidev_ctrl.cpp +++ b/src/raspberrypi/controllers/sasidev_ctrl.cpp @@ -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; } diff --git a/src/raspberrypi/controllers/sasidev_ctrl.h b/src/raspberrypi/controllers/sasidev_ctrl.h index 9e790270..136dc14f 100644 --- a/src/raspberrypi/controllers/sasidev_ctrl.h +++ b/src/raspberrypi/controllers/sasidev_ctrl.h @@ -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 diff --git a/src/raspberrypi/controllers/scsidev_ctrl.cpp b/src/raspberrypi/controllers/scsidev_ctrl.cpp index f85d9f3d..b8699d55 100644 --- a/src/raspberrypi/controllers/scsidev_ctrl.cpp +++ b/src/raspberrypi/controllers/scsidev_ctrl.cpp @@ -17,7 +17,7 @@ #include "controllers/scsidev_ctrl.h" #include "gpiobus.h" #include "devices/scsi_daynaport.h" -#include +#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(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; +} + diff --git a/src/raspberrypi/controllers/scsidev_ctrl.h b/src/raspberrypi/controllers/scsidev_ctrl.h index bb3c7506..9a3621cd 100644 --- a/src/raspberrypi/controllers/scsidev_ctrl.h +++ b/src/raspberrypi/controllers/scsidev_ctrl.h @@ -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; }; diff --git a/src/raspberrypi/devices/ctapdriver.cpp b/src/raspberrypi/devices/ctapdriver.cpp index 06c0664d..0b9de36e 100644 --- a/src/raspberrypi/devices/ctapdriver.cpp +++ b/src/raspberrypi/devices/ctapdriver.cpp @@ -20,25 +20,22 @@ #include #include #endif +// TODO Try to get rid of zlib, there is only one operation using it #include // For crc32() #include "os.h" #include "ctapdriver.h" #include "log.h" +#include "rasutil.h" #include "exceptions.h" #include +#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& const_params) { + map 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&) { 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; } } diff --git a/src/raspberrypi/devices/ctapdriver.h b/src/raspberrypi/devices/ctapdriver.h index 1d4f4c6f..0d55dc1f 100644 --- a/src/raspberrypi/devices/ctapdriver.h +++ b/src/raspberrypi/devices/ctapdriver.h @@ -17,6 +17,7 @@ #include #include "filepath.h" +#include #include #include @@ -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&); + +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 interfaces; + vector interfaces; + + string inet; }; diff --git a/src/raspberrypi/devices/device.cpp b/src/raspberrypi/devices/device.cpp index 31e75fa2..83658f4a 100644 --- a/src/raspberrypi/devices/device.cpp +++ b/src/raspberrypi/devices/device.cpp @@ -8,17 +8,20 @@ //--------------------------------------------------------------------------- #include -#include #include "rascsi_version.h" #include "os.h" #include "log.h" #include "exceptions.h" #include "device.h" +set 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& params) +{ + this->params = GetDefaultParams(); + + for (const auto& param : params) { + this->params[param.first] = param.second; + } +} + void Device::SetStatusCode(int status_code) { if (status_code) { diff --git a/src/raspberrypi/devices/device.h b/src/raspberrypi/devices/device.h index 621ac06e..aeb94d9d 100644 --- a/src/raspberrypi/devices/device.h +++ b/src/raspberrypi/devices/device.h @@ -9,6 +9,7 @@ #pragma once +#include #include #include @@ -104,10 +105,14 @@ private: // Sense Key, ASC and ASCQ int status_code; +protected: + + static set 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&) { return true; }; @@ -166,7 +171,7 @@ public: void SupportsParams(bool supports_paams) { this->supports_params = supports_paams; } const map GetParams() const { return params; } const string GetParam(const string&); - void SetParams(const map& params) { this->params = params; } + void SetParams(const map&); const map GetDefaultParams() const { return default_params; } void SetDefaultParams(const map& default_params) { this->default_params = default_params; } diff --git a/src/raspberrypi/devices/device_factory.cpp b/src/raspberrypi/devices/device_factory.cpp index fb98e06c..86da4c23 100644 --- a/src/raspberrypi/devices/device_factory.cpp +++ b/src/raspberrypi/devices/device_factory.cpp @@ -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; } diff --git a/src/raspberrypi/devices/disk.cpp b/src/raspberrypi/devices/disk.cpp index 519c1693..80d2d027 100644 --- a/src/raspberrypi/devices/disk.cpp +++ b/src/raspberrypi/devices/disk.cpp @@ -20,49 +20,48 @@ #include "exceptions.h" #include "disk.h" #include "mode_page_device.h" -#include -#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(ctrl->cmd[0]))) { - command_t *command = commands[static_cast(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 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) { diff --git a/src/raspberrypi/devices/disk.h b/src/raspberrypi/devices/disk.h index 0c902582..1a3c9687 100644 --- a/src/raspberrypi/devices/disk.h +++ b/src/raspberrypi/devices/disk.h @@ -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 #include #include +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 commands; - - void AddCommand(ScsiDefs::scsi_command, const char*, void (Disk::*)(SASIDEV *)); + Dispatcher 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 disks; - private: void Read(SASIDEV *, uint64_t); void Write(SASIDEV *, uint64_t); diff --git a/src/raspberrypi/devices/dispatcher.h b/src/raspberrypi/devices/dispatcher.h new file mode 100644 index 00000000..6a2a46c4 --- /dev/null +++ b/src/raspberrypi/devices/dispatcher.h @@ -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 + +class SASIDEV; +class SCSIDEV; + +using namespace std; +using namespace scsi_defs; + +template +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 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(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; + } +}; diff --git a/src/raspberrypi/devices/host_services.cpp b/src/raspberrypi/devices/host_services.cpp index f4e3b6b6..6a141399 100644 --- a/src/raspberrypi/devices/host_services.cpp +++ b/src/raspberrypi/devices/host_services.cpp @@ -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(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; } - diff --git a/src/raspberrypi/devices/host_services.h b/src/raspberrypi/devices/host_services.h index 5c6e8e4a..23276189 100644 --- a/src/raspberrypi/devices/host_services.h +++ b/src/raspberrypi/devices/host_services.h @@ -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 dispatcher; + + int AddRealtimeClockPage(int, BYTE *); }; diff --git a/src/raspberrypi/devices/interfaces/scsi_block_commands.h b/src/raspberrypi/devices/interfaces/scsi_block_commands.h index 3a8f08de..584f6af8 100644 --- a/src/raspberrypi/devices/interfaces/scsi_block_commands.h +++ b/src/raspberrypi/devices/interfaces/scsi_block_commands.h @@ -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; }; diff --git a/src/raspberrypi/devices/interfaces/scsi_primary_commands.h b/src/raspberrypi/devices/interfaces/scsi_primary_commands.h index 5a3efdb1..585c90e5 100644 --- a/src/raspberrypi/devices/interfaces/scsi_primary_commands.h +++ b/src/raspberrypi/devices/interfaces/scsi_primary_commands.h @@ -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; }; diff --git a/src/raspberrypi/devices/interfaces/scsi_printer_commands.h b/src/raspberrypi/devices/interfaces/scsi_printer_commands.h new file mode 100644 index 00000000..c4ad78b4 --- /dev/null +++ b/src/raspberrypi/devices/interfaces/scsi_printer_commands.h @@ -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; +}; diff --git a/src/raspberrypi/devices/mode_page_device.cpp b/src/raspberrypi/devices/mode_page_device.cpp index 3f05caa9..7ebce681 100644 --- a/src/raspberrypi/devices/mode_page_device.cpp +++ b/src/raspberrypi/devices/mode_page_device.cpp @@ -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(ctrl->cmd[0]))) { - command_t *command = commands[static_cast(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; diff --git a/src/raspberrypi/devices/mode_page_device.h b/src/raspberrypi/devices/mode_page_device.h index 2c204e51..3b50c236 100644 --- a/src/raspberrypi/devices/mode_page_device.h +++ b/src/raspberrypi/devices/mode_page_device.h @@ -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 commands; + Dispatcher 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 *); diff --git a/src/raspberrypi/devices/primary_device.cpp b/src/raspberrypi/devices/primary_device.cpp index 75728e32..9e3b3122 100644 --- a/src/raspberrypi/devices/primary_device.cpp +++ b/src/raspberrypi/devices/primary_device.cpp @@ -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(ctrl->cmd[0]))) { - command_t *command = commands[static_cast(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; +} diff --git a/src/raspberrypi/devices/primary_device.h b/src/raspberrypi/devices/primary_device.h index 6949e17b..c986bc19 100644 --- a/src/raspberrypi/devices/primary_device.h +++ b/src/raspberrypi/devices/primary_device.h @@ -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 -#include 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 commands; - - void AddCommand(ScsiDefs::scsi_command, const char*, void (PrimaryDevice::*)(SASIDEV *)); + Dispatcher dispatcher; void Inquiry(SASIDEV *); void ReportLuns(SASIDEV *); diff --git a/src/raspberrypi/devices/sasihd.cpp b/src/raspberrypi/devices/sasihd.cpp index ec901478..be8293d2 100644 --- a/src/raspberrypi/devices/sasihd.cpp +++ b/src/raspberrypi/devices/sasihd.cpp @@ -13,11 +13,11 @@ // [ SASI hard disk ] // //--------------------------------------------------------------------------- + #include "sasihd.h" #include "fileio.h" #include "exceptions.h" -#include -#include "../rascsi.h" +#include "../config.h" //=========================================================================== // diff --git a/src/raspberrypi/devices/scsi_daynaport.cpp b/src/raspberrypi/devices/scsi_daynaport.cpp index 5c55f678..f70b6e7f 100644 --- a/src/raspberrypi/devices/scsi_daynaport.cpp +++ b/src/raspberrypi/devices/scsi_daynaport.cpp @@ -28,23 +28,25 @@ //--------------------------------------------------------------------------- #include "scsi_daynaport.h" -#include + +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(ctrl->cmd[0]))) { - command_t *command = commands[static_cast(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& 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. diff --git a/src/raspberrypi/devices/scsi_daynaport.h b/src/raspberrypi/devices/scsi_daynaport.h index 1a08b4f2..1dbbef60 100644 --- a/src/raspberrypi/devices/scsi_daynaport.h +++ b/src/raspberrypi/devices/scsi_daynaport.h @@ -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 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 dispatcher; + typedef struct __attribute__((packed)) { BYTE operation_code; BYTE misc_cdb_information; diff --git a/src/raspberrypi/devices/scsi_host_bridge.cpp b/src/raspberrypi/devices/scsi_host_bridge.cpp index 73d6cd4d..d9be4579 100644 --- a/src/raspberrypi/devices/scsi_host_bridge.cpp +++ b/src/raspberrypi/devices/scsi_host_bridge.cpp @@ -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 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& 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& 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(ctrl->cmd[0]))) { - command_t *command = commands[static_cast(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();} //--------------------------------------------------------------------------- diff --git a/src/raspberrypi/devices/scsi_host_bridge.h b/src/raspberrypi/devices/scsi_host_bridge.h index 1ecee189..e7e304e7 100644 --- a/src/raspberrypi/devices/scsi_host_bridge.h +++ b/src/raspberrypi/devices/scsi_host_bridge.h @@ -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 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 dispatcher; + int GetMacAddr(BYTE *buf); // Get MAC address void SetMacAddr(BYTE *buf); // Set MAC address void ReceivePacket(); // Receive a packet diff --git a/src/raspberrypi/devices/scsi_printer.cpp b/src/raspberrypi/devices/scsi_printer.cpp new file mode 100644 index 00000000..066eb4ac --- /dev/null +++ b/src/raspberrypi/devices/scsi_printer.cpp @@ -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 +#include "controllers/scsidev_ctrl.h" +#include "../rasutil.h" +#include "scsi_printer.h" +#include + +#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& 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); + } +} diff --git a/src/raspberrypi/devices/scsi_printer.h b/src/raspberrypi/devices/scsi_printer.h new file mode 100644 index 00000000..1abc441f --- /dev/null +++ b/src/raspberrypi/devices/scsi_printer.h @@ -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 +#include + +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&); + + 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 dispatcher; + + char filename[sizeof(TMP_FILE_PATTERN) + 1]; + int fd; + + int reserving_initiator; + + time_t reservation_time; + int timeout; +}; diff --git a/src/raspberrypi/devices/scsicd.cpp b/src/raspberrypi/devices/scsicd.cpp index 1ee33df2..255160e6 100644 --- a/src/raspberrypi/devices/scsicd.cpp +++ b/src/raspberrypi/devices/scsicd.cpp @@ -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 -#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(ctrl->cmd[0]))) { - command_t *command = commands[static_cast(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 diff --git a/src/raspberrypi/devices/scsicd.h b/src/raspberrypi/devices/scsicd.h index 2e860467..42150b5c 100644 --- a/src/raspberrypi/devices/scsicd.h +++ b/src/raspberrypi/devices/scsicd.h @@ -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 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 dispatcher; + // Open void OpenCue(const Filepath& path); // Open(CUE) void OpenIso(const Filepath& path); // Open(ISO) diff --git a/src/raspberrypi/devices/scsihd.cpp b/src/raspberrypi/devices/scsihd.cpp index 7b509094..4451c0dc 100644 --- a/src/raspberrypi/devices/scsihd.cpp +++ b/src/raspberrypi/devices/scsihd.cpp @@ -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; diff --git a/src/raspberrypi/devices/scsimo.cpp b/src/raspberrypi/devices/scsimo.cpp index 83496ed6..75d1bc2c 100644 --- a/src/raspberrypi/devices/scsimo.cpp +++ b/src/raspberrypi/devices/scsimo.cpp @@ -18,7 +18,6 @@ #include "fileio.h" #include "exceptions.h" -#include //=========================================================================== // diff --git a/src/raspberrypi/file_access/disk_track_cache.h b/src/raspberrypi/file_access/disk_track_cache.h index a270f31c..7490064d 100644 --- a/src/raspberrypi/file_access/disk_track_cache.h +++ b/src/raspberrypi/file_access/disk_track_cache.h @@ -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); diff --git a/src/raspberrypi/fileio.cpp b/src/raspberrypi/fileio.cpp index 066abddd..c6dc70b2 100644 --- a/src/raspberrypi/fileio.cpp +++ b/src/raspberrypi/fileio.cpp @@ -11,7 +11,8 @@ #include "os.h" #include "filepath.h" #include "fileio.h" -#include "rascsi.h" + +#include "config.h" //=========================================================================== // diff --git a/src/raspberrypi/filepath.cpp b/src/raspberrypi/filepath.cpp index 65555218..1088bcd7 100644 --- a/src/raspberrypi/filepath.cpp +++ b/src/raspberrypi/filepath.cpp @@ -10,8 +10,8 @@ #include "os.h" #include "filepath.h" +#include "config.h" #include "fileio.h" -#include "rascsi.h" //=========================================================================== // diff --git a/src/raspberrypi/gpiobus.cpp b/src/raspberrypi/gpiobus.cpp index f09372af..e1797bb0 100644 --- a/src/raspberrypi/gpiobus.cpp +++ b/src/raspberrypi/gpiobus.cpp @@ -16,8 +16,9 @@ #include "os.h" #include "gpiobus.h" + +#include "config.h" #include "log.h" -#include "rascsi.h" #ifdef __linux__ //--------------------------------------------------------------------------- diff --git a/src/raspberrypi/gpiobus.h b/src/raspberrypi/gpiobus.h index 46babdce..78b01b54 100644 --- a/src/raspberrypi/gpiobus.h +++ b/src/raspberrypi/gpiobus.h @@ -12,7 +12,7 @@ #if !defined(gpiobus_h) #define gpiobus_h -#include "rascsi.h" +#include "config.h" #include "scsi.h" //--------------------------------------------------------------------------- diff --git a/src/raspberrypi/protobuf_util.cpp b/src/raspberrypi/protobuf_util.cpp index bc9239d1..90c71d07 100644 --- a/src/raspberrypi/protobuf_util.cpp +++ b/src/raspberrypi/protobuf_util.cpp @@ -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(); diff --git a/src/raspberrypi/protobuf_util.h b/src/raspberrypi/protobuf_util.h index 7390bbdc..e07efd66 100644 --- a/src/raspberrypi/protobuf_util.h +++ b/src/raspberrypi/protobuf_util.h @@ -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&); diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index b98d69d9..65698978 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -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 #include #include +#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(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) { diff --git a/src/raspberrypi/rascsi_interface.proto b/src/raspberrypi/rascsi_interface.proto index 8268e3b9..5207b7a2 100644 --- a/src/raspberrypi/rascsi_interface.proto +++ b/src/raspberrypi/rascsi_interface.proto @@ -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) diff --git a/src/raspberrypi/rascsi_response.cpp b/src/raspberrypi/rascsi_response.cpp index a9ff6883..8cda4412 100644 --- a/src/raspberrypi/rascsi_response.cpp +++ b/src/raspberrypi/rascsi_response.cpp @@ -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, diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp index 545866d3..09e746c1 100644 --- a/src/raspberrypi/rasctl.cpp +++ b/src/raspberrypi/rasctl.cpp @@ -20,7 +20,7 @@ #include #include -// 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); diff --git a/src/raspberrypi/rasctl_commands.cpp b/src/raspberrypi/rasctl_commands.cpp index af4e21e3..f192432d 100644 --- a/src/raspberrypi/rasctl_commands.cpp +++ b/src/raspberrypi/rasctl_commands.cpp @@ -14,7 +14,6 @@ #include "rasutil.h" #include "rasctl_commands.h" #include "rascsi_interface.pb.h" -#include #include #include @@ -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) { diff --git a/src/raspberrypi/rasctl_display.cpp b/src/raspberrypi/rasctl_display.cpp index f17c0301..92172589 100644 --- a/src/raspberrypi/rasctl_display.cpp +++ b/src/raspberrypi/rasctl_display.cpp @@ -76,10 +76,12 @@ void RasctlDisplay::DisplayDeviceInfo(const PbDevice& pb_device) cout << " "; } + // Creates a sorted map + map 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 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()) { diff --git a/src/raspberrypi/rasutil.cpp b/src/raspberrypi/rasutil.cpp index d5a6bfe7..b7551d77 100644 --- a/src/raspberrypi/rasutil.cpp +++ b/src/raspberrypi/rasutil.cpp @@ -63,6 +63,10 @@ string ras_util::ListDevices(const list& pb_devices) filename = "Host Services"; break; + case SCLP: + filename = "SCSI Printer"; + break; + default: filename = device.file().name(); break; diff --git a/src/raspberrypi/sasidump.cpp b/src/raspberrypi/sasidump.cpp index c0d3ac0f..92d6dc48 100644 --- a/src/raspberrypi/sasidump.cpp +++ b/src/raspberrypi/sasidump.cpp @@ -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? diff --git a/src/raspberrypi/scsi.h b/src/raspberrypi/scsi.h index 98897efd..b648390c 100644 --- a/src/raspberrypi/scsi.h +++ b/src/raspberrypi/scsi.h @@ -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,