From 45cd5e58d1eb46a1839782f10752a438214151b3 Mon Sep 17 00:00:00 2001 From: Uwe Seimet <48174652+uweseimet@users.noreply.github.com> Date: Sun, 13 Feb 2022 20:30:02 +0100 Subject: [PATCH 01/14] Inheritance hierarchy improvements, reduced dependencies to Disk class (#662) * Fixed buster compile-time issue * Host services inherit from ModePageDevice * Call base class * Visibility update * Updated includes * Updated dispatcher * Added TODOs * Logging update * Code cleanup * Use namespace instead of class for ScsiDefs * Renaming * Cleanup * Use dispatcher template in order to remove duplicate code * Updated all dispatchers * Clean up commands * Removed duplicate code * Removed duplicate code * Updated template definition * Fixed typo * Fixed warning * Code cleanup * Device list must be static * Cleanup * Logging update * Added comments * Cleanup * Base class update * SCSIBR is not a subclass of Disk anymore, but of PrimaryDevice * Updated includes * Fixed compile-time issue on the Pi * Header file cleanup * Interface cleanup * Removed wrong override * include file cleanup * Removed obsolete usage of streams * Removed more stream usages * Stream usage cleanup * Include cleanup * Renaming * Include cleanup * Interface update --- src/raspberrypi/{rascsi.h => config.h} | 0 src/raspberrypi/controllers/sasidev_ctrl.h | 2 +- src/raspberrypi/controllers/scsidev_ctrl.cpp | 24 ++- src/raspberrypi/devices/device.cpp | 10 +- src/raspberrypi/devices/device.h | 7 +- src/raspberrypi/devices/disk.cpp | 107 +++++-------- src/raspberrypi/devices/disk.h | 40 ++--- src/raspberrypi/devices/dispatcher.h | 67 ++++++++ src/raspberrypi/devices/host_services.cpp | 143 +++++++++++++----- src/raspberrypi/devices/host_services.h | 28 +++- .../devices/interfaces/scsi_block_commands.h | 13 -- .../interfaces/scsi_primary_commands.h | 6 +- src/raspberrypi/devices/mode_page_device.cpp | 32 +--- src/raspberrypi/devices/mode_page_device.h | 20 +-- src/raspberrypi/devices/primary_device.cpp | 61 +++++--- src/raspberrypi/devices/primary_device.h | 22 ++- src/raspberrypi/devices/sasihd.cpp | 4 +- src/raspberrypi/devices/scsi_daynaport.cpp | 79 ++-------- src/raspberrypi/devices/scsi_daynaport.h | 17 +-- src/raspberrypi/devices/scsi_host_bridge.cpp | 53 ++----- src/raspberrypi/devices/scsi_host_bridge.h | 21 +-- src/raspberrypi/devices/scsicd.cpp | 46 ++---- src/raspberrypi/devices/scsicd.h | 17 +-- src/raspberrypi/devices/scsihd.cpp | 3 - src/raspberrypi/devices/scsimo.cpp | 1 - src/raspberrypi/fileio.cpp | 3 +- src/raspberrypi/filepath.cpp | 2 +- src/raspberrypi/gpiobus.cpp | 3 +- src/raspberrypi/gpiobus.h | 2 +- src/raspberrypi/rascsi.cpp | 32 ++-- src/raspberrypi/rascsi_response.cpp | 2 +- src/raspberrypi/rasctl_commands.cpp | 6 +- src/raspberrypi/sasidump.cpp | 1 - src/raspberrypi/scsi.h | 3 +- 34 files changed, 405 insertions(+), 472 deletions(-) rename src/raspberrypi/{rascsi.h => config.h} (100%) create mode 100644 src/raspberrypi/devices/dispatcher.h 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.h b/src/raspberrypi/controllers/sasidev_ctrl.h index 9e790270..719bfc73 100644 --- a/src/raspberrypi/controllers/sasidev_ctrl.h +++ b/src/raspberrypi/controllers/sasidev_ctrl.h @@ -15,7 +15,7 @@ //--------------------------------------------------------------------------- #pragma once -#include "../rascsi.h" +#include "../config.h" #include "os.h" #include "scsi.h" #include "fileio.h" diff --git a/src/raspberrypi/controllers/scsidev_ctrl.cpp b/src/raspberrypi/controllers/scsidev_ctrl.cpp index f85d9f3d..8fb8952e 100644 --- a/src/raspberrypi/controllers/scsidev_ctrl.cpp +++ b/src/raspberrypi/controllers/scsidev_ctrl.cpp @@ -17,7 +17,6 @@ #include "controllers/scsidev_ctrl.h" #include "gpiobus.h" #include "devices/scsi_daynaport.h" -#include //=========================================================================== // @@ -239,7 +238,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 +246,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 +265,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 +276,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()); @@ -381,9 +380,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 +418,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 +430,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; diff --git a/src/raspberrypi/devices/device.cpp b/src/raspberrypi/devices/device.cpp index 31e75fa2..8e194967 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; diff --git a/src/raspberrypi/devices/device.h b/src/raspberrypi/devices/device.h index 621ac06e..3978eec0 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; }; diff --git a/src/raspberrypi/devices/disk.cpp b/src/raspberrypi/devices/disk.cpp index 44888408..0585ce44 100644 --- a/src/raspberrypi/devices/disk.cpp +++ b/src/raspberrypi/devices/disk.cpp @@ -20,12 +20,11 @@ #include "exceptions.h" #include "disk.h" #include "mode_page_device.h" -#include -Disk::Disk(const std::string id) : ModePageDevice(id), ScsiBlockCommands() +using namespace scsi_defs; + +Disk::Disk(const string& id) : ModePageDevice(id), ScsiBlockCommands() { - disks.insert(this); - // Work initialization configured_sector_size = 0; disk.size = 0; @@ -33,34 +32,34 @@ Disk::Disk(const std::string id) : ModePageDevice(id), ScsiBlockCommands() disk.dcache = NULL; disk.image_offset = 0; - 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() @@ -78,37 +77,12 @@ 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(); - - 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 ModePageDevice::Dispatch(controller); + // The superclass handles the less specific commands + return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller); } //--------------------------------------------------------------------------- @@ -1276,9 +1250,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; } @@ -1339,10 +1312,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; } @@ -1366,9 +1337,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 68b06dcd..59bdb740 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: @@ -52,18 +53,10 @@ private: off_t image_offset; // Offset to actual data } disk_t; - typedef struct _command_t { - const char* name; - void (Disk::*execute)(SASIDEV *); - - _command_t(const char* _name, void (Disk::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { }; - } command_t; - std::map 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; @@ -76,13 +69,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 +84,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 +104,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 @@ -154,8 +148,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..6cd0cc47 --- /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)(SASIDEV *); + + _command_t(const char* _name, void (T::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { }; + } command_t; + map commands; + + void AddCommand(scsi_command opcode, const char* name, void (T::*execute)(SASIDEV *)) + { + commands[opcode] = new command_t(name, execute); + } + + bool Dispatch(T *instance, SCSIDEV *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..b3b11f25 100644 --- a/src/raspberrypi/devices/host_services.cpp +++ b/src/raspberrypi/devices/host_services.cpp @@ -30,24 +30,20 @@ // b) !start && load (EJECT): Shut down the Raspberry Pi // +#include "controllers/scsidev_ctrl.h" #include "host_services.h" +using namespace scsi_defs; + +HostServices::HostServices() : ModePageDevice("SCHS") +{ + 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) @@ -58,28 +54,8 @@ 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, not removable + return PrimaryDevice::Inquiry(3, false, cdb, buf); } void HostServices::StartStopUnit(SASIDEV *controller) @@ -88,10 +64,10 @@ void HostServices::StartStopUnit(SASIDEV *controller) 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; + // Delete all other devices. This will also flush any caches. + for (const Device *device : devices) { + if (device != this) { + delete device; } } @@ -109,7 +85,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 +191,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..7d7aa944 100644 --- a/src/raspberrypi/devices/host_services.h +++ b/src/raspberrypi/devices/host_services.h @@ -5,21 +5,37 @@ // // Copyright (C) 2022 Uwe Seimet // +// RaSCSI 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 StartStopUnit(SASIDEV *); + + 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/mode_page_device.cpp b/src/raspberrypi/devices/mode_page_device.cpp index 3f05caa9..9b9e08bc 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) diff --git a/src/raspberrypi/devices/mode_page_device.h b/src/raspberrypi/devices/mode_page_device.h index 2c204e51..b8200aa2 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..3f097bc8 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) @@ -90,7 +74,7 @@ void PrimaryDevice::Inquiry(SASIDEV *controller) // 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()); ctrl->buffer[0] |= 0x7f; } @@ -183,6 +167,33 @@ bool PrimaryDevice::CheckReady() return true; } +int PrimaryDevice::Inquiry(int type, 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] = 0x01; + 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); diff --git a/src/raspberrypi/devices/primary_device.h b/src/raspberrypi/devices/primary_device.h index 6949e17b..1068afdc 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,29 @@ 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 *); protected: + int Inquiry(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..a789fc00 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,35 +56,12 @@ 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) @@ -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, not removable + return PrimaryDevice::Inquiry(3, 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); diff --git a/src/raspberrypi/devices/scsi_daynaport.h b/src/raspberrypi/devices/scsi_daynaport.h index 1a08b4f2..404965d3 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..a4a956c8 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") +SCSIBR::SCSIBR() : PrimaryDevice("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,10 +57,6 @@ SCSIBR::~SCSIBR() fs->Reset(); delete fs; } - - for (auto const& command : commands) { - delete command.second; - } } bool SCSIBR::Init(const map& params) @@ -96,29 +94,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 +107,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 +150,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..81b19dfd 100644 --- a/src/raspberrypi/devices/scsi_host_bridge.h +++ b/src/raspberrypi/devices/scsi_host_bridge.h @@ -18,7 +18,7 @@ #pragma once #include "os.h" -#include "disk.h" +#include "primary_device.h" #include //=========================================================================== @@ -29,22 +29,9 @@ class CTapDriver; class CFileSys; -class SCSIBR : public Disk +class SCSIBR : public PrimaryDevice { -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 PrimaryDevice 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/scsicd.cpp b/src/raspberrypi/devices/scsicd.cpp index 1744b7f6..a250c443 100644 --- a/src/raspberrypi/devices/scsicd.cpp +++ b/src/raspberrypi/devices/scsicd.cpp @@ -10,14 +10,15 @@ // 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 + +using namespace scsi_defs; //=========================================================================== // @@ -240,8 +241,8 @@ 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); } //--------------------------------------------------------------------------- @@ -253,35 +254,12 @@ 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); } //--------------------------------------------------------------------------- @@ -421,9 +399,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 @@ -499,13 +476,10 @@ void SCSICD::ReadToc(SASIDEV *controller) //--------------------------------------------------------------------------- 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 diff --git a/src/raspberrypi/devices/scsicd.h b/src/raspberrypi/devices/scsicd.h index 2e860467..c52719ab 100644 --- a/src/raspberrypi/devices/scsicd.h +++ b/src/raspberrypi/devices/scsicd.h @@ -71,25 +71,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 +90,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..58981a05 100644 --- a/src/raspberrypi/devices/scsihd.cpp +++ b/src/raspberrypi/devices/scsihd.cpp @@ -119,9 +119,6 @@ void SCSIHD::Open(const Filepath& path) //--------------------------------------------------------------------------- int SCSIHD::Inquiry(const DWORD *cdb, BYTE *buf) { - ASSERT(cdb); - ASSERT(buf); - // EVPD check if (cdb[1] & 0x01) { SetStatusCode(STATUS_INVALIDCDB); 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/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/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 1747e39a..1243720d 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" @@ -34,6 +33,7 @@ #include #include #include +#include "config.h" using namespace std; using namespace spdlog; @@ -520,17 +520,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"); @@ -580,15 +580,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 @@ -709,16 +708,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; } @@ -840,8 +838,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(); diff --git a/src/raspberrypi/rascsi_response.cpp b/src/raspberrypi/rascsi_response.cpp index a9ff6883..6f7b1fbf 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++; } 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/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..ad4f62ee 100644 --- a/src/raspberrypi/scsi.h +++ b/src/raspberrypi/scsi.h @@ -154,8 +154,7 @@ private: static const char* phase_str_table[]; }; -class ScsiDefs { -public: +namespace scsi_defs { enum scsi_command : int { eCmdTestUnitReady = 0x00, eCmdRezero = 0x01, From 46d23a0d5d5d2b86995b1546477a7db0a9a6573e Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Mon, 14 Feb 2022 15:30:33 -0800 Subject: [PATCH 02/14] Proper cleanup of old python protobuf libraries. (#667) --- easyinstall.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) 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 From b3bdd07fa743cd3f926abadc90799776ad858f08 Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Tue, 15 Feb 2022 04:06:27 -0800 Subject: [PATCH 03/14] Web UI and OLED: adding Support Devices (#666) * Use the comparison operator that Python3 likes. * Add SUPPORT_DEVICE_TYPES category, and add Host Service to it. * Add webapp UI for attaching a support device. * Make the OLED screen aware of the support device type. * Tweak test to make it clear that this is experimental functionality. * Tweak device type names --- python/common/src/rascsi/common_settings.py | 1 + python/oled/src/rascsi_oled_monitor.py | 5 ++- python/web/src/device_utils.py | 26 ++++++------ python/web/src/templates/index.html | 46 ++++++++++++++++++++- python/web/src/web.py | 32 +++++++++++++- 5 files changed, 94 insertions(+), 16 deletions(-) diff --git a/python/common/src/rascsi/common_settings.py b/python/common/src/rascsi/common_settings.py index 0bd83a84..2a975c1a 100644 --- a/python/common/src/rascsi/common_settings.py +++ b/python/common/src/rascsi/common_settings.py @@ -8,6 +8,7 @@ WORK_DIR = getcwd() REMOVABLE_DEVICE_TYPES = ("SCCD", "SCRM", "SCMO") NETWORK_DEVICE_TYPES = ("SCDP", "SCBR") +SUPPORT_DEVICE_TYPES = ("SCHS", ) # 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]) diff --git a/python/oled/src/rascsi_oled_monitor.py b/python/oled/src/rascsi_oled_monitor.py index 90ba7d29..7ecd750a 100755 --- a/python/oled/src/rascsi_oled_monitor.py +++ b/python/oled/src/rascsi_oled_monitor.py @@ -46,6 +46,7 @@ from rascsi.socket_cmds import SocketCmds from rascsi.common_settings import ( REMOVABLE_DEVICE_TYPES, NETWORK_DEVICE_TYPES, + SUPPORT_DEVICE_TYPES, ) parser = argparse.ArgumentParser(description="RaSCSI OLED Monitor script") @@ -186,8 +187,8 @@ 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: + # Special handling of devices that don't use image files + elif line["device_type"] in (NETWORK_DEVICE_TYPES + SUPPORT_DEVICE_TYPES): 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 diff --git a/python/web/src/device_utils.py b/python/web/src/device_utils.py index e5abdff7..02711483 100644 --- a/python/web/src/device_utils.py +++ b/python/web/src/device_utils.py @@ -59,20 +59,22 @@ def extend_device_names(device_types): """ 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": + if device_type == "SAHD": + device_name = _("SASI Hard Disk") + elif device_type == "SCHD": + device_name = _("SCSI Hard Disk") + elif device_type == "SCRM": + device_name = _("Removable Disk") + elif device_type == "SCMO": + device_name = _("Magneto-Optical") + elif device_type == "SCCD": + device_name = _("CD-ROM / DVD") + elif device_type == "SCBR": device_name = _("X68000 Host Bridge") - elif device_type is "SCDP": + elif device_type == "SCDP": device_name = _("DaynaPORT SCSI/Link") + elif device_type == "SCHS": + device_name = _("Host Service") else: device_name = _("Unknown Device") mapped_device_types[device_type] = device_name diff --git a/python/web/src/templates/index.html b/python/web/src/templates/index.html index 667f230a..200abe91 100644 --- a/python/web/src/templates/index.html +++ b/python/web/src/templates/index.html @@ -267,7 +267,7 @@ {{ _("Select device type") }} {% for key, value in device_types.items() %} - {% if key not in NETWORK_DEVICE_TYPES %} + {% if key not in (NETWORK_DEVICE_TYPES + SUPPORT_DEVICE_TYPES) %} @@ -360,6 +360,50 @@

{{ _("Install Macproxy to browse the Web with any vintage browser. It's not just for Macs!", url="https://github.com/akuker/RASCSI/wiki/Vintage-Web-Proxy#macproxy") }}

{% endif %} +
+
+ + {{ _("Attach Support Device (EXPERIMENTAL)") }} + +
    +
  • Attach an experimental Support Device that may provide additional functionality for the Host system. +
  • +
  • Relies on driver software on the Host side that utilizes the exposed functionality. +
  • +
+
+ + + + +
+
+ + + + + + + +
+
+
diff --git a/python/web/src/web.py b/python/web/src/web.py index aa16c820..b0885602 100644 --- a/python/web/src/web.py +++ b/python/web/src/web.py @@ -55,6 +55,7 @@ from rascsi.common_settings import ( PROPERTIES_SUFFIX, REMOVABLE_DEVICE_TYPES, NETWORK_DEVICE_TYPES, + SUPPORT_DEVICE_TYPES, RESERVATIONS, ) from rascsi.ractl_cmds import RaCtlCmds @@ -120,7 +121,7 @@ def index(): extended_image_files = [] for image in image_files["files"]: - if image["detected_type"] is not "UNDEFINED": + if image["detected_type"] != "UNDEFINED": image["detected_type_name"] = mapped_device_types[image["detected_type"]] extended_image_files.append(image) @@ -190,6 +191,7 @@ def index(): PROPERTIES_SUFFIX=PROPERTIES_SUFFIX, REMOVABLE_DEVICE_TYPES=REMOVABLE_DEVICE_TYPES, NETWORK_DEVICE_TYPES=NETWORK_DEVICE_TYPES, + SUPPORT_DEVICE_TYPES=SUPPORT_DEVICE_TYPES, ) @@ -493,6 +495,34 @@ def log_level(): return redirect(url_for("index")) +@APP.route("/scsi/attach_support", methods=["POST"]) +@login_required +def attach_support_device(): + """ + Attaches a support device + """ + scsi_id = request.form.get("scsi_id") + unit = request.form.get("unit") + device_type = request.form.get("type") + kwargs = {"unit": int(unit), "device_type": device_type} + process = ractl.attach_image(scsi_id, **kwargs) + process = ReturnCodeMapper.add_msg(process) + if process["status"]: + flash(_( + ( + "Attached support device of type %(device_type)s " + "to SCSI ID %(id_number)s LUN %(unit_number)s" + ), + device_type=device_type, + id_number=scsi_id, + unit_number=unit, + )) + return redirect(url_for("index")) + + flash(process["msg"], "error") + return redirect(url_for("index")) + + @APP.route("/scsi/attach_network", methods=["POST"]) @login_required def attach_network_adapter(): From 511c55a7208e894fc288081cf1d0269b7b7fc57d Mon Sep 17 00:00:00 2001 From: Uwe Seimet <48174652+uweseimet@users.noreply.github.com> Date: Wed, 16 Feb 2022 03:13:22 +0100 Subject: [PATCH 04/14] Added work-around for the Atari Daynaport driver (#665) --- src/raspberrypi/devices/scsi_daynaport.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/raspberrypi/devices/scsi_daynaport.cpp b/src/raspberrypi/devices/scsi_daynaport.cpp index a789fc00..b4d8ea8e 100644 --- a/src/raspberrypi/devices/scsi_daynaport.cpp +++ b/src/raspberrypi/devices/scsi_daynaport.cpp @@ -241,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; @@ -255,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. From 419dca3c4c82aec6de1683f24e5b615702fd3d73 Mon Sep 17 00:00:00 2001 From: Uwe Seimet <48174652+uweseimet@users.noreply.github.com> Date: Thu, 17 Feb 2022 03:04:42 +0100 Subject: [PATCH 05/14] Added support for SCSI printer device (SCLP) (#670) * Fixed buster compile-time issue * Host services inherit from ModePageDevice * Call base class * Visibility update * Updated includes * Updated dispatcher * Added TODOs * Logging update * Code cleanup * Use namespace instead of class for ScsiDefs * Renaming * Cleanup * Use dispatcher template in order to remove duplicate code * Updated all dispatchers * Clean up commands * Removed duplicate code * Removed duplicate code * Updated template definition * Fixed typo * Fixed warning * Code cleanup * Device list must be static * Cleanup * Logging update * Added comments * Cleanup * Base class update * SCSIBR is not a subclass of Disk anymore, but of PrimaryDevice * Updated includes * Fixed compile-time issue on the Pi * Header file cleanup * Interface cleanup * Removed wrong override * include file cleanup * Removed obsolete usage of streams * Removed more stream usages * Stream usage cleanup * Include cleanup * Renaming * Include cleanup * Interface update * SCLP device skeleton * Initial RELEASE/RESERVE UNIT * Added full set of commands * Extracted command phase code * Stripped SCSI controller code * Removed unused code * Commented out code * Initial naive implementation * Added debug output * Disable printing for now * Updated file handling * Updated DataOut() * Added comment * Updated assertion * Comment update * Updated assertion * Code cleanup * Reset bytes to transfer * Reverted change * Refactoring * Moved assertion * Updated ReceiveBytes() * Removed override * Added interface * Code cleanup * Updated TEST UNIT READY * Added flag for byte-oriented transfer * Updated TEST UNIT READY * Length handling update * Updated bytecount handling * Fixed warning * Added TODO * Updated assertion * Enabled priting * Updated error handling * Code cleanup * Logging update * First working version * Use temporary file * Logging update * Handle parameters * Updated format string * Updated logging * File handling update * Code cleanup * Fixed buffer size * Updated file handling * Manpage update * Initial reservation handling * Updated reservation handling * Initial reservation testing * Remember initiator ID * Extract initiator ID * Updated SCSI initiator ID handling * Logging update * Added reservation timeout * Updated timeout handling * Code cleanup * Only pass initiator ID to *SCSI* controller * Added comments * Added comment * Implemented STOP PRINT * Comment update * Comment update * Comment update * Added comment * Comment update * Removed useless comments * Updated printer parameter handling * Updated parameter handling * Manpage update * Manpage update * Comment update * Default printer product name update * Renaming * Updated logging * Logging update * Logging update * Comment update * Code cleanup * Added printer shortcut * Comment update * Comment update * Output formatting update * Updated error handling * Code cleanup * More cleanup * Revert "More cleanup" This reverts commit 05708986ee161c118bb0d3a17c1ddf2f201ccfb8. * Output formatting update * Output format update * Sort parameters * Comment update * Improved parsing of parameters * Manpage update * Updated SCSI level * Removed magic constants * Removed magic constant * Template update * Template usage update * Get rid of SASIDEV for printer * Get rid of SASIDEV for host services * Moved initiator_id field * Moved field * Moved field * Added comment * Error handling must use effective LUN * Removed obsolete casts * Removed unused method declarations * Comment update * Code cleanup * More code cleanup * Optimization * Removed duplicate code * Logging update * Fixed warning * Code cleanup * Added TODOs * TODO update * Backwards compatibility update * Comment update --- doc/rascsi.1 | 4 +- doc/rascsi_man_page.txt | 23 +- doc/rasctl.1 | 1 + doc/rasctl_man_page.txt | 1 + src/raspberrypi/controllers/sasidev_ctrl.cpp | 17 +- src/raspberrypi/controllers/sasidev_ctrl.h | 8 +- src/raspberrypi/controllers/scsidev_ctrl.cpp | 228 +++++++++++++- src/raspberrypi/controllers/scsidev_ctrl.h | 44 +-- src/raspberrypi/devices/device_factory.cpp | 13 + src/raspberrypi/devices/disk.h | 2 +- src/raspberrypi/devices/dispatcher.h | 10 +- src/raspberrypi/devices/host_services.cpp | 7 +- src/raspberrypi/devices/host_services.h | 8 +- .../interfaces/scsi_printer_commands.h | 30 ++ src/raspberrypi/devices/mode_page_device.h | 2 +- src/raspberrypi/devices/primary_device.cpp | 8 +- src/raspberrypi/devices/primary_device.h | 3 +- src/raspberrypi/devices/scsi_daynaport.h | 2 +- src/raspberrypi/devices/scsi_host_bridge.h | 2 +- src/raspberrypi/devices/scsi_printer.cpp | 290 ++++++++++++++++++ src/raspberrypi/devices/scsi_printer.h | 61 ++++ src/raspberrypi/devices/scsicd.h | 2 +- src/raspberrypi/protobuf_util.cpp | 29 ++ src/raspberrypi/protobuf_util.h | 1 + src/raspberrypi/rascsi.cpp | 44 ++- src/raspberrypi/rascsi_interface.proto | 8 +- src/raspberrypi/rasctl.cpp | 11 +- src/raspberrypi/rasctl_display.cpp | 8 +- src/raspberrypi/rasutil.cpp | 4 + src/raspberrypi/scsi.h | 14 +- 30 files changed, 793 insertions(+), 92 deletions(-) create mode 100644 src/raspberrypi/devices/interfaces/scsi_printer_commands.h create mode 100644 src/raspberrypi/devices/scsi_printer.cpp create mode 100644 src/raspberrypi/devices/scsi_printer.h diff --git a/doc/rascsi.1 b/doc/rascsi.1 index 32f60a29..d287b791 100644 --- a/doc/rascsi.1 +++ b/doc/rascsi.1 @@ -73,7 +73,7 @@ The rascsi server port, default is 6868. .BR \-r\fI " " \fIRESERVED_IDS Comma-separated list of IDs to reserve. .BR \-p\fI " " \fITYPE -The optional case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP, 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, e.g. "interfaces=eth0,eth1,wlan0". For SCLP it is the print command to be used and a reservation timeout in seconds, e.g. "cmd=lp -oraw: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..359c738d 100644 --- a/doc/rascsi_man_page.txt +++ b/doc/rascsi_man_page.txt @@ -93,9 +93,9 @@ OPTIONS -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. + 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. -v Display the rascsi version. @@ -108,13 +108,18 @@ OPTIONS (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. + 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, e.g. "inter‐ + faces=eth0,eth1,wlan0". For SCLP it is the print command to be + used and a reservation timeout in seconds, e.g. "cmd=lp + -oraw:timeout=60". -HDn[:u] FILE - n is the SASI ID number (0-15). The effective SASI ID is calcu‐ - lated as n/2, the effective SASI LUN is calculated is the re‐ - mainder of n/2. Alternatively the n:u syntax can be used, where + n is the SASI ID number (0-15). The effective SASI ID is calcu‐ + lated as n/2, the effective SASI LUN is calculated is the re‐ + mainder 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). FILE is the name of the image file to use for the SASI device. @@ -131,7 +136,7 @@ EXAMPLES rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw de‐ - vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter + vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter as ID 6: rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp daynaport diff --git a/doc/rasctl.1 b/doc/rasctl.1 index 1159ee1d..f1cbae83 100644 --- a/doc/rasctl.1 +++ b/doc/rasctl.1 @@ -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..15746b19 100644 --- a/doc/rasctl_man_page.txt +++ b/doc/rasctl_man_page.txt @@ -127,6 +127,7 @@ OPTIONS 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/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 719bfc73..1432d047 100644 --- a/src/raspberrypi/controllers/sasidev_ctrl.h +++ b/src/raspberrypi/controllers/sasidev_ctrl.h @@ -140,7 +140,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 +162,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 +195,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 8fb8952e..2dc73e9a 100644 --- a/src/raspberrypi/controllers/scsidev_ctrl.cpp +++ b/src/raspberrypi/controllers/scsidev_ctrl.cpp @@ -17,6 +17,7 @@ #include "controllers/scsidev_ctrl.h" #include "gpiobus.h" #include "devices/scsi_daynaport.h" +#include "devices/scsi_printer.h" //=========================================================================== // @@ -26,6 +27,7 @@ SCSIDEV::SCSIDEV() : SASIDEV() { + scsi.bytes_to_transfer = 0; shutdown_mode = NONE; // Synchronous transfer work initialization @@ -52,7 +54,7 @@ void SCSIDEV::Reset() SASIDEV::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) { @@ -74,6 +76,8 @@ BUS::phase_t SCSIDEV::Process() return ctrl.phase; } + scsi.initiator_id = initiator_id; + // Phase processing switch (ctrl.phase) { // Bus free phase @@ -154,6 +158,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: @@ -163,7 +170,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: @@ -189,7 +198,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; } @@ -201,6 +210,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; @@ -329,7 +345,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(); @@ -350,7 +366,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; } @@ -360,7 +376,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__); @@ -482,6 +498,11 @@ void SCSIDEV::Send() //--------------------------------------------------------------------------- void SCSIDEV::Receive() { + if (scsi.is_byte_transfer) { + ReceiveBytes(); + return; + } + int len; BYTE data; @@ -566,7 +587,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]); } @@ -681,7 +702,7 @@ void SCSIDEV::Receive() // Transfer MSG // //--------------------------------------------------------------------------- -bool SCSIDEV::XferMsg(DWORD msg) +bool SCSIDEV::XferMsg(int msg) { ASSERT(ctrl.phase == BUS::msgout); @@ -694,3 +715,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 SASIDEV::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..c786cb71 100644 --- a/src/raspberrypi/controllers/scsidev_ctrl.h +++ b/src/raspberrypi/controllers/scsidev_ctrl.h @@ -45,45 +45,53 @@ 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; } + 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: - // 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/device_factory.cpp b/src/raspberrypi/devices/device_factory.cpp index fb98e06c..4528564a 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" @@ -52,6 +53,8 @@ DeviceFactory::DeviceFactory() default_params[SCBR]["interfaces"] = network_interfaces; default_params[SCDP]["interfaces"] = network_interfaces; + default_params[SCLP]["cmd"] = "lp -oraw"; + default_params[SCLP]["timeout"] = "30"; extension_mapping["hdf"] = SAHD; extension_mapping["hds"] = SCHD; @@ -95,6 +98,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 +203,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.h b/src/raspberrypi/devices/disk.h index 59bdb740..84af03b5 100644 --- a/src/raspberrypi/devices/disk.h +++ b/src/raspberrypi/devices/disk.h @@ -53,7 +53,7 @@ private: off_t image_offset; // Offset to actual data } disk_t; - Dispatcher dispatcher; + Dispatcher dispatcher; public: Disk(const string&); diff --git a/src/raspberrypi/devices/dispatcher.h b/src/raspberrypi/devices/dispatcher.h index 6cd0cc47..6a2a46c4 100644 --- a/src/raspberrypi/devices/dispatcher.h +++ b/src/raspberrypi/devices/dispatcher.h @@ -21,7 +21,7 @@ class SCSIDEV; using namespace std; using namespace scsi_defs; -template +template class Dispatcher { public: @@ -36,18 +36,18 @@ public: typedef struct _command_t { const char* name; - void (T::*execute)(SASIDEV *); + void (T::*execute)(U *); - _command_t(const char* _name, void (T::*_execute)(SASIDEV *)) : name(_name), execute(_execute) { }; + _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)(SASIDEV *)) + void AddCommand(scsi_command opcode, const char* name, void (T::*execute)(U *)) { commands[opcode] = new command_t(name, execute); } - bool Dispatch(T *instance, SCSIDEV *controller) + bool Dispatch(T *instance, U *controller) { SASIDEV::ctrl_t *ctrl = controller->GetCtrl(); instance->SetCtrl(ctrl); diff --git a/src/raspberrypi/devices/host_services.cpp b/src/raspberrypi/devices/host_services.cpp index b3b11f25..22852f85 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 +// //--------------------------------------------------------------------------- // @@ -37,6 +39,7 @@ using namespace scsi_defs; HostServices::HostServices() : ModePageDevice("SCHS") { + dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &HostServices::TestUnitReady); dispatcher.AddCommand(eCmdStartStop, "StartStopUnit", &HostServices::StartStopUnit); } @@ -46,7 +49,7 @@ bool HostServices::Dispatch(SCSIDEV *controller) return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller); } -void HostServices::TestUnitReady(SASIDEV *controller) +void HostServices::TestUnitReady(SCSIDEV *controller) { // Always successful controller->Status(); @@ -58,7 +61,7 @@ int HostServices::Inquiry(const DWORD *cdb, BYTE *buf) return PrimaryDevice::Inquiry(3, 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; diff --git a/src/raspberrypi/devices/host_services.h b/src/raspberrypi/devices/host_services.h index 7d7aa944..23276189 100644 --- a/src/raspberrypi/devices/host_services.h +++ b/src/raspberrypi/devices/host_services.h @@ -5,7 +5,7 @@ // // Copyright (C) 2022 Uwe Seimet // -// RaSCSI Host Services with realtime clock and shutdown support +// Host Services with realtime clock and shutdown support // //--------------------------------------------------------------------------- #pragma once @@ -25,8 +25,8 @@ public: virtual bool Dispatch(SCSIDEV *) override; int Inquiry(const DWORD *, BYTE *) override; - void TestUnitReady(SASIDEV *) override; - void StartStopUnit(SASIDEV *); + void TestUnitReady(SCSIDEV *); + void StartStopUnit(SCSIDEV *); int ModeSense6(const DWORD *, BYTE *); int ModeSense10(const DWORD *, BYTE *); @@ -35,7 +35,7 @@ private: typedef ModePageDevice super; - Dispatcher dispatcher; + Dispatcher dispatcher; int AddRealtimeClockPage(int, BYTE *); }; 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.h b/src/raspberrypi/devices/mode_page_device.h index b8200aa2..3b50c236 100644 --- a/src/raspberrypi/devices/mode_page_device.h +++ b/src/raspberrypi/devices/mode_page_device.h @@ -33,7 +33,7 @@ private: typedef PrimaryDevice super; - Dispatcher dispatcher; + Dispatcher dispatcher; void ModeSense6(SASIDEV *); void ModeSense10(SASIDEV *); diff --git a/src/raspberrypi/devices/primary_device.cpp b/src/raspberrypi/devices/primary_device.cpp index 3f097bc8..15717fec 100644 --- a/src/raspberrypi/devices/primary_device.cpp +++ b/src/raspberrypi/devices/primary_device.cpp @@ -184,7 +184,7 @@ int PrimaryDevice::Inquiry(int type, bool is_removable, const DWORD *cdb, BYTE * memset(buf, 0, allocation_length); buf[0] = type; buf[1] = is_removable ? 0x80 : 0x00; - buf[2] = 0x01; + buf[2] = 0x02; buf[4] = 0x1F; // Padded vendor, product, revision @@ -232,3 +232,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 1068afdc..c1410372 100644 --- a/src/raspberrypi/devices/primary_device.h +++ b/src/raspberrypi/devices/primary_device.h @@ -36,6 +36,7 @@ public: bool CheckReady(); virtual int Inquiry(const DWORD *, BYTE *) = 0; virtual int RequestSense(const DWORD *, BYTE *); + virtual bool WriteBytes(BYTE *, uint32_t); protected: @@ -45,7 +46,7 @@ protected: private: - Dispatcher dispatcher; + Dispatcher dispatcher; void Inquiry(SASIDEV *); void ReportLuns(SASIDEV *); diff --git a/src/raspberrypi/devices/scsi_daynaport.h b/src/raspberrypi/devices/scsi_daynaport.h index 404965d3..1dbbef60 100644 --- a/src/raspberrypi/devices/scsi_daynaport.h +++ b/src/raspberrypi/devices/scsi_daynaport.h @@ -92,7 +92,7 @@ public: private: typedef Disk super; - Dispatcher dispatcher; + Dispatcher dispatcher; typedef struct __attribute__((packed)) { BYTE operation_code; diff --git a/src/raspberrypi/devices/scsi_host_bridge.h b/src/raspberrypi/devices/scsi_host_bridge.h index 81b19dfd..7964e9eb 100644 --- a/src/raspberrypi/devices/scsi_host_bridge.h +++ b/src/raspberrypi/devices/scsi_host_bridge.h @@ -50,7 +50,7 @@ public: private: typedef PrimaryDevice super; - Dispatcher dispatcher; + Dispatcher dispatcher; int GetMacAddr(BYTE *buf); // Get MAC address void SetMacAddr(BYTE *buf); // Set MAC address diff --git a/src/raspberrypi/devices/scsi_printer.cpp b/src/raspberrypi/devices/scsi_printer.cpp new file mode 100644 index 00000000..d34e1c74 --- /dev/null +++ b/src/raspberrypi/devices/scsi_printer.cpp @@ -0,0 +1,290 @@ +//--------------------------------------------------------------------------- +// +// 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". This either +// 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. +// By attaching different devices/LUNs multiple printers (i.e. different print commands) are possible. +// +// 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(eCmdReadCapacity10, "SynchronizeBuffer", &SCSIPrinter::SynchronizeBuffer); + dispatcher.AddCommand(eCmdSendDiag, "SendDiagnostic", &SCSIPrinter::SendDiagnostic); + dispatcher.AddCommand(eCmdStartStop, "StopPrint", &SCSIPrinter::StopPrint); +} + +SCSIPrinter::~SCSIPrinter() +{ + DiscardReservation(); +} + +bool SCSIPrinter::Init(const map& params) +{ + // Use default parameters if no parameters were provided + SetParams(params.empty() ? GetDefaultParams() : params); + + if (!GetAsInt(GetParam("timeout"), timeout) || timeout <= 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, not removable + return PrimaryDevice::Inquiry(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) { + 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"); + cmd += " "; + cmd += filename; + + 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; + } + + Cleanup(); + + 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.h b/src/raspberrypi/devices/scsicd.h index c52719ab..fb87d47a 100644 --- a/src/raspberrypi/devices/scsicd.h +++ b/src/raspberrypi/devices/scsicd.h @@ -92,7 +92,7 @@ public: private: typedef Disk super; - Dispatcher dispatcher; + Dispatcher dispatcher; // Open void OpenCue(const Filepath& path); // Open(CUE) 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 1243720d..4bb6ff12 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -50,6 +50,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 @@ -107,7 +109,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"); @@ -868,7 +870,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 << "'"; @@ -1084,7 +1086,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; @@ -1298,19 +1300,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)); @@ -1705,16 +1701,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; @@ -1742,7 +1756,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..d4dc45fe 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,11 @@ 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. + // "interfaces": A prioritized comma-separated list of interfaces to create a network bridge for + // "cmd": The command to be used for printing + // "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/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_display.cpp b/src/raspberrypi/rasctl_display.cpp index f17c0301..3d2190ae 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,7 +173,7 @@ void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types bool isFirst = true; for (const auto& param : params) { if (!isFirst) { - cout << ", "; + cout << ":"; } cout << param.first << "=" << param.second; 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/scsi.h b/src/raspberrypi/scsi.h index ad4f62ee..e5e1e947 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, From 2281a7e53b5767b4dba6d41357ec1b29cb08939f Mon Sep 17 00:00:00 2001 From: Uwe Seimet <48174652+uweseimet@users.noreply.github.com> Date: Fri, 18 Feb 2022 21:03:22 +0100 Subject: [PATCH 06/14] Bridge is not yet ready to not subclass Disk (#679) --- src/raspberrypi/devices/scsi_host_bridge.cpp | 2 +- src/raspberrypi/devices/scsi_host_bridge.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/raspberrypi/devices/scsi_host_bridge.cpp b/src/raspberrypi/devices/scsi_host_bridge.cpp index a4a956c8..e490f6a2 100644 --- a/src/raspberrypi/devices/scsi_host_bridge.cpp +++ b/src/raspberrypi/devices/scsi_host_bridge.cpp @@ -24,7 +24,7 @@ using namespace std; using namespace scsi_defs; -SCSIBR::SCSIBR() : PrimaryDevice("SCBR") +SCSIBR::SCSIBR() : Disk("SCBR") { tap = NULL; m_bTapEnable = false; diff --git a/src/raspberrypi/devices/scsi_host_bridge.h b/src/raspberrypi/devices/scsi_host_bridge.h index 7964e9eb..e7e304e7 100644 --- a/src/raspberrypi/devices/scsi_host_bridge.h +++ b/src/raspberrypi/devices/scsi_host_bridge.h @@ -18,7 +18,7 @@ #pragma once #include "os.h" -#include "primary_device.h" +#include "disk.h" #include //=========================================================================== @@ -29,7 +29,7 @@ class CTapDriver; class CFileSys; -class SCSIBR : public PrimaryDevice +class SCSIBR : public Disk { public: @@ -48,7 +48,7 @@ public: void SendMessage10(SASIDEV *); private: - typedef PrimaryDevice super; + typedef Disk super; Dispatcher dispatcher; From e13cf1ebb4bb11dc0c478cae2c2fe9de7fa1b06b Mon Sep 17 00:00:00 2001 From: Uwe Seimet <48174652+uweseimet@users.noreply.github.com> Date: Fri, 18 Feb 2022 21:04:07 +0100 Subject: [PATCH 07/14] Resolved TODO, rascsi guarantees that LUN 0 is always present (#681) --- src/raspberrypi/devices/primary_device.cpp | 26 ++++------------------ 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/src/raspberrypi/devices/primary_device.cpp b/src/raspberrypi/devices/primary_device.cpp index 15717fec..d32677b5 100644 --- a/src/raspberrypi/devices/primary_device.cpp +++ b/src/raspberrypi/devices/primary_device.cpp @@ -45,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]) { 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; } From 81ca3c0ce843ae82c5e03a9db7e32153e6449d48 Mon Sep 17 00:00:00 2001 From: Uwe Seimet <48174652+uweseimet@users.noreply.github.com> Date: Fri, 18 Feb 2022 21:06:33 +0100 Subject: [PATCH 08/14] Added filename placeholder, execute print command as lp, fixed memory leaks (#677) * Added filename placeholder * Comment update * Execute print command as lp user * Comment update * Comment update * Added operaton parameters * Sort parameter output by name * Removed assertion * Revert "Removed assertion" This reverts commit f9ab582ddccecb5141c80a71645500cfc1500bb4. * Include cleanup * Code cleanup * Updated SCSI reset * Fixed memory leak * Revert "Fixed memory leak" This reverts commit 2dbbdcd192280317193cea80691bda40ed073d10. * Fixed memory leak * Fixed memory leak * Updated operation count check * Fixed memory leaks * Delete temporary PbOperationInfo --- src/raspberrypi/controllers/sasidev_ctrl.h | 1 - src/raspberrypi/controllers/scsidev_ctrl.cpp | 7 ++++--- src/raspberrypi/controllers/scsidev_ctrl.h | 1 + src/raspberrypi/devices/device_factory.cpp | 2 +- src/raspberrypi/devices/scsi_printer.cpp | 18 ++++++++++++++---- src/raspberrypi/rascsi.cpp | 8 +++++++- src/raspberrypi/rascsi_interface.proto | 2 +- src/raspberrypi/rascsi_response.cpp | 6 +++--- src/raspberrypi/rasctl_display.cpp | 5 ++++- 9 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/raspberrypi/controllers/sasidev_ctrl.h b/src/raspberrypi/controllers/sasidev_ctrl.h index 1432d047..136dc14f 100644 --- a/src/raspberrypi/controllers/sasidev_ctrl.h +++ b/src/raspberrypi/controllers/sasidev_ctrl.h @@ -19,7 +19,6 @@ #include "os.h" #include "scsi.h" #include "fileio.h" -#include "log.h" class Device; diff --git a/src/raspberrypi/controllers/scsidev_ctrl.cpp b/src/raspberrypi/controllers/scsidev_ctrl.cpp index 2dc73e9a..b8699d55 100644 --- a/src/raspberrypi/controllers/scsidev_ctrl.cpp +++ b/src/raspberrypi/controllers/scsidev_ctrl.cpp @@ -45,13 +45,14 @@ 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(int initiator_id) @@ -891,7 +892,7 @@ void SCSIDEV::ReceiveBytes() bool SCSIDEV::XferOut(bool cont) { if (!scsi.is_byte_transfer) { - return SASIDEV::XferOut(cont); + return super::XferOut(cont); } ASSERT(ctrl.phase == BUS::dataout); diff --git a/src/raspberrypi/controllers/scsidev_ctrl.h b/src/raspberrypi/controllers/scsidev_ctrl.h index c786cb71..55c4ca15 100644 --- a/src/raspberrypi/controllers/scsidev_ctrl.h +++ b/src/raspberrypi/controllers/scsidev_ctrl.h @@ -77,6 +77,7 @@ public: void SetByteTransfer(bool is_byte_transfer) { scsi.is_byte_transfer = is_byte_transfer; } private: + typedef SASIDEV super; // Phases void BusFree() override; diff --git a/src/raspberrypi/devices/device_factory.cpp b/src/raspberrypi/devices/device_factory.cpp index 4528564a..2cd3eeac 100644 --- a/src/raspberrypi/devices/device_factory.cpp +++ b/src/raspberrypi/devices/device_factory.cpp @@ -53,7 +53,7 @@ DeviceFactory::DeviceFactory() default_params[SCBR]["interfaces"] = network_interfaces; default_params[SCDP]["interfaces"] = network_interfaces; - default_params[SCLP]["cmd"] = "lp -oraw"; + default_params[SCLP]["cmd"] = "lp -oraw %f"; default_params[SCLP]["timeout"] = "30"; extension_mapping["hdf"] = SAHD; diff --git a/src/raspberrypi/devices/scsi_printer.cpp b/src/raspberrypi/devices/scsi_printer.cpp index d34e1c74..ba2e3f65 100644 --- a/src/raspberrypi/devices/scsi_printer.cpp +++ b/src/raspberrypi/devices/scsi_printer.cpp @@ -25,10 +25,12 @@ // 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". This either +// 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. +// 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. // 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. // @@ -73,7 +75,13 @@ bool SCSIPrinter::Init(const map& params) // Use default parameters if no parameters were provided SetParams(params.empty() ? GetDefaultParams() : 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; } @@ -192,8 +200,10 @@ void SCSIPrinter::SynchronizeBuffer(SCSIDEV *controller) fd = -1; string cmd = GetParam("cmd"); - cmd += " "; - cmd += filename; + 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()); diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 4bb6ff12..271f7cc4 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -829,6 +829,8 @@ void TerminationHandler(int signum) { DetachAll(); + Cleanup(); + exit(signum); } @@ -1591,9 +1593,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; diff --git a/src/raspberrypi/rascsi_interface.proto b/src/raspberrypi/rascsi_interface.proto index d4dc45fe..16a4672c 100644 --- a/src/raspberrypi/rascsi_interface.proto +++ b/src/raspberrypi/rascsi_interface.proto @@ -42,7 +42,7 @@ enum PbOperation { // 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 - // "cmd": The command to be used for printing + // "cmd": The command to be used for printing, with "%f" as file placeholder // "timeout": The timeout for printer reservations in seconds ATTACH = 1; diff --git a/src/raspberrypi/rascsi_response.cpp b/src/raspberrypi/rascsi_response.cpp index 6f7b1fbf..6bb9e4ae 100644 --- a/src/raspberrypi/rascsi_response.cpp +++ b/src/raspberrypi/rascsi_response.cpp @@ -370,6 +370,8 @@ 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, "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 +500,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 +512,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_display.cpp b/src/raspberrypi/rasctl_display.cpp index 3d2190ae..a7258e45 100644 --- a/src/raspberrypi/rasctl_display.cpp +++ b/src/raspberrypi/rasctl_display.cpp @@ -304,7 +304,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()) { From 2184992ce721c137fd0fe444d1adac538fdc95f2 Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Sat, 19 Feb 2022 00:04:14 -0800 Subject: [PATCH 09/14] Tentative Web UI for the Printer device. (#676) * Tentative Web UI for the Printer device. * timeout must be a positive value * Change the attach device class method to accept a dict with arbitrary key value pairs of parameters to be passed to the protobuf interface, rather than hard coded ones. Also renames the RaCtlCmds.attach_image() class method to attach_device(). * Make download_to_iso() use the new attach interface. * Dynamically get the form items for support devices. * Change the data structure returned by RaCtlCmds.get_device_types() to a dict, which contains the supported parameters and their default values. Leverage this data in the web ui to derive the form fields from the capabilities of rascsi. * Tweak UI labels. * Update FileCmds.read_config() to work with the new RaCtlCmds.attach_device() method. * Check for numeric value. * Streamline the UI for support devices. * Handle support devices better by the oled screen. * Clean up html. * Dynamically adjust form field size based on data length. --- python/common/src/rascsi/common_settings.py | 2 +- python/common/src/rascsi/file_cmds.py | 19 ++++---- python/common/src/rascsi/ractl_cmds.py | 38 ++++++++------- python/oled/src/rascsi_oled_monitor.py | 4 +- python/web/src/device_utils.py | 6 ++- python/web/src/templates/index.html | 54 +++++++++++---------- python/web/src/web.py | 46 ++++++++++++------ 7 files changed, 97 insertions(+), 72 deletions(-) diff --git a/python/common/src/rascsi/common_settings.py b/python/common/src/rascsi/common_settings.py index 2a975c1a..84b5c2a1 100644 --- a/python/common/src/rascsi/common_settings.py +++ b/python/common/src/rascsi/common_settings.py @@ -8,7 +8,7 @@ WORK_DIR = getcwd() REMOVABLE_DEVICE_TYPES = ("SCCD", "SCRM", "SCMO") NETWORK_DEVICE_TYPES = ("SCDP", "SCBR") -SUPPORT_DEVICE_TYPES = ("SCHS", ) +SUPPORT_DEVICE_TYPES = ("SCLP", "SCHS") # 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]) 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..5fcbc723 100644 --- a/python/common/src/rascsi/ractl_cmds.py +++ b/python/common/src/rascsi/ractl_cmds.py @@ -125,7 +125,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,9 +136,13 @@ 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 = {} + import logging + 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)] = params return {"status": result.status, "device_types": device_types} def get_image_files_info(self): @@ -167,7 +172,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 +190,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) @@ -214,23 +220,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 7ecd750a..d6ede5c1 100755 --- a/python/oled/src/rascsi_oled_monitor.py +++ b/python/oled/src/rascsi_oled_monitor.py @@ -188,9 +188,11 @@ def formatted_output(): else: output.append(f"{line['id']} {line['device_type'][2:4]} {line['status']}") # Special handling of devices that don't use image files - elif line["device_type"] in (NETWORK_DEVICE_TYPES + SUPPORT_DEVICE_TYPES): + elif line["device_type"] in (NETWORK_DEVICE_TYPES): output.append(f"{line['id']} {line['device_type'][2:4]} {line['vendor']} " f"{line['product']}") + elif line["device_type"] in (SUPPORT_DEVICE_TYPES): + output.append(f"{line['id']} {line['device_type'][2:4]} {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 02711483..53ca9b31 100644 --- a/python/web/src/device_utils.py +++ b/python/web/src/device_utils.py @@ -68,13 +68,15 @@ def extend_device_names(device_types): elif device_type == "SCMO": device_name = _("Magneto-Optical") elif device_type == "SCCD": - device_name = _("CD-ROM / DVD") + device_name = _("CD / DVD") elif device_type == "SCBR": device_name = _("X68000 Host Bridge") elif device_type == "SCDP": device_name = _("DaynaPORT SCSI/Link") + elif device_type == "SCLP": + device_name = _("Printer") elif device_type == "SCHS": - device_name = _("Host Service") + device_name = _("Host Services") else: device_name = _("Unknown Device") mapped_device_types[device_type] = device_name diff --git a/python/web/src/templates/index.html b/python/web/src/templates/index.html index 200abe91..0716580e 100644 --- a/python/web/src/templates/index.html +++ b/python/web/src/templates/index.html @@ -316,17 +316,6 @@
- - - - - + + + + + - {% for type in SUPPORT_DEVICE_TYPES %} - - {% endfor %} - + + {% for key, value in device_params[type].items() %} + + {% if value.isnumeric() %} + + {% else %} + + {% endif %} + {% endfor %} - {% for type in NETWORK_DEVICE_TYPES %} - - {% endfor %} - - - + {% for key, value in device_params[type].items() %} + + {% if value.isnumeric() %} + + {% elif key == "interface" %} + - - - - - - - - -
- - - -{% if macproxy_configured %} -

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

-{% else %} -

{{ _("Install Macproxy to browse the Web with any vintage browser. It's not just for Macs!", url="https://github.com/akuker/RASCSI/wiki/Vintage-Web-Proxy#macproxy") }}

-{% endif %} - -
-
- - {{ _("Attach Support Device (EXPERIMENTAL)") }} - -
    -
  • Attach an experimental Support Device that may provide additional functionality for the Host system. -
  • -
  • Relies on driver software on the Host side that utilizes the exposed functionality. -
  • -
-
- - {% for type in SUPPORT_DEVICE_TYPES %} - - - {% endfor %}
-
{{ _("Type:") }} {{ device_types[type] }}
-
-
- - {% for key, value in device_params[type].items() %} - - {% if value.isnumeric() %} - {% else %} - + {% endif %} {% endfor %} @@ -405,6 +360,11 @@
+{% if macproxy_configured %} +

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

+{% else %} +

{{ _("Install Macproxy to browse the Web with any vintage browser. It's not just for Macs!", url="https://github.com/akuker/RASCSI/wiki/Vintage-Web-Proxy#macproxy") }}

+{% endif %}
diff --git a/python/web/src/web.py b/python/web/src/web.py index ae5be1f6..88b809d3 100644 --- a/python/web/src/web.py +++ b/python/web/src/web.py @@ -496,11 +496,11 @@ def log_level(): return redirect(url_for("index")) -@APP.route("/scsi/attach_support", methods=["POST"]) +@APP.route("/scsi/attach_device", methods=["POST"]) @login_required -def attach_support_device(): +def attach_device(): """ - Attaches a support device + Attaches a support device that doesn't take an image file as argument """ params = {} for item in request.form: @@ -511,7 +511,32 @@ def attach_support_device(): elif item == "type": device_type = request.form.get(item) else: - params.update({item: request.form.get(item)}) + 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" 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), @@ -523,66 +548,7 @@ def attach_support_device(): if process["status"]: flash(_( ( - "Attached support device of type %(device_type)s " - "to SCSI ID %(id_number)s LUN %(unit_number)s" - ), - device_type=device_type, - id_number=scsi_id, - unit_number=unit, - )) - return redirect(url_for("index")) - - flash(process["msg"], "error") - return redirect(url_for("index")) - - -@APP.route("/scsi/attach_network", methods=["POST"]) -@login_required -def attach_network_device(): - """ - Attaches a network adapter device - """ - 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") - - 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")) - - kwargs = {"unit": int(unit), "device_type": device_type} - if interface != "": - if "" not in (ip_addr, mask): - interface += (":" + ip_addr + "/" + mask) - kwargs["params"] = {"interfaces": interface} - - process = ractl.attach_device(scsi_id, **kwargs) - process = ReturnCodeMapper.add_msg(process) - if process["status"]: - flash(_( - ( - "Attached network device of type %(device_type)s " + "Attached device of type %(device_type)s " "to SCSI ID %(id_number)s LUN %(unit_number)s" ), device_type=device_type, From 4252d46844dacd39058c369fe0a13114955746bc Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Mon, 21 Feb 2022 09:27:31 -0800 Subject: [PATCH 12/14] Inquire the backend for device capabilities instead of Web UI assumptions (#688) * Add capabilities to RaCtlCmds.get_device_types() to return the image file support boolean, and list of supported block sizes. * Inquire rascsi backend about the min block size rather than hard coding values in the web UI. * Add class methods for getting lists of certain device types. * Use the new class methods to get lists of device types in the web ui. * Make use of the new class methods in the oled script. * Remove now unused constants, and simplify logic in common_settings * Improve device name mapping to extend the existing dictionary rather than creating a new data structure. * Use jinja2 sort filters instead of sorting in python code. Removing redundant variables. * Introduce the get_device_name() utility method which returns the translated name for a device acronym. Use the new method to display device name when attaching devices. * Fix typo * Rename Support Device to Periperal Device. General tweaks to UI strings. * Tweak UI string. * Fix error. --- python/common/src/rascsi/common_settings.py | 8 +-- python/common/src/rascsi/ractl_cmds.py | 48 +++++++++++-- python/oled/src/rascsi_oled_monitor.py | 18 ++--- python/web/src/device_utils.py | 58 ++++++++------- python/web/src/templates/drives.html | 12 ++-- python/web/src/templates/index.html | 28 ++++---- python/web/src/web.py | 80 ++++++++------------- 7 files changed, 135 insertions(+), 117 deletions(-) diff --git a/python/common/src/rascsi/common_settings.py b/python/common/src/rascsi/common_settings.py index 84b5c2a1..8e0d80e5 100644 --- a/python/common/src/rascsi/common_settings.py +++ b/python/common/src/rascsi/common_settings.py @@ -4,14 +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") -SUPPORT_DEVICE_TYPES = ("SCLP", "SCHS") - # 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/ractl_cmds.py b/python/common/src/rascsi/ractl_cmds.py index 5fcbc723..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 @@ -137,14 +136,54 @@ class RaCtlCmds: result = proto.PbResult() result.ParseFromString(data) device_types = {} - import logging 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)] = params + 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. @@ -208,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, diff --git a/python/oled/src/rascsi_oled_monitor.py b/python/oled/src/rascsi_oled_monitor.py index d6ede5c1..343bd396 100755 --- a/python/oled/src/rascsi_oled_monitor.py +++ b/python/oled/src/rascsi_oled_monitor.py @@ -43,12 +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, - SUPPORT_DEVICE_TYPES, -) - parser = argparse.ArgumentParser(description="RaSCSI OLED Monitor script") parser.add_argument( "--rotation", @@ -166,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(): """ @@ -188,11 +183,12 @@ def formatted_output(): else: output.append(f"{line['id']} {line['device_type'][2:4]} {line['status']}") # Special handling of devices that don't use image files - elif line["device_type"] in (NETWORK_DEVICE_TYPES): - output.append(f"{line['id']} {line['device_type'][2:4]} {line['vendor']} " - f"{line['product']}") elif line["device_type"] in (SUPPORT_DEVICE_TYPES): - output.append(f"{line['id']} {line['device_type'][2:4]} {line['product']}") + 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 53ca9b31..0d1d6cf8 100644 --- a/python/web/src/device_utils.py +++ b/python/web/src/device_utils.py @@ -52,33 +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 == "SAHD": - device_name = _("SASI Hard Disk") - elif device_type == "SCHD": - device_name = _("SCSI Hard Disk") - elif device_type == "SCRM": - device_name = _("Removable Disk") - elif device_type == "SCMO": - device_name = _("Magneto-Optical") - elif device_type == "SCCD": - device_name = _("CD / DVD") - elif device_type == "SCBR": - device_name = _("X68000 Host Bridge") - elif device_type == "SCDP": - device_name = _("DaynaPORT SCSI/Link") - elif device_type == "SCLP": - device_name = _("Printer") - elif device_type == "SCHS": - device_name = _("Host Services") - 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 832295d3..7dda1c79 100644 --- a/python/web/src/templates/index.html +++ b/python/web/src/templates/index.html @@ -15,7 +15,7 @@

{% for key, value in device_types.items() %} - {% if key not in (NETWORK_DEVICE_TYPES + SUPPORT_DEVICE_TYPES) %} + {% if key in DISK_DEVICE_TYPES %} {% endif %} {% endfor %} @@ -298,7 +298,7 @@


- {{ _("Attach Support Device") }} + {{ _("Attach Peripheral Device") }}
  • {{ _("DaynaPORT SCSI/Link and X68000 Host Bridge are network devices.", url1="https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link", url2="https://github.com/akuker/RASCSI/wiki/X68000#Host_File_System_driver") }} @@ -317,21 +317,21 @@
- - + + - {% for type in (NETWORK_DEVICE_TYPES + SUPPORT_DEVICE_TYPES) %} + {% for type in PERIPHERAL_DEVICE_TYPES %} {% endif %} diff --git a/python/web/src/web.py b/python/web/src/web.py index df458c96..59a89862 100644 --- a/python/web/src/web.py +++ b/python/web/src/web.py @@ -712,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"] From ba8ad2e7f54d747ff431cf3e4b7e7b4459cff8af Mon Sep 17 00:00:00 2001 From: Uwe Seimet <48174652+uweseimet@users.noreply.github.com> Date: Mon, 21 Feb 2022 21:01:17 +0100 Subject: [PATCH 14/14] Fixed missing media change handling (#690) * Remember media change * Added comment * Added TODO * Updated SCSI level for host services to SPC-5 * Manpage update --- doc/rascsi.1 | 2 +- doc/rascsi_man_page.txt | 33 ++++++++++---------- doc/rasctl.1 | 2 +- doc/rasctl_man_page.txt | 23 +++++++------- src/raspberrypi/devices/disk.cpp | 20 ++++++++++++ src/raspberrypi/devices/disk.h | 2 ++ src/raspberrypi/devices/host_services.cpp | 4 +-- src/raspberrypi/devices/mode_page_device.cpp | 1 + src/raspberrypi/devices/primary_device.cpp | 4 +-- src/raspberrypi/devices/primary_device.h | 2 +- src/raspberrypi/devices/scsi_daynaport.cpp | 4 +-- src/raspberrypi/devices/scsi_printer.cpp | 4 +-- src/raspberrypi/rascsi.cpp | 5 +++ src/raspberrypi/scsi.h | 1 + 14 files changed, 69 insertions(+), 38 deletions(-) diff --git a/doc/rascsi.1 b/doc/rascsi.1 index 8954e32b..8c63bc13 100644 --- a/doc/rascsi.1 +++ b/doc/rascsi.1 @@ -71,7 +71,7 @@ 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, 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 diff --git a/doc/rascsi_man_page.txt b/doc/rascsi_man_page.txt index 12bbb4c4..1f6d8518 100644 --- a/doc/rascsi_man_page.txt +++ b/doc/rascsi_man_page.txt @@ -91,35 +91,36 @@ 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, SCLP, SCHS). If no type is specified for devices - that support 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. + 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 + 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 + 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‐ - lated as n/2, the effective SASI LUN is calculated is the re‐ - mainder of n/2. Alternatively the n:u syntax can be used, where + n is the SASI ID number (0-15). The effective SASI ID is calcu‐ + lated as n/2, the effective SASI LUN is calculated is the re‐ + mainder 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). FILE is the name of the image file to use for the SASI device. @@ -136,7 +137,7 @@ EXAMPLES rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw de‐ - vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter + vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter as ID 6: rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp daynaport diff --git a/doc/rasctl.1 b/doc/rasctl.1 index f1cbae83..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. diff --git a/doc/rasctl_man_page.txt b/doc/rasctl_man_page.txt index 15746b19..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,18 +111,18 @@ 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 @@ -133,16 +134,16 @@ OPTIONS 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/src/raspberrypi/devices/disk.cpp b/src/raspberrypi/devices/disk.cpp index 2a444b76..7bdbc995 100644 --- a/src/raspberrypi/devices/disk.cpp +++ b/src/raspberrypi/devices/disk.cpp @@ -31,6 +31,7 @@ Disk::Disk(const string& id) : ModePageDevice(id), ScsiBlockCommands() disk.blocks = 0; disk.dcache = NULL; disk.image_offset = 0; + disk.is_medium_changed = false; dispatcher.AddCommand(eCmdRezero, "Rezero", &Disk::Rezero); dispatcher.AddCommand(eCmdFormat, "FormatUnit", &Disk::FormatUnit); @@ -79,6 +80,16 @@ Disk::~Disk() bool Disk::Dispatch(SCSIDEV *controller) { + // Media changes must be reported on the next access, i.e. not only for TEST UNIT READY + if (disk.is_medium_changed) { + assert(IsRemovable()); + + disk.is_medium_changed = false; + + controller->Error(ERROR_CODES::sense_key::UNIT_ATTENTION, ERROR_CODES::asc::NOT_READY_TO_READY_CHANGE); + return true; + } + // The superclass handles the less specific commands return dispatcher.Dispatch(this, controller) ? true : super::Dispatch(controller); } @@ -395,6 +406,15 @@ 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); diff --git a/src/raspberrypi/devices/disk.h b/src/raspberrypi/devices/disk.h index 68dd1d50..44e4f95e 100644 --- a/src/raspberrypi/devices/disk.h +++ b/src/raspberrypi/devices/disk.h @@ -51,6 +51,7 @@ private: uint32_t blocks; // Total number of sectors DiskCache *dcache; // Disk cache off_t image_offset; // Offset to actual data + bool is_medium_changed; } disk_t; Dispatcher dispatcher; @@ -61,6 +62,7 @@ public: virtual bool Dispatch(SCSIDEV *) override; + void MediumChanged(); void ReserveFile(const string&); // Media Operations diff --git a/src/raspberrypi/devices/host_services.cpp b/src/raspberrypi/devices/host_services.cpp index f825e197..6a141399 100644 --- a/src/raspberrypi/devices/host_services.cpp +++ b/src/raspberrypi/devices/host_services.cpp @@ -58,8 +58,8 @@ void HostServices::TestUnitReady(SCSIDEV *controller) int HostServices::Inquiry(const DWORD *cdb, BYTE *buf) { - // Processor device, not removable - return PrimaryDevice::Inquiry(3, false, cdb, buf); + // Processor device, SPC-5, not removable + return PrimaryDevice::Inquiry(3, 7, false, cdb, buf); } void HostServices::StartStopUnit(SCSIDEV *controller) diff --git a/src/raspberrypi/devices/mode_page_device.cpp b/src/raspberrypi/devices/mode_page_device.cpp index 9b9e08bc..7ebce681 100644 --- a/src/raspberrypi/devices/mode_page_device.cpp +++ b/src/raspberrypi/devices/mode_page_device.cpp @@ -92,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/primary_device.cpp b/src/raspberrypi/devices/primary_device.cpp index d32677b5..9e3b3122 100644 --- a/src/raspberrypi/devices/primary_device.cpp +++ b/src/raspberrypi/devices/primary_device.cpp @@ -149,7 +149,7 @@ bool PrimaryDevice::CheckReady() return true; } -int PrimaryDevice::Inquiry(int type, bool is_removable, const DWORD *cdb, BYTE *buf) +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) { @@ -166,7 +166,7 @@ int PrimaryDevice::Inquiry(int type, bool is_removable, const DWORD *cdb, BYTE * memset(buf, 0, allocation_length); buf[0] = type; buf[1] = is_removable ? 0x80 : 0x00; - buf[2] = 0x02; + buf[2] = scsi_level; buf[4] = 0x1F; // Padded vendor, product, revision diff --git a/src/raspberrypi/devices/primary_device.h b/src/raspberrypi/devices/primary_device.h index c1410372..c986bc19 100644 --- a/src/raspberrypi/devices/primary_device.h +++ b/src/raspberrypi/devices/primary_device.h @@ -40,7 +40,7 @@ public: protected: - int Inquiry(int, bool, const DWORD *, BYTE *); + int Inquiry(int, int, bool, const DWORD *, BYTE *); SASIDEV::ctrl_t *ctrl; diff --git a/src/raspberrypi/devices/scsi_daynaport.cpp b/src/raspberrypi/devices/scsi_daynaport.cpp index 4c0aeaf7..f70b6e7f 100644 --- a/src/raspberrypi/devices/scsi_daynaport.cpp +++ b/src/raspberrypi/devices/scsi_daynaport.cpp @@ -114,8 +114,8 @@ void SCSIDaynaPort::Open(const Filepath& path) int SCSIDaynaPort::Inquiry(const DWORD *cdb, BYTE *buf) { - // Processor device, not removable - return PrimaryDevice::Inquiry(3, false, cdb, buf); + // Processor device, SCSI-2, not removable + return PrimaryDevice::Inquiry(3, 2, false, cdb, buf); } //--------------------------------------------------------------------------- diff --git a/src/raspberrypi/devices/scsi_printer.cpp b/src/raspberrypi/devices/scsi_printer.cpp index 1c2b1409..066eb4ac 100644 --- a/src/raspberrypi/devices/scsi_printer.cpp +++ b/src/raspberrypi/devices/scsi_printer.cpp @@ -105,8 +105,8 @@ void SCSIPrinter::TestUnitReady(SCSIDEV *controller) int SCSIPrinter::Inquiry(const DWORD *cdb, BYTE *buf) { - // Printer device, not removable - return PrimaryDevice::Inquiry(2, false, cdb, buf); + // Printer device, SCSI-2, not removable + return PrimaryDevice::Inquiry(2, 2, false, cdb, buf); } void SCSIPrinter::ReserveUnit(SCSIDEV *controller) diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 271f7cc4..84d30cfa 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -822,6 +822,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; } diff --git a/src/raspberrypi/scsi.h b/src/raspberrypi/scsi.h index 699dd15b..b648390c 100644 --- a/src/raspberrypi/scsi.h +++ b/src/raspberrypi/scsi.h @@ -58,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 }; };
{{ _("Type") }}{{ _("Actions") }}{{ _("Peripheral") }}{{ _("Parameters and Actions") }}
-
{{ device_types[type] }}
+
{{ device_types[type]["name"] }}
- {% for key, value in device_params[type].items() %} + {% for key, value in device_types[type]["params"].items() %} {% if value.isnumeric() %} - + {% elif key == "interface" %} {{ RESERVATIONS[device.id] }} - + - +